idea ide updates
idea ide updates

  <?xml version="1.0" encoding="UTF-8"?>
  <project version="4">
  <component name="CompilerConfiguration">
  <option name="DEFAULT_COMPILER" value="Javac" />
  <resourceExtensions />
  <wildcardResourcePatterns>
  <entry name="!?*.java" />
  <entry name="!?*.form" />
  <entry name="!?*.class" />
  <entry name="!?*.groovy" />
  <entry name="!?*.scala" />
  <entry name="!?*.flex" />
  <entry name="!?*.kt" />
  <entry name="!?*.clj" />
  </wildcardResourcePatterns>
  <annotationProcessing>
  <profile default="true" name="Default" enabled="false">
  <processorPath useClasspath="true" />
  </profile>
  </annotationProcessing>
  </component>
  </project>
 
 
  <component name="CopyrightManager">
  <settings default="">
  <module2copyright />
  </settings>
  </component>
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<project version="4"> <project version="4">
<component name="ProjectResources"> <component name="ProjectResources">
<default-html-doctype>jar:file:\C:\Program Files (x86)\JetBrains\PhpStorm 5.0.2\lib\webide.jar!\resources\html5-schema\html5.rnc</default-html-doctype> <default-html-doctype>jar:file:\C:\Program Files (x86)\JetBrains\PhpStorm 5.0.2\lib\webide.jar!\resources\html5-schema\html5.rnc</default-html-doctype>
</component> </component>
<component name="ProjectRootManager" version="2" /> <component name="ProjectRootManager" version="2" languageLevel="JDK_1_3" assert-keyword="false" jdk-15="false" />
</project> </project>
   
   
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<module type="WEB_MODULE" version="4"> <module type="WEB_MODULE" version="4">
<component name="NewModuleRootManager"> <component name="FacetManager">
  <facet type="Python" name="Python">
  <configuration sdkName="" />
  </facet>
  </component>
  <component name="NewModuleRootManager" inherit-compiler-output="false">
<content url="file://$MODULE_DIR$" /> <content url="file://$MODULE_DIR$" />
<orderEntry type="inheritedJdk" /> <orderEntry type="inheritedJdk" />
<orderEntry type="sourceFolder" forTests="false" /> <orderEntry type="sourceFolder" forTests="false" />
</component> </component>
</module> </module>
   
   
  <?xml version="1.0" encoding="UTF-8"?>
  <project version="4">
  <component name="Palette2">
  <group name="Swing">
  <item class="com.intellij.uiDesigner.HSpacer" tooltip-text="Horizontal Spacer" icon="/com/intellij/uiDesigner/icons/hspacer.png" removable="false" auto-create-binding="false" can-attach-label="false">
  <default-constraints vsize-policy="1" hsize-policy="6" anchor="0" fill="1" />
  </item>
  <item class="com.intellij.uiDesigner.VSpacer" tooltip-text="Vertical Spacer" icon="/com/intellij/uiDesigner/icons/vspacer.png" removable="false" auto-create-binding="false" can-attach-label="false">
  <default-constraints vsize-policy="6" hsize-policy="1" anchor="0" fill="2" />
  </item>
  <item class="javax.swing.JPanel" icon="/com/intellij/uiDesigner/icons/panel.png" removable="false" auto-create-binding="false" can-attach-label="false">
  <default-constraints vsize-policy="3" hsize-policy="3" anchor="0" fill="3" />
  </item>
  <item class="javax.swing.JScrollPane" icon="/com/intellij/uiDesigner/icons/scrollPane.png" removable="false" auto-create-binding="false" can-attach-label="true">
  <default-constraints vsize-policy="7" hsize-policy="7" anchor="0" fill="3" />
  </item>
  <item class="javax.swing.JButton" icon="/com/intellij/uiDesigner/icons/button.png" removable="false" auto-create-binding="true" can-attach-label="false">
  <default-constraints vsize-policy="0" hsize-policy="3" anchor="0" fill="1" />
  <initial-values>
  <property name="text" value="Button" />
  </initial-values>
  </item>
  <item class="javax.swing.JRadioButton" icon="/com/intellij/uiDesigner/icons/radioButton.png" removable="false" auto-create-binding="true" can-attach-label="false">
  <default-constraints vsize-policy="0" hsize-policy="3" anchor="8" fill="0" />
  <initial-values>
  <property name="text" value="RadioButton" />
  </initial-values>
  </item>
  <item class="javax.swing.JCheckBox" icon="/com/intellij/uiDesigner/icons/checkBox.png" removable="false" auto-create-binding="true" can-attach-label="false">
  <default-constraints vsize-policy="0" hsize-policy="3" anchor="8" fill="0" />
  <initial-values>
  <property name="text" value="CheckBox" />
  </initial-values>
  </item>
  <item class="javax.swing.JLabel" icon="/com/intellij/uiDesigner/icons/label.png" removable="false" auto-create-binding="false" can-attach-label="false">
  <default-constraints vsize-policy="0" hsize-policy="0" anchor="8" fill="0" />
  <initial-values>
  <property name="text" value="Label" />
  </initial-values>
  </item>
  <item class="javax.swing.JTextField" icon="/com/intellij/uiDesigner/icons/textField.png" removable="false" auto-create-binding="true" can-attach-label="true">
  <default-constraints vsize-policy="0" hsize-policy="6" anchor="8" fill="1">
  <preferred-size width="150" height="-1" />
  </default-constraints>
  </item>
  <item class="javax.swing.JPasswordField" icon="/com/intellij/uiDesigner/icons/passwordField.png" removable="false" auto-create-binding="true" can-attach-label="true">
  <default-constraints vsize-policy="0" hsize-policy="6" anchor="8" fill="1">
  <preferred-size width="150" height="-1" />
  </default-constraints>
  </item>
  <item class="javax.swing.JFormattedTextField" icon="/com/intellij/uiDesigner/icons/formattedTextField.png" removable="false" auto-create-binding="true" can-attach-label="true">
  <default-constraints vsize-policy="0" hsize-policy="6" anchor="8" fill="1">
  <preferred-size width="150" height="-1" />
  </default-constraints>
  </item>
  <item class="javax.swing.JTextArea" icon="/com/intellij/uiDesigner/icons/textArea.png" removable="false" auto-create-binding="true" can-attach-label="true">
  <default-constraints vsize-policy="6" hsize-policy="6" anchor="0" fill="3">
  <preferred-size width="150" height="50" />
  </default-constraints>
  </item>
  <item class="javax.swing.JTextPane" icon="/com/intellij/uiDesigner/icons/textPane.png" removable="false" auto-create-binding="true" can-attach-label="true">
  <default-constraints vsize-policy="6" hsize-policy="6" anchor="0" fill="3">
  <preferred-size width="150" height="50" />
  </default-constraints>
  </item>
  <item class="javax.swing.JEditorPane" icon="/com/intellij/uiDesigner/icons/editorPane.png" removable="false" auto-create-binding="true" can-attach-label="true">
  <default-constraints vsize-policy="6" hsize-policy="6" anchor="0" fill="3">
  <preferred-size width="150" height="50" />
  </default-constraints>
  </item>
  <item class="javax.swing.JComboBox" icon="/com/intellij/uiDesigner/icons/comboBox.png" removable="false" auto-create-binding="true" can-attach-label="true">
  <default-constraints vsize-policy="0" hsize-policy="2" anchor="8" fill="1" />
  </item>
  <item class="javax.swing.JTable" icon="/com/intellij/uiDesigner/icons/table.png" removable="false" auto-create-binding="true" can-attach-label="false">
  <default-constraints vsize-policy="6" hsize-policy="6" anchor="0" fill="3">
  <preferred-size width="150" height="50" />
  </default-constraints>
  </item>
  <item class="javax.swing.JList" icon="/com/intellij/uiDesigner/icons/list.png" removable="false" auto-create-binding="true" can-attach-label="false">
  <default-constraints vsize-policy="6" hsize-policy="2" anchor="0" fill="3">
  <preferred-size width="150" height="50" />
  </default-constraints>
  </item>
  <item class="javax.swing.JTree" icon="/com/intellij/uiDesigner/icons/tree.png" removable="false" auto-create-binding="true" can-attach-label="false">
  <default-constraints vsize-policy="6" hsize-policy="6" anchor="0" fill="3">
  <preferred-size width="150" height="50" />
  </default-constraints>
  </item>
  <item class="javax.swing.JTabbedPane" icon="/com/intellij/uiDesigner/icons/tabbedPane.png" removable="false" auto-create-binding="true" can-attach-label="false">
  <default-constraints vsize-policy="3" hsize-policy="3" anchor="0" fill="3">
  <preferred-size width="200" height="200" />
  </default-constraints>
  </item>
  <item class="javax.swing.JSplitPane" icon="/com/intellij/uiDesigner/icons/splitPane.png" removable="false" auto-create-binding="false" can-attach-label="false">
  <default-constraints vsize-policy="3" hsize-policy="3" anchor="0" fill="3">
  <preferred-size width="200" height="200" />
  </default-constraints>
  </item>
  <item class="javax.swing.JSpinner" icon="/com/intellij/uiDesigner/icons/spinner.png" removable="false" auto-create-binding="true" can-attach-label="true">
  <default-constraints vsize-policy="0" hsize-policy="6" anchor="8" fill="1" />
  </item>
  <item class="javax.swing.JSlider" icon="/com/intellij/uiDesigner/icons/slider.png" removable="false" auto-create-binding="true" can-attach-label="false">
  <default-constraints vsize-policy="0" hsize-policy="6" anchor="8" fill="1" />
  </item>
  <item class="javax.swing.JSeparator" icon="/com/intellij/uiDesigner/icons/separator.png" removable="false" auto-create-binding="false" can-attach-label="false">
  <default-constraints vsize-policy="6" hsize-policy="6" anchor="0" fill="3" />
  </item>
  <item class="javax.swing.JProgressBar" icon="/com/intellij/uiDesigner/icons/progressbar.png" removable="false" auto-create-binding="true" can-attach-label="false">
  <default-constraints vsize-policy="0" hsize-policy="6" anchor="0" fill="1" />
  </item>
  <item class="javax.swing.JToolBar" icon="/com/intellij/uiDesigner/icons/toolbar.png" removable="false" auto-create-binding="false" can-attach-label="false">
  <default-constraints vsize-policy="0" hsize-policy="6" anchor="0" fill="1">
  <preferred-size width="-1" height="20" />
  </default-constraints>
  </item>
  <item class="javax.swing.JToolBar$Separator" icon="/com/intellij/uiDesigner/icons/toolbarSeparator.png" removable="false" auto-create-binding="false" can-attach-label="false">
  <default-constraints vsize-policy="0" hsize-policy="0" anchor="0" fill="1" />
  </item>
  <item class="javax.swing.JScrollBar" icon="/com/intellij/uiDesigner/icons/scrollbar.png" removable="false" auto-create-binding="true" can-attach-label="false">
  <default-constraints vsize-policy="6" hsize-policy="0" anchor="0" fill="2" />
  </item>
  </group>
  </component>
  </project>
 
 
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<project version="4"> <project version="4">
<component name="ChangeListManager"> <component name="ChangeListManager">
<list default="true" id="f90ee5b5-97e4-47ec-9b14-d4f4e896f100" name="Default" comment=""> <list default="true" id="f90ee5b5-97e4-47ec-9b14-d4f4e896f100" name="Default" comment="">
<change type="MODIFICATION" beforePath="$PROJECT_DIR$/output.txt" afterPath="$PROJECT_DIR$/output.txt" /> <change type="NEW" beforePath="" afterPath="$PROJECT_DIR$/.idea/compiler.xml" />
  <change type="NEW" beforePath="" afterPath="$PROJECT_DIR$/.idea/copyright/profiles_settings.xml" />
  <change type="NEW" beforePath="" afterPath="$PROJECT_DIR$/.idea/uiDesigner.xml" />
  <change type="MODIFICATION" beforePath="$PROJECT_DIR$/404.html" afterPath="$PROJECT_DIR$/404.html" />
  <change type="MODIFICATION" beforePath="$PROJECT_DIR$/js/flotr2/spec/Chart.js" afterPath="$PROJECT_DIR$/js/flotr2/spec/Chart.js" />
  <change type="MODIFICATION" beforePath="$PROJECT_DIR$/js/flotr2/spec/Color.js" afterPath="$PROJECT_DIR$/js/flotr2/spec/Color.js" />
  <change type="MODIFICATION" beforePath="$PROJECT_DIR$/js/flotr2/spec/Flotr.js" afterPath="$PROJECT_DIR$/js/flotr2/spec/Flotr.js" />
  <change type="MODIFICATION" beforePath="$PROJECT_DIR$/js/flotr2/spec/Graph.js" afterPath="$PROJECT_DIR$/js/flotr2/spec/Graph.js" />
  <change type="MODIFICATION" beforePath="$PROJECT_DIR$/js/flotr2/lib/base64.js" afterPath="$PROJECT_DIR$/js/flotr2/lib/base64.js" />
  <change type="MODIFICATION" beforePath="$PROJECT_DIR$/js/flotr2/make/basic.json" afterPath="$PROJECT_DIR$/js/flotr2/make/basic.json" />
  <change type="MODIFICATION" beforePath="$PROJECT_DIR$/js/flotr2/make/build.json" afterPath="$PROJECT_DIR$/js/flotr2/make/build.json" />
  <change type="MODIFICATION" beforePath="$PROJECT_DIR$/calllog.php" afterPath="$PROJECT_DIR$/calllog.php" />
  <change type="MODIFICATION" beforePath="$PROJECT_DIR$/calls.json.php" afterPath="$PROJECT_DIR$/calls.json.php" />
  <change type="MODIFICATION" beforePath="$PROJECT_DIR$/common.inc.php" afterPath="$PROJECT_DIR$/common.inc.php" />
  <change type="MODIFICATION" beforePath="$PROJECT_DIR$/js/flotr2/examples/old_examples/examples.js" afterPath="$PROJECT_DIR$/js/flotr2/examples/old_examples/examples.js" />
  <change type="MODIFICATION" beforePath="$PROJECT_DIR$/js/flotr2/make/examples.json" afterPath="$PROJECT_DIR$/js/flotr2/make/examples.json" />
  <change type="MODIFICATION" beforePath="$PROJECT_DIR$/js/flotr2/examples/old_examples/extending-flotr.html" afterPath="$PROJECT_DIR$/js/flotr2/examples/old_examples/extending-flotr.html" />
  <change type="MODIFICATION" beforePath="$PROJECT_DIR$/js/flotr2/make/flotr2.json" afterPath="$PROJECT_DIR$/js/flotr2/make/flotr2.json" />
  <change type="MODIFICATION" beforePath="$PROJECT_DIR$/js/flotr2/spec/js/flotr2.stable.js" afterPath="$PROJECT_DIR$/js/flotr2/spec/js/flotr2.stable.js" />
  <change type="MODIFICATION" beforePath="$PROJECT_DIR$/js/flotr2/examples/lib/codemirror/lib/util/formatting.js" afterPath="$PROJECT_DIR$/js/flotr2/examples/lib/codemirror/lib/util/formatting.js" />
  <change type="MODIFICATION" beforePath="$PROJECT_DIR$/generateConvos.php" afterPath="$PROJECT_DIR$/generateConvos.php" />
  <change type="MODIFICATION" beforePath="$PROJECT_DIR$/getfile.php" afterPath="$PROJECT_DIR$/getfile.php" />
  <change type="MODIFICATION" beforePath="$PROJECT_DIR$/js/flotr2/make/ie.json" afterPath="$PROJECT_DIR$/js/flotr2/make/ie.json" />
  <change type="MODIFICATION" beforePath="$PROJECT_DIR$/js/flotr2/examples/old_examples/index.html" afterPath="$PROJECT_DIR$/js/flotr2/examples/old_examples/index.html" />
  <change type="MODIFICATION" beforePath="$PROJECT_DIR$/js/flotr2/spec/index.html" afterPath="$PROJECT_DIR$/js/flotr2/spec/index.html" />
  <change type="MODIFICATION" beforePath="$PROJECT_DIR$/js/flotr2/lib/jasmine/jasmine-html.js" afterPath="$PROJECT_DIR$/js/flotr2/lib/jasmine/jasmine-html.js" />
  <change type="MODIFICATION" beforePath="$PROJECT_DIR$/js/flotr2/lib/jasmine/jasmine.css" afterPath="$PROJECT_DIR$/js/flotr2/lib/jasmine/jasmine.css" />
  <change type="MODIFICATION" beforePath="$PROJECT_DIR$/js/flotr2/lib/jasmine/jasmine.js" afterPath="$PROJECT_DIR$/js/flotr2/lib/jasmine/jasmine.js" />
  <change type="MODIFICATION" beforePath="$PROJECT_DIR$/js/vendor/jquery-1.8.0.min.js" afterPath="$PROJECT_DIR$/js/vendor/jquery-1.8.0.min.js" />
  <change type="MODIFICATION" beforePath="$PROJECT_DIR$/js/flotr2/examples/old_examples/json-data.html" afterPath="$PROJECT_DIR$/js/flotr2/examples/old_examples/json-data.html" />
  <change type="MODIFICATION" beforePath="$PROJECT_DIR$/js/flotr2/examples/old_examples/json-real-data.html" afterPath="$PROJECT_DIR$/js/flotr2/examples/old_examples/json-real-data.html" />
  <change type="MODIFICATION" beforePath="$PROJECT_DIR$/js/flotr2/examples/old_examples/json.txt" afterPath="$PROJECT_DIR$/js/flotr2/examples/old_examples/json.txt" />
  <change type="MODIFICATION" beforePath="$PROJECT_DIR$/js/flotr2/make/lib.json" afterPath="$PROJECT_DIR$/js/flotr2/make/lib.json" />
  <change type="MODIFICATION" beforePath="$PROJECT_DIR$/js/flotr2/examples/old_examples/logarithmic-scale.html" afterPath="$PROJECT_DIR$/js/flotr2/examples/old_examples/logarithmic-scale.html" />
  <change type="MODIFICATION" beforePath="$PROJECT_DIR$/.idea/misc.xml" afterPath="$PROJECT_DIR$/.idea/misc.xml" />
  <change type="MODIFICATION" beforePath="$PROJECT_DIR$/js/vendor/modernizr-2.6.1.min.js" afterPath="$PROJECT_DIR$/js/vendor/modernizr-2.6.1.min.js" />
  <change type="MODIFICATION" beforePath="$PROJECT_DIR$/js/flotr2/examples/old_examples/mouse-zoom-preview.html" afterPath="$PROJECT_DIR$/js/flotr2/examples/old_examples/mouse-zoom-preview.html" />
  <change type="MODIFICATION" beforePath="$PROJECT_DIR$/nbproject/project.xml" afterPath="$PROJECT_DIR$/nbproject/project.xml" />
  <change type="MODIFICATION" beforePath="$PROJECT_DIR$/js/flotr2/lib/prototype.js" afterPath="$PROJECT_DIR$/js/flotr2/lib/prototype.js" />
  <change type="MODIFICATION" beforePath="$PROJECT_DIR$/.idea/scannr.iml" afterPath="$PROJECT_DIR$/.idea/scannr.iml" />
  <change type="MODIFICATION" beforePath="$PROJECT_DIR$/scannr.py" afterPath="$PROJECT_DIR$/scannr.py" />
  <change type="MODIFICATION" beforePath="$PROJECT_DIR$/snd.py" afterPath="$PROJECT_DIR$/snd.py" />
  <change type="MODIFICATION" beforePath="$PROJECT_DIR$/js/flotr2/examples/old_examples/style.css" afterPath="$PROJECT_DIR$/js/flotr2/examples/old_examples/style.css" />
  <change type="MODIFICATION" beforePath="$PROJECT_DIR$/js/flotr2/spec/js/test-background.js" afterPath="$PROJECT_DIR$/js/flotr2/spec/js/test-background.js" />
  <change type="MODIFICATION" beforePath="$PROJECT_DIR$/js/flotr2/spec/js/test-boundaries.js" afterPath="$PROJECT_DIR$/js/flotr2/spec/js/test-boundaries.js" />
  <change type="MODIFICATION" beforePath="$PROJECT_DIR$/js/flotr2/spec/js/test-mountain-nulls.js" afterPath="$PROJECT_DIR$/js/flotr2/spec/js/test-mountain-nulls.js" />
  <change type="MODIFICATION" beforePath="$PROJECT_DIR$/test.py" afterPath="$PROJECT_DIR$/test.py" />
  <change type="MODIFICATION" beforePath="$PROJECT_DIR$/test2.py" afterPath="$PROJECT_DIR$/test2.py" />
  <change type="MODIFICATION" beforePath="$PROJECT_DIR$/trunklog.php" afterPath="$PROJECT_DIR$/trunklog.php" />
<change type="MODIFICATION" beforePath="$PROJECT_DIR$/viewcalls.php" afterPath="$PROJECT_DIR$/viewcalls.php" /> <change type="MODIFICATION" beforePath="$PROJECT_DIR$/viewcalls.php" afterPath="$PROJECT_DIR$/viewcalls.php" />
  <change type="MODIFICATION" beforePath="$PROJECT_DIR$/.idea/workspace.xml" afterPath="$PROJECT_DIR$/.idea/workspace.xml" />
</list> </list>
<ignored path="scannr.iws" /> <ignored path="scannr.iws" />
<ignored path=".idea/workspace.xml" /> <ignored path=".idea/workspace.xml" />
  <file path="/Dummy.txt" changelist="f90ee5b5-97e4-47ec-9b14-d4f4e896f100" time="1356154429152" ignored="false" />
  <file path="/calllog.php" changelist="f90ee5b5-97e4-47ec-9b14-d4f4e896f100" time="1356153807482" ignored="false" />
  <file path="/scannr.py" changelist="f90ee5b5-97e4-47ec-9b14-d4f4e896f100" time="1356154551131" ignored="false" />
  <file path="/start_script.py" changelist="f90ee5b5-97e4-47ec-9b14-d4f4e896f100" time="1356155203132" ignored="false" />
  <file path="/a.java" changelist="f90ee5b5-97e4-47ec-9b14-d4f4e896f100" time="1356155211924" ignored="false" />
  <file path="/a.php" changelist="f90ee5b5-97e4-47ec-9b14-d4f4e896f100" time="1356155216083" ignored="false" />
<option name="TRACKING_ENABLED" value="true" /> <option name="TRACKING_ENABLED" value="true" />
<option name="SHOW_DIALOG" value="false" /> <option name="SHOW_DIALOG" value="false" />
<option name="HIGHLIGHT_CONFLICTS" value="true" /> <option name="HIGHLIGHT_CONFLICTS" value="true" />
<option name="HIGHLIGHT_NON_ACTIVE_CHANGELIST" value="false" /> <option name="HIGHLIGHT_NON_ACTIVE_CHANGELIST" value="false" />
<option name="LAST_RESOLUTION" value="IGNORE" /> <option name="LAST_RESOLUTION" value="IGNORE" />
</component> </component>
<component name="ChangesViewManager" flattened_view="true" show_ignored="false" /> <component name="ChangesViewManager" flattened_view="true" show_ignored="false" />
<component name="CreatePatchCommitExecutor"> <component name="CreatePatchCommitExecutor">
<option name="PATCH_PATH" value="" /> <option name="PATCH_PATH" value="" />
</component> </component>
<component name="DaemonCodeAnalyzer"> <component name="DaemonCodeAnalyzer">
<disable_hints /> <disable_hints />
</component> </component>
  <component name="DebuggerManager">
  <ui_properties default_suspend_policy="SuspendAll" default_condition_enabled="true" />
  <breakpoint_any default_suspend_policy="SuspendAll" default_condition_enabled="true">
  <breakpoint>
  <option name="NOTIFY_CAUGHT" value="true" />
  <option name="NOTIFY_UNCAUGHT" value="true" />
  <option name="ENABLED" value="false" />
  <option name="LOG_ENABLED" value="false" />
  <option name="LOG_EXPRESSION_ENABLED" value="false" />
  <option name="SUSPEND_POLICY" value="SuspendAll" />
  <option name="SUSPEND" value="true" />
  <option name="COUNT_FILTER_ENABLED" value="false" />
  <option name="COUNT_FILTER" value="0" />
  <option name="CONDITION_ENABLED" value="true" />
  <option name="CLASS_FILTERS_ENABLED" value="false" />
  <option name="INSTANCE_FILTERS_ENABLED" value="false" />
  <option name="CONDITION" value="" />
  <option name="LOG_MESSAGE" value="" />
  </breakpoint>
  <breakpoint>
  <option name="NOTIFY_CAUGHT" value="true" />
  <option name="NOTIFY_UNCAUGHT" value="true" />
  <option name="ENABLED" value="false" />
  <option name="LOG_ENABLED" value="false" />
  <option name="LOG_EXPRESSION_ENABLED" value="false" />
  <option name="SUSPEND_POLICY" value="SuspendAll" />
  <option name="SUSPEND" value="true" />
  <option name="COUNT_FILTER_ENABLED" value="false" />
  <option name="COUNT_FILTER" value="0" />
  <option name="CONDITION_ENABLED" value="true" />
  <option name="CLASS_FILTERS_ENABLED" value="false" />
  <option name="INSTANCE_FILTERS_ENABLED" value="false" />
  <option name="CONDITION" value="" />
  <option name="LOG_MESSAGE" value="" />
  </breakpoint>
  </breakpoint_any>
  <breakpoint_rules />
  <ui_properties />
  </component>
<component name="ExecutionTargetManager" SELECTED_TARGET="default_target" /> <component name="ExecutionTargetManager" SELECTED_TARGET="default_target" />
  <component name="FavoritesManager">
  <favorites_list name="scannr" />
  </component>
<component name="FileEditorManager"> <component name="FileEditorManager">
<leaf> <leaf>
<file leaf-file-name="viewcalls.php" pinned="false" current="true" current-in-tab="true"> <file leaf-file-name="calllog.php" pinned="false" current="false" current-in-tab="false">
<entry file="file://$PROJECT_DIR$/viewcalls.php"> <entry file="file://$PROJECT_DIR$/calllog.php">
<provider selected="true" editor-type-id="text-editor"> <provider selected="true" editor-type-id="text-editor">
<state line="41" column="69" selection-start="1388" selection-end="1388" vertical-scroll-proportion="1.2659575"> <state line="9" column="0" selection-start="189" selection-end="189" vertical-scroll-proportion="0.0">
<folding>  
<element signature="e#255#287#0" expanded="true" />  
</folding>  
</state>  
</provider>  
</entry>  
</file>  
<file leaf-file-name="common.inc.php" pinned="false" current="false" current-in-tab="false">  
<entry file="file://$PROJECT_DIR$/common.inc.php">  
<provider selected="true" editor-type-id="text-editor">  
<state line="60" column="117" selection-start="2262" selection-end="2262" vertical-scroll-proportion="-39.23077">  
<folding /> <folding />
</state> </state>
</provider> </provider>
</entry> </entry>
</file> </file>
<file leaf-file-name="calls.json.php" pinned="false" current="false" current-in-tab="false"> <file leaf-file-name="scannr.py" pinned="false" current="true" current-in-tab="true">
<entry file="file://$PROJECT_DIR$/calls.json.php"> <entry file="file://$PROJECT_DIR$/scannr.py">
<provider selected="true" editor-type-id="text-editor"> <provider selected="true" editor-type-id="text-editor">
<state line="72" column="41" selection-start="2843" selection-end="2843" vertical-scroll-proportion="-43.153847"> <state line="80" column="64" selection-start="2271" selection-end="2271" vertical-scroll-proportion="-0.31178707">
<folding /> <folding />
</state> </state>
</provider> </provider>
</entry> </entry>
</file> </file>
</leaf> </leaf>
</component> </component>
<component name="FindManager"> <component name="FindManager">
<FindUsagesManager> <FindUsagesManager>
<setting name="OPEN_NEW_TAB" value="false" /> <setting name="OPEN_NEW_TAB" value="false" />
</FindUsagesManager> </FindUsagesManager>
</component> </component>
<component name="Git.Settings"> <component name="Git.Settings">
  <option name="SYNC_SETTING" value="DONT" />
<option name="RECENT_GIT_ROOT_PATH" value="$PROJECT_DIR$" /> <option name="RECENT_GIT_ROOT_PATH" value="$PROJECT_DIR$" />
  </component>
  <component name="GitLogSettings">
  <option name="myDateState">
  <MyDateState />
  </option>
</component> </component>
<component name="IdeDocumentHistory"> <component name="IdeDocumentHistory">
<option name="changedFiles"> <option name="changedFiles">
<list> <list>
<option value="$PROJECT_DIR$/scannr.py" />  
<option value="$PROJECT_DIR$/common.inc.php" /> <option value="$PROJECT_DIR$/common.inc.php" />
<option value="$PROJECT_DIR$/calls.json.php" /> <option value="$PROJECT_DIR$/calls.json.php" />
<option value="$PROJECT_DIR$/viewcalls.php" /> <option value="$PROJECT_DIR$/viewcalls.php" />
  <option value="$PROJECT_DIR$/calllog.php" />
  <option value="$PROJECT_DIR$/scannr.py" />
</list> </list>
</option> </option>
</component> </component>
<component name="PhpWorkspaceProjectConfiguration" backward_compatibility_performed="true" /> <component name="PhpWorkspaceProjectConfiguration" backward_compatibility_performed="true" interpreter_name="PHP" />
<component name="ProjectFrameBounds"> <component name="ProjectFrameBounds">
<option name="x" value="619" /> <option name="y" value="22" />
<option name="width" value="825" /> <option name="width" value="1680" />
<option name="height" value="600" /> <option name="height" value="937" />
</component> </component>
<component name="ProjectLevelVcsManager" settingsEditedManually="true"> <component name="ProjectLevelVcsManager" settingsEditedManually="true">
<OptionsSetting value="true" id="Add" /> <OptionsSetting value="true" id="Add" />
<OptionsSetting value="true" id="Remove" /> <OptionsSetting value="true" id="Remove" />
<OptionsSetting value="true" id="Checkout" /> <OptionsSetting value="true" id="Checkout" />
<OptionsSetting value="true" id="Update" /> <OptionsSetting value="true" id="Update" />
<OptionsSetting value="true" id="Status" /> <OptionsSetting value="true" id="Status" />
<OptionsSetting value="true" id="Edit" /> <OptionsSetting value="true" id="Edit" />
<ConfirmationsSetting value="0" id="Add" /> <ConfirmationsSetting value="0" id="Add" />
<ConfirmationsSetting value="0" id="Remove" /> <ConfirmationsSetting value="0" id="Remove" />
</component> </component>
<component name="ProjectReloadState"> <component name="ProjectReloadState">
<option name="STATE" value="0" /> <option name="STATE" value="0" />
</component> </component>
<component name="ProjectView"> <component name="ProjectView">
<navigator currentView="ProjectPane" proportions="" version="1" splitterProportion="0.5"> <navigator currentView="ProjectPane" proportions="" version="1" splitterProportion="0.5">
<flattenPackages /> <flattenPackages />
<showMembers /> <showMembers />
<showModules /> <showModules />
<showLibraryContents ProjectPane="true" /> <showLibraryContents ProjectPane="true" />
<hideEmptyPackages /> <hideEmptyPackages />
<abbreviatePackageNames /> <abbreviatePackageNames />
<autoscrollToSource /> <autoscrollToSource />
<autoscrollFromSource /> <autoscrollFromSource />
<sortByType /> <sortByType />
</navigator> </navigator>
<panes> <panes>
  <pane id="PackagesPane" />
<pane id="ProjectPane"> <pane id="ProjectPane">
<subPane> <subPane>
<PATH> <PATH>
<PATH_ELEMENT> <PATH_ELEMENT>
<option name="myItemId" value="scannr" /> <option name="myItemId" value="scannr" />
<option name="myItemType" value="com.intellij.ide.projectView.impl.nodes.ProjectViewProjectNode" /> <option name="myItemType" value="com.intellij.ide.projectView.impl.nodes.ProjectViewProjectNode" />
</PATH_ELEMENT> </PATH_ELEMENT>
</PATH> </PATH>
  <PATH>
  <PATH_ELEMENT>
  <option name="myItemId" value="scannr" />
  <option name="myItemType" value="com.intellij.ide.projectView.impl.nodes.ProjectViewProjectNode" />
  </PATH_ELEMENT>
  <PATH_ELEMENT>
  <option name="myItemId" value="scannr" />
  <option name="myItemType" value="com.intellij.ide.projectView.impl.nodes.PsiDirectoryNode" />
  </PATH_ELEMENT>
  </PATH>
</subPane> </subPane>
</pane> </pane>
<pane id="Scope" /> <pane id="Scope" />
</panes> </panes>
</component> </component>
<component name="PropertiesComponent"> <component name="PropertiesComponent">
<property name="options.splitter.main.proportions" value="0.3" /> <property name="options.splitter.main.proportions" value="0.3" />
<property name="WebServerToolWindowFactoryState" value="false" /> <property name="WebServerToolWindowFactoryState" value="false" />
<property name="options.lastSelected" value="project.propVCSSupport.Mappings" /> <property name="options.lastSelected" value="tasks" />
<property name="last_opened_file_path" value="$PROJECT_DIR$/../busui/myway/myway_timeliness.php" /> <property name="last_opened_file_path" value="$PROJECT_DIR$/../busui/myway/myway_timeliness.php" />
  <property name="FullScreen" value="false" />
<property name="options.splitter.details.proportions" value="0.2" /> <property name="options.splitter.details.proportions" value="0.2" />
<property name="options.searchVisible" value="true" /> <property name="options.searchVisible" value="true" />
  </component>
  <component name="PyConsoleOptionsProvider">
  <option name="myPythonConsoleState">
  <PyConsoleSettings />
  </option>
  <option name="myDjangoConsoleState">
  <PyConsoleSettings />
  </option>
</component> </component>
<component name="RunManager"> <component name="RunManager">
<configuration default="true" type="PHPUnitRunConfigurationType" factoryName="PHPUnit"> <configuration default="true" type="PHPUnitRunConfigurationType" factoryName="PHPUnit">
<TestRunner /> <TestRunner />
<method /> <method />
</configuration> </configuration>
<configuration default="true" type="PhpLocalRunConfigurationType" factoryName="PHP Console"> <configuration default="true" type="JavascriptDebugSession" factoryName="Local" singleton="true">
<method />  
</configuration>  
<configuration default="true" type="JavascriptDebugSession" factoryName="Local">  
<JSDebuggerConfigurationSettings> <JSDebuggerConfigurationSettings>
<option name="engineId" value="embedded" /> <option name="engineId" value="embedded" />
<option name="fileUrl" /> <option name="fileUrl" />
</JSDebuggerConfigurationSettings> </JSDebuggerConfigurationSettings>
<method /> <method />
</configuration> </configuration>
<list size="0" /> <list size="0" />
</component> </component>
<component name="ShelveChangesManager" show_recycled="false" /> <component name="ShelveChangesManager" show_recycled="false" />
<component name="SvnConfiguration" maxAnnotateRevisions="500" myUseAcceleration="nothing" myAutoUpdateAfterCommit="false" cleanupOnStartRun="false"> <component name="SvnConfiguration" maxAnnotateRevisions="500" myUseAcceleration="nothing" myAutoUpdateAfterCommit="false" cleanupOnStartRun="false">
<option name="USER" value="" /> <option name="USER" value="" />
<option name="PASSWORD" value="" /> <option name="PASSWORD" value="" />
<option name="mySSHConnectionTimeout" value="30000" /> <option name="mySSHConnectionTimeout" value="30000" />
<option name="mySSHReadTimeout" value="30000" /> <option name="mySSHReadTimeout" value="30000" />
<option name="LAST_MERGED_REVISION" /> <option name="LAST_MERGED_REVISION" />
<option name="MERGE_DRY_RUN" value="false" /> <option name="MERGE_DRY_RUN" value="false" />
<option name="MERGE_DIFF_USE_ANCESTRY" value="true" /> <option name="MERGE_DIFF_USE_ANCESTRY" value="true" />
<option name="UPDATE_LOCK_ON_DEMAND" value="false" /> <option name="UPDATE_LOCK_ON_DEMAND" value="false" />
<option name="IGNORE_SPACES_IN_MERGE" value="false" /> <option name="IGNORE_SPACES_IN_MERGE" value="false" />
<option name="DETECT_NESTED_COPIES" value="true" /> <option name="DETECT_NESTED_COPIES" value="true" />
<option name="CHECK_NESTED_FOR_QUICK_MERGE" value="false" /> <option name="CHECK_NESTED_FOR_QUICK_MERGE" value="false" />
<option name="IGNORE_SPACES_IN_ANNOTATE" value="true" /> <option name="IGNORE_SPACES_IN_ANNOTATE" value="true" />
<option name="SHOW_MERGE_SOURCES_IN_ANNOTATE" value="true" /> <option name="SHOW_MERGE_SOURCES_IN_ANNOTATE" value="true" />
<option name="FORCE_UPDATE" value="false" /> <option name="FORCE_UPDATE" value="false" />
<option name="IGNORE_EXTERNALS" value="false" /> <option name="IGNORE_EXTERNALS" value="false" />
<myIsUseDefaultProxy>false</myIsUseDefaultProxy> <myIsUseDefaultProxy>false</myIsUseDefaultProxy>
</component> </component>
<component name="TaskManager"> <component name="TaskManager">
<task active="true" id="Default" summary="Default task"> <task active="true" id="Default" summary="Default task">
<changelist id="f90ee5b5-97e4-47ec-9b14-d4f4e896f100" name="Default" comment="" /> <changelist id="f90ee5b5-97e4-47ec-9b14-d4f4e896f100" name="Default" comment="" />
<created>1350026709905</created> <created>1350026709905</created>
<updated>1350026709905</updated> <updated>1350026709905</updated>
</task> </task>
<servers /> <servers />
</component> </component>
<component name="ToolWindowManager"> <component name="ToolWindowManager">
<frame x="619" y="0" width="825" height="600" extended-state="0" /> <frame x="0" y="22" width="1680" height="937" extended-state="6" />
<editor active="false" /> <editor active="true" />
<layout> <layout>
<window_info id="Changes" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.33" sideWeight="0.5" order="7" side_tool="false" content_ui="tabs" /> <window_info id="Changes" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.33" sideWeight="0.5" order="7" side_tool="false" content_ui="tabs" />
  <window_info id="Palette" active="false" anchor="right" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.33" sideWeight="0.5" order="3" side_tool="false" content_ui="tabs" />
  <window_info id="Ant Build" active="false" anchor="right" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.25" sideWeight="0.5" order="1" side_tool="false" content_ui="tabs" />
  <window_info id="Debug" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.4" sideWeight="0.5" order="3" side_tool="false" content_ui="tabs" />
  <window_info id="Event Log" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.32925338" sideWeight="0.5" order="7" side_tool="true" content_ui="tabs" />
  <window_info id="Favorites" active="false" anchor="left" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.33" sideWeight="0.5" order="2" side_tool="true" content_ui="tabs" />
  <window_info id="Version Control" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.33" sideWeight="0.5" order="7" side_tool="false" content_ui="tabs" />
<window_info id="TODO" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.33" sideWeight="0.5" order="6" side_tool="false" content_ui="tabs" /> <window_info id="TODO" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.33" sideWeight="0.5" order="6" side_tool="false" content_ui="tabs" />
<window_info id="Database" active="false" anchor="right" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.33" sideWeight="0.5" order="3" side_tool="false" content_ui="tabs" />  
<window_info id="Structure" active="false" anchor="left" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.25" sideWeight="0.5" order="1" side_tool="true" content_ui="tabs" /> <window_info id="Structure" active="false" anchor="left" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.25" sideWeight="0.5" order="1" side_tool="true" content_ui="tabs" />
<window_info id="Project" active="false" anchor="left" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="true" weight="0.44036698" sideWeight="0.6706349" order="0" side_tool="false" content_ui="combo" /> <window_info id="Maven Projects" active="false" anchor="right" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.33" sideWeight="0.5" order="3" side_tool="false" content_ui="tabs" />
<window_info id="Debug" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.4" sideWeight="0.5" order="3" side_tool="false" content_ui="tabs" /> <window_info id="Commander" active="false" anchor="right" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.4" sideWeight="0.5" order="0" side_tool="false" content_ui="tabs" />
<window_info id="Favorites" active="false" anchor="left" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.33" sideWeight="0.5" order="2" side_tool="true" content_ui="tabs" /> <window_info id="Project" active="false" anchor="left" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="true" weight="0.19645043" sideWeight="0.6707466" order="0" side_tool="false" content_ui="combo" />
<window_info id="Event Log" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.32936507" sideWeight="0.5" order="7" side_tool="true" content_ui="tabs" />  
<window_info id="Run" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.33" sideWeight="0.5" order="2" side_tool="false" content_ui="tabs" /> <window_info id="Run" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.33" sideWeight="0.5" order="2" side_tool="false" content_ui="tabs" />
<window_info id="Version Control" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.33" sideWeight="0.5" order="7" side_tool="false" content_ui="tabs" />  
<window_info id="Cvs" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.25" sideWeight="0.5" order="4" side_tool="false" content_ui="tabs" /> <window_info id="Cvs" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.25" sideWeight="0.5" order="4" side_tool="false" content_ui="tabs" />
<window_info id="Message" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.33" sideWeight="0.5" order="0" side_tool="false" content_ui="tabs" /> <window_info id="Message" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.33" sideWeight="0.5" order="0" side_tool="false" content_ui="tabs" />
<window_info id="Ant Build" active="false" anchor="right" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.25" sideWeight="0.5" order="1" side_tool="false" content_ui="tabs" /> <window_info id="Database" active="false" anchor="right" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.33" sideWeight="0.5" order="3" side_tool="false" content_ui="tabs" />
<window_info id="Find" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.33" sideWeight="0.5" order="1" side_tool="false" content_ui="tabs" /> <window_info id="Find" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.33" sideWeight="0.5" order="1" side_tool="false" content_ui="tabs" />
<window_info id="Commander" active="false" anchor="right" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.4" sideWeight="0.5" order="0" side_tool="false" content_ui="tabs" />  
<window_info id="Hierarchy" active="false" anchor="right" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.25" sideWeight="0.5" order="2" side_tool="false" content_ui="combo" /> <window_info id="Hierarchy" active="false" anchor="right" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.25" sideWeight="0.5" order="2" side_tool="false" content_ui="combo" />
<window_info id="Inspection" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.4" sideWeight="0.5" order="5" side_tool="false" content_ui="tabs" /> <window_info id="Inspection" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.4" sideWeight="0.5" order="5" side_tool="false" content_ui="tabs" />
</layout> </layout>
</component> </component>
<component name="VcsContentAnnotationSettings"> <component name="VcsContentAnnotationSettings">
<option name="myLimit" value="2678400000" /> <option name="myLimit" value="2678400000" />
</component> </component>
<component name="VcsManagerConfiguration"> <component name="VcsManagerConfiguration">
<option name="OFFER_MOVE_TO_ANOTHER_CHANGELIST_ON_PARTIAL_COMMIT" value="true" /> <option name="OFFER_MOVE_TO_ANOTHER_CHANGELIST_ON_PARTIAL_COMMIT" value="true" />
<option name="CHECK_CODE_SMELLS_BEFORE_PROJECT_COMMIT" value="true" /> <option name="CHECK_CODE_SMELLS_BEFORE_PROJECT_COMMIT" value="true" />
<option name="CHECK_NEW_TODO" value="true" /> <option name="CHECK_NEW_TODO" value="true" />
<option name="myTodoPanelSettings"> <option name="myTodoPanelSettings">
<value> <value>
<are-packages-shown value="false" /> <are-packages-shown value="false" />
<are-modules-shown value="false" /> <are-modules-shown value="false" />
<flatten-packages value="false" /> <flatten-packages value="false" />
<is-autoscroll-to-source value="false" /> <is-autoscroll-to-source value="false" />
</value> </value>
</option> </option>
<option name="PERFORM_UPDATE_IN_BACKGROUND" value="true" /> <option name="PERFORM_UPDATE_IN_BACKGROUND" value="true" />
<option name="PERFORM_COMMIT_IN_BACKGROUND" value="true" /> <option name="PERFORM_COMMIT_IN_BACKGROUND" value="true" />
<option name="PERFORM_EDIT_IN_BACKGROUND" value="true" /> <option name="PERFORM_EDIT_IN_BACKGROUND" value="true" />
<option name="PERFORM_CHECKOUT_IN_BACKGROUND" value="true" /> <option name="PERFORM_CHECKOUT_IN_BACKGROUND" value="true" />
<option name="PERFORM_ADD_REMOVE_IN_BACKGROUND" value="true" /> <option name="PERFORM_ADD_REMOVE_IN_BACKGROUND" value="true" />
<option name="PERFORM_ROLLBACK_IN_BACKGROUND" value="false" /> <option name="PERFORM_ROLLBACK_IN_BACKGROUND" value="false" />
<option name="CHECK_LOCALLY_CHANGED_CONFLICTS_IN_BACKGROUND" value="false" /> <option name="CHECK_LOCALLY_CHANGED_CONFLICTS_IN_BACKGROUND" value="false" />
<option name="CHANGED_ON_SERVER_INTERVAL" value="60" /> <option name="CHANGED_ON_SERVER_INTERVAL" value="60" />
<option name="SHOW_ONLY_CHANGED_IN_SELECTION_DIFF" value="true" /> <option name="SHOW_ONLY_CHANGED_IN_SELECTION_DIFF" value="true" />
<option name="CHECK_COMMIT_MESSAGE_SPELLING" value="true" /> <option name="CHECK_COMMIT_MESSAGE_SPELLING" value="true" />
<option name="DEFAULT_PATCH_EXTENSION" value="patch" /> <option name="DEFAULT_PATCH_EXTENSION" value="patch" />
<option name="SHORT_DIFF_HORISONTALLY" value="true" /> <option name="SHORT_DIFF_HORISONTALLY" value="true" />
<option name="SHORT_DIFF_EXTRA_LINES" value="2" /> <option name="SHORT_DIFF_EXTRA_LINES" value="2" />
<option name="SOFT_WRAPS_IN_SHORT_DIFF" value="true" /> <option name="SOFT_WRAPS_IN_SHORT_DIFF" value="true" />
<option name="INCLUDE_TEXT_INTO_PATCH" value="false" /> <option name="INCLUDE_TEXT_INTO_PATCH" value="false" />
<option name="INCLUDE_TEXT_INTO_SHELF" value="false" /> <option name="INCLUDE_TEXT_INTO_SHELF" value="false" />
<option name="SHOW_FILE_HISTORY_DETAILS" value="true" /> <option name="SHOW_FILE_HISTORY_DETAILS" value="true" />
<option name="SHOW_VCS_ERROR_NOTIFICATIONS" value="true" /> <option name="SHOW_VCS_ERROR_NOTIFICATIONS" value="true" />
  <option name="SHOW_DIRTY_RECURSIVELY" value="false" />
  <option name="LIMIT_HISTORY" value="true" />
  <option name="MAXIMUM_HISTORY_ROWS" value="1000" />
<option name="FORCE_NON_EMPTY_COMMENT" value="false" /> <option name="FORCE_NON_EMPTY_COMMENT" value="false" />
<option name="CLEAR_INITIAL_COMMIT_MESSAGE" value="false" /> <option name="CLEAR_INITIAL_COMMIT_MESSAGE" value="false" />
<option name="LAST_COMMIT_MESSAGE" /> <option name="LAST_COMMIT_MESSAGE" />
<option name="MAKE_NEW_CHANGELIST_ACTIVE" value="false" /> <option name="MAKE_NEW_CHANGELIST_ACTIVE" value="false" />
<option name="OPTIMIZE_IMPORTS_BEFORE_PROJECT_COMMIT" value="false" /> <option name="OPTIMIZE_IMPORTS_BEFORE_PROJECT_COMMIT" value="false" />
<option name="CHECK_FILES_UP_TO_DATE_BEFORE_COMMIT" value="false" /> <option name="CHECK_FILES_UP_TO_DATE_BEFORE_COMMIT" value="false" />
<option name="REFORMAT_BEFORE_PROJECT_COMMIT" value="false" /> <option name="REFORMAT_BEFORE_PROJECT_COMMIT" value="false" />
<option name="REFORMAT_BEFORE_FILE_COMMIT" value="false" /> <option name="REFORMAT_BEFORE_FILE_COMMIT" value="false" />
<option name="FILE_HISTORY_DIALOG_COMMENTS_SPLITTER_PROPORTION" value="0.8" /> <option name="FILE_HISTORY_DIALOG_COMMENTS_SPLITTER_PROPORTION" value="0.8" />
<option name="FILE_HISTORY_DIALOG_SPLITTER_PROPORTION" value="0.5" /> <option name="FILE_HISTORY_DIALOG_SPLITTER_PROPORTION" value="0.5" />
<option name="ACTIVE_VCS_NAME" /> <option name="ACTIVE_VCS_NAME" />
<option name="UPDATE_GROUP_BY_PACKAGES" value="false" /> <option name="UPDATE_GROUP_BY_PACKAGES" value="false" />
<option name="UPDATE_GROUP_BY_CHANGELIST" value="false" /> <option name="UPDATE_GROUP_BY_CHANGELIST" value="false" />
<option name="SHOW_FILE_HISTORY_AS_TREE" value="false" /> <option name="SHOW_FILE_HISTORY_AS_TREE" value="false" />
<option name="FILE_HISTORY_SPLITTER_PROPORTION" value="0.6" /> <option name="FILE_HISTORY_SPLITTER_PROPORTION" value="0.6" />
</component> </component>
<component name="XDebuggerManager"> <component name="XDebuggerManager">
<breakpoint-manager /> <breakpoint-manager />
</component> </component>
  <component name="antWorkspaceConfiguration">
  <option name="IS_AUTOSCROLL_TO_SOURCE" value="false" />
  <option name="FILTER_TARGETS" value="false" />
  </component>
<component name="editorHistoryManager"> <component name="editorHistoryManager">
<entry file="file://$PROJECT_DIR$/viewcalls.php"> <entry file="file://$PROJECT_DIR$/calllog.php">
<provider selected="true" editor-type-id="text-editor"> <provider selected="true" editor-type-id="text-editor">
<state line="2" column="13" selection-start="46" selection-end="46" vertical-scroll-proportion="0.0"> <state line="9" column="0" selection-start="189" selection-end="189" vertical-scroll-proportion="0.0">
<folding>  
<element signature="e#255#287#0" expanded="true" />  
</folding>  
</state>  
</provider>  
</entry>  
<entry file="file://$PROJECT_DIR$/common.inc.php">  
<provider selected="true" editor-type-id="text-editor">  
<state line="34" column="0" selection-start="1179" selection-end="1179" vertical-scroll-proportion="0.0">  
<folding /> <folding />
</state> </state>
</provider> </provider>
</entry> </entry>
<entry file="file://$PROJECT_DIR$/calls.json.php"> <entry file="file://$PROJECT_DIR$/scannr.py">
<provider selected="true" editor-type-id="text-editor"> <provider selected="true" editor-type-id="text-editor">
<state line="2" column="9" selection-start="42" selection-end="42" vertical-scroll-proportion="0.0"> <state line="12" column="21" selection-start="328" selection-end="328" vertical-scroll-proportion="0.0">
<folding /> <folding />
</state> </state>
</provider> </provider>
</entry> </entry>
<entry file="file://$PROJECT_DIR$/trunklog.php"> <entry file="file://$PROJECT_DIR$/calllog.php">
<provider selected="true" editor-type-id="text-editor"> <provider selected="true" editor-type-id="text-editor">
<state line="0" column="0" selection-start="0" selection-end="0" vertical-scroll-proportion="0.0" /> <state line="0" column="0" selection-start="0" selection-end="0" vertical-scroll-proportion="0.0">
</provider>  
</entry>  
<entry file="file://$PROJECT_DIR$/../disclosr/include/template.inc.php">  
<provider selected="true" editor-type-id="text-editor">  
<state line="72" column="93" selection-start="2545" selection-end="2635" vertical-scroll-proportion="0.43404254" />  
</provider>  
</entry>  
<entry file="file://$PROJECT_DIR$/../disclosr/include/common.inc.php">  
<provider selected="true" editor-type-id="text-editor">  
<state line="0" column="0" selection-start="0" selection-end="0" vertical-scroll-proportion="0.0" />  
</provider>  
</entry>  
<entry file="file://C:/tmp/h5bp-html5-boilerplate-2279296/index.html">  
<provider selected="true" editor-type-id="text-editor">  
<state line="38" column="11" selection-start="1245" selection-end="1982" vertical-scroll-proportion="0.93352604" />  
</provider>  
</entry>  
<entry file="file://$PROJECT_DIR$/../busui/myway/myway_timeliness.php">  
<provider selected="true" editor-type-id="text-editor">  
<state line="27" column="5" selection-start="1003" selection-end="1018" vertical-scroll-proportion="26.346153" />  
</provider>  
</entry>  
<entry file="file://$PROJECT_DIR$/scannr.py">  
<provider selected="true" editor-type-id="text-editor">  
<state line="66" column="23" selection-start="2084" selection-end="2084" vertical-scroll-proportion="0.0" />  
</provider>  
</entry>  
<entry file="file://$PROJECT_DIR$/snd.py">  
<provider selected="true" editor-type-id="text-editor">  
<state line="0" column="0" selection-start="0" selection-end="0" vertical-scroll-proportion="0.0" />  
</provider>  
</entry>  
<entry file="file://$PROJECT_DIR$/common.inc.php">  
<provider selected="true" editor-type-id="text-editor">  
<state line="60" column="117" selection-start="2262" selection-end="2262" vertical-scroll-proportion="-39.23077">  
<folding /> <folding />
</state> </state>
</provider> </provider>
</entry> </entry>
  <entry file="file://$PROJECT_DIR$/viewcalls.php">
  <provider selected="true" editor-type-id="text-editor">
  <state line="2" column="13" selection-start="46" selection-end="46" vertical-scroll-proportion="0.0" />
  </provider>
  </entry>
  <entry file="file://$PROJECT_DIR$/common.inc.php">
  <provider selected="true" editor-type-id="text-editor">
  <state line="34" column="0" selection-start="1179" selection-end="1179" vertical-scroll-proportion="0.0" />
  </provider>
  </entry>
<entry file="file://$PROJECT_DIR$/calls.json.php"> <entry file="file://$PROJECT_DIR$/calls.json.php">
<provider selected="true" editor-type-id="text-editor"> <provider selected="true" editor-type-id="text-editor">
<state line="72" column="41" selection-start="2843" selection-end="2843" vertical-scroll-proportion="-43.153847"> <state line="2" column="9" selection-start="42" selection-end="42" vertical-scroll-proportion="0.0" />
  </provider>
  </entry>
  <entry file="file://$PROJECT_DIR$/trunklog.php">
  <provider selected="true" editor-type-id="text-editor">
  <state line="0" column="0" selection-start="0" selection-end="0" vertical-scroll-proportion="0.0" />
  </provider>
  </entry>
  <entry file="file://$PROJECT_DIR$/../disclosr/include/template.inc.php">
  <provider selected="true" editor-type-id="text-editor">
  <state line="72" column="93" selection-start="2545" selection-end="2635" vertical-scroll-proportion="0.43404254" />
  </provider>
  </entry>
  <entry file="file://$PROJECT_DIR$/../disclosr/include/common.inc.php">
  <provider selected="true" editor-type-id="text-editor">
  <state line="0" column="0" selection-start="0" selection-end="0" vertical-scroll-proportion="0.0" />
  </provider>
  </entry>
  <entry file="file://$PROJECT_DIR$/../busui/myway/myway_timeliness.php">
  <provider selected="true" editor-type-id="text-editor">
  <state line="27" column="5" selection-start="1003" selection-end="1018" vertical-scroll-proportion="26.346153" />
  </provider>
  </entry>
  <entry file="file://$PROJECT_DIR$/snd.py">
  <provider selected="true" editor-type-id="text-editor">
  <state line="0" column="0" selection-start="0" selection-end="0" vertical-scroll-proportion="0.0" />
  </provider>
  </entry>
  <entry file="file://$PROJECT_DIR$/common.inc.php">
  <provider selected="true" editor-type-id="text-editor">
  <state line="60" column="117" selection-start="2262" selection-end="2262" vertical-scroll-proportion="-39.23077" />
  </provider>
  </entry>
  <entry file="file://$PROJECT_DIR$/calls.json.php">
  <provider selected="true" editor-type-id="text-editor">
  <state line="72" column="41" selection-start="2843" selection-end="2843" vertical-scroll-proportion="-43.153847" />
  </provider>
  </entry>
  <entry file="file://$PROJECT_DIR$/viewcalls.php">
  <provider selected="true" editor-type-id="text-editor">
  <state line="41" column="69" selection-start="1388" selection-end="1388" vertical-scroll-proportion="1.2659575" />
  </provider>
  </entry>
  <entry file="file://$PROJECT_DIR$/calllog.php">
  <provider selected="true" editor-type-id="text-editor">
  <state line="9" column="0" selection-start="189" selection-end="189" vertical-scroll-proportion="0.0">
<folding /> <folding />
</state> </state>
</provider> </provider>
</entry> </entry>
<entry file="file://$PROJECT_DIR$/viewcalls.php"> <entry file="file://$PROJECT_DIR$/scannr.py">
<provider selected="true" editor-type-id="text-editor"> <provider selected="true" editor-type-id="text-editor">
<state line="41" column="69" selection-start="1388" selection-end="1388" vertical-scroll-proportion="1.2659575"> <state line="80" column="64" selection-start="2271" selection-end="2271" vertical-scroll-proportion="-0.31178707">
<folding> <folding />
<element signature="e#255#287#0" expanded="true" />  
</folding>  
</state> </state>
</provider> </provider>
</entry> </entry>
</component> </component>
</project> </project>
   
   
file:a/404.html -> file:b/404.html
<!DOCTYPE html> <!DOCTYPE html>
<html lang="en"> <html lang="en">
<head> <head>
<meta charset="utf-8"> <meta charset="utf-8">
<title>Page Not Found :(</title> <title>Page Not Found :(</title>
<style> <style>
::-moz-selection { ::-moz-selection {
background: #b3d4fc; background: #b3d4fc;
text-shadow: none; text-shadow: none;
} }
   
::selection { ::selection {
background: #b3d4fc; background: #b3d4fc;
text-shadow: none; text-shadow: none;
} }
   
html { html {
padding: 30px 10px; padding: 30px 10px;
font-size: 20px; font-size: 20px;
line-height: 1.4; line-height: 1.4;
color: #737373; color: #737373;
background: #f0f0f0; background: #f0f0f0;
-webkit-text-size-adjust: 100%; -webkit-text-size-adjust: 100%;
-ms-text-size-adjust: 100%; -ms-text-size-adjust: 100%;
} }
   
html, html,
input { input {
font-family: "Helvetica Neue", Helvetica, Arial, sans-serif; font-family: "Helvetica Neue", Helvetica, Arial, sans-serif;
} }
   
body { body {
max-width: 500px; max-width: 500px;
_width: 500px; _width: 500px;
padding: 30px 20px 50px; padding: 30px 20px 50px;
border: 1px solid #b3b3b3; border: 1px solid #b3b3b3;
border-radius: 4px; border-radius: 4px;
margin: 0 auto; margin: 0 auto;
box-shadow: 0 1px 10px #a7a7a7, inset 0 1px 0 #fff; box-shadow: 0 1px 10px #a7a7a7, inset 0 1px 0 #fff;
background: #fcfcfc; background: #fcfcfc;
} }
   
h1 { h1 {
margin: 0 10px; margin: 0 10px;
font-size: 50px; font-size: 50px;
text-align: center; text-align: center;
} }
   
h1 span { h1 span {
color: #bbb; color: #bbb;
} }
   
h3 { h3 {
margin: 1.5em 0 0.5em; margin: 1.5em 0 0.5em;
} }
   
p { p {
margin: 1em 0; margin: 1em 0;
} }
   
ul { ul {
padding: 0 0 0 40px; padding: 0 0 0 40px;
margin: 1em 0; margin: 1em 0;
} }
   
.container { .container {
max-width: 380px; max-width: 380px;
_width: 380px; _width: 380px;
margin: 0 auto; margin: 0 auto;
} }
   
/* google search */ /* google search */
   
#goog-fixurl ul { #goog-fixurl ul {
list-style: none; list-style: none;
padding: 0; padding: 0;
margin: 0; margin: 0;
} }
   
#goog-fixurl form { #goog-fixurl form {
margin: 0; margin: 0;
} }
   
#goog-wm-qt, #goog-wm-qt,
#goog-wm-sb { #goog-wm-sb {
border: 1px solid #bbb; border: 1px solid #bbb;
font-size: 16px; font-size: 16px;
line-height: normal; line-height: normal;
vertical-align: top; vertical-align: top;
color: #444; color: #444;
border-radius: 2px; border-radius: 2px;
} }
   
#goog-wm-qt { #goog-wm-qt {
width: 220px; width: 220px;
height: 20px; height: 20px;
padding: 5px; padding: 5px;
margin: 5px 10px 0 0; margin: 5px 10px 0 0;
box-shadow: inset 0 1px 1px #ccc; box-shadow: inset 0 1px 1px #ccc;
} }
   
#goog-wm-sb { #goog-wm-sb {
display: inline-block; display: inline-block;
height: 32px; height: 32px;
padding: 0 10px; padding: 0 10px;
margin: 5px 0 0; margin: 5px 0 0;
white-space: nowrap; white-space: nowrap;
cursor: pointer; cursor: pointer;
background-color: #f5f5f5; background-color: #f5f5f5;
background-image: -webkit-linear-gradient(rgba(255,255,255,0), #f1f1f1); background-image: -webkit-linear-gradient(rgba(255, 255, 255, 0), #f1f1f1);
background-image: -moz-linear-gradient(rgba(255,255,255,0), #f1f1f1); background-image: -moz-linear-gradient(rgba(255, 255, 255, 0), #f1f1f1);
background-image: -ms-linear-gradient(rgba(255,255,255,0), #f1f1f1); background-image: -ms-linear-gradient(rgba(255, 255, 255, 0), #f1f1f1);
background-image: -o-linear-gradient(rgba(255,255,255,0), #f1f1f1); background-image: -o-linear-gradient(rgba(255, 255, 255, 0), #f1f1f1);
-webkit-appearance: none; -webkit-appearance: none;
-moz-appearance: none; -moz-appearance: none;
appearance: none; appearance: none;
*overflow: visible; *overflow: visible;
*display: inline; *display: inline;
*zoom: 1; *zoom: 1;
} }
   
#goog-wm-sb:hover, #goog-wm-sb:hover,
#goog-wm-sb:focus { #goog-wm-sb:focus {
border-color: #aaa; border-color: #aaa;
box-shadow: 0 1px 1px rgba(0, 0, 0, 0.1); box-shadow: 0 1px 1px rgba(0, 0, 0, 0.1);
background-color: #f8f8f8; background-color: #f8f8f8;
} }
   
#goog-wm-qt:hover, #goog-wm-qt:hover,
#goog-wm-qt:focus { #goog-wm-qt:focus {
border-color: #105cb6; border-color: #105cb6;
outline: 0; outline: 0;
color: #222; color: #222;
} }
   
input::-moz-focus-inner { input::-moz-focus-inner {
padding: 0; padding: 0;
border: 0; border: 0;
} }
</style> </style>
</head> </head>
<body> <body>
<div class="container"> <div class="container">
<h1>Not found <span>:(</span></h1> <h1>Not found <span>:(</span></h1>
<p>Sorry, but the page you were trying to view does not exist.</p>  
<p>It looks like this was the result of either:</p> <p>Sorry, but the page you were trying to view does not exist.</p>
<ul>  
<li>a mistyped address</li> <p>It looks like this was the result of either:</p>
<li>an out-of-date link</li> <ul>
</ul> <li>a mistyped address</li>
<script> <li>an out-of-date link</li>
var GOOG_FIXURL_LANG = (navigator.language || '').slice(0,2),GOOG_FIXURL_SITE = location.host; </ul>
</script> <script>
<script src="http://linkhelp.clients.google.com/tbproxy/lh/wm/fixurl.js"></script> var GOOG_FIXURL_LANG = (navigator.language || '').slice(0, 2), GOOG_FIXURL_SITE = location.host;
</div> </script>
</body> <script src="http://linkhelp.clients.google.com/tbproxy/lh/wm/fixurl.js"></script>
  </div>
  </body>
</html> </html>
   
<?php <?php
include ('common.inc.php'); include ('common.inc.php');
$sth = $conn->prepare( 'select * from recordings $sth = $conn->prepare('select * from recordings
order by call_timestamp desc limit 10'); order by call_timestamp desc limit 1000');
   
$sth->execute(Array()); $sth->execute(Array());
   
$row = 0; $row = 0;
echo "<table>"; echo "<table>";
foreach ($sth->fetchAll() as $data) { foreach ($sth->fetchAll() as $data) {
   
   
echo "<tr>"; echo "<tr>";
for ($c=0; $c < count($data); $c++) { for ($c = 0; $c < count($data); $c++) {
echo '<td>'.$data[$c] . "</td>\n"; echo '<td>' . $data[$c] . "</td>\n";
} }
echo "</tr>"; echo "</tr>";
} }
$row++; $row++;
echo "</table>"; echo "</table>";
?>  
   
<?php <?php
include('common.inc.php'); include('common.inc.php');
function getTGIDValuesByHour($TGID, $timeFrom, $timeTo) { function getTGIDValuesByHour($TGID, $timeFrom, $timeTo)
  {
global $conn; global $conn;
$sth = $conn->prepare( 'select tgid, min(call_timestamp) as time, count(*), min(length), max(length), avg(length), stddev(length) from recordings $sth = $conn->prepare('select tgid, min(call_timestamp) as time, count(*), min(length), max(length), avg(length), stddev(length) from recordings
  where call_timestamp between to_timestamp(?) and to_timestamp(?)
group by tgid, date_trunc(\'hour\', call_timestamp) order by time'); group by tgid, date_trunc(\'hour\', call_timestamp) order by time');
   
$sth->execute( ); $sth->execute(Array($timeFrom, $timeTo));
//Array($TGID, $timeFrom, $timeTo) return $sth->fetchAll();
return $sth->fetchAll();  
   
   
} }
   
function getTGIDValuesByDay($TGID, $dayFrom, $dayTo) { function getTGIDValuesByDay($TGID, $dayFrom, $dayTo)
  {
global $conn; global $conn;
$sth = $conn->prepare('select min(time) as time, min(value), max(value), avg(value), stddev(value) from sensor_values where sensor_id = ? $sth = $conn->prepare('select min(time) as time, min(value), max(value), avg(value), stddev(value) from sensor_values where sensor_id = ?
group by sensor_id, date_trunc(\'day\', time) order by time'); group by sensor_id, date_trunc(\'day\', time) order by time');
   
$sth->execute( Array($TGID)); $sth->execute(Array($TGID));
return $sth->fetchAll(); return $sth->fetchAll();
} }
function getTGIDDataYears($TGID, $timeFrom, $timeTo) { function getTGIDDataYears($TGID, $timeFrom, $timeTo)
  {
global $conn; global $conn;
$sth = $conn->prepare("select distinct extract('year' from call_timestamp) as year from recordings where tgid = ? order by year"); $sth = $conn->prepare("select distinct extract('year' from call_timestamp) as year from recordings where tgid = ? order by year");
   
$sth->execute(Array($TGID)); $sth->execute(Array($TGID));
return $sth->fetchAll(); return $sth->fetchAll();
} }
   
function getTGIDDataMonths($TGID, $timeFrom, $timeTo) { function getTGIDDataMonths($TGID, $timeFrom, $timeTo)
  {
global $conn; global $conn;
$sth = $conn->prepare("select distinct extract('month' from call_timestamp) as month, extract('year' from call_timestamp) as year from recordings where tgid = ? order by year, month"); $sth = $conn->prepare("select distinct extract('month' from call_timestamp) as month, extract('year' from call_timestamp) as year from recordings where tgid = ? order by year, month");
   
$sth->execute(Array($TGID)); $sth->execute(Array($TGID));
return $sth->fetchAll(); return $sth->fetchAll();
} }
   
function getTGIDDataDays($TGID, $timeFrom, $timeTo) { function getTGIDDataDays($TGID, $timeFrom, $timeTo)
  {
global $conn; global $conn;
$sth = $conn->prepare("select distinct extract('day' from call_timestamp) as day, extract('month' from call_timestamp) as month, extract('year' from call_timestamp) as year from recordings where tgid = ? order by year,month,day"); $sth = $conn->prepare("select distinct extract('day' from call_timestamp) as day, extract('month' from call_timestamp) as month, extract('year' from call_timestamp) as year from recordings where tgid = ? order by year,month,day");
   
   
$sth->execute(Array($TGID)); $sth->execute(Array($TGID));
return $sth->fetchAll(); return $sth->fetchAll();
} }
$action = (isset($_REQUEST['action']) ? $_REQUEST['action'] : ''); $action = (isset($_REQUEST['action']) ? $_REQUEST['action'] : '');
$TGID = (isset($_REQUEST['tgid']) ? $_REQUEST['tgid'] : ''); $TGID = (isset($_REQUEST['tgid']) ? $_REQUEST['tgid'] : '');
$timefrom = (isset($_REQUEST['from']) ? $_REQUEST['from'] : ''); $timefrom = (isset($_REQUEST['from']) ? $_REQUEST['from'] : '');
$timeto = (isset($_REQUEST['to']) ? $_REQUEST['to'] : ''); $timeto = (isset($_REQUEST['to']) ? $_REQUEST['to'] : '');
   
if ($action == "data_description") { if ($action == "data_description") {
$timefrom = strtotime($timefrom); $timefrom = strtotime($timefrom);
$timeto = strtotime($timeto); $timeto = strtotime($timeto);
$years = getTGIDDataYears($TGID, $timefrom, $timeto); $years = getTGIDDataYears($TGID, $timefrom, $timeto);
   
$months = getTGIDDataMonths($TGID, $timefrom, $timeto); $months = getTGIDDataMonths($TGID, $timefrom, $timeto);
$days = getTGIDDataDays($TGID, $timefrom, $timeto); $days = getTGIDDataDays($TGID, $timefrom, $timeto);
   
echo json_encode(Array("years" => $years, "months" => $months, "days" => $days echo json_encode(Array("years" => $years, "months" => $months, "days" => $days
)); ));
} }
   
   
if (strpos($action,"graph") !== false) { if (strpos($action, "graph") !== false) {
$values = getTGIDValuesByHour($TGID, $timefrom, $timeto); $values = getTGIDValuesByHour($TGID, $timefrom, $timeto);
$label = $TGID; $label = $TGID;
$data = Array(); $data = Array();
$tzoffset = get_timezone_offset("UTC"); $tzoffset = get_timezone_offset("UTC");
foreach ($values as $value) { foreach ($values as $value) {
if ($action == "graphlength") { if ($action == "graphlength") {
$data[$value['tgid']][] = Array((strtotime($value['time']) + $tzoffset) * 1000, intval($value['avg'])); $data[$value['tgid']][] = Array((strtotime($value['time']) + $tzoffset) * 1000, intval($value['avg']));
} else if ($action == "graphcount") { } else if ($action == "graphcount") {
$data[$value['tgid']][] = Array((strtotime($value['time']) + $tzoffset) * 1000, intval($value['count'])); $data[$value['tgid']][] = Array((strtotime($value['time']) + $tzoffset) * 1000, intval($value['count']));
} }
} }
echo json_encode(Array("label" => $label, "data" => $data, echo json_encode(Array("label" => $label, "data" => $data,
"previous" => Array( "previous" => Array(
"from" => $timefrom - (24 * 60 * 60), "from" => $timefrom - (24 * 60 * 60),
"to" => $timefrom) "to" => $timefrom)
, ,
"next" => Array( "next" => Array(
"to" => $timeto + (24 * 60 * 60), "to" => $timeto + (24 * 60 * 60),
"from" => $timeto) "from" => $timeto)
) )
); );
} }
   
   
   
?> ?>
   
<?php <?php
date_default_timezone_set("Australia/Sydney"); date_default_timezone_set("Australia/Sydney");
try { try {
$conn = new PDO("pgsql:dbname=scannr;user=postgres;password=snmc;host=localhost"); $conn = new PDO("pgsql:dbname=scannr;user=postgres;password=snmc;host=localhost");
$conn->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); $conn->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
} } catch (PDOException $e) {
catch(PDOException $e) {  
die('Unable to connect to database server.'); die('Unable to connect to database server.');
} }
catch(Exception $e) { catch (Exception $e) {
die('Unknown error in ' . __FILE__ . '.'); die('Unknown error in ' . __FILE__ . '.');
} }
$conn->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_WARNING); $conn->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_WARNING);
$basePath = ""; $basePath = "";
$DATA_DIR = "./data"; $DATA_DIR = "./data";
   
/** Returns the offset from the origin timezone to the remote timezone, in seconds. /** Returns the offset from the origin timezone to the remote timezone, in seconds.
* @param $remote_tz; * @param $remote_tz;
* @param $origin_tz; If null the servers current timezone is used as the origin. * @param $origin_tz; If null the servers current timezone is used as the origin.
* @return int; * @return int;
*/ */
function get_timezone_offset($remote_tz, $origin_tz = null) { function get_timezone_offset($remote_tz, $origin_tz = null)
  {
if ($origin_tz === null) { if ($origin_tz === null) {
if (!is_string($origin_tz = date_default_timezone_get())) { if (!is_string($origin_tz = date_default_timezone_get())) {
return false; // A UTC timestamp was returned -- bail out! return false; // A UTC timestamp was returned -- bail out!
} }
} }
$origin_dtz = new DateTimeZone($origin_tz); $origin_dtz = new DateTimeZone($origin_tz);
$remote_dtz = new DateTimeZone($remote_tz); $remote_dtz = new DateTimeZone($remote_tz);
$origin_dt = new DateTime("now", $origin_dtz); $origin_dt = new DateTime("now", $origin_dtz);
$remote_dt = new DateTime("now", $remote_dtz); $remote_dt = new DateTime("now", $remote_dtz);
$offset = $origin_dtz->getOffset($origin_dt) - $remote_dtz->getOffset($remote_dt); $offset = $origin_dtz->getOffset($origin_dt) - $remote_dtz->getOffset($remote_dt);
return $offset; return $offset;
} }
   
   
  function include_header($title)
  {
  global $basePath;
  ?>
  <!DOCTYPE html>
  <!--[if lt IE 7]>
  <html class="no-js lt-ie9 lt-ie8 lt-ie7"> <![endif]-->
  <!--[if IE 7]>
  <html class="no-js lt-ie9 lt-ie8"> <![endif]-->
  <!--[if IE 8]>
  <html class="no-js lt-ie9"> <![endif]-->
  <!--[if gt IE 8]><!--> <html class="no-js"> <!--<![endif]-->
  <head>
  <meta charset="utf-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
  <title></title>
  <meta name="description" content="">
  <meta name="viewport" content="width=device-width">
   
  <!-- Place favicon.ico and apple-touch-icon.png in the root directory -->
   
  <link rel="stylesheet" href="css/normalize.css">
  <link rel="stylesheet" href="css/main.css">
  <script src="js/vendor/modernizr-2.6.1.min.js"></script>
  <script src="//ajax.googleapis.com/ajax/libs/jquery/1.8.0/jquery.min.js"></script>
  <!--<script>window.jQuery || document.write('<script src="js/vendor/jquery-1.8.0.min.js"><\/script>')</script>-->
  <script type="text/javascript" src="<?php echo $basePath ?>js/flotr2/flotr2.js"></script>
  <script src="js/plugins.js"></script>
  <script src="js/main.js"></script>
  </head>
  <body>
  <!--[if lt IE 7]>
  <p class="chromeframe">You are using an outdated browser. <a href="http://browsehappy.com/">Upgrade your browser
  today</a> or <a href="http://www.google.com/chromeframe/?redirect=true">install Google Chrome Frame</a> to
  better experience this site.</p>
  <![endif]-->
   
  <!-- Add your site or application content here -->
  <?php
  }
   
  function include_footer()
  {
  global $basePath;
  ?>
   
   
  <!-- Google Analytics: change UA-XXXXX-X to be your site's ID. -->
  <script>
  var _gaq = [
  ['_setAccount', 'UA-XXXXX-X'],
  ['_trackPageview']
  ];
  (function (d, t) {
  var g = d.createElement(t), s = d.getElementsByTagName(t)[0];
  g.src = ('https:' == location.protocol ? '//ssl' : '//www') + '.google-analytics.com/ga.js';
  s.parentNode.insertBefore(g, s)
  }(document, 'script'));
  </script>
  </body>
  </html>
   
function include_header($title) { <?php
global $basePath;  
?>  
<!DOCTYPE html>  
<!--[if lt IE 7]> <html class="no-js lt-ie9 lt-ie8 lt-ie7"> <![endif]-->  
<!--[if IE 7]> <html class="no-js lt-ie9 lt-ie8"> <![endif]-->  
<!--[if IE 8]> <html class="no-js lt-ie9"> <![endif]-->  
<!--[if gt IE 8]><!--> <html class="no-js"> <!--<![endif]-->  
<head>  
<meta charset="utf-8">  
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">  
<title></title>  
<meta name="description" content="">  
<meta name="viewport" content="width=device-width">  
   
<!-- Place favicon.ico and apple-touch-icon.png in the root directory -->  
   
<link rel="stylesheet" href="css/normalize.css">  
<link rel="stylesheet" href="css/main.css">  
<script src="js/vendor/modernizr-2.6.1.min.js"></script>  
<script src="//ajax.googleapis.com/ajax/libs/jquery/1.8.0/jquery.min.js"></script>  
<!--<script>window.jQuery || document.write('<script src="js/vendor/jquery-1.8.0.min.js"><\/script>')</script>-->  
<script type="text/javascript" src="<?php echo $basePath ?>js/flotr2/flotr2.js"></script>  
<script src="js/plugins.js"></script>  
<script src="js/main.js"></script>  
</head>  
<body>  
<!--[if lt IE 7]>  
<p class="chromeframe">You are using an outdated browser. <a href="http://browsehappy.com/">Upgrade your browser today</a> or <a href="http://www.google.com/chromeframe/?redirect=true">install Google Chrome Frame</a> to better experience this site.</p>  
<![endif]-->  
   
<!-- Add your site or application content here -->  
<?php  
}  
   
function include_footer() {  
global $basePath;  
?>  
   
   
<!-- Google Analytics: change UA-XXXXX-X to be your site's ID. -->  
<script>  
var _gaq=[['_setAccount','UA-XXXXX-X'],['_trackPageview']];  
(function(d,t){var g=d.createElement(t),s=d.getElementsByTagName(t)[0];  
g.src=('https:'==location.protocol?'//ssl':'//www')+'.google-analytics.com/ga.js';  
s.parentNode.insertBefore(g,s)}(document,'script'));  
</script>  
</body>  
</html>  
   
<?php  
   
} }
   
   
<?php <?php
include('common.inc.php'); include('common.inc.php');
$sth = $conn->prepare( 'select * from recordings limit 100;'); $sth = $conn->prepare('select * from recordings limit 100;');
   
$sth->execute( ); $sth->execute();
$recordings = $sth->fetchAll(); $recordings = $sth->fetchAll();
$convos = Array(); $convos = Array();
$convo = Array(); $convo = Array();
foreach ($recordings as $i => $recording) { foreach ($recordings as $i => $recording) {
   
if (count($convo) > 0 && strcasecmp($convo[count($convos)]['tgid'], $recording['tgid']) != 0) { if (count($convo) > 0) {
$convos[] = $convo; echo "<br> " . strcasecmp($convos[count($convos) - 1][0]['call_timestamp'], $recording['call_timestamp']);
$convo = Array(); if (abs(strcasecmp($convos[count($convos) - 1][0]['call_timestamp'], $recording['call_timestamp'])) > 2) {
} ; echo " " . $convos[count($convos) - 1][0]['call_timestamp'] . " " . $recording['call_timestamp'];
//print_r($recording); }
$convo[] = $recording; if (strcasecmp($convos[count($convos) - 1][0]['tgid'], $recording['tgid']) != 0) {
//print_r($convo); $convos[] = $convo;
echo "<br>\n"; $convo = Array();
  }
  }
  ;
  //print_r($recording);
  $convo[] = $recording;
  //print_r($convo);
  //echo "<br>\n";
} }
foreach ($convos as $i => $convo) { foreach ($convos as $i => $convo) {
print_r($convo); foreach ($convo as $recording) {
echo "<br>\n"; echo $recording['filename'] . " , ";
  }
  echo "<br><hr>\n";
} }
?> ?>
   
   
file:b/getfile.php (new)
  <?php
  $reqfile = "path/to/file.3gp";
  $contenttype = "audio/3gpp";
 
  if ($fn = fopen($reqfile, "rba")) {
  header("Content-Type: " . $contenttype);
  header("Content-Length: " . filesize($reqfile));
  header("Last-Modified: " . gmdate("D, d M Y H:i:s") . " GMT");
  header("Pragma: no-cache");
  header("Expires: Mon, 26 Jul 1997 06:00:00 GMT");
  header("Cache-Control: no-store, no-cache, must-revalidate, max-age=0, post-check=0, pre-check=0");
  passthru("ffmpeg -i 2012-09-29-1348911268.34-demo.wav -ar 8000 -ab 4.75k -");
  fpassthru($fn);
  fclose($fn);
  } else {
  exit("error....");
  }
  exit();
  ?>
 
jasmine.HtmlReporterHelpers = {}; jasmine.HtmlReporterHelpers = {};
   
jasmine.HtmlReporterHelpers.createDom = function(type, attrs, childrenVarArgs) { jasmine.HtmlReporterHelpers.createDom = function (type, attrs, childrenVarArgs) {
var el = document.createElement(type); var el = document.createElement(type);
   
for (var i = 2; i < arguments.length; i++) { for (var i = 2; i < arguments.length; i++) {
var child = arguments[i]; var child = arguments[i];
   
if (typeof child === 'string') { if (typeof child === 'string') {
el.appendChild(document.createTextNode(child)); el.appendChild(document.createTextNode(child));
} else { } else {
if (child) { if (child) {
el.appendChild(child); el.appendChild(child);
} }
} }
} }
   
for (var attr in attrs) { for (var attr in attrs) {
if (attr == "className") { if (attr == "className") {
el[attr] = attrs[attr]; el[attr] = attrs[attr];
} else { } else {
el.setAttribute(attr, attrs[attr]); el.setAttribute(attr, attrs[attr]);
} }
} }
   
return el; return el;
}; };
   
jasmine.HtmlReporterHelpers.getSpecStatus = function(child) { jasmine.HtmlReporterHelpers.getSpecStatus = function (child) {
var results = child.results(); var results = child.results();
var status = results.passed() ? 'passed' : 'failed'; var status = results.passed() ? 'passed' : 'failed';
if (results.skipped) { if (results.skipped) {
status = 'skipped'; status = 'skipped';
} }
   
return status; return status;
}; };
   
jasmine.HtmlReporterHelpers.appendToSummary = function(child, childElement) { jasmine.HtmlReporterHelpers.appendToSummary = function (child, childElement) {
var parentDiv = this.dom.summary; var parentDiv = this.dom.summary;
var parentSuite = (typeof child.parentSuite == 'undefined') ? 'suite' : 'parentSuite'; var parentSuite = (typeof child.parentSuite == 'undefined') ? 'suite' : 'parentSuite';
var parent = child[parentSuite]; var parent = child[parentSuite];
   
if (parent) { if (parent) {
if (typeof this.views.suites[parent.id] == 'undefined') { if (typeof this.views.suites[parent.id] == 'undefined') {
this.views.suites[parent.id] = new jasmine.HtmlReporter.SuiteView(parent, this.dom, this.views); this.views.suites[parent.id] = new jasmine.HtmlReporter.SuiteView(parent, this.dom, this.views);
} }
parentDiv = this.views.suites[parent.id].element; parentDiv = this.views.suites[parent.id].element;
} }
   
parentDiv.appendChild(childElement); parentDiv.appendChild(childElement);
}; };
   
   
jasmine.HtmlReporterHelpers.addHelpers = function(ctor) { jasmine.HtmlReporterHelpers.addHelpers = function (ctor) {
for(var fn in jasmine.HtmlReporterHelpers) { for (var fn in jasmine.HtmlReporterHelpers) {
ctor.prototype[fn] = jasmine.HtmlReporterHelpers[fn]; ctor.prototype[fn] = jasmine.HtmlReporterHelpers[fn];
} }
}; };
   
jasmine.HtmlReporter = function(_doc) { jasmine.HtmlReporter = function (_doc) {
var self = this; var self = this;
var doc = _doc || window.document; var doc = _doc || window.document;
   
var reporterView; var reporterView;
   
var dom = {}; var dom = {};
   
// Jasmine Reporter Public Interface // Jasmine Reporter Public Interface
self.logRunningSpecs = false; self.logRunningSpecs = false;
   
self.reportRunnerStarting = function(runner) { self.reportRunnerStarting = function (runner) {
var specs = runner.specs() || []; var specs = runner.specs() || [];
   
if (specs.length == 0) { if (specs.length == 0) {
return; return;
} }
   
createReporterDom(runner.env.versionString()); createReporterDom(runner.env.versionString());
doc.body.appendChild(dom.reporter); doc.body.appendChild(dom.reporter);
   
reporterView = new jasmine.HtmlReporter.ReporterView(dom); reporterView = new jasmine.HtmlReporter.ReporterView(dom);
reporterView.addSpecs(specs, self.specFilter); reporterView.addSpecs(specs, self.specFilter);
}; };
   
self.reportRunnerResults = function(runner) { self.reportRunnerResults = function (runner) {
reporterView && reporterView.complete(); reporterView && reporterView.complete();
}; };
   
self.reportSuiteResults = function(suite) { self.reportSuiteResults = function (suite) {
reporterView.suiteComplete(suite); reporterView.suiteComplete(suite);
}; };
   
self.reportSpecStarting = function(spec) { self.reportSpecStarting = function (spec) {
if (self.logRunningSpecs) { if (self.logRunningSpecs) {
self.log('>> Jasmine Running ' + spec.suite.description + ' ' + spec.description + '...'); self.log('>> Jasmine Running ' + spec.suite.description + ' ' + spec.description + '...');
} }
}; };
   
self.reportSpecResults = function(spec) { self.reportSpecResults = function (spec) {
reporterView.specComplete(spec); reporterView.specComplete(spec);
}; };
   
self.log = function() { self.log = function () {
  var console = jasmine.getGlobal().console;
  if (console && console.log) {
  if (console.log.apply) {
  console.log.apply(console, arguments);
  } else {
  console.log(arguments); // ie fix: console.log.apply doesn't exist on ie
  }
  }
  };
   
  self.specFilter = function (spec) {
  if (!focusedSpecName()) {
  return true;
  }
   
  return spec.getFullName().indexOf(focusedSpecName()) === 0;
  };
   
  return self;
   
  function focusedSpecName() {
  var specName;
   
  (function memoizeFocusedSpec() {
  if (specName) {
  return;
  }
   
  var paramMap = [];
  var params = doc.location.search.substring(1).split('&');
   
  for (var i = 0; i < params.length; i++) {
  var p = params[i].split('=');
  paramMap[decodeURIComponent(p[0])] = decodeURIComponent(p[1]);
  }
   
  specName = paramMap.spec;
  })();
   
  return specName;
  }
   
  function createReporterDom(version) {
  dom.reporter = self.createDom('div', { id: 'HTMLReporter', className: 'jasmine_reporter' },
  dom.banner = self.createDom('div', { className: 'banner' },
  self.createDom('span', { className: 'title' }, "Jasmine "),
  self.createDom('span', { className: 'version' }, version)),
   
  dom.symbolSummary = self.createDom('ul', {className: 'symbolSummary'}),
  dom.alert = self.createDom('div', {className: 'alert'}),
  dom.results = self.createDom('div', {className: 'results'},
  dom.summary = self.createDom('div', { className: 'summary' }),
  dom.details = self.createDom('div', { id: 'details' }))
  );
  }
  };
  jasmine.HtmlReporterHelpers.addHelpers(jasmine.HtmlReporter);
  jasmine.HtmlReporter.ReporterView = function (dom) {
  this.startedAt = new Date();
  this.runningSpecCount = 0;
  this.completeSpecCount = 0;
  this.passedCount = 0;
  this.failedCount = 0;
  this.skippedCount = 0;
   
  this.createResultsMenu = function () {
  this.resultsMenu = this.createDom('span', {className: 'resultsMenu bar'},
  this.summaryMenuItem = this.createDom('a', {className: 'summaryMenuItem', href: "#"}, '0 specs'),
  ' | ',
  this.detailsMenuItem = this.createDom('a', {className: 'detailsMenuItem', href: "#"}, '0 failing'));
   
  this.summaryMenuItem.onclick = function () {
  dom.reporter.className = dom.reporter.className.replace(/ showDetails/g, '');
  };
   
  this.detailsMenuItem.onclick = function () {
  showDetails();
  };
  };
   
  this.addSpecs = function (specs, specFilter) {
  this.totalSpecCount = specs.length;
   
  this.views = {
  specs: {},
  suites: {}
  };
   
  for (var i = 0; i < specs.length; i++) {
  var spec = specs[i];
  this.views.specs[spec.id] = new jasmine.HtmlReporter.SpecView(spec, dom, this.views);
  if (specFilter(spec)) {
  this.runningSpecCount++;
  }
  }
  };
   
  this.specComplete = function (spec) {
  this.completeSpecCount++;
   
  if (isUndefined(this.views.specs[spec.id])) {
  this.views.specs[spec.id] = new jasmine.HtmlReporter.SpecView(spec, dom);
  }
   
  var specView = this.views.specs[spec.id];
   
  switch (specView.status()) {
  case 'passed':
  this.passedCount++;
  break;
   
  case 'failed':
  this.failedCount++;
  break;
   
  case 'skipped':
  this.skippedCount++;
  break;
  }
   
  specView.refresh();
  this.refresh();
  };
   
  this.suiteComplete = function (suite) {
  var suiteView = this.views.suites[suite.id];
  if (isUndefined(suiteView)) {
  return;
  }
  suiteView.refresh();
  };
   
  this.refresh = function () {
   
  if (isUndefined(this.resultsMenu)) {
  this.createResultsMenu();
  }
   
  // currently running UI
  if (isUndefined(this.runningAlert)) {
  this.runningAlert = this.createDom('a', {href: "?", className: "runningAlert bar"});
  dom.alert.appendChild(this.runningAlert);
  }
  this.runningAlert.innerHTML = "Running " + this.completeSpecCount + " of " + specPluralizedFor(this.totalSpecCount);
   
  // skipped specs UI
  if (isUndefined(this.skippedAlert)) {
  this.skippedAlert = this.createDom('a', {href: "?", className: "skippedAlert bar"});
  }
   
  this.skippedAlert.innerHTML = "Skipping " + this.skippedCount + " of " + specPluralizedFor(this.totalSpecCount) + " - run all";
   
  if (this.skippedCount === 1 && isDefined(dom.alert)) {
  dom.alert.appendChild(this.skippedAlert);
  }
   
  // passing specs UI
  if (isUndefined(this.passedAlert)) {
  this.passedAlert = this.createDom('span', {href: "?", className: "passingAlert bar"});
  }
  this.passedAlert.innerHTML = "Passing " + specPluralizedFor(this.passedCount);
   
  // failing specs UI
  if (isUndefined(this.failedAlert)) {
  this.failedAlert = this.createDom('span', {href: "?", className: "failingAlert bar"});
  }
  this.failedAlert.innerHTML = "Failing " + specPluralizedFor(this.failedCount);
   
  if (this.failedCount === 1 && isDefined(dom.alert)) {
  dom.alert.appendChild(this.failedAlert);
  dom.alert.appendChild(this.resultsMenu);
  }
   
  // summary info
  this.summaryMenuItem.innerHTML = "" + specPluralizedFor(this.runningSpecCount);
  this.detailsMenuItem.innerHTML = "" + this.failedCount + " failing";
  };
   
  this.complete = function () {
  dom.alert.removeChild(this.runningAlert);
   
  this.skippedAlert.innerHTML = "Ran " + this.runningSpecCount + " of " + specPluralizedFor(this.totalSpecCount) + " - run all";
   
  if (this.failedCount === 0) {
  dom.alert.appendChild(this.createDom('span', {className: 'passingAlert bar'}, "Passing " + specPluralizedFor(this.passedCount)));
  } else {
  showDetails();
  }
   
  dom.banner.appendChild(this.createDom('span', {className: 'duration'}, "finished in " + ((new Date().getTime() - this.startedAt.getTime()) / 1000) + "s"));
  };
   
  return this;
   
  function showDetails() {
  if (dom.reporter.className.search(/showDetails/) === -1) {
  dom.reporter.className += " showDetails";
  }
  }
   
  function isUndefined(obj) {
  return typeof obj === 'undefined';
  }
   
  function isDefined(obj) {
  return !isUndefined(obj);
  }
   
  function specPluralizedFor(count) {
  var str = count + " spec";
  if (count > 1) {
  str += "s"
  }
  return str;
  }
   
  };
   
  jasmine.HtmlReporterHelpers.addHelpers(jasmine.HtmlReporter.ReporterView);
   
   
  jasmine.HtmlReporter.SpecView = function (spec, dom, views) {
  this.spec = spec;
  this.dom = dom;
  this.views = views;
   
  this.symbol = this.createDom('li', { className: 'pending' });
  this.dom.symbolSummary.appendChild(this.symbol);
   
  this.summary = this.createDom('div', { className: 'specSummary' },
  this.createDom('a', {
  className: 'description',
  href: '?spec=' + encodeURIComponent(this.spec.getFullName()),
  title: this.spec.getFullName()
  }, this.spec.description)
  );
   
  this.detail = this.createDom('div', { className: 'specDetail' },
  this.createDom('a', {
  className: 'description',
  href: '?spec=' + encodeURIComponent(this.spec.getFullName()),
  title: this.spec.getFullName()
  }, this.spec.getFullName())
  );
  };
   
  jasmine.HtmlReporter.SpecView.prototype.status = function () {
  return this.getSpecStatus(this.spec);
  };
   
  jasmine.HtmlReporter.SpecView.prototype.refresh = function () {
  this.symbol.className = this.status();
   
  switch (this.status()) {
  case 'skipped':
  break;
   
  case 'passed':
  this.appendSummaryToSuiteDiv();
  break;
   
  case 'failed':
  this.appendSummaryToSuiteDiv();
  this.appendFailureDetail();
  break;
  }
  };
   
  jasmine.HtmlReporter.SpecView.prototype.appendSummaryToSuiteDiv = function () {
  this.summary.className += ' ' + this.status();
  this.appendToSummary(this.spec, this.summary);
  };
   
  jasmine.HtmlReporter.SpecView.prototype.appendFailureDetail = function () {
  this.detail.className += ' ' + this.status();
   
  var resultItems = this.spec.results().getItems();
  var messagesDiv = this.createDom('div', { className: 'messages' });
   
  for (var i = 0; i < resultItems.length; i++) {
  var result = resultItems[i];
   
  if (result.type == 'log') {
  messagesDiv.appendChild(this.createDom('div', {className: 'resultMessage log'}, result.toString()));
  } else if (result.type == 'expect' && result.passed && !result.passed()) {
  messagesDiv.appendChild(this.createDom('div', {className: 'resultMessage fail'}, result.message));
   
  if (result.trace.stack) {
  messagesDiv.appendChild(this.createDom('div', {className: 'stackTrace'}, result.trace.stack));
  }
  }
  }
   
  if (messagesDiv.childNodes.length > 0) {
  this.detail.appendChild(messagesDiv);
  this.dom.details.appendChild(this.detail);
  }
  };
   
  jasmine.HtmlReporterHelpers.addHelpers(jasmine.HtmlReporter.SpecView);
  jasmine.HtmlReporter.SuiteView = function (suite, dom, views) {
  this.suite = suite;
  this.dom = dom;
  this.views = views;
   
  this.element = this.createDom('div', { className: 'suite' },
  this.createDom('a', { className: 'description', href: '?spec=' + encodeURIComponent(this.suite.getFullName()) }, this.suite.description)
  );
   
  this.appendToSummary(this.suite, this.element);
  };
   
  jasmine.HtmlReporter.SuiteView.prototype.status = function () {
  return this.getSpecStatus(this.suite);
  };
   
  jasmine.HtmlReporter.SuiteView.prototype.refresh = function () {
  this.element.className += " " + this.status();
  };
   
  jasmine.HtmlReporterHelpers.addHelpers(jasmine.HtmlReporter.SuiteView);
   
  /* @deprecated Use jasmine.HtmlReporter instead
  */
  jasmine.TrivialReporter = function (doc) {
  this.document = doc || document;
  this.suiteDivs = {};
  this.logRunningSpecs = false;
  };
   
  jasmine.TrivialReporter.prototype.createDom = function (type, attrs, childrenVarArgs) {
  var el = document.createElement(type);
   
  for (var i = 2; i < arguments.length; i++) {
  var child = arguments[i];
   
  if (typeof child === 'string') {
  el.appendChild(document.createTextNode(child));
  } else {
  if (child) {
  el.appendChild(child);
  }
  }
  }
   
  for (var attr in attrs) {
  if (attr == "className") {
  el[attr] = attrs[attr];
  } else {
  el.setAttribute(attr, attrs[attr]);
  }
  }
   
  return el;
  };
   
  jasmine.TrivialReporter.prototype.reportRunnerStarting = function (runner) {
  var showPassed, showSkipped;
   
  this.outerDiv = this.createDom('div', { id: 'TrivialReporter', className: 'jasmine_reporter' },
  this.createDom('div', { className: 'banner' },
  this.createDom('div', { className: 'logo' },
  this.createDom('span', { className: 'title' }, "Jasmine"),
  this.createDom('span', { className: 'version' }, runner.env.versionString())),
  this.createDom('div', { className: 'options' },
  "Show ",
  showPassed = this.createDom('input', { id: "__jasmine_TrivialReporter_showPassed__", type: 'checkbox' }),
  this.createDom('label', { "for": "__jasmine_TrivialReporter_showPassed__" }, " passed "),
  showSkipped = this.createDom('input', { id: "__jasmine_TrivialReporter_showSkipped__", type: 'checkbox' }),
  this.createDom('label', { "for": "__jasmine_TrivialReporter_showSkipped__" }, " skipped")
  )
  ),
   
  this.runnerDiv = this.createDom('div', { className: 'runner running' },
  this.createDom('a', { className: 'run_spec', href: '?' }, "run all"),
  this.runnerMessageSpan = this.createDom('span', {}, "Running..."),
  this.finishedAtSpan = this.createDom('span', { className: 'finished-at' }, ""))
  );
   
  this.document.body.appendChild(this.outerDiv);
   
  var suites = runner.suites();
  for (var i = 0; i < suites.length; i++) {
  var suite = suites[i];
  var suiteDiv = this.createDom('div', { className: 'suite' },
  this.createDom('a', { className: 'run_spec', href: '?spec=' + encodeURIComponent(suite.getFullName()) }, "run"),
  this.createDom('a', { className: 'description', href: '?spec=' + encodeURIComponent(suite.getFullName()) }, suite.description));
  this.suiteDivs[suite.id] = suiteDiv;
  var parentDiv = this.outerDiv;
  if (suite.parentSuite) {
  parentDiv = this.suiteDivs[suite.parentSuite.id];
  }
  parentDiv.appendChild(suiteDiv);
  }
   
  this.startedAt = new Date();
   
  var self = this;
  showPassed.onclick = function (evt) {
  if (showPassed.checked) {
  self.outerDiv.className += ' show-passed';
  } else {
  self.outerDiv.className = self.outerDiv.className.replace(/ show-passed/, '');
  }
  };
   
  showSkipped.onclick = function (evt) {
  if (showSkipped.checked) {
  self.outerDiv.className += ' show-skipped';
  } else {
  self.outerDiv.className = self.outerDiv.className.replace(/ show-skipped/, '');
  }
  };
  };
   
  jasmine.TrivialReporter.prototype.reportRunnerResults = function (runner) {
  var results = runner.results();
  var className = (results.failedCount > 0) ? "runner failed" : "runner passed";
  this.runnerDiv.setAttribute("class", className);
  //do it twice for IE
  this.runnerDiv.setAttribute("className", className);
  var specs = runner.specs();
  var specCount = 0;
  for (var i = 0; i < specs.length; i++) {
  if (this.specFilter(specs[i])) {
  specCount++;
  }
  }
  var message = "" + specCount + " spec" + (specCount == 1 ? "" : "s" ) + ", " + results.failedCount + " failure" + ((results.failedCount == 1) ? "" : "s");
  message += " in " + ((new Date().getTime() - this.startedAt.getTime()) / 1000) + "s";
  this.runnerMessageSpan.replaceChild(this.createDom('a', { className: 'description', href: '?'}, message), this.runnerMessageSpan.firstChild);
   
  this.finishedAtSpan.appendChild(document.createTextNode("Finished at " + new Date().toString()));
  };
   
  jasmine.TrivialReporter.prototype.reportSuiteResults = function (suite) {
  var results = suite.results();
  var status = results.passed() ? 'passed' : 'failed';
  if (results.totalCount === 0) { // todo: change this to check results.skipped
  status = 'skipped';
  }
  this.suiteDivs[suite.id].className += " " + status;
  };
   
  jasmine.TrivialReporter.prototype.reportSpecStarting = function (spec) {
  if (this.logRunningSpecs) {
  this.log('>> Jasmine Running ' + spec.suite.description + ' ' + spec.description + '...');
  }
  };
   
  jasmine.TrivialReporter.prototype.reportSpecResults = function (spec) {
  var results = spec.results();
  var status = results.passed() ? 'passed' : 'failed';
  if (results.skipped) {
  status = 'skipped';
  }
  var specDiv = this.createDom('div', { className: 'spec ' + status },
  this.createDom('a', { className: 'run_spec', href: '?spec=' + encodeURIComponent(spec.getFullName()) }, "run"),
  this.createDom('a', {
  className: 'description',
  href: '?spec=' + encodeURIComponent(spec.getFullName()),
  title: spec.getFullName()
  }, spec.description));
   
   
  var resultItems = results.getItems();
  var messagesDiv = this.createDom('div', { className: 'messages' });
  for (var i = 0; i < resultItems.length; i++) {
  var result = resultItems[i];
   
  if (result.type == 'log') {
  messagesDiv.appendChild(this.createDom('div', {className: 'resultMessage log'}, result.toString()));
  } else if (result.type == 'expect' && result.passed && !result.passed()) {
  messagesDiv.appendChild(this.createDom('div', {className: 'resultMessage fail'}, result.message));
   
  if (result.trace.stack) {
  messagesDiv.appendChild(this.createDom('div', {className: 'stackTrace'}, result.trace.stack));
  }
  }
  }
   
  if (messagesDiv.childNodes.length > 0) {
  specDiv.appendChild(messagesDiv);
  }
   
  this.suiteDivs[spec.suite.id].appendChild(specDiv);
  };
   
  jasmine.TrivialReporter.prototype.log = function () {
var console = jasmine.getGlobal().console; var console = jasmine.getGlobal().console;
if (console && console.log) { if (console && console.log) {
if (console.log.apply) { if (console.log.apply) {
console.log.apply(console, arguments); console.log.apply(console, arguments);
} else { } else {
console.log(arguments); // ie fix: console.log.apply doesn't exist on ie console.log(arguments); // ie fix: console.log.apply doesn't exist on ie
} }
} }
}; };
   
self.specFilter = function(spec) { jasmine.TrivialReporter.prototype.getLocation = function () {
if (!focusedSpecName()) { return this.document.location;
return true; };
}  
  jasmine.TrivialReporter.prototype.specFilter = function (spec) {
return spec.getFullName().indexOf(focusedSpecName()) === 0; var paramMap = {};
}; var params = this.getLocation().search.substring(1).split('&');
  for (var i = 0; i < params.length; i++) {
return self;  
   
function focusedSpecName() {  
var specName;  
   
(function memoizeFocusedSpec() {  
if (specName) {  
return;  
}  
   
var paramMap = [];  
var params = doc.location.search.substring(1).split('&');  
   
for (var i = 0; i < params.length; i++) {  
var p = params[i].split('='); var p = params[i].split('=');
paramMap[decodeURIComponent(p[0])] = decodeURIComponent(p[1]); paramMap[decodeURIComponent(p[0])] = decodeURIComponent(p[1]);
} }
   
specName = paramMap.spec; if (!paramMap.spec) {
})(); return true;
  }
return specName; return spec.getFullName().indexOf(paramMap.spec) === 0;
} };
   
function createReporterDom(version) {  
dom.reporter = self.createDom('div', { id: 'HTMLReporter', className: 'jasmine_reporter' },  
dom.banner = self.createDom('div', { className: 'banner' },  
self.createDom('span', { className: 'title' }, "Jasmine "),  
self.createDom('span', { className: 'version' }, version)),  
   
dom.symbolSummary = self.createDom('ul', {className: 'symbolSummary'}),  
dom.alert = self.createDom('div', {className: 'alert'}),  
dom.results = self.createDom('div', {className: 'results'},  
dom.summary = self.createDom('div', { className: 'summary' }),  
dom.details = self.createDom('div', { id: 'details' }))  
);  
}  
};  
jasmine.HtmlReporterHelpers.addHelpers(jasmine.HtmlReporter);jasmine.HtmlReporter.ReporterView = function(dom) {  
this.startedAt = new Date();  
this.runningSpecCount = 0;  
this.completeSpecCount = 0;  
this.passedCount = 0;  
this.failedCount = 0;  
this.skippedCount = 0;  
   
this.createResultsMenu = function() {  
this.resultsMenu = this.createDom('span', {className: 'resultsMenu bar'},  
this.summaryMenuItem = this.createDom('a', {className: 'summaryMenuItem', href: "#"}, '0 specs'),  
' | ',  
this.detailsMenuItem = this.createDom('a', {className: 'detailsMenuItem', href: "#"}, '0 failing'));  
   
this.summaryMenuItem.onclick = function() {  
dom.reporter.className = dom.reporter.className.replace(/ showDetails/g, '');  
};  
   
this.detailsMenuItem.onclick = function() {  
showDetails();  
};  
};  
   
this.addSpecs = function(specs, specFilter) {  
this.totalSpecCount = specs.length;  
   
this.views = {  
specs: {},  
suites: {}  
};  
   
for (var i = 0; i < specs.length; i++) {  
var spec = specs[i];  
this.views.specs[spec.id] = new jasmine.HtmlReporter.SpecView(spec, dom, this.views);  
if (specFilter(spec)) {  
this.runningSpecCount++;  
}  
}  
};  
   
this.specComplete = function(spec) {  
this.completeSpecCount++;  
   
if (isUndefined(this.views.specs[spec.id])) {  
this.views.specs[spec.id] = new jasmine.HtmlReporter.SpecView(spec, dom);  
}  
   
var specView = this.views.specs[spec.id];  
   
switch (specView.status()) {  
case 'passed':  
this.passedCount++;  
break;  
   
case 'failed':  
this.failedCount++;  
break;  
   
case 'skipped':  
this.skippedCount++;  
break;  
}  
   
specView.refresh();  
this.refresh();  
};  
   
this.suiteComplete = function(suite) {  
var suiteView = this.views.suites[suite.id];  
if (isUndefined(suiteView)) {  
return;  
}  
suiteView.refresh();  
};  
   
this.refresh = function() {  
   
if (isUndefined(this.resultsMenu)) {  
this.createResultsMenu();  
}  
   
// currently running UI  
if (isUndefined(this.runningAlert)) {  
this.runningAlert = this.createDom('a', {href: "?", className: "runningAlert bar"});  
dom.alert.appendChild(this.runningAlert);  
}  
this.runningAlert.innerHTML = "Running " + this.completeSpecCount + " of " + specPluralizedFor(this.totalSpecCount);  
   
// skipped specs UI  
if (isUndefined(this.skippedAlert)) {  
this.skippedAlert = this.createDom('a', {href: "?", className: "skippedAlert bar"});  
}  
   
this.skippedAlert.innerHTML = "Skipping " + this.skippedCount + " of " + specPluralizedFor(this.totalSpecCount) + " - run all";  
   
if (this.skippedCount === 1 && isDefined(dom.alert)) {  
dom.alert.appendChild(this.skippedAlert);  
}  
   
// passing specs UI  
if (isUndefined(this.passedAlert)) {  
this.passedAlert = this.createDom('span', {href: "?", className: "passingAlert bar"});  
}  
this.passedAlert.innerHTML = "Passing " + specPluralizedFor(this.passedCount);  
   
// failing specs UI  
if (isUndefined(this.failedAlert)) {  
this.failedAlert = this.createDom('span', {href: "?", className: "failingAlert bar"});  
}  
this.failedAlert.innerHTML = "Failing " + specPluralizedFor(this.failedCount);  
   
if (this.failedCount === 1 && isDefined(dom.alert)) {  
dom.alert.appendChild(this.failedAlert);  
dom.alert.appendChild(this.resultsMenu);  
}  
   
// summary info  
this.summaryMenuItem.innerHTML = "" + specPluralizedFor(this.runningSpecCount);  
this.detailsMenuItem.innerHTML = "" + this.failedCount + " failing";  
};  
   
this.complete = function() {  
dom.alert.removeChild(this.runningAlert);  
   
this.skippedAlert.innerHTML = "Ran " + this.runningSpecCount + " of " + specPluralizedFor(this.totalSpecCount) + " - run all";  
   
if (this.failedCount === 0) {  
dom.alert.appendChild(this.createDom('span', {className: 'passingAlert bar'}, "Passing " + specPluralizedFor(this.passedCount)));  
} else {  
showDetails();  
}  
   
dom.banner.appendChild(this.createDom('span', {className: 'duration'}, "finished in " + ((new Date().getTime() - this.startedAt.getTime()) / 1000) + "s"));  
};  
   
return this;  
   
function showDetails() {  
if (dom.reporter.className.search(/showDetails/) === -1) {  
dom.reporter.className += " showDetails";  
}  
}  
   
function isUndefined(obj) {  
return typeof obj === 'undefined';  
}  
   
function isDefined(obj) {  
return !isUndefined(obj);  
}  
   
function specPluralizedFor(count) {  
var str = count + " spec";  
if (count > 1) {  
str += "s"  
}  
return str;  
}  
   
};  
   
jasmine.HtmlReporterHelpers.addHelpers(jasmine.HtmlReporter.ReporterView);  
   
   
jasmine.HtmlReporter.SpecView = function(spec, dom, views) {  
this.spec = spec;  
this.dom = dom;  
this.views = views;  
   
this.symbol = this.createDom('li', { className: 'pending' });  
this.dom.symbolSummary.appendChild(this.symbol);  
   
this.summary = this.createDom('div', { className: 'specSummary' },  
this.createDom('a', {  
className: 'description',  
href: '?spec=' + encodeURIComponent(this.spec.getFullName()),  
title: this.spec.getFullName()  
}, this.spec.description)  
);  
   
this.detail = this.createDom('div', { className: 'specDetail' },  
this.createDom('a', {  
className: 'description',  
href: '?spec=' + encodeURIComponent(this.spec.getFullName()),  
title: this.spec.getFullName()  
}, this.spec.getFullName())  
);  
};  
   
jasmine.HtmlReporter.SpecView.prototype.status = function() {  
return this.getSpecStatus(this.spec);  
};  
   
jasmine.HtmlReporter.SpecView.prototype.refresh = function() {  
this.symbol.className = this.status();  
   
switch (this.status()) {  
case 'skipped':  
break;  
   
case 'passed':  
this.appendSummaryToSuiteDiv();  
break;  
   
case 'failed':  
this.appendSummaryToSuiteDiv();  
this.appendFailureDetail();  
break;  
}  
};  
   
jasmine.HtmlReporter.SpecView.prototype.appendSummaryToSuiteDiv = function() {  
this.summary.className += ' ' + this.status();  
this.appendToSummary(this.spec, this.summary);  
};  
   
jasmine.HtmlReporter.SpecView.prototype.appendFailureDetail = function() {  
this.detail.className += ' ' + this.status();  
   
var resultItems = this.spec.results().getItems();  
var messagesDiv = this.createDom('div', { className: 'messages' });  
   
for (var i = 0; i < resultItems.length; i++) {  
var result = resultItems[i];  
   
if (result.type == 'log') {  
messagesDiv.appendChild(this.createDom('div', {className: 'resultMessage log'}, result.toString()));  
} else if (result.type == 'expect' && result.passed && !result.passed()) {  
messagesDiv.appendChild(this.createDom('div', {className: 'resultMessage fail'}, result.message));  
   
if (result.trace.stack) {  
messagesDiv.appendChild(this.createDom('div', {className: 'stackTrace'}, result.trace.stack));  
}  
}  
}  
   
if (messagesDiv.childNodes.length > 0) {  
this.detail.appendChild(messagesDiv);  
this.dom.details.appendChild(this.detail);  
}  
};  
   
jasmine.HtmlReporterHelpers.addHelpers(jasmine.HtmlReporter.SpecView);jasmine.HtmlReporter.SuiteView = function(suite, dom, views) {  
this.suite = suite;  
this.dom = dom;  
this.views = views;  
   
this.element = this.createDom('div', { className: 'suite' },  
this.createDom('a', { className: 'description', href: '?spec=' + encodeURIComponent(this.suite.getFullName()) }, this.suite.description)  
);  
   
this.appendToSummary(this.suite, this.element);  
};  
   
jasmine.HtmlReporter.SuiteView.prototype.status = function() {  
return this.getSpecStatus(this.suite);  
};  
   
jasmine.HtmlReporter.SuiteView.prototype.refresh = function() {  
this.element.className += " " + this.status();  
};  
   
jasmine.HtmlReporterHelpers.addHelpers(jasmine.HtmlReporter.SuiteView);  
   
/* @deprecated Use jasmine.HtmlReporter instead  
*/  
jasmine.TrivialReporter = function(doc) {  
this.document = doc || document;  
this.suiteDivs = {};  
this.logRunningSpecs = false;  
};  
   
jasmine.TrivialReporter.prototype.createDom = function(type, attrs, childrenVarArgs) {  
var el = document.createElement(type);  
   
for (var i = 2; i < arguments.length; i++) {  
var child = arguments[i];  
   
if (typeof child === 'string') {  
el.appendChild(document.createTextNode(child));  
} else {  
if (child) { el.appendChild(child); }  
}  
}  
   
for (var attr in attrs) {  
if (attr == "className") {  
el[attr] = attrs[attr];  
} else {  
el.setAttribute(attr, attrs[attr]);  
}  
}  
   
return el;  
};  
   
jasmine.TrivialReporter.prototype.reportRunnerStarting = function(runner) {  
var showPassed, showSkipped;  
   
this.outerDiv = this.createDom('div', { id: 'TrivialReporter', className: 'jasmine_reporter' },  
this.createDom('div', { className: 'banner' },  
this.createDom('div', { className: 'logo' },  
this.createDom('span', { className: 'title' }, "Jasmine"),  
this.createDom('span', { className: 'version' }, runner.env.versionString())),  
this.createDom('div', { className: 'options' },  
"Show ",  
showPassed = this.createDom('input', { id: "__jasmine_TrivialReporter_showPassed__", type: 'checkbox' }),  
this.createDom('label', { "for": "__jasmine_TrivialReporter_showPassed__" }, " passed "),  
showSkipped = this.createDom('input', { id: "__jasmine_TrivialReporter_showSkipped__", type: 'checkbox' }),  
this.createDom('label', { "for": "__jasmine_TrivialReporter_showSkipped__" }, " skipped")  
)  
),  
   
this.runnerDiv = this.createDom('div', { className: 'runner running' },  
this.createDom('a', { className: 'run_spec', href: '?' }, "run all"),  
this.runnerMessageSpan = this.createDom('span', {}, "Running..."),  
this.finishedAtSpan = this.createDom('span', { className: 'finished-at' }, ""))  
);  
   
this.document.body.appendChild(this.outerDiv);  
   
var suites = runner.suites();  
for (var i = 0; i < suites.length; i++) {  
var suite = suites[i];  
var suiteDiv = this.createDom('div', { className: 'suite' },  
this.createDom('a', { className: 'run_spec', href: '?spec=' + encodeURIComponent(suite.getFullName()) }, "run"),  
this.createDom('a', { className: 'description', href: '?spec=' + encodeURIComponent(suite.getFullName()) }, suite.description));  
this.suiteDivs[suite.id] = suiteDiv;  
var parentDiv = this.outerDiv;  
if (suite.parentSuite) {  
parentDiv = this.suiteDivs[suite.parentSuite.id];  
}  
parentDiv.appendChild(suiteDiv);  
}  
   
this.startedAt = new Date();  
   
var self = this;  
showPassed.onclick = function(evt) {  
if (showPassed.checked) {  
self.outerDiv.className += ' show-passed';  
} else {  
self.outerDiv.className = self.outerDiv.className.replace(/ show-passed/, '');  
}  
};  
   
showSkipped.onclick = function(evt) {  
if (showSkipped.checked) {  
self.outerDiv.className += ' show-skipped';  
} else {  
self.outerDiv.className = self.outerDiv.className.replace(/ show-skipped/, '');  
}  
};  
};  
   
jasmine.TrivialReporter.prototype.reportRunnerResults = function(runner) {  
var results = runner.results();  
var className = (results.failedCount > 0) ? "runner failed" : "runner passed";  
this.runnerDiv.setAttribute("class", className);  
//do it twice for IE  
this.runnerDiv.setAttribute("className", className);  
var specs = runner.specs();  
var specCount = 0;  
for (var i = 0; i < specs.length; i++) {  
if (this.specFilter(specs[i])) {  
specCount++;  
}  
}  
var message = "" + specCount + " spec" + (specCount == 1 ? "" : "s" ) + ", " + results.failedCount + " failure" + ((results.failedCount == 1) ? "" : "s");  
message += " in " + ((new Date().getTime() - this.startedAt.getTime()) / 1000) + "s";  
this.runnerMessageSpan.replaceChild(this.createDom('a', { className: 'description', href: '?'}, message), this.runnerMessageSpan.firstChild);  
   
this.finishedAtSpan.appendChild(document.createTextNode("Finished at " + new Date().toString()));  
};  
   
jasmine.TrivialReporter.prototype.reportSuiteResults = function(suite) {  
var results = suite.results();  
var status = results.passed() ? 'passed' : 'failed';  
if (results.totalCount === 0) { // todo: change this to check results.skipped  
status = 'skipped';  
}  
this.suiteDivs[suite.id].className += " " + status;  
};  
   
jasmine.TrivialReporter.prototype.reportSpecStarting = function(spec) {  
if (this.logRunningSpecs) {  
this.log('>> Jasmine Running ' + spec.suite.description + ' ' + spec.description + '...');  
}  
};  
   
jasmine.TrivialReporter.prototype.reportSpecResults = function(spec) {  
var results = spec.results();  
var status = results.passed() ? 'passed' : 'failed';  
if (results.skipped) {  
status = 'skipped';  
}  
var specDiv = this.createDom('div', { className: 'spec ' + status },  
this.createDom('a', { className: 'run_spec', href: '?spec=' + encodeURIComponent(spec.getFullName()) }, "run"),  
this.createDom('a', {  
className: 'description',  
href: '?spec=' + encodeURIComponent(spec.getFullName()),  
title: spec.getFullName()  
}, spec.description));  
   
   
var resultItems = results.getItems();  
var messagesDiv = this.createDom('div', { className: 'messages' });  
for (var i = 0; i < resultItems.length; i++) {  
var result = resultItems[i];  
   
if (result.type == 'log') {  
messagesDiv.appendChild(this.createDom('div', {className: 'resultMessage log'}, result.toString()));  
} else if (result.type == 'expect' && result.passed && !result.passed()) {  
messagesDiv.appendChild(this.createDom('div', {className: 'resultMessage fail'}, result.message));  
   
if (result.trace.stack) {  
messagesDiv.appendChild(this.createDom('div', {className: 'stackTrace'}, result.trace.stack));  
}  
}  
}  
   
if (messagesDiv.childNodes.length > 0) {  
specDiv.appendChild(messagesDiv);  
}  
   
this.suiteDivs[spec.suite.id].appendChild(specDiv);  
};  
   
jasmine.TrivialReporter.prototype.log = function() {  
var console = jasmine.getGlobal().console;  
if (console && console.log) {  
if (console.log.apply) {  
console.log.apply(console, arguments);  
} else {  
console.log(arguments); // ie fix: console.log.apply doesn't exist on ie  
}  
}  
};  
   
jasmine.TrivialReporter.prototype.getLocation = function() {  
return this.document.location;  
};  
   
jasmine.TrivialReporter.prototype.specFilter = function(spec) {  
var paramMap = {};  
var params = this.getLocation().search.substring(1).split('&');  
for (var i = 0; i < params.length; i++) {  
var p = params[i].split('=');  
paramMap[decodeURIComponent(p[0])] = decodeURIComponent(p[1]);  
}  
   
if (!paramMap.spec) {  
return true;  
}  
return spec.getFullName().indexOf(paramMap.spec) === 0;  
};  
   
body { background-color: #eeeeee; padding: 0; margin: 5px; overflow-y: scroll; } body {
  background-color: #eeeeee;
#HTMLReporter { font-size: 11px; font-family: Monaco, "Lucida Console", monospace; line-height: 14px; color: #333333; } padding: 0;
#HTMLReporter a { text-decoration: none; } margin: 5px;
#HTMLReporter a:hover { text-decoration: underline; } overflow-y: scroll;
#HTMLReporter p, #HTMLReporter h1, #HTMLReporter h2, #HTMLReporter h3, #HTMLReporter h4, #HTMLReporter h5, #HTMLReporter h6 { margin: 0; line-height: 14px; } }
#HTMLReporter .banner, #HTMLReporter .symbolSummary, #HTMLReporter .summary, #HTMLReporter .resultMessage, #HTMLReporter .specDetail .description, #HTMLReporter .alert .bar, #HTMLReporter .stackTrace { padding-left: 9px; padding-right: 9px; }  
#HTMLReporter #jasmine_content { position: fixed; right: 100%; } #HTMLReporter {
#HTMLReporter .version { color: #aaaaaa; } font-size: 11px;
#HTMLReporter .banner { margin-top: 14px; } font-family: Monaco, "Lucida Console", monospace;
#HTMLReporter .duration { color: #aaaaaa; float: right; } line-height: 14px;
#HTMLReporter .symbolSummary { overflow: hidden; *zoom: 1; margin: 14px 0; } color: #333333;
#HTMLReporter .symbolSummary li { display: block; float: left; height: 7px; width: 14px; margin-bottom: 7px; font-size: 16px; } }
#HTMLReporter .symbolSummary li.passed { font-size: 14px; }  
#HTMLReporter .symbolSummary li.passed:before { color: #5e7d00; content: "\02022"; } #HTMLReporter a {
#HTMLReporter .symbolSummary li.failed { line-height: 9px; } text-decoration: none;
#HTMLReporter .symbolSummary li.failed:before { color: #b03911; content: "x"; font-weight: bold; margin-left: -1px; } }
#HTMLReporter .symbolSummary li.skipped { font-size: 14px; }  
#HTMLReporter .symbolSummary li.skipped:before { color: #bababa; content: "\02022"; } #HTMLReporter a:hover {
#HTMLReporter .symbolSummary li.pending { line-height: 11px; } text-decoration: underline;
#HTMLReporter .symbolSummary li.pending:before { color: #aaaaaa; content: "-"; } }
#HTMLReporter .bar { line-height: 28px; font-size: 14px; display: block; color: #eee; }  
#HTMLReporter .runningAlert { background-color: #666666; } #HTMLReporter p, #HTMLReporter h1, #HTMLReporter h2, #HTMLReporter h3, #HTMLReporter h4, #HTMLReporter h5, #HTMLReporter h6 {
#HTMLReporter .skippedAlert { background-color: #aaaaaa; } margin: 0;
#HTMLReporter .skippedAlert:first-child { background-color: #333333; } line-height: 14px;
#HTMLReporter .skippedAlert:hover { text-decoration: none; color: white; text-decoration: underline; } }
#HTMLReporter .passingAlert { background-color: #a6b779; }  
#HTMLReporter .passingAlert:first-child { background-color: #5e7d00; } #HTMLReporter .banner, #HTMLReporter .symbolSummary, #HTMLReporter .summary, #HTMLReporter .resultMessage, #HTMLReporter .specDetail .description, #HTMLReporter .alert .bar, #HTMLReporter .stackTrace {
#HTMLReporter .failingAlert { background-color: #cf867e; } padding-left: 9px;
#HTMLReporter .failingAlert:first-child { background-color: #b03911; } padding-right: 9px;
#HTMLReporter .results { margin-top: 14px; } }
#HTMLReporter #details { display: none; }  
#HTMLReporter .resultsMenu, #HTMLReporter .resultsMenu a { background-color: #fff; color: #333333; } #HTMLReporter #jasmine_content {
#HTMLReporter.showDetails .summaryMenuItem { font-weight: normal; text-decoration: inherit; } position: fixed;
#HTMLReporter.showDetails .summaryMenuItem:hover { text-decoration: underline; } right: 100%;
#HTMLReporter.showDetails .detailsMenuItem { font-weight: bold; text-decoration: underline; } }
#HTMLReporter.showDetails .summary { display: none; }  
#HTMLReporter.showDetails #details { display: block; } #HTMLReporter .version {
#HTMLReporter .summaryMenuItem { font-weight: bold; text-decoration: underline; } color: #aaaaaa;
#HTMLReporter .summary { margin-top: 14px; } }
#HTMLReporter .summary .suite .suite, #HTMLReporter .summary .specSummary { margin-left: 14px; }  
#HTMLReporter .summary .specSummary.passed a { color: #5e7d00; } #HTMLReporter .banner {
#HTMLReporter .summary .specSummary.failed a { color: #b03911; } margin-top: 14px;
#HTMLReporter .description + .suite { margin-top: 0; } }
#HTMLReporter .suite { margin-top: 14px; }  
#HTMLReporter .suite a { color: #333333; } #HTMLReporter .duration {
#HTMLReporter #details .specDetail { margin-bottom: 28px; } color: #aaaaaa;
#HTMLReporter #details .specDetail .description { display: block; color: white; background-color: #b03911; } float: right;
#HTMLReporter .resultMessage { padding-top: 14px; color: #333333; } }
#HTMLReporter .resultMessage span.result { display: block; }  
#HTMLReporter .stackTrace { margin: 5px 0 0 0; max-height: 224px; overflow: auto; line-height: 18px; color: #666666; border: 1px solid #ddd; background: white; white-space: pre; } #HTMLReporter .symbolSummary {
  overflow: hidden;
#TrivialReporter { padding: 8px 13px; position: absolute; top: 0; bottom: 0; left: 0; right: 0; overflow-y: scroll; background-color: white; font-family: "Helvetica Neue Light", "Lucida Grande", "Calibri", "Arial", sans-serif; /*.resultMessage {*/ /*white-space: pre;*/ /*}*/ } *zoom: 1;
#TrivialReporter a:visited, #TrivialReporter a { color: #303; } margin: 14px 0;
#TrivialReporter a:hover, #TrivialReporter a:active { color: blue; } }
#TrivialReporter .run_spec { float: right; padding-right: 5px; font-size: .8em; text-decoration: none; }  
#TrivialReporter .banner { color: #303; background-color: #fef; padding: 5px; } #HTMLReporter .symbolSummary li {
#TrivialReporter .logo { float: left; font-size: 1.1em; padding-left: 5px; } display: block;
#TrivialReporter .logo .version { font-size: .6em; padding-left: 1em; } float: left;
#TrivialReporter .runner.running { background-color: yellow; } height: 7px;
#TrivialReporter .options { text-align: right; font-size: .8em; } width: 14px;
#TrivialReporter .suite { border: 1px outset gray; margin: 5px 0; padding-left: 1em; } margin-bottom: 7px;
#TrivialReporter .suite .suite { margin: 5px; } font-size: 16px;
#TrivialReporter .suite.passed { background-color: #dfd; } }
#TrivialReporter .suite.failed { background-color: #fdd; }  
#TrivialReporter .spec { margin: 5px; padding-left: 1em; clear: both; } #HTMLReporter .symbolSummary li.passed {
#TrivialReporter .spec.failed, #TrivialReporter .spec.passed, #TrivialReporter .spec.skipped { padding-bottom: 5px; border: 1px solid gray; } font-size: 14px;
#TrivialReporter .spec.failed { background-color: #fbb; border-color: red; } }
#TrivialReporter .spec.passed { background-color: #bfb; border-color: green; }  
#TrivialReporter .spec.skipped { background-color: #bbb; } #HTMLReporter .symbolSummary li.passed:before {
#TrivialReporter .messages { border-left: 1px dashed gray; padding-left: 1em; padding-right: 1em; } color: #5e7d00;
#TrivialReporter .passed { background-color: #cfc; display: none; } content: "\02022";
#TrivialReporter .failed { background-color: #fbb; } }
#TrivialReporter .skipped { color: #777; background-color: #eee; display: none; }  
#TrivialReporter .resultMessage span.result { display: block; line-height: 2em; color: black; } #HTMLReporter .symbolSummary li.failed {
#TrivialReporter .resultMessage .mismatch { color: black; } line-height: 9px;
#TrivialReporter .stackTrace { white-space: pre; font-size: .8em; margin-left: 10px; max-height: 5em; overflow: auto; border: 1px inset red; padding: 1em; background: #eef; } }
#TrivialReporter .finished-at { padding-left: 1em; font-size: .6em; }  
#TrivialReporter.show-passed .passed, #TrivialReporter.show-skipped .skipped { display: block; } #HTMLReporter .symbolSummary li.failed:before {
#TrivialReporter #jasmine_content { position: fixed; right: 100%; } color: #b03911;
#TrivialReporter .runner { border: 1px solid gray; display: block; margin: 5px 0; padding: 2px 0 2px 10px; } content: "x";
  font-weight: bold;
  margin-left: -1px;
  }
   
  #HTMLReporter .symbolSummary li.skipped {
  font-size: 14px;
  }
   
  #HTMLReporter .symbolSummary li.skipped:before {
  color: #bababa;
  content: "\02022";
  }
   
  #HTMLReporter .symbolSummary li.pending {
  line-height: 11px;
  }
   
  #HTMLReporter .symbolSummary li.pending:before {
  color: #aaaaaa;
  content: "-";
  }
   
  #HTMLReporter .bar {
  line-height: 28px;
  font-size: 14px;
  display: block;
  color: #eee;
  }
   
  #HTMLReporter .runningAlert {
  background-color: #666666;
  }
   
  #HTMLReporter .skippedAlert {
  background-color: #aaaaaa;
  }
   
  #HTMLReporter .skippedAlert:first-child {
  background-color: #333333;
  }
   
  #HTMLReporter .skippedAlert:hover {
  text-decoration: none;
  color: white;
  text-decoration: underline;
  }
   
  #HTMLReporter .passingAlert {
  background-color: #a6b779;
  }
   
  #HTMLReporter .passingAlert:first-child {
  background-color: #5e7d00;
  }
   
  #HTMLReporter .failingAlert {
  background-color: #cf867e;
  }
   
  #HTMLReporter .failingAlert:first-child {
  background-color: #b03911;
  }
   
  #HTMLReporter .results {
  margin-top: 14px;
  }
   
  #HTMLReporter #details {
  display: none;
  }
   
  #HTMLReporter .resultsMenu, #HTMLReporter .resultsMenu a {
  background-color: #fff;
  color: #333333;
  }
   
  #HTMLReporter.showDetails .summaryMenuItem {
  font-weight: normal;
  text-decoration: inherit;
  }
   
  #HTMLReporter.showDetails .summaryMenuItem:hover {
  text-decoration: underline;
  }
   
  #HTMLReporter.showDetails .detailsMenuItem {
  font-weight: bold;
  text-decoration: underline;
  }
   
  #HTMLReporter.showDetails .summary {
  display: none;
  }
   
  #HTMLReporter.showDetails #details {
  display: block;
  }
   
  #HTMLReporter .summaryMenuItem {
  font-weight: bold;
  text-decoration: underline;
  }
   
  #HTMLReporter .summary {
  margin-top: 14px;
  }
   
  #HTMLReporter .summary .suite .suite, #HTMLReporter .summary .specSummary {
  margin-left: 14px;
  }
   
  #HTMLReporter .summary .specSummary.passed a {
  color: #5e7d00;
  }
   
  #HTMLReporter .summary .specSummary.failed a {
  color: #b03911;
  }
   
  #HTMLReporter .description + .suite {
  margin-top: 0;
  }
   
  #HTMLReporter .suite {
  margin-top: 14px;
  }
   
  #HTMLReporter .suite a {
  color: #333333;
  }
   
  #HTMLReporter #details .specDetail {
  margin-bottom: 28px;
  }
   
  #HTMLReporter #details .specDetail .description {
  display: block;
  color: white;
  background-color: #b03911;
  }
   
  #HTMLReporter .resultMessage {
  padding-top: 14px;
  color: #333333;
  }
   
  #HTMLReporter .resultMessage span.result {
  display: block;
  }
   
  #HTMLReporter .stackTrace {
  margin: 5px 0 0 0;
  max-height: 224px;
  overflow: auto;
  line-height: 18px;
  color: #666666;
  border: 1px solid #ddd;
  background: white;
  white-space: pre;
  }
   
  #TrivialReporter {
  padding: 8px 13px;
  position: absolute;
  top: 0;
  bottom: 0;
  left: 0;
  right: 0;
  overflow-y: scroll;
  background-color: white;
  font-family: "Helvetica Neue Light", "Lucida Grande", "Calibri", "Arial", sans-serif; /*.resultMessage {*/
  /*white-space: pre;*/
  /*}*/
  }
   
  #TrivialReporter a:visited, #TrivialReporter a {
  color: #303;
  }
   
  #TrivialReporter a:hover, #TrivialReporter a:active {
  color: blue;
  }
   
  #TrivialReporter .run_spec {
  float: right;
  padding-right: 5px;
  font-size: .8em;
  text-decoration: none;
  }
   
  #TrivialReporter .banner {
  color: #303;
  background-color: #fef;
  padding: 5px;
  }
   
  #TrivialReporter .logo {
  float: left;
  font-size: 1.1em;
  padding-left: 5px;
  }
   
  #TrivialReporter .logo .version {
  font-size: .6em;
  padding-left: 1em;
  }
   
  #TrivialReporter .runner.running {
  background-color: yellow;
  }
   
  #TrivialReporter .options {
  text-align: right;
  font-size: .8em;
  }
   
  #TrivialReporter .suite {
  border: 1px outset gray;
  margin: 5px 0;
  padding-left: 1em;
  }
   
  #TrivialReporter .suite .suite {
  margin: 5px;
  }
   
  #TrivialReporter .suite.passed {
  background-color: #dfd;
  }
   
  #TrivialReporter .suite.failed {
  background-color: #fdd;
  }
   
  #TrivialReporter .spec {
  margin: 5px;
  padding-left: 1em;
  clear: both;
  }
   
  #TrivialReporter .spec.failed, #TrivialReporter .spec.passed, #TrivialReporter .spec.skipped {
  padding-bottom: 5px;
  border: 1px solid gray;
  }
   
  #TrivialReporter .spec.failed {
  background-color: #fbb;
  border-color: red;
  }
   
  #TrivialReporter .spec.passed {
  background-color: #bfb;
  border-color: green;
  }
   
  #TrivialReporter .spec.skipped {
  background-color: #bbb;
  }
   
  #TrivialReporter .messages {
  border-left: 1px dashed gray;
  padding-left: 1em;
  padding-right: 1em;
  }
   
  #TrivialReporter .passed {
  background-color: #cfc;
  display: none;
  }
   
  #TrivialReporter .failed {
  background-color: #fbb;
  }
   
  #TrivialReporter .skipped {
  color: #777;
  background-color: #eee;
  display: none;
  }
   
  #TrivialReporter .resultMessage span.result {
  display: block;
  line-height: 2em;
  color: black;
  }
   
  #TrivialReporter .resultMessage .mismatch {
  color: black;
  }
   
  #TrivialReporter .stackTrace {
  white-space: pre;
  font-size: .8em;
  margin-left: 10px;
  max-height: 5em;
  overflow: auto;
  border: 1px inset red;
  padding: 1em;
  background: #eef;
  }
   
  #TrivialReporter .finished-at {
  padding-left: 1em;
  font-size: .6em;
  }
   
  #TrivialReporter.show-passed .passed, #TrivialReporter.show-skipped .skipped {
  display: block;
  }
   
  #TrivialReporter #jasmine_content {
  position: fixed;
  right: 100%;
  }
   
  #TrivialReporter .runner {
  border: 1px solid gray;
  display: block;
  margin: 5px 0;
  padding: 2px 0 2px 10px;
  }
   
var isCommonJS = typeof window == "undefined"; var isCommonJS = typeof window == "undefined";
   
/** /**
* Top level namespace for Jasmine, a lightweight JavaScript BDD/spec/testing framework. * Top level namespace for Jasmine, a lightweight JavaScript BDD/spec/testing framework.
* *
* @namespace * @namespace
*/ */
var jasmine = {}; var jasmine = {};
if (isCommonJS) exports.jasmine = jasmine; if (isCommonJS) exports.jasmine = jasmine;
/** /**
* @private * @private
*/ */
jasmine.unimplementedMethod_ = function() { jasmine.unimplementedMethod_ = function () {
throw new Error("unimplemented method"); throw new Error("unimplemented method");
}; };
   
/** /**
* Use <code>jasmine.undefined</code> instead of <code>undefined</code>, since <code>undefined</code> is just * Use <code>jasmine.undefined</code> instead of <code>undefined</code>, since <code>undefined</code> is just
* a plain old variable and may be redefined by somebody else. * a plain old variable and may be redefined by somebody else.
* *
* @private * @private
*/ */
jasmine.undefined = jasmine.___undefined___; jasmine.undefined = jasmine.___undefined___;
   
/** /**
* Show diagnostic messages in the console if set to true * Show diagnostic messages in the console if set to true
* *
*/ */
jasmine.VERBOSE = false; jasmine.VERBOSE = false;
   
/** /**
* Default interval in milliseconds for event loop yields (e.g. to allow network activity or to refresh the screen with the HTML-based runner). Small values here may result in slow test running. Zero means no updates until all tests have completed. * Default interval in milliseconds for event loop yields (e.g. to allow network activity or to refresh the screen with the HTML-based runner). Small values here may result in slow test running. Zero means no updates until all tests have completed.
* *
*/ */
jasmine.DEFAULT_UPDATE_INTERVAL = 250; jasmine.DEFAULT_UPDATE_INTERVAL = 250;
   
/** /**
* Default timeout interval in milliseconds for waitsFor() blocks. * Default timeout interval in milliseconds for waitsFor() blocks.
*/ */
jasmine.DEFAULT_TIMEOUT_INTERVAL = 5000; jasmine.DEFAULT_TIMEOUT_INTERVAL = 5000;
   
jasmine.getGlobal = function() { jasmine.getGlobal = function () {
function getGlobal() { function getGlobal() {
return this; return this;
} }
   
return getGlobal(); return getGlobal();
}; };
   
/** /**
* Allows for bound functions to be compared. Internal use only. * Allows for bound functions to be compared. Internal use only.
* *
* @ignore * @ignore
* @private * @private
* @param base {Object} bound 'this' for the function * @param base {Object} bound 'this' for the function
* @param name {Function} function to find * @param name {Function} function to find
*/ */
jasmine.bindOriginal_ = function(base, name) { jasmine.bindOriginal_ = function (base, name) {
var original = base[name]; var original = base[name];
if (original.apply) { if (original.apply) {
return function() { return function () {
return original.apply(base, arguments); return original.apply(base, arguments);
}; };
} else { } else {
// IE support // IE support
return jasmine.getGlobal()[name]; return jasmine.getGlobal()[name];
} }
}; };
   
jasmine.setTimeout = jasmine.bindOriginal_(jasmine.getGlobal(), 'setTimeout'); jasmine.setTimeout = jasmine.bindOriginal_(jasmine.getGlobal(), 'setTimeout');
jasmine.clearTimeout = jasmine.bindOriginal_(jasmine.getGlobal(), 'clearTimeout'); jasmine.clearTimeout = jasmine.bindOriginal_(jasmine.getGlobal(), 'clearTimeout');
jasmine.setInterval = jasmine.bindOriginal_(jasmine.getGlobal(), 'setInterval'); jasmine.setInterval = jasmine.bindOriginal_(jasmine.getGlobal(), 'setInterval');
jasmine.clearInterval = jasmine.bindOriginal_(jasmine.getGlobal(), 'clearInterval'); jasmine.clearInterval = jasmine.bindOriginal_(jasmine.getGlobal(), 'clearInterval');
   
jasmine.MessageResult = function(values) { jasmine.MessageResult = function (values) {
this.type = 'log'; this.type = 'log';
this.values = values; this.values = values;
this.trace = new Error(); // todo: test better this.trace = new Error(); // todo: test better
}; };
   
jasmine.MessageResult.prototype.toString = function() { jasmine.MessageResult.prototype.toString = function () {
var text = ""; var text = "";
for (var i = 0; i < this.values.length; i++) { for (var i = 0; i < this.values.length; i++) {
if (i > 0) text += " "; if (i > 0) text += " ";
if (jasmine.isString_(this.values[i])) { if (jasmine.isString_(this.values[i])) {
text += this.values[i]; text += this.values[i];
} else { } else {
text += jasmine.pp(this.values[i]); text += jasmine.pp(this.values[i]);
} }
} }
return text; return text;
}; };
   
jasmine.ExpectationResult = function(params) { jasmine.ExpectationResult = function (params) {
this.type = 'expect'; this.type = 'expect';
this.matcherName = params.matcherName; this.matcherName = params.matcherName;
this.passed_ = params.passed; this.passed_ = params.passed;
this.expected = params.expected; this.expected = params.expected;
this.actual = params.actual; this.actual = params.actual;
this.message = this.passed_ ? 'Passed.' : params.message; this.message = this.passed_ ? 'Passed.' : params.message;
   
var trace = (params.trace || new Error(this.message)); var trace = (params.trace || new Error(this.message));
this.trace = this.passed_ ? '' : trace; this.trace = this.passed_ ? '' : trace;
}; };
   
jasmine.ExpectationResult.prototype.toString = function () { jasmine.ExpectationResult.prototype.toString = function () {
return this.message; return this.message;
}; };
   
jasmine.ExpectationResult.prototype.passed = function () { jasmine.ExpectationResult.prototype.passed = function () {
return this.passed_; return this.passed_;
}; };
   
/** /**
* Getter for the Jasmine environment. Ensures one gets created * Getter for the Jasmine environment. Ensures one gets created
*/ */
jasmine.getEnv = function() { jasmine.getEnv = function () {
var env = jasmine.currentEnv_ = jasmine.currentEnv_ || new jasmine.Env(); var env = jasmine.currentEnv_ = jasmine.currentEnv_ || new jasmine.Env();
return env; return env;
}; };
   
/** /**
* @ignore * @ignore
* @private * @private
* @param value * @param value
* @returns {Boolean} * @returns {Boolean}
*/ */
jasmine.isArray_ = function(value) { jasmine.isArray_ = function (value) {
return jasmine.isA_("Array", value); return jasmine.isA_("Array", value);
}; };
   
/** /**
* @ignore * @ignore
* @private * @private
* @param value * @param value
* @returns {Boolean} * @returns {Boolean}
*/ */
jasmine.isString_ = function(value) { jasmine.isString_ = function (value) {
return jasmine.isA_("String", value); return jasmine.isA_("String", value);
}; };
   
/** /**
* @ignore * @ignore
* @private * @private
* @param value * @param value
* @returns {Boolean} * @returns {Boolean}
*/ */
jasmine.isNumber_ = function(value) { jasmine.isNumber_ = function (value) {
return jasmine.isA_("Number", value); return jasmine.isA_("Number", value);
}; };
   
/** /**
* @ignore * @ignore
* @private * @private
* @param {String} typeName * @param {String} typeName
* @param value * @param value
* @returns {Boolean} * @returns {Boolean}
*/ */
jasmine.isA_ = function(typeName, value) { jasmine.isA_ = function (typeName, value) {
return Object.prototype.toString.apply(value) === '[object ' + typeName + ']'; return Object.prototype.toString.apply(value) === '[object ' + typeName + ']';
}; };
   
/** /**
* Pretty printer for expecations. Takes any object and turns it into a human-readable string. * Pretty printer for expecations. Takes any object and turns it into a human-readable string.
* *
* @param value {Object} an object to be outputted * @param value {Object} an object to be outputted
* @returns {String} * @returns {String}
*/ */
jasmine.pp = function(value) { jasmine.pp = function (value) {
var stringPrettyPrinter = new jasmine.StringPrettyPrinter(); var stringPrettyPrinter = new jasmine.StringPrettyPrinter();
stringPrettyPrinter.format(value); stringPrettyPrinter.format(value);
return stringPrettyPrinter.string; return stringPrettyPrinter.string;
}; };
   
/** /**
* Returns true if the object is a DOM Node. * Returns true if the object is a DOM Node.
* *
* @param {Object} obj object to check * @param {Object} obj object to check
* @returns {Boolean} * @returns {Boolean}
*/ */
jasmine.isDomNode = function(obj) { jasmine.isDomNode = function (obj) {
return obj.nodeType > 0; return obj.nodeType > 0;
}; };
   
/** /**
* Returns a matchable 'generic' object of the class type. For use in expecations of type when values don't matter. * Returns a matchable 'generic' object of the class type. For use in expecations of type when values don't matter.
* *
* @example * @example
* // don't care about which function is passed in, as long as it's a function * // don't care about which function is passed in, as long as it's a function
* expect(mySpy).toHaveBeenCalledWith(jasmine.any(Function)); * expect(mySpy).toHaveBeenCalledWith(jasmine.any(Function));
* *
* @param {Class} clazz * @param {Class} clazz
* @returns matchable object of the type clazz * @returns matchable object of the type clazz
*/ */
jasmine.any = function(clazz) { jasmine.any = function (clazz) {
return new jasmine.Matchers.Any(clazz); return new jasmine.Matchers.Any(clazz);
}; };
   
/** /**
* Returns a matchable subset of a JSON object. For use in expectations when you don't care about all of the * Returns a matchable subset of a JSON object. For use in expectations when you don't care about all of the
* attributes on the object. * attributes on the object.
* *
* @example * @example
* // don't care about any other attributes than foo. * // don't care about any other attributes than foo.
* expect(mySpy).toHaveBeenCalledWith(jasmine.objectContaining({foo: "bar"}); * expect(mySpy).toHaveBeenCalledWith(jasmine.objectContaining({foo: "bar"});
* *
* @param sample {Object} sample * @param sample {Object} sample
* @returns matchable object for the sample * @returns matchable object for the sample
*/ */
jasmine.objectContaining = function (sample) { jasmine.objectContaining = function (sample) {
return new jasmine.Matchers.ObjectContaining(sample); return new jasmine.Matchers.ObjectContaining(sample);
}; };
   
/** /**
* Jasmine Spies are test doubles that can act as stubs, spies, fakes or when used in an expecation, mocks. * Jasmine Spies are test doubles that can act as stubs, spies, fakes or when used in an expecation, mocks.
* *
* Spies should be created in test setup, before expectations. They can then be checked, using the standard Jasmine * Spies should be created in test setup, before expectations. They can then be checked, using the standard Jasmine
* expectation syntax. Spies can be checked if they were called or not and what the calling params were. * expectation syntax. Spies can be checked if they were called or not and what the calling params were.
* *
* A Spy has the following fields: wasCalled, callCount, mostRecentCall, and argsForCall (see docs). * A Spy has the following fields: wasCalled, callCount, mostRecentCall, and argsForCall (see docs).
* *
* Spies are torn down at the end of every spec. * Spies are torn down at the end of every spec.
* *
* Note: Do <b>not</b> call new jasmine.Spy() directly - a spy must be created using spyOn, jasmine.createSpy or jasmine.createSpyObj. * Note: Do <b>not</b> call new jasmine.Spy() directly - a spy must be created using spyOn, jasmine.createSpy or jasmine.createSpyObj.
* *
* @example * @example
* // a stub * // a stub
* var myStub = jasmine.createSpy('myStub'); // can be used anywhere * var myStub = jasmine.createSpy('myStub'); // can be used anywhere
* *
* // spy example * // spy example
* var foo = { * var foo = {
* not: function(bool) { return !bool; } * not: function(bool) { return !bool; }
* } * }
* *
* // actual foo.not will not be called, execution stops * // actual foo.not will not be called, execution stops
* spyOn(foo, 'not'); * spyOn(foo, 'not');
   
// foo.not spied upon, execution will continue to implementation // foo.not spied upon, execution will continue to implementation
* spyOn(foo, 'not').andCallThrough(); * spyOn(foo, 'not').andCallThrough();
* *
* // fake example * // fake example
* var foo = { * var foo = {
* not: function(bool) { return !bool; } * not: function(bool) { return !bool; }
* } * }
* *
* // foo.not(val) will return val * // foo.not(val) will return val
* spyOn(foo, 'not').andCallFake(function(value) {return value;}); * spyOn(foo, 'not').andCallFake(function(value) {return value;});
* *
* // mock example * // mock example
* foo.not(7 == 7); * foo.not(7 == 7);
* expect(foo.not).toHaveBeenCalled(); * expect(foo.not).toHaveBeenCalled();
* expect(foo.not).toHaveBeenCalledWith(true); * expect(foo.not).toHaveBeenCalledWith(true);
* *
* @constructor * @constructor
* @see spyOn, jasmine.createSpy, jasmine.createSpyObj * @see spyOn, jasmine.createSpy, jasmine.createSpyObj
* @param {String} name * @param {String} name
*/ */
jasmine.Spy = function(name) { jasmine.Spy = function (name) {
/** /**
* The name of the spy, if provided. * The name of the spy, if provided.
*/ */
this.identity = name || 'unknown'; this.identity = name || 'unknown';
/** /**
* Is this Object a spy? * Is this Object a spy?
*/ */
this.isSpy = true; this.isSpy = true;
/** /**
* The actual function this spy stubs. * The actual function this spy stubs.
*/ */
this.plan = function() { this.plan = function () {
}; };
/** /**
* Tracking of the most recent call to the spy. * Tracking of the most recent call to the spy.
* @example * @example
* var mySpy = jasmine.createSpy('foo'); * var mySpy = jasmine.createSpy('foo');
* mySpy(1, 2); * mySpy(1, 2);
* mySpy.mostRecentCall.args = [1, 2]; * mySpy.mostRecentCall.args = [1, 2];
*/ */
this.mostRecentCall = {}; this.mostRecentCall = {};
   
/** /**
* Holds arguments for each call to the spy, indexed by call count * Holds arguments for each call to the spy, indexed by call count
* @example * @example
* var mySpy = jasmine.createSpy('foo'); * var mySpy = jasmine.createSpy('foo');
* mySpy(1, 2); * mySpy(1, 2);
* mySpy(7, 8); * mySpy(7, 8);
* mySpy.mostRecentCall.args = [7, 8]; * mySpy.mostRecentCall.args = [7, 8];
* mySpy.argsForCall[0] = [1, 2]; * mySpy.argsForCall[0] = [1, 2];
* mySpy.argsForCall[1] = [7, 8]; * mySpy.argsForCall[1] = [7, 8];
*/ */
this.argsForCall = []; this.argsForCall = [];
this.calls = []; this.calls = [];
}; };
   
/** /**
* Tells a spy to call through to the actual implemenatation. * Tells a spy to call through to the actual implemenatation.
* *
* @example * @example
* var foo = { * var foo = {
* bar: function() { // do some stuff } * bar: function() { // do some stuff }
* } * }
* *
* // defining a spy on an existing property: foo.bar * // defining a spy on an existing property: foo.bar
* spyOn(foo, 'bar').andCallThrough(); * spyOn(foo, 'bar').andCallThrough();
*/ */
jasmine.Spy.prototype.andCallThrough = function() { jasmine.Spy.prototype.andCallThrough = function () {
this.plan = this.originalValue; this.plan = this.originalValue;
return this; return this;
}; };
   
/** /**
* For setting the return value of a spy. * For setting the return value of a spy.
* *
* @example * @example
* // defining a spy from scratch: foo() returns 'baz' * // defining a spy from scratch: foo() returns 'baz'
* var foo = jasmine.createSpy('spy on foo').andReturn('baz'); * var foo = jasmine.createSpy('spy on foo').andReturn('baz');
* *
* // defining a spy on an existing property: foo.bar() returns 'baz' * // defining a spy on an existing property: foo.bar() returns 'baz'
* spyOn(foo, 'bar').andReturn('baz'); * spyOn(foo, 'bar').andReturn('baz');
* *
* @param {Object} value * @param {Object} value
*/ */
jasmine.Spy.prototype.andReturn = function(value) { jasmine.Spy.prototype.andReturn = function (value) {
this.plan = function() { this.plan = function () {
return value; return value;
}; };
return this; return this;
}; };
   
/** /**
* For throwing an exception when a spy is called. * For throwing an exception when a spy is called.
* *
* @example * @example
* // defining a spy from scratch: foo() throws an exception w/ message 'ouch' * // defining a spy from scratch: foo() throws an exception w/ message 'ouch'
* var foo = jasmine.createSpy('spy on foo').andThrow('baz'); * var foo = jasmine.createSpy('spy on foo').andThrow('baz');
* *
* // defining a spy on an existing property: foo.bar() throws an exception w/ message 'ouch' * // defining a spy on an existing property: foo.bar() throws an exception w/ message 'ouch'
* spyOn(foo, 'bar').andThrow('baz'); * spyOn(foo, 'bar').andThrow('baz');
* *
* @param {String} exceptionMsg * @param {String} exceptionMsg
*/ */
jasmine.Spy.prototype.andThrow = function(exceptionMsg) { jasmine.Spy.prototype.andThrow = function (exceptionMsg) {
this.plan = function() { this.plan = function () {
throw exceptionMsg; throw exceptionMsg;
}; };
return this; return this;
}; };
   
/** /**
* Calls an alternate implementation when a spy is called. * Calls an alternate implementation when a spy is called.
* *
* @example * @example
* var baz = function() { * var baz = function() {
* // do some stuff, return something * // do some stuff, return something
* } * }
* // defining a spy from scratch: foo() calls the function baz * // defining a spy from scratch: foo() calls the function baz
* var foo = jasmine.createSpy('spy on foo').andCall(baz); * var foo = jasmine.createSpy('spy on foo').andCall(baz);
* *
* // defining a spy on an existing property: foo.bar() calls an anonymnous function * // defining a spy on an existing property: foo.bar() calls an anonymnous function
* spyOn(foo, 'bar').andCall(function() { return 'baz';} ); * spyOn(foo, 'bar').andCall(function() { return 'baz';} );
* *
* @param {Function} fakeFunc * @param {Function} fakeFunc
*/ */
jasmine.Spy.prototype.andCallFake = function(fakeFunc) { jasmine.Spy.prototype.andCallFake = function (fakeFunc) {
this.plan = fakeFunc; this.plan = fakeFunc;
return this; return this;
}; };
   
/** /**
* Resets all of a spy's the tracking variables so that it can be used again. * Resets all of a spy's the tracking variables so that it can be used again.
* *
* @example * @example
* spyOn(foo, 'bar'); * spyOn(foo, 'bar');
* *
* foo.bar(); * foo.bar();
* *
* expect(foo.bar.callCount).toEqual(1); * expect(foo.bar.callCount).toEqual(1);
* *
* foo.bar.reset(); * foo.bar.reset();
* *
* expect(foo.bar.callCount).toEqual(0); * expect(foo.bar.callCount).toEqual(0);
*/ */
jasmine.Spy.prototype.reset = function() { jasmine.Spy.prototype.reset = function () {
this.wasCalled = false; this.wasCalled = false;
this.callCount = 0; this.callCount = 0;
this.argsForCall = []; this.argsForCall = [];
this.calls = []; this.calls = [];
this.mostRecentCall = {}; this.mostRecentCall = {};
}; };
   
jasmine.createSpy = function(name) { jasmine.createSpy = function (name) {
   
var spyObj = function() { var spyObj = function () {
spyObj.wasCalled = true; spyObj.wasCalled = true;
spyObj.callCount++; spyObj.callCount++;
var args = jasmine.util.argsToArray(arguments); var args = jasmine.util.argsToArray(arguments);
spyObj.mostRecentCall.object = this; spyObj.mostRecentCall.object = this;
spyObj.mostRecentCall.args = args; spyObj.mostRecentCall.args = args;
spyObj.argsForCall.push(args); spyObj.argsForCall.push(args);
spyObj.calls.push({object: this, args: args}); spyObj.calls.push({object: this, args: args});
return spyObj.plan.apply(this, arguments); return spyObj.plan.apply(this, arguments);
}; };
   
var spy = new jasmine.Spy(name); var spy = new jasmine.Spy(name);
   
for (var prop in spy) { for (var prop in spy) {
spyObj[prop] = spy[prop]; spyObj[prop] = spy[prop];
} }
   
spyObj.reset(); spyObj.reset();
   
return spyObj; return spyObj;
}; };
   
/** /**
* Determines whether an object is a spy. * Determines whether an object is a spy.
* *
* @param {jasmine.Spy|Object} putativeSpy * @param {jasmine.Spy|Object} putativeSpy
* @returns {Boolean} * @returns {Boolean}
*/ */
jasmine.isSpy = function(putativeSpy) { jasmine.isSpy = function (putativeSpy) {
return putativeSpy && putativeSpy.isSpy; return putativeSpy && putativeSpy.isSpy;
}; };
   
/** /**
* Creates a more complicated spy: an Object that has every property a function that is a spy. Used for stubbing something * Creates a more complicated spy: an Object that has every property a function that is a spy. Used for stubbing something
* large in one call. * large in one call.
* *
* @param {String} baseName name of spy class * @param {String} baseName name of spy class
* @param {Array} methodNames array of names of methods to make spies * @param {Array} methodNames array of names of methods to make spies
*/ */
jasmine.createSpyObj = function(baseName, methodNames) { jasmine.createSpyObj = function (baseName, methodNames) {
if (!jasmine.isArray_(methodNames) || methodNames.length === 0) { if (!jasmine.isArray_(methodNames) || methodNames.length === 0) {
throw new Error('createSpyObj requires a non-empty array of method names to create spies for'); throw new Error('createSpyObj requires a non-empty array of method names to create spies for');
} }
var obj = {}; var obj = {};
for (var i = 0; i < methodNames.length; i++) { for (var i = 0; i < methodNames.length; i++) {
obj[methodNames[i]] = jasmine.createSpy(baseName + '.' + methodNames[i]); obj[methodNames[i]] = jasmine.createSpy(baseName + '.' + methodNames[i]);
} }
return obj; return obj;
}; };
   
/** /**
* All parameters are pretty-printed and concatenated together, then written to the current spec's output. * All parameters are pretty-printed and concatenated together, then written to the current spec's output.
* *
* Be careful not to leave calls to <code>jasmine.log</code> in production code. * Be careful not to leave calls to <code>jasmine.log</code> in production code.
*/ */
jasmine.log = function() { jasmine.log = function () {
var spec = jasmine.getEnv().currentSpec; var spec = jasmine.getEnv().currentSpec;
spec.log.apply(spec, arguments); spec.log.apply(spec, arguments);
}; };
   
/** /**
* Function that installs a spy on an existing object's method name. Used within a Spec to create a spy. * Function that installs a spy on an existing object's method name. Used within a Spec to create a spy.
* *
* @example * @example
* // spy example * // spy example
* var foo = { * var foo = {
* not: function(bool) { return !bool; } * not: function(bool) { return !bool; }
* } * }
* spyOn(foo, 'not'); // actual foo.not will not be called, execution stops * spyOn(foo, 'not'); // actual foo.not will not be called, execution stops
* *
* @see jasmine.createSpy * @see jasmine.createSpy
* @param obj * @param obj
* @param methodName * @param methodName
* @returns a Jasmine spy that can be chained with all spy methods * @returns a Jasmine spy that can be chained with all spy methods
*/ */
var spyOn = function(obj, methodName) { var spyOn = function (obj, methodName) {
return jasmine.getEnv().currentSpec.spyOn(obj, methodName); return jasmine.getEnv().currentSpec.spyOn(obj, methodName);
}; };
if (isCommonJS) exports.spyOn = spyOn; if (isCommonJS) exports.spyOn = spyOn;
   
/** /**
* Creates a Jasmine spec that will be added to the current suite. * Creates a Jasmine spec that will be added to the current suite.
* *
* // TODO: pending tests * // TODO: pending tests
* *
* @example * @example
* it('should be true', function() { * it('should be true', function() {
* expect(true).toEqual(true); * expect(true).toEqual(true);
* }); * });
* *
* @param {String} desc description of this specification * @param {String} desc description of this specification
* @param {Function} func defines the preconditions and expectations of the spec * @param {Function} func defines the preconditions and expectations of the spec
*/ */
var it = function(desc, func) { var it = function (desc, func) {
return jasmine.getEnv().it(desc, func); return jasmine.getEnv().it(desc, func);
}; };
if (isCommonJS) exports.it = it; if (isCommonJS) exports.it = it;
   
/** /**
* Creates a <em>disabled</em> Jasmine spec. * Creates a <em>disabled</em> Jasmine spec.
* *
* A convenience method that allows existing specs to be disabled temporarily during development. * A convenience method that allows existing specs to be disabled temporarily during development.
* *
* @param {String} desc description of this specification * @param {String} desc description of this specification
* @param {Function} func defines the preconditions and expectations of the spec * @param {Function} func defines the preconditions and expectations of the spec
*/ */
var xit = function(desc, func) { var xit = function (desc, func) {
return jasmine.getEnv().xit(desc, func); return jasmine.getEnv().xit(desc, func);
}; };
if (isCommonJS) exports.xit = xit; if (isCommonJS) exports.xit = xit;
   
/** /**
* Starts a chain for a Jasmine expectation. * Starts a chain for a Jasmine expectation.
* *
* It is passed an Object that is the actual value and should chain to one of the many * It is passed an Object that is the actual value and should chain to one of the many
* jasmine.Matchers functions. * jasmine.Matchers functions.
* *
* @param {Object} actual Actual value to test against and expected value * @param {Object} actual Actual value to test against and expected value
*/ */
var expect = function(actual) { var expect = function (actual) {
return jasmine.getEnv().currentSpec.expect(actual); return jasmine.getEnv().currentSpec.expect(actual);
}; };
if (isCommonJS) exports.expect = expect; if (isCommonJS) exports.expect = expect;
   
/** /**
* Defines part of a jasmine spec. Used in cominbination with waits or waitsFor in asynchrnous specs. * Defines part of a jasmine spec. Used in cominbination with waits or waitsFor in asynchrnous specs.
* *
* @param {Function} func Function that defines part of a jasmine spec. * @param {Function} func Function that defines part of a jasmine spec.
*/ */
var runs = function(func) { var runs = function (func) {
jasmine.getEnv().currentSpec.runs(func); jasmine.getEnv().currentSpec.runs(func);
}; };
if (isCommonJS) exports.runs = runs; if (isCommonJS) exports.runs = runs;
   
/** /**
* Waits a fixed time period before moving to the next block. * Waits a fixed time period before moving to the next block.
* *
* @deprecated Use waitsFor() instead * @deprecated Use waitsFor() instead
* @param {Number} timeout milliseconds to wait * @param {Number} timeout milliseconds to wait
*/ */
var waits = function(timeout) { var waits = function (timeout) {
jasmine.getEnv().currentSpec.waits(timeout); jasmine.getEnv().currentSpec.waits(timeout);
}; };
if (isCommonJS) exports.waits = waits; if (isCommonJS) exports.waits = waits;
   
/** /**
* Waits for the latchFunction to return true before proceeding to the next block. * Waits for the latchFunction to return true before proceeding to the next block.
* *
* @param {Function} latchFunction * @param {Function} latchFunction
* @param {String} optional_timeoutMessage * @param {String} optional_timeoutMessage
* @param {Number} optional_timeout * @param {Number} optional_timeout
*/ */
var waitsFor = function(latchFunction, optional_timeoutMessage, optional_timeout) { var waitsFor = function (latchFunction, optional_timeoutMessage, optional_timeout) {
jasmine.getEnv().currentSpec.waitsFor.apply(jasmine.getEnv().currentSpec, arguments); jasmine.getEnv().currentSpec.waitsFor.apply(jasmine.getEnv().currentSpec, arguments);
}; };
if (isCommonJS) exports.waitsFor = waitsFor; if (isCommonJS) exports.waitsFor = waitsFor;
   
/** /**
* A function that is called before each spec in a suite. * A function that is called before each spec in a suite.
* *
* Used for spec setup, including validating assumptions. * Used for spec setup, including validating assumptions.
* *
* @param {Function} beforeEachFunction * @param {Function} beforeEachFunction
*/ */
var beforeEach = function(beforeEachFunction) { var beforeEach = function (beforeEachFunction) {
jasmine.getEnv().beforeEach(beforeEachFunction); jasmine.getEnv().beforeEach(beforeEachFunction);
}; };
if (isCommonJS) exports.beforeEach = beforeEach; if (isCommonJS) exports.beforeEach = beforeEach;
   
/** /**
* A function that is called after each spec in a suite. * A function that is called after each spec in a suite.
* *
* Used for restoring any state that is hijacked during spec execution. * Used for restoring any state that is hijacked during spec execution.
* *
* @param {Function} afterEachFunction * @param {Function} afterEachFunction
*/ */
var afterEach = function(afterEachFunction) { var afterEach = function (afterEachFunction) {
jasmine.getEnv().afterEach(afterEachFunction); jasmine.getEnv().afterEach(afterEachFunction);
}; };
if (isCommonJS) exports.afterEach = afterEach; if (isCommonJS) exports.afterEach = afterEach;
   
/** /**
* Defines a suite of specifications. * Defines a suite of specifications.
* *
* Stores the description and all defined specs in the Jasmine environment as one suite of specs. Variables declared * Stores the description and all defined specs in the Jasmine environment as one suite of specs. Variables declared
* are accessible by calls to beforeEach, it, and afterEach. Describe blocks can be nested, allowing for specialization * are accessible by calls to beforeEach, it, and afterEach. Describe blocks can be nested, allowing for specialization
* of setup in some tests. * of setup in some tests.
* *
* @example * @example
* // TODO: a simple suite * // TODO: a simple suite
* *
* // TODO: a simple suite with a nested describe block * // TODO: a simple suite with a nested describe block
* *
* @param {String} description A string, usually the class under test. * @param {String} description A string, usually the class under test.
* @param {Function} specDefinitions function that defines several specs. * @param {Function} specDefinitions function that defines several specs.
*/ */
var describe = function(description, specDefinitions) { var describe = function (description, specDefinitions) {
return jasmine.getEnv().describe(description, specDefinitions); return jasmine.getEnv().describe(description, specDefinitions);
}; };
if (isCommonJS) exports.describe = describe; if (isCommonJS) exports.describe = describe;
   
/** /**
* Disables a suite of specifications. Used to disable some suites in a file, or files, temporarily during development. * Disables a suite of specifications. Used to disable some suites in a file, or files, temporarily during development.
* *
* @param {String} description A string, usually the class under test. * @param {String} description A string, usually the class under test.
* @param {Function} specDefinitions function that defines several specs. * @param {Function} specDefinitions function that defines several specs.
*/ */
var xdescribe = function(description, specDefinitions) { var xdescribe = function (description, specDefinitions) {
return jasmine.getEnv().xdescribe(description, specDefinitions); return jasmine.getEnv().xdescribe(description, specDefinitions);
}; };
if (isCommonJS) exports.xdescribe = xdescribe; if (isCommonJS) exports.xdescribe = xdescribe;
   
   
// Provide the XMLHttpRequest class for IE 5.x-6.x: // Provide the XMLHttpRequest class for IE 5.x-6.x:
jasmine.XmlHttpRequest = (typeof XMLHttpRequest == "undefined") ? function() { jasmine.XmlHttpRequest = (typeof XMLHttpRequest == "undefined") ? function () {
function tryIt(f) { function tryIt(f) {
try { try {
return f(); return f();
} catch(e) { } catch (e) {
} }
return null; return null;
} }
   
var xhr = tryIt(function() { var xhr = tryIt(function () {
return new ActiveXObject("Msxml2.XMLHTTP.6.0"); return new ActiveXObject("Msxml2.XMLHTTP.6.0");
}) ||  
tryIt(function() {  
return new ActiveXObject("Msxml2.XMLHTTP.3.0");  
}) || }) ||
tryIt(function() { tryIt(function () {
return new ActiveXObject("Msxml2.XMLHTTP"); return new ActiveXObject("Msxml2.XMLHTTP.3.0");
}) || }) ||
tryIt(function() { tryIt(function () {
return new ActiveXObject("Microsoft.XMLHTTP"); return new ActiveXObject("Msxml2.XMLHTTP");
}); }) ||
  tryIt(function () {
if (!xhr) throw new Error("This browser does not support XMLHttpRequest."); return new ActiveXObject("Microsoft.XMLHTTP");
  });
return xhr;  
  if (!xhr) throw new Error("This browser does not support XMLHttpRequest.");
   
  return xhr;
} : XMLHttpRequest; } : XMLHttpRequest;
/** /**
* @namespace * @namespace
*/ */
jasmine.util = {}; jasmine.util = {};
   
/** /**
* Declare that a child class inherit it's prototype from the parent class. * Declare that a child class inherit it's prototype from the parent class.
* *
* @private * @private
* @param {Function} childClass * @param {Function} childClass
* @param {Function} parentClass * @param {Function} parentClass
*/ */
jasmine.util.inherit = function(childClass, parentClass) { jasmine.util.inherit = function (childClass, parentClass) {
/** /**
* @private * @private
*/ */
var subclass = function() { var subclass = function () {
}; };
subclass.prototype = parentClass.prototype; subclass.prototype = parentClass.prototype;
childClass.prototype = new subclass(); childClass.prototype = new subclass();
}; };
   
jasmine.util.formatException = function(e) { jasmine.util.formatException = function (e) {
var lineNumber; var lineNumber;
if (e.line) { if (e.line) {
lineNumber = e.line; lineNumber = e.line;
} }
else if (e.lineNumber) { else if (e.lineNumber) {
lineNumber = e.lineNumber; lineNumber = e.lineNumber;
} }
   
var file; var file;
   
if (e.sourceURL) { if (e.sourceURL) {
file = e.sourceURL; file = e.sourceURL;
} }
else if (e.fileName) { else if (e.fileName) {
file = e.fileName; file = e.fileName;
} }
   
var message = (e.name && e.message) ? (e.name + ': ' + e.message) : e.toString(); var message = (e.name && e.message) ? (e.name + ': ' + e.message) : e.toString();
   
if (file && lineNumber) { if (file && lineNumber) {
message += ' in ' + file + ' (line ' + lineNumber + ')'; message += ' in ' + file + ' (line ' + lineNumber + ')';
} }
   
return message; return message;
}; };
   
jasmine.util.htmlEscape = function(str) { jasmine.util.htmlEscape = function (str) {
if (!str) return str; if (!str) return str;
return str.replace(/&/g, '&amp;') return str.replace(/&/g, '&amp;')
.replace(/</g, '&lt;') .replace(/</g, '&lt;')
.replace(/>/g, '&gt;'); .replace(/>/g, '&gt;');
}; };
   
jasmine.util.argsToArray = function(args) { jasmine.util.argsToArray = function (args) {
var arrayOfArgs = []; var arrayOfArgs = [];
for (var i = 0; i < args.length; i++) arrayOfArgs.push(args[i]); for (var i = 0; i < args.length; i++) arrayOfArgs.push(args[i]);
return arrayOfArgs; return arrayOfArgs;
}; };
   
jasmine.util.extend = function(destination, source) { jasmine.util.extend = function (destination, source) {
for (var property in source) destination[property] = source[property]; for (var property in source) destination[property] = source[property];
return destination; return destination;
}; };
   
/** /**
* Environment for Jasmine * Environment for Jasmine
* *
* @constructor * @constructor
*/ */
jasmine.Env = function() { jasmine.Env = function () {
this.currentSpec = null; this.currentSpec = null;
this.currentSuite = null; this.currentSuite = null;
this.currentRunner_ = new jasmine.Runner(this); this.currentRunner_ = new jasmine.Runner(this);
   
this.reporter = new jasmine.MultiReporter(); this.reporter = new jasmine.MultiReporter();
   
this.updateInterval = jasmine.DEFAULT_UPDATE_INTERVAL; this.updateInterval = jasmine.DEFAULT_UPDATE_INTERVAL;
this.defaultTimeoutInterval = jasmine.DEFAULT_TIMEOUT_INTERVAL; this.defaultTimeoutInterval = jasmine.DEFAULT_TIMEOUT_INTERVAL;
this.lastUpdate = 0; this.lastUpdate = 0;
this.specFilter = function() { this.specFilter = function () {
return true; return true;
}; };
   
this.nextSpecId_ = 0; this.nextSpecId_ = 0;
this.nextSuiteId_ = 0; this.nextSuiteId_ = 0;
this.equalityTesters_ = []; this.equalityTesters_ = [];
   
// wrap matchers // wrap matchers
this.matchersClass = function() { this.matchersClass = function () {
jasmine.Matchers.apply(this, arguments); jasmine.Matchers.apply(this, arguments);
}; };
jasmine.util.inherit(this.matchersClass, jasmine.Matchers); jasmine.util.inherit(this.matchersClass, jasmine.Matchers);
   
jasmine.Matchers.wrapInto_(jasmine.Matchers.prototype, this.matchersClass); jasmine.Matchers.wrapInto_(jasmine.Matchers.prototype, this.matchersClass);
}; };
   
   
jasmine.Env.prototype.setTimeout = jasmine.setTimeout; jasmine.Env.prototype.setTimeout = jasmine.setTimeout;
jasmine.Env.prototype.clearTimeout = jasmine.clearTimeout; jasmine.Env.prototype.clearTimeout = jasmine.clearTimeout;
jasmine.Env.prototype.setInterval = jasmine.setInterval; jasmine.Env.prototype.setInterval = jasmine.setInterval;
jasmine.Env.prototype.clearInterval = jasmine.clearInterval; jasmine.Env.prototype.clearInterval = jasmine.clearInterval;
   
/** /**
* @returns an object containing jasmine version build info, if set. * @returns an object containing jasmine version build info, if set.
*/ */
jasmine.Env.prototype.version = function () { jasmine.Env.prototype.version = function () {
if (jasmine.version_) { if (jasmine.version_) {
return jasmine.version_; return jasmine.version_;
} else { } else {
throw new Error('Version not set'); throw new Error('Version not set');
} }
}; };
   
/** /**
* @returns string containing jasmine version build info, if set. * @returns string containing jasmine version build info, if set.
*/ */
jasmine.Env.prototype.versionString = function() { jasmine.Env.prototype.versionString = function () {
if (!jasmine.version_) { if (!jasmine.version_) {
return "version unknown"; return "version unknown";
} }
   
var version = this.version(); var version = this.version();
var versionString = version.major + "." + version.minor + "." + version.build; var versionString = version.major + "." + version.minor + "." + version.build;
if (version.release_candidate) { if (version.release_candidate) {
versionString += ".rc" + version.release_candidate; versionString += ".rc" + version.release_candidate;
} }
versionString += " revision " + version.revision; versionString += " revision " + version.revision;
return versionString; return versionString;
}; };
   
/** /**
* @returns a sequential integer starting at 0 * @returns a sequential integer starting at 0
*/ */
jasmine.Env.prototype.nextSpecId = function () { jasmine.Env.prototype.nextSpecId = function () {
return this.nextSpecId_++; return this.nextSpecId_++;
}; };
   
/** /**
* @returns a sequential integer starting at 0 * @returns a sequential integer starting at 0
*/ */
jasmine.Env.prototype.nextSuiteId = function () { jasmine.Env.prototype.nextSuiteId = function () {
return this.nextSuiteId_++; return this.nextSuiteId_++;
}; };
   
/** /**
* Register a reporter to receive status updates from Jasmine. * Register a reporter to receive status updates from Jasmine.
* @param {jasmine.Reporter} reporter An object which will receive status updates. * @param {jasmine.Reporter} reporter An object which will receive status updates.
*/ */
jasmine.Env.prototype.addReporter = function(reporter) { jasmine.Env.prototype.addReporter = function (reporter) {
this.reporter.addReporter(reporter); this.reporter.addReporter(reporter);
}; };
   
jasmine.Env.prototype.execute = function() { jasmine.Env.prototype.execute = function () {
this.currentRunner_.execute(); this.currentRunner_.execute();
}; };
   
jasmine.Env.prototype.describe = function(description, specDefinitions) { jasmine.Env.prototype.describe = function (description, specDefinitions) {
var suite = new jasmine.Suite(this, description, specDefinitions, this.currentSuite); var suite = new jasmine.Suite(this, description, specDefinitions, this.currentSuite);
   
var parentSuite = this.currentSuite; var parentSuite = this.currentSuite;
if (parentSuite) { if (parentSuite) {
parentSuite.add(suite); parentSuite.add(suite);
} else { } else {
this.currentRunner_.add(suite); this.currentRunner_.add(suite);
} }
   
this.currentSuite = suite; this.currentSuite = suite;
   
var declarationError = null; var declarationError = null;
try { try {
specDefinitions.call(suite); specDefinitions.call(suite);
} catch(e) { } catch (e) {
declarationError = e; declarationError = e;
} }
   
if (declarationError) { if (declarationError) {
this.it("encountered a declaration exception", function() { this.it("encountered a declaration exception", function () {
throw declarationError; throw declarationError;
}); });
} }
   
this.currentSuite = parentSuite; this.currentSuite = parentSuite;
   
return suite; return suite;
}; };
   
jasmine.Env.prototype.beforeEach = function(beforeEachFunction) { jasmine.Env.prototype.beforeEach = function (beforeEachFunction) {
if (this.currentSuite) { if (this.currentSuite) {
this.currentSuite.beforeEach(beforeEachFunction); this.currentSuite.beforeEach(beforeEachFunction);
} else { } else {
this.currentRunner_.beforeEach(beforeEachFunction); this.currentRunner_.beforeEach(beforeEachFunction);
} }
}; };
   
jasmine.Env.prototype.currentRunner = function () { jasmine.Env.prototype.currentRunner = function () {
return this.currentRunner_; return this.currentRunner_;
}; };
   
jasmine.Env.prototype.afterEach = function(afterEachFunction) { jasmine.Env.prototype.afterEach = function (afterEachFunction) {
if (this.currentSuite) { if (this.currentSuite) {
this.currentSuite.afterEach(afterEachFunction); this.currentSuite.afterEach(afterEachFunction);
} else { } else {
this.currentRunner_.afterEach(afterEachFunction); this.currentRunner_.afterEach(afterEachFunction);
} }
   
}; };
   
jasmine.Env.prototype.xdescribe = function(desc, specDefinitions) { jasmine.Env.prototype.xdescribe = function (desc, specDefinitions) {
return { return {
execute: function() { execute: function () {
} }
}; };
}; };
   
jasmine.Env.prototype.it = function(description, func) { jasmine.Env.prototype.it = function (description, func) {
var spec = new jasmine.Spec(this, this.currentSuite, description); var spec = new jasmine.Spec(this, this.currentSuite, description);
this.currentSuite.add(spec); this.currentSuite.add(spec);
this.currentSpec = spec; this.currentSpec = spec;
   
if (func) { if (func) {
spec.runs(func); spec.runs(func);
} }
   
return spec; return spec;
}; };
   
jasmine.Env.prototype.xit = function(desc, func) { jasmine.Env.prototype.xit = function (desc, func) {
return { return {
id: this.nextSpecId(), id: this.nextSpecId(),
runs: function() { runs: function () {
} }
}; };
}; };
   
jasmine.Env.prototype.compareObjects_ = function(a, b, mismatchKeys, mismatchValues) { jasmine.Env.prototype.compareObjects_ = function (a, b, mismatchKeys, mismatchValues) {
if (a.__Jasmine_been_here_before__ === b && b.__Jasmine_been_here_before__ === a) { if (a.__Jasmine_been_here_before__ === b && b.__Jasmine_been_here_before__ === a) {
return true; return true;
} }
   
a.__Jasmine_been_here_before__ = b; a.__Jasmine_been_here_before__ = b;
b.__Jasmine_been_here_before__ = a; b.__Jasmine_been_here_before__ = a;
   
var hasKey = function(obj, keyName) { var hasKey = function (obj, keyName) {
return obj !== null && obj[keyName] !== jasmine.undefined; return obj !== null && obj[keyName] !== jasmine.undefined;
}; };
   
for (var property in b) { for (var property in b) {
if (!hasKey(a, property) && hasKey(b, property)) { if (!hasKey(a, property) && hasKey(b, property)) {
mismatchKeys.push("expected has key '" + property + "', but missing from actual."); mismatchKeys.push("expected has key '" + property + "', but missing from actual.");
} }
} }
for (property in a) { for (property in a) {
if (!hasKey(b, property) && hasKey(a, property)) { if (!hasKey(b, property) && hasKey(a, property)) {
mismatchKeys.push("expected missing key '" + property + "', but present in actual."); mismatchKeys.push("expected missing key '" + property + "', but present in actual.");
} }
} }
for (property in b) { for (property in b) {
if (property == '__Jasmine_been_here_before__') continue; if (property == '__Jasmine_been_here_before__') continue;
if (!this.equals_(a[property], b[property], mismatchKeys, mismatchValues)) { if (!this.equals_(a[property], b[property], mismatchKeys, mismatchValues)) {
mismatchValues.push("'" + property + "' was '" + (b[property] ? jasmine.util.htmlEscape(b[property].toString()) : b[property]) + "' in expected, but was '" + (a[property] ? jasmine.util.htmlEscape(a[property].toString()) : a[property]) + "' in actual."); mismatchValues.push("'" + property + "' was '" + (b[property] ? jasmine.util.htmlEscape(b[property].toString()) : b[property]) + "' in expected, but was '" + (a[property] ? jasmine.util.htmlEscape(a[property].toString()) : a[property]) + "' in actual.");
} }
} }
   
if (jasmine.isArray_(a) && jasmine.isArray_(b) && a.length != b.length) { if (jasmine.isArray_(a) && jasmine.isArray_(b) && a.length != b.length) {
mismatchValues.push("arrays were not the same length"); mismatchValues.push("arrays were not the same length");
} }
   
delete a.__Jasmine_been_here_before__; delete a.__Jasmine_been_here_before__;
delete b.__Jasmine_been_here_before__; delete b.__Jasmine_been_here_before__;
return (mismatchKeys.length === 0 && mismatchValues.length === 0); return (mismatchKeys.length === 0 && mismatchValues.length === 0);
}; };
   
jasmine.Env.prototype.equals_ = function(a, b, mismatchKeys, mismatchValues) { jasmine.Env.prototype.equals_ = function (a, b, mismatchKeys, mismatchValues) {
mismatchKeys = mismatchKeys || []; mismatchKeys = mismatchKeys || [];
mismatchValues = mismatchValues || []; mismatchValues = mismatchValues || [];
   
for (var i = 0; i < this.equalityTesters_.length; i++) { for (var i = 0; i < this.equalityTesters_.length; i++) {
var equalityTester = this.equalityTesters_[i]; var equalityTester = this.equalityTesters_[i];
var result = equalityTester(a, b, this, mismatchKeys, mismatchValues); var result = equalityTester(a, b, this, mismatchKeys, mismatchValues);
if (result !== jasmine.undefined) return result; if (result !== jasmine.undefined) return result;
} }
   
if (a === b) return true; if (a === b) return true;
   
if (a === jasmine.undefined || a === null || b === jasmine.undefined || b === null) { if (a === jasmine.undefined || a === null || b === jasmine.undefined || b === null) {
return (a == jasmine.undefined && b == jasmine.undefined); return (a == jasmine.undefined && b == jasmine.undefined);
} }
   
if (jasmine.isDomNode(a) && jasmine.isDomNode(b)) { if (jasmine.isDomNode(a) && jasmine.isDomNode(b)) {
return a === b; return a === b;
} }
   
if (a instanceof Date && b instanceof Date) { if (a instanceof Date && b instanceof Date) {
return a.getTime() == b.getTime(); return a.getTime() == b.getTime();
} }
   
if (a.jasmineMatches) { if (a.jasmineMatches) {
return a.jasmineMatches(b); return a.jasmineMatches(b);
} }
   
if (b.jasmineMatches) { if (b.jasmineMatches) {
return b.jasmineMatches(a); return b.jasmineMatches(a);
} }
   
if (a instanceof jasmine.Matchers.ObjectContaining) { if (a instanceof jasmine.Matchers.ObjectContaining) {
return a.matches(b); return a.matches(b);
} }
   
if (b instanceof jasmine.Matchers.ObjectContaining) { if (b instanceof jasmine.Matchers.ObjectContaining) {
return b.matches(a); return b.matches(a);
} }
   
if (jasmine.isString_(a) && jasmine.isString_(b)) { if (jasmine.isString_(a) && jasmine.isString_(b)) {
return (a == b); return (a == b);
} }
   
if (jasmine.isNumber_(a) && jasmine.isNumber_(b)) { if (jasmine.isNumber_(a) && jasmine.isNumber_(b)) {
return (a == b); return (a == b);
} }
   
if (typeof a === "object" && typeof b === "object") { if (typeof a === "object" && typeof b === "object") {
return this.compareObjects_(a, b, mismatchKeys, mismatchValues); return this.compareObjects_(a, b, mismatchKeys, mismatchValues);
} }
   
//Straight check //Straight check
return (a === b); return (a === b);
}; };
   
jasmine.Env.prototype.contains_ = function(haystack, needle) { jasmine.Env.prototype.contains_ = function (haystack, needle) {
if (jasmine.isArray_(haystack)) { if (jasmine.isArray_(haystack)) {
for (var i = 0; i < haystack.length; i++) { for (var i = 0; i < haystack.length; i++) {
if (this.equals_(haystack[i], needle)) return true; if (this.equals_(haystack[i], needle)) return true;
} }
return false; return false;
} }
return haystack.indexOf(needle) >= 0; return haystack.indexOf(needle) >= 0;
}; };
   
jasmine.Env.prototype.addEqualityTester = function(equalityTester) { jasmine.Env.prototype.addEqualityTester = function (equalityTester) {
this.equalityTesters_.push(equalityTester); this.equalityTesters_.push(equalityTester);
}; };
/** No-op base class for Jasmine reporters. /** No-op base class for Jasmine reporters.
* *
* @constructor * @constructor
*/ */
jasmine.Reporter = function() { jasmine.Reporter = function () {
}; };
   
//noinspection JSUnusedLocalSymbols //noinspection JSUnusedLocalSymbols
jasmine.Reporter.prototype.reportRunnerStarting = function(runner) { jasmine.Reporter.prototype.reportRunnerStarting = function (runner) {
}; };
   
//noinspection JSUnusedLocalSymbols //noinspection JSUnusedLocalSymbols
jasmine.Reporter.prototype.reportRunnerResults = function(runner) { jasmine.Reporter.prototype.reportRunnerResults = function (runner) {
}; };
   
//noinspection JSUnusedLocalSymbols //noinspection JSUnusedLocalSymbols
jasmine.Reporter.prototype.reportSuiteResults = function(suite) { jasmine.Reporter.prototype.reportSuiteResults = function (suite) {
}; };
   
//noinspection JSUnusedLocalSymbols //noinspection JSUnusedLocalSymbols
jasmine.Reporter.prototype.reportSpecStarting = function(spec) { jasmine.Reporter.prototype.reportSpecStarting = function (spec) {
}; };
   
//noinspection JSUnusedLocalSymbols //noinspection JSUnusedLocalSymbols
jasmine.Reporter.prototype.reportSpecResults = function(spec) { jasmine.Reporter.prototype.reportSpecResults = function (spec) {
}; };
   
//noinspection JSUnusedLocalSymbols //noinspection JSUnusedLocalSymbols
jasmine.Reporter.prototype.log = function(str) { jasmine.Reporter.prototype.log = function (str) {
}; };
   
/** /**
* Blocks are functions with executable code that make up a spec. * Blocks are functions with executable code that make up a spec.
* *
* @constructor * @constructor
* @param {jasmine.Env} env * @param {jasmine.Env} env
* @param {Function} func * @param {Function} func
* @param {jasmine.Spec} spec * @param {jasmine.Spec} spec
*/ */
jasmine.Block = function(env, func, spec) { jasmine.Block = function (env, func, spec) {
this.env = env; this.env = env;
this.func = func; this.func = func;
this.spec = spec; this.spec = spec;
}; };
   
jasmine.Block.prototype.execute = function(onComplete) { jasmine.Block.prototype.execute = function (onComplete) {
try { try {
this.func.apply(this.spec); this.func.apply(this.spec);
} catch (e) { } catch (e) {
this.spec.fail(e); this.spec.fail(e);
} }
onComplete(); onComplete();
}; };
/** JavaScript API reporter. /** JavaScript API reporter.
* *
* @constructor * @constructor
*/ */
jasmine.JsApiReporter = function() { jasmine.JsApiReporter = function () {
this.started = false; this.started = false;
this.finished = false; this.finished = false;
this.suites_ = []; this.suites_ = [];
this.results_ = {}; this.results_ = {};
}; };
   
jasmine.JsApiReporter.prototype.reportRunnerStarting = function(runner) { jasmine.JsApiReporter.prototype.reportRunnerStarting = function (runner) {
this.started = true; this.started = true;
var suites = runner.topLevelSuites(); var suites = runner.topLevelSuites();
for (var i = 0; i < suites.length; i++) { for (var i = 0; i < suites.length; i++) {
var suite = suites[i]; var suite = suites[i];
this.suites_.push(this.summarize_(suite)); this.suites_.push(this.summarize_(suite));
} }
}; };
   
jasmine.JsApiReporter.prototype.suites = function() { jasmine.JsApiReporter.prototype.suites = function () {
return this.suites_; return this.suites_;
}; };
   
jasmine.JsApiReporter.prototype.summarize_ = function(suiteOrSpec) { jasmine.JsApiReporter.prototype.summarize_ = function (suiteOrSpec) {
var isSuite = suiteOrSpec instanceof jasmine.Suite; var isSuite = suiteOrSpec instanceof jasmine.Suite;
var summary = { var summary = {
id: suiteOrSpec.id, id: suiteOrSpec.id,
name: suiteOrSpec.description, name: suiteOrSpec.description,
type: isSuite ? 'suite' : 'spec', type: isSuite ? 'suite' : 'spec',
children: [] children: []
}; };
   
if (isSuite) { if (isSuite) {
var children = suiteOrSpec.children(); var children = suiteOrSpec.children();
for (var i = 0; i < children.length; i++) { for (var i = 0; i < children.length; i++) {
summary.children.push(this.summarize_(children[i])); summary.children.push(this.summarize_(children[i]));
} }
} }
return summary; return summary;
}; };
   
jasmine.JsApiReporter.prototype.results = function() { jasmine.JsApiReporter.prototype.results = function () {
return this.results_; return this.results_;
}; };
   
jasmine.JsApiReporter.prototype.resultsForSpec = function(specId) { jasmine.JsApiReporter.prototype.resultsForSpec = function (specId) {
return this.results_[specId]; return this.results_[specId];
}; };
   
//noinspection JSUnusedLocalSymbols //noinspection JSUnusedLocalSymbols
jasmine.JsApiReporter.prototype.reportRunnerResults = function(runner) { jasmine.JsApiReporter.prototype.reportRunnerResults = function (runner) {
this.finished = true; this.finished = true;
}; };
   
//noinspection JSUnusedLocalSymbols //noinspection JSUnusedLocalSymbols
jasmine.JsApiReporter.prototype.reportSuiteResults = function(suite) { jasmine.JsApiReporter.prototype.reportSuiteResults = function (suite) {
}; };
   
//noinspection JSUnusedLocalSymbols //noinspection JSUnusedLocalSymbols
jasmine.JsApiReporter.prototype.reportSpecResults = function(spec) { jasmine.JsApiReporter.prototype.reportSpecResults = function (spec) {
this.results_[spec.id] = { this.results_[spec.id] = {
messages: spec.results().getItems(), messages: spec.results().getItems(),
result: spec.results().failedCount > 0 ? "failed" : "passed" result: spec.results().failedCount > 0 ? "failed" : "passed"
}; };
}; };
   
//noinspection JSUnusedLocalSymbols //noinspection JSUnusedLocalSymbols
jasmine.JsApiReporter.prototype.log = function(str) { jasmine.JsApiReporter.prototype.log = function (str) {
}; };
   
jasmine.JsApiReporter.prototype.resultsForSpecs = function(specIds){ jasmine.JsApiReporter.prototype.resultsForSpecs = function (specIds) {
var results = {}; var results = {};
for (var i = 0; i < specIds.length; i++) { for (var i = 0; i < specIds.length; i++) {
var specId = specIds[i]; var specId = specIds[i];
results[specId] = this.summarizeResult_(this.results_[specId]); results[specId] = this.summarizeResult_(this.results_[specId]);
} }
return results; return results;
}; };
   
jasmine.JsApiReporter.prototype.summarizeResult_ = function(result){ jasmine.JsApiReporter.prototype.summarizeResult_ = function (result) {
var summaryMessages = []; var summaryMessages = [];
var messagesLength = result.messages.length; var messagesLength = result.messages.length;
for (var messageIndex = 0; messageIndex < messagesLength; messageIndex++) { for (var messageIndex = 0; messageIndex < messagesLength; messageIndex++) {
var resultMessage = result.messages[messageIndex]; var resultMessage = result.messages[messageIndex];
summaryMessages.push({ summaryMessages.push({
text: resultMessage.type == 'log' ? resultMessage.toString() : jasmine.undefined, text: resultMessage.type == 'log' ? resultMessage.toString() : jasmine.undefined,
passed: resultMessage.passed ? resultMessage.passed() : true, passed: resultMessage.passed ? resultMessage.passed() : true,
type: resultMessage.type, type: resultMessage.type,
message: resultMessage.message, message: resultMessage.message,
trace: { trace: {
stack: resultMessage.passed && !resultMessage.passed() ? resultMessage.trace.stack : jasmine.undefined stack: resultMessage.passed && !resultMessage.passed() ? resultMessage.trace.stack : jasmine.undefined
} }
}); });
} }
   
return { return {
result : result.result, result: result.result,
messages : summaryMessages messages: summaryMessages
}; };
}; };
   
/** /**
* @constructor * @constructor
* @param {jasmine.Env} env * @param {jasmine.Env} env
* @param actual * @param actual
* @param {jasmine.Spec} spec * @param {jasmine.Spec} spec
*/ */
jasmine.Matchers = function(env, actual, spec, opt_isNot) { jasmine.Matchers = function (env, actual, spec, opt_isNot) {
this.env = env; this.env = env;
this.actual = actual; this.actual = actual;
this.spec = spec; this.spec = spec;
this.isNot = opt_isNot || false; this.isNot = opt_isNot || false;
this.reportWasCalled_ = false; this.reportWasCalled_ = false;
}; };
   
// todo: @deprecated as of Jasmine 0.11, remove soon [xw] // todo: @deprecated as of Jasmine 0.11, remove soon [xw]
jasmine.Matchers.pp = function(str) { jasmine.Matchers.pp = function (str) {
throw new Error("jasmine.Matchers.pp() is no longer supported, please use jasmine.pp() instead!"); throw new Error("jasmine.Matchers.pp() is no longer supported, please use jasmine.pp() instead!");
}; };
   
// todo: @deprecated Deprecated as of Jasmine 0.10. Rewrite your custom matchers to return true or false. [xw] // todo: @deprecated Deprecated as of Jasmine 0.10. Rewrite your custom matchers to return true or false. [xw]
jasmine.Matchers.prototype.report = function(result, failing_message, details) { jasmine.Matchers.prototype.report = function (result, failing_message, details) {
throw new Error("As of jasmine 0.11, custom matchers must be implemented differently -- please see jasmine docs"); throw new Error("As of jasmine 0.11, custom matchers must be implemented differently -- please see jasmine docs");
}; };
   
jasmine.Matchers.wrapInto_ = function(prototype, matchersClass) { jasmine.Matchers.wrapInto_ = function (prototype, matchersClass) {
for (var methodName in prototype) { for (var methodName in prototype) {
if (methodName == 'report') continue; if (methodName == 'report') continue;
var orig = prototype[methodName]; var orig = prototype[methodName];
matchersClass.prototype[methodName] = jasmine.Matchers.matcherFn_(methodName, orig); matchersClass.prototype[methodName] = jasmine.Matchers.matcherFn_(methodName, orig);
} }
}; };
   
jasmine.Matchers.matcherFn_ = function(matcherName, matcherFunction) { jasmine.Matchers.matcherFn_ = function (matcherName, matcherFunction) {
return function() { return function () {
var matcherArgs = jasmine.util.argsToArray(arguments); var matcherArgs = jasmine.util.argsToArray(arguments);
var result = matcherFunction.apply(this, arguments); var result = matcherFunction.apply(this, arguments);
   
if (this.isNot) { if (this.isNot) {
result = !result; result = !result;
} }
   
if (this.reportWasCalled_) return result; if (this.reportWasCalled_) return result;
   
var message; var message;
if (!result) { if (!result) {
if (this.message) { if (this.message) {
message = this.message.apply(this, arguments); message = this.message.apply(this, arguments);
if (jasmine.isArray_(message)) { if (jasmine.isArray_(message)) {
message = message[this.isNot ? 1 : 0]; message = message[this.isNot ? 1 : 0];
} }
} else { } else {
var englishyPredicate = matcherName.replace(/[A-Z]/g, function(s) { return ' ' + s.toLowerCase(); }); var englishyPredicate = matcherName.replace(/[A-Z]/g, function (s) {
message = "Expected " + jasmine.pp(this.actual) + (this.isNot ? " not " : " ") + englishyPredicate; return ' ' + s.toLowerCase();
if (matcherArgs.length > 0) { });
for (var i = 0; i < matcherArgs.length; i++) { message = "Expected " + jasmine.pp(this.actual) + (this.isNot ? " not " : " ") + englishyPredicate;
if (i > 0) message += ","; if (matcherArgs.length > 0) {
message += " " + jasmine.pp(matcherArgs[i]); for (var i = 0; i < matcherArgs.length; i++) {
} if (i > 0) message += ",";
} message += " " + jasmine.pp(matcherArgs[i]);
message += "."; }
} }
} message += ".";
var expectationResult = new jasmine.ExpectationResult({ }
matcherName: matcherName, }
passed: result, var expectationResult = new jasmine.ExpectationResult({
expected: matcherArgs.length > 1 ? matcherArgs : matcherArgs[0], matcherName: matcherName,
actual: this.actual, passed: result,
message: message expected: matcherArgs.length > 1 ? matcherArgs : matcherArgs[0],
}); actual: this.actual,
this.spec.addMatcherResult(expectationResult); message: message
return jasmine.undefined; });
}; this.spec.addMatcherResult(expectationResult);
}; return jasmine.undefined;
  };
  };
   
   
/** /**
* toBe: compares the actual to the expected using === * toBe: compares the actual to the expected using ===
* @param expected * @param expected
*/ */
jasmine.Matchers.prototype.toBe = function(expected) { jasmine.Matchers.prototype.toBe = function (expected) {
return this.actual === expected; return this.actual === expected;
}; };
   
/** /**
* toNotBe: compares the actual to the expected using !== * toNotBe: compares the actual to the expected using !==
* @param expected * @param expected
* @deprecated as of 1.0. Use not.toBe() instead. * @deprecated as of 1.0. Use not.toBe() instead.
*/ */
jasmine.Matchers.prototype.toNotBe = function(expected) { jasmine.Matchers.prototype.toNotBe = function (expected) {
return this.actual !== expected; return this.actual !== expected;
}; };
   
/** /**
* toEqual: compares the actual to the expected using common sense equality. Handles Objects, Arrays, etc. * toEqual: compares the actual to the expected using common sense equality. Handles Objects, Arrays, etc.
* *
* @param expected * @param expected
*/ */
jasmine.Matchers.prototype.toEqual = function(expected) { jasmine.Matchers.prototype.toEqual = function (expected) {
return this.env.equals_(this.actual, expected); return this.env.equals_(this.actual, expected);
}; };
   
/** /**
* toNotEqual: compares the actual to the expected using the ! of jasmine.Matchers.toEqual * toNotEqual: compares the actual to the expected using the ! of jasmine.Matchers.toEqual
* @param expected * @param expected
* @deprecated as of 1.0. Use not.toEqual() instead. * @deprecated as of 1.0. Use not.toEqual() instead.
*/ */
jasmine.Matchers.prototype.toNotEqual = function(expected) { jasmine.Matchers.prototype.toNotEqual = function (expected) {
return !this.env.equals_(this.actual, expected); return !this.env.equals_(this.actual, expected);
}; };
   
/** /**
* Matcher that compares the actual to the expected using a regular expression. Constructs a RegExp, so takes * Matcher that compares the actual to the expected using a regular expression. Constructs a RegExp, so takes
* a pattern or a String. * a pattern or a String.
* *
* @param expected * @param expected
*/ */
jasmine.Matchers.prototype.toMatch = function(expected) { jasmine.Matchers.prototype.toMatch = function (expected) {
return new RegExp(expected).test(this.actual); return new RegExp(expected).test(this.actual);
}; };
   
/** /**
* Matcher that compares the actual to the expected using the boolean inverse of jasmine.Matchers.toMatch * Matcher that compares the actual to the expected using the boolean inverse of jasmine.Matchers.toMatch
* @param expected * @param expected
* @deprecated as of 1.0. Use not.toMatch() instead. * @deprecated as of 1.0. Use not.toMatch() instead.
*/ */
jasmine.Matchers.prototype.toNotMatch = function(expected) { jasmine.Matchers.prototype.toNotMatch = function (expected) {
return !(new RegExp(expected).test(this.actual)); return !(new RegExp(expected).test(this.actual));
}; };
   
/** /**
* Matcher that compares the actual to jasmine.undefined. * Matcher that compares the actual to jasmine.undefined.
*/ */
jasmine.Matchers.prototype.toBeDefined = function() { jasmine.Matchers.prototype.toBeDefined = function () {
return (this.actual !== jasmine.undefined); return (this.actual !== jasmine.undefined);
}; };
   
/** /**
* Matcher that compares the actual to jasmine.undefined. * Matcher that compares the actual to jasmine.undefined.
*/ */
jasmine.Matchers.prototype.toBeUndefined = function() { jasmine.Matchers.prototype.toBeUndefined = function () {
return (this.actual === jasmine.undefined); return (this.actual === jasmine.undefined);
}; };
   
/** /**
* Matcher that compares the actual to null. * Matcher that compares the actual to null.
*/ */
jasmine.Matchers.prototype.toBeNull = function() { jasmine.Matchers.prototype.toBeNull = function () {
return (this.actual === null); return (this.actual === null);
}; };
   
/** /**
* Matcher that boolean not-nots the actual. * Matcher that boolean not-nots the actual.
*/ */
jasmine.Matchers.prototype.toBeTruthy = function() { jasmine.Matchers.prototype.toBeTruthy = function () {
return !!this.actual; return !!this.actual;
}; };
   
   
/** /**
* Matcher that boolean nots the actual. * Matcher that boolean nots the actual.
*/ */
jasmine.Matchers.prototype.toBeFalsy = function() { jasmine.Matchers.prototype.toBeFalsy = function () {
return !this.actual; return !this.actual;
}; };
   
   
/** /**
* Matcher that checks to see if the actual, a Jasmine spy, was called. * Matcher that checks to see if the actual, a Jasmine spy, was called.
*/ */
jasmine.Matchers.prototype.toHaveBeenCalled = function() { jasmine.Matchers.prototype.toHaveBeenCalled = function () {
if (arguments.length > 0) { if (arguments.length > 0) {
throw new Error('toHaveBeenCalled does not take arguments, use toHaveBeenCalledWith'); throw new Error('toHaveBeenCalled does not take arguments, use toHaveBeenCalledWith');
} }
   
if (!jasmine.isSpy(this.actual)) { if (!jasmine.isSpy(this.actual)) {
throw new Error('Expected a spy, but got ' + jasmine.pp(this.actual) + '.'); throw new Error('Expected a spy, but got ' + jasmine.pp(this.actual) + '.');
} }
   
this.message = function() { this.message = function () {
return [ return [
"Expected spy " + this.actual.identity + " to have been called.", "Expected spy " + this.actual.identity + " to have been called.",
"Expected spy " + this.actual.identity + " not to have been called." "Expected spy " + this.actual.identity + " not to have been called."
]; ];
}; };
   
return this.actual.wasCalled; return this.actual.wasCalled;
}; };
   
/** @deprecated Use expect(xxx).toHaveBeenCalled() instead */ /** @deprecated Use expect(xxx).toHaveBeenCalled() instead */
jasmine.Matchers.prototype.wasCalled = jasmine.Matchers.prototype.toHaveBeenCalled; jasmine.Matchers.prototype.wasCalled = jasmine.Matchers.prototype.toHaveBeenCalled;
   
/** /**
* Matcher that checks to see if the actual, a Jasmine spy, was not called. * Matcher that checks to see if the actual, a Jasmine spy, was not called.
* *
* @deprecated Use expect(xxx).not.toHaveBeenCalled() instead * @deprecated Use expect(xxx).not.toHaveBeenCalled() instead
*/ */
jasmine.Matchers.prototype.wasNotCalled = function() { jasmine.Matchers.prototype.wasNotCalled = function () {
if (arguments.length > 0) { if (arguments.length > 0) {
throw new Error('wasNotCalled does not take arguments'); throw new Error('wasNotCalled does not take arguments');
} }
   
if (!jasmine.isSpy(this.actual)) { if (!jasmine.isSpy(this.actual)) {
throw new Error('Expected a spy, but got ' + jasmine.pp(this.actual) + '.'); throw new Error('Expected a spy, but got ' + jasmine.pp(this.actual) + '.');
} }
   
this.message = function() { this.message = function () {
return [ return [
"Expected spy " + this.actual.identity + " to not have been called.", "Expected spy " + this.actual.identity + " to not have been called.",
"Expected spy " + this.actual.identity + " to have been called." "Expected spy " + this.actual.identity + " to have been called."
]; ];
}; };
   
return !this.actual.wasCalled; return !this.actual.wasCalled;
}; };
   
/** /**
* Matcher that checks to see if the actual, a Jasmine spy, was called with a set of parameters. * Matcher that checks to see if the actual, a Jasmine spy, was called with a set of parameters.
* *
* @example * @example
* *
*/ */
jasmine.Matchers.prototype.toHaveBeenCalledWith = function() { jasmine.Matchers.prototype.toHaveBeenCalledWith = function () {
var expectedArgs = jasmine.util.argsToArray(arguments); var expectedArgs = jasmine.util.argsToArray(arguments);
if (!jasmine.isSpy(this.actual)) { if (!jasmine.isSpy(this.actual)) {
throw new Error('Expected a spy, but got ' + jasmine.pp(this.actual) + '.'); throw new Error('Expected a spy, but got ' + jasmine.pp(this.actual) + '.');
} }
this.message = function() { this.message = function () {
if (this.actual.callCount === 0) { if (this.actual.callCount === 0) {
// todo: what should the failure message for .not.toHaveBeenCalledWith() be? is this right? test better. [xw] // todo: what should the failure message for .not.toHaveBeenCalledWith() be? is this right? test better. [xw]
return [ return [
"Expected spy " + this.actual.identity + " to have been called with " + jasmine.pp(expectedArgs) + " but it was never called.", "Expected spy " + this.actual.identity + " to have been called with " + jasmine.pp(expectedArgs) + " but it was never called.",
"Expected spy " + this.actual.identity + " not to have been called with " + jasmine.pp(expectedArgs) + " but it was." "Expected spy " + this.actual.identity + " not to have been called with " + jasmine.pp(expectedArgs) + " but it was."
]; ];
} else { } else {
return [ return [
"Expected spy " + this.actual.identity + " to have been called with " + jasmine.pp(expectedArgs) + " but was called with " + jasmine.pp(this.actual.argsForCall), "Expected spy " + this.actual.identity + " to have been called with " + jasmine.pp(expectedArgs) + " but was called with " + jasmine.pp(this.actual.argsForCall),
"Expected spy " + this.actual.identity + " not to have been called with " + jasmine.pp(expectedArgs) + " but was called with " + jasmine.pp(this.actual.argsForCall) "Expected spy " + this.actual.identity + " not to have been called with " + jasmine.pp(expectedArgs) + " but was called with " + jasmine.pp(this.actual.argsForCall)
]; ];
} }
}; };
   
return this.env.contains_(this.actual.argsForCall, expectedArgs); return this.env.contains_(this.actual.argsForCall, expectedArgs);
}; };
   
/** @deprecated Use expect(xxx).toHaveBeenCalledWith() instead */ /** @deprecated Use expect(xxx).toHaveBeenCalledWith() instead */
jasmine.Matchers.prototype.wasCalledWith = jasmine.Matchers.prototype.toHaveBeenCalledWith; jasmine.Matchers.prototype.wasCalledWith = jasmine.Matchers.prototype.toHaveBeenCalledWith;
   
/** @deprecated Use expect(xxx).not.toHaveBeenCalledWith() instead */ /** @deprecated Use expect(xxx).not.toHaveBeenCalledWith() instead */
jasmine.Matchers.prototype.wasNotCalledWith = function() { jasmine.Matchers.prototype.wasNotCalledWith = function () {
var expectedArgs = jasmine.util.argsToArray(arguments); var expectedArgs = jasmine.util.argsToArray(arguments);
if (!jasmine.isSpy(this.actual)) { if (!jasmine.isSpy(this.actual)) {
throw new Error('Expected a spy, but got ' + jasmine.pp(this.actual) + '.'); throw new Error('Expected a spy, but got ' + jasmine.pp(this.actual) + '.');
} }
   
this.message = function() { this.message = function () {
return [ return [
"Expected spy not to have been called with " + jasmine.pp(expectedArgs) + " but it was", "Expected spy not to have been called with " + jasmine.pp(expectedArgs) + " but it was",
"Expected spy to have been called with " + jasmine.pp(expectedArgs) + " but it was" "Expected spy to have been called with " + jasmine.pp(expectedArgs) + " but it was"
]; ];
}; };
   
return !this.env.contains_(this.actual.argsForCall, expectedArgs); return !this.env.contains_(this.actual.argsForCall, expectedArgs);
}; };
   
/** /**
* Matcher that checks that the expected item is an element in the actual Array. * Matcher that checks that the expected item is an element in the actual Array.
* *
* @param {Object} expected * @param {Object} expected
*/ */
jasmine.Matchers.prototype.toContain = function(expected) { jasmine.Matchers.prototype.toContain = function (expected) {
return this.env.contains_(this.actual, expected); return this.env.contains_(this.actual, expected);
}; };
   
/** /**
* Matcher that checks that the expected item is NOT an element in the actual Array. * Matcher that checks that the expected item is NOT an element in the actual Array.
* *
* @param {Object} expected * @param {Object} expected
* @deprecated as of 1.0. Use not.toContain() instead. * @deprecated as of 1.0. Use not.toContain() instead.
*/ */
jasmine.Matchers.prototype.toNotContain = function(expected) { jasmine.Matchers.prototype.toNotContain = function (expected) {
return !this.env.contains_(this.actual, expected); return !this.env.contains_(this.actual, expected);
}; };
   
jasmine.Matchers.prototype.toBeLessThan = function(expected) { jasmine.Matchers.prototype.toBeLessThan = function (expected) {
return this.actual < expected; return this.actual < expected;
}; };
   
jasmine.Matchers.prototype.toBeGreaterThan = function(expected) { jasmine.Matchers.prototype.toBeGreaterThan = function (expected) {
return this.actual > expected; return this.actual > expected;
}; };
   
/** /**
* Matcher that checks that the expected item is equal to the actual item * Matcher that checks that the expected item is equal to the actual item
* up to a given level of decimal precision (default 2). * up to a given level of decimal precision (default 2).
* *
* @param {Number} expected * @param {Number} expected
* @param {Number} precision * @param {Number} precision
*/ */
jasmine.Matchers.prototype.toBeCloseTo = function(expected, precision) { jasmine.Matchers.prototype.toBeCloseTo = function (expected, precision) {
if (!(precision === 0)) { if (!(precision === 0)) {
precision = precision || 2; precision = precision || 2;
} }
var multiplier = Math.pow(10, precision); var multiplier = Math.pow(10, precision);
var actual = Math.round(this.actual * multiplier); var actual = Math.round(this.actual * multiplier);
expected = Math.round(expected * multiplier); expected = Math.round(expected * multiplier);
return expected == actual; return expected == actual;
}; };
   
/** /**
* Matcher that checks that the expected exception was thrown by the actual. * Matcher that checks that the expected exception was thrown by the actual.
* *
* @param {String} expected * @param {String} expected
*/ */
jasmine.Matchers.prototype.toThrow = function(expected) { jasmine.Matchers.prototype.toThrow = function (expected) {
var result = false; var result = false;
var exception; var exception;
if (typeof this.actual != 'function') { if (typeof this.actual != 'function') {
throw new Error('Actual is not a function'); throw new Error('Actual is not a function');
} }
try { try {
this.actual(); this.actual();
} catch (e) { } catch (e) {
exception = e; exception = e;
} }
if (exception) { if (exception) {
result = (expected === jasmine.undefined || this.env.equals_(exception.message || exception, expected.message || expected)); result = (expected === jasmine.undefined || this.env.equals_(exception.message || exception, expected.message || expected));
} }
   
var not = this.isNot ? "not " : ""; var not = this.isNot ? "not " : "";
   
this.message = function() { this.message = function () {
if (exception && (expected === jasmine.undefined || !this.env.equals_(exception.message || exception, expected.message || expected))) { if (exception && (expected === jasmine.undefined || !this.env.equals_(exception.message || exception, expected.message || expected))) {
return ["Expected function " + not + "to throw", expected ? expected.message || expected : "an exception", ", but it threw", exception.message || exception].join(' '); return ["Expected function " + not + "to throw", expected ? expected.message || expected : "an exception", ", but it threw", exception.message || exception].join(' ');
} else { } else {
return "Expected function to throw an exception."; return "Expected function to throw an exception.";
} }
}; };
   
return result; return result;
}; };
   
jasmine.Matchers.Any = function(expectedClass) { jasmine.Matchers.Any = function (expectedClass) {
this.expectedClass = expectedClass; this.expectedClass = expectedClass;
}; };
   
jasmine.Matchers.Any.prototype.jasmineMatches = function(other) { jasmine.Matchers.Any.prototype.jasmineMatches = function (other) {
if (this.expectedClass == String) { if (this.expectedClass == String) {
return typeof other == 'string' || other instanceof String; return typeof other == 'string' || other instanceof String;
} }
   
if (this.expectedClass == Number) { if (this.expectedClass == Number) {
return typeof other == 'number' || other instanceof Number; return typeof other == 'number' || other instanceof Number;
} }
   
if (this.expectedClass == Function) { if (this.expectedClass == Function) {
return typeof other == 'function' || other instanceof Function; return typeof other == 'function' || other instanceof Function;
} }
   
if (this.expectedClass == Object) { if (this.expectedClass == Object) {
return typeof other == 'object'; return typeof other == 'object';
} }
   
return other instanceof this.expectedClass; return other instanceof this.expectedClass;
}; };
   
jasmine.Matchers.Any.prototype.jasmineToString = function() { jasmine.Matchers.Any.prototype.jasmineToString = function () {
return '<jasmine.any(' + this.expectedClass + ')>'; return '<jasmine.any(' + this.expectedClass + ')>';
}; };
   
jasmine.Matchers.ObjectContaining = function (sample) { jasmine.Matchers.ObjectContaining = function (sample) {
this.sample = sample; this.sample = sample;
}; };
   
jasmine.Matchers.ObjectContaining.prototype.jasmineMatches = function(other, mismatchKeys, mismatchValues) { jasmine.Matchers.ObjectContaining.prototype.jasmineMatches = function (other, mismatchKeys, mismatchValues) {
mismatchKeys = mismatchKeys || []; mismatchKeys = mismatchKeys || [];
mismatchValues = mismatchValues || []; mismatchValues = mismatchValues || [];
   
var env = jasmine.getEnv(); var env = jasmine.getEnv();
   
var hasKey = function(obj, keyName) { var hasKey = function (obj, keyName) {
return obj != null && obj[keyName] !== jasmine.undefined; return obj != null && obj[keyName] !== jasmine.undefined;
}; };
   
for (var property in this.sample) { for (var property in this.sample) {
if (!hasKey(other, property) && hasKey(this.sample, property)) { if (!hasKey(other, property) && hasKey(this.sample, property)) {
mismatchKeys.push("expected has key '" + property + "', but missing from actual."); mismatchKeys.push("expected has key '" + property + "', but missing from actual.");
} }
else if (!env.equals_(this.sample[property], other[property], mismatchKeys, mismatchValues)) { else if (!env.equals_(this.sample[property], other[property], mismatchKeys, mismatchValues)) {
mismatchValues.push("'" + property + "' was '" + (other[property] ? jasmine.util.htmlEscape(other[property].toString()) : other[property]) + "' in expected, but was '" + (this.sample[property] ? jasmine.util.htmlEscape(this.sample[property].toString()) : this.sample[property]) + "' in actual."); mismatchValues.push("'" + property + "' was '" + (other[property] ? jasmine.util.htmlEscape(other[property].toString()) : other[property]) + "' in expected, but was '" + (this.sample[property] ? jasmine.util.htmlEscape(this.sample[property].toString()) : this.sample[property]) + "' in actual.");
} }
} }
   
return (mismatchKeys.length === 0 && mismatchValues.length === 0); return (mismatchKeys.length === 0 && mismatchValues.length === 0);
}; };
   
jasmine.Matchers.ObjectContaining.prototype.jasmineToString = function () { jasmine.Matchers.ObjectContaining.prototype.jasmineToString = function () {
return "<jasmine.objectContaining(" + jasmine.pp(this.sample) + ")>"; return "<jasmine.objectContaining(" + jasmine.pp(this.sample) + ")>";
}; };
// Mock setTimeout, clearTimeout // Mock setTimeout, clearTimeout
// Contributed by Pivotal Computer Systems, www.pivotalsf.com // Contributed by Pivotal Computer Systems, www.pivotalsf.com
   
jasmine.FakeTimer = function() { jasmine.FakeTimer = function () {
this.reset(); this.reset();
   
var self = this; var self = this;
self.setTimeout = function(funcToCall, millis) { self.setTimeout = function (funcToCall, millis) {
self.timeoutsMade++; self.timeoutsMade++;
self.scheduleFunction(self.timeoutsMade, funcToCall, millis, false); self.scheduleFunction(self.timeoutsMade, funcToCall, millis, false);
return self.timeoutsMade; return self.timeoutsMade;
}; };
   
self.setInterval = function(funcToCall, millis) { self.setInterval = function (funcToCall, millis) {
self.timeoutsMade++; self.timeoutsMade++;
self.scheduleFunction(self.timeoutsMade, funcToCall, millis, true); self.scheduleFunction(self.timeoutsMade, funcToCall, millis, true);
return self.timeoutsMade; return self.timeoutsMade;
}; };
   
self.clearTimeout = function(timeoutKey) { self.clearTimeout = function (timeoutKey) {
self.scheduledFunctions[timeoutKey] = jasmine.undefined; self.scheduledFunctions[timeoutKey] = jasmine.undefined;
}; };
   
self.clearInterval = function(timeoutKey) { self.clearInterval = function (timeoutKey) {
self.scheduledFunctions[timeoutKey] = jasmine.undefined; self.scheduledFunctions[timeoutKey] = jasmine.undefined;
}; };
   
}; };
   
jasmine.FakeTimer.prototype.reset = function() { jasmine.FakeTimer.prototype.reset = function () {
this.timeoutsMade = 0; this.timeoutsMade = 0;
this.scheduledFunctions = {}; this.scheduledFunctions = {};
this.nowMillis = 0; this.nowMillis = 0;
}; };
   
jasmine.FakeTimer.prototype.tick = function(millis) { jasmine.FakeTimer.prototype.tick = function (millis) {
var oldMillis = this.nowMillis; var oldMillis = this.nowMillis;
var newMillis = oldMillis + millis; var newMillis = oldMillis + millis;
this.runFunctionsWithinRange(oldMillis, newMillis); this.runFunctionsWithinRange(oldMillis, newMillis);
this.nowMillis = newMillis; this.nowMillis = newMillis;
}; };
   
jasmine.FakeTimer.prototype.runFunctionsWithinRange = function(oldMillis, nowMillis) { jasmine.FakeTimer.prototype.runFunctionsWithinRange = function (oldMillis, nowMillis) {
var scheduledFunc; var scheduledFunc;
var funcsToRun = []; var funcsToRun = [];
for (var timeoutKey in this.scheduledFunctions) { for (var timeoutKey in this.scheduledFunctions) {
scheduledFunc = this.scheduledFunctions[timeoutKey]; scheduledFunc = this.scheduledFunctions[timeoutKey];
if (scheduledFunc != jasmine.undefined && if (scheduledFunc != jasmine.undefined &&
scheduledFunc.runAtMillis >= oldMillis && scheduledFunc.runAtMillis >= oldMillis &&
scheduledFunc.runAtMillis <= nowMillis) { scheduledFunc.runAtMillis <= nowMillis) {
funcsToRun.push(scheduledFunc); funcsToRun.push(scheduledFunc);
this.scheduledFunctions[timeoutKey] = jasmine.undefined; this.scheduledFunctions[timeoutKey] = jasmine.undefined;
} }
} }
   
if (funcsToRun.length > 0) { if (funcsToRun.length > 0) {
funcsToRun.sort(function(a, b) { funcsToRun.sort(function (a, b) {
return a.runAtMillis - b.runAtMillis; return a.runAtMillis - b.runAtMillis;
}); });
for (var i = 0; i < funcsToRun.length; ++i) { for (var i = 0; i < funcsToRun.length; ++i) {
try { try {
var funcToRun = funcsToRun[i]; var funcToRun = funcsToRun[i];
this.nowMillis = funcToRun.runAtMillis; this.nowMillis = funcToRun.runAtMillis;
funcToRun.funcToCall(); funcToRun.funcToCall();
if (funcToRun.recurring) { if (funcToRun.recurring) {
this.scheduleFunction(funcToRun.timeoutKey, this.scheduleFunction(funcToRun.timeoutKey,
funcToRun.funcToCall, funcToRun.funcToCall,
funcToRun.millis, funcToRun.millis,
true); true);
} }
} catch(e) { } catch (e) {
} }
} }
this.runFunctionsWithinRange(oldMillis, nowMillis); this.runFunctionsWithinRange(oldMillis, nowMillis);
} }
}; };
   
jasmine.FakeTimer.prototype.scheduleFunction = function(timeoutKey, funcToCall, millis, recurring) { jasmine.FakeTimer.prototype.scheduleFunction = function (timeoutKey, funcToCall, millis, recurring) {
this.scheduledFunctions[timeoutKey] = { this.scheduledFunctions[timeoutKey] = {
runAtMillis: this.nowMillis + millis, runAtMillis: this.nowMillis + millis,
funcToCall: funcToCall, funcToCall: funcToCall,
recurring: recurring, recurring: recurring,
timeoutKey: timeoutKey, timeoutKey: timeoutKey,
millis: millis millis: millis
}; };
}; };
   
/** /**
* @namespace * @namespace
*/ */
jasmine.Clock = { jasmine.Clock = {
defaultFakeTimer: new jasmine.FakeTimer(), defaultFakeTimer: new jasmine.FakeTimer(),
   
reset: function() { reset: function () {
jasmine.Clock.assertInstalled(); jasmine.Clock.assertInstalled();
jasmine.Clock.defaultFakeTimer.reset(); jasmine.Clock.defaultFakeTimer.reset();
}, },
   
tick: function(millis) { tick: function (millis) {
jasmine.Clock.assertInstalled(); jasmine.Clock.assertInstalled();
jasmine.Clock.defaultFakeTimer.tick(millis); jasmine.Clock.defaultFakeTimer.tick(millis);
}, },
   
runFunctionsWithinRange: function(oldMillis, nowMillis) { runFunctionsWithinRange: function (oldMillis, nowMillis) {
jasmine.Clock.defaultFakeTimer.runFunctionsWithinRange(oldMillis, nowMillis); jasmine.Clock.defaultFakeTimer.runFunctionsWithinRange(oldMillis, nowMillis);
}, },
   
scheduleFunction: function(timeoutKey, funcToCall, millis, recurring) { scheduleFunction: function (timeoutKey, funcToCall, millis, recurring) {
jasmine.Clock.defaultFakeTimer.scheduleFunction(timeoutKey, funcToCall, millis, recurring); jasmine.Clock.defaultFakeTimer.scheduleFunction(timeoutKey, funcToCall, millis, recurring);
}, },
   
useMock: function() { useMock: function () {
if (!jasmine.Clock.isInstalled()) { if (!jasmine.Clock.isInstalled()) {
var spec = jasmine.getEnv().currentSpec; var spec = jasmine.getEnv().currentSpec;
spec.after(jasmine.Clock.uninstallMock); spec.after(jasmine.Clock.uninstallMock);
   
jasmine.Clock.installMock(); jasmine.Clock.installMock();
} }
}, },
   
installMock: function() { installMock: function () {
jasmine.Clock.installed = jasmine.Clock.defaultFakeTimer; jasmine.Clock.installed = jasmine.Clock.defaultFakeTimer;
}, },
   
uninstallMock: function() { uninstallMock: function () {
jasmine.Clock.assertInstalled(); jasmine.Clock.assertInstalled();
jasmine.Clock.installed = jasmine.Clock.real; jasmine.Clock.installed = jasmine.Clock.real;
}, },
   
real: { real: {
setTimeout: jasmine.getGlobal().setTimeout, setTimeout: jasmine.getGlobal().setTimeout,
clearTimeout: jasmine.getGlobal().clearTimeout, clearTimeout: jasmine.getGlobal().clearTimeout,
setInterval: jasmine.getGlobal().setInterval, setInterval: jasmine.getGlobal().setInterval,
clearInterval: jasmine.getGlobal().clearInterval clearInterval: jasmine.getGlobal().clearInterval
}, },
   
assertInstalled: function() { assertInstalled: function () {
if (!jasmine.Clock.isInstalled()) { if (!jasmine.Clock.isInstalled()) {
throw new Error("Mock clock is not installed, use jasmine.Clock.useMock()"); throw new Error("Mock clock is not installed, use jasmine.Clock.useMock()");
} }
}, },
   
isInstalled: function() { isInstalled: function () {
return jasmine.Clock.installed == jasmine.Clock.defaultFakeTimer; return jasmine.Clock.installed == jasmine.Clock.defaultFakeTimer;
}, },
   
installed: null installed: null
}; };
jasmine.Clock.installed = jasmine.Clock.real; jasmine.Clock.installed = jasmine.Clock.real;
   
//else for IE support //else for IE support
jasmine.getGlobal().setTimeout = function(funcToCall, millis) { jasmine.getGlobal().setTimeout = function (funcToCall, millis) {
if (jasmine.Clock.installed.setTimeout.apply) { if (jasmine.Clock.installed.setTimeout.apply) {
return jasmine.Clock.installed.setTimeout.apply(this, arguments); return jasmine.Clock.installed.setTimeout.apply(this, arguments);
} else { } else {
return jasmine.Clock.installed.setTimeout(funcToCall, millis); return jasmine.Clock.installed.setTimeout(funcToCall, millis);
} }
}; };
   
jasmine.getGlobal().setInterval = function(funcToCall, millis) { jasmine.getGlobal().setInterval = function (funcToCall, millis) {
if (jasmine.Clock.installed.setInterval.apply) { if (jasmine.Clock.installed.setInterval.apply) {
return jasmine.Clock.installed.setInterval.apply(this, arguments); return jasmine.Clock.installed.setInterval.apply(this, arguments);
} else { } else {
return jasmine.Clock.installed.setInterval(funcToCall, millis); return jasmine.Clock.installed.setInterval(funcToCall, millis);
} }
}; };
   
jasmine.getGlobal().clearTimeout = function(timeoutKey) { jasmine.getGlobal().clearTimeout = function (timeoutKey) {
if (jasmine.Clock.installed.clearTimeout.apply) { if (jasmine.Clock.installed.clearTimeout.apply) {
return jasmine.Clock.installed.clearTimeout.apply(this, arguments); return jasmine.Clock.installed.clearTimeout.apply(this, arguments);
} else { } else {
return jasmine.Clock.installed.clearTimeout(timeoutKey); return jasmine.Clock.installed.clearTimeout(timeoutKey);
} }
}; };
   
jasmine.getGlobal().clearInterval = function(timeoutKey) { jasmine.getGlobal().clearInterval = function (timeoutKey) {
if (jasmine.Clock.installed.clearTimeout.apply) { if (jasmine.Clock.installed.clearTimeout.apply) {
return jasmine.Clock.installed.clearInterval.apply(this, arguments); return jasmine.Clock.installed.clearInterval.apply(this, arguments);
} else { } else {
return jasmine.Clock.installed.clearInterval(timeoutKey); return jasmine.Clock.installed.clearInterval(timeoutKey);
} }
}; };
   
/** /**
* @constructor * @constructor
*/ */
jasmine.MultiReporter = function() { jasmine.MultiReporter = function () {
this.subReporters_ = []; this.subReporters_ = [];
}; };
jasmine.util.inherit(jasmine.MultiReporter, jasmine.Reporter); jasmine.util.inherit(jasmine.MultiReporter, jasmine.Reporter);
   
jasmine.MultiReporter.prototype.addReporter = function(reporter) { jasmine.MultiReporter.prototype.addReporter = function (reporter) {
this.subReporters_.push(reporter); this.subReporters_.push(reporter);
}; };
   
(function() { (function () {
var functionNames = [ var functionNames = [
"reportRunnerStarting", "reportRunnerStarting",
"reportRunnerResults", "reportRunnerResults",
"reportSuiteResults", "reportSuiteResults",
"reportSpecStarting", "reportSpecStarting",
"reportSpecResults", "reportSpecResults",
"log" "log"
]; ];
for (var i = 0; i < functionNames.length; i++) { for (var i = 0; i < functionNames.length; i++) {
var functionName = functionNames[i]; var functionName = functionNames[i];
jasmine.MultiReporter.prototype[functionName] = (function(functionName) { jasmine.MultiReporter.prototype[functionName] = (function (functionName) {
return function() { return function () {
for (var j = 0; j < this.subReporters_.length; j++) { for (var j = 0; j < this.subReporters_.length; j++) {
var subReporter = this.subReporters_[j]; var subReporter = this.subReporters_[j];
if (subReporter[functionName]) { if (subReporter[functionName]) {
subReporter[functionName].apply(subReporter, arguments); subReporter[functionName].apply(subReporter, arguments);
} }
} }
}; };
})(functionName); })(functionName);
} }
})(); })();
/** /**
* Holds results for a set of Jasmine spec. Allows for the results array to hold another jasmine.NestedResults * Holds results for a set of Jasmine spec. Allows for the results array to hold another jasmine.NestedResults
* *
* @constructor * @constructor
*/ */
jasmine.NestedResults = function() { jasmine.NestedResults = function () {
/** /**
* The total count of results * The total count of results
*/ */
this.totalCount = 0; this.totalCount = 0;
/** /**
* Number of passed results * Number of passed results
*/ */
this.passedCount = 0; this.passedCount = 0;
/** /**
* Number of failed results * Number of failed results
*/ */
this.failedCount = 0; this.failedCount = 0;
/** /**
* Was this suite/spec skipped? * Was this suite/spec skipped?
*/ */
this.skipped = false; this.skipped = false;
/** /**
* @ignore * @ignore
*/ */
this.items_ = []; this.items_ = [];
}; };
   
/** /**
* Roll up the result counts. * Roll up the result counts.
* *
* @param result * @param result
*/ */
jasmine.NestedResults.prototype.rollupCounts = function(result) { jasmine.NestedResults.prototype.rollupCounts = function (result) {
this.totalCount += result.totalCount; this.totalCount += result.totalCount;
this.passedCount += result.passedCount; this.passedCount += result.passedCount;
this.failedCount += result.failedCount; this.failedCount += result.failedCount;
}; };
   
/** /**
* Adds a log message. * Adds a log message.
* @param values Array of message parts which will be concatenated later. * @param values Array of message parts which will be concatenated later.
*/ */
jasmine.NestedResults.prototype.log = function(values) { jasmine.NestedResults.prototype.log = function (values) {
this.items_.push(new jasmine.MessageResult(values)); this.items_.push(new jasmine.MessageResult(values));
}; };
   
/** /**
* Getter for the results: message & results. * Getter for the results: message & results.
*/ */
jasmine.NestedResults.prototype.getItems = function() { jasmine.NestedResults.prototype.getItems = function () {
return this.items_; return this.items_;
}; };
   
/** /**
* Adds a result, tracking counts (total, passed, & failed) * Adds a result, tracking counts (total, passed, & failed)
* @param {jasmine.ExpectationResult|jasmine.NestedResults} result * @param {jasmine.ExpectationResult|jasmine.NestedResults} result
*/ */
jasmine.NestedResults.prototype.addResult = function(result) { jasmine.NestedResults.prototype.addResult = function (result) {
if (result.type != 'log') { if (result.type != 'log') {
if (result.items_) { if (result.items_) {
this.rollupCounts(result); this.rollupCounts(result);
} else { } else {
this.totalCount++; this.totalCount++;
if (result.passed()) { if (result.passed()) {
this.passedCount++; this.passedCount++;
} else { } else {
this.failedCount++; this.failedCount++;
} }
} }
} }
this.items_.push(result); this.items_.push(result);
}; };
   
/** /**
* @returns {Boolean} True if <b>everything</b> below passed * @returns {Boolean} True if <b>everything</b> below passed
*/ */
jasmine.NestedResults.prototype.passed = function() { jasmine.NestedResults.prototype.passed = function () {
return this.passedCount === this.totalCount; return this.passedCount === this.totalCount;
}; };
/** /**
* Base class for pretty printing for expectation results. * Base class for pretty printing for expectation results.
*/ */
jasmine.PrettyPrinter = function() { jasmine.PrettyPrinter = function () {
this.ppNestLevel_ = 0; this.ppNestLevel_ = 0;
}; };
   
/** /**
* Formats a value in a nice, human-readable string. * Formats a value in a nice, human-readable string.
* *
* @param value * @param value
*/ */
jasmine.PrettyPrinter.prototype.format = function(value) { jasmine.PrettyPrinter.prototype.format = function (value) {
if (this.ppNestLevel_ > 40) { if (this.ppNestLevel_ > 40) {
throw new Error('jasmine.PrettyPrinter: format() nested too deeply!'); throw new Error('jasmine.PrettyPrinter: format() nested too deeply!');
} }
   
this.ppNestLevel_++; this.ppNestLevel_++;
try { try {
if (value === jasmine.undefined) { if (value === jasmine.undefined) {
this.emitScalar('undefined'); this.emitScalar('undefined');
} else if (value === null) { } else if (value === null) {
this.emitScalar('null'); this.emitScalar('null');
} else if (value === jasmine.getGlobal()) { } else if (value === jasmine.getGlobal()) {
this.emitScalar('<global>'); this.emitScalar('<global>');
} else if (value.jasmineToString) { } else if (value.jasmineToString) {
this.emitScalar(value.jasmineToString()); this.emitScalar(value.jasmineToString());
} else if (typeof value === 'string') { } else if (typeof value === 'string') {
this.emitString(value); this.emitString(value);
} else if (jasmine.isSpy(value)) { } else if (jasmine.isSpy(value)) {
this.emitScalar("spy on " + value.identity); this.emitScalar("spy on " + value.identity);
} else if (value instanceof RegExp) { } else if (value instanceof RegExp) {
this.emitScalar(value.toString()); this.emitScalar(value.toString());
} else if (typeof value === 'function') { } else if (typeof value === 'function') {
this.emitScalar('Function'); this.emitScalar('Function');
} else if (typeof value.nodeType === 'number') { } else if (typeof value.nodeType === 'number') {
this.emitScalar('HTMLNode'); this.emitScalar('HTMLNode');
} else if (value instanceof Date) { } else if (value instanceof Date) {
this.emitScalar('Date(' + value + ')'); this.emitScalar('Date(' + value + ')');
} else if (value.__Jasmine_been_here_before__) { } else if (value.__Jasmine_been_here_before__) {
this.emitScalar('<circular reference: ' + (jasmine.isArray_(value) ? 'Array' : 'Object') + '>'); this.emitScalar('<circular reference: ' + (jasmine.isArray_(value) ? 'Array' : 'Object') + '>');
} else if (jasmine.isArray_(value) || typeof value == 'object') { } else if (jasmine.isArray_(value) || typeof value == 'object') {
value.__Jasmine_been_here_before__ = true; value.__Jasmine_been_here_before__ = true;
if (jasmine.isArray_(value)) { if (jasmine.isArray_(value)) {
this.emitArray(value); this.emitArray(value);
} else { } else {
this.emitObject(value); this.emitObject(value);
} }
delete value.__Jasmine_been_here_before__; delete value.__Jasmine_been_here_before__;
} else { } else {
this.emitScalar(value.toString()); this.emitScalar(value.toString());
} }
} finally { } finally {
this.ppNestLevel_--; this.ppNestLevel_--;
} }
}; };
   
jasmine.PrettyPrinter.prototype.iterateObject = function(obj, fn) { jasmine.PrettyPrinter.prototype.iterateObject = function (obj, fn) {
for (var property in obj) { for (var property in obj) {
if (property == '__Jasmine_been_here_before__') continue; if (property == '__Jasmine_been_here_before__') continue;
fn(property, obj.__lookupGetter__ ? (obj.__lookupGetter__(property) !== jasmine.undefined && fn(property, obj.__lookupGetter__ ? (obj.__lookupGetter__(property) !== jasmine.undefined &&
obj.__lookupGetter__(property) !== null) : false); obj.__lookupGetter__(property) !== null) : false);
} }
}; };
   
jasmine.PrettyPrinter.prototype.emitArray = jasmine.unimplementedMethod_; jasmine.PrettyPrinter.prototype.emitArray = jasmine.unimplementedMethod_;
jasmine.PrettyPrinter.prototype.emitObject = jasmine.unimplementedMethod_; jasmine.PrettyPrinter.prototype.emitObject = jasmine.unimplementedMethod_;
jasmine.PrettyPrinter.prototype.emitScalar = jasmine.unimplementedMethod_; jasmine.PrettyPrinter.prototype.emitScalar = jasmine.unimplementedMethod_;
jasmine.PrettyPrinter.prototype.emitString = jasmine.unimplementedMethod_; jasmine.PrettyPrinter.prototype.emitString = jasmine.unimplementedMethod_;
   
jasmine.StringPrettyPrinter = function() { jasmine.StringPrettyPrinter = function () {
jasmine.PrettyPrinter.call(this); jasmine.PrettyPrinter.call(this);
   
this.string = ''; this.string = '';
}; };
jasmine.util.inherit(jasmine.StringPrettyPrinter, jasmine.PrettyPrinter); jasmine.util.inherit(jasmine.StringPrettyPrinter, jasmine.PrettyPrinter);
   
jasmine.StringPrettyPrinter.prototype.emitScalar = function(value) { jasmine.StringPrettyPrinter.prototype.emitScalar = function (value) {
this.append(value); this.append(value);
}; };
   
jasmine.StringPrettyPrinter.prototype.emitString = function(value) { jasmine.StringPrettyPrinter.prototype.emitString = function (value) {
this.append("'" + value + "'"); this.append("'" + value + "'");
}; };
   
jasmine.StringPrettyPrinter.prototype.emitArray = function(array) { jasmine.StringPrettyPrinter.prototype.emitArray = function (array) {
this.append('[ '); this.append('[ ');
for (var i = 0; i < array.length; i++) { for (var i = 0; i < array.length; i++) {
if (i > 0) { if (i > 0) {
this.append(', '); this.append(', ');
} }
this.format(array[i]); this.format(array[i]);
} }
this.append(' ]'); this.append(' ]');
}; };
   
jasmine.StringPrettyPrinter.prototype.emitObject = function(obj) { jasmine.StringPrettyPrinter.prototype.emitObject = function (obj) {
var self = this; var self = this;
this.append('{ '); this.append('{ ');
var first = true; var first = true;
   
this.iterateObject(obj, function(property, isGetter) { this.iterateObject(obj, function (property, isGetter) {
if (first) { if (first) {
first = false; first = false;
} else { } else {
self.append(', '); self.append(', ');
} }
   
self.append(property); self.append(property);
self.append(' : '); self.append(' : ');
if (isGetter) { if (isGetter) {
self.append('<getter>'); self.append('<getter>');
} else { } else {
self.format(obj[property]); self.format(obj[property]);
} }
}); });
   
this.append(' }'); this.append(' }');
}; };
   
jasmine.StringPrettyPrinter.prototype.append = function(value) { jasmine.StringPrettyPrinter.prototype.append = function (value) {
this.string += value; this.string += value;
}; };
jasmine.Queue = function(env) { jasmine.Queue = function (env) {
this.env = env; this.env = env;
this.blocks = []; this.blocks = [];
this.running = false; this.running = false;
this.index = 0; this.index = 0;
this.offset = 0; this.offset = 0;
this.abort = false; this.abort = false;
}; };
   
jasmine.Queue.prototype.addBefore = function(block) { jasmine.Queue.prototype.addBefore = function (block) {
this.blocks.unshift(block); this.blocks.unshift(block);
}; };
   
jasmine.Queue.prototype.add = function(block) { jasmine.Queue.prototype.add = function (block) {
this.blocks.push(block); this.blocks.push(block);
}; };
   
jasmine.Queue.prototype.insertNext = function(block) { jasmine.Queue.prototype.insertNext = function (block) {
this.blocks.splice((this.index + this.offset + 1), 0, block); this.blocks.splice((this.index + this.offset + 1), 0, block);
this.offset++; this.offset++;
}; };
   
jasmine.Queue.prototype.start = function(onComplete) { jasmine.Queue.prototype.start = function (onComplete) {
this.running = true; this.running = true;
this.onComplete = onComplete; this.onComplete = onComplete;
this.next_(); this.next_();
}; };
   
jasmine.Queue.prototype.isRunning = function() { jasmine.Queue.prototype.isRunning = function () {
return this.running; return this.running;
}; };
   
jasmine.Queue.LOOP_DONT_RECURSE = true; jasmine.Queue.LOOP_DONT_RECURSE = true;
   
jasmine.Queue.prototype.next_ = function() { jasmine.Queue.prototype.next_ = function () {
var self = this; var self = this;
var goAgain = true; var goAgain = true;
   
while (goAgain) { while (goAgain) {
goAgain = false; goAgain = false;
   
if (self.index < self.blocks.length && !this.abort) { if (self.index < self.blocks.length && !this.abort) {
var calledSynchronously = true; var calledSynchronously = true;
var completedSynchronously = false; var completedSynchronously = false;
   
var onComplete = function () { var onComplete = function () {
if (jasmine.Queue.LOOP_DONT_RECURSE && calledSynchronously) { if (jasmine.Queue.LOOP_DONT_RECURSE && calledSynchronously) {
completedSynchronously = true; completedSynchronously = true;
return; return;
} }
   
if (self.blocks[self.index].abort) { if (self.blocks[self.index].abort) {
self.abort = true; self.abort = true;
} }
   
self.offset = 0; self.offset = 0;
self.index++; self.index++;
   
var now = new Date().getTime(); var now = new Date().getTime();
if (self.env.updateInterval && now - self.env.lastUpdate > self.env.updateInterval) { if (self.env.updateInterval && now - self.env.lastUpdate > self.env.updateInterval) {
self.env.lastUpdate = now; self.env.lastUpdate = now;
self.env.setTimeout(function() { self.env.setTimeout(function () {
self.next_(); self.next_();
}, 0); }, 0);
  } else {
  if (jasmine.Queue.LOOP_DONT_RECURSE && completedSynchronously) {
  goAgain = true;
  } else {
  self.next_();
  }
  }
  };
  self.blocks[self.index].execute(onComplete);
   
  calledSynchronously = false;
  if (completedSynchronously) {
  onComplete();
  }
   
} else { } else {
if (jasmine.Queue.LOOP_DONT_RECURSE && completedSynchronously) { self.running = false;
goAgain = true; if (self.onComplete) {
} else { self.onComplete();
self.next_(); }
} }
} }
}; };
self.blocks[self.index].execute(onComplete);  
  jasmine.Queue.prototype.results = function () {
calledSynchronously = false; var results = new jasmine.NestedResults();
if (completedSynchronously) { for (var i = 0; i < this.blocks.length; i++) {
onComplete(); if (this.blocks[i].results) {
} results.addResult(this.blocks[i].results());
  }
} else { }
self.running = false; return results;
if (self.onComplete) {  
self.onComplete();  
}  
}  
}  
};  
   
jasmine.Queue.prototype.results = function() {  
var results = new jasmine.NestedResults();  
for (var i = 0; i < this.blocks.length; i++) {  
if (this.blocks[i].results) {  
results.addResult(this.blocks[i].results());  
}  
}  
return results;  
}; };
   
   
/** /**
* Runner * Runner
* *
* @constructor * @constructor
* @param {jasmine.Env} env * @param {jasmine.Env} env
*/ */
jasmine.Runner = function(env) { jasmine.Runner = function (env) {
var self = this; var self = this;
self.env = env; self.env = env;
self.queue = new jasmine.Queue(env); self.queue = new jasmine.Queue(env);
self.before_ = []; self.before_ = [];
self.after_ = []; self.after_ = [];
self.suites_ = []; self.suites_ = [];
}; };
   
jasmine.Runner.prototype.execute = function() { jasmine.Runner.prototype.execute = function () {
var self = this; var self = this;
if (self.env.reporter.reportRunnerStarting) { if (self.env.reporter.reportRunnerStarting) {
self.env.reporter.reportRunnerStarting(this); self.env.reporter.reportRunnerStarting(this);
} }
self.queue.start(function () { self.queue.start(function () {
self.finishCallback(); self.finishCallback();
}); });
}; };
   
jasmine.Runner.prototype.beforeEach = function(beforeEachFunction) { jasmine.Runner.prototype.beforeEach = function (beforeEachFunction) {
beforeEachFunction.typeName = 'beforeEach'; beforeEachFunction.typeName = 'beforeEach';
this.before_.splice(0,0,beforeEachFunction); this.before_.splice(0, 0, beforeEachFunction);
}; };
   
jasmine.Runner.prototype.afterEach = function(afterEachFunction) { jasmine.Runner.prototype.afterEach = function (afterEachFunction) {
afterEachFunction.typeName = 'afterEach'; afterEachFunction.typeName = 'afterEach';
this.after_.splice(0,0,afterEachFunction); this.after_.splice(0, 0, afterEachFunction);
}; };
   
   
jasmine.Runner.prototype.finishCallback = function() { jasmine.Runner.prototype.finishCallback = function () {
this.env.reporter.reportRunnerResults(this); this.env.reporter.reportRunnerResults(this);
}; };
   
jasmine.Runner.prototype.addSuite = function(suite) { jasmine.Runner.prototype.addSuite = function (suite) {
this.suites_.push(suite); this.suites_.push(suite);
}; };
   
jasmine.Runner.prototype.add = function(block) { jasmine.Runner.prototype.add = function (block) {
if (block instanceof jasmine.Suite) { if (block instanceof jasmine.Suite) {
this.addSuite(block); this.addSuite(block);
} }
this.queue.add(block); this.queue.add(block);
}; };
   
jasmine.Runner.prototype.specs = function () { jasmine.Runner.prototype.specs = function () {
var suites = this.suites(); var suites = this.suites();
var specs = []; var specs = [];
for (var i = 0; i < suites.length; i++) { for (var i = 0; i < suites.length; i++) {
specs = specs.concat(suites[i].specs()); specs = specs.concat(suites[i].specs());
} }
return specs; return specs;
}; };
   
jasmine.Runner.prototype.suites = function() { jasmine.Runner.prototype.suites = function () {
return this.suites_; return this.suites_;
}; };
   
jasmine.Runner.prototype.topLevelSuites = function() { jasmine.Runner.prototype.topLevelSuites = function () {
var topLevelSuites = []; var topLevelSuites = [];
for (var i = 0; i < this.suites_.length; i++) { for (var i = 0; i < this.suites_.length; i++) {
if (!this.suites_[i].parentSuite) { if (!this.suites_[i].parentSuite) {
topLevelSuites.push(this.suites_[i]); topLevelSuites.push(this.suites_[i]);
} }
} }
return topLevelSuites; return topLevelSuites;
}; };
   
jasmine.Runner.prototype.results = function() { jasmine.Runner.prototype.results = function () {
return this.queue.results(); return this.queue.results();
}; };
/** /**
* Internal representation of a Jasmine specification, or test. * Internal representation of a Jasmine specification, or test.
* *
* @constructor * @constructor
* @param {jasmine.Env} env * @param {jasmine.Env} env
* @param {jasmine.Suite} suite * @param {jasmine.Suite} suite
* @param {String} description * @param {String} description
*/ */
jasmine.Spec = function(env, suite, description) { jasmine.Spec = function (env, suite, description) {
if (!env) { if (!env) {
throw new Error('jasmine.Env() required'); throw new Error('jasmine.Env() required');
} }
if (!suite) { if (!suite) {
throw new Error('jasmine.Suite() required'); throw new Error('jasmine.Suite() required');
} }
var spec = this; var spec = this;
spec.id = env.nextSpecId ? env.nextSpecId() : null; spec.id = env.nextSpecId ? env.nextSpecId() : null;
spec.env = env; spec.env = env;
spec.suite = suite; spec.suite = suite;
spec.description = description; spec.description = description;
spec.queue = new jasmine.Queue(env); spec.queue = new jasmine.Queue(env);
   
spec.afterCallbacks = []; spec.afterCallbacks = [];
spec.spies_ = []; spec.spies_ = [];
   
spec.results_ = new jasmine.NestedResults(); spec.results_ = new jasmine.NestedResults();
spec.results_.description = description; spec.results_.description = description;
spec.matchersClass = null; spec.matchersClass = null;
}; };
   
jasmine.Spec.prototype.getFullName = function() { jasmine.Spec.prototype.getFullName = function () {
return this.suite.getFullName() + ' ' + this.description + '.'; return this.suite.getFullName() + ' ' + this.description + '.';
}; };
   
   
jasmine.Spec.prototype.results = function() { jasmine.Spec.prototype.results = function () {
return this.results_; return this.results_;
}; };
   
/** /**
* All parameters are pretty-printed and concatenated together, then written to the spec's output. * All parameters are pretty-printed and concatenated together, then written to the spec's output.
* *
* Be careful not to leave calls to <code>jasmine.log</code> in production code. * Be careful not to leave calls to <code>jasmine.log</code> in production code.
*/ */
jasmine.Spec.prototype.log = function() { jasmine.Spec.prototype.log = function () {
return this.results_.log(arguments); return this.results_.log(arguments);
}; };
   
jasmine.Spec.prototype.runs = function (func) { jasmine.Spec.prototype.runs = function (func) {
var block = new jasmine.Block(this.env, func, this); var block = new jasmine.Block(this.env, func, this);
this.addToQueue(block); this.addToQueue(block);
return this; return this;
}; };
   
jasmine.Spec.prototype.addToQueue = function (block) { jasmine.Spec.prototype.addToQueue = function (block) {
if (this.queue.isRunning()) { if (this.queue.isRunning()) {
this.queue.insertNext(block); this.queue.insertNext(block);
} else { } else {
this.queue.add(block); this.queue.add(block);
} }
}; };
   
/** /**
* @param {jasmine.ExpectationResult} result * @param {jasmine.ExpectationResult} result
*/ */
jasmine.Spec.prototype.addMatcherResult = function(result) { jasmine.Spec.prototype.addMatcherResult = function (result) {
this.results_.addResult(result); this.results_.addResult(result);
}; };
   
jasmine.Spec.prototype.expect = function(actual) { jasmine.Spec.prototype.expect = function (actual) {
var positive = new (this.getMatchersClass_())(this.env, actual, this); var positive = new (this.getMatchersClass_())(this.env, actual, this);
positive.not = new (this.getMatchersClass_())(this.env, actual, this, true); positive.not = new (this.getMatchersClass_())(this.env, actual, this, true);
return positive; return positive;
}; };
   
/** /**
* Waits a fixed time period before moving to the next block. * Waits a fixed time period before moving to the next block.
* *
* @deprecated Use waitsFor() instead * @deprecated Use waitsFor() instead
* @param {Number} timeout milliseconds to wait * @param {Number} timeout milliseconds to wait
*/ */
jasmine.Spec.prototype.waits = function(timeout) { jasmine.Spec.prototype.waits = function (timeout) {
var waitsFunc = new jasmine.WaitsBlock(this.env, timeout, this); var waitsFunc = new jasmine.WaitsBlock(this.env, timeout, this);
this.addToQueue(waitsFunc); this.addToQueue(waitsFunc);
return this; return this;
}; };
   
/** /**
* Waits for the latchFunction to return true before proceeding to the next block. * Waits for the latchFunction to return true before proceeding to the next block.
* *
* @param {Function} latchFunction * @param {Function} latchFunction
* @param {String} optional_timeoutMessage * @param {String} optional_timeoutMessage
* @param {Number} optional_timeout * @param {Number} optional_timeout
*/ */
jasmine.Spec.prototype.waitsFor = function(latchFunction, optional_timeoutMessage, optional_timeout) { jasmine.Spec.prototype.waitsFor = function (latchFunction, optional_timeoutMessage, optional_timeout) {
var latchFunction_ = null; var latchFunction_ = null;
var optional_timeoutMessage_ = null; var optional_timeoutMessage_ = null;
var optional_timeout_ = null; var optional_timeout_ = null;
   
for (var i = 0; i < arguments.length; i++) { for (var i = 0; i < arguments.length; i++) {
var arg = arguments[i]; var arg = arguments[i];
switch (typeof arg) { switch (typeof arg) {
case 'function': case 'function':
latchFunction_ = arg; latchFunction_ = arg;
break; break;
case 'string': case 'string':
optional_timeoutMessage_ = arg; optional_timeoutMessage_ = arg;
break; break;
case 'number': case 'number':
optional_timeout_ = arg; optional_timeout_ = arg;
break; break;
} }
} }
   
var waitsForFunc = new jasmine.WaitsForBlock(this.env, optional_timeout_, latchFunction_, optional_timeoutMessage_, this); var waitsForFunc = new jasmine.WaitsForBlock(this.env, optional_timeout_, latchFunction_, optional_timeoutMessage_, this);
this.addToQueue(waitsForFunc); this.addToQueue(waitsForFunc);
return this; return this;
}; };
   
jasmine.Spec.prototype.fail = function (e) { jasmine.Spec.prototype.fail = function (e) {
var expectationResult = new jasmine.ExpectationResult({ var expectationResult = new jasmine.ExpectationResult({
passed: false, passed: false,
message: e ? jasmine.util.formatException(e) : 'Exception', message: e ? jasmine.util.formatException(e) : 'Exception',
trace: { stack: e.stack } trace: { stack: e.stack }
}); });
this.results_.addResult(expectationResult); this.results_.addResult(expectationResult);
}; };
   
jasmine.Spec.prototype.getMatchersClass_ = function() { jasmine.Spec.prototype.getMatchersClass_ = function () {
return this.matchersClass || this.env.matchersClass; return this.matchersClass || this.env.matchersClass;
}; };
   
jasmine.Spec.prototype.addMatchers = function(matchersPrototype) { jasmine.Spec.prototype.addMatchers = function (matchersPrototype) {
var parent = this.getMatchersClass_(); var parent = this.getMatchersClass_();
var newMatchersClass = function() { var newMatchersClass = function () {
parent.apply(this, arguments); parent.apply(this, arguments);
}; };
jasmine.util.inherit(newMatchersClass, parent); jasmine.util.inherit(newMatchersClass, parent);
jasmine.Matchers.wrapInto_(matchersPrototype, newMatchersClass); jasmine.Matchers.wrapInto_(matchersPrototype, newMatchersClass);
this.matchersClass = newMatchersClass; this.matchersClass = newMatchersClass;
}; };
   
jasmine.Spec.prototype.finishCallback = function() { jasmine.Spec.prototype.finishCallback = function () {
this.env.reporter.reportSpecResults(this); this.env.reporter.reportSpecResults(this);
}; };
   
jasmine.Spec.prototype.finish = function(onComplete) { jasmine.Spec.prototype.finish = function (onComplete) {
this.removeAllSpies(); this.removeAllSpies();
this.finishCallback(); this.finishCallback();
if (onComplete) { if (onComplete) {
onComplete(); onComplete();
} }
}; };
   
jasmine.Spec.prototype.after = function(doAfter) { jasmine.Spec.prototype.after = function (doAfter) {
if (this.queue.isRunning()) { if (this.queue.isRunning()) {
this.queue.add(new jasmine.Block(this.env, doAfter, this)); this.queue.add(new jasmine.Block(this.env, doAfter, this));
} else { } else {
this.afterCallbacks.unshift(doAfter); this.afterCallbacks.unshift(doAfter);
} }
}; };
   
jasmine.Spec.prototype.execute = function(onComplete) { jasmine.Spec.prototype.execute = function (onComplete) {
var spec = this; var spec = this;
if (!spec.env.specFilter(spec)) { if (!spec.env.specFilter(spec)) {
spec.results_.skipped = true; spec.results_.skipped = true;
spec.finish(onComplete); spec.finish(onComplete);
return; return;
} }
   
this.env.reporter.reportSpecStarting(this); this.env.reporter.reportSpecStarting(this);
   
spec.env.currentSpec = spec; spec.env.currentSpec = spec;
   
spec.addBeforesAndAftersToQueue(); spec.addBeforesAndAftersToQueue();
   
spec.queue.start(function () { spec.queue.start(function () {
spec.finish(onComplete); spec.finish(onComplete);
}); });
}; };
   
jasmine.Spec.prototype.addBeforesAndAftersToQueue = function() { jasmine.Spec.prototype.addBeforesAndAftersToQueue = function () {
var runner = this.env.currentRunner(); var runner = this.env.currentRunner();
var i; var i;
   
for (var suite = this.suite; suite; suite = suite.parentSuite) { for (var suite = this.suite; suite; suite = suite.parentSuite) {
for (i = 0; i < suite.before_.length; i++) { for (i = 0; i < suite.before_.length; i++) {
this.queue.addBefore(new jasmine.Block(this.env, suite.before_[i], this)); this.queue.addBefore(new jasmine.Block(this.env, suite.before_[i], this));
} }
} }
for (i = 0; i < runner.before_.length; i++) { for (i = 0; i < runner.before_.length; i++) {
this.queue.addBefore(new jasmine.Block(this.env, runner.before_[i], this)); this.queue.addBefore(new jasmine.Block(this.env, runner.before_[i], this));
} }
for (i = 0; i < this.afterCallbacks.length; i++) { for (i = 0; i < this.afterCallbacks.length; i++) {
this.queue.add(new jasmine.Block(this.env, this.afterCallbacks[i], this)); this.queue.add(new jasmine.Block(this.env, this.afterCallbacks[i], this));
} }
for (suite = this.suite; suite; suite = suite.parentSuite) { for (suite = this.suite; suite; suite = suite.parentSuite) {
for (i = 0; i < suite.after_.length; i++) { for (i = 0; i < suite.after_.length; i++) {
this.queue.add(new jasmine.Block(this.env, suite.after_[i], this)); this.queue.add(new jasmine.Block(this.env, suite.after_[i], this));
} }
} }
for (i = 0; i < runner.after_.length; i++) { for (i = 0; i < runner.after_.length; i++) {
this.queue.add(new jasmine.Block(this.env, runner.after_[i], this)); this.queue.add(new jasmine.Block(this.env, runner.after_[i], this));
} }
}; };
   
jasmine.Spec.prototype.explodes = function() { jasmine.Spec.prototype.explodes = function () {
throw 'explodes function should not have been called'; throw 'explodes function should not have been called';
}; };
   
jasmine.Spec.prototype.spyOn = function(obj, methodName, ignoreMethodDoesntExist) { jasmine.Spec.prototype.spyOn = function (obj, methodName, ignoreMethodDoesntExist) {
if (obj == jasmine.undefined) { if (obj == jasmine.undefined) {
throw "spyOn could not find an object to spy upon for " + methodName + "()"; throw "spyOn could not find an object to spy upon for " + methodName + "()";
} }
   
if (!ignoreMethodDoesntExist && obj[methodName] === jasmine.undefined) { if (!ignoreMethodDoesntExist && obj[methodName] === jasmine.undefined) {
throw methodName + '() method does not exist'; throw methodName + '() method does not exist';
} }
   
if (!ignoreMethodDoesntExist && obj[methodName] && obj[methodName].isSpy) { if (!ignoreMethodDoesntExist && obj[methodName] && obj[methodName].isSpy) {
throw new Error(methodName + ' has already been spied upon'); throw new Error(methodName + ' has already been spied upon');
} }
   
var spyObj = jasmine.createSpy(methodName); var spyObj = jasmine.createSpy(methodName);
   
this.spies_.push(spyObj); this.spies_.push(spyObj);
spyObj.baseObj = obj; spyObj.baseObj = obj;
spyObj.methodName = methodName; spyObj.methodName = methodName;
spyObj.originalValue = obj[methodName]; spyObj.originalValue = obj[methodName];
   
obj[methodName] = spyObj; obj[methodName] = spyObj;
   
return spyObj; return spyObj;
}; };
   
jasmine.Spec.prototype.removeAllSpies = function() { jasmine.Spec.prototype.removeAllSpies = function () {
for (var i = 0; i < this.spies_.length; i++) { for (var i = 0; i < this.spies_.length; i++) {
var spy = this.spies_[i]; var spy = this.spies_[i];
spy.baseObj[spy.methodName] = spy.originalValue; spy.baseObj[spy.methodName] = spy.originalValue;
} }
this.spies_ = []; this.spies_ = [];
}; };
   
/** /**
* Internal representation of a Jasmine suite. * Internal representation of a Jasmine suite.
* *
* @constructor * @constructor
* @param {jasmine.Env} env * @param {jasmine.Env} env
* @param {String} description * @param {String} description
* @param {Function} specDefinitions * @param {Function} specDefinitions
* @param {jasmine.Suite} parentSuite * @param {jasmine.Suite} parentSuite
*/ */
jasmine.Suite = function(env, description, specDefinitions, parentSuite) { jasmine.Suite = function (env, description, specDefinitions, parentSuite) {
var self = this; var self = this;
self.id = env.nextSuiteId ? env.nextSuiteId() : null; self.id = env.nextSuiteId ? env.nextSuiteId() : null;
self.description = description; self.description = description;
self.queue = new jasmine.Queue(env); self.queue = new jasmine.Queue(env);
self.parentSuite = parentSuite; self.parentSuite = parentSuite;
self.env = env; self.env = env;
self.before_ = []; self.before_ = [];
self.after_ = []; self.after_ = [];
self.children_ = []; self.children_ = [];
self.suites_ = []; self.suites_ = [];
self.specs_ = []; self.specs_ = [];
}; };
   
jasmine.Suite.prototype.getFullName = function() { jasmine.Suite.prototype.getFullName = function () {
var fullName = this.description; var fullName = this.description;
for (var parentSuite = this.parentSuite; parentSuite; parentSuite = parentSuite.parentSuite) { for (var parentSuite = this.parentSuite; parentSuite; parentSuite = parentSuite.parentSuite) {
fullName = parentSuite.description + ' ' + fullName; fullName = parentSuite.description + ' ' + fullName;
} }
return fullName; return fullName;
}; };
   
jasmine.Suite.prototype.finish = function(onComplete) { jasmine.Suite.prototype.finish = function (onComplete) {
this.env.reporter.reportSuiteResults(this); this.env.reporter.reportSuiteResults(this);
this.finished = true; this.finished = true;
if (typeof(onComplete) == 'function') { if (typeof(onComplete) == 'function') {
onComplete(); onComplete();
} }
}; };
   
jasmine.Suite.prototype.beforeEach = function(beforeEachFunction) { jasmine.Suite.prototype.beforeEach = function (beforeEachFunction) {
beforeEachFunction.typeName = 'beforeEach'; beforeEachFunction.typeName = 'beforeEach';
this.before_.unshift(beforeEachFunction); this.before_.unshift(beforeEachFunction);
}; };
   
jasmine.Suite.prototype.afterEach = function(afterEachFunction) { jasmine.Suite.prototype.afterEach = function (afterEachFunction) {
afterEachFunction.typeName = 'afterEach'; afterEachFunction.typeName = 'afterEach';
this.after_.unshift(afterEachFunction); this.after_.unshift(afterEachFunction);
}; };
   
jasmine.Suite.prototype.results = function() { jasmine.Suite.prototype.results = function () {
return this.queue.results(); return this.queue.results();
}; };
   
jasmine.Suite.prototype.add = function(suiteOrSpec) { jasmine.Suite.prototype.add = function (suiteOrSpec) {
this.children_.push(suiteOrSpec); this.children_.push(suiteOrSpec);
if (suiteOrSpec instanceof jasmine.Suite) { if (suiteOrSpec instanceof jasmine.Suite) {
this.suites_.push(suiteOrSpec); this.suites_.push(suiteOrSpec);
this.env.currentRunner().addSuite(suiteOrSpec); this.env.currentRunner().addSuite(suiteOrSpec);
} else { } else {
this.specs_.push(suiteOrSpec); this.specs_.push(suiteOrSpec);
} }
this.queue.add(suiteOrSpec); this.queue.add(suiteOrSpec);
}; };
   
jasmine.Suite.prototype.specs = function() { jasmine.Suite.prototype.specs = function () {
return this.specs_; return this.specs_;
}; };
   
jasmine.Suite.prototype.suites = function() { jasmine.Suite.prototype.suites = function () {
return this.suites_; return this.suites_;
}; };
   
jasmine.Suite.prototype.children = function() { jasmine.Suite.prototype.children = function () {
return this.children_; return this.children_;
}; };
   
jasmine.Suite.prototype.execute = function(onComplete) { jasmine.Suite.prototype.execute = function (onComplete) {
var self = this; var self = this;
this.queue.start(function () { this.queue.start(function () {
self.finish(onComplete); self.finish(onComplete);
}); });
}; };
jasmine.WaitsBlock = function(env, timeout, spec) { jasmine.WaitsBlock = function (env, timeout, spec) {
this.timeout = timeout; this.timeout = timeout;
jasmine.Block.call(this, env, null, spec); jasmine.Block.call(this, env, null, spec);
}; };
   
jasmine.util.inherit(jasmine.WaitsBlock, jasmine.Block); jasmine.util.inherit(jasmine.WaitsBlock, jasmine.Block);
   
jasmine.WaitsBlock.prototype.execute = function (onComplete) { jasmine.WaitsBlock.prototype.execute = function (onComplete) {
if (jasmine.VERBOSE) { if (jasmine.VERBOSE) {
this.env.reporter.log('>> Jasmine waiting for ' + this.timeout + ' ms...'); this.env.reporter.log('>> Jasmine waiting for ' + this.timeout + ' ms...');
} }
this.env.setTimeout(function () { this.env.setTimeout(function () {
onComplete(); onComplete();
}, this.timeout); }, this.timeout);
}; };
/** /**
* A block which waits for some condition to become true, with timeout. * A block which waits for some condition to become true, with timeout.
* *
* @constructor * @constructor
* @extends jasmine.Block * @extends jasmine.Block
* @param {jasmine.Env} env The Jasmine environment. * @param {jasmine.Env} env The Jasmine environment.
* @param {Number} timeout The maximum time in milliseconds to wait for the condition to become true. * @param {Number} timeout The maximum time in milliseconds to wait for the condition to become true.
* @param {Function} latchFunction A function which returns true when the desired condition has been met. * @param {Function} latchFunction A function which returns true when the desired condition has been met.
* @param {String} message The message to display if the desired condition hasn't been met within the given time period. * @param {String} message The message to display if the desired condition hasn't been met within the given time period.
* @param {jasmine.Spec} spec The Jasmine spec. * @param {jasmine.Spec} spec The Jasmine spec.
*/ */
jasmine.WaitsForBlock = function(env, timeout, latchFunction, message, spec) { jasmine.WaitsForBlock = function (env, timeout, latchFunction, message, spec) {
this.timeout = timeout || env.defaultTimeoutInterval; this.timeout = timeout || env.defaultTimeoutInterval;
this.latchFunction = latchFunction; this.latchFunction = latchFunction;
this.message = message; this.message = message;
this.totalTimeSpentWaitingForLatch = 0; this.totalTimeSpentWaitingForLatch = 0;
jasmine.Block.call(this, env, null, spec); jasmine.Block.call(this, env, null, spec);
}; };
jasmine.util.inherit(jasmine.WaitsForBlock, jasmine.Block); jasmine.util.inherit(jasmine.WaitsForBlock, jasmine.Block);
   
jasmine.WaitsForBlock.TIMEOUT_INCREMENT = 10; jasmine.WaitsForBlock.TIMEOUT_INCREMENT = 10;
   
jasmine.WaitsForBlock.prototype.execute = function(onComplete) { jasmine.WaitsForBlock.prototype.execute = function (onComplete) {
if (jasmine.VERBOSE) { if (jasmine.VERBOSE) {
this.env.reporter.log('>> Jasmine waiting for ' + (this.message || 'something to happen')); this.env.reporter.log('>> Jasmine waiting for ' + (this.message || 'something to happen'));
} }
var latchFunctionResult; var latchFunctionResult;
try { try {
latchFunctionResult = this.latchFunction.apply(this.spec); latchFunctionResult = this.latchFunction.apply(this.spec);
} catch (e) { } catch (e) {
this.spec.fail(e); this.spec.fail(e);
onComplete(); onComplete();
return; return;
} }
   
if (latchFunctionResult) { if (latchFunctionResult) {
onComplete(); onComplete();
} else if (this.totalTimeSpentWaitingForLatch >= this.timeout) { } else if (this.totalTimeSpentWaitingForLatch >= this.timeout) {
var message = 'timed out after ' + this.timeout + ' msec waiting for ' + (this.message || 'something to happen'); var message = 'timed out after ' + this.timeout + ' msec waiting for ' + (this.message || 'something to happen');
this.spec.fail({ this.spec.fail({
name: 'timeout', name: 'timeout',
message: message message: message
}); });
   
this.abort = true; this.abort = true;
onComplete(); onComplete();
} else { } else {
this.totalTimeSpentWaitingForLatch += jasmine.WaitsForBlock.TIMEOUT_INCREMENT; this.totalTimeSpentWaitingForLatch += jasmine.WaitsForBlock.TIMEOUT_INCREMENT;
var self = this; var self = this;
this.env.setTimeout(function() { this.env.setTimeout(function () {
self.execute(onComplete); self.execute(onComplete);
}, jasmine.WaitsForBlock.TIMEOUT_INCREMENT); }, jasmine.WaitsForBlock.TIMEOUT_INCREMENT);
} }
}; };
   
jasmine.version_= { jasmine.version_ = {
"major": 1, "major": 1,
"minor": 2, "minor": 2,
"build": 0, "build": 0,
"revision": 1337005947 "revision": 1337005947
}; };
   
{ {
"JAVASCRIPT": { "JAVASCRIPT": {
"DIST_DIR": "./build", "DIST_DIR": "./build",
"flotr2-basic": [ "flotr2-basic": [
"./js/Flotr.js", "./js/Flotr.js",
"./js/DefaultOptions.js", "./js/DefaultOptions.js",
"./js/DOM.js", "./js/DOM.js",
"./js/EventAdapter.js", "./js/EventAdapter.js",
"./js/Color.js", "./js/Color.js",
"./js/Date.js", "./js/Date.js",
"./js/Text.js", "./js/Text.js",
"./js/Graph.js", "./js/Graph.js",
"./js/Axis.js", "./js/Axis.js",
"./js/Series.js", "./js/Series.js",
"./js/types/lines.js", "./js/types/lines.js",
"./js/types/bars.js", "./js/types/bars.js",
"./js/types/markers.js", "./js/types/markers.js",
"./js/types/points.js", "./js/types/points.js",
"./js/plugins/grid.js", "./js/plugins/grid.js",
"./js/plugins/labels.js", "./js/plugins/labels.js",
"./js/plugins/legend.js", "./js/plugins/legend.js",
"./js/plugins/titles.js" "./js/plugins/titles.js"
] ]
} }
} }
   
{ {
"JAVASCRIPT": { "JAVASCRIPT": {
"DIST_DIR": "./build", "DIST_DIR": "./build",
"ie": [ "ie": [
{ "src": "./lib/excanvas.js", "jshint": false }, {
{ "src": "./lib/base64.js", "jshint": false }, "src": "./lib/excanvas.js",
{ "src": "./lib/canvastext.js", "jshint": false } "jshint": false
], },
"lib": [ {
{ "src": "./lib/bean.js", "jshint": false }, "src": "./lib/base64.js",
{ "src": "./lib/underscore.js", "jshint": false } "jshint": false
], },
"flotr2": [ {
"./js/Flotr.js", "src": "./lib/canvastext.js",
"./js/DefaultOptions.js", "jshint": false
"./js/Color.js", }
"./js/Date.js", ],
"./js/DOM.js", "lib": [
"./js/EventAdapter.js", {
"./js/Text.js", "src": "./lib/bean.js",
"./js/Graph.js", "jshint": false
"./js/Axis.js", },
"./js/Series.js", {
"./js/types/lines.js", "src": "./lib/underscore.js",
"./js/types/bars.js", "jshint": false
"./js/types/bubbles.js", }
"./js/types/candles.js", ],
"./js/types/gantt.js", "flotr2": [
"./js/types/markers.js", "./js/Flotr.js",
"./js/types/pie.js", "./js/DefaultOptions.js",
"./js/types/points.js", "./js/Color.js",
"./js/types/radar.js", "./js/Date.js",
"./js/types/timeline.js", "./js/DOM.js",
"./js/plugins/crosshair.js", "./js/EventAdapter.js",
"./js/plugins/download.js", "./js/Text.js",
"./js/plugins/grid.js", "./js/Graph.js",
"./js/plugins/hit.js", "./js/Axis.js",
"./js/plugins/selection.js", "./js/Series.js",
"./js/plugins/labels.js", "./js/types/lines.js",
"./js/plugins/legend.js", "./js/types/bars.js",
"./js/plugins/spreadsheet.js", "./js/types/bubbles.js",
"./js/plugins/titles.js" "./js/types/candles.js",
], "./js/types/gantt.js",
"flotr2-basic": [ "./js/types/markers.js",
"./js/Flotr.js", "./js/types/pie.js",
"./js/DefaultOptions.js", "./js/types/points.js",
"./js/DOM.js", "./js/types/radar.js",
"./js/EventAdapter.js", "./js/types/timeline.js",
"./js/Color.js", "./js/plugins/crosshair.js",
"./js/Date.js", "./js/plugins/download.js",
"./js/Text.js", "./js/plugins/grid.js",
"./js/Graph.js", "./js/plugins/hit.js",
"./js/Axis.js", "./js/plugins/selection.js",
"./js/Series.js", "./js/plugins/labels.js",
"./js/types/lines.js", "./js/plugins/legend.js",
"./js/types/bars.js", "./js/plugins/spreadsheet.js",
"./js/types/markers.js", "./js/plugins/titles.js"
"./js/types/points.js", ],
"./js/plugins/grid.js", "flotr2-basic": [
"./js/plugins/labels.js", "./js/Flotr.js",
"./js/plugins/legend.js", "./js/DefaultOptions.js",
"./js/plugins/titles.js" "./js/DOM.js",
], "./js/EventAdapter.js",
"examples": [ "./js/Color.js",
"./examples/js/Examples.js", "./js/Date.js",
"./examples/js/Example.js", "./js/Text.js",
"./examples/js/Editor.js", "./js/Graph.js",
"./examples/js/Profile.js" "./js/Axis.js",
], "./js/Series.js",
"examples-types": [ "./js/types/lines.js",
"./examples/js/ExampleList.js", "./js/types/bars.js",
"./examples/js/examples/basic.js", "./js/types/markers.js",
"./examples/js/examples/basic-stacked.js", "./js/types/points.js",
"./examples/js/examples/basic-axis.js", "./js/plugins/grid.js",
"./examples/js/examples/basic-bars.js", "./js/plugins/labels.js",
"./examples/js/examples/basic-bars-stacked.js", "./js/plugins/legend.js",
"./examples/js/examples/basic-pie.js", "./js/plugins/titles.js"
"./examples/js/examples/basic-radar.js", ],
"./examples/js/examples/basic-bubble.js", "examples": [
"./examples/js/examples/basic-candle.js", "./examples/js/Examples.js",
"./examples/js/examples/basic-legend.js", "./examples/js/Example.js",
"./examples/js/examples/mouse-tracking.js", "./examples/js/Editor.js",
"./examples/js/examples/mouse-zoom.js", "./examples/js/Profile.js"
"./examples/js/examples/mouse-drag.js", ],
"./examples/js/examples/basic-time.js", "examples-types": [
"./examples/js/examples/negative-values.js", "./examples/js/ExampleList.js",
"./examples/js/examples/click-example.js", "./examples/js/examples/basic.js",
"./examples/js/examples/download-image.js", "./examples/js/examples/basic-stacked.js",
"./examples/js/examples/download-data.js", "./examples/js/examples/basic-axis.js",
"./examples/js/examples/advanced-titles.js", "./examples/js/examples/basic-bars.js",
"./examples/js/examples/color-gradients.js", "./examples/js/examples/basic-bars-stacked.js",
"./examples/js/examples/profile-bars.js", "./examples/js/examples/basic-pie.js",
"./examples/js/examples/basic-timeline.js", "./examples/js/examples/basic-radar.js",
"./examples/js/examples/advanced-markers.js" "./examples/js/examples/basic-bubble.js",
] "./examples/js/examples/basic-candle.js",
} "./examples/js/examples/basic-legend.js",
  "./examples/js/examples/mouse-tracking.js",
  "./examples/js/examples/mouse-zoom.js",
  "./examples/js/examples/mouse-drag.js",
  "./examples/js/examples/basic-time.js",
  "./examples/js/examples/negative-values.js",
  "./examples/js/examples/click-example.js",
  "./examples/js/examples/download-image.js",
  "./examples/js/examples/download-data.js",
  "./examples/js/examples/advanced-titles.js",
  "./examples/js/examples/color-gradients.js",
  "./examples/js/examples/profile-bars.js",
  "./examples/js/examples/basic-timeline.js",
  "./examples/js/examples/advanced-markers.js"
  ]
  }
} }
   
{ {
"JAVASCRIPT": { "JAVASCRIPT": {
"DIST_DIR": "./build", "DIST_DIR": "./build",
"examples": [ "examples": [
"./examples/js/Examples.js", "./examples/js/Examples.js",
"./examples/js/Example.js", "./examples/js/Example.js",
"./examples/js/Editor.js", "./examples/js/Editor.js",
"./examples/js/Profile.js" "./examples/js/Profile.js"
], ],
"examples-types": [ "examples-types": [
"./examples/js/ExampleList.js", "./examples/js/ExampleList.js",
"./examples/js/examples/basic.js", "./examples/js/examples/basic.js",
"./examples/js/examples/basic-stacked.js", "./examples/js/examples/basic-stacked.js",
"./examples/js/examples/basic-stepped.js", "./examples/js/examples/basic-stepped.js",
"./examples/js/examples/basic-axis.js", "./examples/js/examples/basic-axis.js",
"./examples/js/examples/basic-bars.js", "./examples/js/examples/basic-bars.js",
"./examples/js/examples/basic-bars-stacked.js", "./examples/js/examples/basic-bars-stacked.js",
"./examples/js/examples/basic-pie.js", "./examples/js/examples/basic-pie.js",
"./examples/js/examples/basic-radar.js", "./examples/js/examples/basic-radar.js",
"./examples/js/examples/basic-bubble.js", "./examples/js/examples/basic-bubble.js",
"./examples/js/examples/basic-candle.js", "./examples/js/examples/basic-candle.js",
"./examples/js/examples/basic-legend.js", "./examples/js/examples/basic-legend.js",
"./examples/js/examples/mouse-tracking.js", "./examples/js/examples/mouse-tracking.js",
"./examples/js/examples/mouse-zoom.js", "./examples/js/examples/mouse-zoom.js",
"./examples/js/examples/mouse-drag.js", "./examples/js/examples/mouse-drag.js",
"./examples/js/examples/basic-time.js", "./examples/js/examples/basic-time.js",
"./examples/js/examples/negative-values.js", "./examples/js/examples/negative-values.js",
"./examples/js/examples/click-example.js", "./examples/js/examples/click-example.js",
"./examples/js/examples/download-image.js", "./examples/js/examples/download-image.js",
"./examples/js/examples/download-data.js", "./examples/js/examples/download-data.js",
"./examples/js/examples/advanced-titles.js", "./examples/js/examples/advanced-titles.js",
"./examples/js/examples/color-gradients.js", "./examples/js/examples/color-gradients.js",
"./examples/js/examples/profile-bars.js", "./examples/js/examples/profile-bars.js",
"./examples/js/examples/basic-timeline.js", "./examples/js/examples/basic-timeline.js",
"./examples/js/examples/advanced-markers.js" "./examples/js/examples/advanced-markers.js"
] ]
} }
} }
   
{ {
"JAVASCRIPT": { "JAVASCRIPT": {
"DIST_DIR": "./build", "DIST_DIR": "./build",
"flotr2": [ "flotr2": [
"./js/Flotr.js", "./js/Flotr.js",
"./js/DefaultOptions.js", "./js/DefaultOptions.js",
"./js/Color.js", "./js/Color.js",
"./js/Date.js", "./js/Date.js",
"./js/DOM.js", "./js/DOM.js",
"./js/EventAdapter.js", "./js/EventAdapter.js",
"./js/Text.js", "./js/Text.js",
"./js/Graph.js", "./js/Graph.js",
"./js/Axis.js", "./js/Axis.js",
"./js/Series.js", "./js/Series.js",
"./js/types/lines.js", "./js/types/lines.js",
"./js/types/bars.js", "./js/types/bars.js",
"./js/types/bubbles.js", "./js/types/bubbles.js",
"./js/types/candles.js", "./js/types/candles.js",
"./js/types/gantt.js", "./js/types/gantt.js",
"./js/types/markers.js", "./js/types/markers.js",
"./js/types/pie.js", "./js/types/pie.js",
"./js/types/points.js", "./js/types/points.js",
"./js/types/radar.js", "./js/types/radar.js",
"./js/types/timeline.js", "./js/types/timeline.js",
"./js/plugins/crosshair.js", "./js/plugins/crosshair.js",
"./js/plugins/download.js", "./js/plugins/download.js",
"./js/plugins/grid.js", "./js/plugins/grid.js",
"./js/plugins/hit.js", "./js/plugins/hit.js",
"./js/plugins/selection.js", "./js/plugins/selection.js",
"./js/plugins/labels.js", "./js/plugins/labels.js",
"./js/plugins/legend.js", "./js/plugins/legend.js",
"./js/plugins/spreadsheet.js", "./js/plugins/spreadsheet.js",
"./js/plugins/titles.js" "./js/plugins/titles.js"
] ]
} }
} }
   
{ {
"JAVASCRIPT": { "JAVASCRIPT": {
"DIST_DIR": "./build", "DIST_DIR": "./build",
"ie": [ "ie": [
{ "src": "./lib/excanvas.js", "jshint": false }, {
{ "src": "./lib/base64.js", "jshint": false }, "src": "./lib/excanvas.js",
{ "src": "./lib/canvastext.js", "jshint": false } "jshint": false
] },
} {
  "src": "./lib/base64.js",
  "jshint": false
  },
  {
  "src": "./lib/canvastext.js",
  "jshint": false
  }
  ]
  }
} }
   
{ {
"JAVASCRIPT": { "JAVASCRIPT": {
"DIST_DIR": "./build", "DIST_DIR": "./build",
"bean": [ "bean": [
{ "src": "./lib/bean.js", "jshint": false } {
], "src": "./lib/bean.js",
"underscore": [ "jshint": false
{ "src": "./lib/underscore.js", "jshint": false } }
] ],
} "underscore": [
  {
  "src": "./lib/underscore.js",
  "jshint": false
  }
  ]
  }
} }
   
describe('Charts', function () { describe('Charts', function () {
   
var var
width = 480, width = 480,
height = 320, height = 320,
a, b, options, defaults; a, b, options, defaults;
   
defaults = { defaults = {
width : 480, width: 480,
height : 320, height: 320,
color : "rgb(192,216,0)", color: "rgb(192,216,0)",
context : null, context: null,
data : null, data: null,
fill : false, fill: false,
fillColor : null, fillColor: null,
fillOpacity : 0.4, fillOpacity: 0.4,
fillStyle : "rgba(192,216,0,0.4)", fillStyle: "rgba(192,216,0,0.4)",
fontColor : "#545454", fontColor: "#545454",
fontSize : 7.5, fontSize: 7.5,
htmlText : true, htmlText: true,
lineWidth : 2, lineWidth: 2,
shadowSize : 4, shadowSize: 4,
show : false, show: false,
stacked : false, stacked: false,
textEnabled : true, textEnabled: true,
xScale : function (x) { return x; }, xScale: function (x) {
yScale : function (y) { return height - y; } return x;
}; },
  yScale: function (y) {
  return height - y;
  }
  };
   
/** /**
* @param skip bool Skip test against development version (use this when developing test) * @param skip bool Skip test against development version (use this when developing test)
*/ */
function drawTest (data, o, skip) { function drawTest(data, o, skip) {
options.data = data; options.data = data;
if (o) _.extend(options, o); if (o) _.extend(options, o);
   
if (!skip) TestFlotr.graphTypes.lines.draw(options); if (!skip) TestFlotr.graphTypes.lines.draw(options);
options.context = b.getContext('2d'); options.context = b.getContext('2d');
StableFlotr.graphTypes.lines.draw(options); StableFlotr.graphTypes.lines.draw(options);
   
expect(b).toImageDiffEqual(a); expect(b).toImageDiffEqual(a);
} }
   
describe('Lines', function () { describe('Lines', function () {
   
beforeEach(function () { beforeEach(function () {
options = _.clone(defaults); options = _.clone(defaults);
  });
   
  /*
  describe('Data', function () {
  it('gets a range', function () {
  options.stacked = true;
  options.data = [[0, 0], [240, 160], [480, 320]];
  range = TestFlotr.graphTypes.lines.range(options);
  expect(range.min).toEqual(0);
  expect(range.max).toEqual(320);
  });
  });
  */
   
  describe('Draw', function () {
   
  beforeEach(function () {
  this.addMatchers(imagediff.jasmine);
  a = imagediff.createCanvas(width, height);
  b = imagediff.createCanvas(width, height);
  options.context = a.getContext('2d');
  });
   
  it('draws a line chart', function () {
  drawTest([
  [0, 0],
  [240, 300],
  [480, 0]
  ]);
  });
   
  it('skips null values', function () {
  drawTest([
  [0, 0],
  [100, 50],
  [200, null],
  [300, 150],
  [400, 200],
  [480, 240]
  ]);
  });
   
  it('draws two lines', function () {
  // First line
  drawTest([
  [0, 0],
  [240, 160],
  [480, 320]
  ]);
   
  // Second Line
  options.context = a.getContext('2d');
  drawTest([
  [0, 320],
  [240, 160],
  [480, 0]
  ]);
  });
   
  it('fills a line', function () {
  drawTest([
  [0, 0],
  [240, 300],
  [480, 0]
  ], {
  fill: true
  });
  });
   
  it('draws no shadow', function () {
  drawTest([
  [0, 0],
  [240, 300],
  [480, 0]
  ], {
  shadowSize: 0
  });
  });
  });
}); });
   
/*  
describe('Data', function () {  
it('gets a range', function () {  
options.stacked = true;  
options.data = [[0, 0], [240, 160], [480, 320]];  
range = TestFlotr.graphTypes.lines.range(options);  
expect(range.min).toEqual(0);  
expect(range.max).toEqual(320);  
});  
});  
*/  
   
describe('Draw', function () {  
   
beforeEach(function () {  
this.addMatchers(imagediff.jasmine);  
a = imagediff.createCanvas(width, height);  
b = imagediff.createCanvas(width, height);  
options.context = a.getContext('2d');  
});  
   
it('draws a line chart', function () {  
drawTest([  
[0, 0],  
[240, 300],  
[480, 0]  
]);  
});  
   
it('skips null values', function () {  
drawTest([  
[0, 0],  
[100, 50],  
[200, null],  
[300, 150],  
[400, 200],  
[480, 240]  
]);  
});  
   
it('draws two lines', function () {  
// First line  
drawTest([[0, 0], [240, 160], [480, 320]]);  
   
// Second Line  
options.context = a.getContext('2d');  
drawTest([[0, 320], [240, 160], [480, 0]]);  
});  
   
it('fills a line', function () {  
drawTest([  
[0, 0],  
[240, 300],  
[480, 0]  
], {  
fill : true  
});  
});  
   
it('draws no shadow', function () {  
drawTest([  
[0, 0],  
[240, 300],  
[480, 0]  
], {  
shadowSize : 0  
});  
});  
});  
});  
}); });
   
describe('Colors', function () { describe('Colors', function () {
   
describe('Color Construction', function () { describe('Color Construction', function () {
it('should have a color class', function () { it('should have a color class', function () {
expect(TestFlotr.Color).not.toBeUndefined(); expect(TestFlotr.Color).not.toBeUndefined();
  });
   
  it('should create a color', function () {
  var color = new TestFlotr.Color(0, 0, 0, 0);
  expect(color).toBeTruthy();
  });
   
  it('should have rgba attributes', function () {
  var color = new TestFlotr.Color(0, 0, 0, 0);
  expect(color.r).toEqual(0);
  expect(color.g).toEqual(0);
  expect(color.b).toEqual(0);
  expect(color.a).toEqual(1.0);
  });
}); });
   
it('should create a color', function () { describe('Color Manipulation', function () {
var color = new TestFlotr.Color(0, 0, 0, 0);  
expect(color).toBeTruthy(); var
  color;
   
  afterEach(function () {
  color = null;
  });
   
  it('normalizes colors to upper bound', function () {
  color = new TestFlotr.Color(1000, 1000, 1000, 10);
  expect(color.r).toEqual(255);
  expect(color.g).toEqual(255);
  expect(color.b).toEqual(255);
  expect(color.a).toEqual(1.0);
  });
   
  it('normalizes colors to lower bound', function () {
  color = new TestFlotr.Color(-1000, -1000, -1000, -10);
  expect(color.r).toEqual(0);
  expect(color.g).toEqual(0);
  expect(color.b).toEqual(0);
  expect(color.a).toEqual(0.0);
  });
   
  it('scales colors', function () {
  color = new TestFlotr.Color(200, 200, 200, 1.0);
  color.scale(.5, .5, .5, .5);
  expect(color.r).toEqual(100);
  expect(color.g).toEqual(100);
  expect(color.b).toEqual(100);
  expect(color.a).toEqual(0.5);
  });
}); });
   
it('should have rgba attributes', function () { describe('Color Conversion', function () {
var color = new TestFlotr.Color(0, 0, 0, 0);  
expect(color.r).toEqual(0); var
expect(color.g).toEqual(0); color;
expect(color.b).toEqual(0);  
expect(color.a).toEqual(1.0); beforeEach(function () {
  color = new TestFlotr.Color(200, 200, 200, 1.0);
  });
  afterEach(function () {
  color = null;
  });
   
  it('should convert colors to strings, rgb', function () {
  expect(color.toString()).toEqual('rgb(200,200,200)');
  });
   
  it('should convert colors to strings, rgba', function () {
  color.a = 0.5;
  color.normalize();
  expect(color.toString()).toEqual('rgba(200,200,200,0.5)');
  });
   
  it('should clone colors', function () {
   
  var
  color2 = color.clone();
   
  expect(color.toString()).toEqual(color2.toString());
   
  color.a = 0.5;
  color.normalize();
  color2 = color.clone();
  expect(color.toString()).toEqual(color2.toString());
  });
}); });
});  
   
describe('Color Manipulation', function () {  
   
var  
color;  
   
afterEach(function () {  
color = null;  
});  
   
it('normalizes colors to upper bound', function () {  
color = new TestFlotr.Color(1000, 1000, 1000, 10);  
expect(color.r).toEqual(255);  
expect(color.g).toEqual(255);  
expect(color.b).toEqual(255);  
expect(color.a).toEqual(1.0);  
});  
   
it('normalizes colors to lower bound', function () {  
color = new TestFlotr.Color(-1000, -1000, -1000, -10);  
expect(color.r).toEqual(0);  
expect(color.g).toEqual(0);  
expect(color.b).toEqual(0);  
expect(color.a).toEqual(0.0);  
});  
   
it('scales colors', function () {  
color = new TestFlotr.Color(200, 200, 200, 1.0);  
color.scale(.5, .5, .5, .5);  
expect(color.r).toEqual(100);  
expect(color.g).toEqual(100);  
expect(color.b).toEqual(100);  
expect(color.a).toEqual(0.5);  
});  
});  
   
describe('Color Conversion', function () {  
   
var  
color;  
   
beforeEach(function () {  
color = new TestFlotr.Color(200, 200, 200, 1.0);  
});  
afterEach(function () {  
color = null;  
});  
   
it('should convert colors to strings, rgb', function () {  
expect(color.toString()).toEqual('rgb(200,200,200)');  
});  
   
it('should convert colors to strings, rgba', function () {  
color.a = 0.5;  
color.normalize();  
expect(color.toString()).toEqual('rgba(200,200,200,0.5)');  
});  
   
it('should clone colors', function () {  
   
var  
color2 = color.clone();  
   
expect(color.toString()).toEqual(color2.toString());  
   
color.a = 0.5;  
color.normalize();  
color2 = color.clone();  
expect(color.toString()).toEqual(color2.toString());  
});  
});  
}); });
   
describe('Flotr', function () { describe('Flotr', function () {
   
describe('Plots', function () { describe('Plots', function () {
   
var var
nodeA, nodeB, nodeA, nodeB,
a, b; a, b;
   
beforeEach(function () { beforeEach(function () {
   
// Add imagediff matchers // Add imagediff matchers
this.addMatchers(imagediff.jasmine); this.addMatchers(imagediff.jasmine);
   
nodeA = buildNode(); nodeA = buildNode();
nodeB = buildNode(); nodeB = buildNode();
  });
   
  afterEach(function () {
  destroyNode(nodeA);
  destroyNode(nodeB);
  a = null;
  b = null;
  Flotr = null;
  });
   
  _.each(TestFlotr.ExampleList.examples, function (example, key) {
   
  it('should draw a `' + example.name + '`line graph', function () {
   
  executeExampleTest(example, StableFlotr, nodeA);
  executeExampleTest(example, TestFlotr, nodeB);
   
  if (example.timeout) {
  waits(example.timeout);
  runs(function () {
  expect(nodeB.graph.ctx).toImageDiffEqual(nodeA.graph.ctx, example.tolerance || 0);
  });
  } else {
  expect(nodeB.graph.ctx).toImageDiffEqual(nodeA.graph.ctx, example.tolerance || 0);
  }
  });
  });
   
  // Helpers
   
  function executeExampleTest(example, flotr, node) {
  Math.seedrandom(example.key);
  Flotr = flotr;
  example.callback.apply(this, [node].concat(example.args || []));
  }
   
  function buildNode() {
  var node = document.createElement('div');
  document.body.appendChild(node);
  node.style.width = '320px';
  node.style.height = '240px';
  return node;
  }
   
  function destroyNode(node) {
  document.body.removeChild(node);
  }
}); });
   
afterEach(function () { describe('Main', function () {
destroyNode(nodeA);  
destroyNode(nodeB); it('gets a tick size', function () {
a = null; expect(TestFlotr.getTickSize).not.toBeUndefined();
b = null; expect(TestFlotr.getTickSize(10, 0, 100, 1)).toEqual(10);
Flotr = null; expect(TestFlotr.getTickSize(20, 0, 100, 1)).toEqual(5);
  expect(TestFlotr.getTickSize(5, 10, 110, 1)).toEqual(20);
  expect(TestFlotr.getTickSize(0, 0, 10, 1)).toEqual(Number.POSITIVE_INFINITY);
  expect(isNaN(TestFlotr.getTickSize(0, 0, -10, 1))).toBeTruthy();
  });
}); });
   
_.each(TestFlotr.ExampleList.examples, function (example, key) {  
   
it('should draw a `' + example.name + '`line graph', function () {  
   
executeExampleTest(example, StableFlotr, nodeA);  
executeExampleTest(example, TestFlotr, nodeB);  
   
if (example.timeout) {  
waits(example.timeout);  
runs (function () {  
expect(nodeB.graph.ctx).toImageDiffEqual(nodeA.graph.ctx, example.tolerance || 0);  
});  
} else {  
expect(nodeB.graph.ctx).toImageDiffEqual(nodeA.graph.ctx, example.tolerance || 0);  
}  
});  
});  
   
// Helpers  
   
function executeExampleTest (example, flotr, node) {  
Math.seedrandom(example.key);  
Flotr = flotr;  
example.callback.apply(this, [node].concat(example.args || []));  
}  
   
function buildNode () {  
var node = document.createElement('div');  
document.body.appendChild(node);  
node.style.width = '320px';  
node.style.height = '240px';  
return node;  
}  
   
function destroyNode (node) {  
document.body.removeChild(node);  
}  
});  
   
describe('Main', function () {  
   
it('gets a tick size', function () {  
expect(TestFlotr.getTickSize).not.toBeUndefined();  
expect(TestFlotr.getTickSize(10, 0, 100, 1)).toEqual(10);  
expect(TestFlotr.getTickSize(20, 0, 100, 1)).toEqual(5);  
expect(TestFlotr.getTickSize(5, 10, 110, 1)).toEqual(20);  
expect(TestFlotr.getTickSize(0, 0, 10, 1)).toEqual(Number.POSITIVE_INFINITY);  
expect(isNaN(TestFlotr.getTickSize(0, 0, -10, 1))).toBeTruthy();  
});  
});  
}); });
   
describe('Graph', function () { describe('Graph', function () {
   
describe('Options', function () { describe('Options', function () {
var var
nodeA, nodeB, nodeA, nodeB,
a, b, x, i, a, b, x, i,
d1 = [], d1 = [],
options = {}; options = {};
   
for (i = 0; i < 100; i++) { for (i = 0; i < 100; i++) {
x = (i*1000*3600*24*36.5); x = (i * 1000 * 3600 * 24 * 36.5);
d1.push([x, i+Math.random()*30+Math.sin(i/20+Math.random()*2)*20+Math.sin(i/10+Math.random())*10]); d1.push([x, i + Math.random() * 30 + Math.sin(i / 20 + Math.random() * 2) * 20 + Math.sin(i / 10 + Math.random()) * 10]);
} }
   
options = { options = {
xaxis : { xaxis: {
mode : 'time', mode: 'time',
labelsAngle : 45 labelsAngle: 45
}, },
selection : { selection: {
mode : 'x' mode: 'x'
}, },
HtmlText : false, HtmlText: false,
}; };
   
beforeEach(function () { beforeEach(function () {
nodeA = buildNode(); nodeA = buildNode();
Flotr = TestFlotr; Flotr = TestFlotr;
  });
   
  afterEach(function () {
  destroyNode(nodeA);
  a = b = null;
  Flotr = null;
  });
   
  it('should override nested default options with user options', function () {
  a = new TestFlotr.Graph(nodeA, d1, options);
  expect(a.options.xaxis.mode).toEqual(options.xaxis.mode);
  });
   
  it('should retain default options if user option\'s nested object does not define property', function () {
  a = new TestFlotr.Graph(nodeA, d1, options);
  expect(a.options.xaxis.tickFormatter).toBeTruthy();
  });
   
  it('should not affect default options when modifying graph options (objects)', function () {
  a = new TestFlotr.Graph(nodeA, d1, options);
  a.options.x2axis = {
  titleAlign: 'left'
  };
  a.options.xaxis.scaling = 'logarithmic';
  expect(TestFlotr.defaultOptions.xaxis.scaling).toEqual('linear');
  expect(TestFlotr.defaultOptions.x2axis.titleAlign).toBeFalsy();
  });
   
  /*
  it('should not affect default options when modifying graph options (arrays)', function() {
  a = new TestFlotr.Graph(nodeA, d1, options);
  a.options.colors[1] = '#bada55';
  expect(TestFlotr.defaultOptions.colors[1]).toNotBe('#bada55');
  });
  */
   
}); });
   
afterEach(function () { function buildNode() {
destroyNode(nodeA); var node = document.createElement('div');
a = b = null; document.body.appendChild(node);
Flotr = null; node.style.width = '320px';
}); node.style.height = '240px';
  return node;
  }
   
it('should override nested default options with user options', function() { function destroyNode(node) {
a = new TestFlotr.Graph(nodeA, d1, options); document.body.removeChild(node);
expect(a.options.xaxis.mode).toEqual(options.xaxis.mode); }
});  
   
it('should retain default options if user option\'s nested object does not define property', function() {  
a = new TestFlotr.Graph(nodeA, d1, options);  
expect(a.options.xaxis.tickFormatter).toBeTruthy();  
});  
   
it('should not affect default options when modifying graph options (objects)', function() {  
a = new TestFlotr.Graph(nodeA, d1, options);  
a.options.x2axis = {  
titleAlign : 'left'  
};  
a.options.xaxis.scaling = 'logarithmic';  
expect(TestFlotr.defaultOptions.xaxis.scaling).toEqual('linear');  
expect(TestFlotr.defaultOptions.x2axis.titleAlign).toBeFalsy();  
});  
   
/*  
it('should not affect default options when modifying graph options (arrays)', function() {  
a = new TestFlotr.Graph(nodeA, d1, options);  
a.options.colors[1] = '#bada55';  
expect(TestFlotr.defaultOptions.colors[1]).toNotBe('#bada55');  
});  
*/  
   
});  
   
function buildNode () {  
var node = document.createElement('div');  
document.body.appendChild(node);  
node.style.width = '320px';  
node.style.height = '240px';  
return node;  
}  
   
function destroyNode (node) {  
document.body.removeChild(node);  
}  
   
}); });
   
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
"http://www.w3.org/TR/html4/loose.dtd"> "http://www.w3.org/TR/html4/loose.dtd">
<html> <html>
<head> <head>
<title>Jasmine Spec Runner</title> <title>Jasmine Spec Runner</title>
   
<link rel="shortcut icon" type="image/png" href="../lib/jasmine/jasmine_favicon.png"> <link rel="shortcut icon" type="image/png" href="../lib/jasmine/jasmine_favicon.png">
   
<link rel="stylesheet" type="text/css" href="../lib/jasmine/jasmine.css"> <link rel="stylesheet" type="text/css" href="../lib/jasmine/jasmine.css">
<script type="text/javascript" src="../lib/jasmine/jasmine.js"></script> <script type="text/javascript" src="../lib/jasmine/jasmine.js"></script>
<script type="text/javascript" src="../lib/jasmine/jasmine-html.js"></script> <script type="text/javascript" src="../lib/jasmine/jasmine-html.js"></script>
<script type="text/javascript" src="../lib/imagediff.js"></script> <script type="text/javascript" src="../lib/imagediff.js"></script>
<script type="text/javascript" src="../examples/lib/randomseed.js"></script> <script type="text/javascript" src="../examples/lib/randomseed.js"></script>
   
<!-- include source files here... --> <!-- include source files here... -->
<script type="text/javascript" src="js/flotr2.stable.js"></script> <script type="text/javascript" src="js/flotr2.stable.js"></script>
<script type="text/javascript"> <script type="text/javascript">
var StableFlotr = Flotr.noConflict(); var StableFlotr = Flotr.noConflict();
</script> </script>
   
<script src="../lib/bean.js"></script> <script src="../lib/bean.js"></script>
<script src="../lib/underscore.js"></script> <script src="../lib/underscore.js"></script>
<script src="../js/Flotr.js"></script> <script src="../js/Flotr.js"></script>
<script src="../js/DefaultOptions.js"></script> <script src="../js/DefaultOptions.js"></script>
<script src="../js/Color.js"></script> <script src="../js/Color.js"></script>
<script src="../js/Date.js"></script> <script src="../js/Date.js"></script>
<script src="../js/DOM.js"></script> <script src="../js/DOM.js"></script>
<script src="../js/EventAdapter.js"></script> <script src="../js/EventAdapter.js"></script>
<script src="../js/Text.js"></script> <script src="../js/Text.js"></script>
<script src="../js/Graph.js"></script> <script src="../js/Graph.js"></script>
<script src="../js/Axis.js"></script> <script src="../js/Axis.js"></script>
<script src="../js/Series.js"></script> <script src="../js/Series.js"></script>
<script src="../js/types/lines.js"></script> <script src="../js/types/lines.js"></script>
<script src="../js/types/bars.js"></script> <script src="../js/types/bars.js"></script>
<script src="../js/types/bubbles.js"></script> <script src="../js/types/bubbles.js"></script>
<script src="../js/types/candles.js"></script> <script src="../js/types/candles.js"></script>
<script src="../js/types/gantt.js"></script> <script src="../js/types/gantt.js"></script>
<script src="../js/types/markers.js"></script> <script src="../js/types/markers.js"></script>
<script src="../js/types/pie.js"></script> <script src="../js/types/pie.js"></script>
<script src="../js/types/points.js"></script> <script src="../js/types/points.js"></script>
<script src="../js/types/radar.js"></script> <script src="../js/types/radar.js"></script>
<script src="../js/types/timeline.js"></script> <script src="../js/types/timeline.js"></script>
<script src="../js/plugins/crosshair.js"></script> <script src="../js/plugins/crosshair.js"></script>
<script src="../js/plugins/download.js"></script> <script src="../js/plugins/download.js"></script>
<script src="../js/plugins/grid.js"></script> <script src="../js/plugins/grid.js"></script>
<script src="../js/plugins/hit.js"></script> <script src="../js/plugins/hit.js"></script>
<script src="../js/plugins/selection.js"></script> <script src="../js/plugins/selection.js"></script>
<script src="../js/plugins/labels.js"></script> <script src="../js/plugins/labels.js"></script>
<script src="../js/plugins/legend.js"></script> <script src="../js/plugins/legend.js"></script>
<script src="../js/plugins/spreadsheet.js"></script> <script src="../js/plugins/spreadsheet.js"></script>
<script src="../js/plugins/titles.js"></script> <script src="../js/plugins/titles.js"></script>
<script src="../js/charts/lines.js"></script> <script src="../js/charts/lines.js"></script>
   
<!-- Test Examples --> <!-- Test Examples -->
<script type="text/javascript" src="../flotr2.examples.types.js"></script> <script type="text/javascript" src="../flotr2.examples.types.js"></script>
<script type="text/javascript" src="js/test-background.js"></script> <script type="text/javascript" src="js/test-background.js"></script>
<script type="text/javascript" src="js/test-boundaries.js"></script> <script type="text/javascript" src="js/test-boundaries.js"></script>
<script type="text/javascript" src="js/test-mountain-nulls.js"></script> <script type="text/javascript" src="js/test-mountain-nulls.js"></script>
   
<script type="text/javascript"> <script type="text/javascript">
var TestFlotr = Flotr.noConflict(); var TestFlotr = Flotr.noConflict();
</script> </script>
   
<!-- include spec files here... --> <!-- include spec files here... -->
<script type="text/javascript" src="Flotr.js"></script> <script type="text/javascript" src="Flotr.js"></script>
<script type="text/javascript" src="Color.js"></script> <script type="text/javascript" src="Color.js"></script>
<script type="text/javascript" src="Graph.js"></script> <script type="text/javascript" src="Graph.js"></script>
<script type="text/javascript" src="Chart.js"></script> <script type="text/javascript" src="Chart.js"></script>
   
</head> </head>
   
<body> <body>
</body> </body>
<script type="text/javascript"> <script type="text/javascript">
(function() { (function () {
var jasmineEnv = jasmine.getEnv(); var jasmineEnv = jasmine.getEnv();
jasmineEnv.updateInterval = 1000; jasmineEnv.updateInterval = 1000;
   
var reporter = new jasmine.HtmlReporter(); var reporter = new jasmine.HtmlReporter();
   
jasmineEnv.addReporter(reporter); jasmineEnv.addReporter(reporter);
   
jasmineEnv.specFilter = function(spec) { jasmineEnv.specFilter = function (spec) {
return reporter.specFilter(spec); return reporter.specFilter(spec);
}; };
   
var currentWindowOnload = window.onload; var currentWindowOnload = window.onload;
   
window.onload = function() { window.onload = function () {
if (currentWindowOnload) { if (currentWindowOnload) {
currentWindowOnload(); currentWindowOnload();
} }
execJasmine(); execJasmine();
}; };
   
function execJasmine() { function execJasmine() {
jasmineEnv.execute(); jasmineEnv.execute();
} }
   
})(); })();
</script> </script>
</html> </html>
   
/*! /*!
* bean.js - copyright Jacob Thornton 2011 * bean.js - copyright Jacob Thornton 2011
* https://github.com/fat/bean * https://github.com/fat/bean
* MIT License * MIT License
* special thanks to: * special thanks to:
* dean edwards: http://dean.edwards.name/ * dean edwards: http://dean.edwards.name/
* dperini: https://github.com/dperini/nwevents * dperini: https://github.com/dperini/nwevents
* the entire mootools team: github.com/mootools/mootools-core * the entire mootools team: github.com/mootools/mootools-core
*/ */
/*global module:true, define:true*/ /*global module:true, define:true*/
!function (name, context, definition) { !function (name, context, definition) {
if (typeof module !== 'undefined') module.exports = definition(name, context); if (typeof module !== 'undefined') module.exports = definition(name, context);
else if (typeof define === 'function' && typeof define.amd === 'object') define(definition); else if (typeof define === 'function' && typeof define.amd === 'object') define(definition);
else context[name] = definition(name, context); else context[name] = definition(name, context);
}('bean', this, function (name, context) { }('bean', this, function (name, context) {
var win = window var win = window
, old = context[name] , old = context[name]
, overOut = /over|out/ , overOut = /over|out/
, namespaceRegex = /[^\.]*(?=\..*)\.|.*/ , namespaceRegex = /[^\.]*(?=\..*)\.|.*/
, nameRegex = /\..*/ , nameRegex = /\..*/
, addEvent = 'addEventListener' , addEvent = 'addEventListener'
, attachEvent = 'attachEvent' , attachEvent = 'attachEvent'
, removeEvent = 'removeEventListener' , removeEvent = 'removeEventListener'
, detachEvent = 'detachEvent' , detachEvent = 'detachEvent'
, doc = document || {} , doc = document || {}
, root = doc.documentElement || {} , root = doc.documentElement || {}
, W3C_MODEL = root[addEvent] , W3C_MODEL = root[addEvent]
, eventSupport = W3C_MODEL ? addEvent : attachEvent , eventSupport = W3C_MODEL ? addEvent : attachEvent
, slice = Array.prototype.slice , slice = Array.prototype.slice
, mouseTypeRegex = /click|mouse|menu|drag|drop/i , mouseTypeRegex = /click|mouse|menu|drag|drop/i
, touchTypeRegex = /^touch|^gesture/i , touchTypeRegex = /^touch|^gesture/i
, ONE = { one: 1 } // singleton for quick matching making add() do one() , ONE = { one: 1 } // singleton for quick matching making add() do one()
   
, nativeEvents = (function (hash, events, i) { , nativeEvents = (function (hash, events, i) {
for (i = 0; i < events.length; i++) for (i = 0; i < events.length; i++)
hash[events[i]] = 1 hash[events[i]] = 1
return hash return hash
})({}, ( })({}, (
'click dblclick mouseup mousedown contextmenu ' + // mouse buttons 'click dblclick mouseup mousedown contextmenu ' + // mouse buttons
'mousewheel DOMMouseScroll ' + // mouse wheel 'mousewheel DOMMouseScroll ' + // mouse wheel
'mouseover mouseout mousemove selectstart selectend ' + // mouse movement 'mouseover mouseout mousemove selectstart selectend ' + // mouse movement
'keydown keypress keyup ' + // keyboard 'keydown keypress keyup ' + // keyboard
'orientationchange ' + // mobile 'orientationchange ' + // mobile
'focus blur change reset select submit ' + // form elements 'focus blur change reset select submit ' + // form elements
'load unload beforeunload resize move DOMContentLoaded readystatechange ' + // window 'load unload beforeunload resize move DOMContentLoaded readystatechange ' + // window
'error abort scroll ' + // misc 'error abort scroll ' + // misc
(W3C_MODEL ? // element.fireEvent('onXYZ'... is not forgiving if we try to fire an event (W3C_MODEL ? // element.fireEvent('onXYZ'... is not forgiving if we try to fire an event
// that doesn't actually exist, so make sure we only do these on newer browsers // that doesn't actually exist, so make sure we only do these on newer browsers
'show ' + // mouse buttons 'show ' + // mouse buttons
'input invalid ' + // form elements 'input invalid ' + // form elements
'touchstart touchmove touchend touchcancel ' + // touch 'touchstart touchmove touchend touchcancel ' + // touch
'gesturestart gesturechange gestureend ' + // gesture 'gesturestart gesturechange gestureend ' + // gesture
'message readystatechange pageshow pagehide popstate ' + // window 'message readystatechange pageshow pagehide popstate ' + // window
'hashchange offline online ' + // window 'hashchange offline online ' + // window
'afterprint beforeprint ' + // printing 'afterprint beforeprint ' + // printing
'dragstart dragenter dragover dragleave drag drop dragend ' + // dnd 'dragstart dragenter dragover dragleave drag drop dragend ' + // dnd
'loadstart progress suspend emptied stalled loadmetadata ' + // media 'loadstart progress suspend emptied stalled loadmetadata ' + // media
'loadeddata canplay canplaythrough playing waiting seeking ' + // media 'loadeddata canplay canplaythrough playing waiting seeking ' + // media
'seeked ended durationchange timeupdate play pause ratechange ' + // media 'seeked ended durationchange timeupdate play pause ratechange ' + // media
'volumechange cuechange ' + // media 'volumechange cuechange ' + // media
'checking noupdate downloading cached updateready obsolete ' + // appcache 'checking noupdate downloading cached updateready obsolete ' + // appcache
'' : '') '' : '')
).split(' ') ).split(' ')
) )
   
, customEvents = (function () { , customEvents = (function () {
function isDescendant(parent, node) { function isDescendant(parent, node) {
while ((node = node.parentNode) !== null) { while ((node = node.parentNode) !== null) {
if (node === parent) return true if (node === parent) return true
} }
return false  
}  
   
function check(event) {  
var related = event.relatedTarget  
if (!related) return related === null  
return (related !== this && related.prefix !== 'xul' && !/document/.test(this.toString()) && !isDescendant(this, related))  
}  
   
return {  
mouseenter: { base: 'mouseover', condition: check }  
, mouseleave: { base: 'mouseout', condition: check }  
, mousewheel: { base: /Firefox/.test(navigator.userAgent) ? 'DOMMouseScroll' : 'mousewheel' }  
}  
})()  
   
, fixEvent = (function () {  
var commonProps = 'altKey attrChange attrName bubbles cancelable ctrlKey currentTarget detail eventPhase getModifierState isTrusted metaKey relatedNode relatedTarget shiftKey srcElement target timeStamp type view which'.split(' ')  
, mouseProps = commonProps.concat('button buttons clientX clientY dataTransfer fromElement offsetX offsetY pageX pageY screenX screenY toElement'.split(' '))  
, keyProps = commonProps.concat('char charCode key keyCode'.split(' '))  
, touchProps = commonProps.concat('touches targetTouches changedTouches scale rotation'.split(' '))  
, preventDefault = 'preventDefault'  
, createPreventDefault = function (event) {  
return function () {  
if (event[preventDefault])  
event[preventDefault]()  
else  
event.returnValue = false  
}  
}  
, stopPropagation = 'stopPropagation'  
, createStopPropagation = function (event) {  
return function () {  
if (event[stopPropagation])  
event[stopPropagation]()  
else  
event.cancelBubble = true  
}  
}  
, createStop = function (synEvent) {  
return function () {  
synEvent[preventDefault]()  
synEvent[stopPropagation]()  
synEvent.stopped = true  
}  
}  
, copyProps = function (event, result, props) {  
var i, p  
for (i = props.length; i--;) {  
p = props[i]  
if (!(p in result) && p in event) result[p] = event[p]  
}  
}  
   
return function (event, isNative) {  
var result = { originalEvent: event, isNative: isNative }  
if (!event)  
return result  
   
var props  
, type = event.type  
, target = event.target || event.srcElement  
   
result[preventDefault] = createPreventDefault(event)  
result[stopPropagation] = createStopPropagation(event)  
result.stop = createStop(result)  
result.target = target && target.nodeType === 3 ? target.parentNode : target  
   
if (isNative) { // we only need basic augmentation on custom events, the rest is too expensive  
if (type.indexOf('key') !== -1) {  
props = keyProps  
result.keyCode = event.which || event.keyCode  
} else if (mouseTypeRegex.test(type)) {  
props = mouseProps  
result.rightClick = event.which === 3 || event.button === 2  
result.pos = { x: 0, y: 0 }  
if (event.pageX || event.pageY) {  
result.clientX = event.pageX  
result.clientY = event.pageY  
} else if (event.clientX || event.clientY) {  
result.clientX = event.clientX + doc.body.scrollLeft + root.scrollLeft  
result.clientY = event.clientY + doc.body.scrollTop + root.scrollTop  
}  
if (overOut.test(type))  
result.relatedTarget = event.relatedTarget || event[(type === 'mouseover' ? 'from' : 'to') + 'Element']  
} else if (touchTypeRegex.test(type)) {  
props = touchProps  
}  
copyProps(event, result, props || commonProps)  
}  
return result  
}  
})()  
   
// if we're in old IE we can't do onpropertychange on doc or win so we use doc.documentElement for both  
, targetElement = function (element, isNative) {  
return !W3C_MODEL && !isNative && (element === doc || element === win) ? root : element  
}  
   
// we use one of these per listener, of any type  
, RegEntry = (function () {  
function entry(element, type, handler, original, namespaces) {  
this.element = element  
this.type = type  
this.handler = handler  
this.original = original  
this.namespaces = namespaces  
this.custom = customEvents[type]  
this.isNative = nativeEvents[type] && element[eventSupport]  
this.eventType = W3C_MODEL || this.isNative ? type : 'propertychange'  
this.customType = !W3C_MODEL && !this.isNative && type  
this.target = targetElement(element, this.isNative)  
this.eventSupport = this.target[eventSupport]  
}  
   
entry.prototype = {  
// given a list of namespaces, is our entry in any of them?  
inNamespaces: function (checkNamespaces) {  
var i, j  
if (!checkNamespaces)  
return true  
if (!this.namespaces)  
return false return false
for (i = checkNamespaces.length; i--;) { }
for (j = this.namespaces.length; j--;) {  
if (checkNamespaces[i] === this.namespaces[j]) function check(event) {
return true var related = event.relatedTarget
} if (!related) return related === null
} return (related !== this && related.prefix !== 'xul' && !/document/.test(this.toString()) && !isDescendant(this, related))
return false }
}  
  return {
// match by element, original fn (opt), handler fn (opt) mouseenter: { base: 'mouseover', condition: check }, mouseleave: { base: 'mouseout', condition: check }, mousewheel: { base: /Firefox/.test(navigator.userAgent) ? 'DOMMouseScroll' : 'mousewheel' }
, matches: function (checkElement, checkOriginal, checkHandler) { }
return this.element === checkElement && })()
(!checkOriginal || this.original === checkOriginal) &&  
(!checkHandler || this.handler === checkHandler) , fixEvent = (function () {
} var commonProps = 'altKey attrChange attrName bubbles cancelable ctrlKey currentTarget detail eventPhase getModifierState isTrusted metaKey relatedNode relatedTarget shiftKey srcElement target timeStamp type view which'.split(' ')
} , mouseProps = commonProps.concat('button buttons clientX clientY dataTransfer fromElement offsetX offsetY pageX pageY screenX screenY toElement'.split(' '))
  , keyProps = commonProps.concat('char charCode key keyCode'.split(' '))
return entry , touchProps = commonProps.concat('touches targetTouches changedTouches scale rotation'.split(' '))
})() , preventDefault = 'preventDefault'
  , createPreventDefault = function (event) {
, registry = (function () { return function () {
// our map stores arrays by event type, just because it's better than storing if (event[preventDefault])
// everything in a single array. uses '$' as a prefix for the keys for safety event[preventDefault]()
var map = {} else
  event.returnValue = false
// generic functional search of our registry for matching listeners, }
// `fn` returns false to break out of the loop }
, forAll = function (element, type, original, handler, fn) { , stopPropagation = 'stopPropagation'
if (!type || type === '*') { , createStopPropagation = function (event) {
// search the whole registry return function () {
for (var t in map) { if (event[stopPropagation])
if (t.charAt(0) === '$') event[stopPropagation]()
forAll(element, t.substr(1), original, handler, fn) else
} event.cancelBubble = true
} else { }
var i = 0, l, list = map['$' + type], all = element === '*' }
if (!list) , createStop = function (synEvent) {
return return function () {
for (l = list.length; i < l; i++) { synEvent[preventDefault]()
if (all || list[i].matches(element, original, handler)) synEvent[stopPropagation]()
if (!fn(list[i], list, i, type)) synEvent.stopped = true
return }
} }
} , copyProps = function (event, result, props) {
} var i, p
  for (i = props.length; i--;) {
, has = function (element, type, original) { p = props[i]
// we're not using forAll here simply because it's a bit slower and this if (!(p in result) && p in event) result[p] = event[p]
// needs to be fast }
var i, list = map['$' + type] }
if (list) {  
for (i = list.length; i--;) { return function (event, isNative) {
if (list[i].matches(element, original, null)) var result = { originalEvent: event, isNative: isNative }
return true if (!event)
} return result
}  
return false var props
} , type = event.type
  , target = event.target || event.srcElement
, get = function (element, type, original) {  
var entries = [] result[preventDefault] = createPreventDefault(event)
forAll(element, type, original, null, function (entry) { return entries.push(entry) }) result[stopPropagation] = createStopPropagation(event)
return entries result.stop = createStop(result)
} result.target = target && target.nodeType === 3 ? target.parentNode : target
   
, put = function (entry) { if (isNative) { // we only need basic augmentation on custom events, the rest is too expensive
(map['$' + entry.type] || (map['$' + entry.type] = [])).push(entry) if (type.indexOf('key') !== -1) {
return entry props = keyProps
} result.keyCode = event.which || event.keyCode
  } else if (mouseTypeRegex.test(type)) {
, del = function (entry) { props = mouseProps
forAll(entry.element, entry.type, null, entry.handler, function (entry, list, i) { result.rightClick = event.which === 3 || event.button === 2
list.splice(i, 1) result.pos = { x: 0, y: 0 }
if (list.length === 0) if (event.pageX || event.pageY) {
delete map['$' + entry.type] result.clientX = event.pageX
return false result.clientY = event.pageY
}) } else if (event.clientX || event.clientY) {
} result.clientX = event.clientX + doc.body.scrollLeft + root.scrollLeft
  result.clientY = event.clientY + doc.body.scrollTop + root.scrollTop
  }
  if (overOut.test(type))
  result.relatedTarget = event.relatedTarget || event[(type === 'mouseover' ? 'from' : 'to') + 'Element']
  } else if (touchTypeRegex.test(type)) {
  props = touchProps
  }
  copyProps(event, result, props || commonProps)
  }
  return result
  }
  })()
   
  // if we're in old IE we can't do onpropertychange on doc or win so we use doc.documentElement for both
  , targetElement = function (element, isNative) {
  return !W3C_MODEL && !isNative && (element === doc || element === win) ? root : element
  }
   
  // we use one of these per listener, of any type
  , RegEntry = (function () {
  function entry(element, type, handler, original, namespaces) {
  this.element = element
  this.type = type
  this.handler = handler
  this.original = original
  this.namespaces = namespaces
  this.custom = customEvents[type]
  this.isNative = nativeEvents[type] && element[eventSupport]
  this.eventType = W3C_MODEL || this.isNative ? type : 'propertychange'
  this.customType = !W3C_MODEL && !this.isNative && type
  this.target = targetElement(element, this.isNative)
  this.eventSupport = this.target[eventSupport]
  }
   
  entry.prototype = {
  // given a list of namespaces, is our entry in any of them?
  inNamespaces: function (checkNamespaces) {
  var i, j
  if (!checkNamespaces)
  return true
  if (!this.namespaces)
  return false
  for (i = checkNamespaces.length; i--;) {
  for (j = this.namespaces.length; j--;) {
  if (checkNamespaces[i] === this.namespaces[j])
  return true
  }
  }
  return false
  }
   
  // match by element, original fn (opt), handler fn (opt)
  , matches: function (checkElement, checkOriginal, checkHandler) {
  return this.element === checkElement &&
  (!checkOriginal || this.original === checkOriginal) &&
  (!checkHandler || this.handler === checkHandler)
  }
  }
   
  return entry
  })()
   
  , registry = (function () {
  // our map stores arrays by event type, just because it's better than storing
  // everything in a single array. uses '$' as a prefix for the keys for safety
  var map = {}
   
  // generic functional search of our registry for matching listeners,
  // `fn` returns false to break out of the loop
  , forAll = function (element, type, original, handler, fn) {
  if (!type || type === '*') {
  // search the whole registry
  for (var t in map) {
  if (t.charAt(0) === '$')
  forAll(element, t.substr(1), original, handler, fn)
  }
  } else {
  var i = 0, l, list = map['$' + type], all = element === '*'
  if (!list)
  return
  for (l = list.length; i < l; i++) {
  if (all || list[i].matches(element, original, handler))
  if (!fn(list[i], list, i, type))
  return
  }
  }
  }
   
  , has = function (element, type, original) {
  // we're not using forAll here simply because it's a bit slower and this
  // needs to be fast
  var i, list = map['$' + type]
  if (list) {
  for (i = list.length; i--;) {
  if (list[i].matches(element, original, null))
  return true
  }
  }
  return false
  }
   
  , get = function (element, type, original) {
  var entries = []
  forAll(element, type, original, null, function (entry) {
  return entries.push(entry)
  })
  return entries
  }
   
  , put = function (entry) {
  (map['$' + entry.type] || (map['$' + entry.type] = [])).push(entry)
  return entry
  }
   
  , del = function (entry) {
  forAll(entry.element, entry.type, null, entry.handler, function (entry, list, i) {
  list.splice(i, 1)
  if (list.length === 0)
  delete map['$' + entry.type]
  return false
  })
  }
   
// dump all entries, used for onunload // dump all entries, used for onunload
, entries = function () { , entries = function () {
var t, entries = [] var t, entries = []
for (t in map) { for (t in map) {
if (t.charAt(0) === '$') if (t.charAt(0) === '$')
entries = entries.concat(map[t]) entries = entries.concat(map[t])
} }
return entries return entries
} }
   
return { has: has, get: get, put: put, del: del, entries: entries } return { has: has, get: get, put: put, del: del, entries: entries }
})() })()
   
// add and remove listeners to DOM elements // add and remove listeners to DOM elements
, listener = W3C_MODEL ? function (element, type, fn, add) { , listener = W3C_MODEL ? function (element, type, fn, add) {
element[add ? addEvent : removeEvent](type, fn, false) element[add ? addEvent : removeEvent](type, fn, false)
} : function (element, type, fn, add, custom) { } : function (element, type, fn, add, custom) {
if (custom && add && element['_on' + custom] === null) if (custom && add && element['_on' + custom] === null)
element['_on' + custom] = 0 element['_on' + custom] = 0
element[add ? attachEvent : detachEvent]('on' + type, fn) element[add ? attachEvent : detachEvent]('on' + type, fn)
} }
   
, nativeHandler = function (element, fn, args) { , nativeHandler = function (element, fn, args) {
return function (event) { return function (event) {
event = fixEvent(event || ((this.ownerDocument || this.document || this).parentWindow || win).event, true) event = fixEvent(event || ((this.ownerDocument || this.document || this).parentWindow || win).event, true)
return fn.apply(element, [event].concat(args)) return fn.apply(element, [event].concat(args))
} }
} }
   
, customHandler = function (element, fn, type, condition, args, isNative) { , customHandler = function (element, fn, type, condition, args, isNative) {
return function (event) { return function (event) {
if (condition ? condition.apply(this, arguments) : W3C_MODEL ? true : event && event.propertyName === '_on' + type || !event) { if (condition ? condition.apply(this, arguments) : W3C_MODEL ? true : event && event.propertyName === '_on' + type || !event) {
if (event) if (event)
event = fixEvent(event || ((this.ownerDocument || this.document || this).parentWindow || win).event, isNative) event = fixEvent(event || ((this.ownerDocument || this.document || this).parentWindow || win).event, isNative)
fn.apply(element, event && (!args || args.length === 0) ? arguments : slice.call(arguments, event ? 0 : 1).concat(args)) fn.apply(element, event && (!args || args.length === 0) ? arguments : slice.call(arguments, event ? 0 : 1).concat(args))
} }
} }
} }
   
, once = function (rm, element, type, fn, originalFn) { , once = function (rm, element, type, fn, originalFn) {
// wrap the handler in a handler that does a remove as well // wrap the handler in a handler that does a remove as well
return function () { return function () {
rm(element, type, originalFn) rm(element, type, originalFn)
fn.apply(this, arguments) fn.apply(this, arguments)
} }
} }
   
, removeListener = function (element, orgType, handler, namespaces) { , removeListener = function (element, orgType, handler, namespaces) {
var i, l, entry var i, l, entry
, type = (orgType && orgType.replace(nameRegex, '')) , type = (orgType && orgType.replace(nameRegex, ''))
, handlers = registry.get(element, type, handler) , handlers = registry.get(element, type, handler)
   
for (i = 0, l = handlers.length; i < l; i++) { for (i = 0, l = handlers.length; i < l; i++) {
if (handlers[i].inNamespaces(namespaces)) { if (handlers[i].inNamespaces(namespaces)) {
if ((entry = handlers[i]).eventSupport) if ((entry = handlers[i]).eventSupport)
listener(entry.target, entry.eventType, entry.handler, false, entry.type) listener(entry.target, entry.eventType, entry.handler, false, entry.type)
// TODO: this is problematic, we have a registry.get() and registry.del() that // TODO: this is problematic, we have a registry.get() and registry.del() that
// both do registry searches so we waste cycles doing this. Needs to be rolled into // both do registry searches so we waste cycles doing this. Needs to be rolled into
// a single registry.forAll(fn) that removes while finding, but the catch is that // a single registry.forAll(fn) that removes while finding, but the catch is that
// we'll be splicing the arrays that we're iterating over. Needs extra tests to // we'll be splicing the arrays that we're iterating over. Needs extra tests to
// make sure we don't screw it up. @rvagg // make sure we don't screw it up. @rvagg
registry.del(entry) registry.del(entry)
} }
} }
} }
   
, addListener = function (element, orgType, fn, originalFn, args) { , addListener = function (element, orgType, fn, originalFn, args) {
var entry var entry
, type = orgType.replace(nameRegex, '') , type = orgType.replace(nameRegex, '')
, namespaces = orgType.replace(namespaceRegex, '').split('.') , namespaces = orgType.replace(namespaceRegex, '').split('.')
   
if (registry.has(element, type, fn)) if (registry.has(element, type, fn))
return element // no dupe return element // no dupe
if (type === 'unload') if (type === 'unload')
fn = once(removeListener, element, type, fn, originalFn) // self clean-up fn = once(removeListener, element, type, fn, originalFn) // self clean-up
if (customEvents[type]) { if (customEvents[type]) {
if (customEvents[type].condition) if (customEvents[type].condition)
fn = customHandler(element, fn, type, customEvents[type].condition, true) fn = customHandler(element, fn, type, customEvents[type].condition, true)
type = customEvents[type].base || type type = customEvents[type].base || type
} }
entry = registry.put(new RegEntry(element, type, fn, originalFn, namespaces[0] && namespaces)) entry = registry.put(new RegEntry(element, type, fn, originalFn, namespaces[0] && namespaces))
entry.handler = entry.isNative ? entry.handler = entry.isNative ?
nativeHandler(element, entry.handler, args) : nativeHandler(element, entry.handler, args) :
customHandler(element, entry.handler, type, false, args, false) customHandler(element, entry.handler, type, false, args, false)
if (entry.eventSupport) if (entry.eventSupport)
listener(entry.target, entry.eventType, entry.handler, true, entry.customType) listener(entry.target, entry.eventType, entry.handler, true, entry.customType)
} }
   
, del = function (selector, fn, $) { , del = function (selector, fn, $) {
return function (e) { return function (e) {
var target, i, array = typeof selector === 'string' ? $(selector, this) : selector var target, i, array = typeof selector === 'string' ? $(selector, this) : selector
for (target = e.target; target && target !== this; target = target.parentNode) { for (target = e.target; target && target !== this; target = target.parentNode) {
for (i = array.length; i--;) { for (i = array.length; i--;) {
if (array[i] === target) { if (array[i] === target) {
return fn.apply(target, arguments) return fn.apply(target, arguments)
} }
} }
} }
} }
} }
   
, remove = function (element, typeSpec, fn) { , remove = function (element, typeSpec, fn) {
var k, m, type, namespaces, i var k, m, type, namespaces, i
, rm = removeListener , rm = removeListener
, isString = typeSpec && typeof typeSpec === 'string' , isString = typeSpec && typeof typeSpec === 'string'
   
if (isString && typeSpec.indexOf(' ') > 0) { if (isString && typeSpec.indexOf(' ') > 0) {
// remove(el, 't1 t2 t3', fn) or remove(el, 't1 t2 t3') // remove(el, 't1 t2 t3', fn) or remove(el, 't1 t2 t3')
typeSpec = typeSpec.split(' ') typeSpec = typeSpec.split(' ')
for (i = typeSpec.length; i--;) for (i = typeSpec.length; i--;)
remove(element, typeSpec[i], fn) remove(element, typeSpec[i], fn)
return element return element
} }
type = isString && typeSpec.replace(nameRegex, '') type = isString && typeSpec.replace(nameRegex, '')
if (type && customEvents[type]) if (type && customEvents[type])
type = customEvents[type].type type = customEvents[type].type
if (!typeSpec || isString) { if (!typeSpec || isString) {
// remove(el) or remove(el, t1.ns) or remove(el, .ns) or remove(el, .ns1.ns2.ns3) // remove(el) or remove(el, t1.ns) or remove(el, .ns) or remove(el, .ns1.ns2.ns3)
if (namespaces = isString && typeSpec.replace(namespaceRegex, '')) if (namespaces = isString && typeSpec.replace(namespaceRegex, ''))
namespaces = namespaces.split('.') namespaces = namespaces.split('.')
rm(element, type, fn, namespaces) rm(element, type, fn, namespaces)
} else if (typeof typeSpec === 'function') { } else if (typeof typeSpec === 'function') {
// remove(el, fn) // remove(el, fn)
rm(element, null, typeSpec) rm(element, null, typeSpec)
} else { } else {
// remove(el, { t1: fn1, t2, fn2 }) // remove(el, { t1: fn1, t2, fn2 })
for (k in typeSpec) { for (k in typeSpec) {
if (typeSpec.hasOwnProperty(k)) if (typeSpec.hasOwnProperty(k))
remove(element, k, typeSpec[k]) remove(element, k, typeSpec[k])
} }
} }
return element return element
} }
   
, add = function (element, events, fn, delfn, $) { , add = function (element, events, fn, delfn, $) {
var type, types, i, args var type, types, i, args
, originalFn = fn , originalFn = fn
, isDel = fn && typeof fn === 'string' , isDel = fn && typeof fn === 'string'
   
if (events && !fn && typeof events === 'object') { if (events && !fn && typeof events === 'object') {
for (type in events) { for (type in events) {
if (events.hasOwnProperty(type)) if (events.hasOwnProperty(type))
add.apply(this, [ element, type, events[type] ]) add.apply(this, [ element, type, events[type] ])
} }
} else { } else {
args = arguments.length > 3 ? slice.call(arguments, 3) : [] args = arguments.length > 3 ? slice.call(arguments, 3) : []
types = (isDel ? fn : events).split(' ') types = (isDel ? fn : events).split(' ')
isDel && (fn = del(events, (originalFn = delfn), $)) && (args = slice.call(args, 1)) isDel && (fn = del(events, (originalFn = delfn), $)) && (args = slice.call(args, 1))
// special case for one() // special case for one()
this === ONE && (fn = once(remove, element, events, fn, originalFn)) this === ONE && (fn = once(remove, element, events, fn, originalFn))
for (i = types.length; i--;) addListener(element, types[i], fn, originalFn, args) for (i = types.length; i--;) addListener(element, types[i], fn, originalFn, args)
} }
return element return element
} }
   
, one = function () { , one = function () {
return add.apply(ONE, arguments) return add.apply(ONE, arguments)
} }
   
, fireListener = W3C_MODEL ? function (isNative, type, element) { , fireListener = W3C_MODEL ? function (isNative, type, element) {
var evt = doc.createEvent(isNative ? 'HTMLEvents' : 'UIEvents') var evt = doc.createEvent(isNative ? 'HTMLEvents' : 'UIEvents')
evt[isNative ? 'initEvent' : 'initUIEvent'](type, true, true, win, 1) evt[isNative ? 'initEvent' : 'initUIEvent'](type, true, true, win, 1)
element.dispatchEvent(evt) element.dispatchEvent(evt)
} : function (isNative, type, element) { } : function (isNative, type, element) {
element = targetElement(element, isNative) element = targetElement(element, isNative)
// if not-native then we're using onpropertychange so we just increment a custom property // if not-native then we're using onpropertychange so we just increment a custom property
isNative ? element.fireEvent('on' + type, doc.createEventObject()) : element['_on' + type]++ isNative ? element.fireEvent('on' + type, doc.createEventObject()) : element['_on' + type]++
} }
   
, fire = function (element, type, args) { , fire = function (element, type, args) {
var i, j, l, names, handlers var i, j, l, names, handlers
, types = type.split(' ') , types = type.split(' ')
   
for (i = types.length; i--;) { for (i = types.length; i--;) {
type = types[i].replace(nameRegex, '') type = types[i].replace(nameRegex, '')
if (names = types[i].replace(namespaceRegex, '')) if (names = types[i].replace(namespaceRegex, ''))
names = names.split('.') names = names.split('.')
if (!names && !args && element[eventSupport]) { if (!names && !args && element[eventSupport]) {
fireListener(nativeEvents[type], type, element) fireListener(nativeEvents[type], type, element)
} else { } else {
// non-native event, either because of a namespace, arguments or a non DOM element // non-native event, either because of a namespace, arguments or a non DOM element
// iterate over all listeners and manually 'fire' // iterate over all listeners and manually 'fire'
handlers = registry.get(element, type) handlers = registry.get(element, type)
args = [false].concat(args) args = [false].concat(args)
for (j = 0, l = handlers.length; j < l; j++) { for (j = 0, l = handlers.length; j < l; j++) {
if (handlers[j].inNamespaces(names)) if (handlers[j].inNamespaces(names))
handlers[j].handler.apply(element, args) handlers[j].handler.apply(element, args)
} }
} }
} }
return element return element
} }
   
, clone = function (element, from, type) { , clone = function (element, from, type) {
var i = 0 var i = 0
, handlers = registry.get(from, type) , handlers = registry.get(from, type)
, l = handlers.length , l = handlers.length
   
for (;i < l; i++) for (; i < l; i++)
handlers[i].original && add(element, handlers[i].type, handlers[i].original) handlers[i].original && add(element, handlers[i].type, handlers[i].original)
return element return element
} }
   
, bean = { , bean = {
add: add add: add, one: one, remove: remove, clone: clone, fire: fire, noConflict: function () {
, one: one context[name] = old
, remove: remove return this
, clone: clone }
, fire: fire }
, noConflict: function () {  
context[name] = old if (win[attachEvent]) {
return this // for IE, clean up on unload to avoid leaks
} var cleanup = function () {
} var i, entries = registry.entries()
  for (i in entries) {
if (win[attachEvent]) { if (entries[i].type && entries[i].type !== 'unload')
// for IE, clean up on unload to avoid leaks remove(entries[i].element, entries[i].type)
var cleanup = function () { }
var i, entries = registry.entries() win[detachEvent]('onunload', cleanup)
for (i in entries) { win.CollectGarbage && win.CollectGarbage()
if (entries[i].type && entries[i].type !== 'unload') }
remove(entries[i].element, entries[i].type) win[attachEvent]('onunload', cleanup)
}  
win[detachEvent]('onunload', cleanup)  
win.CollectGarbage && win.CollectGarbage()  
} }
win[attachEvent]('onunload', cleanup)  
} return bean
   
return bean  
}); });
// Underscore.js 1.1.7 // Underscore.js 1.1.7
// (c) 2011 Jeremy Ashkenas, DocumentCloud Inc. // (c) 2011 Jeremy Ashkenas, DocumentCloud Inc.
// Underscore is freely distributable under the MIT license. // Underscore is freely distributable under the MIT license.
// Portions of Underscore are inspired or borrowed from Prototype, // Portions of Underscore are inspired or borrowed from Prototype,
// Oliver Steele's Functional, and John Resig's Micro-Templating. // Oliver Steele's Functional, and John Resig's Micro-Templating.
// For all details and documentation: // For all details and documentation:
// http://documentcloud.github.com/underscore // http://documentcloud.github.com/underscore
   
(function() { (function () {
   
// Baseline setup // Baseline setup
// -------------- // --------------
   
// Establish the root object, `window` in the browser, or `global` on the server. // Establish the root object, `window` in the browser, or `global` on the server.
var root = this; var root = this;
   
// Save the previous value of the `_` variable. // Save the previous value of the `_` variable.
var previousUnderscore = root._; var previousUnderscore = root._;
   
// Establish the object that gets returned to break out of a loop iteration. // Establish the object that gets returned to break out of a loop iteration.
var breaker = {}; var breaker = {};
   
// Save bytes in the minified (but not gzipped) version: // Save bytes in the minified (but not gzipped) version:
var ArrayProto = Array.prototype, ObjProto = Object.prototype, FuncProto = Function.prototype; var ArrayProto = Array.prototype, ObjProto = Object.prototype, FuncProto = Function.prototype;
   
// Create quick reference variables for speed access to core prototypes. // Create quick reference variables for speed access to core prototypes.
var slice = ArrayProto.slice, var slice = ArrayProto.slice,
unshift = ArrayProto.unshift, unshift = ArrayProto.unshift,
toString = ObjProto.toString, toString = ObjProto.toString,
hasOwnProperty = ObjProto.hasOwnProperty; hasOwnProperty = ObjProto.hasOwnProperty;
   
// All **ECMAScript 5** native function implementations that we hope to use // All **ECMAScript 5** native function implementations that we hope to use
// are declared here. // are declared here.
var var
nativeForEach = ArrayProto.forEach, nativeForEach = ArrayProto.forEach,
nativeMap = ArrayProto.map, nativeMap = ArrayProto.map,
nativeReduce = ArrayProto.reduce, nativeReduce = ArrayProto.reduce,
nativeReduceRight = ArrayProto.reduceRight, nativeReduceRight = ArrayProto.reduceRight,
nativeFilter = ArrayProto.filter, nativeFilter = ArrayProto.filter,
nativeEvery = ArrayProto.every, nativeEvery = ArrayProto.every,
nativeSome = ArrayProto.some, nativeSome = ArrayProto.some,
nativeIndexOf = ArrayProto.indexOf, nativeIndexOf = ArrayProto.indexOf,
nativeLastIndexOf = ArrayProto.lastIndexOf, nativeLastIndexOf = ArrayProto.lastIndexOf,
nativeIsArray = Array.isArray, nativeIsArray = Array.isArray,
nativeKeys = Object.keys, nativeKeys = Object.keys,
nativeBind = FuncProto.bind; nativeBind = FuncProto.bind;
   
// Create a safe reference to the Underscore object for use below. // Create a safe reference to the Underscore object for use below.
var _ = function(obj) { return new wrapper(obj); }; var _ = function (obj) {
  return new wrapper(obj);
// Export the Underscore object for **CommonJS**, with backwards-compatibility };
// for the old `require()` API. If we're not in CommonJS, add `_` to the  
// global object. // Export the Underscore object for **CommonJS**, with backwards-compatibility
if (typeof module !== 'undefined' && module.exports) { // for the old `require()` API. If we're not in CommonJS, add `_` to the
module.exports = _; // global object.
_._ = _; if (typeof module !== 'undefined' && module.exports) {
} else { module.exports = _;
// Exported as a string, for Closure Compiler "advanced" mode. _._ = _;
root['_'] = _;  
}  
   
// Current version.  
_.VERSION = '1.1.7';  
   
// Collection Functions  
// --------------------  
   
// The cornerstone, an `each` implementation, aka `forEach`.  
// Handles objects with the built-in `forEach`, arrays, and raw objects.  
// Delegates to **ECMAScript 5**'s native `forEach` if available.  
var each = _.each = _.forEach = function(obj, iterator, context) {  
if (obj == null) return;  
if (nativeForEach && obj.forEach === nativeForEach) {  
obj.forEach(iterator, context);  
} else if (obj.length === +obj.length) {  
for (var i = 0, l = obj.length; i < l; i++) {  
if (i in obj && iterator.call(context, obj[i], i, obj) === breaker) return;  
}  
} else { } else {
for (var key in obj) { // Exported as a string, for Closure Compiler "advanced" mode.
if (hasOwnProperty.call(obj, key)) { root['_'] = _;
if (iterator.call(context, obj[key], key, obj) === breaker) return;  
}  
}  
} }
};  
  // Current version.
// Return the results of applying the iterator to each element. _.VERSION = '1.1.7';
// Delegates to **ECMAScript 5**'s native `map` if available.  
_.map = function(obj, iterator, context) { // Collection Functions
var results = []; // --------------------
if (obj == null) return results;  
if (nativeMap && obj.map === nativeMap) return obj.map(iterator, context); // The cornerstone, an `each` implementation, aka `forEach`.
each(obj, function(value, index, list) { // Handles objects with the built-in `forEach`, arrays, and raw objects.
results[results.length] = iterator.call(context, value, index, list); // Delegates to **ECMAScript 5**'s native `forEach` if available.
  var each = _.each = _.forEach = function (obj, iterator, context) {
  if (obj == null) return;
  if (nativeForEach && obj.forEach === nativeForEach) {
  obj.forEach(iterator, context);
  } else if (obj.length === +obj.length) {
  for (var i = 0, l = obj.length; i < l; i++) {
  if (i in obj && iterator.call(context, obj[i], i, obj) === breaker) return;
  }
  } else {
  for (var key in obj) {
  if (hasOwnProperty.call(obj, key)) {
  if (iterator.call(context, obj[key], key, obj) === breaker) return;
  }
  }
  }
  };
   
  // Return the results of applying the iterator to each element.
  // Delegates to **ECMAScript 5**'s native `map` if available.
  _.map = function (obj, iterator, context) {
  var results = [];
  if (obj == null) return results;
  if (nativeMap && obj.map === nativeMap) return obj.map(iterator, context);
  each(obj, function (value, index, list) {
  results[results.length] = iterator.call(context, value, index, list);
  });
  return results;
  };
   
  // **Reduce** builds up a single result from a list of values, aka `inject`,
  // or `foldl`. Delegates to **ECMAScript 5**'s native `reduce` if available.
  _.reduce = _.foldl = _.inject = function (obj, iterator, memo, context) {
  var initial = memo !== void 0;
  if (obj == null) obj = [];
  if (nativeReduce && obj.reduce === nativeReduce) {
  if (context) iterator = _.bind(iterator, context);
  return initial ? obj.reduce(iterator, memo) : obj.reduce(iterator);
  }
  each(obj, function (value, index, list) {
  if (!initial) {
  memo = value;
  initial = true;
  } else {
  memo = iterator.call(context, memo, value, index, list);
  }
  });
  if (!initial) throw new TypeError("Reduce of empty array with no initial value");
  return memo;
  };
   
  // The right-associative version of reduce, also known as `foldr`.
  // Delegates to **ECMAScript 5**'s native `reduceRight` if available.
  _.reduceRight = _.foldr = function (obj, iterator, memo, context) {
  if (obj == null) obj = [];
  if (nativeReduceRight && obj.reduceRight === nativeReduceRight) {
  if (context) iterator = _.bind(iterator, context);
  return memo !== void 0 ? obj.reduceRight(iterator, memo) : obj.reduceRight(iterator);
  }
  var reversed = (_.isArray(obj) ? obj.slice() : _.toArray(obj)).reverse();
  return _.reduce(reversed, iterator, memo, context);
  };
   
  // Return the first value which passes a truth test. Aliased as `detect`.
  _.find = _.detect = function (obj, iterator, context) {
  var result;
  any(obj, function (value, index, list) {
  if (iterator.call(context, value, index, list)) {
  result = value;
  return true;
  }
  });
  return result;
  };
   
  // Return all the elements that pass a truth test.
  // Delegates to **ECMAScript 5**'s native `filter` if available.
  // Aliased as `select`.
  _.filter = _.select = function (obj, iterator, context) {
  var results = [];
  if (obj == null) return results;
  if (nativeFilter && obj.filter === nativeFilter) return obj.filter(iterator, context);
  each(obj, function (value, index, list) {
  if (iterator.call(context, value, index, list)) results[results.length] = value;
  });
  return results;
  };
   
  // Return all the elements for which a truth test fails.
  _.reject = function (obj, iterator, context) {
  var results = [];
  if (obj == null) return results;
  each(obj, function (value, index, list) {
  if (!iterator.call(context, value, index, list)) results[results.length] = value;
  });
  return results;
  };
   
  // Determine whether all of the elements match a truth test.
  // Delegates to **ECMAScript 5**'s native `every` if available.
  // Aliased as `all`.
  _.every = _.all = function (obj, iterator, context) {
  var result = true;
  if (obj == null) return result;
  if (nativeEvery && obj.every === nativeEvery) return obj.every(iterator, context);
  each(obj, function (value, index, list) {
  if (!(result = result && iterator.call(context, value, index, list))) return breaker;
  });
  return result;
  };
   
  // Determine if at least one element in the object matches a truth test.
  // Delegates to **ECMAScript 5**'s native `some` if available.
  // Aliased as `any`.
  var any = _.some = _.any = function (obj, iterator, context) {
  iterator = iterator || _.identity;
  var result = false;
  if (obj == null) return result;
  if (nativeSome && obj.some === nativeSome) return obj.some(iterator, context);
  each(obj, function (value, index, list) {
  if (result |= iterator.call(context, value, index, list)) return breaker;
  });
  return !!result;
  };
   
  // Determine if a given value is included in the array or object using `===`.
  // Aliased as `contains`.
  _.include = _.contains = function (obj, target) {
  var found = false;
  if (obj == null) return found;
  if (nativeIndexOf && obj.indexOf === nativeIndexOf) return obj.indexOf(target) != -1;
  any(obj, function (value) {
  if (found = value === target) return true;
  });
  return found;
  };
   
  // Invoke a method (with arguments) on every item in a collection.
  _.invoke = function (obj, method) {
  var args = slice.call(arguments, 2);
  return _.map(obj, function (value) {
  return (method.call ? method || value : value[method]).apply(value, args);
  });
  };
   
  // Convenience version of a common use case of `map`: fetching a property.
  _.pluck = function (obj, key) {
  return _.map(obj, function (value) {
  return value[key];
  });
  };
   
  // Return the maximum element or (element-based computation).
  _.max = function (obj, iterator, context) {
  if (!iterator && _.isArray(obj)) return Math.max.apply(Math, obj);
  var result = {computed: -Infinity};
  each(obj, function (value, index, list) {
  var computed = iterator ? iterator.call(context, value, index, list) : value;
  computed >= result.computed && (result = {value: value, computed: computed});
  });
  return result.value;
  };
   
  // Return the minimum element (or element-based computation).
  _.min = function (obj, iterator, context) {
  if (!iterator && _.isArray(obj)) return Math.min.apply(Math, obj);
  var result = {computed: Infinity};
  each(obj, function (value, index, list) {
  var computed = iterator ? iterator.call(context, value, index, list) : value;
  computed < result.computed && (result = {value: value, computed: computed});
  });
  return result.value;
  };
   
  // Sort the object's values by a criterion produced by an iterator.
  _.sortBy = function (obj, iterator, context) {
  return _.pluck(_.map(obj,function (value, index, list) {
  return {
  value: value,
  criteria: iterator.call(context, value, index, list)
  };
  }).sort(function (left, right) {
  var a = left.criteria, b = right.criteria;
  return a < b ? -1 : a > b ? 1 : 0;
  }), 'value');
  };
   
  // Groups the object's values by a criterion produced by an iterator
  _.groupBy = function (obj, iterator) {
  var result = {};
  each(obj, function (value, index) {
  var key = iterator(value, index);
  (result[key] || (result[key] = [])).push(value);
  });
  return result;
  };
   
  // Use a comparator function to figure out at what index an object should
  // be inserted so as to maintain order. Uses binary search.
  _.sortedIndex = function (array, obj, iterator) {
  iterator || (iterator = _.identity);
  var low = 0, high = array.length;
  while (low < high) {
  var mid = (low + high) >> 1;
  iterator(array[mid]) < iterator(obj) ? low = mid + 1 : high = mid;
  }
  return low;
  };
   
  // Safely convert anything iterable into a real, live array.
  _.toArray = function (iterable) {
  if (!iterable) return [];
  if (iterable.toArray) return iterable.toArray();
  if (_.isArray(iterable)) return slice.call(iterable);
  if (_.isArguments(iterable)) return slice.call(iterable);
  return _.values(iterable);
  };
   
  // Return the number of elements in an object.
  _.size = function (obj) {
  return _.toArray(obj).length;
  };
   
  // Array Functions
  // ---------------
   
  // Get the first element of an array. Passing **n** will return the first N
  // values in the array. Aliased as `head`. The **guard** check allows it to work
  // with `_.map`.
  _.first = _.head = function (array, n, guard) {
  return (n != null) && !guard ? slice.call(array, 0, n) : array[0];
  };
   
  // Returns everything but the first entry of the array. Aliased as `tail`.
  // Especially useful on the arguments object. Passing an **index** will return
  // the rest of the values in the array from that index onward. The **guard**
  // check allows it to work with `_.map`.
  _.rest = _.tail = function (array, index, guard) {
  return slice.call(array, (index == null) || guard ? 1 : index);
  };
   
  // Get the last element of an array.
  _.last = function (array) {
  return array[array.length - 1];
  };
   
  // Trim out all falsy values from an array.
  _.compact = function (array) {
  return _.filter(array, function (value) {
  return !!value;
  });
  };
   
  // Return a completely flattened version of an array.
  _.flatten = function (array) {
  return _.reduce(array, function (memo, value) {
  if (_.isArray(value)) return memo.concat(_.flatten(value));
  memo[memo.length] = value;
  return memo;
  }, []);
  };
   
  // Return a version of the array that does not contain the specified value(s).
  _.without = function (array) {
  return _.difference(array, slice.call(arguments, 1));
  };
   
  // Produce a duplicate-free version of the array. If the array has already
  // been sorted, you have the option of using a faster algorithm.
  // Aliased as `unique`.
  _.uniq = _.unique = function (array, isSorted) {
  return _.reduce(array, function (memo, el, i) {
  if (0 == i || (isSorted === true ? _.last(memo) != el : !_.include(memo, el))) memo[memo.length] = el;
  return memo;
  }, []);
  };
   
  // Produce an array that contains the union: each distinct element from all of
  // the passed-in arrays.
  _.union = function () {
  return _.uniq(_.flatten(arguments));
  };
   
  // Produce an array that contains every item shared between all the
  // passed-in arrays. (Aliased as "intersect" for back-compat.)
  _.intersection = _.intersect = function (array) {
  var rest = slice.call(arguments, 1);
  return _.filter(_.uniq(array), function (item) {
  return _.every(rest, function (other) {
  return _.indexOf(other, item) >= 0;
  });
  });
  };
   
  // Take the difference between one array and another.
  // Only the elements present in just the first array will remain.
  _.difference = function (array, other) {
  return _.filter(array, function (value) {
  return !_.include(other, value);
  });
  };
   
  // Zip together multiple lists into a single array -- elements that share
  // an index go together.
  _.zip = function () {
  var args = slice.call(arguments);
  var length = _.max(_.pluck(args, 'length'));
  var results = new Array(length);
  for (var i = 0; i < length; i++) results[i] = _.pluck(args, "" + i);
  return results;
  };
   
  // If the browser doesn't supply us with indexOf (I'm looking at you, **MSIE**),
  // we need this function. Return the position of the first occurrence of an
  // item in an array, or -1 if the item is not included in the array.
  // Delegates to **ECMAScript 5**'s native `indexOf` if available.
  // If the array is large and already in sort order, pass `true`
  // for **isSorted** to use binary search.
  _.indexOf = function (array, item, isSorted) {
  if (array == null) return -1;
  var i, l;
  if (isSorted) {
  i = _.sortedIndex(array, item);
  return array[i] === item ? i : -1;
  }
  if (nativeIndexOf && array.indexOf === nativeIndexOf) return array.indexOf(item);
  for (i = 0, l = array.length; i < l; i++) if (array[i] === item) return i;
  return -1;
  };
   
   
  // Delegates to **ECMAScript 5**'s native `lastIndexOf` if available.
  _.lastIndexOf = function (array, item) {
  if (array == null) return -1;
  if (nativeLastIndexOf && array.lastIndexOf === nativeLastIndexOf) return array.lastIndexOf(item);
  var i = array.length;
  while (i--) if (array[i] === item) return i;
  return -1;
  };
   
  // Generate an integer Array containing an arithmetic progression. A port of
  // the native Python `range()` function. See
  // [the Python documentation](http://docs.python.org/library/functions.html#range).
  _.range = function (start, stop, step) {
  if (arguments.length <= 1) {
  stop = start || 0;
  start = 0;
  }
  step = arguments[2] || 1;
   
  var len = Math.max(Math.ceil((stop - start) / step), 0);
  var idx = 0;
  var range = new Array(len);
   
  while (idx < len) {
  range[idx++] = start;
  start += step;
  }
   
  return range;
  };
   
  // Function (ahem) Functions
  // ------------------
   
  // Create a function bound to a given object (assigning `this`, and arguments,
  // optionally). Binding with arguments is also known as `curry`.
  // Delegates to **ECMAScript 5**'s native `Function.bind` if available.
  // We check for `func.bind` first, to fail fast when `func` is undefined.
  _.bind = function (func, obj) {
  if (func.bind === nativeBind && nativeBind) return nativeBind.apply(func, slice.call(arguments, 1));
  var args = slice.call(arguments, 2);
  return function () {
  return func.apply(obj, args.concat(slice.call(arguments)));
  };
  };
   
  // Bind all of an object's methods to that object. Useful for ensuring that
  // all callbacks defined on an object belong to it.
  _.bindAll = function (obj) {
  var funcs = slice.call(arguments, 1);
  if (funcs.length == 0) funcs = _.functions(obj);
  each(funcs, function (f) {
  obj[f] = _.bind(obj[f], obj);
  });
  return obj;
  };
   
  // Memoize an expensive function by storing its results.
  _.memoize = function (func, hasher) {
  var memo = {};
  hasher || (hasher = _.identity);
  return function () {
  var key = hasher.apply(this, arguments);
  return hasOwnProperty.call(memo, key) ? memo[key] : (memo[key] = func.apply(this, arguments));
  };
  };
   
  // Delays a function for the given number of milliseconds, and then calls
  // it with the arguments supplied.
  _.delay = function (func, wait) {
  var args = slice.call(arguments, 2);
  return setTimeout(function () {
  return func.apply(func, args);
  }, wait);
  };
   
  // Defers a function, scheduling it to run after the current call stack has
  // cleared.
  _.defer = function (func) {
  return _.delay.apply(_, [func, 1].concat(slice.call(arguments, 1)));
  };
   
  // Internal function used to implement `_.throttle` and `_.debounce`.
  var limit = function (func, wait, debounce) {
  var timeout;
  return function () {
  var context = this, args = arguments;
  var throttler = function () {
  timeout = null;
  func.apply(context, args);
  };
  if (debounce) clearTimeout(timeout);
  if (debounce || !timeout) timeout = setTimeout(throttler, wait);
  };
  };
   
  // Returns a function, that, when invoked, will only be triggered at most once
  // during a given window of time.
  _.throttle = function (func, wait) {
  return limit(func, wait, false);
  };
   
  // Returns a function, that, as long as it continues to be invoked, will not
  // be triggered. The function will be called after it stops being called for
  // N milliseconds.
  _.debounce = function (func, wait) {
  return limit(func, wait, true);
  };
   
  // Returns a function that will be executed at most one time, no matter how
  // often you call it. Useful for lazy initialization.
  _.once = function (func) {
  var ran = false, memo;
  return function () {
  if (ran) return memo;
  ran = true;
  return memo = func.apply(this, arguments);
  };
  };
   
  // Returns the first function passed as an argument to the second,
  // allowing you to adjust arguments, run code before and after, and
  // conditionally execute the original function.
  _.wrap = function (func, wrapper) {
  return function () {
  var args = [func].concat(slice.call(arguments));
  return wrapper.apply(this, args);
  };
  };
   
  // Returns a function that is the composition of a list of functions, each
  // consuming the return value of the function that follows.
  _.compose = function () {
  var funcs = slice.call(arguments);
  return function () {
  var args = slice.call(arguments);
  for (var i = funcs.length - 1; i >= 0; i--) {
  args = [funcs[i].apply(this, args)];
  }
  return args[0];
  };
  };
   
  // Returns a function that will only be executed after being called N times.
  _.after = function (times, func) {
  return function () {
  if (--times < 1) {
  return func.apply(this, arguments);
  }
  };
  };
   
   
  // Object Functions
  // ----------------
   
  // Retrieve the names of an object's properties.
  // Delegates to **ECMAScript 5**'s native `Object.keys`
  _.keys = nativeKeys || function (obj) {
  if (obj !== Object(obj)) throw new TypeError('Invalid object');
  var keys = [];
  for (var key in obj) if (hasOwnProperty.call(obj, key)) keys[keys.length] = key;
  return keys;
  };
   
  // Retrieve the values of an object's properties.
  _.values = function (obj) {
  return _.map(obj, _.identity);
  };
   
  // Return a sorted list of the function names available on the object.
  // Aliased as `methods`
  _.functions = _.methods = function (obj) {
  var names = [];
  for (var key in obj) {
  if (_.isFunction(obj[key])) names.push(key);
  }
  return names.sort();
  };
   
  // Extend a given object with all the properties in passed-in object(s).
  _.extend = function (obj) {
  each(slice.call(arguments, 1), function (source) {
  for (var prop in source) {
  if (source[prop] !== void 0) obj[prop] = source[prop];
  }
  });
  return obj;
  };
   
  // Fill in a given object with default properties.
  _.defaults = function (obj) {
  each(slice.call(arguments, 1), function (source) {
  for (var prop in source) {
  if (obj[prop] == null) obj[prop] = source[prop];
  }
  });
  return obj;
  };
   
  // Create a (shallow-cloned) duplicate of an object.
  _.clone = function (obj) {
  return _.isArray(obj) ? obj.slice() : _.extend({}, obj);
  };
   
  // Invokes interceptor with the obj, and then returns obj.
  // The primary purpose of this method is to "tap into" a method chain, in
  // order to perform operations on intermediate results within the chain.
  _.tap = function (obj, interceptor) {
  interceptor(obj);
  return obj;
  };
   
  // Perform a deep comparison to check if two objects are equal.
  _.isEqual = function (a, b) {
  // Check object identity.
  if (a === b) return true;
  // Different types?
  var atype = typeof(a), btype = typeof(b);
  if (atype != btype) return false;
  // Basic equality test (watch out for coercions).
  if (a == b) return true;
  // One is falsy and the other truthy.
  if ((!a && b) || (a && !b)) return false;
  // Unwrap any wrapped objects.
  if (a._chain) a = a._wrapped;
  if (b._chain) b = b._wrapped;
  // One of them implements an isEqual()?
  if (a.isEqual) return a.isEqual(b);
  if (b.isEqual) return b.isEqual(a);
  // Check dates' integer values.
  if (_.isDate(a) && _.isDate(b)) return a.getTime() === b.getTime();
  // Both are NaN?
  if (_.isNaN(a) && _.isNaN(b)) return false;
  // Compare regular expressions.
  if (_.isRegExp(a) && _.isRegExp(b))
  return a.source === b.source &&
  a.global === b.global &&
  a.ignoreCase === b.ignoreCase &&
  a.multiline === b.multiline;
  // If a is not an object by this point, we can't handle it.
  if (atype !== 'object') return false;
  // Check for different array lengths before comparing contents.
  if (a.length && (a.length !== b.length)) return false;
  // Nothing else worked, deep compare the contents.
  var aKeys = _.keys(a), bKeys = _.keys(b);
  // Different object sizes?
  if (aKeys.length != bKeys.length) return false;
  // Recursive comparison of contents.
  for (var key in a) if (!(key in b) || !_.isEqual(a[key], b[key])) return false;
  return true;
  };
   
  // Is a given array or object empty?
  _.isEmpty = function (obj) {
  if (_.isArray(obj) || _.isString(obj)) return obj.length === 0;
  for (var key in obj) if (hasOwnProperty.call(obj, key)) return false;
  return true;
  };
   
  // Is a given value a DOM element?
  _.isElement = function (obj) {
  return !!(obj && obj.nodeType == 1);
  };
   
  // Is a given value an array?
  // Delegates to ECMA5's native Array.isArray
  _.isArray = nativeIsArray || function (obj) {
  return toString.call(obj) === '[object Array]';
  };
   
  // Is a given variable an object?
  _.isObject = function (obj) {
  return obj === Object(obj);
  };
   
  // Is a given variable an arguments object?
  _.isArguments = function (obj) {
  return !!(obj && hasOwnProperty.call(obj, 'callee'));
  };
   
  // Is a given value a function?
  _.isFunction = function (obj) {
  return !!(obj && obj.constructor && obj.call && obj.apply);
  };
   
  // Is a given value a string?
  _.isString = function (obj) {
  return !!(obj === '' || (obj && obj.charCodeAt && obj.substr));
  };
   
  // Is a given value a number?
  _.isNumber = function (obj) {
  return !!(obj === 0 || (obj && obj.toExponential && obj.toFixed));
  };
   
  // Is the given value `NaN`? `NaN` happens to be the only value in JavaScript
  // that does not equal itself.
  _.isNaN = function (obj) {
  return obj !== obj;
  };
   
  // Is a given value a boolean?
  _.isBoolean = function (obj) {
  return obj === true || obj === false;
  };
   
  // Is a given value a date?
  _.isDate = function (obj) {
  return !!(obj && obj.getTimezoneOffset && obj.setUTCFullYear);
  };
   
  // Is the given value a regular expression?
  _.isRegExp = function (obj) {
  return !!(obj && obj.test && obj.exec && (obj.ignoreCase || obj.ignoreCase === false));
  };
   
  // Is a given value equal to null?
  _.isNull = function (obj) {
  return obj === null;
  };
   
  // Is a given variable undefined?
  _.isUndefined = function (obj) {
  return obj === void 0;
  };
   
  // Utility Functions
  // -----------------
   
  // Run Underscore.js in *noConflict* mode, returning the `_` variable to its
  // previous owner. Returns a reference to the Underscore object.
  _.noConflict = function () {
  root._ = previousUnderscore;
  return this;
  };
   
  // Keep the identity function around for default iterators.
  _.identity = function (value) {
  return value;
  };
   
  // Run a function **n** times.
  _.times = function (n, iterator, context) {
  for (var i = 0; i < n; i++) iterator.call(context, i);
  };
   
  // Add your own custom functions to the Underscore object, ensuring that
  // they're correctly added to the OOP wrapper as well.
  _.mixin = function (obj) {
  each(_.functions(obj), function (name) {
  addToWrapper(name, _[name] = obj[name]);
  });
  };
   
  // Generate a unique integer id (unique within the entire client session).
  // Useful for temporary DOM ids.
  var idCounter = 0;
  _.uniqueId = function (prefix) {
  var id = idCounter++;
  return prefix ? prefix + id : id;
  };
   
  // By default, Underscore uses ERB-style template delimiters, change the
  // following template settings to use alternative delimiters.
  _.templateSettings = {
  evaluate: /<%([\s\S]+?)%>/g,
  interpolate: /<%=([\s\S]+?)%>/g
  };
   
  // JavaScript micro-templating, similar to John Resig's implementation.
  // Underscore templating handles arbitrary delimiters, preserves whitespace,
  // and correctly escapes quotes within interpolated code.
  _.template = function (str, data) {
  var c = _.templateSettings;
  var tmpl = 'var __p=[],print=function(){__p.push.apply(__p,arguments);};' +
  'with(obj||{}){__p.push(\'' +
  str.replace(/\\/g, '\\\\')
  .replace(/'/g, "\\'")
  .replace(c.interpolate, function (match, code) {
  return "'," + code.replace(/\\'/g, "'") + ",'";
  })
  .replace(c.evaluate || null, function (match, code) {
  return "');" + code.replace(/\\'/g, "'")
  .replace(/[\r\n\t]/g, ' ') + "__p.push('";
  })
  .replace(/\r/g, '\\r')
  .replace(/\n/g, '\\n')
  .replace(/\t/g, '\\t')
  + "');}return __p.join('');";
  var func = new Function('obj', tmpl);
  return data ? func(data) : func;
  };
   
  // The OOP Wrapper
  // ---------------
   
  // If Underscore is called as a function, it returns a wrapped object that
  // can be used OO-style. This wrapper holds altered versions of all the
  // underscore functions. Wrapped objects may be chained.
  var wrapper = function (obj) {
  this._wrapped = obj;
  };
   
  // Expose `wrapper.prototype` as `_.prototype`
  _.prototype = wrapper.prototype;
   
  // Helper function to continue chaining intermediate results.
  var result = function (obj, chain) {
  return chain ? _(obj).chain() : obj;
  };
   
  // A method to easily add functions to the OOP wrapper.
  var addToWrapper = function (name, func) {
  wrapper.prototype[name] = function () {
  var args = slice.call(arguments);
  unshift.call(args, this._wrapped);
  return result(func.apply(_, args), this._chain);
  };
  };
   
  // Add all of the Underscore functions to the wrapper object.
  _.mixin(_);
   
  // Add all mutator Array functions to the wrapper.
  each(['pop', 'push', 'reverse', 'shift', 'sort', 'splice', 'unshift'], function (name) {
  var method = ArrayProto[name];
  wrapper.prototype[name] = function () {
  method.apply(this._wrapped, arguments);
  return result(this._wrapped, this._chain);
  };
}); });
return results;  
}; // Add all accessor Array functions to the wrapper.
  each(['concat', 'join', 'slice'], function (name) {
// **Reduce** builds up a single result from a list of values, aka `inject`, var method = ArrayProto[name];
// or `foldl`. Delegates to **ECMAScript 5**'s native `reduce` if available. wrapper.prototype[name] = function () {
_.reduce = _.foldl = _.inject = function(obj, iterator, memo, context) { return result(method.apply(this._wrapped, arguments), this._chain);
var initial = memo !== void 0; };
if (obj == null) obj = [];  
if (nativeReduce && obj.reduce === nativeReduce) {  
if (context) iterator = _.bind(iterator, context);  
return initial ? obj.reduce(iterator, memo) : obj.reduce(iterator);  
}  
each(obj, function(value, index, list) {  
if (!initial) {  
memo = value;  
initial = true;  
} else {  
memo = iterator.call(context, memo, value, index, list);  
}  
}); });
if (!initial) throw new TypeError("Reduce of empty array with no initial value");  
return memo; // Start chaining a wrapped Underscore object.
}; wrapper.prototype.chain = function () {
  this._chain = true;
// The right-associative version of reduce, also known as `foldr`. return this;
// Delegates to **ECMAScript 5**'s native `reduceRight` if available. };
_.reduceRight = _.foldr = function(obj, iterator, memo, context) {  
if (obj == null) obj = []; // Extracts the result from a wrapped and chained object.
if (nativeReduceRight && obj.reduceRight === nativeReduceRight) { wrapper.prototype.value = function () {
if (context) iterator = _.bind(iterator, context); return this._wrapped;
return memo !== void 0 ? obj.reduceRight(iterator, memo) : obj.reduceRight(iterator); };
}  
var reversed = (_.isArray(obj) ? obj.slice() : _.toArray(obj)).reverse();  
return _.reduce(reversed, iterator, memo, context);  
};  
   
// Return the first value which passes a truth test. Aliased as `detect`.  
_.find = _.detect = function(obj, iterator, context) {  
var result;  
any(obj, function(value, index, list) {  
if (iterator.call(context, value, index, list)) {  
result = value;  
return true;  
}  
});  
return result;  
};  
   
// Return all the elements that pass a truth test.  
// Delegates to **ECMAScript 5**'s native `filter` if available.  
// Aliased as `select`.  
_.filter = _.select = function(obj, iterator, context) {  
var results = [];  
if (obj == null) return results;  
if (nativeFilter && obj.filter === nativeFilter) return obj.filter(iterator, context);  
each(obj, function(value, index, list) {  
if (iterator.call(context, value, index, list)) results[results.length] = value;  
});  
return results;  
};  
   
// Return all the elements for which a truth test fails.  
_.reject = function(obj, iterator, context) {  
var results = [];  
if (obj == null) return results;  
each(obj, function(value, index, list) {  
if (!iterator.call(context, value, index, list)) results[results.length] = value;  
});  
return results;  
};  
   
// Determine whether all of the elements match a truth test.  
// Delegates to **ECMAScript 5**'s native `every` if available.  
// Aliased as `all`.  
_.every = _.all = function(obj, iterator, context) {  
var result = true;  
if (obj == null) return result;  
if (nativeEvery && obj.every === nativeEvery) return obj.every(iterator, context);  
each(obj, function(value, index, list) {  
if (!(result = result && iterator.call(context, value, index, list))) return breaker;  
});  
return result;  
};  
   
// Determine if at least one element in the object matches a truth test.  
// Delegates to **ECMAScript 5**'s native `some` if available.  
// Aliased as `any`.  
var any = _.some = _.any = function(obj, iterator, context) {  
iterator = iterator || _.identity;  
var result = false;  
if (obj == null) return result;  
if (nativeSome && obj.some === nativeSome) return obj.some(iterator, context);  
each(obj, function(value, index, list) {  
if (result |= iterator.call(context, value, index, list)) return breaker;  
});  
return !!result;  
};  
   
// Determine if a given value is included in the array or object using `===`.  
// Aliased as `contains`.  
_.include = _.contains = function(obj, target) {  
var found = false;  
if (obj == null) return found;  
if (nativeIndexOf && obj.indexOf === nativeIndexOf) return obj.indexOf(target) != -1;  
any(obj, function(value) {  
if (found = value === target) return true;  
});  
return found;  
};  
   
// Invoke a method (with arguments) on every item in a collection.  
_.invoke = function(obj, method) {  
var args = slice.call(arguments, 2);  
return _.map(obj, function(value) {  
return (method.call ? method || value : value[method]).apply(value, args);  
});  
};  
   
// Convenience version of a common use case of `map`: fetching a property.  
_.pluck = function(obj, key) {  
return _.map(obj, function(value){ return value[key]; });  
};  
   
// Return the maximum element or (element-based computation).  
_.max = function(obj, iterator, context) {  
if (!iterator && _.isArray(obj)) return Math.max.apply(Math, obj);  
var result = {computed : -Infinity};  
each(obj, function(value, index, list) {  
var computed = iterator ? iterator.call(context, value, index, list) : value;  
computed >= result.computed && (result = {value : value, computed : computed});  
});  
return result.value;  
};  
   
// Return the minimum element (or element-based computation).  
_.min = function(obj, iterator, context) {  
if (!iterator && _.isArray(obj)) return Math.min.apply(Math, obj);  
var result = {computed : Infinity};  
each(obj, function(value, index, list) {  
var computed = iterator ? iterator.call(context, value, index, list) : value;  
computed < result.computed && (result = {value : value, computed : computed});  
});  
return result.value;  
};  
   
// Sort the object's values by a criterion produced by an iterator.  
_.sortBy = function(obj, iterator, context) {  
return _.pluck(_.map(obj, function(value, index, list) {  
return {  
value : value,  
criteria : iterator.call(context, value, index, list)  
};  
}).sort(function(left, right) {  
var a = left.criteria, b = right.criteria;  
return a < b ? -1 : a > b ? 1 : 0;  
}), 'value');  
};  
   
// Groups the object's values by a criterion produced by an iterator  
_.groupBy = function(obj, iterator) {  
var result = {};  
each(obj, function(value, index) {  
var key = iterator(value, index);  
(result[key] || (result[key] = [])).push(value);  
});  
return result;  
};  
   
// Use a comparator function to figure out at what index an object should  
// be inserted so as to maintain order. Uses binary search.  
_.sortedIndex = function(array, obj, iterator) {  
iterator || (iterator = _.identity);  
var low = 0, high = array.length;  
while (low < high) {  
var mid = (low + high) >> 1;  
iterator(array[mid]) < iterator(obj) ? low = mid + 1 : high = mid;  
}  
return low;  
};  
   
// Safely convert anything iterable into a real, live array.  
_.toArray = function(iterable) {  
if (!iterable) return [];  
if (iterable.toArray) return iterable.toArray();  
if (_.isArray(iterable)) return slice.call(iterable);  
if (_.isArguments(iterable)) return slice.call(iterable);  
return _.values(iterable);  
};  
   
// Return the number of elements in an object.  
_.size = function(obj) {  
return _.toArray(obj).length;  
};  
   
// Array Functions  
// ---------------  
   
// Get the first element of an array. Passing **n** will return the first N  
// values in the array. Aliased as `head`. The **guard** check allows it to work  
// with `_.map`.  
_.first = _.head = function(array, n, guard) {  
return (n != null) && !guard ? slice.call(array, 0, n) : array[0];  
};  
   
// Returns everything but the first entry of the array. Aliased as `tail`.  
// Especially useful on the arguments object. Passing an **index** will return  
// the rest of the values in the array from that index onward. The **guard**  
// check allows it to work with `_.map`.  
_.rest = _.tail = function(array, index, guard) {  
return slice.call(array, (index == null) || guard ? 1 : index);  
};  
   
// Get the last element of an array.  
_.last = function(array) {  
return array[array.length - 1];  
};  
   
// Trim out all falsy values from an array.  
_.compact = function(array) {  
return _.filter(array, function(value){ return !!value; });  
};  
   
// Return a completely flattened version of an array.  
_.flatten = function(array) {  
return _.reduce(array, function(memo, value) {  
if (_.isArray(value)) return memo.concat(_.flatten(value));  
memo[memo.length] = value;  
return memo;  
}, []);  
};  
   
// Return a version of the array that does not contain the specified value(s).  
_.without = function(array) {  
return _.difference(array, slice.call(arguments, 1));  
};  
   
// Produce a duplicate-free version of the array. If the array has already  
// been sorted, you have the option of using a faster algorithm.  
// Aliased as `unique`.  
_.uniq = _.unique = function(array, isSorted) {  
return _.reduce(array, function(memo, el, i) {  
if (0 == i || (isSorted === true ? _.last(memo) != el : !_.include(memo, el))) memo[memo.length] = el;  
return memo;  
}, []);  
};  
   
// Produce an array that contains the union: each distinct element from all of  
// the passed-in arrays.  
_.union = function() {  
return _.uniq(_.flatten(arguments));  
};  
   
// Produce an array that contains every item shared between all the  
// passed-in arrays. (Aliased as "intersect" for back-compat.)  
_.intersection = _.intersect = function(array) {  
var rest = slice.call(arguments, 1);  
return _.filter(_.uniq(array), function(item) {  
return _.every(rest, function(other) {  
return _.indexOf(other, item) >= 0;  
});  
});  
};  
   
// Take the difference between one array and another.  
// Only the elements present in just the first array will remain.  
_.difference = function(array, other) {  
return _.filter(array, function(value){ return !_.include(other, value); });  
};  
   
// Zip together multiple lists into a single array -- elements that share  
// an index go together.  
_.zip = function() {  
var args = slice.call(arguments);  
var length = _.max(_.pluck(args, 'length'));  
var results = new Array(length);  
for (var i = 0; i < length; i++) results[i] = _.pluck(args, "" + i);  
return results;  
};  
   
// If the browser doesn't supply us with indexOf (I'm looking at you, **MSIE**),  
// we need this function. Return the position of the first occurrence of an  
// item in an array, or -1 if the item is not included in the array.  
// Delegates to **ECMAScript 5**'s native `indexOf` if available.  
// If the array is large and already in sort order, pass `true`  
// for **isSorted** to use binary search.  
_.indexOf = function(array, item, isSorted) {  
if (array == null) return -1;  
var i, l;  
if (isSorted) {  
i = _.sortedIndex(array, item);  
return array[i] === item ? i : -1;  
}  
if (nativeIndexOf && array.indexOf === nativeIndexOf) return array.indexOf(item);  
for (i = 0, l = array.length; i < l; i++) if (array[i] === item) return i;  
return -1;  
};  
   
   
// Delegates to **ECMAScript 5**'s native `lastIndexOf` if available.  
_.lastIndexOf = function(array, item) {  
if (array == null) return -1;  
if (nativeLastIndexOf && array.lastIndexOf === nativeLastIndexOf) return array.lastIndexOf(item);  
var i = array.length;  
while (i--) if (array[i] === item) return i;  
return -1;  
};  
   
// Generate an integer Array containing an arithmetic progression. A port of  
// the native Python `range()` function. See  
// [the Python documentation](http://docs.python.org/library/functions.html#range).  
_.range = function(start, stop, step) {  
if (arguments.length <= 1) {  
stop = start || 0;  
start = 0;  
}  
step = arguments[2] || 1;  
   
var len = Math.max(Math.ceil((stop - start) / step), 0);  
var idx = 0;  
var range = new Array(len);  
   
while(idx < len) {  
range[idx++] = start;  
start += step;  
}  
   
return range;  
};  
   
// Function (ahem) Functions  
// ------------------  
   
// Create a function bound to a given object (assigning `this`, and arguments,  
// optionally). Binding with arguments is also known as `curry`.  
// Delegates to **ECMAScript 5**'s native `Function.bind` if available.  
// We check for `func.bind` first, to fail fast when `func` is undefined.  
_.bind = function(func, obj) {  
if (func.bind === nativeBind && nativeBind) return nativeBind.apply(func, slice.call(arguments, 1));  
var args = slice.call(arguments, 2);  
return function() {  
return func.apply(obj, args.concat(slice.call(arguments)));  
};  
};  
   
// Bind all of an object's methods to that object. Useful for ensuring that  
// all callbacks defined on an object belong to it.  
_.bindAll = function(obj) {  
var funcs = slice.call(arguments, 1);  
if (funcs.length == 0) funcs = _.functions(obj);  
each(funcs, function(f) { obj[f] = _.bind(obj[f], obj); });  
return obj;  
};  
   
// Memoize an expensive function by storing its results.  
_.memoize = function(func, hasher) {  
var memo = {};  
hasher || (hasher = _.identity);  
return function() {  
var key = hasher.apply(this, arguments);  
return hasOwnProperty.call(memo, key) ? memo[key] : (memo[key] = func.apply(this, arguments));  
};  
};  
   
// Delays a function for the given number of milliseconds, and then calls  
// it with the arguments supplied.  
_.delay = function(func, wait) {  
var args = slice.call(arguments, 2);  
return setTimeout(function(){ return func.apply(func, args); }, wait);  
};  
   
// Defers a function, scheduling it to run after the current call stack has  
// cleared.  
_.defer = function(func) {  
return _.delay.apply(_, [func, 1].concat(slice.call(arguments, 1)));  
};  
   
// Internal function used to implement `_.throttle` and `_.debounce`.  
var limit = function(func, wait, debounce) {  
var timeout;  
return function() {  
var context = this, args = arguments;  
var throttler = function() {  
timeout = null;  
func.apply(context, args);  
};  
if (debounce) clearTimeout(timeout);  
if (debounce || !timeout) timeout = setTimeout(throttler, wait);  
};  
};  
   
// Returns a function, that, when invoked, will only be triggered at most once  
// during a given window of time.  
_.throttle = function(func, wait) {  
return limit(func, wait, false);  
};  
   
// Returns a function, that, as long as it continues to be invoked, will not  
// be triggered. The function will be called after it stops being called for  
// N milliseconds.  
_.debounce = function(func, wait) {  
return limit(func, wait, true);  
};  
   
// Returns a function that will be executed at most one time, no matter how  
// often you call it. Useful for lazy initialization.  
_.once = function(func) {  
var ran = false, memo;  
return function() {  
if (ran) return memo;  
ran = true;  
return memo = func.apply(this, arguments);  
};  
};  
   
// Returns the first function passed as an argument to the second,  
// allowing you to adjust arguments, run code before and after, and  
// conditionally execute the original function.  
_.wrap = function(func, wrapper) {  
return function() {  
var args = [func].concat(slice.call(arguments));  
return wrapper.apply(this, args);  
};  
};  
   
// Returns a function that is the composition of a list of functions, each  
// consuming the return value of the function that follows.  
_.compose = function() {  
var funcs = slice.call(arguments);  
return function() {  
var args = slice.call(arguments);  
for (var i = funcs.length - 1; i >= 0; i--) {  
args = [funcs[i].apply(this, args)];  
}  
return args[0];  
};  
};  
   
// Returns a function that will only be executed after being called N times.  
_.after = function(times, func) {  
return function() {  
if (--times < 1) { return func.apply(this, arguments); }  
};  
};  
   
   
// Object Functions  
// ----------------  
   
// Retrieve the names of an object's properties.  
// Delegates to **ECMAScript 5**'s native `Object.keys`  
_.keys = nativeKeys || function(obj) {  
if (obj !== Object(obj)) throw new TypeError('Invalid object');  
var keys = [];  
for (var key in obj) if (hasOwnProperty.call(obj, key)) keys[keys.length] = key;  
return keys;  
};  
   
// Retrieve the values of an object's properties.  
_.values = function(obj) {  
return _.map(obj, _.identity);  
};  
   
// Return a sorted list of the function names available on the object.  
// Aliased as `methods`  
_.functions = _.methods = function(obj) {  
var names = [];  
for (var key in obj) {  
if (_.isFunction(obj[key])) names.push(key);  
}  
return names.sort();  
};  
   
// Extend a given object with all the properties in passed-in object(s).  
_.extend = function(obj) {  
each(slice.call(arguments, 1), function(source) {  
for (var prop in source) {  
if (source[prop] !== void 0) obj[prop] = source[prop];  
}  
});  
return obj;  
};  
   
// Fill in a given object with default properties.  
_.defaults = function(obj) {  
each(slice.call(arguments, 1), function(source) {  
for (var prop in source) {  
if (obj[prop] == null) obj[prop] = source[prop];  
}  
});  
return obj;  
};  
   
// Create a (shallow-cloned) duplicate of an object.  
_.clone = function(obj) {  
return _.isArray(obj) ? obj.slice() : _.extend({}, obj);  
};  
   
// Invokes interceptor with the obj, and then returns obj.  
// The primary purpose of this method is to "tap into" a method chain, in  
// order to perform operations on intermediate results within the chain.  
_.tap = function(obj, interceptor) {  
interceptor(obj);  
return obj;  
};  
   
// Perform a deep comparison to check if two objects are equal.  
_.isEqual = function(a, b) {  
// Check object identity.  
if (a === b) return true;  
// Different types?  
var atype = typeof(a), btype = typeof(b);  
if (atype != btype) return false;  
// Basic equality test (watch out for coercions).  
if (a == b) return true;  
// One is falsy and the other truthy.  
if ((!a && b) || (a && !b)) return false;  
// Unwrap any wrapped objects.  
if (a._chain) a = a._wrapped;  
if (b._chain) b = b._wrapped;  
// One of them implements an isEqual()?  
if (a.isEqual) return a.isEqual(b);  
if (b.isEqual) return b.isEqual(a);  
// Check dates' integer values.  
if (_.isDate(a) && _.isDate(b)) return a.getTime() === b.getTime();  
// Both are NaN?  
if (_.isNaN(a) && _.isNaN(b)) return false;  
// Compare regular expressions.  
if (_.isRegExp(a) && _.isRegExp(b))  
return a.source === b.source &&  
a.global === b.global &&  
a.ignoreCase === b.ignoreCase &&  
a.multiline === b.multiline;  
// If a is not an object by this point, we can't handle it.  
if (atype !== 'object') return false;  
// Check for different array lengths before comparing contents.  
if (a.length && (a.length !== b.length)) return false;  
// Nothing else worked, deep compare the contents.  
var aKeys = _.keys(a), bKeys = _.keys(b);  
// Different object sizes?  
if (aKeys.length != bKeys.length) return false;  
// Recursive comparison of contents.  
for (var key in a) if (!(key in b) || !_.isEqual(a[key], b[key])) return false;  
return true;  
};  
   
// Is a given array or object empty?  
_.isEmpty = function(obj) {  
if (_.isArray(obj) || _.isString(obj)) return obj.length === 0;  
for (var key in obj) if (hasOwnProperty.call(obj, key)) return false;  
return true;  
};  
   
// Is a given value a DOM element?  
_.isElement = function(obj) {  
return !!(obj && obj.nodeType == 1);  
};  
   
// Is a given value an array?  
// Delegates to ECMA5's native Array.isArray  
_.isArray = nativeIsArray || function(obj) {  
return toString.call(obj) === '[object Array]';  
};  
   
// Is a given variable an object?  
_.isObject = function(obj) {  
return obj === Object(obj);  
};  
   
// Is a given variable an arguments object?  
_.isArguments = function(obj) {  
return !!(obj && hasOwnProperty.call(obj, 'callee'));  
};  
   
// Is a given value a function?  
_.isFunction = function(obj) {  
return !!(obj && obj.constructor && obj.call && obj.apply);  
};  
   
// Is a given value a string?  
_.isString = function(obj) {  
return !!(obj === '' || (obj && obj.charCodeAt && obj.substr));  
};  
   
// Is a given value a number?  
_.isNumber = function(obj) {  
return !!(obj === 0 || (obj && obj.toExponential && obj.toFixed));  
};  
   
// Is the given value `NaN`? `NaN` happens to be the only value in JavaScript  
// that does not equal itself.  
_.isNaN = function(obj) {  
return obj !== obj;  
};  
   
// Is a given value a boolean?  
_.isBoolean = function(obj) {  
return obj === true || obj === false;  
};  
   
// Is a given value a date?  
_.isDate = function(obj) {  
return !!(obj && obj.getTimezoneOffset && obj.setUTCFullYear);  
};  
   
// Is the given value a regular expression?  
_.isRegExp = function(obj) {  
return !!(obj && obj.test && obj.exec && (obj.ignoreCase || obj.ignoreCase === false));  
};  
   
// Is a given value equal to null?  
_.isNull = function(obj) {  
return obj === null;  
};  
   
// Is a given variable undefined?  
_.isUndefined = function(obj) {  
return obj === void 0;  
};  
   
// Utility Functions  
// -----------------  
   
// Run Underscore.js in *noConflict* mode, returning the `_` variable to its  
// previous owner. Returns a reference to the Underscore object.  
_.noConflict = function() {  
root._ = previousUnderscore;  
return this;  
};  
   
// Keep the identity function around for default iterators.  
_.identity = function(value) {  
return value;  
};  
   
// Run a function **n** times.  
_.times = function (n, iterator, context) {  
for (var i = 0; i < n; i++) iterator.call(context, i);  
};  
   
// Add your own custom functions to the Underscore object, ensuring that  
// they're correctly added to the OOP wrapper as well.  
_.mixin = function(obj) {  
each(_.functions(obj), function(name){  
addToWrapper(name, _[name] = obj[name]);  
});  
};  
   
// Generate a unique integer id (unique within the entire client session).  
// Useful for temporary DOM ids.  
var idCounter = 0;  
_.uniqueId = function(prefix) {  
var id = idCounter++;  
return prefix ? prefix + id : id;  
};  
   
// By default, Underscore uses ERB-style template delimiters, change the  
// following template settings to use alternative delimiters.  
_.templateSettings = {  
evaluate : /<%([\s\S]+?)%>/g,  
interpolate : /<%=([\s\S]+?)%>/g  
};  
   
// JavaScript micro-templating, similar to John Resig's implementation.  
// Underscore templating handles arbitrary delimiters, preserves whitespace,  
// and correctly escapes quotes within interpolated code.  
_.template = function(str, data) {  
var c = _.templateSettings;  
var tmpl = 'var __p=[],print=function(){__p.push.apply(__p,arguments);};' +  
'with(obj||{}){__p.push(\'' +  
str.replace(/\\/g, '\\\\')  
.replace(/'/g, "\\'")  
.replace(c.interpolate, function(match, code) {  
return "'," + code.replace(/\\'/g, "'") + ",'";  
})  
.replace(c.evaluate || null, function(match, code) {  
return "');" + code.replace(/\\'/g, "'")  
.replace(/[\r\n\t]/g, ' ') + "__p.push('";  
})  
.replace(/\r/g, '\\r')  
.replace(/\n/g, '\\n')  
.replace(/\t/g, '\\t')  
+ "');}return __p.join('');";  
var func = new Function('obj', tmpl);  
return data ? func(data) : func;  
};  
   
// The OOP Wrapper  
// ---------------  
   
// If Underscore is called as a function, it returns a wrapped object that  
// can be used OO-style. This wrapper holds altered versions of all the  
// underscore functions. Wrapped objects may be chained.  
var wrapper = function(obj) { this._wrapped = obj; };  
   
// Expose `wrapper.prototype` as `_.prototype`  
_.prototype = wrapper.prototype;  
   
// Helper function to continue chaining intermediate results.  
var result = function(obj, chain) {  
return chain ? _(obj).chain() : obj;  
};  
   
// A method to easily add functions to the OOP wrapper.  
var addToWrapper = function(name, func) {  
wrapper.prototype[name] = function() {  
var args = slice.call(arguments);  
unshift.call(args, this._wrapped);  
return result(func.apply(_, args), this._chain);  
};  
};  
   
// Add all of the Underscore functions to the wrapper object.  
_.mixin(_);  
   
// Add all mutator Array functions to the wrapper.  
each(['pop', 'push', 'reverse', 'shift', 'sort', 'splice', 'unshift'], function(name) {  
var method = ArrayProto[name];  
wrapper.prototype[name] = function() {  
method.apply(this._wrapped, arguments);  
return result(this._wrapped, this._chain);  
};  
});  
   
// Add all accessor Array functions to the wrapper.  
each(['concat', 'join', 'slice'], function(name) {  
var method = ArrayProto[name];  
wrapper.prototype[name] = function() {  
return result(method.apply(this._wrapped, arguments), this._chain);  
};  
});  
   
// Start chaining a wrapped Underscore object.  
wrapper.prototype.chain = function() {  
this._chain = true;  
return this;  
};  
   
// Extracts the result from a wrapped and chained object.  
wrapper.prototype.value = function() {  
return this._wrapped;  
};  
   
})(); })();
/** /**
* Flotr2 (c) 2012 Carl Sutherland * Flotr2 (c) 2012 Carl Sutherland
* MIT License * MIT License
* Special thanks to: * Special thanks to:
* Flotr: http://code.google.com/p/flotr/ (fork) * Flotr: http://code.google.com/p/flotr/ (fork)
* Flot: https://github.com/flot/flot (original fork) * Flot: https://github.com/flot/flot (original fork)
*/ */
(function () { (function () {
   
var var
global = this, global = this,
previousFlotr = this.Flotr, previousFlotr = this.Flotr,
Flotr; Flotr;
   
Flotr = { Flotr = {
_: _, _: _,
bean: bean, bean: bean,
isIphone: /iphone/i.test(navigator.userAgent), isIphone: /iphone/i.test(navigator.userAgent),
isIE: (navigator.appVersion.indexOf("MSIE") != -1 ? parseFloat(navigator.appVersion.split("MSIE")[1]) : false), isIE: (navigator.appVersion.indexOf("MSIE") != -1 ? parseFloat(navigator.appVersion.split("MSIE")[1]) : false),
   
/** /**
* An object of the registered graph types. Use Flotr.addType(type, object) * An object of the registered graph types. Use Flotr.addType(type, object)
* to add your own type. * to add your own type.
*/ */
graphTypes: {}, graphTypes: {},
   
/** /**
* The list of the registered plugins * The list of the registered plugins
*/ */
plugins: {}, plugins: {},
   
/** /**
* Can be used to add your own chart type. * Can be used to add your own chart type.
* @param {String} name - Type of chart, like 'pies', 'bars' etc. * @param {String} name - Type of chart, like 'pies', 'bars' etc.
* @param {String} graphType - The object containing the basic drawing functions (draw, etc) * @param {String} graphType - The object containing the basic drawing functions (draw, etc)
*/ */
addType: function(name, graphType){ addType: function (name, graphType) {
Flotr.graphTypes[name] = graphType; Flotr.graphTypes[name] = graphType;
Flotr.defaultOptions[name] = graphType.options || {}; Flotr.defaultOptions[name] = graphType.options || {};
Flotr.defaultOptions.defaultType = Flotr.defaultOptions.defaultType || name; Flotr.defaultOptions.defaultType = Flotr.defaultOptions.defaultType || name;
}, },
   
/** /**
* Can be used to add a plugin * Can be used to add a plugin
* @param {String} name - The name of the plugin * @param {String} name - The name of the plugin
* @param {String} plugin - The object containing the plugin's data (callbacks, options, function1, function2, ...) * @param {String} plugin - The object containing the plugin's data (callbacks, options, function1, function2, ...)
*/ */
addPlugin: function(name, plugin){ addPlugin: function (name, plugin) {
Flotr.plugins[name] = plugin; Flotr.plugins[name] = plugin;
Flotr.defaultOptions[name] = plugin.options || {}; Flotr.defaultOptions[name] = plugin.options || {};
}, },
   
/** /**
* Draws the graph. This function is here for backwards compatibility with Flotr version 0.1.0alpha. * Draws the graph. This function is here for backwards compatibility with Flotr version 0.1.0alpha.
* You could also draw graphs by directly calling Flotr.Graph(element, data, options). * You could also draw graphs by directly calling Flotr.Graph(element, data, options).
* @param {Element} el - element to insert the graph into * @param {Element} el - element to insert the graph into
* @param {Object} data - an array or object of dataseries * @param {Object} data - an array or object of dataseries
* @param {Object} options - an object containing options * @param {Object} options - an object containing options
* @param {Class} _GraphKlass_ - (optional) Class to pass the arguments to, defaults to Flotr.Graph * @param {Class} _GraphKlass_ - (optional) Class to pass the arguments to, defaults to Flotr.Graph
* @return {Object} returns a new graph object and of course draws the graph. * @return {Object} returns a new graph object and of course draws the graph.
*/ */
draw: function(el, data, options, GraphKlass){ draw: function (el, data, options, GraphKlass) {
GraphKlass = GraphKlass || Flotr.Graph; GraphKlass = GraphKlass || Flotr.Graph;
return new GraphKlass(el, data, options); return new GraphKlass(el, data, options);
}, },
   
/** /**
* Recursively merges two objects. * Recursively merges two objects.
* @param {Object} src - source object (likely the object with the least properties) * @param {Object} src - source object (likely the object with the least properties)
* @param {Object} dest - destination object (optional, object with the most properties) * @param {Object} dest - destination object (optional, object with the most properties)
* @return {Object} recursively merged Object * @return {Object} recursively merged Object
* @TODO See if we can't remove this. * @TODO See if we can't remove this.
*/ */
merge: function(src, dest){ merge: function (src, dest) {
var i, v, result = dest || {}; var i, v, result = dest || {};
   
for (i in src) { for (i in src) {
v = src[i]; v = src[i];
if (v && typeof(v) === 'object') { if (v && typeof(v) === 'object') {
if (v.constructor === Array) { if (v.constructor === Array) {
result[i] = this._.clone(v); result[i] = this._.clone(v);
} else if (v.constructor !== RegExp && !this._.isElement(v)) { } else if (v.constructor !== RegExp && !this._.isElement(v)) {
result[i] = Flotr.merge(v, (dest ? dest[i] : undefined)); result[i] = Flotr.merge(v, (dest ? dest[i] : undefined));
} else { } else {
result[i] = v; result[i] = v;
} }
} else { } else {
result[i] = v; result[i] = v;
} }
} }
   
return result; return result;
}, },
   
/** /**
* Recursively clones an object. * Recursively clones an object.
* @param {Object} object - The object to clone * @param {Object} object - The object to clone
* @return {Object} the clone * @return {Object} the clone
* @TODO See if we can't remove this. * @TODO See if we can't remove this.
*/ */
clone: function(object){ clone: function (object) {
return Flotr.merge(object, {}); return Flotr.merge(object, {});
}, },
   
/** /**
* Function calculates the ticksize and returns it. * Function calculates the ticksize and returns it.
* @param {Integer} noTicks - number of ticks * @param {Integer} noTicks - number of ticks
* @param {Integer} min - lower bound integer value for the current axis * @param {Integer} min - lower bound integer value for the current axis
* @param {Integer} max - upper bound integer value for the current axis * @param {Integer} max - upper bound integer value for the current axis
* @param {Integer} decimals - number of decimals for the ticks * @param {Integer} decimals - number of decimals for the ticks
* @return {Integer} returns the ticksize in pixels * @return {Integer} returns the ticksize in pixels
*/ */
getTickSize: function(noTicks, min, max, decimals){ getTickSize: function (noTicks, min, max, decimals) {
var delta = (max - min) / noTicks, var delta = (max - min) / noTicks,
magn = Flotr.getMagnitude(delta), magn = Flotr.getMagnitude(delta),
tickSize = 10, tickSize = 10,
norm = delta / magn; // Norm is between 1.0 and 10.0. norm = delta / magn; // Norm is between 1.0 and 10.0.
   
if(norm < 1.5) tickSize = 1; if (norm < 1.5) tickSize = 1;
else if(norm < 2.25) tickSize = 2; else if (norm < 2.25) tickSize = 2;
else if(norm < 3) tickSize = ((decimals === 0) ? 2 : 2.5); else if (norm < 3) tickSize = ((decimals === 0) ? 2 : 2.5);
else if(norm < 7.5) tickSize = 5; else if (norm < 7.5) tickSize = 5;
   
return tickSize * magn; return tickSize * magn;
}, },
   
/** /**
* Default tick formatter. * Default tick formatter.
* @param {String, Integer} val - tick value integer * @param {String, Integer} val - tick value integer
* @param {Object} axisOpts - the axis' options * @param {Object} axisOpts - the axis' options
* @return {String} formatted tick string * @return {String} formatted tick string
*/ */
defaultTickFormatter: function(val, axisOpts){ defaultTickFormatter: function (val, axisOpts) {
return val+''; return val + '';
}, },
   
/** /**
* Formats the mouse tracker values. * Formats the mouse tracker values.
* @param {Object} obj - Track value Object {x:..,y:..} * @param {Object} obj - Track value Object {x:..,y:..}
* @return {String} Formatted track string * @return {String} Formatted track string
*/ */
defaultTrackFormatter: function(obj){ defaultTrackFormatter: function (obj) {
return '('+obj.x+', '+obj.y+')'; return '(' + obj.x + ', ' + obj.y + ')';
}, },
   
/** /**
* Utility function to convert file size values in bytes to kB, MB, ... * Utility function to convert file size values in bytes to kB, MB, ...
* @param value {Number} - The value to convert * @param value {Number} - The value to convert
* @param precision {Number} - The number of digits after the comma (default: 2) * @param precision {Number} - The number of digits after the comma (default: 2)
* @param base {Number} - The base (default: 1000) * @param base {Number} - The base (default: 1000)
*/ */
engineeringNotation: function(value, precision, base){ engineeringNotation: function (value, precision, base) {
var sizes = ['Y','Z','E','P','T','G','M','k',''], var sizes = ['Y', 'Z', 'E', 'P', 'T', 'G', 'M', 'k', ''],
fractionSizes = ['y','z','a','f','p','n','µ','m',''], fractionSizes = ['y', 'z', 'a', 'f', 'p', 'n', 'µ', 'm', ''],
total = sizes.length; total = sizes.length;
   
base = base || 1000; base = base || 1000;
precision = Math.pow(10, precision || 2); precision = Math.pow(10, precision || 2);
   
if (value === 0) return 0; if (value === 0) return 0;
   
if (value > 1) { if (value > 1) {
while (total-- && (value >= base)) value /= base; while (total-- && (value >= base)) value /= base;
} }
else { else {
sizes = fractionSizes; sizes = fractionSizes;
total = sizes.length; total = sizes.length;
while (total-- && (value < 1)) value *= base; while (total-- && (value < 1)) value *= base;
} }
   
return (Math.round(value * precision) / precision) + sizes[total]; return (Math.round(value * precision) / precision) + sizes[total];
}, },
   
/** /**
* Returns the magnitude of the input value. * Returns the magnitude of the input value.
* @param {Integer, Float} x - integer or float value * @param {Integer, Float} x - integer or float value
* @return {Integer, Float} returns the magnitude of the input value * @return {Integer, Float} returns the magnitude of the input value
*/ */
getMagnitude: function(x){ getMagnitude: function (x) {
return Math.pow(10, Math.floor(Math.log(x) / Math.LN10)); return Math.pow(10, Math.floor(Math.log(x) / Math.LN10));
}, },
toPixel: function(val){ toPixel: function (val) {
return Math.floor(val)+0.5;//((val-Math.round(val) < 0.4) ? (Math.floor(val)-0.5) : val); return Math.floor(val) + 0.5;//((val-Math.round(val) < 0.4) ? (Math.floor(val)-0.5) : val);
}, },
toRad: function(angle){ toRad: function (angle) {
return -angle * (Math.PI/180); return -angle * (Math.PI / 180);
}, },
floorInBase: function(n, base) { floorInBase: function (n, base) {
return base * Math.floor(n / base); return base * Math.floor(n / base);
}, },
drawText: function(ctx, text, x, y, style) { drawText: function (ctx, text, x, y, style) {
if (!ctx.fillText) { if (!ctx.fillText) {
ctx.drawText(text, x, y, style); ctx.drawText(text, x, y, style);
return; return;
} }
   
style = this._.extend({ style = this._.extend({
size: Flotr.defaultOptions.fontSize, size: Flotr.defaultOptions.fontSize,
color: '#000000', color: '#000000',
textAlign: 'left', textAlign: 'left',
textBaseline: 'bottom', textBaseline: 'bottom',
weight: 1, weight: 1,
angle: 0 angle: 0
}, style); }, style);
   
ctx.save(); ctx.save();
ctx.translate(x, y); ctx.translate(x, y);
ctx.rotate(style.angle); ctx.rotate(style.angle);
ctx.fillStyle = style.color; ctx.fillStyle = style.color;
ctx.font = (style.weight > 1 ? "bold " : "") + (style.size*1.3) + "px sans-serif"; ctx.font = (style.weight > 1 ? "bold " : "") + (style.size * 1.3) + "px sans-serif";
ctx.textAlign = style.textAlign; ctx.textAlign = style.textAlign;
ctx.textBaseline = style.textBaseline; ctx.textBaseline = style.textBaseline;
ctx.fillText(text, 0, 0); ctx.fillText(text, 0, 0);
ctx.restore(); ctx.restore();
}, },
getBestTextAlign: function(angle, style) { getBestTextAlign: function (angle, style) {
style = style || {textAlign: 'center', textBaseline: 'middle'}; style = style || {textAlign: 'center', textBaseline: 'middle'};
angle += Flotr.getTextAngleFromAlign(style); angle += Flotr.getTextAngleFromAlign(style);
   
if (Math.abs(Math.cos(angle)) > 10e-3) if (Math.abs(Math.cos(angle)) > 10e-3)
style.textAlign = (Math.cos(angle) > 0 ? 'right' : 'left'); style.textAlign = (Math.cos(angle) > 0 ? 'right' : 'left');
   
if (Math.abs(Math.sin(angle)) > 10e-3) if (Math.abs(Math.sin(angle)) > 10e-3)
style.textBaseline = (Math.sin(angle) > 0 ? 'top' : 'bottom'); style.textBaseline = (Math.sin(angle) > 0 ? 'top' : 'bottom');
   
return style; return style;
}, },
alignTable: { alignTable: {
'right middle' : 0, 'right middle': 0,
'right top' : Math.PI/4, 'right top': Math.PI / 4,
'center top' : Math.PI/2, 'center top': Math.PI / 2,
'left top' : 3*(Math.PI/4), 'left top': 3 * (Math.PI / 4),
'left middle' : Math.PI, 'left middle': Math.PI,
'left bottom' : -3*(Math.PI/4), 'left bottom': -3 * (Math.PI / 4),
'center bottom': -Math.PI/2, 'center bottom': -Math.PI / 2,
'right bottom' : -Math.PI/4, 'right bottom': -Math.PI / 4,
'center middle': 0 'center middle': 0
}, },
getTextAngleFromAlign: function(style) { getTextAngleFromAlign: function (style) {
return Flotr.alignTable[style.textAlign+' '+style.textBaseline] || 0; return Flotr.alignTable[style.textAlign + ' ' + style.textBaseline] || 0;
}, },
noConflict : function () { noConflict: function () {
global.Flotr = previousFlotr; global.Flotr = previousFlotr;
return this; return this;
} }
}; };
   
global.Flotr = Flotr; global.Flotr = Flotr;
   
})(); })();
   
/** /**
* Flotr Defaults * Flotr Defaults
*/ */
Flotr.defaultOptions = { Flotr.defaultOptions = {
colors: ['#00A8F0', '#C0D800', '#CB4B4B', '#4DA74D', '#9440ED'], //=> The default colorscheme. When there are > 5 series, additional colors are generated. colors: ['#00A8F0', '#C0D800', '#CB4B4B', '#4DA74D', '#9440ED'], //=> The default colorscheme. When there are > 5 series, additional colors are generated.
ieBackgroundColor: '#FFFFFF', // Background color for excanvas clipping ieBackgroundColor: '#FFFFFF', // Background color for excanvas clipping
title: null, // => The graph's title title: null, // => The graph's title
subtitle: null, // => The graph's subtitle subtitle: null, // => The graph's subtitle
shadowSize: 4, // => size of the 'fake' shadow shadowSize: 4, // => size of the 'fake' shadow
defaultType: null, // => default series type defaultType: null, // => default series type
HtmlText: true, // => wether to draw the text using HTML or on the canvas HtmlText: true, // => wether to draw the text using HTML or on the canvas
fontColor: '#545454', // => default font color fontColor: '#545454', // => default font color
fontSize: 7.5, // => canvas' text font size fontSize: 7.5, // => canvas' text font size
resolution: 1, // => resolution of the graph, to have printer-friendly graphs ! resolution: 1, // => resolution of the graph, to have printer-friendly graphs !
parseFloat: true, // => whether to preprocess data for floats (ie. if input is string) parseFloat: true, // => whether to preprocess data for floats (ie. if input is string)
xaxis: { xaxis: {
ticks: null, // => format: either [1, 3] or [[1, 'a'], 3] ticks: null, // => format: either [1, 3] or [[1, 'a'], 3]
minorTicks: null, // => format: either [1, 3] or [[1, 'a'], 3] minorTicks: null, // => format: either [1, 3] or [[1, 'a'], 3]
showLabels: true, // => setting to true will show the axis ticks labels, hide otherwise showLabels: true, // => setting to true will show the axis ticks labels, hide otherwise
showMinorLabels: false,// => true to show the axis minor ticks labels, false to hide showMinorLabels: false,// => true to show the axis minor ticks labels, false to hide
labelsAngle: 0, // => labels' angle, in degrees labelsAngle: 0, // => labels' angle, in degrees
title: null, // => axis title title: null, // => axis title
titleAngle: 0, // => axis title's angle, in degrees titleAngle: 0, // => axis title's angle, in degrees
noTicks: 5, // => number of ticks for automagically generated ticks noTicks: 5, // => number of ticks for automagically generated ticks
minorTickFreq: null, // => number of minor ticks between major ticks for autogenerated ticks minorTickFreq: null, // => number of minor ticks between major ticks for autogenerated ticks
tickFormatter: Flotr.defaultTickFormatter, // => fn: number, Object -> string tickFormatter: Flotr.defaultTickFormatter, // => fn: number, Object -> string
tickDecimals: null, // => no. of decimals, null means auto tickDecimals: null, // => no. of decimals, null means auto
min: null, // => min. value to show, null means set automatically min: null, // => min. value to show, null means set automatically
max: null, // => max. value to show, null means set automatically max: null, // => max. value to show, null means set automatically
autoscale: false, // => Turns autoscaling on with true autoscale: false, // => Turns autoscaling on with true
autoscaleMargin: 0, // => margin in % to add if auto-setting min/max autoscaleMargin: 0, // => margin in % to add if auto-setting min/max
color: null, // => color of the ticks color: null, // => color of the ticks
mode: 'normal', // => can be 'time' or 'normal' mode: 'normal', // => can be 'time' or 'normal'
timeFormat: null, timeFormat: null,
timeMode:'UTC', // => For UTC time ('local' for local time). timeMode: 'UTC', // => For UTC time ('local' for local time).
timeUnit:'millisecond',// => Unit for time (millisecond, second, minute, hour, day, month, year) timeUnit: 'millisecond',// => Unit for time (millisecond, second, minute, hour, day, month, year)
scaling: 'linear', // => Scaling, can be 'linear' or 'logarithmic' scaling: 'linear', // => Scaling, can be 'linear' or 'logarithmic'
base: Math.E, base: Math.E,
titleAlign: 'center', titleAlign: 'center',
margin: true // => Turn off margins with false margin: true // => Turn off margins with false
}, },
x2axis: {}, x2axis: {},
yaxis: { yaxis: {
ticks: null, // => format: either [1, 3] or [[1, 'a'], 3] ticks: null, // => format: either [1, 3] or [[1, 'a'], 3]
minorTicks: null, // => format: either [1, 3] or [[1, 'a'], 3] minorTicks: null, // => format: either [1, 3] or [[1, 'a'], 3]
showLabels: true, // => setting to true will show the axis ticks labels, hide otherwise showLabels: true, // => setting to true will show the axis ticks labels, hide otherwise
showMinorLabels: false,// => true to show the axis minor ticks labels, false to hide showMinorLabels: false,// => true to show the axis minor ticks labels, false to hide
labelsAngle: 0, // => labels' angle, in degrees labelsAngle: 0, // => labels' angle, in degrees
title: null, // => axis title title: null, // => axis title
titleAngle: 90, // => axis title's angle, in degrees titleAngle: 90, // => axis title's angle, in degrees
noTicks: 5, // => number of ticks for automagically generated ticks noTicks: 5, // => number of ticks for automagically generated ticks
minorTickFreq: null, // => number of minor ticks between major ticks for autogenerated ticks minorTickFreq: null, // => number of minor ticks between major ticks for autogenerated ticks
tickFormatter: Flotr.defaultTickFormatter, // => fn: number, Object -> string tickFormatter: Flotr.defaultTickFormatter, // => fn: number, Object -> string
tickDecimals: null, // => no. of decimals, null means auto tickDecimals: null, // => no. of decimals, null means auto
min: null, // => min. value to show, null means set automatically min: null, // => min. value to show, null means set automatically
max: null, // => max. value to show, null means set automatically max: null, // => max. value to show, null means set automatically
autoscale: false, // => Turns autoscaling on with true autoscale: false, // => Turns autoscaling on with true
autoscaleMargin: 0, // => margin in % to add if auto-setting min/max autoscaleMargin: 0, // => margin in % to add if auto-setting min/max
color: null, // => The color of the ticks color: null, // => The color of the ticks
scaling: 'linear', // => Scaling, can be 'linear' or 'logarithmic' scaling: 'linear', // => Scaling, can be 'linear' or 'logarithmic'
base: Math.E, base: Math.E,
titleAlign: 'center', titleAlign: 'center',
margin: true // => Turn off margins with false margin: true // => Turn off margins with false
}, },
y2axis: { y2axis: {
titleAngle: 270 titleAngle: 270
}, },
grid: { grid: {
color: '#545454', // => primary color used for outline and labels color: '#545454', // => primary color used for outline and labels
backgroundColor: null, // => null for transparent, else color backgroundColor: null, // => null for transparent, else color
backgroundImage: null, // => background image. String or object with src, left and top backgroundImage: null, // => background image. String or object with src, left and top
watermarkAlpha: 0.4, // => watermarkAlpha: 0.4, // =>
tickColor: '#DDDDDD', // => color used for the ticks tickColor: '#DDDDDD', // => color used for the ticks
labelMargin: 3, // => margin in pixels labelMargin: 3, // => margin in pixels
verticalLines: true, // => whether to show gridlines in vertical direction verticalLines: true, // => whether to show gridlines in vertical direction
minorVerticalLines: null, // => whether to show gridlines for minor ticks in vertical dir. minorVerticalLines: null, // => whether to show gridlines for minor ticks in vertical dir.
horizontalLines: true, // => whether to show gridlines in horizontal direction horizontalLines: true, // => whether to show gridlines in horizontal direction
minorHorizontalLines: null, // => whether to show gridlines for minor ticks in horizontal dir. minorHorizontalLines: null, // => whether to show gridlines for minor ticks in horizontal dir.
outlineWidth: 1, // => width of the grid outline/border in pixels outlineWidth: 1, // => width of the grid outline/border in pixels
outline : 'nsew', // => walls of the outline to display outline: 'nsew', // => walls of the outline to display
circular: false // => if set to true, the grid will be circular, must be used when radars are drawn circular: false // => if set to true, the grid will be circular, must be used when radars are drawn
}, },
mouse: { mouse: {
track: false, // => true to track the mouse, no tracking otherwise track: false, // => true to track the mouse, no tracking otherwise
trackAll: false, trackAll: false,
position: 'se', // => position of the value box (default south-east) position: 'se', // => position of the value box (default south-east)
relative: false, // => next to the mouse cursor relative: false, // => next to the mouse cursor
trackFormatter: Flotr.defaultTrackFormatter, // => formats the values in the value box trackFormatter: Flotr.defaultTrackFormatter, // => formats the values in the value box
margin: 5, // => margin in pixels of the valuebox margin: 5, // => margin in pixels of the valuebox
lineColor: '#FF3F19', // => line color of points that are drawn when mouse comes near a value of a series lineColor: '#FF3F19', // => line color of points that are drawn when mouse comes near a value of a series
trackDecimals: 1, // => decimals for the track values trackDecimals: 1, // => decimals for the track values
sensibility: 2, // => the lower this number, the more precise you have to aim to show a value sensibility: 2, // => the lower this number, the more precise you have to aim to show a value
trackY: true, // => whether or not to track the mouse in the y axis trackY: true, // => whether or not to track the mouse in the y axis
radius: 3, // => radius of the track point radius: 3, // => radius of the track point
fillColor: null, // => color to fill our select bar with only applies to bar and similar graphs (only bars for now) fillColor: null, // => color to fill our select bar with only applies to bar and similar graphs (only bars for now)
fillOpacity: 0.4 // => opacity of the fill color, set to 1 for a solid fill, 0 hides the fill fillOpacity: 0.4 // => opacity of the fill color, set to 1 for a solid fill, 0 hides the fill
} }
}; };
   
/** /**
* Flotr Color * Flotr Color
*/ */
   
(function () { (function () {
   
var var
_ = Flotr._; _ = Flotr._;
   
// Constructor // Constructor
function Color (r, g, b, a) { function Color(r, g, b, a) {
this.rgba = ['r','g','b','a']; this.rgba = ['r', 'g', 'b', 'a'];
var x = 4; var x = 4;
while(-1<--x){ while (-1 < --x) {
this[this.rgba[x]] = arguments[x] || ((x==3) ? 1.0 : 0); this[this.rgba[x]] = arguments[x] || ((x == 3) ? 1.0 : 0);
} }
this.normalize(); this.normalize();
} }
   
// Constants // Constants
var COLOR_NAMES = { var COLOR_NAMES = {
aqua:[0,255,255],azure:[240,255,255],beige:[245,245,220],black:[0,0,0],blue:[0,0,255], aqua: [0, 255, 255], azure: [240, 255, 255], beige: [245, 245, 220], black: [0, 0, 0], blue: [0, 0, 255],
brown:[165,42,42],cyan:[0,255,255],darkblue:[0,0,139],darkcyan:[0,139,139],darkgrey:[169,169,169], brown: [165, 42, 42], cyan: [0, 255, 255], darkblue: [0, 0, 139], darkcyan: [0, 139, 139], darkgrey: [169, 169, 169],
darkgreen:[0,100,0],darkkhaki:[189,183,107],darkmagenta:[139,0,139],darkolivegreen:[85,107,47], darkgreen: [0, 100, 0], darkkhaki: [189, 183, 107], darkmagenta: [139, 0, 139], darkolivegreen: [85, 107, 47],
darkorange:[255,140,0],darkorchid:[153,50,204],darkred:[139,0,0],darksalmon:[233,150,122], darkorange: [255, 140, 0], darkorchid: [153, 50, 204], darkred: [139, 0, 0], darksalmon: [233, 150, 122],
darkviolet:[148,0,211],fuchsia:[255,0,255],gold:[255,215,0],green:[0,128,0],indigo:[75,0,130], darkviolet: [148, 0, 211], fuchsia: [255, 0, 255], gold: [255, 215, 0], green: [0, 128, 0], indigo: [75, 0, 130],
khaki:[240,230,140],lightblue:[173,216,230],lightcyan:[224,255,255],lightgreen:[144,238,144], khaki: [240, 230, 140], lightblue: [173, 216, 230], lightcyan: [224, 255, 255], lightgreen: [144, 238, 144],
lightgrey:[211,211,211],lightpink:[255,182,193],lightyellow:[255,255,224],lime:[0,255,0],magenta:[255,0,255], lightgrey: [211, 211, 211], lightpink: [255, 182, 193], lightyellow: [255, 255, 224], lime: [0, 255, 0], magenta: [255, 0, 255],
maroon:[128,0,0],navy:[0,0,128],olive:[128,128,0],orange:[255,165,0],pink:[255,192,203],purple:[128,0,128], maroon: [128, 0, 0], navy: [0, 0, 128], olive: [128, 128, 0], orange: [255, 165, 0], pink: [255, 192, 203], purple: [128, 0, 128],
violet:[128,0,128],red:[255,0,0],silver:[192,192,192],white:[255,255,255],yellow:[255,255,0] violet: [128, 0, 128], red: [255, 0, 0], silver: [192, 192, 192], white: [255, 255, 255], yellow: [255, 255, 0]
}; };
   
Color.prototype = { Color.prototype = {
scale: function(rf, gf, bf, af){ scale: function (rf, gf, bf, af) {
var x = 4; var x = 4;
while (-1 < --x) { while (-1 < --x) {
if (!_.isUndefined(arguments[x])) this[this.rgba[x]] *= arguments[x]; if (!_.isUndefined(arguments[x])) this[this.rgba[x]] *= arguments[x];
} }
return this.normalize(); return this.normalize();
}, },
alpha: function(alpha) { alpha: function (alpha) {
if (!_.isUndefined(alpha) && !_.isNull(alpha)) { if (!_.isUndefined(alpha) && !_.isNull(alpha)) {
this.a = alpha; this.a = alpha;
} }
return this.normalize(); return this.normalize();
}, },
clone: function(){ clone: function () {
return new Color(this.r, this.b, this.g, this.a); return new Color(this.r, this.b, this.g, this.a);
}, },
limit: function(val,minVal,maxVal){ limit: function (val, minVal, maxVal) {
return Math.max(Math.min(val, maxVal), minVal); return Math.max(Math.min(val, maxVal), minVal);
}, },
normalize: function(){ normalize: function () {
var limit = this.limit; var limit = this.limit;
this.r = limit(parseInt(this.r, 10), 0, 255); this.r = limit(parseInt(this.r, 10), 0, 255);
this.g = limit(parseInt(this.g, 10), 0, 255); this.g = limit(parseInt(this.g, 10), 0, 255);
this.b = limit(parseInt(this.b, 10), 0, 255); this.b = limit(parseInt(this.b, 10), 0, 255);
this.a = limit(this.a, 0, 1); this.a = limit(this.a, 0, 1);
return this; return this;
}, },
distance: function(color){ distance: function (color) {
if (!color) return; if (!color) return;
color = new Color.parse(color); color = new Color.parse(color);
var dist = 0, x = 3; var dist = 0, x = 3;
while(-1<--x){ while (-1 < --x) {
dist += Math.abs(this[this.rgba[x]] - color[this.rgba[x]]); dist += Math.abs(this[this.rgba[x]] - color[this.rgba[x]]);
} }
return dist; return dist;
}, },
toString: function(){ toString: function () {
return (this.a >= 1.0) ? 'rgb('+[this.r,this.g,this.b].join(',')+')' : 'rgba('+[this.r,this.g,this.b,this.a].join(',')+')'; return (this.a >= 1.0) ? 'rgb(' + [this.r, this.g, this.b].join(',') + ')' : 'rgba(' + [this.r, this.g, this.b, this.a].join(',') + ')';
}, },
contrast: function () { contrast: function () {
var var
test = 1 - ( 0.299 * this.r + 0.587 * this.g + 0.114 * this.b) / 255; test = 1 - ( 0.299 * this.r + 0.587 * this.g + 0.114 * this.b) / 255;
return (test < 0.5 ? '#000000' : '#ffffff'); return (test < 0.5 ? '#000000' : '#ffffff');
} }
}; };
   
_.extend(Color, { _.extend(Color, {
/** /**
* Parses a color string and returns a corresponding Color. * Parses a color string and returns a corresponding Color.
* The different tests are in order of probability to improve speed. * The different tests are in order of probability to improve speed.
* @param {String, Color} str - string thats representing a color * @param {String, Color} str - string thats representing a color
* @return {Color} returns a Color object or false * @return {Color} returns a Color object or false
*/ */
parse: function(color){ parse: function (color) {
if (color instanceof Color) return color; if (color instanceof Color) return color;
   
var result; var result;
   
// #a0b1c2 // #a0b1c2
if((result = /#([a-fA-F0-9]{2})([a-fA-F0-9]{2})([a-fA-F0-9]{2})/.exec(color))) if ((result = /#([a-fA-F0-9]{2})([a-fA-F0-9]{2})([a-fA-F0-9]{2})/.exec(color)))
return new Color(parseInt(result[1], 16), parseInt(result[2], 16), parseInt(result[3], 16)); return new Color(parseInt(result[1], 16), parseInt(result[2], 16), parseInt(result[3], 16));
   
// rgb(num,num,num) // rgb(num,num,num)
if((result = /rgb\(\s*([0-9]{1,3})\s*,\s*([0-9]{1,3})\s*,\s*([0-9]{1,3})\s*\)/.exec(color))) if ((result = /rgb\(\s*([0-9]{1,3})\s*,\s*([0-9]{1,3})\s*,\s*([0-9]{1,3})\s*\)/.exec(color)))
return new Color(parseInt(result[1], 10), parseInt(result[2], 10), parseInt(result[3], 10)); return new Color(parseInt(result[1], 10), parseInt(result[2], 10), parseInt(result[3], 10));
   
// #fff // #fff
if((result = /#([a-fA-F0-9])([a-fA-F0-9])([a-fA-F0-9])/.exec(color))) if ((result = /#([a-fA-F0-9])([a-fA-F0-9])([a-fA-F0-9])/.exec(color)))
return new Color(parseInt(result[1]+result[1],16), parseInt(result[2]+result[2],16), parseInt(result[3]+result[3],16)); return new Color(parseInt(result[1] + result[1], 16), parseInt(result[2] + result[2], 16), parseInt(result[3] + result[3], 16));
   
// rgba(num,num,num,num) // rgba(num,num,num,num)
if((result = /rgba\(\s*([0-9]{1,3})\s*,\s*([0-9]{1,3})\s*,\s*([0-9]{1,3})\s*,\s*([0-9]+(?:\.[0-9]+)?)\s*\)/.exec(color))) if ((result = /rgba\(\s*([0-9]{1,3})\s*,\s*([0-9]{1,3})\s*,\s*([0-9]{1,3})\s*,\s*([0-9]+(?:\.[0-9]+)?)\s*\)/.exec(color)))
return new Color(parseInt(result[1], 10), parseInt(result[2], 10), parseInt(result[3], 10), parseFloat(result[4])); return new Color(parseInt(result[1], 10), parseInt(result[2], 10), parseInt(result[3], 10), parseFloat(result[4]));
   
// rgb(num%,num%,num%) // rgb(num%,num%,num%)
if((result = /rgb\(\s*([0-9]+(?:\.[0-9]+)?)\%\s*,\s*([0-9]+(?:\.[0-9]+)?)\%\s*,\s*([0-9]+(?:\.[0-9]+)?)\%\s*\)/.exec(color))) if ((result = /rgb\(\s*([0-9]+(?:\.[0-9]+)?)\%\s*,\s*([0-9]+(?:\.[0-9]+)?)\%\s*,\s*([0-9]+(?:\.[0-9]+)?)\%\s*\)/.exec(color)))
return new Color(parseFloat(result[1])*2.55, parseFloat(result[2])*2.55, parseFloat(result[3])*2.55); return new Color(parseFloat(result[1]) * 2.55, parseFloat(result[2]) * 2.55, parseFloat(result[3]) * 2.55);
   
// rgba(num%,num%,num%,num) // rgba(num%,num%,num%,num)
if((result = /rgba\(\s*([0-9]+(?:\.[0-9]+)?)\%\s*,\s*([0-9]+(?:\.[0-9]+)?)\%\s*,\s*([0-9]+(?:\.[0-9]+)?)\%\s*,\s*([0-9]+(?:\.[0-9]+)?)\s*\)/.exec(color))) if ((result = /rgba\(\s*([0-9]+(?:\.[0-9]+)?)\%\s*,\s*([0-9]+(?:\.[0-9]+)?)\%\s*,\s*([0-9]+(?:\.[0-9]+)?)\%\s*,\s*([0-9]+(?:\.[0-9]+)?)\s*\)/.exec(color)))
return new Color(parseFloat(result[1])*2.55, parseFloat(result[2])*2.55, parseFloat(result[3])*2.55, parseFloat(result[4])); return new Color(parseFloat(result[1]) * 2.55, parseFloat(result[2]) * 2.55, parseFloat(result[3]) * 2.55, parseFloat(result[4]));
   
// Otherwise, we're most likely dealing with a named color. // Otherwise, we're most likely dealing with a named color.
var name = (color+'').replace(/^\s*([\S\s]*?)\s*$/, '$1').toLowerCase(); var name = (color + '').replace(/^\s*([\S\s]*?)\s*$/, '$1').toLowerCase();
if(name == 'transparent'){ if (name == 'transparent') {
return new Color(255, 255, 255, 0); return new Color(255, 255, 255, 0);
} }
return (result = COLOR_NAMES[name]) ? new Color(result[0], result[1], result[2]) : new Color(0, 0, 0, 0); return (result = COLOR_NAMES[name]) ? new Color(result[0], result[1], result[2]) : new Color(0, 0, 0, 0);
}, },
   
/** /**
* Process color and options into color style. * Process color and options into color style.
*/ */
processColor: function(color, options) { processColor: function (color, options) {
   
var opacity = options.opacity; var opacity = options.opacity;
if (!color) return 'rgba(0, 0, 0, 0)'; if (!color) return 'rgba(0, 0, 0, 0)';
if (color instanceof Color) return color.alpha(opacity).toString(); if (color instanceof Color) return color.alpha(opacity).toString();
if (_.isString(color)) return Color.parse(color).alpha(opacity).toString(); if (_.isString(color)) return Color.parse(color).alpha(opacity).toString();
   
var grad = color.colors ? color : {colors: color}; var grad = color.colors ? color : {colors: color};
   
if (!options.ctx) { if (!options.ctx) {
if (!_.isArray(grad.colors)) return 'rgba(0, 0, 0, 0)'; if (!_.isArray(grad.colors)) return 'rgba(0, 0, 0, 0)';
return Color.parse(_.isArray(grad.colors[0]) ? grad.colors[0][1] : grad.colors[0]).alpha(opacity).toString(); return Color.parse(_.isArray(grad.colors[0]) ? grad.colors[0][1] : grad.colors[0]).alpha(opacity).toString();
} }
grad = _.extend({start: 'top', end: 'bottom'}, grad); grad = _.extend({start: 'top', end: 'bottom'}, grad);
   
if (/top/i.test(grad.start)) options.x1 = 0; if (/top/i.test(grad.start)) options.x1 = 0;
if (/left/i.test(grad.start)) options.y1 = 0; if (/left/i.test(grad.start)) options.y1 = 0;
if (/bottom/i.test(grad.end)) options.x2 = 0; if (/bottom/i.test(grad.end)) options.x2 = 0;
if (/right/i.test(grad.end)) options.y2 = 0; if (/right/i.test(grad.end)) options.y2 = 0;
   
var i, c, stop, gradient = options.ctx.createLinearGradient(options.x1, options.y1, options.x2, options.y2); var i, c, stop, gradient = options.ctx.createLinearGradient(options.x1, options.y1, options.x2, options.y2);
for (i = 0; i < grad.colors.length; i++) { for (i = 0; i < grad.colors.length; i++) {
c = grad.colors[i]; c = grad.colors[i];
if (_.isArray(c)) { if (_.isArray(c)) {
stop = c[0]; stop = c[0];
c = c[1]; c = c[1];
} }
else stop = i / (grad.colors.length-1); else stop = i / (grad.colors.length - 1);
gradient.addColorStop(stop, Color.parse(c).alpha(opacity)); gradient.addColorStop(stop, Color.parse(c).alpha(opacity));
} }
return gradient; return gradient;
} }
}); });
   
Flotr.Color = Color; Flotr.Color = Color;
   
})(); })();
   
/** /**
* Flotr Date * Flotr Date
*/ */
Flotr.Date = { Flotr.Date = {
   
set : function (date, name, mode, value) { set: function (date, name, mode, value) {
mode = mode || 'UTC'; mode = mode || 'UTC';
name = 'set' + (mode === 'UTC' ? 'UTC' : '') + name; name = 'set' + (mode === 'UTC' ? 'UTC' : '') + name;
date[name](value); date[name](value);
}, },
   
get : function (date, name, mode) { get: function (date, name, mode) {
mode = mode || 'UTC'; mode = mode || 'UTC';
name = 'get' + (mode === 'UTC' ? 'UTC' : '') + name; name = 'get' + (mode === 'UTC' ? 'UTC' : '') + name;
return date[name](); return date[name]();
}, },
   
format: function(d, format, mode) { format: function (d, format, mode) {
if (!d) return; if (!d) return;
   
// We should maybe use an "official" date format spec, like PHP date() or ColdFusion // We should maybe use an "official" date format spec, like PHP date() or ColdFusion
// http://fr.php.net/manual/en/function.date.php // http://fr.php.net/manual/en/function.date.php
// http://livedocs.adobe.com/coldfusion/8/htmldocs/help.html?content=functions_c-d_29.html // http://livedocs.adobe.com/coldfusion/8/htmldocs/help.html?content=functions_c-d_29.html
var var
get = this.get, get = this.get,
tokens = { tokens = {
h: get(d, 'Hours', mode).toString(), h: get(d, 'Hours', mode).toString(),
H: leftPad(get(d, 'Hours', mode)), H: leftPad(get(d, 'Hours', mode)),
M: leftPad(get(d, 'Minutes', mode)), M: leftPad(get(d, 'Minutes', mode)),
S: leftPad(get(d, 'Seconds', mode)), S: leftPad(get(d, 'Seconds', mode)),
s: get(d, 'Milliseconds', mode), s: get(d, 'Milliseconds', mode),
d: get(d, 'Date', mode).toString(), d: get(d, 'Date', mode).toString(),
m: (get(d, 'Month') + 1).toString(), m: (get(d, 'Month') + 1).toString(),
y: get(d, 'FullYear').toString(), y: get(d, 'FullYear').toString(),
b: Flotr.Date.monthNames[get(d, 'Month', mode)] b: Flotr.Date.monthNames[get(d, 'Month', mode)]
}; };
   
function leftPad(n){ function leftPad(n) {
n += ''; n += '';
return n.length == 1 ? "0" + n : n; return n.length == 1 ? "0" + n : n;
} }
   
var r = [], c, var r = [], c,
escape = false; escape = false;
   
for (var i = 0; i < format.length; ++i) { for (var i = 0; i < format.length; ++i) {
c = format.charAt(i); c = format.charAt(i);
   
if (escape) { if (escape) {
r.push(tokens[c] || c); r.push(tokens[c] || c);
escape = false; escape = false;
} }
else if (c == "%") else if (c == "%")
escape = true; escape = true;
else else
r.push(c); r.push(c);
} }
return r.join(''); return r.join('');
}, },
getFormat: function(time, span) { getFormat: function (time, span) {
var tu = Flotr.Date.timeUnits; var tu = Flotr.Date.timeUnits;
if (time < tu.second) return "%h:%M:%S.%s"; if (time < tu.second) return "%h:%M:%S.%s";
else if (time < tu.minute) return "%h:%M:%S"; else if (time < tu.minute) return "%h:%M:%S";
else if (time < tu.day) return (span < 2 * tu.day) ? "%h:%M" : "%b %d %h:%M"; else if (time < tu.day) return (span < 2 * tu.day) ? "%h:%M" : "%b %d %h:%M";
else if (time < tu.month) return "%b %d"; else if (time < tu.month) return "%b %d";
else if (time < tu.year) return (span < tu.year) ? "%b" : "%b %y"; else if (time < tu.year) return (span < tu.year) ? "%b" : "%b %y";
else return "%y"; else return "%y";
}, },
formatter: function (v, axis) { formatter: function (v, axis) {
var var
options = axis.options, options = axis.options,
scale = Flotr.Date.timeUnits[options.timeUnit], scale = Flotr.Date.timeUnits[options.timeUnit],
d = new Date(v * scale); d = new Date(v * scale);
   
// first check global format // first check global format
if (axis.options.timeFormat) if (axis.options.timeFormat)
return Flotr.Date.format(d, options.timeFormat, options.timeMode); return Flotr.Date.format(d, options.timeFormat, options.timeMode);
   
var span = (axis.max - axis.min) * scale, var span = (axis.max - axis.min) * scale,
t = axis.tickSize * Flotr.Date.timeUnits[axis.tickUnit]; t = axis.tickSize * Flotr.Date.timeUnits[axis.tickUnit];
   
return Flotr.Date.format(d, Flotr.Date.getFormat(t, span), options.timeMode); return Flotr.Date.format(d, Flotr.Date.getFormat(t, span), options.timeMode);
}, },
generator: function(axis) { generator: function (axis) {
   
var var
set = this.set, set = this.set,
get = this.get, get = this.get,
timeUnits = this.timeUnits, timeUnits = this.timeUnits,
spec = this.spec, spec = this.spec,
options = axis.options, options = axis.options,
mode = options.timeMode, mode = options.timeMode,
scale = timeUnits[options.timeUnit], scale = timeUnits[options.timeUnit],
min = axis.min * scale, min = axis.min * scale,
max = axis.max * scale, max = axis.max * scale,
delta = (max - min) / options.noTicks, delta = (max - min) / options.noTicks,
ticks = [], ticks = [],
tickSize = axis.tickSize, tickSize = axis.tickSize,
tickUnit, tickUnit,
formatter, i; formatter, i;
   
// Use custom formatter or time tick formatter // Use custom formatter or time tick formatter
formatter = (options.tickFormatter === Flotr.defaultTickFormatter ? formatter = (options.tickFormatter === Flotr.defaultTickFormatter ?
this.formatter : options.tickFormatter this.formatter : options.tickFormatter
); );
   
for (i = 0; i < spec.length - 1; ++i) { for (i = 0; i < spec.length - 1; ++i) {
var d = spec[i][0] * timeUnits[spec[i][1]]; var d = spec[i][0] * timeUnits[spec[i][1]];
if (delta < (d + spec[i+1][0] * timeUnits[spec[i+1][1]]) / 2 && d >= tickSize) if (delta < (d + spec[i + 1][0] * timeUnits[spec[i + 1][1]]) / 2 && d >= tickSize)
break; break;
} }
tickSize = spec[i][0]; tickSize = spec[i][0];
tickUnit = spec[i][1]; tickUnit = spec[i][1];
   
// special-case the possibility of several years // special-case the possibility of several years
if (tickUnit == "year") { if (tickUnit == "year") {
tickSize = Flotr.getTickSize(options.noTicks*timeUnits.year, min, max, 0); tickSize = Flotr.getTickSize(options.noTicks * timeUnits.year, min, max, 0);
   
// Fix for 0.5 year case // Fix for 0.5 year case
if (tickSize == 0.5) { if (tickSize == 0.5) {
tickUnit = "month"; tickUnit = "month";
tickSize = 6; tickSize = 6;
} }
} }
   
axis.tickUnit = tickUnit; axis.tickUnit = tickUnit;
axis.tickSize = tickSize; axis.tickSize = tickSize;
   
var var
d = new Date(min); d = new Date(min);
   
var step = tickSize * timeUnits[tickUnit]; var step = tickSize * timeUnits[tickUnit];
   
function setTick (name) { function setTick(name) {
set(d, name, mode, Flotr.floorInBase( set(d, name, mode, Flotr.floorInBase(
get(d, name, mode), tickSize get(d, name, mode), tickSize
)); ));
} }
   
switch (tickUnit) { switch (tickUnit) {
case "millisecond": setTick('Milliseconds'); break; case "millisecond":
case "second": setTick('Seconds'); break; setTick('Milliseconds');
case "minute": setTick('Minutes'); break; break;
case "hour": setTick('Hours'); break; case "second":
case "month": setTick('Month'); break; setTick('Seconds');
case "year": setTick('FullYear'); break; break;
} case "minute":
  setTick('Minutes');
// reset smaller components break;
if (step >= timeUnits.second) set(d, 'Milliseconds', mode, 0); case "hour":
if (step >= timeUnits.minute) set(d, 'Seconds', mode, 0); setTick('Hours');
if (step >= timeUnits.hour) set(d, 'Minutes', mode, 0); break;
if (step >= timeUnits.day) set(d, 'Hours', mode, 0); case "month":
if (step >= timeUnits.day * 4) set(d, 'Date', mode, 1); setTick('Month');
if (step >= timeUnits.year) set(d, 'Month', mode, 0); break;
  case "year":
var carry = 0, v = NaN, prev; setTick('FullYear');
do { break;
prev = v; }
v = d.getTime();  
ticks.push({ v: v / scale, label: formatter(v / scale, axis) }); // reset smaller components
if (tickUnit == "month") { if (step >= timeUnits.second) set(d, 'Milliseconds', mode, 0);
if (tickSize < 1) { if (step >= timeUnits.minute) set(d, 'Seconds', mode, 0);
/* a bit complicated - we'll divide the month up but we need to take care of fractions if (step >= timeUnits.hour) set(d, 'Minutes', mode, 0);
so we don't end up in the middle of a day */ if (step >= timeUnits.day) set(d, 'Hours', mode, 0);
set(d, 'Date', mode, 1); if (step >= timeUnits.day * 4) set(d, 'Date', mode, 1);
var start = d.getTime(); if (step >= timeUnits.year) set(d, 'Month', mode, 0);
set(d, 'Month', mode, get(d, 'Month', mode) + 1)  
var end = d.getTime(); var carry = 0, v = NaN, prev;
d.setTime(v + carry * timeUnits.hour + (end - start) * tickSize); do {
carry = get(d, 'Hours', mode) prev = v;
set(d, 'Hours', mode, 0); v = d.getTime();
} ticks.push({ v: v / scale, label: formatter(v / scale, axis) });
else if (tickUnit == "month") {
set(d, 'Month', mode, get(d, 'Month', mode) + tickSize); if (tickSize < 1) {
} /* a bit complicated - we'll divide the month up but we need to take care of fractions
else if (tickUnit == "year") { so we don't end up in the middle of a day */
set(d, 'FullYear', mode, get(d, 'FullYear', mode) + tickSize); set(d, 'Date', mode, 1);
} var start = d.getTime();
else set(d, 'Month', mode, get(d, 'Month', mode) + 1)
d.setTime(v + step); var end = d.getTime();
  d.setTime(v + carry * timeUnits.hour + (end - start) * tickSize);
} while (v < max && v != prev); carry = get(d, 'Hours', mode)
  set(d, 'Hours', mode, 0);
return ticks; }
}, else
timeUnits: { set(d, 'Month', mode, get(d, 'Month', mode) + tickSize);
millisecond: 1, }
second: 1000, else if (tickUnit == "year") {
minute: 1000 * 60, set(d, 'FullYear', mode, get(d, 'FullYear', mode) + tickSize);
hour: 1000 * 60 * 60, }
day: 1000 * 60 * 60 * 24, else
month: 1000 * 60 * 60 * 24 * 30, d.setTime(v + step);
year: 1000 * 60 * 60 * 24 * 365.2425  
}, } while (v < max && v != prev);
// the allowed tick sizes, after 1 year we use an integer algorithm  
spec: [ return ticks;
[1, "millisecond"], [20, "millisecond"], [50, "millisecond"], [100, "millisecond"], [200, "millisecond"], [500, "millisecond"], },
[1, "second"], [2, "second"], [5, "second"], [10, "second"], [30, "second"], timeUnits: {
[1, "minute"], [2, "minute"], [5, "minute"], [10, "minute"], [30, "minute"], millisecond: 1,
[1, "hour"], [2, "hour"], [4, "hour"], [8, "hour"], [12, "hour"], second: 1000,
[1, "day"], [2, "day"], [3, "day"], minute: 1000 * 60,
[0.25, "month"], [0.5, "month"], [1, "month"], [2, "month"], [3, "month"], [6, "month"], hour: 1000 * 60 * 60,
[1, "year"] day: 1000 * 60 * 60 * 24,
], month: 1000 * 60 * 60 * 24 * 30,
monthNames: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"] year: 1000 * 60 * 60 * 24 * 365.2425
  },
  // the allowed tick sizes, after 1 year we use an integer algorithm
  spec: [
  [1, "millisecond"],
  [20, "millisecond"],
  [50, "millisecond"],
  [100, "millisecond"],
  [200, "millisecond"],
  [500, "millisecond"],
  [1, "second"],
  [2, "second"],
  [5, "second"],
  [10, "second"],
  [30, "second"],
  [1, "minute"],
  [2, "minute"],
  [5, "minute"],
  [10, "minute"],
  [30, "minute"],
  [1, "hour"],
  [2, "hour"],
  [4, "hour"],
  [8, "hour"],
  [12, "hour"],
  [1, "day"],
  [2, "day"],
  [3, "day"],
  [0.25, "month"],
  [0.5, "month"],
  [1, "month"],
  [2, "month"],
  [3, "month"],
  [6, "month"],
  [1, "year"]
  ],
  monthNames: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
}; };
   
(function () { (function () {
   
var _ = Flotr._; var _ = Flotr._;
   
Flotr.DOM = { Flotr.DOM = {
addClass: function(element, name){ addClass: function (element, name) {
var classList = (element.className ? element.className : ''); var classList = (element.className ? element.className : '');
if (_.include(classList.split(/\s+/g), name)) return; if (_.include(classList.split(/\s+/g), name)) return;
element.className = (classList ? classList + ' ' : '') + name; element.className = (classList ? classList + ' ' : '') + name;
}, },
/** /**
* Create an element. * Create an element.
*/ */
create: function(tag){ create: function (tag) {
return document.createElement(tag); return document.createElement(tag);
}, },
node: function(html) { node: function (html) {
var div = Flotr.DOM.create('div'), n; var div = Flotr.DOM.create('div'), n;
div.innerHTML = html; div.innerHTML = html;
n = div.children[0]; n = div.children[0];
div.innerHTML = ''; div.innerHTML = '';
return n; return n;
}, },
/** /**
* Remove all children. * Remove all children.
*/ */
empty: function(element){ empty: function (element) {
element.innerHTML = ''; element.innerHTML = '';
/* /*
if (!element) return; if (!element) return;
_.each(element.childNodes, function (e) { _.each(element.childNodes, function (e) {
Flotr.DOM.empty(e); Flotr.DOM.empty(e);
element.removeChild(e); element.removeChild(e);
}); });
*/ */
}, },
hide: function(element){ hide: function (element) {
Flotr.DOM.setStyles(element, {display:'none'}); Flotr.DOM.setStyles(element, {display: 'none'});
}, },
/** /**
* Insert a child. * Insert a child.
* @param {Element} element * @param {Element} element
* @param {Element|String} Element or string to be appended. * @param {Element|String} Element or string to be appended.
*/ */
insert: function(element, child){ insert: function (element, child) {
if(_.isString(child)) if (_.isString(child))
element.innerHTML += child; element.innerHTML += child;
else if (_.isElement(child)) else if (_.isElement(child))
element.appendChild(child); element.appendChild(child);
}, },
// @TODO find xbrowser implementation // @TODO find xbrowser implementation
opacity: function(element, opacity) { opacity: function (element, opacity) {
element.style.opacity = opacity; element.style.opacity = opacity;
}, },
position: function(element, p){ position: function (element, p) {
if (!element.offsetParent) if (!element.offsetParent)
return {left: (element.offsetLeft || 0), top: (element.offsetTop || 0)}; return {left: (element.offsetLeft || 0), top: (element.offsetTop || 0)};
   
p = this.position(element.offsetParent); p = this.position(element.offsetParent);
p.left += element.offsetLeft; p.left += element.offsetLeft;
p.top += element.offsetTop; p.top += element.offsetTop;
return p; return p;
}, },
removeClass: function(element, name) { removeClass: function (element, name) {
var classList = (element.className ? element.className : ''); var classList = (element.className ? element.className : '');
element.className = _.filter(classList.split(/\s+/g), function (c) { element.className = _.filter(classList.split(/\s+/g),function (c) {
if (c != name) return true; } if (c != name) return true;
).join(' '); }
}, ).join(' ');
setStyles: function(element, o) { },
_.each(o, function (value, key) { setStyles: function (element, o) {
element.style[key] = value; _.each(o, function (value, key) {
}); element.style[key] = value;
}, });
show: function(element){ },
Flotr.DOM.setStyles(element, {display:''}); show: function (element) {
}, Flotr.DOM.setStyles(element, {display: ''});
/** },
* Return element size. /**
*/ * Return element size.
size: function(element){ */
return { size: function (element) {
height : element.offsetHeight, return {
width : element.offsetWidth }; height: element.offsetHeight,
} width: element.offsetWidth };
}; }
  };
   
})(); })();
   
/** /**
* Flotr Event Adapter * Flotr Event Adapter
*/ */
(function () { (function () {
var var
F = Flotr, F = Flotr,
bean = F.bean; bean = F.bean;
F.EventAdapter = { F.EventAdapter = {
observe: function(object, name, callback) { observe: function (object, name, callback) {
bean.add(object, name, callback); bean.add(object, name, callback);
return this; return this;
}, },
fire: function(object, name, args) { fire: function (object, name, args) {
bean.fire(object, name, args); bean.fire(object, name, args);
if (typeof(Prototype) != 'undefined') if (typeof(Prototype) != 'undefined')
Event.fire(object, name, args); Event.fire(object, name, args);
// @TODO Someone who uses mootools, add mootools adapter for existing applciations. // @TODO Someone who uses mootools, add mootools adapter for existing applciations.
return this; return this;
}, },
stopObserving: function(object, name, callback) { stopObserving: function (object, name, callback) {
bean.remove(object, name, callback); bean.remove(object, name, callback);
return this; return this;
}, },
eventPointer: function(e) { eventPointer: function (e) {
if (!F._.isUndefined(e.touches) && e.touches.length > 0) { if (!F._.isUndefined(e.touches) && e.touches.length > 0) {
return { return {
x : e.touches[0].pageX, x: e.touches[0].pageX,
y : e.touches[0].pageY y: e.touches[0].pageY
}; };
} else if (!F._.isUndefined(e.changedTouches) && e.changedTouches.length > 0) { } else if (!F._.isUndefined(e.changedTouches) && e.changedTouches.length > 0) {
return { return {
x : e.changedTouches[0].pageX, x: e.changedTouches[0].pageX,
y : e.changedTouches[0].pageY y: e.changedTouches[0].pageY
}; };
} else if (e.pageX || e.pageY) { } else if (e.pageX || e.pageY) {
return { return {
x : e.pageX, x: e.pageX,
y : e.pageY y: e.pageY
}; };
} else if (e.clientX || e.clientY) { } else if (e.clientX || e.clientY) {
var var
d = document, d = document,
b = d.body, b = d.body,
de = d.documentElement; de = d.documentElement;
return { return {
x: e.clientX + b.scrollLeft + de.scrollLeft, x: e.clientX + b.scrollLeft + de.scrollLeft,
y: e.clientY + b.scrollTop + de.scrollTop y: e.clientY + b.scrollTop + de.scrollTop
}; };
} }
} }
}; };
})(); })();
   
/** /**
* Text Utilities * Text Utilities
*/ */
(function () { (function () {
   
var  
F = Flotr,  
D = F.DOM,  
_ = F._,  
   
Text = function (o) {  
this.o = o;  
};  
   
Text.prototype = {  
   
dimensions : function (text, canvasStyle, htmlStyle, className) {  
   
if (!text) return { width : 0, height : 0 };  
   
return (this.o.html) ?  
this.html(text, this.o.element, htmlStyle, className) :  
this.canvas(text, canvasStyle);  
},  
   
canvas : function (text, style) {  
   
if (!this.o.textEnabled) return;  
style = style || {};  
   
var var
metrics = this.measureText(text, style), F = Flotr,
width = metrics.width, D = F.DOM,
height = style.size || F.defaultOptions.fontSize, _ = F._,
angle = style.angle || 0,  
cosAngle = Math.cos(angle), Text = function (o) {
sinAngle = Math.sin(angle), this.o = o;
widthPadding = 2, };
heightPadding = 6,  
bounds; Text.prototype = {
   
bounds = { dimensions: function (text, canvasStyle, htmlStyle, className) {
width: Math.abs(cosAngle * width) + Math.abs(sinAngle * height) + widthPadding,  
height: Math.abs(sinAngle * width) + Math.abs(cosAngle * height) + heightPadding if (!text) return { width: 0, height: 0 };
};  
  return (this.o.html) ?
return bounds; this.html(text, this.o.element, htmlStyle, className) :
}, this.canvas(text, canvasStyle);
  },
html : function (text, element, style, className) {  
  canvas: function (text, style) {
var div = D.create('div');  
  if (!this.o.textEnabled) return;
D.setStyles(div, { 'position' : 'absolute', 'top' : '-10000px' }); style = style || {};
D.insert(div, '<div style="'+style+'" class="'+className+' flotr-dummy-div">' + text + '</div>');  
D.insert(this.o.element, div); var
  metrics = this.measureText(text, style),
return D.size(div); width = metrics.width,
}, height = style.size || F.defaultOptions.fontSize,
  angle = style.angle || 0,
measureText : function (text, style) { cosAngle = Math.cos(angle),
  sinAngle = Math.sin(angle),
var widthPadding = 2,
context = this.o.ctx, heightPadding = 6,
metrics; bounds;
   
if (!context.fillText || (F.isIphone && context.measure)) { bounds = {
return { width : context.measure(text, style)}; width: Math.abs(cosAngle * width) + Math.abs(sinAngle * height) + widthPadding,
} height: Math.abs(sinAngle * width) + Math.abs(cosAngle * height) + heightPadding
  };
style = _.extend({  
size: F.defaultOptions.fontSize, return bounds;
weight: 1, },
angle: 0  
}, style); html: function (text, element, style, className) {
   
context.save(); var div = D.create('div');
context.font = (style.weight > 1 ? "bold " : "") + (style.size*1.3) + "px sans-serif";  
metrics = context.measureText(text); D.setStyles(div, { 'position': 'absolute', 'top': '-10000px' });
context.restore(); D.insert(div, '<div style="' + style + '" class="' + className + ' flotr-dummy-div">' + text + '</div>');
  D.insert(this.o.element, div);
return metrics;  
} return D.size(div);
}; },
   
Flotr.Text = Text; measureText: function (text, style) {
   
  var
  context = this.o.ctx,
  metrics;
   
  if (!context.fillText || (F.isIphone && context.measure)) {
  return { width: context.measure(text, style)};
  }
   
  style = _.extend({
  size: F.defaultOptions.fontSize,
  weight: 1,
  angle: 0
  }, style);
   
  context.save();
  context.font = (style.weight > 1 ? "bold " : "") + (style.size * 1.3) + "px sans-serif";
  metrics = context.measureText(text);
  context.restore();
   
  return metrics;
  }
  };
   
  Flotr.Text = Text;
   
})(); })();
   
/** /**
* Flotr Graph class that plots a graph on creation. * Flotr Graph class that plots a graph on creation.
*/ */
(function () { (function () {
   
var var
D = Flotr.DOM, D = Flotr.DOM,
E = Flotr.EventAdapter, E = Flotr.EventAdapter,
_ = Flotr._, _ = Flotr._,
flotr = Flotr; flotr = Flotr;
/** /**
* Flotr Graph constructor. * Flotr Graph constructor.
* @param {Element} el - element to insert the graph into * @param {Element} el - element to insert the graph into
* @param {Object} data - an array or object of dataseries * @param {Object} data - an array or object of dataseries
* @param {Object} options - an object containing options * @param {Object} options - an object containing options
*/ */
Graph = function(el, data, options){ Graph = function (el, data, options) {
// Let's see if we can get away with out this [JS] // Let's see if we can get away with out this [JS]
// try { // try {
this._setEl(el); this._setEl(el);
this._initMembers(); this._initMembers();
this._initPlugins(); this._initPlugins();
   
E.fire(this.el, 'flotr:beforeinit', [this]); E.fire(this.el, 'flotr:beforeinit', [this]);
   
this.data = data; this.data = data;
this.series = flotr.Series.getSeries(data); this.series = flotr.Series.getSeries(data);
this._initOptions(options); this._initOptions(options);
this._initGraphTypes(); this._initGraphTypes();
this._initCanvas(); this._initCanvas();
this._text = new flotr.Text({ this._text = new flotr.Text({
element : this.el, element: this.el,
ctx : this.ctx, ctx: this.ctx,
html : this.options.HtmlText, html: this.options.HtmlText,
textEnabled : this.textEnabled textEnabled: this.textEnabled
}); });
E.fire(this.el, 'flotr:afterconstruct', [this]); E.fire(this.el, 'flotr:afterconstruct', [this]);
this._initEvents(); this._initEvents();
   
this.findDataRanges(); this.findDataRanges();
this.calculateSpacing(); this.calculateSpacing();
   
this.draw(_.bind(function() { this.draw(_.bind(function () {
E.fire(this.el, 'flotr:afterinit', [this]); E.fire(this.el, 'flotr:afterinit', [this]);
}, this)); }, this));
/* /*
try { try {
} catch (e) { } catch (e) {
try { try {
console.error(e); console.error(e);
} catch (e2) {} } catch (e2) {}
}*/ }*/
}; };
   
function observe (object, name, callback) { function observe(object, name, callback) {
E.observe.apply(this, arguments); E.observe.apply(this, arguments);
this._handles.push(arguments); this._handles.push(arguments);
return this; return this;
}  
   
Graph.prototype = {  
   
destroy: function () {  
E.fire(this.el, 'flotr:destroy');  
_.each(this._handles, function (handle) {  
E.stopObserving.apply(this, handle);  
});  
this._handles = [];  
this.el.graph = null;  
},  
   
observe : observe,  
   
/**  
* @deprecated  
*/  
_observe : observe,  
   
processColor: function(color, options){  
var o = { x1: 0, y1: 0, x2: this.plotWidth, y2: this.plotHeight, opacity: 1, ctx: this.ctx };  
_.extend(o, options);  
return flotr.Color.processColor(color, o);  
},  
/**  
* Function determines the min and max values for the xaxis and yaxis.  
*  
* TODO logarithmic range validation (consideration of 0)  
*/  
findDataRanges: function(){  
var a = this.axes,  
xaxis, yaxis, range;  
   
_.each(this.series, function (series) {  
range = series.getRange();  
if (range) {  
xaxis = series.xaxis;  
yaxis = series.yaxis;  
xaxis.datamin = Math.min(range.xmin, xaxis.datamin);  
xaxis.datamax = Math.max(range.xmax, xaxis.datamax);  
yaxis.datamin = Math.min(range.ymin, yaxis.datamin);  
yaxis.datamax = Math.max(range.ymax, yaxis.datamax);  
xaxis.used = (xaxis.used || range.xused);  
yaxis.used = (yaxis.used || range.yused);  
}  
}, this);  
   
// Check for empty data, no data case (none used)  
if (!a.x.used && !a.x2.used) a.x.used = true;  
if (!a.y.used && !a.y2.used) a.y.used = true;  
   
_.each(a, function (axis) {  
axis.calculateRange();  
});  
   
var  
types = _.keys(flotr.graphTypes),  
drawn = false;  
   
_.each(this.series, function (series) {  
if (series.hide) return;  
_.each(types, function (type) {  
if (series[type] && series[type].show) {  
this.extendRange(type, series);  
drawn = true;  
}  
}, this);  
if (!drawn) {  
this.extendRange(this.options.defaultType, series);  
}  
}, this);  
},  
   
extendRange : function (type, series) {  
if (this[type].extendRange) this[type].extendRange(series, series.data, series[type], this[type]);  
if (this[type].extendYRange) this[type].extendYRange(series.yaxis, series.data, series[type], this[type]);  
if (this[type].extendXRange) this[type].extendXRange(series.xaxis, series.data, series[type], this[type]);  
},  
   
/**  
* Calculates axis label sizes.  
*/  
calculateSpacing: function(){  
   
var a = this.axes,  
options = this.options,  
series = this.series,  
margin = options.grid.labelMargin,  
T = this._text,  
x = a.x,  
x2 = a.x2,  
y = a.y,  
y2 = a.y2,  
maxOutset = options.grid.outlineWidth,  
i, j, l, dim;  
   
// TODO post refactor, fix this  
_.each(a, function (axis) {  
axis.calculateTicks();  
axis.calculateTextDimensions(T, options);  
});  
   
// Title height  
dim = T.dimensions(  
options.title,  
{size: options.fontSize*1.5},  
'font-size:1em;font-weight:bold;',  
'flotr-title'  
);  
this.titleHeight = dim.height;  
   
// Subtitle height  
dim = T.dimensions(  
options.subtitle,  
{size: options.fontSize},  
'font-size:smaller;',  
'flotr-subtitle'  
);  
this.subtitleHeight = dim.height;  
   
for(j = 0; j < options.length; ++j){  
if (series[j].points.show){  
maxOutset = Math.max(maxOutset, series[j].points.radius + series[j].points.lineWidth/2);  
}  
} }
   
var p = this.plotOffset; Graph.prototype = {
if (x.options.margin === false) {  
p.bottom = 0; destroy: function () {
p.top = 0; E.fire(this.el, 'flotr:destroy');
} else { _.each(this._handles, function (handle) {
p.bottom += (options.grid.circular ? 0 : (x.used && x.options.showLabels ? (x.maxLabel.height + margin) : 0)) + E.stopObserving.apply(this, handle);
(x.used && x.options.title ? (x.titleSize.height + margin) : 0) + maxOutset; });
  this._handles = [];
p.top += (options.grid.circular ? 0 : (x2.used && x2.options.showLabels ? (x2.maxLabel.height + margin) : 0)) + this.el.graph = null;
(x2.used && x2.options.title ? (x2.titleSize.height + margin) : 0) + this.subtitleHeight + this.titleHeight + maxOutset; },
}  
if (y.options.margin === false) { observe: observe,
p.left = 0;  
p.right = 0; /**
} else { * @deprecated
p.left += (options.grid.circular ? 0 : (y.used && y.options.showLabels ? (y.maxLabel.width + margin) : 0)) + */
(y.used && y.options.title ? (y.titleSize.width + margin) : 0) + maxOutset; _observe: observe,
   
p.right += (options.grid.circular ? 0 : (y2.used && y2.options.showLabels ? (y2.maxLabel.width + margin) : 0)) + processColor: function (color, options) {
(y2.used && y2.options.title ? (y2.titleSize.width + margin) : 0) + maxOutset; var o = { x1: 0, y1: 0, x2: this.plotWidth, y2: this.plotHeight, opacity: 1, ctx: this.ctx };
} _.extend(o, options);
  return flotr.Color.processColor(color, o);
p.top = Math.floor(p.top); // In order the outline not to be blured },
  /**
this.plotWidth = this.canvasWidth - p.left - p.right; * Function determines the min and max values for the xaxis and yaxis.
this.plotHeight = this.canvasHeight - p.bottom - p.top; *
  * TODO logarithmic range validation (consideration of 0)
// TODO post refactor, fix this */
x.length = x2.length = this.plotWidth; findDataRanges: function () {
y.length = y2.length = this.plotHeight; var a = this.axes,
y.offset = y2.offset = this.plotHeight; xaxis, yaxis, range;
x.setScale();  
x2.setScale(); _.each(this.series, function (series) {
y.setScale(); range = series.getRange();
y2.setScale(); if (range) {
}, xaxis = series.xaxis;
/** yaxis = series.yaxis;
* Draws grid, labels, series and outline. xaxis.datamin = Math.min(range.xmin, xaxis.datamin);
*/ xaxis.datamax = Math.max(range.xmax, xaxis.datamax);
draw: function(after) { yaxis.datamin = Math.min(range.ymin, yaxis.datamin);
  yaxis.datamax = Math.max(range.ymax, yaxis.datamax);
var xaxis.used = (xaxis.used || range.xused);
context = this.ctx, yaxis.used = (yaxis.used || range.yused);
i; }
  }, this);
E.fire(this.el, 'flotr:beforedraw', [this.series, this]);  
  // Check for empty data, no data case (none used)
if (this.series.length) { if (!a.x.used && !a.x2.used) a.x.used = true;
  if (!a.y.used && !a.y2.used) a.y.used = true;
context.save();  
context.translate(this.plotOffset.left, this.plotOffset.top); _.each(a, function (axis) {
  axis.calculateRange();
for (i = 0; i < this.series.length; i++) { });
if (!this.series[i].hide) this.drawSeries(this.series[i]);  
} var
  types = _.keys(flotr.graphTypes),
context.restore(); drawn = false;
this.clip();  
} _.each(this.series, function (series) {
  if (series.hide) return;
E.fire(this.el, 'flotr:afterdraw', [this.series, this]); _.each(types, function (type) {
if (after) after(); if (series[type] && series[type].show) {
}, this.extendRange(type, series);
/** drawn = true;
* Actually draws the graph. }
* @param {Object} series - series to draw }, this);
*/ if (!drawn) {
drawSeries: function(series){ this.extendRange(this.options.defaultType, series);
  }
function drawChart (series, typeKey) { }, this);
var options = this.getOptions(series, typeKey); },
this[typeKey].draw(options);  
} extendRange: function (type, series) {
  if (this[type].extendRange) this[type].extendRange(series, series.data, series[type], this[type]);
var drawn = false; if (this[type].extendYRange) this[type].extendYRange(series.yaxis, series.data, series[type], this[type]);
series = series || this.series; if (this[type].extendXRange) this[type].extendXRange(series.xaxis, series.data, series[type], this[type]);
  },
_.each(flotr.graphTypes, function (type, typeKey) {  
if (series[typeKey] && series[typeKey].show && this[typeKey]) { /**
drawn = true; * Calculates axis label sizes.
drawChart.call(this, series, typeKey); */
} calculateSpacing: function () {
}, this);  
  var a = this.axes,
if (!drawn) drawChart.call(this, series, this.options.defaultType); options = this.options,
}, series = this.series,
  margin = options.grid.labelMargin,
getOptions : function (series, typeKey) { T = this._text,
var x = a.x,
type = series[typeKey], x2 = a.x2,
graphType = this[typeKey], y = a.y,
options = { y2 = a.y2,
context : this.ctx, maxOutset = options.grid.outlineWidth,
width : this.plotWidth, i, j, l, dim;
height : this.plotHeight,  
fontSize : this.options.fontSize, // TODO post refactor, fix this
fontColor : this.options.fontColor, _.each(a, function (axis) {
textEnabled : this.textEnabled, axis.calculateTicks();
htmlText : this.options.HtmlText, axis.calculateTextDimensions(T, options);
text : this._text, // TODO Is this necessary? });
element : this.el,  
data : series.data, // Title height
color : series.color, dim = T.dimensions(
shadowSize : series.shadowSize, options.title,
xScale : _.bind(series.xaxis.d2p, series.xaxis), {size: options.fontSize * 1.5},
yScale : _.bind(series.yaxis.d2p, series.yaxis) 'font-size:1em;font-weight:bold;',
}; 'flotr-title'
  );
options = flotr.merge(type, options); this.titleHeight = dim.height;
   
// Fill // Subtitle height
options.fillStyle = this.processColor( dim = T.dimensions(
type.fillColor || series.color, options.subtitle,
{opacity: type.fillOpacity} {size: options.fontSize},
); 'font-size:smaller;',
  'flotr-subtitle'
return options; );
}, this.subtitleHeight = dim.height;
/**  
* Calculates the coordinates from a mouse event object. for (j = 0; j < options.length; ++j) {
* @param {Event} event - Mouse Event object. if (series[j].points.show) {
* @return {Object} Object with coordinates of the mouse. maxOutset = Math.max(maxOutset, series[j].points.radius + series[j].points.lineWidth / 2);
*/ }
getEventPosition: function (e){ }
   
var var p = this.plotOffset;
d = document, if (x.options.margin === false) {
b = d.body, p.bottom = 0;
de = d.documentElement, p.top = 0;
axes = this.axes, } else {
plotOffset = this.plotOffset, p.bottom += (options.grid.circular ? 0 : (x.used && x.options.showLabels ? (x.maxLabel.height + margin) : 0)) +
lastMousePos = this.lastMousePos, (x.used && x.options.title ? (x.titleSize.height + margin) : 0) + maxOutset;
pointer = E.eventPointer(e),  
dx = pointer.x - lastMousePos.pageX, p.top += (options.grid.circular ? 0 : (x2.used && x2.options.showLabels ? (x2.maxLabel.height + margin) : 0)) +
dy = pointer.y - lastMousePos.pageY, (x2.used && x2.options.title ? (x2.titleSize.height + margin) : 0) + this.subtitleHeight + this.titleHeight + maxOutset;
r, rx, ry; }
  if (y.options.margin === false) {
if ('ontouchstart' in this.el) { p.left = 0;
r = D.position(this.overlay); p.right = 0;
rx = pointer.x - r.left - plotOffset.left; } else {
ry = pointer.y - r.top - plotOffset.top; p.left += (options.grid.circular ? 0 : (y.used && y.options.showLabels ? (y.maxLabel.width + margin) : 0)) +
} else { (y.used && y.options.title ? (y.titleSize.width + margin) : 0) + maxOutset;
r = this.overlay.getBoundingClientRect();  
rx = e.clientX - r.left - plotOffset.left - b.scrollLeft - de.scrollLeft; p.right += (options.grid.circular ? 0 : (y2.used && y2.options.showLabels ? (y2.maxLabel.width + margin) : 0)) +
ry = e.clientY - r.top - plotOffset.top - b.scrollTop - de.scrollTop; (y2.used && y2.options.title ? (y2.titleSize.width + margin) : 0) + maxOutset;
} }
   
return { p.top = Math.floor(p.top); // In order the outline not to be blured
x: axes.x.p2d(rx),  
x2: axes.x2.p2d(rx), this.plotWidth = this.canvasWidth - p.left - p.right;
y: axes.y.p2d(ry), this.plotHeight = this.canvasHeight - p.bottom - p.top;
y2: axes.y2.p2d(ry),  
relX: rx, // TODO post refactor, fix this
relY: ry, x.length = x2.length = this.plotWidth;
dX: dx, y.length = y2.length = this.plotHeight;
dY: dy, y.offset = y2.offset = this.plotHeight;
absX: pointer.x, x.setScale();
absY: pointer.y, x2.setScale();
pageX: pointer.x, y.setScale();
pageY: pointer.y y2.setScale();
}; },
}, /**
/** * Draws grid, labels, series and outline.
* Observes the 'click' event and fires the 'flotr:click' event. */
* @param {Event} event - 'click' Event object. draw: function (after) {
*/  
clickHandler: function(event){ var
if(this.ignoreClick){ context = this.ctx,
this.ignoreClick = false; i;
return this.ignoreClick;  
} E.fire(this.el, 'flotr:beforedraw', [this.series, this]);
E.fire(this.el, 'flotr:click', [this.getEventPosition(event), this]);  
}, if (this.series.length) {
/**  
* Observes mouse movement over the graph area. Fires the 'flotr:mousemove' event. context.save();
* @param {Event} event - 'mousemove' Event object. context.translate(this.plotOffset.left, this.plotOffset.top);
*/  
mouseMoveHandler: function(event){ for (i = 0; i < this.series.length; i++) {
if (this.mouseDownMoveHandler) return; if (!this.series[i].hide) this.drawSeries(this.series[i]);
var pos = this.getEventPosition(event); }
E.fire(this.el, 'flotr:mousemove', [event, pos, this]);  
this.lastMousePos = pos; context.restore();
}, this.clip();
/** }
* Observes the 'mousedown' event.  
* @param {Event} event - 'mousedown' Event object. E.fire(this.el, 'flotr:afterdraw', [this.series, this]);
*/ if (after) after();
mouseDownHandler: function (event){ },
  /**
/* * Actually draws the graph.
// @TODO Context menu? * @param {Object} series - series to draw
if(event.isRightClick()) { */
event.stop(); drawSeries: function (series) {
   
var overlay = this.overlay; function drawChart(series, typeKey) {
overlay.hide(); var options = this.getOptions(series, typeKey);
  this[typeKey].draw(options);
function cancelContextMenu () { }
overlay.show();  
E.stopObserving(document, 'mousemove', cancelContextMenu); var drawn = false;
} series = series || this.series;
E.observe(document, 'mousemove', cancelContextMenu);  
return; _.each(flotr.graphTypes, function (type, typeKey) {
} if (series[typeKey] && series[typeKey].show && this[typeKey]) {
*/ drawn = true;
  drawChart.call(this, series, typeKey);
if (this.mouseUpHandler) return; }
this.mouseUpHandler = _.bind(function (e) { }, this);
E.stopObserving(document, 'mouseup', this.mouseUpHandler);  
E.stopObserving(document, 'mousemove', this.mouseDownMoveHandler); if (!drawn) drawChart.call(this, series, this.options.defaultType);
this.mouseDownMoveHandler = null; },
this.mouseUpHandler = null;  
// @TODO why? getOptions: function (series, typeKey) {
//e.stop(); var
E.fire(this.el, 'flotr:mouseup', [e, this]); type = series[typeKey],
}, this); graphType = this[typeKey],
this.mouseDownMoveHandler = _.bind(function (e) { options = {
var pos = this.getEventPosition(e); context: this.ctx,
E.fire(this.el, 'flotr:mousemove', [event, pos, this]); width: this.plotWidth,
this.lastMousePos = pos; height: this.plotHeight,
}, this); fontSize: this.options.fontSize,
E.observe(document, 'mouseup', this.mouseUpHandler); fontColor: this.options.fontColor,
E.observe(document, 'mousemove', this.mouseDownMoveHandler); textEnabled: this.textEnabled,
E.fire(this.el, 'flotr:mousedown', [event, this]); htmlText: this.options.HtmlText,
this.ignoreClick = false; text: this._text, // TODO Is this necessary?
}, element: this.el,
drawTooltip: function(content, x, y, options) { data: series.data,
var mt = this.getMouseTrack(), color: series.color,
style = 'opacity:0.7;background-color:#000;color:#fff;display:none;position:absolute;padding:2px 8px;-moz-border-radius:4px;border-radius:4px;white-space:nowrap;', shadowSize: series.shadowSize,
p = options.position, xScale: _.bind(series.xaxis.d2p, series.xaxis),
m = options.margin, yScale: _.bind(series.yaxis.d2p, series.yaxis)
plotOffset = this.plotOffset; };
   
if(x !== null && y !== null){ options = flotr.merge(type, options);
if (!options.relative) { // absolute to the canvas  
if(p.charAt(0) == 'n') style += 'top:' + (m + plotOffset.top) + 'px;bottom:auto;'; // Fill
else if(p.charAt(0) == 's') style += 'bottom:' + (m + plotOffset.bottom) + 'px;top:auto;'; options.fillStyle = this.processColor(
if(p.charAt(1) == 'e') style += 'right:' + (m + plotOffset.right) + 'px;left:auto;'; type.fillColor || series.color,
else if(p.charAt(1) == 'w') style += 'left:' + (m + plotOffset.left) + 'px;right:auto;'; {opacity: type.fillOpacity}
} );
else { // relative to the mouse  
if(p.charAt(0) == 'n') style += 'bottom:' + (m - plotOffset.top - y + this.canvasHeight) + 'px;top:auto;'; return options;
else if(p.charAt(0) == 's') style += 'top:' + (m + plotOffset.top + y) + 'px;bottom:auto;'; },
if(p.charAt(1) == 'e') style += 'left:' + (m + plotOffset.left + x) + 'px;right:auto;'; /**
else if(p.charAt(1) == 'w') style += 'right:' + (m - plotOffset.left - x + this.canvasWidth) + 'px;left:auto;'; * Calculates the coordinates from a mouse event object.
} * @param {Event} event - Mouse Event object.
  * @return {Object} Object with coordinates of the mouse.
mt.style.cssText = style; */
D.empty(mt); getEventPosition: function (e) {
D.insert(mt, content);  
D.show(mt); var
} d = document,
else { b = d.body,
D.hide(mt); de = d.documentElement,
} axes = this.axes,
}, plotOffset = this.plotOffset,
  lastMousePos = this.lastMousePos,
clip: function (ctx) { pointer = E.eventPointer(e),
  dx = pointer.x - lastMousePos.pageX,
var dy = pointer.y - lastMousePos.pageY,
o = this.plotOffset, r, rx, ry;
w = this.canvasWidth,  
h = this.canvasHeight; if ('ontouchstart' in this.el) {
  r = D.position(this.overlay);
ctx = ctx || this.ctx; rx = pointer.x - r.left - plotOffset.left;
  ry = pointer.y - r.top - plotOffset.top;
if (flotr.isIE && flotr.isIE < 9) { } else {
// Clipping for excanvas :-( r = this.overlay.getBoundingClientRect();
ctx.save(); rx = e.clientX - r.left - plotOffset.left - b.scrollLeft - de.scrollLeft;
ctx.fillStyle = this.processColor(this.options.ieBackgroundColor); ry = e.clientY - r.top - plotOffset.top - b.scrollTop - de.scrollTop;
ctx.fillRect(0, 0, w, o.top); }
ctx.fillRect(0, 0, o.left, h);  
ctx.fillRect(0, h - o.bottom, w, o.bottom); return {
ctx.fillRect(w - o.right, 0, o.right,h); x: axes.x.p2d(rx),
ctx.restore(); x2: axes.x2.p2d(rx),
} else { y: axes.y.p2d(ry),
ctx.clearRect(0, 0, w, o.top); y2: axes.y2.p2d(ry),
ctx.clearRect(0, 0, o.left, h); relX: rx,
ctx.clearRect(0, h - o.bottom, w, o.bottom); relY: ry,
ctx.clearRect(w - o.right, 0, o.right,h); dX: dx,
} dY: dy,
}, absX: pointer.x,
  absY: pointer.y,
_initMembers: function() { pageX: pointer.x,
this._handles = []; pageY: pointer.y
this.lastMousePos = {pageX: null, pageY: null }; };
this.plotOffset = {left: 0, right: 0, top: 0, bottom: 0}; },
this.ignoreClick = true; /**
this.prevHit = null; * Observes the 'click' event and fires the 'flotr:click' event.
}, * @param {Event} event - 'click' Event object.
  */
_initGraphTypes: function() { clickHandler: function (event) {
_.each(flotr.graphTypes, function(handler, graphType){ if (this.ignoreClick) {
this[graphType] = flotr.clone(handler); this.ignoreClick = false;
}, this); return this.ignoreClick;
}, }
  E.fire(this.el, 'flotr:click', [this.getEventPosition(event), this]);
_initEvents: function () { },
  /**
var * Observes mouse movement over the graph area. Fires the 'flotr:mousemove' event.
el = this.el, * @param {Event} event - 'mousemove' Event object.
touchendHandler, movement, touchend; */
  mouseMoveHandler: function (event) {
if ('ontouchstart' in el) { if (this.mouseDownMoveHandler) return;
  var pos = this.getEventPosition(event);
touchendHandler = _.bind(function (e) { E.fire(this.el, 'flotr:mousemove', [event, pos, this]);
touchend = true; this.lastMousePos = pos;
E.stopObserving(document, 'touchend', touchendHandler); },
E.fire(el, 'flotr:mouseup', [event, this]); /**
this.multitouches = null; * Observes the 'mousedown' event.
  * @param {Event} event - 'mousedown' Event object.
if (!movement) { */
this.clickHandler(e); mouseDownHandler: function (event) {
}  
}, this); /*
  // @TODO Context menu?
this.observe(this.overlay, 'touchstart', _.bind(function (e) { if(event.isRightClick()) {
movement = false; event.stop();
touchend = false;  
this.ignoreClick = false; var overlay = this.overlay;
  overlay.hide();
if (e.touches && e.touches.length > 1) {  
this.multitouches = e.touches; function cancelContextMenu () {
} overlay.show();
  E.stopObserving(document, 'mousemove', cancelContextMenu);
E.fire(el, 'flotr:mousedown', [event, this]); }
this.observe(document, 'touchend', touchendHandler); E.observe(document, 'mousemove', cancelContextMenu);
}, this)); return;
  }
this.observe(this.overlay, 'touchmove', _.bind(function (e) { */
   
var pos = this.getEventPosition(e); if (this.mouseUpHandler) return;
  this.mouseUpHandler = _.bind(function (e) {
e.preventDefault(); E.stopObserving(document, 'mouseup', this.mouseUpHandler);
  E.stopObserving(document, 'mousemove', this.mouseDownMoveHandler);
movement = true; this.mouseDownMoveHandler = null;
  this.mouseUpHandler = null;
if (this.multitouches || (e.touches && e.touches.length > 1)) { // @TODO why?
this.multitouches = e.touches; //e.stop();
} else { E.fire(this.el, 'flotr:mouseup', [e, this]);
if (!touchend) { }, this);
E.fire(el, 'flotr:mousemove', [event, pos, this]); this.mouseDownMoveHandler = _.bind(function (e) {
} var pos = this.getEventPosition(e);
} E.fire(this.el, 'flotr:mousemove', [event, pos, this]);
this.lastMousePos = pos; this.lastMousePos = pos;
}, this)); }, this);
  E.observe(document, 'mouseup', this.mouseUpHandler);
} else { E.observe(document, 'mousemove', this.mouseDownMoveHandler);
this. E.fire(this.el, 'flotr:mousedown', [event, this]);
observe(this.overlay, 'mousedown', _.bind(this.mouseDownHandler, this)). this.ignoreClick = false;
observe(el, 'mousemove', _.bind(this.mouseMoveHandler, this)). },
observe(this.overlay, 'click', _.bind(this.clickHandler, this)). drawTooltip: function (content, x, y, options) {
observe(el, 'mouseout', function () { var mt = this.getMouseTrack(),
E.fire(el, 'flotr:mouseout'); style = 'opacity:0.7;background-color:#000;color:#fff;display:none;position:absolute;padding:2px 8px;-moz-border-radius:4px;border-radius:4px;white-space:nowrap;',
}); p = options.position,
} m = options.margin,
}, plotOffset = this.plotOffset;
   
/** if (x !== null && y !== null) {
* Initializes the canvas and it's overlay canvas element. When the browser is IE, this makes use if (!options.relative) { // absolute to the canvas
* of excanvas. The overlay canvas is inserted for displaying interactions. After the canvas elements if (p.charAt(0) == 'n') style += 'top:' + (m + plotOffset.top) + 'px;bottom:auto;';
* are created, the elements are inserted into the container element. else if (p.charAt(0) == 's') style += 'bottom:' + (m + plotOffset.bottom) + 'px;top:auto;';
*/ if (p.charAt(1) == 'e') style += 'right:' + (m + plotOffset.right) + 'px;left:auto;';
_initCanvas: function(){ else if (p.charAt(1) == 'w') style += 'left:' + (m + plotOffset.left) + 'px;right:auto;';
var el = this.el, }
o = this.options, else { // relative to the mouse
children = el.children, if (p.charAt(0) == 'n') style += 'bottom:' + (m - plotOffset.top - y + this.canvasHeight) + 'px;top:auto;';
removedChildren = [], else if (p.charAt(0) == 's') style += 'top:' + (m + plotOffset.top + y) + 'px;bottom:auto;';
child, i, if (p.charAt(1) == 'e') style += 'left:' + (m + plotOffset.left + x) + 'px;right:auto;';
size, style; else if (p.charAt(1) == 'w') style += 'right:' + (m - plotOffset.left - x + this.canvasWidth) + 'px;left:auto;';
  }
// Empty the el  
for (i = children.length; i--;) { mt.style.cssText = style;
child = children[i]; D.empty(mt);
if (!this.canvas && child.className === 'flotr-canvas') { D.insert(mt, content);
this.canvas = child; D.show(mt);
} else if (!this.overlay && child.className === 'flotr-overlay') { }
this.overlay = child; else {
} else { D.hide(mt);
removedChildren.push(child); }
} },
}  
for (i = removedChildren.length; i--;) { clip: function (ctx) {
el.removeChild(removedChildren[i]);  
} var
  o = this.plotOffset,
D.setStyles(el, {position: 'relative'}); // For positioning labels and overlay. w = this.canvasWidth,
size = {}; h = this.canvasHeight;
size.width = el.clientWidth;  
size.height = el.clientHeight; ctx = ctx || this.ctx;
   
if(size.width <= 0 || size.height <= 0 || o.resolution <= 0){ if (flotr.isIE && flotr.isIE < 9) {
throw 'Invalid dimensions for plot, width = ' + size.width + ', height = ' + size.height + ', resolution = ' + o.resolution; // Clipping for excanvas :-(
} ctx.save();
  ctx.fillStyle = this.processColor(this.options.ieBackgroundColor);
// Main canvas for drawing graph types ctx.fillRect(0, 0, w, o.top);
this.canvas = getCanvas(this.canvas, 'canvas'); ctx.fillRect(0, 0, o.left, h);
// Overlay canvas for interactive features ctx.fillRect(0, h - o.bottom, w, o.bottom);
this.overlay = getCanvas(this.overlay, 'overlay'); ctx.fillRect(w - o.right, 0, o.right, h);
this.ctx = getContext(this.canvas); ctx.restore();
this.ctx.clearRect(0, 0, this.canvas.width, this.canvas.height); } else {
this.octx = getContext(this.overlay); ctx.clearRect(0, 0, w, o.top);
this.octx.clearRect(0, 0, this.overlay.width, this.overlay.height); ctx.clearRect(0, 0, o.left, h);
this.canvasHeight = size.height; ctx.clearRect(0, h - o.bottom, w, o.bottom);
this.canvasWidth = size.width; ctx.clearRect(w - o.right, 0, o.right, h);
this.textEnabled = !!this.ctx.drawText || !!this.ctx.fillText; // Enable text functions }
  },
function getCanvas(canvas, name){  
if(!canvas){ _initMembers: function () {
canvas = D.create('canvas'); this._handles = [];
if (typeof FlashCanvas != "undefined" && typeof canvas.getContext === 'function') { this.lastMousePos = {pageX: null, pageY: null };
FlashCanvas.initElement(canvas); this.plotOffset = {left: 0, right: 0, top: 0, bottom: 0};
} this.ignoreClick = true;
canvas.className = 'flotr-'+name; this.prevHit = null;
canvas.style.cssText = 'position:absolute;left:0px;top:0px;'; },
D.insert(el, canvas);  
} _initGraphTypes: function () {
_.each(size, function(size, attribute){ _.each(flotr.graphTypes, function (handler, graphType) {
D.show(canvas); this[graphType] = flotr.clone(handler);
if (name == 'canvas' && canvas.getAttribute(attribute) === size) { }, this);
return; },
}  
canvas.setAttribute(attribute, size * o.resolution); _initEvents: function () {
canvas.style[attribute] = size + 'px';  
}); var
canvas.context_ = null; // Reset the ExCanvas context el = this.el,
return canvas; touchendHandler, movement, touchend;
}  
  if ('ontouchstart' in el) {
function getContext(canvas){  
if(window.G_vmlCanvasManager) window.G_vmlCanvasManager.initElement(canvas); // For ExCanvas touchendHandler = _.bind(function (e) {
var context = canvas.getContext('2d'); touchend = true;
if(!window.G_vmlCanvasManager) context.scale(o.resolution, o.resolution); E.stopObserving(document, 'touchend', touchendHandler);
return context; E.fire(el, 'flotr:mouseup', [event, this]);
} this.multitouches = null;
},  
  if (!movement) {
_initPlugins: function(){ this.clickHandler(e);
// TODO Should be moved to flotr and mixed in. }
_.each(flotr.plugins, function(plugin, name){ }, this);
_.each(plugin.callbacks, function(fn, c){  
this.observe(this.el, c, _.bind(fn, this)); this.observe(this.overlay, 'touchstart', _.bind(function (e) {
}, this); movement = false;
this[name] = flotr.clone(plugin); touchend = false;
_.each(this[name], function(fn, p){ this.ignoreClick = false;
if (_.isFunction(fn))  
this[name][p] = _.bind(fn, this); if (e.touches && e.touches.length > 1) {
}, this); this.multitouches = e.touches;
}, this); }
},  
  E.fire(el, 'flotr:mousedown', [event, this]);
/** this.observe(document, 'touchend', touchendHandler);
* Sets options and initializes some variables and color specific values, used by the constructor. }, this));
* @param {Object} opts - options object  
*/ this.observe(this.overlay, 'touchmove', _.bind(function (e) {
_initOptions: function(opts){  
var options = flotr.clone(flotr.defaultOptions); var pos = this.getEventPosition(e);
options.x2axis = _.extend(_.clone(options.xaxis), options.x2axis);  
options.y2axis = _.extend(_.clone(options.yaxis), options.y2axis); e.preventDefault();
this.options = flotr.merge(opts || {}, options);  
  movement = true;
if (this.options.grid.minorVerticalLines === null &&  
this.options.xaxis.scaling === 'logarithmic') { if (this.multitouches || (e.touches && e.touches.length > 1)) {
this.options.grid.minorVerticalLines = true; this.multitouches = e.touches;
} } else {
if (this.options.grid.minorHorizontalLines === null && if (!touchend) {
this.options.yaxis.scaling === 'logarithmic') { E.fire(el, 'flotr:mousemove', [event, pos, this]);
this.options.grid.minorHorizontalLines = true; }
} }
  this.lastMousePos = pos;
E.fire(this.el, 'flotr:afterinitoptions', [this]); }, this));
   
this.axes = flotr.Axis.getAxes(this.options); } else {
  this.
// Initialize some variables used throughout this function. observe(this.overlay, 'mousedown', _.bind(this.mouseDownHandler, this)).
var assignedColors = [], observe(el, 'mousemove', _.bind(this.mouseMoveHandler, this)).
colors = [], observe(this.overlay, 'click', _.bind(this.clickHandler, this)).
ln = this.series.length, observe(el, 'mouseout', function () {
neededColors = this.series.length, E.fire(el, 'flotr:mouseout');
oc = this.options.colors, });
usedColors = [], }
variation = 0, },
c, i, j, s;  
  /**
// Collect user-defined colors from series. * Initializes the canvas and it's overlay canvas element. When the browser is IE, this makes use
for(i = neededColors - 1; i > -1; --i){ * of excanvas. The overlay canvas is inserted for displaying interactions. After the canvas elements
c = this.series[i].color; * are created, the elements are inserted into the container element.
if(c){ */
--neededColors; _initCanvas: function () {
if(_.isNumber(c)) assignedColors.push(c); var el = this.el,
else usedColors.push(flotr.Color.parse(c)); o = this.options,
} children = el.children,
} removedChildren = [],
  child, i,
// Calculate the number of colors that need to be generated. size, style;
for(i = assignedColors.length - 1; i > -1; --i)  
neededColors = Math.max(neededColors, assignedColors[i] + 1); // Empty the el
  for (i = children.length; i--;) {
// Generate needed number of colors. child = children[i];
for(i = 0; colors.length < neededColors;){ if (!this.canvas && child.className === 'flotr-canvas') {
c = (oc.length == i) ? new flotr.Color(100, 100, 100) : flotr.Color.parse(oc[i]); this.canvas = child;
  } else if (!this.overlay && child.className === 'flotr-overlay') {
// Make sure each serie gets a different color. this.overlay = child;
var sign = variation % 2 == 1 ? -1 : 1, } else {
factor = 1 + sign * Math.ceil(variation / 2) * 0.2; removedChildren.push(child);
c.scale(factor, factor, factor); }
  }
/** for (i = removedChildren.length; i--;) {
* @todo if we're getting too close to something else, we should probably skip this one el.removeChild(removedChildren[i]);
*/ }
colors.push(c);  
  D.setStyles(el, {position: 'relative'}); // For positioning labels and overlay.
if(++i >= oc.length){ size = {};
i = 0; size.width = el.clientWidth;
++variation; size.height = el.clientHeight;
}  
} if (size.width <= 0 || size.height <= 0 || o.resolution <= 0) {
  throw 'Invalid dimensions for plot, width = ' + size.width + ', height = ' + size.height + ', resolution = ' + o.resolution;
// Fill the options with the generated colors. }
for(i = 0, j = 0; i < ln; ++i){  
s = this.series[i]; // Main canvas for drawing graph types
  this.canvas = getCanvas(this.canvas, 'canvas');
// Assign the color. // Overlay canvas for interactive features
if (!s.color){ this.overlay = getCanvas(this.overlay, 'overlay');
s.color = colors[j++].toString(); this.ctx = getContext(this.canvas);
}else if(_.isNumber(s.color)){ this.ctx.clearRect(0, 0, this.canvas.width, this.canvas.height);
s.color = colors[s.color].toString(); this.octx = getContext(this.overlay);
} this.octx.clearRect(0, 0, this.overlay.width, this.overlay.height);
  this.canvasHeight = size.height;
// Every series needs an axis this.canvasWidth = size.width;
if (!s.xaxis) s.xaxis = this.axes.x; this.textEnabled = !!this.ctx.drawText || !!this.ctx.fillText; // Enable text functions
if (s.xaxis == 1) s.xaxis = this.axes.x;  
else if (s.xaxis == 2) s.xaxis = this.axes.x2; function getCanvas(canvas, name) {
  if (!canvas) {
if (!s.yaxis) s.yaxis = this.axes.y; canvas = D.create('canvas');
if (s.yaxis == 1) s.yaxis = this.axes.y; if (typeof FlashCanvas != "undefined" && typeof canvas.getContext === 'function') {
else if (s.yaxis == 2) s.yaxis = this.axes.y2; FlashCanvas.initElement(canvas);
  }
// Apply missing options to the series. canvas.className = 'flotr-' + name;
for (var t in flotr.graphTypes){ canvas.style.cssText = 'position:absolute;left:0px;top:0px;';
s[t] = _.extend(_.clone(this.options[t]), s[t]); D.insert(el, canvas);
} }
s.mouse = _.extend(_.clone(this.options.mouse), s.mouse); _.each(size, function (size, attribute) {
  D.show(canvas);
if (_.isUndefined(s.shadowSize)) s.shadowSize = this.options.shadowSize; if (name == 'canvas' && canvas.getAttribute(attribute) === size) {
} return;
}, }
  canvas.setAttribute(attribute, size * o.resolution);
_setEl: function(el) { canvas.style[attribute] = size + 'px';
if (!el) throw 'The target container doesn\'t exist'; });
else if (el.graph instanceof Graph) el.graph.destroy(); canvas.context_ = null; // Reset the ExCanvas context
else if (!el.clientWidth) throw 'The target container must be visible'; return canvas;
  }
el.graph = this;  
this.el = el; function getContext(canvas) {
} if (window.G_vmlCanvasManager) window.G_vmlCanvasManager.initElement(canvas); // For ExCanvas
}; var context = canvas.getContext('2d');
  if (!window.G_vmlCanvasManager) context.scale(o.resolution, o.resolution);
Flotr.Graph = Graph; return context;
  }
  },
   
  _initPlugins: function () {
  // TODO Should be moved to flotr and mixed in.
  _.each(flotr.plugins, function (plugin, name) {
  _.each(plugin.callbacks, function (fn, c) {
  this.observe(this.el, c, _.bind(fn, this));
  }, this);
  this[name] = flotr.clone(plugin);
  _.each(this[name], function (fn, p) {
  if (_.isFunction(fn))
  this[name][p] = _.bind(fn, this);
  }, this);
  }, this);
  },
   
  /**
  * Sets options and initializes some variables and color specific values, used by the constructor.
  * @param {Object} opts - options object
  */
  _initOptions: function (opts) {
  var options = flotr.clone(flotr.defaultOptions);
  options.x2axis = _.extend(_.clone(options.xaxis), options.x2axis);
  options.y2axis = _.extend(_.clone(options.yaxis), options.y2axis);
  this.options = flotr.merge(opts || {}, options);
   
  if (this.options.grid.minorVerticalLines === null &&
  this.options.xaxis.scaling === 'logarithmic') {
  this.options.grid.minorVerticalLines = true;
  }
  if (this.options.grid.minorHorizontalLines === null &&
  this.options.yaxis.scaling === 'logarithmic') {
  this.options.grid.minorHorizontalLines = true;
  }
   
  E.fire(this.el, 'flotr:afterinitoptions', [this]);
   
  this.axes = flotr.Axis.getAxes(this.options);
   
  // Initialize some variables used throughout this function.
  var assignedColors = [],
  colors = [],
  ln = this.series.length,
  neededColors = this.series.length,
  oc = this.options.colors,
  usedColors = [],
  variation = 0,
  c, i, j, s;
   
  // Collect user-defined colors from series.
  for (i = neededColors - 1; i > -1; --i) {
  c = this.series[i].color;
  if (c) {
  --neededColors;
  if (_.isNumber(c)) assignedColors.push(c);
  else usedColors.push(flotr.Color.parse(c));
  }
  }
   
  // Calculate the number of colors that need to be generated.
  for (i = assignedColors.length - 1; i > -1; --i)
  neededColors = Math.max(neededColors, assignedColors[i] + 1);
   
  // Generate needed number of colors.
  for (i = 0; colors.length < neededColors;) {
  c = (oc.length == i) ? new flotr.Color(100, 100, 100) : flotr.Color.parse(oc[i]);
   
  // Make sure each serie gets a different color.
  var sign = variation % 2 == 1 ? -1 : 1,
  factor = 1 + sign * Math.ceil(variation / 2) * 0.2;
  c.scale(factor, factor, factor);
   
  /**
  * @todo if we're getting too close to something else, we should probably skip this one
  */
  colors.push(c);
   
  if (++i >= oc.length) {
  i = 0;
  ++variation;
  }
  }
   
  // Fill the options with the generated colors.
  for (i = 0, j = 0; i < ln; ++i) {
  s = this.series[i];
   
  // Assign the color.
  if (!s.color) {
  s.color = colors[j++].toString();
  } else if (_.isNumber(s.color)) {
  s.color = colors[s.color].toString();
  }
   
  // Every series needs an axis
  if (!s.xaxis) s.xaxis = this.axes.x;
  if (s.xaxis == 1) s.xaxis = this.axes.x;
  else if (s.xaxis == 2) s.xaxis = this.axes.x2;
   
  if (!s.yaxis) s.yaxis = this.axes.y;
  if (s.yaxis == 1) s.yaxis = this.axes.y;
  else if (s.yaxis == 2) s.yaxis = this.axes.y2;
   
  // Apply missing options to the series.
  for (var t in flotr.graphTypes) {
  s[t] = _.extend(_.clone(this.options[t]), s[t]);
  }
  s.mouse = _.extend(_.clone(this.options.mouse), s.mouse);
   
  if (_.isUndefined(s.shadowSize)) s.shadowSize = this.options.shadowSize;
  }
  },
   
  _setEl: function (el) {
  if (!el) throw 'The target container doesn\'t exist';
  else if (el.graph instanceof Graph) el.graph.destroy();
  else if (!el.clientWidth) throw 'The target container must be visible';
   
  el.graph = this;
  this.el = el;
  }
  };
   
  Flotr.Graph = Graph;
   
})(); })();
   
/** /**
* Flotr Axis Library * Flotr Axis Library
*/ */
   
(function () { (function () {
   
var var
_ = Flotr._, _ = Flotr._,
LOGARITHMIC = 'logarithmic'; LOGARITHMIC = 'logarithmic';
   
function Axis (o) { function Axis(o) {
   
this.orientation = 1; this.orientation = 1;
this.offset = 0; this.offset = 0;
this.datamin = Number.MAX_VALUE; this.datamin = Number.MAX_VALUE;
this.datamax = -Number.MAX_VALUE; this.datamax = -Number.MAX_VALUE;
   
_.extend(this, o); _.extend(this, o);
   
this._setTranslations(); this._setTranslations();
} }
   
   
// Prototype // Prototype
Axis.prototype = { Axis.prototype = {
   
setScale : function () { setScale: function () {
var length = this.length; var length = this.length;
if (this.options.scaling == LOGARITHMIC) { if (this.options.scaling == LOGARITHMIC) {
this.scale = length / (log(this.max, this.options.base) - log(this.min, this.options.base)); this.scale = length / (log(this.max, this.options.base) - log(this.min, this.options.base));
} else { } else {
this.scale = length / (this.max - this.min); this.scale = length / (this.max - this.min);
  }
  },
   
  calculateTicks: function () {
  var options = this.options;
   
  this.ticks = [];
  this.minorTicks = [];
   
  // User Ticks
  if (options.ticks) {
  this._cleanUserTicks(options.ticks, this.ticks);
  this._cleanUserTicks(options.minorTicks || [], this.minorTicks);
  }
  else {
  if (options.mode == 'time') {
  this._calculateTimeTicks();
  } else if (options.scaling === 'logarithmic') {
  this._calculateLogTicks();
  } else {
  this._calculateTicks();
  }
  }
   
  // Ticks to strings
  _.each(this.ticks, function (tick) {
  tick.label += '';
  });
  _.each(this.minorTicks, function (tick) {
  tick.label += '';
  });
  },
   
  /**
  * Calculates the range of an axis to apply autoscaling.
  */
  calculateRange: function () {
   
  if (!this.used) return;
   
  var axis = this,
  o = axis.options,
  min = o.min !== null ? o.min : axis.datamin,
  max = o.max !== null ? o.max : axis.datamax,
  margin = o.autoscaleMargin;
   
  if (o.scaling == 'logarithmic') {
  if (min <= 0) min = axis.datamin;
   
  // Let it widen later on
  if (max <= 0) max = min;
  }
   
  if (max == min) {
  var widen = max ? 0.01 : 1.00;
  if (o.min === null) min -= widen;
  if (o.max === null) max += widen;
  }
   
  if (o.scaling === 'logarithmic') {
  if (min < 0) min = max / o.base; // Could be the result of widening
   
  var maxexp = Math.log(max);
  if (o.base != Math.E) maxexp /= Math.log(o.base);
  maxexp = Math.ceil(maxexp);
   
  var minexp = Math.log(min);
  if (o.base != Math.E) minexp /= Math.log(o.base);
  minexp = Math.ceil(minexp);
   
  axis.tickSize = Flotr.getTickSize(o.noTicks, minexp, maxexp, o.tickDecimals === null ? 0 : o.tickDecimals);
   
  // Try to determine a suitable amount of miniticks based on the length of a decade
  if (o.minorTickFreq === null) {
  if (maxexp - minexp > 10)
  o.minorTickFreq = 0;
  else if (maxexp - minexp > 5)
  o.minorTickFreq = 2;
  else
  o.minorTickFreq = 5;
  }
  } else {
  axis.tickSize = Flotr.getTickSize(o.noTicks, min, max, o.tickDecimals);
  }
   
  axis.min = min;
  axis.max = max; //extendRange may use axis.min or axis.max, so it should be set before it is caled
   
  // Autoscaling. @todo This probably fails with log scale. Find a testcase and fix it
  if (o.min === null && o.autoscale) {
  axis.min -= axis.tickSize * margin;
  // Make sure we don't go below zero if all values are positive.
  if (axis.min < 0 && axis.datamin >= 0) axis.min = 0;
  axis.min = axis.tickSize * Math.floor(axis.min / axis.tickSize);
  }
   
  if (o.max === null && o.autoscale) {
  axis.max += axis.tickSize * margin;
  if (axis.max > 0 && axis.datamax <= 0 && axis.datamax != axis.datamin) axis.max = 0;
  axis.max = axis.tickSize * Math.ceil(axis.max / axis.tickSize);
  }
   
  if (axis.min == axis.max) axis.max = axis.min + 1;
  },
   
  calculateTextDimensions: function (T, options) {
   
  var maxLabel = '',
  length,
  i;
   
  if (this.options.showLabels) {
  for (i = 0; i < this.ticks.length; ++i) {
  length = this.ticks[i].label.length;
  if (length > maxLabel.length) {
  maxLabel = this.ticks[i].label;
  }
  }
  }
   
  this.maxLabel = T.dimensions(
  maxLabel,
  {size: options.fontSize, angle: Flotr.toRad(this.options.labelsAngle)},
  'font-size:smaller;',
  'flotr-grid-label'
  );
   
  this.titleSize = T.dimensions(
  this.options.title,
  {size: options.fontSize * 1.2, angle: Flotr.toRad(this.options.titleAngle)},
  'font-weight:bold;',
  'flotr-axis-title'
  );
  },
   
  _cleanUserTicks: function (ticks, axisTicks) {
   
  var axis = this, options = this.options,
  v, i, label, tick;
   
  if (_.isFunction(ticks)) ticks = ticks({min: axis.min, max: axis.max});
   
  for (i = 0; i < ticks.length; ++i) {
  tick = ticks[i];
  if (typeof(tick) === 'object') {
  v = tick[0];
  label = (tick.length > 1) ? tick[1] : options.tickFormatter(v, {min: axis.min, max: axis.max});
  } else {
  v = tick;
  label = options.tickFormatter(v, {min: this.min, max: this.max});
  }
  axisTicks[i] = { v: v, label: label };
  }
  },
   
  _calculateTimeTicks: function () {
  this.ticks = Flotr.Date.generator(this);
  },
   
  _calculateLogTicks: function () {
   
  var axis = this,
  o = axis.options,
  v,
  decadeStart;
   
  var max = Math.log(axis.max);
  if (o.base != Math.E) max /= Math.log(o.base);
  max = Math.ceil(max);
   
  var min = Math.log(axis.min);
  if (o.base != Math.E) min /= Math.log(o.base);
  min = Math.ceil(min);
   
  for (i = min; i < max; i += axis.tickSize) {
  decadeStart = (o.base == Math.E) ? Math.exp(i) : Math.pow(o.base, i);
  // Next decade begins here:
  var decadeEnd = decadeStart * ((o.base == Math.E) ? Math.exp(axis.tickSize) : Math.pow(o.base, axis.tickSize));
  var stepSize = (decadeEnd - decadeStart) / o.minorTickFreq;
   
  axis.ticks.push({v: decadeStart, label: o.tickFormatter(decadeStart, {min: axis.min, max: axis.max})});
  for (v = decadeStart + stepSize; v < decadeEnd; v += stepSize)
  axis.minorTicks.push({v: v, label: o.tickFormatter(v, {min: axis.min, max: axis.max})});
  }
   
  // Always show the value at the would-be start of next decade (end of this decade)
  decadeStart = (o.base == Math.E) ? Math.exp(i) : Math.pow(o.base, i);
  axis.ticks.push({v: decadeStart, label: o.tickFormatter(decadeStart, {min: axis.min, max: axis.max})});
  },
   
  _calculateTicks: function () {
   
  var axis = this,
  o = axis.options,
  tickSize = axis.tickSize,
  min = axis.min,
  max = axis.max,
  start = tickSize * Math.ceil(min / tickSize), // Round to nearest multiple of tick size.
  decimals,
  minorTickSize,
  v, v2,
  i, j;
   
  if (o.minorTickFreq)
  minorTickSize = tickSize / o.minorTickFreq;
   
  // Then store all possible ticks.
  for (i = 0; (v = v2 = start + i * tickSize) <= max; ++i) {
   
  // Round (this is always needed to fix numerical instability).
  decimals = o.tickDecimals;
  if (decimals === null) decimals = 1 - Math.floor(Math.log(tickSize) / Math.LN10);
  if (decimals < 0) decimals = 0;
   
  v = v.toFixed(decimals);
  axis.ticks.push({ v: v, label: o.tickFormatter(v, {min: axis.min, max: axis.max}) });
   
  if (o.minorTickFreq) {
  for (j = 0; j < o.minorTickFreq && (i * tickSize + j * minorTickSize) < max; ++j) {
  v = v2 + j * minorTickSize;
  axis.minorTicks.push({ v: v, label: o.tickFormatter(v, {min: axis.min, max: axis.max}) });
  }
  }
  }
   
  },
   
  _setTranslations: function (logarithmic) {
  this.d2p = (logarithmic ? d2pLog : d2p);
  this.p2d = (logarithmic ? p2dLog : p2d);
  }
  };
   
   
  // Static Methods
  _.extend(Axis, {
  getAxes: function (options) {
  return {
  x: new Axis({options: options.xaxis, n: 1, length: this.plotWidth}),
  x2: new Axis({options: options.x2axis, n: 2, length: this.plotWidth}),
  y: new Axis({options: options.yaxis, n: 1, length: this.plotHeight, offset: this.plotHeight, orientation: -1}),
  y2: new Axis({options: options.y2axis, n: 2, length: this.plotHeight, offset: this.plotHeight, orientation: -1})
  };
  }
  });
   
   
  // Helper Methods
   
  function d2p(dataValue) {
  return this.offset + this.orientation * (dataValue - this.min) * this.scale;
} }
},  
  function p2d(pointValue) {
calculateTicks : function () { return (this.offset + this.orientation * pointValue) / this.scale + this.min;
var options = this.options;  
   
this.ticks = [];  
this.minorTicks = [];  
   
// User Ticks  
if(options.ticks){  
this._cleanUserTicks(options.ticks, this.ticks);  
this._cleanUserTicks(options.minorTicks || [], this.minorTicks);  
} }
else {  
if (options.mode == 'time') { function d2pLog(dataValue) {
this._calculateTimeTicks(); return this.offset + this.orientation * (log(dataValue, this.options.base) - log(this.min, this.options.base)) * this.scale;
} else if (options.scaling === 'logarithmic') {  
this._calculateLogTicks();  
} else {  
this._calculateTicks();  
}  
} }
   
// Ticks to strings function p2dLog(pointValue) {
_.each(this.ticks, function (tick) { tick.label += ''; }); return exp((this.offset + this.orientation * pointValue) / this.scale + log(this.min, this.options.base), this.options.base);
_.each(this.minorTicks, function (tick) { tick.label += ''; });  
},  
   
/**  
* Calculates the range of an axis to apply autoscaling.  
*/  
calculateRange: function () {  
   
if (!this.used) return;  
   
var axis = this,  
o = axis.options,  
min = o.min !== null ? o.min : axis.datamin,  
max = o.max !== null ? o.max : axis.datamax,  
margin = o.autoscaleMargin;  
   
if (o.scaling == 'logarithmic') {  
if (min <= 0) min = axis.datamin;  
   
// Let it widen later on  
if (max <= 0) max = min;  
} }
   
if (max == min) { function log(value, base) {
var widen = max ? 0.01 : 1.00; value = Math.log(Math.max(value, Number.MIN_VALUE));
if (o.min === null) min -= widen; if (base !== Math.E)
if (o.max === null) max += widen; value /= Math.log(base);
  return value;
} }
   
if (o.scaling === 'logarithmic') { function exp(value, base) {
if (min < 0) min = max / o.base; // Could be the result of widening return (base === Math.E) ? Math.exp(value) : Math.pow(base, value);
   
var maxexp = Math.log(max);  
if (o.base != Math.E) maxexp /= Math.log(o.base);  
maxexp = Math.ceil(maxexp);  
   
var minexp = Math.log(min);  
if (o.base != Math.E) minexp /= Math.log(o.base);  
minexp = Math.ceil(minexp);  
   
axis.tickSize = Flotr.getTickSize(o.noTicks, minexp, maxexp, o.tickDecimals === null ? 0 : o.tickDecimals);  
   
// Try to determine a suitable amount of miniticks based on the length of a decade  
if (o.minorTickFreq === null) {  
if (maxexp - minexp > 10)  
o.minorTickFreq = 0;  
else if (maxexp - minexp > 5)  
o.minorTickFreq = 2;  
else  
o.minorTickFreq = 5;  
}  
} else {  
axis.tickSize = Flotr.getTickSize(o.noTicks, min, max, o.tickDecimals);  
} }
   
axis.min = min; Flotr.Axis = Axis;
axis.max = max; //extendRange may use axis.min or axis.max, so it should be set before it is caled  
   
// Autoscaling. @todo This probably fails with log scale. Find a testcase and fix it  
if(o.min === null && o.autoscale){  
axis.min -= axis.tickSize * margin;  
// Make sure we don't go below zero if all values are positive.  
if(axis.min < 0 && axis.datamin >= 0) axis.min = 0;  
axis.min = axis.tickSize * Math.floor(axis.min / axis.tickSize);  
}  
   
if(o.max === null && o.autoscale){  
axis.max += axis.tickSize * margin;  
if(axis.max > 0 && axis.datamax <= 0 && axis.datamax != axis.datamin) axis.max = 0;  
axis.max = axis.tickSize * Math.ceil(axis.max / axis.tickSize);  
}  
   
if (axis.min == axis.max) axis.max = axis.min + 1;  
},  
   
calculateTextDimensions : function (T, options) {  
   
var maxLabel = '',  
length,  
i;  
   
if (this.options.showLabels) {  
for (i = 0; i < this.ticks.length; ++i) {  
length = this.ticks[i].label.length;  
if (length > maxLabel.length){  
maxLabel = this.ticks[i].label;  
}  
}  
}  
   
this.maxLabel = T.dimensions(  
maxLabel,  
{size:options.fontSize, angle: Flotr.toRad(this.options.labelsAngle)},  
'font-size:smaller;',  
'flotr-grid-label'  
);  
   
this.titleSize = T.dimensions(  
this.options.title,  
{size:options.fontSize*1.2, angle: Flotr.toRad(this.options.titleAngle)},  
'font-weight:bold;',  
'flotr-axis-title'  
);  
},  
   
_cleanUserTicks : function (ticks, axisTicks) {  
   
var axis = this, options = this.options,  
v, i, label, tick;  
   
if(_.isFunction(ticks)) ticks = ticks({min : axis.min, max : axis.max});  
   
for(i = 0; i < ticks.length; ++i){  
tick = ticks[i];  
if(typeof(tick) === 'object'){  
v = tick[0];  
label = (tick.length > 1) ? tick[1] : options.tickFormatter(v, {min : axis.min, max : axis.max});  
} else {  
v = tick;  
label = options.tickFormatter(v, {min : this.min, max : this.max});  
}  
axisTicks[i] = { v: v, label: label };  
}  
},  
   
_calculateTimeTicks : function () {  
this.ticks = Flotr.Date.generator(this);  
},  
   
_calculateLogTicks : function () {  
   
var axis = this,  
o = axis.options,  
v,  
decadeStart;  
   
var max = Math.log(axis.max);  
if (o.base != Math.E) max /= Math.log(o.base);  
max = Math.ceil(max);  
   
var min = Math.log(axis.min);  
if (o.base != Math.E) min /= Math.log(o.base);  
min = Math.ceil(min);  
   
for (i = min; i < max; i += axis.tickSize) {  
decadeStart = (o.base == Math.E) ? Math.exp(i) : Math.pow(o.base, i);  
// Next decade begins here:  
var decadeEnd = decadeStart * ((o.base == Math.E) ? Math.exp(axis.tickSize) : Math.pow(o.base, axis.tickSize));  
var stepSize = (decadeEnd - decadeStart) / o.minorTickFreq;  
   
axis.ticks.push({v: decadeStart, label: o.tickFormatter(decadeStart, {min : axis.min, max : axis.max})});  
for (v = decadeStart + stepSize; v < decadeEnd; v += stepSize)  
axis.minorTicks.push({v: v, label: o.tickFormatter(v, {min : axis.min, max : axis.max})});  
}  
   
// Always show the value at the would-be start of next decade (end of this decade)  
decadeStart = (o.base == Math.E) ? Math.exp(i) : Math.pow(o.base, i);  
axis.ticks.push({v: decadeStart, label: o.tickFormatter(decadeStart, {min : axis.min, max : axis.max})});  
},  
   
_calculateTicks : function () {  
   
var axis = this,  
o = axis.options,  
tickSize = axis.tickSize,  
min = axis.min,  
max = axis.max,  
start = tickSize * Math.ceil(min / tickSize), // Round to nearest multiple of tick size.  
decimals,  
minorTickSize,  
v, v2,  
i, j;  
   
if (o.minorTickFreq)  
minorTickSize = tickSize / o.minorTickFreq;  
   
// Then store all possible ticks.  
for (i = 0; (v = v2 = start + i * tickSize) <= max; ++i){  
   
// Round (this is always needed to fix numerical instability).  
decimals = o.tickDecimals;  
if (decimals === null) decimals = 1 - Math.floor(Math.log(tickSize) / Math.LN10);  
if (decimals < 0) decimals = 0;  
   
v = v.toFixed(decimals);  
axis.ticks.push({ v: v, label: o.tickFormatter(v, {min : axis.min, max : axis.max}) });  
   
if (o.minorTickFreq) {  
for (j = 0; j < o.minorTickFreq && (i * tickSize + j * minorTickSize) < max; ++j) {  
v = v2 + j * minorTickSize;  
axis.minorTicks.push({ v: v, label: o.tickFormatter(v, {min : axis.min, max : axis.max}) });  
}  
}  
}  
   
},  
   
_setTranslations : function (logarithmic) {  
this.d2p = (logarithmic ? d2pLog : d2p);  
this.p2d = (logarithmic ? p2dLog : p2d);  
}  
};  
   
   
// Static Methods  
_.extend(Axis, {  
getAxes : function (options) {  
return {  
x: new Axis({options: options.xaxis, n: 1, length: this.plotWidth}),  
x2: new Axis({options: options.x2axis, n: 2, length: this.plotWidth}),  
y: new Axis({options: options.yaxis, n: 1, length: this.plotHeight, offset: this.plotHeight, orientation: -1}),  
y2: new Axis({options: options.y2axis, n: 2, length: this.plotHeight, offset: this.plotHeight, orientation: -1})  
};  
}  
});  
   
   
// Helper Methods  
   
function d2p (dataValue) {  
return this.offset + this.orientation * (dataValue - this.min) * this.scale;  
}  
   
function p2d (pointValue) {  
return (this.offset + this.orientation * pointValue) / this.scale + this.min;  
}  
   
function d2pLog (dataValue) {  
return this.offset + this.orientation * (log(dataValue, this.options.base) - log(this.min, this.options.base)) * this.scale;  
}  
   
function p2dLog (pointValue) {  
return exp((this.offset + this.orientation * pointValue) / this.scale + log(this.min, this.options.base), this.options.base);  
}  
   
function log (value, base) {  
value = Math.log(Math.max(value, Number.MIN_VALUE));  
if (base !== Math.E)  
value /= Math.log(base);  
return value;  
}  
   
function exp (value, base) {  
return (base === Math.E) ? Math.exp(value) : Math.pow(base, value);  
}  
   
Flotr.Axis = Axis;  
   
})(); })();
   
/** /**
* Flotr Series Library * Flotr Series Library
*/ */
   
(function () { (function () {
   
var  
_ = Flotr._;  
   
function Series (o) {  
_.extend(this, o);  
}  
   
Series.prototype = {  
   
getRange: function () {  
   
var var
data = this.data, _ = Flotr._;
length = data.length,  
xmin = Number.MAX_VALUE, function Series(o) {
ymin = Number.MAX_VALUE, _.extend(this, o);
xmax = -Number.MAX_VALUE,  
ymax = -Number.MAX_VALUE,  
xused = false,  
yused = false,  
x, y, i;  
   
if (length < 0 || this.hide) return false;  
   
for (i = 0; i < length; i++) {  
x = data[i][0];  
y = data[i][1];  
if (x < xmin) { xmin = x; xused = true; }  
if (x > xmax) { xmax = x; xused = true; }  
if (y < ymin) { ymin = y; yused = true; }  
if (y > ymax) { ymax = y; yused = true; }  
} }
   
return { Series.prototype = {
xmin : xmin,  
xmax : xmax, getRange: function () {
ymin : ymin,  
ymax : ymax, var
xused : xused, data = this.data,
yused : yused length = data.length,
}; xmin = Number.MAX_VALUE,
} ymin = Number.MAX_VALUE,
}; xmax = -Number.MAX_VALUE,
  ymax = -Number.MAX_VALUE,
_.extend(Series, { xused = false,
/** yused = false,
* Collects dataseries from input and parses the series into the right format. It returns an Array x, y, i;
* of Objects each having at least the 'data' key set.  
* @param {Array, Object} data - Object or array of dataseries if (length < 0 || this.hide) return false;
* @return {Array} Array of Objects parsed into the right format ({(...,) data: [[x1,y1], [x2,y2], ...] (, ...)})  
*/ for (i = 0; i < length; i++) {
getSeries: function(data){ x = data[i][0];
return _.map(data, function(s){ y = data[i][1];
var series; if (x < xmin) {
if (s.data) { xmin = x;
series = new Series(); xused = true;
_.extend(series, s); }
} else { if (x > xmax) {
series = new Series({data:s}); xmax = x;
} xused = true;
return series; }
  if (y < ymin) {
  ymin = y;
  yused = true;
  }
  if (y > ymax) {
  ymax = y;
  yused = true;
  }
  }
   
  return {
  xmin: xmin,
  xmax: xmax,
  ymin: ymin,
  ymax: ymax,
  xused: xused,
  yused: yused
  };
  }
  };
   
  _.extend(Series, {
  /**
  * Collects dataseries from input and parses the series into the right format. It returns an Array
  * of Objects each having at least the 'data' key set.
  * @param {Array, Object} data - Object or array of dataseries
  * @return {Array} Array of Objects parsed into the right format ({(...,) data: [[x1,y1], [x2,y2], ...] (, ...)})
  */
  getSeries: function (data) {
  return _.map(data, function (s) {
  var series;
  if (s.data) {
  series = new Series();
  _.extend(series, s);
  } else {
  series = new Series({data: s});
  }
  return series;
  });
  }
}); });
}  
}); Flotr.Series = Series;
   
Flotr.Series = Series;  
   
})(); })();
   
/** Lines **/ /** Lines **/
Flotr.addType('lines', { Flotr.addType('lines', {
options: { options: {
show: false, // => setting to true will show lines, false will hide show: false, // => setting to true will show lines, false will hide
lineWidth: 2, // => line width in pixels lineWidth: 2, // => line width in pixels
fill: false, // => true to fill the area from the line to the x axis, false for (transparent) no fill fill: false, // => true to fill the area from the line to the x axis, false for (transparent) no fill
fillBorder: false, // => draw a border around the fill fillBorder: false, // => draw a border around the fill
fillColor: null, // => fill color fillColor: null, // => fill color
fillOpacity: 0.4, // => opacity of the fill color, set to 1 for a solid fill, 0 hides the fill fillOpacity: 0.4, // => opacity of the fill color, set to 1 for a solid fill, 0 hides the fill
steps: false, // => draw steps steps: false, // => draw steps
stacked: false // => setting to true will show stacked lines, false will show normal lines stacked: false // => setting to true will show stacked lines, false will show normal lines
}, },
   
stack : { stack: {
values : [] values: []
}, },
   
/** /**
* Draws lines series in the canvas element. * Draws lines series in the canvas element.
* @param {Object} options * @param {Object} options
*/ */
draw : function (options) { draw: function (options) {
   
var var
context = options.context, context = options.context,
lineWidth = options.lineWidth, lineWidth = options.lineWidth,
shadowSize = options.shadowSize, shadowSize = options.shadowSize,
offset; offset;
   
context.save(); context.save();
context.lineJoin = 'round'; context.lineJoin = 'round';
   
if (shadowSize) { if (shadowSize) {
   
context.lineWidth = shadowSize / 2; context.lineWidth = shadowSize / 2;
offset = lineWidth / 2 + context.lineWidth / 2; offset = lineWidth / 2 + context.lineWidth / 2;
   
// @TODO do this instead with a linear gradient // @TODO do this instead with a linear gradient
context.strokeStyle = "rgba(0,0,0,0.1)"; context.strokeStyle = "rgba(0,0,0,0.1)";
this.plot(options, offset + shadowSize / 2, false); this.plot(options, offset + shadowSize / 2, false);
   
context.strokeStyle = "rgba(0,0,0,0.2)"; context.strokeStyle = "rgba(0,0,0,0.2)";
this.plot(options, offset, false); this.plot(options, offset, false);
  }
   
  context.lineWidth = lineWidth;
  context.strokeStyle = options.color;
   
  this.plot(options, 0, true);
   
  context.restore();
  },
   
  plot: function (options, shadowOffset, incStack) {
   
  var
  context = options.context,
  width = options.width,
  height = options.height,
  xScale = options.xScale,
  yScale = options.yScale,
  data = options.data,
  stack = options.stacked ? this.stack : false,
  length = data.length - 1,
  prevx = null,
  prevy = null,
  zero = yScale(0),
  start = null,
  x1, x2, y1, y2, stack1, stack2, i;
   
  if (length < 1) return;
   
  context.beginPath();
   
  for (i = 0; i < length; ++i) {
   
  // To allow empty values
  if (data[i][1] === null || data[i + 1][1] === null) {
  if (options.fill) {
  if (i > 0 && data[i][1]) {
  context.stroke();
  fill();
  start = null;
  context.closePath();
  context.beginPath();
  }
  }
  continue;
  }
   
  // Zero is infinity for log scales
  // TODO handle zero for logarithmic
  // if (xa.options.scaling === 'logarithmic' && (data[i][0] <= 0 || data[i+1][0] <= 0)) continue;
  // if (ya.options.scaling === 'logarithmic' && (data[i][1] <= 0 || data[i+1][1] <= 0)) continue;
   
  x1 = xScale(data[i][0]);
  x2 = xScale(data[i + 1][0]);
   
  if (start === null) start = data[i];
   
  if (stack) {
   
  stack1 = stack.values[data[i][0]] || 0;
  stack2 = stack.values[data[i + 1][0]] || stack.values[data[i][0]] || 0;
   
  y1 = yScale(data[i][1] + stack1);
  y2 = yScale(data[i + 1][1] + stack2);
   
  if (incStack) {
  stack.values[data[i][0]] = data[i][1] + stack1;
   
  if (i == length - 1)
  stack.values[data[i + 1][0]] = data[i + 1][1] + stack2;
  }
  }
  else {
  y1 = yScale(data[i][1]);
  y2 = yScale(data[i + 1][1]);
  }
   
  if (
  (y1 > height && y2 > height) ||
  (y1 < 0 && y2 < 0) ||
  (x1 < 0 && x2 < 0) ||
  (x1 > width && x2 > width)
  ) continue;
   
  if ((prevx != x1) || (prevy != y1 + shadowOffset))
  context.moveTo(x1, y1 + shadowOffset);
   
  prevx = x2;
  prevy = y2 + shadowOffset;
  if (options.steps) {
  context.lineTo(prevx + shadowOffset / 2, y1 + shadowOffset);
  context.lineTo(prevx + shadowOffset / 2, prevy);
  } else {
  context.lineTo(prevx, prevy);
  }
  }
   
  if (!options.fill || options.fill && !options.fillBorder) context.stroke();
   
  fill();
   
  function fill() {
  // TODO stacked lines
  if (!shadowOffset && options.fill && start) {
  x1 = xScale(start[0]);
  context.fillStyle = options.fillStyle;
  context.lineTo(x2, zero);
  context.lineTo(x1, zero);
  context.lineTo(x1, yScale(start[1]));
  context.fill();
  if (options.fillBorder) {
  context.stroke();
  }
  }
  }
   
  context.closePath();
  },
   
  // Perform any pre-render precalculations (this should be run on data first)
  // - Pie chart total for calculating measures
  // - Stacks for lines and bars
  // precalculate : function () {
  // }
  //
  //
  // Get any bounds after pre calculation (axis can fetch this if does not have explicit min/max)
  // getBounds : function () {
  // }
  // getMin : function () {
  // }
  // getMax : function () {
  // }
  //
  //
  // Padding around rendered elements
  // getPadding : function () {
  // }
   
  extendYRange: function (axis, data, options, lines) {
   
  var o = axis.options;
   
  // If stacked and auto-min
  if (options.stacked && ((!o.max && o.max !== 0) || (!o.min && o.min !== 0))) {
   
  var
  newmax = axis.max,
  newmin = axis.min,
  positiveSums = lines.positiveSums || {},
  negativeSums = lines.negativeSums || {},
  x, j;
   
  for (j = 0; j < data.length; j++) {
   
  x = data[j][0] + '';
   
  // Positive
  if (data[j][1] > 0) {
  positiveSums[x] = (positiveSums[x] || 0) + data[j][1];
  newmax = Math.max(newmax, positiveSums[x]);
  }
   
  // Negative
  else {
  negativeSums[x] = (negativeSums[x] || 0) + data[j][1];
  newmin = Math.min(newmin, negativeSums[x]);
  }
  }
   
  lines.negativeSums = negativeSums;
  lines.positiveSums = positiveSums;
   
  axis.max = newmax;
  axis.min = newmin;
  }
   
  if (options.steps) {
   
  this.hit = function (options) {
  var
  data = options.data,
  args = options.args,
  yScale = options.yScale,
  mouse = args[0],
  length = data.length,
  n = args[1],
  x = mouse.x,
  relY = mouse.relY,
  i;
   
  for (i = 0; i < length - 1; i++) {
  if (x >= data[i][0] && x <= data[i + 1][0]) {
  if (Math.abs(yScale(data[i][1]) - relY) < 8) {
  n.x = data[i][0];
  n.y = data[i][1];
  n.index = i;
  n.seriesIndex = options.index;
  }
  break;
  }
  }
  };
   
  this.drawHit = function (options) {
  var
  context = options.context,
  args = options.args,
  data = options.data,
  xScale = options.xScale,
  index = args.index,
  x = xScale(args.x),
  y = options.yScale(args.y),
  x2;
   
  if (data.length - 1 > index) {
  x2 = options.xScale(data[index + 1][0]);
  context.save();
  context.strokeStyle = options.color;
  context.lineWidth = options.lineWidth;
  context.beginPath();
  context.moveTo(x, y);
  context.lineTo(x2, y);
  context.stroke();
  context.closePath();
  context.restore();
  }
  };
   
  this.clearHit = function (options) {
  var
  context = options.context,
  args = options.args,
  data = options.data,
  xScale = options.xScale,
  width = options.lineWidth,
  index = args.index,
  x = xScale(args.x),
  y = options.yScale(args.y),
  x2;
   
  if (data.length - 1 > index) {
  x2 = options.xScale(data[index + 1][0]);
  context.clearRect(x - width, y - width, x2 - x + 2 * width, 2 * width);
  }
  };
  }
} }
   
context.lineWidth = lineWidth;  
context.strokeStyle = options.color;  
   
this.plot(options, 0, true);  
   
context.restore();  
},  
   
plot : function (options, shadowOffset, incStack) {  
   
var  
context = options.context,  
width = options.width,  
height = options.height,  
xScale = options.xScale,  
yScale = options.yScale,  
data = options.data,  
stack = options.stacked ? this.stack : false,  
length = data.length - 1,  
prevx = null,  
prevy = null,  
zero = yScale(0),  
start = null,  
x1, x2, y1, y2, stack1, stack2, i;  
   
if (length < 1) return;  
   
context.beginPath();  
   
for (i = 0; i < length; ++i) {  
   
// To allow empty values  
if (data[i][1] === null || data[i+1][1] === null) {  
if (options.fill) {  
if (i > 0 && data[i][1]) {  
context.stroke();  
fill();  
start = null;  
context.closePath();  
context.beginPath();  
}  
}  
continue;  
}  
   
// Zero is infinity for log scales  
// TODO handle zero for logarithmic  
// if (xa.options.scaling === 'logarithmic' && (data[i][0] <= 0 || data[i+1][0] <= 0)) continue;  
// if (ya.options.scaling === 'logarithmic' && (data[i][1] <= 0 || data[i+1][1] <= 0)) continue;  
   
x1 = xScale(data[i][0]);  
x2 = xScale(data[i+1][0]);  
   
if (start === null) start = data[i];  
   
if (stack) {  
   
stack1 = stack.values[data[i][0]] || 0;  
stack2 = stack.values[data[i+1][0]] || stack.values[data[i][0]] || 0;  
   
y1 = yScale(data[i][1] + stack1);  
y2 = yScale(data[i+1][1] + stack2);  
   
if(incStack){  
stack.values[data[i][0]] = data[i][1]+stack1;  
   
if(i == length-1)  
stack.values[data[i+1][0]] = data[i+1][1]+stack2;  
}  
}  
else{  
y1 = yScale(data[i][1]);  
y2 = yScale(data[i+1][1]);  
}  
   
if (  
(y1 > height && y2 > height) ||  
(y1 < 0 && y2 < 0) ||  
(x1 < 0 && x2 < 0) ||  
(x1 > width && x2 > width)  
) continue;  
   
if((prevx != x1) || (prevy != y1 + shadowOffset))  
context.moveTo(x1, y1 + shadowOffset);  
   
prevx = x2;  
prevy = y2 + shadowOffset;  
if (options.steps) {  
context.lineTo(prevx + shadowOffset / 2, y1 + shadowOffset);  
context.lineTo(prevx + shadowOffset / 2, prevy);  
} else {  
context.lineTo(prevx, prevy);  
}  
}  
   
if (!options.fill || options.fill && !options.fillBorder) context.stroke();  
   
fill();  
   
function fill () {  
// TODO stacked lines  
if(!shadowOffset && options.fill && start){  
x1 = xScale(start[0]);  
context.fillStyle = options.fillStyle;  
context.lineTo(x2, zero);  
context.lineTo(x1, zero);  
context.lineTo(x1, yScale(start[1]));  
context.fill();  
if (options.fillBorder) {  
context.stroke();  
}  
}  
}  
   
context.closePath();  
},  
   
// Perform any pre-render precalculations (this should be run on data first)  
// - Pie chart total for calculating measures  
// - Stacks for lines and bars  
// precalculate : function () {  
// }  
//  
//  
// Get any bounds after pre calculation (axis can fetch this if does not have explicit min/max)  
// getBounds : function () {  
// }  
// getMin : function () {  
// }  
// getMax : function () {  
// }  
//  
//  
// Padding around rendered elements  
// getPadding : function () {  
// }  
   
extendYRange : function (axis, data, options, lines) {  
   
var o = axis.options;  
   
// If stacked and auto-min  
if (options.stacked && ((!o.max && o.max !== 0) || (!o.min && o.min !== 0))) {  
   
var  
newmax = axis.max,  
newmin = axis.min,  
positiveSums = lines.positiveSums || {},  
negativeSums = lines.negativeSums || {},  
x, j;  
   
for (j = 0; j < data.length; j++) {  
   
x = data[j][0] + '';  
   
// Positive  
if (data[j][1] > 0) {  
positiveSums[x] = (positiveSums[x] || 0) + data[j][1];  
newmax = Math.max(newmax, positiveSums[x]);  
}  
   
// Negative  
else {  
negativeSums[x] = (negativeSums[x] || 0) + data[j][1];  
newmin = Math.min(newmin, negativeSums[x]);  
}  
}  
   
lines.negativeSums = negativeSums;  
lines.positiveSums = positiveSums;  
   
axis.max = newmax;  
axis.min = newmin;  
}  
   
if (options.steps) {  
   
this.hit = function (options) {  
var  
data = options.data,  
args = options.args,  
yScale = options.yScale,  
mouse = args[0],  
length = data.length,  
n = args[1],  
x = mouse.x,  
relY = mouse.relY,  
i;  
   
for (i = 0; i < length - 1; i++) {  
if (x >= data[i][0] && x <= data[i+1][0]) {  
if (Math.abs(yScale(data[i][1]) - relY) < 8) {  
n.x = data[i][0];  
n.y = data[i][1];  
n.index = i;  
n.seriesIndex = options.index;  
}  
break;  
}  
}  
};  
   
this.drawHit = function (options) {  
var  
context = options.context,  
args = options.args,  
data = options.data,  
xScale = options.xScale,  
index = args.index,  
x = xScale(args.x),  
y = options.yScale(args.y),  
x2;  
   
if (data.length - 1 > index) {  
x2 = options.xScale(data[index + 1][0]);  
context.save();  
context.strokeStyle = options.color;  
context.lineWidth = options.lineWidth;  
context.beginPath();  
context.moveTo(x, y);  
context.lineTo(x2, y);  
context.stroke();  
context.closePath();  
context.restore();  
}  
};  
   
this.clearHit = function (options) {  
var  
context = options.context,  
args = options.args,  
data = options.data,  
xScale = options.xScale,  
width = options.lineWidth,  
index = args.index,  
x = xScale(args.x),  
y = options.yScale(args.y),  
x2;  
   
if (data.length - 1 > index) {  
x2 = options.xScale(data[index + 1][0]);  
context.clearRect(x - width, y - width, x2 - x + 2 * width, 2 * width);  
}  
};  
}  
}  
   
}); });
   
/** Bars **/ /** Bars **/
Flotr.addType('bars', { Flotr.addType('bars', {
   
options: { options: {
show: false, // => setting to true will show bars, false will hide show: false, // => setting to true will show bars, false will hide
lineWidth: 2, // => in pixels lineWidth: 2, // => in pixels
barWidth: 1, // => in units of the x axis barWidth: 1, // => in units of the x axis
fill: true, // => true to fill the area from the line to the x axis, false for (transparent) no fill fill: true, // => true to fill the area from the line to the x axis, false for (transparent) no fill
fillColor: null, // => fill color fillColor: null, // => fill color
fillOpacity: 0.4, // => opacity of the fill color, set to 1 for a solid fill, 0 hides the fill fillOpacity: 0.4, // => opacity of the fill color, set to 1 for a solid fill, 0 hides the fill
horizontal: false, // => horizontal bars (x and y inverted) horizontal: false, // => horizontal bars (x and y inverted)
stacked: false, // => stacked bar charts stacked: false, // => stacked bar charts
centered: true, // => center the bars to their x axis value centered: true, // => center the bars to their x axis value
topPadding: 0.1, // => top padding in percent topPadding: 0.1, // => top padding in percent
grouped: false // => groups bars together which share x value, hit not supported. grouped: false // => groups bars together which share x value, hit not supported.
}, },
   
stack : { stack: {
positive : [], positive: [],
negative : [], negative: [],
_positive : [], // Shadow _positive: [], // Shadow
_negative : [] // Shadow _negative: [] // Shadow
}, },
   
draw : function (options) { draw: function (options) {
var var
context = options.context; context = options.context;
   
this.current += 1; this.current += 1;
   
context.save();  
context.lineJoin = 'miter';  
// @TODO linewidth not interpreted the right way.  
context.lineWidth = options.lineWidth;  
context.strokeStyle = options.color;  
if (options.fill) context.fillStyle = options.fillStyle;  
   
this.plot(options);  
   
context.restore();  
},  
   
plot : function (options) {  
   
var  
data = options.data,  
context = options.context,  
shadowSize = options.shadowSize,  
i, geometry, left, top, width, height;  
   
if (data.length < 1) return;  
   
this.translate(context, options.horizontal);  
   
for (i = 0; i < data.length; i++) {  
   
geometry = this.getBarGeometry(data[i][0], data[i][1], options);  
if (geometry === null) continue;  
   
left = geometry.left;  
top = geometry.top;  
width = geometry.width;  
height = geometry.height;  
   
if (options.fill) context.fillRect(left, top, width, height);  
if (shadowSize) {  
context.save(); context.save();
context.fillStyle = 'rgba(0,0,0,0.05)'; context.lineJoin = 'miter';
context.fillRect(left + shadowSize, top + shadowSize, width, height); // @TODO linewidth not interpreted the right way.
  context.lineWidth = options.lineWidth;
  context.strokeStyle = options.color;
  if (options.fill) context.fillStyle = options.fillStyle;
   
  this.plot(options);
   
context.restore(); context.restore();
} },
if (options.lineWidth) {  
context.strokeRect(left, top, width, height); plot: function (options) {
}  
  var
  data = options.data,
  context = options.context,
  shadowSize = options.shadowSize,
  i, geometry, left, top, width, height;
   
  if (data.length < 1) return;
   
  this.translate(context, options.horizontal);
   
  for (i = 0; i < data.length; i++) {
   
  geometry = this.getBarGeometry(data[i][0], data[i][1], options);
  if (geometry === null) continue;
   
  left = geometry.left;
  top = geometry.top;
  width = geometry.width;
  height = geometry.height;
   
  if (options.fill) context.fillRect(left, top, width, height);
  if (shadowSize) {
  context.save();
  context.fillStyle = 'rgba(0,0,0,0.05)';
  context.fillRect(left + shadowSize, top + shadowSize, width, height);
  context.restore();
  }
  if (options.lineWidth) {
  context.strokeRect(left, top, width, height);
  }
  }
  },
   
  translate: function (context, horizontal) {
  if (horizontal) {
  context.rotate(-Math.PI / 2);
  context.scale(-1, 1);
  }
  },
   
  getBarGeometry: function (x, y, options) {
   
  var
  horizontal = options.horizontal,
  barWidth = options.barWidth,
  centered = options.centered,
  stack = options.stacked ? this.stack : false,
  lineWidth = options.lineWidth,
  bisection = centered ? barWidth / 2 : 0,
  xScale = horizontal ? options.yScale : options.xScale,
  yScale = horizontal ? options.xScale : options.yScale,
  xValue = horizontal ? y : x,
  yValue = horizontal ? x : y,
  stackOffset = 0,
  stackValue, left, right, top, bottom;
   
  if (options.grouped) {
  this.current / this.groups
  xValue = xValue - bisection;
  barWidth = barWidth / this.groups;
  bisection = barWidth / 2;
  xValue = xValue + barWidth * this.current - bisection;
  }
   
  // Stacked bars
  if (stack) {
  stackValue = yValue > 0 ? stack.positive : stack.negative;
  stackOffset = stackValue[xValue] || stackOffset;
  stackValue[xValue] = stackOffset + yValue;
  }
   
  left = xScale(xValue - bisection);
  right = xScale(xValue + barWidth - bisection);
  top = yScale(yValue + stackOffset);
  bottom = yScale(stackOffset);
   
  // TODO for test passing... probably looks better without this
  if (bottom < 0) bottom = 0;
   
  // TODO Skipping...
  // if (right < xa.min || left > xa.max || top < ya.min || bottom > ya.max) continue;
   
  return (x === null || y === null) ? null : {
  x: xValue,
  y: yValue,
  xScale: xScale,
  yScale: yScale,
  top: top,
  left: Math.min(left, right) - lineWidth / 2,
  width: Math.abs(right - left) - lineWidth,
  height: bottom - top
  };
  },
   
  hit: function (options) {
  var
  data = options.data,
  args = options.args,
  mouse = args[0],
  n = args[1],
  x = mouse.x,
  y = mouse.y,
  hitGeometry = this.getBarGeometry(x, y, options),
  width = hitGeometry.width / 2,
  left = hitGeometry.left,
  geometry, i;
   
  for (i = data.length; i--;) {
  geometry = this.getBarGeometry(data[i][0], data[i][1], options);
  if (geometry.y > hitGeometry.y && Math.abs(left - geometry.left) < width) {
  n.x = data[i][0];
  n.y = data[i][1];
  n.index = i;
  n.seriesIndex = options.index;
  }
  }
  },
   
  drawHit: function (options) {
  // TODO hits for stacked bars; implement using calculateStack option?
  var
  context = options.context,
  args = options.args,
  geometry = this.getBarGeometry(args.x, args.y, options),
  left = geometry.left,
  top = geometry.top,
  width = geometry.width,
  height = geometry.height;
   
  context.save();
  context.strokeStyle = options.color;
  context.lineWidth = options.lineWidth;
  this.translate(context, options.horizontal);
   
  // Draw highlight
  context.beginPath();
  context.moveTo(left, top + height);
  context.lineTo(left, top);
  context.lineTo(left + width, top);
  context.lineTo(left + width, top + height);
  if (options.fill) {
  context.fillStyle = options.fillStyle;
  context.fill();
  }
  context.stroke();
  context.closePath();
   
  context.restore();
  },
   
  clearHit: function (options) {
  var
  context = options.context,
  args = options.args,
  geometry = this.getBarGeometry(args.x, args.y, options),
  left = geometry.left,
  width = geometry.width,
  top = geometry.top,
  height = geometry.height,
  lineWidth = 2 * options.lineWidth;
   
  context.save();
  this.translate(context, options.horizontal);
  context.clearRect(
  left - lineWidth,
  Math.min(top, top + height) - lineWidth,
  width + 2 * lineWidth,
  Math.abs(height) + 2 * lineWidth
  );
  context.restore();
  },
   
  extendXRange: function (axis, data, options, bars) {
  this._extendRange(axis, data, options, bars);
  this.groups = (this.groups + 1) || 1;
  this.current = 0;
  },
   
  extendYRange: function (axis, data, options, bars) {
  this._extendRange(axis, data, options, bars);
  },
  _extendRange: function (axis, data, options, bars) {
   
  var
  max = axis.options.max;
   
  if (_.isNumber(max) || _.isString(max)) return;
   
  var
  newmin = axis.min,
  newmax = axis.max,
  horizontal = options.horizontal,
  orientation = axis.orientation,
  positiveSums = this.positiveSums || {},
  negativeSums = this.negativeSums || {},
  value, datum, index, j;
   
  // Sides of bars
  if ((orientation == 1 && !horizontal) || (orientation == -1 && horizontal)) {
  if (options.centered) {
  newmax = Math.max(axis.datamax + options.barWidth, newmax);
  newmin = Math.min(axis.datamin - options.barWidth, newmin);
  }
  }
   
  if (options.stacked &&
  ((orientation == 1 && horizontal) || (orientation == -1 && !horizontal))) {
   
  for (j = data.length; j--;) {
  value = data[j][(orientation == 1 ? 1 : 0)] + '';
  datum = data[j][(orientation == 1 ? 0 : 1)];
   
  // Positive
  if (datum > 0) {
  positiveSums[value] = (positiveSums[value] || 0) + datum;
  newmax = Math.max(newmax, positiveSums[value]);
  }
   
  // Negative
  else {
  negativeSums[value] = (negativeSums[value] || 0) + datum;
  newmin = Math.min(newmin, negativeSums[value]);
  }
  }
  }
   
  // End of bars
  if ((orientation == 1 && horizontal) || (orientation == -1 && !horizontal)) {
  if (options.topPadding && (axis.max === axis.datamax || (options.stacked && this.stackMax !== newmax))) {
  newmax += options.topPadding * (newmax - newmin);
  }
  }
   
  this.stackMin = newmin;
  this.stackMax = newmax;
  this.negativeSums = negativeSums;
  this.positiveSums = positiveSums;
   
  axis.max = newmax;
  axis.min = newmin;
} }
},  
   
translate : function (context, horizontal) {  
if (horizontal) {  
context.rotate(-Math.PI / 2);  
context.scale(-1, 1);  
}  
},  
   
getBarGeometry : function (x, y, options) {  
   
var  
horizontal = options.horizontal,  
barWidth = options.barWidth,  
centered = options.centered,  
stack = options.stacked ? this.stack : false,  
lineWidth = options.lineWidth,  
bisection = centered ? barWidth / 2 : 0,  
xScale = horizontal ? options.yScale : options.xScale,  
yScale = horizontal ? options.xScale : options.yScale,  
xValue = horizontal ? y : x,  
yValue = horizontal ? x : y,  
stackOffset = 0,  
stackValue, left, right, top, bottom;  
   
if (options.grouped) {  
this.current / this.groups  
xValue = xValue - bisection;  
barWidth = barWidth / this.groups;  
bisection = barWidth / 2;  
xValue = xValue + barWidth * this.current - bisection;  
}  
   
// Stacked bars  
if (stack) {  
stackValue = yValue > 0 ? stack.positive : stack.negative;  
stackOffset = stackValue[xValue] || stackOffset;  
stackValue[xValue] = stackOffset + yValue;  
}  
   
left = xScale(xValue - bisection);  
right = xScale(xValue + barWidth - bisection);  
top = yScale(yValue + stackOffset);  
bottom = yScale(stackOffset);  
   
// TODO for test passing... probably looks better without this  
if (bottom < 0) bottom = 0;  
   
// TODO Skipping...  
// if (right < xa.min || left > xa.max || top < ya.min || bottom > ya.max) continue;  
   
return (x === null || y === null) ? null : {  
x : xValue,  
y : yValue,  
xScale : xScale,  
yScale : yScale,  
top : top,  
left : Math.min(left, right) - lineWidth / 2,  
width : Math.abs(right - left) - lineWidth,  
height : bottom - top  
};  
},  
   
hit : function (options) {  
var  
data = options.data,  
args = options.args,  
mouse = args[0],  
n = args[1],  
x = mouse.x,  
y = mouse.y,  
hitGeometry = this.getBarGeometry(x, y, options),  
width = hitGeometry.width / 2,  
left = hitGeometry.left,  
geometry, i;  
   
for (i = data.length; i--;) {  
geometry = this.getBarGeometry(data[i][0], data[i][1], options);  
if (geometry.y > hitGeometry.y && Math.abs(left - geometry.left) < width) {  
n.x = data[i][0];  
n.y = data[i][1];  
n.index = i;  
n.seriesIndex = options.index;  
}  
}  
},  
   
drawHit : function (options) {  
// TODO hits for stacked bars; implement using calculateStack option?  
var  
context = options.context,  
args = options.args,  
geometry = this.getBarGeometry(args.x, args.y, options),  
left = geometry.left,  
top = geometry.top,  
width = geometry.width,  
height = geometry.height;  
   
context.save();  
context.strokeStyle = options.color;  
context.lineWidth = options.lineWidth;  
this.translate(context, options.horizontal);  
   
// Draw highlight  
context.beginPath();  
context.moveTo(left, top + height);  
context.lineTo(left, top);  
context.lineTo(left + width, top);  
context.lineTo(left + width, top + height);  
if (options.fill) {  
context.fillStyle = options.fillStyle;  
context.fill();  
}  
context.stroke();  
context.closePath();  
   
context.restore();  
},  
   
clearHit: function (options) {  
var  
context = options.context,  
args = options.args,  
geometry = this.getBarGeometry(args.x, args.y, options),  
left = geometry.left,  
width = geometry.width,  
top = geometry.top,  
height = geometry.height,  
lineWidth = 2 * options.lineWidth;  
   
context.save();  
this.translate(context, options.horizontal);  
context.clearRect(  
left - lineWidth,  
Math.min(top, top + height) - lineWidth,  
width + 2 * lineWidth,  
Math.abs(height) + 2 * lineWidth  
);  
context.restore();  
},  
   
extendXRange : function (axis, data, options, bars) {  
this._extendRange(axis, data, options, bars);  
this.groups = (this.groups + 1) || 1;  
this.current = 0;  
},  
   
extendYRange : function (axis, data, options, bars) {  
this._extendRange(axis, data, options, bars);  
},  
_extendRange: function (axis, data, options, bars) {  
   
var  
max = axis.options.max;  
   
if (_.isNumber(max) || _.isString(max)) return;  
   
var  
newmin = axis.min,  
newmax = axis.max,  
horizontal = options.horizontal,  
orientation = axis.orientation,  
positiveSums = this.positiveSums || {},  
negativeSums = this.negativeSums || {},  
value, datum, index, j;  
   
// Sides of bars  
if ((orientation == 1 && !horizontal) || (orientation == -1 && horizontal)) {  
if (options.centered) {  
newmax = Math.max(axis.datamax + options.barWidth, newmax);  
newmin = Math.min(axis.datamin - options.barWidth, newmin);  
}  
}  
   
if (options.stacked &&  
((orientation == 1 && horizontal) || (orientation == -1 && !horizontal))){  
   
for (j = data.length; j--;) {  
value = data[j][(orientation == 1 ? 1 : 0)]+'';  
datum = data[j][(orientation == 1 ? 0 : 1)];  
   
// Positive  
if (datum > 0) {  
positiveSums[value] = (positiveSums[value] || 0) + datum;  
newmax = Math.max(newmax, positiveSums[value]);  
}  
   
// Negative  
else {  
negativeSums[value] = (negativeSums[value] || 0) + datum;  
newmin = Math.min(newmin, negativeSums[value]);  
}  
}  
}  
   
// End of bars  
if ((orientation == 1 && horizontal) || (orientation == -1 && !horizontal)) {  
if (options.topPadding && (axis.max === axis.datamax || (options.stacked && this.stackMax !== newmax))) {  
newmax += options.topPadding * (newmax - newmin);  
}  
}  
   
this.stackMin = newmin;  
this.stackMax = newmax;  
this.negativeSums = negativeSums;  
this.positiveSums = positiveSums;  
   
axis.max = newmax;  
axis.min = newmin;  
}  
   
}); });
   
/** Bubbles **/ /** Bubbles **/
Flotr.addType('bubbles', { Flotr.addType('bubbles', {
options: { options: {
show: false, // => setting to true will show radar chart, false will hide show: false, // => setting to true will show radar chart, false will hide
lineWidth: 2, // => line width in pixels lineWidth: 2, // => line width in pixels
fill: true, // => true to fill the area from the line to the x axis, false for (transparent) no fill fill: true, // => true to fill the area from the line to the x axis, false for (transparent) no fill
fillOpacity: 0.4, // => opacity of the fill color, set to 1 for a solid fill, 0 hides the fill fillOpacity: 0.4, // => opacity of the fill color, set to 1 for a solid fill, 0 hides the fill
baseRadius: 2 // => ratio of the radar, against the plot size baseRadius: 2 // => ratio of the radar, against the plot size
}, },
draw : function (options) { draw: function (options) {
var var
context = options.context, context = options.context,
shadowSize = options.shadowSize; shadowSize = options.shadowSize;
   
context.save(); context.save();
context.lineWidth = options.lineWidth; context.lineWidth = options.lineWidth;
   
// Shadows // Shadows
context.fillStyle = 'rgba(0,0,0,0.05)'; context.fillStyle = 'rgba(0,0,0,0.05)';
context.strokeStyle = 'rgba(0,0,0,0.05)'; context.strokeStyle = 'rgba(0,0,0,0.05)';
this.plot(options, shadowSize / 2); this.plot(options, shadowSize / 2);
context.strokeStyle = 'rgba(0,0,0,0.1)'; context.strokeStyle = 'rgba(0,0,0,0.1)';
this.plot(options, shadowSize / 4); this.plot(options, shadowSize / 4);
   
// Chart // Chart
context.strokeStyle = options.color; context.strokeStyle = options.color;
context.fillStyle = options.fillStyle; context.fillStyle = options.fillStyle;
this.plot(options); this.plot(options);
   
context.restore(); context.restore();
}, },
plot : function (options, offset) { plot: function (options, offset) {
   
var var
data = options.data, data = options.data,
context = options.context, context = options.context,
geometry, geometry,
i, x, y, z; i, x, y, z;
   
offset = offset || 0; offset = offset || 0;
   
for (i = 0; i < data.length; ++i){ for (i = 0; i < data.length; ++i) {
   
geometry = this.getGeometry(data[i], options); geometry = this.getGeometry(data[i], options);
   
context.beginPath(); context.beginPath();
context.arc(geometry.x + offset, geometry.y + offset, geometry.z, 0, 2 * Math.PI, true); context.arc(geometry.x + offset, geometry.y + offset, geometry.z, 0, 2 * Math.PI, true);
context.stroke(); context.stroke();
if (options.fill) context.fill(); if (options.fill) context.fill();
context.closePath(); context.closePath();
  }
  },
  getGeometry: function (point, options) {
  return {
  x: options.xScale(point[0]),
  y: options.yScale(point[1]),
  z: point[2] * options.baseRadius
  };
  },
  hit: function (options) {
  var
  data = options.data,
  args = options.args,
  mouse = args[0],
  n = args[1],
  x = mouse.x,
  y = mouse.y,
  distance,
  geometry,
  dx, dy;
   
  n.best = n.best || Number.MAX_VALUE;
   
  for (i = data.length; i--;) {
  geometry = this.getGeometry(data[i], options);
   
  dx = geometry.x - options.xScale(x);
  dy = geometry.y - options.yScale(y);
  distance = Math.sqrt(dx * dx + dy * dy);
   
  if (distance < geometry.z && geometry.z < n.best) {
  n.x = data[i][0];
  n.y = data[i][1];
  n.index = i;
  n.seriesIndex = options.index;
  n.best = geometry.z;
  }
  }
  },
  drawHit: function (options) {
   
  var
  context = options.context,
  geometry = this.getGeometry(options.data[options.args.index], options);
   
  context.save();
  context.lineWidth = options.lineWidth;
  context.fillStyle = options.fillStyle;
  context.strokeStyle = options.color;
  context.beginPath();
  context.arc(geometry.x, geometry.y, geometry.z, 0, 2 * Math.PI, true);
  context.fill();
  context.stroke();
  context.closePath();
  context.restore();
  },
  clearHit: function (options) {
   
  var
  context = options.context,
  geometry = this.getGeometry(options.data[options.args.index], options),
  offset = geometry.z + options.lineWidth;
   
  context.save();
  context.clearRect(
  geometry.x - offset,
  geometry.y - offset,
  2 * offset,
  2 * offset
  );
  context.restore();
} }
}, // TODO Add a hit calculation method (like pie)
getGeometry : function (point, options) {  
return {  
x : options.xScale(point[0]),  
y : options.yScale(point[1]),  
z : point[2] * options.baseRadius  
};  
},  
hit : function (options) {  
var  
data = options.data,  
args = options.args,  
mouse = args[0],  
n = args[1],  
x = mouse.x,  
y = mouse.y,  
distance,  
geometry,  
dx, dy;  
   
n.best = n.best || Number.MAX_VALUE;  
   
for (i = data.length; i--;) {  
geometry = this.getGeometry(data[i], options);  
   
dx = geometry.x - options.xScale(x);  
dy = geometry.y - options.yScale(y);  
distance = Math.sqrt(dx * dx + dy * dy);  
   
if (distance < geometry.z && geometry.z < n.best) {  
n.x = data[i][0];  
n.y = data[i][1];  
n.index = i;  
n.seriesIndex = options.index;  
n.best = geometry.z;  
}  
}  
},  
drawHit : function (options) {  
   
var  
context = options.context,  
geometry = this.getGeometry(options.data[options.args.index], options);  
   
context.save();  
context.lineWidth = options.lineWidth;  
context.fillStyle = options.fillStyle;  
context.strokeStyle = options.color;  
context.beginPath();  
context.arc(geometry.x, geometry.y, geometry.z, 0, 2 * Math.PI, true);  
context.fill();  
context.stroke();  
context.closePath();  
context.restore();  
},  
clearHit : function (options) {  
   
var  
context = options.context,  
geometry = this.getGeometry(options.data[options.args.index], options),  
offset = geometry.z + options.lineWidth;  
   
context.save();  
context.clearRect(  
geometry.x - offset,  
geometry.y - offset,  
2 * offset,  
2 * offset  
);  
context.restore();  
}  
// TODO Add a hit calculation method (like pie)  
}); });
   
/** Candles **/ /** Candles **/
Flotr.addType('candles', { Flotr.addType('candles', {
options: { options: {
show: false, // => setting to true will show candle sticks, false will hide show: false, // => setting to true will show candle sticks, false will hide
lineWidth: 1, // => in pixels lineWidth: 1, // => in pixels
wickLineWidth: 1, // => in pixels wickLineWidth: 1, // => in pixels
candleWidth: 0.6, // => in units of the x axis candleWidth: 0.6, // => in units of the x axis
fill: true, // => true to fill the area from the line to the x axis, false for (transparent) no fill fill: true, // => true to fill the area from the line to the x axis, false for (transparent) no fill
upFillColor: '#00A8F0',// => up sticks fill color upFillColor: '#00A8F0',// => up sticks fill color
downFillColor: '#CB4B4B',// => down sticks fill color downFillColor: '#CB4B4B',// => down sticks fill color
fillOpacity: 0.5, // => opacity of the fill color, set to 1 for a solid fill, 0 hides the fill fillOpacity: 0.5, // => opacity of the fill color, set to 1 for a solid fill, 0 hides the fill
// TODO Test this barcharts option. // TODO Test this barcharts option.
barcharts: false // => draw as barcharts (not standard bars but financial barcharts) barcharts: false // => draw as barcharts (not standard bars but financial barcharts)
}, },
   
draw : function (options) { draw: function (options) {
   
var var
context = options.context; context = options.context;
   
context.save();  
context.lineJoin = 'miter';  
context.lineCap = 'butt';  
// @TODO linewidth not interpreted the right way.  
context.lineWidth = options.wickLineWidth || options.lineWidth;  
   
this.plot(options);  
   
context.restore();  
},  
   
plot : function (options) {  
   
var  
data = options.data,  
context = options.context,  
xScale = options.xScale,  
yScale = options.yScale,  
width = options.candleWidth / 2,  
shadowSize = options.shadowSize,  
lineWidth = options.lineWidth,  
wickLineWidth = options.wickLineWidth,  
pixelOffset = (wickLineWidth % 2) / 2,  
color,  
datum, x, y,  
open, high, low, close,  
left, right, bottom, top, bottom2, top2,  
i;  
   
if (data.length < 1) return;  
   
for (i = 0; i < data.length; i++) {  
datum = data[i];  
x = datum[0];  
open = datum[1];  
high = datum[2];  
low = datum[3];  
close = datum[4];  
left = xScale(x - width);  
right = xScale(x + width);  
bottom = yScale(low);  
top = yScale(high);  
bottom2 = yScale(Math.min(open, close));  
top2 = yScale(Math.max(open, close));  
   
/*  
// TODO skipping  
if(right < xa.min || left > xa.max || top < ya.min || bottom > ya.max)  
continue;  
*/  
   
color = options[open > close ? 'downFillColor' : 'upFillColor'];  
   
// Fill the candle.  
// TODO Test the barcharts option  
if (options.fill && !options.barcharts) {  
context.fillStyle = 'rgba(0,0,0,0.05)';  
context.fillRect(left + shadowSize, top2 + shadowSize, right - left, bottom2 - top2);  
context.save(); context.save();
context.globalAlpha = options.fillOpacity; context.lineJoin = 'miter';
context.fillStyle = color; context.lineCap = 'butt';
context.fillRect(left, top2 + lineWidth, right - left, bottom2 - top2); // @TODO linewidth not interpreted the right way.
  context.lineWidth = options.wickLineWidth || options.lineWidth;
   
  this.plot(options);
   
context.restore(); context.restore();
} },
   
// Draw candle outline/border, high, low. plot: function (options) {
if (lineWidth || wickLineWidth) {  
  var
x = Math.floor((left + right) / 2) + pixelOffset; data = options.data,
  context = options.context,
context.strokeStyle = color; xScale = options.xScale,
context.beginPath(); yScale = options.yScale,
  width = options.candleWidth / 2,
// TODO Again with the bartcharts shadowSize = options.shadowSize,
if (options.barcharts) { lineWidth = options.lineWidth,
  wickLineWidth = options.wickLineWidth,
context.moveTo(x, Math.floor(top + width)); pixelOffset = (wickLineWidth % 2) / 2,
context.lineTo(x, Math.floor(bottom + width)); color,
  datum, x, y,
y = Math.floor(open + width) + 0.5; open, high, low, close,
context.moveTo(Math.floor(left) + pixelOffset, y); left, right, bottom, top, bottom2, top2,
context.lineTo(x, y); i;
   
y = Math.floor(close + width) + 0.5; if (data.length < 1) return;
context.moveTo(Math.floor(right) + pixelOffset, y);  
context.lineTo(x, y); for (i = 0; i < data.length; i++) {
} else { datum = data[i];
context.strokeRect(left, top2 + lineWidth, right - left, bottom2 - top2); x = datum[0];
  open = datum[1];
context.moveTo(x, Math.floor(top2 + lineWidth)); high = datum[2];
context.lineTo(x, Math.floor(top + lineWidth)); low = datum[3];
context.moveTo(x, Math.floor(bottom2 + lineWidth)); close = datum[4];
context.lineTo(x, Math.floor(bottom + lineWidth)); left = xScale(x - width);
} right = xScale(x + width);
  bottom = yScale(low);
context.closePath(); top = yScale(high);
context.stroke(); bottom2 = yScale(Math.min(open, close));
} top2 = yScale(Math.max(open, close));
   
  /*
  // TODO skipping
  if(right < xa.min || left > xa.max || top < ya.min || bottom > ya.max)
  continue;
  */
   
  color = options[open > close ? 'downFillColor' : 'upFillColor'];
   
  // Fill the candle.
  // TODO Test the barcharts option
  if (options.fill && !options.barcharts) {
  context.fillStyle = 'rgba(0,0,0,0.05)';
  context.fillRect(left + shadowSize, top2 + shadowSize, right - left, bottom2 - top2);
  context.save();
  context.globalAlpha = options.fillOpacity;
  context.fillStyle = color;
  context.fillRect(left, top2 + lineWidth, right - left, bottom2 - top2);
  context.restore();
  }
   
  // Draw candle outline/border, high, low.
  if (lineWidth || wickLineWidth) {
   
  x = Math.floor((left + right) / 2) + pixelOffset;
   
  context.strokeStyle = color;
  context.beginPath();
   
  // TODO Again with the bartcharts
  if (options.barcharts) {
   
  context.moveTo(x, Math.floor(top + width));
  context.lineTo(x, Math.floor(bottom + width));
   
  y = Math.floor(open + width) + 0.5;
  context.moveTo(Math.floor(left) + pixelOffset, y);
  context.lineTo(x, y);
   
  y = Math.floor(close + width) + 0.5;
  context.moveTo(Math.floor(right) + pixelOffset, y);
  context.lineTo(x, y);
  } else {
  context.strokeRect(left, top2 + lineWidth, right - left, bottom2 - top2);
   
  context.moveTo(x, Math.floor(top2 + lineWidth));
  context.lineTo(x, Math.floor(top + lineWidth));
  context.moveTo(x, Math.floor(bottom2 + lineWidth));
  context.lineTo(x, Math.floor(bottom + lineWidth));
  }
   
  context.closePath();
  context.stroke();
  }
  }
  },
  extendXRange: function (axis, data, options) {
  if (axis.options.max === null) {
  axis.max = Math.max(axis.datamax + 0.5, axis.max);
  axis.min = Math.min(axis.datamin - 0.5, axis.min);
  }
} }
},  
extendXRange: function (axis, data, options) {  
if (axis.options.max === null) {  
axis.max = Math.max(axis.datamax + 0.5, axis.max);  
axis.min = Math.min(axis.datamin - 0.5, axis.min);  
}  
}  
}); });
   
/** Gantt /** Gantt
* Base on data in form [s,y,d] where: * Base on data in form [s,y,d] where:
* y - executor or simply y value * y - executor or simply y value
* s - task start value * s - task start value
* d - task duration * d - task duration
* **/ * **/
Flotr.addType('gantt', { Flotr.addType('gantt', {
options: { options: {
show: false, // => setting to true will show gantt, false will hide show: false, // => setting to true will show gantt, false will hide
lineWidth: 2, // => in pixels lineWidth: 2, // => in pixels
barWidth: 1, // => in units of the x axis barWidth: 1, // => in units of the x axis
fill: true, // => true to fill the area from the line to the x axis, false for (transparent) no fill fill: true, // => true to fill the area from the line to the x axis, false for (transparent) no fill
fillColor: null, // => fill color fillColor: null, // => fill color
fillOpacity: 0.4, // => opacity of the fill color, set to 1 for a solid fill, 0 hides the fill fillOpacity: 0.4, // => opacity of the fill color, set to 1 for a solid fill, 0 hides the fill
centered: true // => center the bars to their x axis value centered: true // => center the bars to their x axis value
}, },
/**  
* Draws gantt series in the canvas element.  
* @param {Object} series - Series with options.gantt.show = true.  
*/  
draw: function(series) {  
var ctx = this.ctx,  
bw = series.gantt.barWidth,  
lw = Math.min(series.gantt.lineWidth, bw);  
   
ctx.save();  
ctx.translate(this.plotOffset.left, this.plotOffset.top);  
ctx.lineJoin = 'miter';  
   
/** /**
* @todo linewidth not interpreted the right way. * Draws gantt series in the canvas element.
  * @param {Object} series - Series with options.gantt.show = true.
*/ */
ctx.lineWidth = lw; draw: function (series) {
ctx.strokeStyle = series.color; var ctx = this.ctx,
  bw = series.gantt.barWidth,
ctx.save(); lw = Math.min(series.gantt.lineWidth, bw);
this.gantt.plotShadows(series, bw, 0, series.gantt.fill);  
ctx.restore(); ctx.save();
  ctx.translate(this.plotOffset.left, this.plotOffset.top);
if(series.gantt.fill){ ctx.lineJoin = 'miter';
var color = series.gantt.fillColor || series.color;  
ctx.fillStyle = this.processColor(color, {opacity: series.gantt.fillOpacity}); /**
  * @todo linewidth not interpreted the right way.
  */
  ctx.lineWidth = lw;
  ctx.strokeStyle = series.color;
   
  ctx.save();
  this.gantt.plotShadows(series, bw, 0, series.gantt.fill);
  ctx.restore();
   
  if (series.gantt.fill) {
  var color = series.gantt.fillColor || series.color;
  ctx.fillStyle = this.processColor(color, {opacity: series.gantt.fillOpacity});
  }
   
  this.gantt.plot(series, bw, 0, series.gantt.fill);
  ctx.restore();
  },
  plot: function (series, barWidth, offset, fill) {
  var data = series.data;
  if (data.length < 1) return;
   
  var xa = series.xaxis,
  ya = series.yaxis,
  ctx = this.ctx, i;
   
  for (i = 0; i < data.length; i++) {
  var y = data[i][0],
  s = data[i][1],
  d = data[i][2],
  drawLeft = true, drawTop = true, drawRight = true;
   
  if (s === null || d === null) continue;
   
  var left = s,
  right = s + d,
  bottom = y - (series.gantt.centered ? barWidth / 2 : 0),
  top = y + barWidth - (series.gantt.centered ? barWidth / 2 : 0);
   
  if (right < xa.min || left > xa.max || top < ya.min || bottom > ya.max)
  continue;
   
  if (left < xa.min) {
  left = xa.min;
  drawLeft = false;
  }
   
  if (right > xa.max) {
  right = xa.max;
  if (xa.lastSerie != series)
  drawTop = false;
  }
   
  if (bottom < ya.min)
  bottom = ya.min;
   
  if (top > ya.max) {
  top = ya.max;
  if (ya.lastSerie != series)
  drawTop = false;
  }
   
  /**
  * Fill the bar.
  */
  if (fill) {
  ctx.beginPath();
  ctx.moveTo(xa.d2p(left), ya.d2p(bottom) + offset);
  ctx.lineTo(xa.d2p(left), ya.d2p(top) + offset);
  ctx.lineTo(xa.d2p(right), ya.d2p(top) + offset);
  ctx.lineTo(xa.d2p(right), ya.d2p(bottom) + offset);
  ctx.fill();
  ctx.closePath();
  }
   
  /**
  * Draw bar outline/border.
  */
  if (series.gantt.lineWidth && (drawLeft || drawRight || drawTop)) {
  ctx.beginPath();
  ctx.moveTo(xa.d2p(left), ya.d2p(bottom) + offset);
   
  ctx[drawLeft ? 'lineTo' : 'moveTo'](xa.d2p(left), ya.d2p(top) + offset);
  ctx[drawTop ? 'lineTo' : 'moveTo'](xa.d2p(right), ya.d2p(top) + offset);
  ctx[drawRight ? 'lineTo' : 'moveTo'](xa.d2p(right), ya.d2p(bottom) + offset);
   
  ctx.stroke();
  ctx.closePath();
  }
  }
  },
  plotShadows: function (series, barWidth, offset) {
  var data = series.data;
  if (data.length < 1) return;
   
  var i, y, s, d,
  xa = series.xaxis,
  ya = series.yaxis,
  ctx = this.ctx,
  sw = this.options.shadowSize;
   
  for (i = 0; i < data.length; i++) {
  y = data[i][0];
  s = data[i][1];
  d = data[i][2];
   
  if (s === null || d === null) continue;
   
  var left = s,
  right = s + d,
  bottom = y - (series.gantt.centered ? barWidth / 2 : 0),
  top = y + barWidth - (series.gantt.centered ? barWidth / 2 : 0);
   
  if (right < xa.min || left > xa.max || top < ya.min || bottom > ya.max)
  continue;
   
  if (left < xa.min) left = xa.min;
  if (right > xa.max) right = xa.max;
  if (bottom < ya.min) bottom = ya.min;
  if (top > ya.max) top = ya.max;
   
  var width = xa.d2p(right) - xa.d2p(left) - ((xa.d2p(right) + sw <= this.plotWidth) ? 0 : sw);
  var height = ya.d2p(bottom) - ya.d2p(top) - ((ya.d2p(bottom) + sw <= this.plotHeight) ? 0 : sw );
   
  ctx.fillStyle = 'rgba(0,0,0,0.05)';
  ctx.fillRect(Math.min(xa.d2p(left) + sw, this.plotWidth), Math.min(ya.d2p(top) + sw, this.plotHeight), width, height);
  }
  },
  extendXRange: function (axis) {
  if (axis.options.max === null) {
  var newmin = axis.min,
  newmax = axis.max,
  i, j, x, s, g,
  stackedSumsPos = {},
  stackedSumsNeg = {},
  lastSerie = null;
   
  for (i = 0; i < this.series.length; ++i) {
  s = this.series[i];
  g = s.gantt;
   
  if (g.show && s.xaxis == axis) {
  for (j = 0; j < s.data.length; j++) {
  if (g.show) {
  y = s.data[j][0] + '';
  stackedSumsPos[y] = Math.max((stackedSumsPos[y] || 0), s.data[j][1] + s.data[j][2]);
  lastSerie = s;
  }
  }
  for (j in stackedSumsPos) {
  newmax = Math.max(stackedSumsPos[j], newmax);
  }
  }
  }
  axis.lastSerie = lastSerie;
  axis.max = newmax;
  axis.min = newmin;
  }
  },
  extendYRange: function (axis) {
  if (axis.options.max === null) {
  var newmax = Number.MIN_VALUE,
  newmin = Number.MAX_VALUE,
  i, j, s, g,
  stackedSumsPos = {},
  stackedSumsNeg = {},
  lastSerie = null;
   
  for (i = 0; i < this.series.length; ++i) {
  s = this.series[i];
  g = s.gantt;
   
  if (g.show && !s.hide && s.yaxis == axis) {
  var datamax = Number.MIN_VALUE, datamin = Number.MAX_VALUE;
  for (j = 0; j < s.data.length; j++) {
  datamax = Math.max(datamax, s.data[j][0]);
  datamin = Math.min(datamin, s.data[j][0]);
  }
   
  if (g.centered) {
  newmax = Math.max(datamax + 0.5, newmax);
  newmin = Math.min(datamin - 0.5, newmin);
  }
  else {
  newmax = Math.max(datamax + 1, newmax);
  newmin = Math.min(datamin, newmin);
  }
  // For normal horizontal bars
  if (g.barWidth + datamax > newmax) {
  newmax = axis.max + g.barWidth;
  }
  }
  }
  axis.lastSerie = lastSerie;
  axis.max = newmax;
  axis.min = newmin;
  axis.tickSize = Flotr.getTickSize(axis.options.noTicks, newmin, newmax, axis.options.tickDecimals);
  }
} }
   
this.gantt.plot(series, bw, 0, series.gantt.fill);  
ctx.restore();  
},  
plot: function(series, barWidth, offset, fill){  
var data = series.data;  
if(data.length < 1) return;  
   
var xa = series.xaxis,  
ya = series.yaxis,  
ctx = this.ctx, i;  
   
for(i = 0; i < data.length; i++){  
var y = data[i][0],  
s = data[i][1],  
d = data[i][2],  
drawLeft = true, drawTop = true, drawRight = true;  
   
if (s === null || d === null) continue;  
   
var left = s,  
right = s + d,  
bottom = y - (series.gantt.centered ? barWidth/2 : 0),  
top = y + barWidth - (series.gantt.centered ? barWidth/2 : 0);  
   
if(right < xa.min || left > xa.max || top < ya.min || bottom > ya.max)  
continue;  
   
if(left < xa.min){  
left = xa.min;  
drawLeft = false;  
}  
   
if(right > xa.max){  
right = xa.max;  
if (xa.lastSerie != series)  
drawTop = false;  
}  
   
if(bottom < ya.min)  
bottom = ya.min;  
   
if(top > ya.max){  
top = ya.max;  
if (ya.lastSerie != series)  
drawTop = false;  
}  
   
/**  
* Fill the bar.  
*/  
if(fill){  
ctx.beginPath();  
ctx.moveTo(xa.d2p(left), ya.d2p(bottom) + offset);  
ctx.lineTo(xa.d2p(left), ya.d2p(top) + offset);  
ctx.lineTo(xa.d2p(right), ya.d2p(top) + offset);  
ctx.lineTo(xa.d2p(right), ya.d2p(bottom) + offset);  
ctx.fill();  
ctx.closePath();  
}  
   
/**  
* Draw bar outline/border.  
*/  
if(series.gantt.lineWidth && (drawLeft || drawRight || drawTop)){  
ctx.beginPath();  
ctx.moveTo(xa.d2p(left), ya.d2p(bottom) + offset);  
   
ctx[drawLeft ?'lineTo':'moveTo'](xa.d2p(left), ya.d2p(top) + offset);  
ctx[drawTop ?'lineTo':'moveTo'](xa.d2p(right), ya.d2p(top) + offset);  
ctx[drawRight?'lineTo':'moveTo'](xa.d2p(right), ya.d2p(bottom) + offset);  
   
ctx.stroke();  
ctx.closePath();  
}  
}  
},  
plotShadows: function(series, barWidth, offset){  
var data = series.data;  
if(data.length < 1) return;  
   
var i, y, s, d,  
xa = series.xaxis,  
ya = series.yaxis,  
ctx = this.ctx,  
sw = this.options.shadowSize;  
   
for(i = 0; i < data.length; i++){  
y = data[i][0];  
s = data[i][1];  
d = data[i][2];  
   
if (s === null || d === null) continue;  
   
var left = s,  
right = s + d,  
bottom = y - (series.gantt.centered ? barWidth/2 : 0),  
top = y + barWidth - (series.gantt.centered ? barWidth/2 : 0);  
   
if(right < xa.min || left > xa.max || top < ya.min || bottom > ya.max)  
continue;  
   
if(left < xa.min) left = xa.min;  
if(right > xa.max) right = xa.max;  
if(bottom < ya.min) bottom = ya.min;  
if(top > ya.max) top = ya.max;  
   
var width = xa.d2p(right)-xa.d2p(left)-((xa.d2p(right)+sw <= this.plotWidth) ? 0 : sw);  
var height = ya.d2p(bottom)-ya.d2p(top)-((ya.d2p(bottom)+sw <= this.plotHeight) ? 0 : sw );  
   
ctx.fillStyle = 'rgba(0,0,0,0.05)';  
ctx.fillRect(Math.min(xa.d2p(left)+sw, this.plotWidth), Math.min(ya.d2p(top)+sw, this.plotHeight), width, height);  
}  
},  
extendXRange: function(axis) {  
if(axis.options.max === null){  
var newmin = axis.min,  
newmax = axis.max,  
i, j, x, s, g,  
stackedSumsPos = {},  
stackedSumsNeg = {},  
lastSerie = null;  
   
for(i = 0; i < this.series.length; ++i){  
s = this.series[i];  
g = s.gantt;  
   
if(g.show && s.xaxis == axis) {  
for (j = 0; j < s.data.length; j++) {  
if (g.show) {  
y = s.data[j][0]+'';  
stackedSumsPos[y] = Math.max((stackedSumsPos[y] || 0), s.data[j][1]+s.data[j][2]);  
lastSerie = s;  
}  
}  
for (j in stackedSumsPos) {  
newmax = Math.max(stackedSumsPos[j], newmax);  
}  
}  
}  
axis.lastSerie = lastSerie;  
axis.max = newmax;  
axis.min = newmin;  
}  
},  
extendYRange: function(axis){  
if(axis.options.max === null){  
var newmax = Number.MIN_VALUE,  
newmin = Number.MAX_VALUE,  
i, j, s, g,  
stackedSumsPos = {},  
stackedSumsNeg = {},  
lastSerie = null;  
   
for(i = 0; i < this.series.length; ++i){  
s = this.series[i];  
g = s.gantt;  
   
if (g.show && !s.hide && s.yaxis == axis) {  
var datamax = Number.MIN_VALUE, datamin = Number.MAX_VALUE;  
for(j=0; j < s.data.length; j++){  
datamax = Math.max(datamax,s.data[j][0]);  
datamin = Math.min(datamin,s.data[j][0]);  
}  
   
if (g.centered) {  
newmax = Math.max(datamax + 0.5, newmax);  
newmin = Math.min(datamin - 0.5, newmin);  
}  
else {  
newmax = Math.max(datamax + 1, newmax);  
newmin = Math.min(datamin, newmin);  
}  
// For normal horizontal bars  
if (g.barWidth + datamax > newmax){  
newmax = axis.max + g.barWidth;  
}  
}  
}  
axis.lastSerie = lastSerie;  
axis.max = newmax;  
axis.min = newmin;  
axis.tickSize = Flotr.getTickSize(axis.options.noTicks, newmin, newmax, axis.options.tickDecimals);  
}  
}  
}); });
   
/** Markers **/ /** Markers **/
/** /**
* Formats the marker labels. * Formats the marker labels.
* @param {Object} obj - Marker value Object {x:..,y:..} * @param {Object} obj - Marker value Object {x:..,y:..}
* @return {String} Formatted marker string * @return {String} Formatted marker string
*/ */
(function () { (function () {
   
Flotr.defaultMarkerFormatter = function(obj){ Flotr.defaultMarkerFormatter = function (obj) {
return (Math.round(obj.y*100)/100)+''; return (Math.round(obj.y * 100) / 100) + '';
}; };
   
Flotr.addType('markers', { Flotr.addType('markers', {
options: { options: {
show: false, // => setting to true will show markers, false will hide show: false, // => setting to true will show markers, false will hide
lineWidth: 1, // => line width of the rectangle around the marker lineWidth: 1, // => line width of the rectangle around the marker
color: '#000000', // => text color color: '#000000', // => text color
fill: false, // => fill or not the marekers' rectangles fill: false, // => fill or not the marekers' rectangles
fillColor: "#FFFFFF", // => fill color fillColor: "#FFFFFF", // => fill color
fillOpacity: 0.4, // => fill opacity fillOpacity: 0.4, // => fill opacity
stroke: false, // => draw the rectangle around the markers stroke: false, // => draw the rectangle around the markers
position: 'ct', // => the markers position (vertical align: b, m, t, horizontal align: l, c, r) position: 'ct', // => the markers position (vertical align: b, m, t, horizontal align: l, c, r)
verticalMargin: 0, // => the margin between the point and the text. verticalMargin: 0, // => the margin between the point and the text.
labelFormatter: Flotr.defaultMarkerFormatter, labelFormatter: Flotr.defaultMarkerFormatter,
fontSize: Flotr.defaultOptions.fontSize, fontSize: Flotr.defaultOptions.fontSize,
stacked: false, // => true if markers should be stacked stacked: false, // => true if markers should be stacked
stackingType: 'b', // => define staching behavior, (b- bars like, a - area like) (see Issue 125 for details) stackingType: 'b', // => define staching behavior, (b- bars like, a - area like) (see Issue 125 for details)
horizontal: false // => true if markers should be horizontal (For now only in a case on horizontal stacked bars, stacks should be calculated horizontaly) horizontal: false // => true if markers should be horizontal (For now only in a case on horizontal stacked bars, stacks should be calculated horizontaly)
}, },
   
// TODO test stacked markers. // TODO test stacked markers.
stack : { stack: {
positive : [], positive: [],
negative : [], negative: [],
values : [] values: []
}, },
   
draw : function (options) { draw: function (options) {
   
var var
data = options.data, data = options.data,
context = options.context, context = options.context,
stack = options.stacked ? options.stack : false, stack = options.stacked ? options.stack : false,
stackType = options.stackingType, stackType = options.stackingType,
stackOffsetNeg, stackOffsetNeg,
stackOffsetPos, stackOffsetPos,
stackOffset, stackOffset,
i, x, y, label; i, x, y, label;
   
context.save(); context.save();
context.lineJoin = 'round'; context.lineJoin = 'round';
context.lineWidth = options.lineWidth; context.lineWidth = options.lineWidth;
context.strokeStyle = 'rgba(0,0,0,0.5)'; context.strokeStyle = 'rgba(0,0,0,0.5)';
context.fillStyle = options.fillStyle; context.fillStyle = options.fillStyle;
   
function stackPos (a, b) { function stackPos(a, b) {
stackOffsetPos = stack.negative[a] || 0; stackOffsetPos = stack.negative[a] || 0;
stackOffsetNeg = stack.positive[a] || 0; stackOffsetNeg = stack.positive[a] || 0;
if (b > 0) { if (b > 0) {
stack.positive[a] = stackOffsetPos + b; stack.positive[a] = stackOffsetPos + b;
return stackOffsetPos + b; return stackOffsetPos + b;
} else { } else {
stack.negative[a] = stackOffsetNeg + b; stack.negative[a] = stackOffsetNeg + b;
return stackOffsetNeg + b; return stackOffsetNeg + b;
} }
  }
   
  for (i = 0; i < data.length; ++i) {
   
  x = data[i][0];
  y = data[i][1];
   
  if (stack) {
  if (stackType == 'b') {
  if (options.horizontal) y = stackPos(y, x);
  else x = stackPos(x, y);
  } else if (stackType == 'a') {
  stackOffset = stack.values[x] || 0;
  stack.values[x] = stackOffset + y;
  y = stackOffset + y;
  }
  }
   
  label = options.labelFormatter({x: x, y: y, index: i, data: data});
  this.plot(options.xScale(x), options.yScale(y), label, options);
  }
  context.restore();
  },
  plot: function (x, y, label, options) {
  var context = options.context;
  if (isImage(label) && !label.complete) {
  throw 'Marker image not loaded.';
  } else {
  this._plot(x, y, label, options);
  }
  },
   
  _plot: function (x, y, label, options) {
  var context = options.context,
  margin = 2,
  left = x,
  top = y,
  dim;
   
  if (isImage(label))
  dim = {height: label.height, width: label.width};
  else
  dim = options.text.canvas(label);
   
  dim.width = Math.floor(dim.width + margin * 2);
  dim.height = Math.floor(dim.height + margin * 2);
   
  if (options.position.indexOf('c') != -1) left -= dim.width / 2 + margin;
  else if (options.position.indexOf('l') != -1) left -= dim.width;
   
  if (options.position.indexOf('m') != -1) top -= dim.height / 2 + margin;
  else if (options.position.indexOf('t') != -1) top -= dim.height + options.verticalMargin;
  else top += options.verticalMargin;
   
  left = Math.floor(left) + 0.5;
  top = Math.floor(top) + 0.5;
   
  if (options.fill)
  context.fillRect(left, top, dim.width, dim.height);
   
  if (options.stroke)
  context.strokeRect(left, top, dim.width, dim.height);
   
  if (isImage(label))
  context.drawImage(label, left + margin, top + margin);
  else
  Flotr.drawText(context, label, left + margin, top + margin, {textBaseline: 'top', textAlign: 'left', size: options.fontSize, color: options.color});
  }
  });
   
  function isImage(i) {
  return typeof i === 'object' && i.constructor && (Image ? true : i.constructor === Image);
} }
   
for (i = 0; i < data.length; ++i) {  
   
x = data[i][0];  
y = data[i][1];  
   
if (stack) {  
if (stackType == 'b') {  
if (options.horizontal) y = stackPos(y, x);  
else x = stackPos(x, y);  
} else if (stackType == 'a') {  
stackOffset = stack.values[x] || 0;  
stack.values[x] = stackOffset + y;  
y = stackOffset + y;  
}  
}  
   
label = options.labelFormatter({x: x, y: y, index: i, data : data});  
this.plot(options.xScale(x), options.yScale(y), label, options);  
}  
context.restore();  
},  
plot: function(x, y, label, options) {  
var context = options.context;  
if (isImage(label) && !label.complete) {  
throw 'Marker image not loaded.';  
} else {  
this._plot(x, y, label, options);  
}  
},  
   
_plot: function(x, y, label, options) {  
var context = options.context,  
margin = 2,  
left = x,  
top = y,  
dim;  
   
if (isImage(label))  
dim = {height : label.height, width: label.width};  
else  
dim = options.text.canvas(label);  
   
dim.width = Math.floor(dim.width+margin*2);  
dim.height = Math.floor(dim.height+margin*2);  
   
if (options.position.indexOf('c') != -1) left -= dim.width/2 + margin;  
else if (options.position.indexOf('l') != -1) left -= dim.width;  
   
if (options.position.indexOf('m') != -1) top -= dim.height/2 + margin;  
else if (options.position.indexOf('t') != -1) top -= dim.height + options.verticalMargin;  
else top += options.verticalMargin;  
   
left = Math.floor(left)+0.5;  
top = Math.floor(top)+0.5;  
   
if(options.fill)  
context.fillRect(left, top, dim.width, dim.height);  
   
if(options.stroke)  
context.strokeRect(left, top, dim.width, dim.height);  
   
if (isImage(label))  
context.drawImage(label, left+margin, top+margin);  
else  
Flotr.drawText(context, label, left+margin, top+margin, {textBaseline: 'top', textAlign: 'left', size: options.fontSize, color: options.color});  
}  
});  
   
function isImage (i) {  
return typeof i === 'object' && i.constructor && (Image ? true : i.constructor === Image);  
}  
   
})(); })();
   
/** Pie **/ /** Pie **/
/** /**
* Formats the pies labels. * Formats the pies labels.
* @param {Object} slice - Slice object * @param {Object} slice - Slice object
* @return {String} Formatted pie label string * @return {String} Formatted pie label string
*/ */
(function () { (function () {
   
var  
_ = Flotr._;  
   
Flotr.defaultPieLabelFormatter = function (total, value) {  
return (100 * value / total).toFixed(2)+'%';  
};  
   
Flotr.addType('pie', {  
options: {  
show: false, // => setting to true will show bars, false will hide  
lineWidth: 1, // => in pixels  
fill: true, // => true to fill the area from the line to the x axis, false for (transparent) no fill  
fillColor: null, // => fill color  
fillOpacity: 0.6, // => opacity of the fill color, set to 1 for a solid fill, 0 hides the fill  
explode: 6, // => the number of pixels the splices will be far from the center  
sizeRatio: 0.6, // => the size ratio of the pie relative to the plot  
startAngle: Math.PI/4, // => the first slice start angle  
labelFormatter: Flotr.defaultPieLabelFormatter,  
pie3D: false, // => whether to draw the pie in 3 dimenstions or not (ineffective)  
pie3DviewAngle: (Math.PI/2 * 0.8),  
pie3DspliceThickness: 20  
},  
   
draw : function (options) {  
   
// TODO 3D charts what?  
   
var var
data = options.data, _ = Flotr._;
context = options.context,  
canvas = context.canvas, Flotr.defaultPieLabelFormatter = function (total, value) {
lineWidth = options.lineWidth, return (100 * value / total).toFixed(2) + '%';
shadowSize = options.shadowSize, };
sizeRatio = options.sizeRatio,  
height = options.height, Flotr.addType('pie', {
width = options.width, options: {
explode = options.explode, show: false, // => setting to true will show bars, false will hide
color = options.color, lineWidth: 1, // => in pixels
fill = options.fill, fill: true, // => true to fill the area from the line to the x axis, false for (transparent) no fill
fillStyle = options.fillStyle, fillColor: null, // => fill color
radius = Math.min(canvas.width, canvas.height) * sizeRatio / 2, fillOpacity: 0.6, // => opacity of the fill color, set to 1 for a solid fill, 0 hides the fill
value = data[0][1], explode: 6, // => the number of pixels the splices will be far from the center
html = [], sizeRatio: 0.6, // => the size ratio of the pie relative to the plot
vScale = 1,//Math.cos(series.pie.viewAngle); startAngle: Math.PI / 4, // => the first slice start angle
measure = Math.PI * 2 * value / this.total, labelFormatter: Flotr.defaultPieLabelFormatter,
startAngle = this.startAngle || (2 * Math.PI * options.startAngle), // TODO: this initial startAngle is already in radians (fixing will be test-unstable) pie3D: false, // => whether to draw the pie in 3 dimenstions or not (ineffective)
endAngle = startAngle + measure, pie3DviewAngle: (Math.PI / 2 * 0.8),
bisection = startAngle + measure / 2, pie3DspliceThickness: 20
label = options.labelFormatter(this.total, value), },
//plotTickness = Math.sin(series.pie.viewAngle)*series.pie.spliceThickness / vScale;  
explodeCoeff = explode + radius + 4, draw: function (options) {
distX = Math.cos(bisection) * explodeCoeff,  
distY = Math.sin(bisection) * explodeCoeff, // TODO 3D charts what?
textAlign = distX < 0 ? 'right' : 'left',  
textBaseline = distY > 0 ? 'top' : 'bottom', var
style, data = options.data,
x, y, context = options.context,
distX, distY; canvas = context.canvas,
  lineWidth = options.lineWidth,
context.save(); shadowSize = options.shadowSize,
context.translate(width / 2, height / 2); sizeRatio = options.sizeRatio,
context.scale(1, vScale); height = options.height,
  width = options.width,
x = Math.cos(bisection) * explode; explode = options.explode,
y = Math.sin(bisection) * explode; color = options.color,
  fill = options.fill,
// Shadows fillStyle = options.fillStyle,
if (shadowSize > 0) { radius = Math.min(canvas.width, canvas.height) * sizeRatio / 2,
this.plotSlice(x + shadowSize, y + shadowSize, radius, startAngle, endAngle, context); value = data[0][1],
if (fill) { html = [],
context.fillStyle = 'rgba(0,0,0,0.1)'; vScale = 1,//Math.cos(series.pie.viewAngle);
context.fill(); measure = Math.PI * 2 * value / this.total,
} startAngle = this.startAngle || (2 * Math.PI * options.startAngle), // TODO: this initial startAngle is already in radians (fixing will be test-unstable)
} endAngle = startAngle + measure,
  bisection = startAngle + measure / 2,
this.plotSlice(x, y, radius, startAngle, endAngle, context); label = options.labelFormatter(this.total, value),
if (fill) { //plotTickness = Math.sin(series.pie.viewAngle)*series.pie.spliceThickness / vScale;
context.fillStyle = fillStyle; explodeCoeff = explode + radius + 4,
context.fill(); distX = Math.cos(bisection) * explodeCoeff,
} distY = Math.sin(bisection) * explodeCoeff,
context.lineWidth = lineWidth; textAlign = distX < 0 ? 'right' : 'left',
context.strokeStyle = color; textBaseline = distY > 0 ? 'top' : 'bottom',
context.stroke(); style,
  x, y,
style = { distX, distY;
size : options.fontSize * 1.2,  
color : options.fontColor, context.save();
weight : 1.5 context.translate(width / 2, height / 2);
}; context.scale(1, vScale);
   
if (label) { x = Math.cos(bisection) * explode;
if (options.htmlText || !options.textEnabled) { y = Math.sin(bisection) * explode;
divStyle = 'position:absolute;' + textBaseline + ':' + (height / 2 + (textBaseline === 'top' ? distY : -distY)) + 'px;';  
divStyle += textAlign + ':' + (width / 2 + (textAlign === 'right' ? -distX : distX)) + 'px;'; // Shadows
html.push('<div style="', divStyle, '" class="flotr-grid-label">', label, '</div>'); if (shadowSize > 0) {
} this.plotSlice(x + shadowSize, y + shadowSize, radius, startAngle, endAngle, context);
else { if (fill) {
style.textAlign = textAlign; context.fillStyle = 'rgba(0,0,0,0.1)';
style.textBaseline = textBaseline; context.fill();
Flotr.drawText(context, label, distX, distY, style); }
} }
}  
  this.plotSlice(x, y, radius, startAngle, endAngle, context);
if (options.htmlText || !options.textEnabled) { if (fill) {
var div = Flotr.DOM.node('<div style="color:' + options.fontColor + '" class="flotr-labels"></div>'); context.fillStyle = fillStyle;
Flotr.DOM.insert(div, html.join('')); context.fill();
Flotr.DOM.insert(options.element, div); }
} context.lineWidth = lineWidth;
  context.strokeStyle = color;
context.restore(); context.stroke();
   
// New start angle style = {
this.startAngle = endAngle; size: options.fontSize * 1.2,
this.slices = this.slices || []; color: options.fontColor,
this.slices.push({ weight: 1.5
radius : Math.min(canvas.width, canvas.height) * sizeRatio / 2, };
x : x,  
y : y, if (label) {
explode : explode, if (options.htmlText || !options.textEnabled) {
start : startAngle, divStyle = 'position:absolute;' + textBaseline + ':' + (height / 2 + (textBaseline === 'top' ? distY : -distY)) + 'px;';
end : endAngle divStyle += textAlign + ':' + (width / 2 + (textAlign === 'right' ? -distX : distX)) + 'px;';
  html.push('<div style="', divStyle, '" class="flotr-grid-label">', label, '</div>');
  }
  else {
  style.textAlign = textAlign;
  style.textBaseline = textBaseline;
  Flotr.drawText(context, label, distX, distY, style);
  }
  }
   
  if (options.htmlText || !options.textEnabled) {
  var div = Flotr.DOM.node('<div style="color:' + options.fontColor + '" class="flotr-labels"></div>');
  Flotr.DOM.insert(div, html.join(''));
  Flotr.DOM.insert(options.element, div);
  }
   
  context.restore();
   
  // New start angle
  this.startAngle = endAngle;
  this.slices = this.slices || [];
  this.slices.push({
  radius: Math.min(canvas.width, canvas.height) * sizeRatio / 2,
  x: x,
  y: y,
  explode: explode,
  start: startAngle,
  end: endAngle
  });
  },
  plotSlice: function (x, y, radius, startAngle, endAngle, context) {
  context.beginPath();
  context.moveTo(x, y);
  context.arc(x, y, radius, startAngle, endAngle, false);
  context.lineTo(x, y);
  context.closePath();
  },
  hit: function (options) {
   
  var
  data = options.data[0],
  args = options.args,
  index = options.index,
  mouse = args[0],
  n = args[1],
  slice = this.slices[index],
  x = mouse.relX - options.width / 2,
  y = mouse.relY - options.height / 2,
  r = Math.sqrt(x * x + y * y),
  theta = Math.atan(y / x),
  circle = Math.PI * 2,
  explode = slice.explode || options.explode,
  start = slice.start % circle,
  end = slice.end % circle;
   
  if (x < 0) {
  theta += Math.PI;
  } else if (x > 0 && y < 0) {
  theta += circle;
  }
   
  if (r < slice.radius + explode && r > explode) {
  if ((start >= end && (theta < end || theta > start)) ||
  (theta > start && theta < end)) {
   
  // TODO Decouple this from hit plugin (chart shouldn't know what n means)
  n.x = data[0];
  n.y = data[1];
  n.sAngle = start;
  n.eAngle = end;
  n.index = 0;
  n.seriesIndex = index;
  n.fraction = data[1] / this.total;
  }
  }
  },
  drawHit: function (options) {
  var
  context = options.context,
  slice = this.slices[options.args.seriesIndex];
   
  context.save();
  context.translate(options.width / 2, options.height / 2);
  this.plotSlice(slice.x, slice.y, slice.radius, slice.start, slice.end, context);
  context.stroke();
  context.restore();
  },
  clearHit: function (options) {
  var
  context = options.context,
  slice = this.slices[options.args.seriesIndex],
  padding = 2 * options.lineWidth,
  radius = slice.radius + padding;
   
  context.save();
  context.translate(options.width / 2, options.height / 2);
  context.clearRect(
  slice.x - radius,
  slice.y - radius,
  2 * radius + padding,
  2 * radius + padding
  );
  context.restore();
  },
  extendYRange: function (axis, data) {
  this.total = (this.total || 0) + data[0][1];
  }
}); });
},  
plotSlice : function (x, y, radius, startAngle, endAngle, context) {  
context.beginPath();  
context.moveTo(x, y);  
context.arc(x, y, radius, startAngle, endAngle, false);  
context.lineTo(x, y);  
context.closePath();  
},  
hit : function (options) {  
   
var  
data = options.data[0],  
args = options.args,  
index = options.index,  
mouse = args[0],  
n = args[1],  
slice = this.slices[index],  
x = mouse.relX - options.width / 2,  
y = mouse.relY - options.height / 2,  
r = Math.sqrt(x * x + y * y),  
theta = Math.atan(y / x),  
circle = Math.PI * 2,  
explode = slice.explode || options.explode,  
start = slice.start % circle,  
end = slice.end % circle;  
   
if (x < 0) {  
theta += Math.PI;  
} else if (x > 0 && y < 0) {  
theta += circle;  
}  
   
if (r < slice.radius + explode && r > explode) {  
if ((start >= end && (theta < end || theta > start)) ||  
(theta > start && theta < end)) {  
   
// TODO Decouple this from hit plugin (chart shouldn't know what n means)  
n.x = data[0];  
n.y = data[1];  
n.sAngle = start;  
n.eAngle = end;  
n.index = 0;  
n.seriesIndex = index;  
n.fraction = data[1] / this.total;  
}  
}  
},  
drawHit: function (options) {  
var  
context = options.context,  
slice = this.slices[options.args.seriesIndex];  
   
context.save();  
context.translate(options.width / 2, options.height / 2);  
this.plotSlice(slice.x, slice.y, slice.radius, slice.start, slice.end, context);  
context.stroke();  
context.restore();  
},  
clearHit : function (options) {  
var  
context = options.context,  
slice = this.slices[options.args.seriesIndex],  
padding = 2 * options.lineWidth,  
radius = slice.radius + padding;  
   
context.save();  
context.translate(options.width / 2, options.height / 2);  
context.clearRect(  
slice.x - radius,  
slice.y - radius,  
2 * radius + padding,  
2 * radius + padding  
);  
context.restore();  
},  
extendYRange : function (axis, data) {  
this.total = (this.total || 0) + data[0][1];  
}  
});  
})(); })();
   
/** Points **/ /** Points **/
Flotr.addType('points', { Flotr.addType('points', {
options: { options: {
show: false, // => setting to true will show points, false will hide show: false, // => setting to true will show points, false will hide
radius: 3, // => point radius (pixels) radius: 3, // => point radius (pixels)
lineWidth: 2, // => line width in pixels lineWidth: 2, // => line width in pixels
fill: true, // => true to fill the points with a color, false for (transparent) no fill fill: true, // => true to fill the points with a color, false for (transparent) no fill
fillColor: '#FFFFFF', // => fill color fillColor: '#FFFFFF', // => fill color
fillOpacity: 0.4 // => opacity of color inside the points fillOpacity: 0.4 // => opacity of color inside the points
}, },
   
draw : function (options) { draw: function (options) {
var var
context = options.context, context = options.context,
lineWidth = options.lineWidth, lineWidth = options.lineWidth,
shadowSize = options.shadowSize; shadowSize = options.shadowSize;
   
context.save(); context.save();
   
if (shadowSize > 0) { if (shadowSize > 0) {
context.lineWidth = shadowSize / 2; context.lineWidth = shadowSize / 2;
   
context.strokeStyle = 'rgba(0,0,0,0.1)'; context.strokeStyle = 'rgba(0,0,0,0.1)';
this.plot(options, shadowSize / 2 + context.lineWidth / 2); this.plot(options, shadowSize / 2 + context.lineWidth / 2);
   
context.strokeStyle = 'rgba(0,0,0,0.2)'; context.strokeStyle = 'rgba(0,0,0,0.2)';
this.plot(options, context.lineWidth / 2); this.plot(options, context.lineWidth / 2);
  }
   
  context.lineWidth = options.lineWidth;
  context.strokeStyle = options.color;
  context.fillStyle = options.fillColor || options.color;
   
  this.plot(options);
  context.restore();
  },
   
  plot: function (options, offset) {
  var
  data = options.data,
  context = options.context,
  xScale = options.xScale,
  yScale = options.yScale,
  i, x, y;
   
  for (i = data.length - 1; i > -1; --i) {
  y = data[i][1];
  if (y === null) continue;
   
  x = xScale(data[i][0]);
  y = yScale(y);
   
  if (x < 0 || x > options.width || y < 0 || y > options.height) continue;
   
  context.beginPath();
  if (offset) {
  context.arc(x, y + offset, options.radius, 0, Math.PI, false);
  } else {
  context.arc(x, y, options.radius, 0, 2 * Math.PI, true);
  if (options.fill) context.fill();
  }
  context.stroke();
  context.closePath();
  }
} }
   
context.lineWidth = options.lineWidth;  
context.strokeStyle = options.color;  
context.fillStyle = options.fillColor || options.color;  
   
this.plot(options);  
context.restore();  
},  
   
plot : function (options, offset) {  
var  
data = options.data,  
context = options.context,  
xScale = options.xScale,  
yScale = options.yScale,  
i, x, y;  
   
for (i = data.length - 1; i > -1; --i) {  
y = data[i][1];  
if (y === null) continue;  
   
x = xScale(data[i][0]);  
y = yScale(y);  
   
if (x < 0 || x > options.width || y < 0 || y > options.height) continue;  
   
context.beginPath();  
if (offset) {  
context.arc(x, y + offset, options.radius, 0, Math.PI, false);  
} else {  
context.arc(x, y, options.radius, 0, 2 * Math.PI, true);  
if (options.fill) context.fill();  
}  
context.stroke();  
context.closePath();  
}  
}  
}); });
   
/** Radar **/ /** Radar **/
Flotr.addType('radar', { Flotr.addType('radar', {
options: { options: {
show: false, // => setting to true will show radar chart, false will hide show: false, // => setting to true will show radar chart, false will hide
lineWidth: 2, // => line width in pixels lineWidth: 2, // => line width in pixels
fill: true, // => true to fill the area from the line to the x axis, false for (transparent) no fill fill: true, // => true to fill the area from the line to the x axis, false for (transparent) no fill
fillOpacity: 0.4, // => opacity of the fill color, set to 1 for a solid fill, 0 hides the fill fillOpacity: 0.4, // => opacity of the fill color, set to 1 for a solid fill, 0 hides the fill
radiusRatio: 0.90 // => ratio of the radar, against the plot size radiusRatio: 0.90 // => ratio of the radar, against the plot size
}, },
draw : function (options) { draw: function (options) {
  var
  context = options.context,
  shadowSize = options.shadowSize;
   
  context.save();
  context.translate(options.width / 2, options.height / 2);
  context.lineWidth = options.lineWidth;
   
  // Shadow
  context.fillStyle = 'rgba(0,0,0,0.05)';
  context.strokeStyle = 'rgba(0,0,0,0.05)';
  this.plot(options, shadowSize / 2);
  context.strokeStyle = 'rgba(0,0,0,0.1)';
  this.plot(options, shadowSize / 4);
   
  // Chart
  context.strokeStyle = options.color;
  context.fillStyle = options.fillStyle;
  this.plot(options);
   
  context.restore();
  },
  plot: function (options, offset) {
  var
  data = options.data,
  context = options.context,
  radius = Math.min(options.height, options.width) * options.radiusRatio / 2,
  step = 2 * Math.PI / data.length,
  angle = -Math.PI / 2,
  i, ratio;
   
  offset = offset || 0;
   
  context.beginPath();
  for (i = 0; i < data.length; ++i) {
  ratio = data[i][1] / this.max;
   
  context[i === 0 ? 'moveTo' : 'lineTo'](
  Math.cos(i * step + angle) * radius * ratio + offset,
  Math.sin(i * step + angle) * radius * ratio + offset
  );
  }
  context.closePath();
  if (options.fill) context.fill();
  context.stroke();
  },
  extendYRange: function (axis, data) {
  this.max = Math.max(axis.max, this.max || -Number.MAX_VALUE);
  }
  });
   
  Flotr.addType('timeline', {
  options: {
  show: false,
  lineWidth: 1,
  barWidth: 0.2,
  fill: true,
  fillColor: null,
  fillOpacity: 0.4,
  centered: true
  },
   
  draw: function (options) {
   
  var
  context = options.context;
   
  context.save();
  context.lineJoin = 'miter';
  context.lineWidth = options.lineWidth;
  context.strokeStyle = options.color;
  context.fillStyle = options.fillStyle;
   
  this.plot(options);
   
  context.restore();
  },
   
  plot: function (options) {
   
  var
  data = options.data,
  context = options.context,
  xScale = options.xScale,
  yScale = options.yScale,
  barWidth = options.barWidth,
  lineWidth = options.lineWidth,
  i;
   
  Flotr._.each(data, function (timeline) {
   
  var
  x = timeline[0],
  y = timeline[1],
  w = timeline[2],
  h = barWidth,
   
  xt = Math.ceil(xScale(x)),
  wt = Math.ceil(xScale(x + w)) - xt,
  yt = Math.round(yScale(y)),
  ht = Math.round(yScale(y - h)) - yt,
   
  x0 = xt - lineWidth / 2,
  y0 = Math.round(yt - ht / 2) - lineWidth / 2;
   
  context.strokeRect(x0, y0, wt, ht);
  context.fillRect(x0, y0, wt, ht);
   
  });
  },
   
  extendRange: function (series) {
   
  var
  data = series.data,
  xa = series.xaxis,
  ya = series.yaxis,
  w = series.timeline.barWidth;
   
  if (xa.options.min === null)
  xa.min = xa.datamin - w / 2;
   
  if (xa.options.max === null) {
   
  var
  max = xa.max;
   
  Flotr._.each(data, function (timeline) {
  max = Math.max(max, timeline[0] + timeline[2]);
  }, this);
   
  xa.max = max + w / 2;
  }
   
  if (ya.options.min === null)
  ya.min = ya.datamin - w;
  if (ya.options.min === null)
  ya.max = ya.datamax + w;
  }
   
  });
   
  (function () {
   
  var D = Flotr.DOM;
   
  Flotr.addPlugin('crosshair', {
  options: {
  mode: null, // => one of null, 'x', 'y' or 'xy'
  color: '#FF0000', // => crosshair color
  hideCursor: true // => hide the cursor when the crosshair is shown
  },
  callbacks: {
  'flotr:mousemove': function (e, pos) {
  if (this.options.crosshair.mode) {
  this.crosshair.clearCrosshair();
  this.crosshair.drawCrosshair(pos);
  }
  }
  },
  /**
  * Draws the selection box.
  */
  drawCrosshair: function (pos) {
  var octx = this.octx,
  options = this.options.crosshair,
  plotOffset = this.plotOffset,
  x = plotOffset.left + pos.relX + 0.5,
  y = plotOffset.top + pos.relY + 0.5;
   
  if (pos.relX < 0 || pos.relY < 0 || pos.relX > this.plotWidth || pos.relY > this.plotHeight) {
  this.el.style.cursor = null;
  D.removeClass(this.el, 'flotr-crosshair');
  return;
  }
   
  if (options.hideCursor) {
  this.el.style.cursor = 'none';
  D.addClass(this.el, 'flotr-crosshair');
  }
   
  octx.save();
  octx.strokeStyle = options.color;
  octx.lineWidth = 1;
  octx.beginPath();
   
  if (options.mode.indexOf('x') != -1) {
  octx.moveTo(x, plotOffset.top);
  octx.lineTo(x, plotOffset.top + this.plotHeight);
  }
   
  if (options.mode.indexOf('y') != -1) {
  octx.moveTo(plotOffset.left, y);
  octx.lineTo(plotOffset.left + this.plotWidth, y);
  }
   
  octx.stroke();
  octx.restore();
  },
  /**
  * Removes the selection box from the overlay canvas.
  */
  clearCrosshair: function () {
   
  var
  plotOffset = this.plotOffset,
  position = this.lastMousePos,
  context = this.octx;
   
  if (position) {
  context.clearRect(
  position.relX + plotOffset.left,
  plotOffset.top,
  1,
  this.plotHeight + 1
  );
  context.clearRect(
  plotOffset.left,
  position.relY + plotOffset.top,
  this.plotWidth + 1,
  1
  );
  }
  }
  });
  })();
   
  (function () {
   
var var
context = options.context, D = Flotr.DOM,
shadowSize = options.shadowSize; _ = Flotr._;
   
context.save(); function getImage(type, canvas, width, height) {
context.translate(options.width / 2, options.height / 2);  
context.lineWidth = options.lineWidth; // TODO add scaling for w / h
  var
// Shadow mime = 'image/' + type,
context.fillStyle = 'rgba(0,0,0,0.05)'; data = canvas.toDataURL(mime),
context.strokeStyle = 'rgba(0,0,0,0.05)'; image = new Image();
this.plot(options, shadowSize / 2); image.src = data;
context.strokeStyle = 'rgba(0,0,0,0.1)'; return image;
this.plot(options, shadowSize / 4); }
   
// Chart Flotr.addPlugin('download', {
context.strokeStyle = options.color;  
context.fillStyle = options.fillStyle; saveImage: function (type, width, height, replaceCanvas) {
this.plot(options); var image = null;
  if (Flotr.isIE && Flotr.isIE < 9) {
context.restore(); image = '<html><body>' + this.canvas.firstChild.innerHTML + '</body></html>';
}, return window.open().document.write(image);
plot : function (options, offset) { }
   
  if (type !== 'jpeg' && type !== 'png') return;
   
  image = getImage(type, this.canvas, width, height);
   
  if (_.isElement(image) && replaceCanvas) {
  this.download.restoreCanvas();
  D.hide(this.canvas);
  D.hide(this.overlay);
  D.setStyles({position: 'absolute'});
  D.insert(this.el, image);
  this.saveImageElement = image;
  } else {
  return window.open(image.src);
  }
  },
   
  restoreCanvas: function () {
  D.show(this.canvas);
  D.show(this.overlay);
  if (this.saveImageElement) this.el.removeChild(this.saveImageElement);
  this.saveImageElement = null;
  }
  });
   
  })();
   
  (function () {
   
  var E = Flotr.EventAdapter,
  _ = Flotr._;
   
  Flotr.addPlugin('graphGrid', {
   
  callbacks: {
  'flotr:beforedraw': function () {
  this.graphGrid.drawGrid();
  },
  'flotr:afterdraw': function () {
  this.graphGrid.drawOutline();
  }
  },
   
  drawGrid: function () {
   
  var
  ctx = this.ctx,
  options = this.options,
  grid = options.grid,
  verticalLines = grid.verticalLines,
  horizontalLines = grid.horizontalLines,
  minorVerticalLines = grid.minorVerticalLines,
  minorHorizontalLines = grid.minorHorizontalLines,
  plotHeight = this.plotHeight,
  plotWidth = this.plotWidth,
  a, v, i, j;
   
  if (verticalLines || minorVerticalLines ||
  horizontalLines || minorHorizontalLines) {
  E.fire(this.el, 'flotr:beforegrid', [this.axes.x, this.axes.y, options, this]);
  }
  ctx.save();
  ctx.lineWidth = 1;
  ctx.strokeStyle = grid.tickColor;
   
  function circularHorizontalTicks(ticks) {
  for (i = 0; i < ticks.length; ++i) {
  var ratio = ticks[i].v / a.max;
  for (j = 0; j <= sides; ++j) {
  ctx[j === 0 ? 'moveTo' : 'lineTo'](
  Math.cos(j * coeff + angle) * radius * ratio,
  Math.sin(j * coeff + angle) * radius * ratio
  );
  }
  }
  }
   
  function drawGridLines(ticks, callback) {
  _.each(_.pluck(ticks, 'v'), function (v) {
  // Don't show lines on upper and lower bounds.
  if ((v <= a.min || v >= a.max) ||
  (v == a.min || v == a.max) && grid.outlineWidth)
  return;
  callback(Math.floor(a.d2p(v)) + ctx.lineWidth / 2);
  });
  }
   
  function drawVerticalLines(x) {
  ctx.moveTo(x, 0);
  ctx.lineTo(x, plotHeight);
  }
   
  function drawHorizontalLines(y) {
  ctx.moveTo(0, y);
  ctx.lineTo(plotWidth, y);
  }
   
  if (grid.circular) {
  ctx.translate(this.plotOffset.left + plotWidth / 2, this.plotOffset.top + plotHeight / 2);
  var radius = Math.min(plotHeight, plotWidth) * options.radar.radiusRatio / 2,
  sides = this.axes.x.ticks.length,
  coeff = 2 * (Math.PI / sides),
  angle = -Math.PI / 2;
   
  // Draw grid lines in vertical direction.
  ctx.beginPath();
   
  a = this.axes.y;
   
  if (horizontalLines) {
  circularHorizontalTicks(a.ticks);
  }
  if (minorHorizontalLines) {
  circularHorizontalTicks(a.minorTicks);
  }
   
  if (verticalLines) {
  _.times(sides, function (i) {
  ctx.moveTo(0, 0);
  ctx.lineTo(Math.cos(i * coeff + angle) * radius, Math.sin(i * coeff + angle) * radius);
  });
  }
  ctx.stroke();
  }
  else {
  ctx.translate(this.plotOffset.left, this.plotOffset.top);
   
  // Draw grid background, if present in options.
  if (grid.backgroundColor) {
  ctx.fillStyle = this.processColor(grid.backgroundColor, {x1: 0, y1: 0, x2: plotWidth, y2: plotHeight});
  ctx.fillRect(0, 0, plotWidth, plotHeight);
  }
   
  ctx.beginPath();
   
  a = this.axes.x;
  if (verticalLines) drawGridLines(a.ticks, drawVerticalLines);
  if (minorVerticalLines) drawGridLines(a.minorTicks, drawVerticalLines);
   
  a = this.axes.y;
  if (horizontalLines) drawGridLines(a.ticks, drawHorizontalLines);
  if (minorHorizontalLines) drawGridLines(a.minorTicks, drawHorizontalLines);
   
  ctx.stroke();
  }
   
  ctx.restore();
  if (verticalLines || minorVerticalLines ||
  horizontalLines || minorHorizontalLines) {
  E.fire(this.el, 'flotr:aftergrid', [this.axes.x, this.axes.y, options, this]);
  }
  },
   
  drawOutline: function () {
  var
  that = this,
  options = that.options,
  grid = options.grid,
  outline = grid.outline,
  ctx = that.ctx,
  backgroundImage = grid.backgroundImage,
  plotOffset = that.plotOffset,
  leftOffset = plotOffset.left,
  topOffset = plotOffset.top,
  plotWidth = that.plotWidth,
  plotHeight = that.plotHeight,
  v, img, src, left, top, globalAlpha;
   
  if (!grid.outlineWidth) return;
   
  ctx.save();
   
  if (grid.circular) {
  ctx.translate(leftOffset + plotWidth / 2, topOffset + plotHeight / 2);
  var radius = Math.min(plotHeight, plotWidth) * options.radar.radiusRatio / 2,
  sides = this.axes.x.ticks.length,
  coeff = 2 * (Math.PI / sides),
  angle = -Math.PI / 2;
   
  // Draw axis/grid border.
  ctx.beginPath();
  ctx.lineWidth = grid.outlineWidth;
  ctx.strokeStyle = grid.color;
  ctx.lineJoin = 'round';
   
  for (i = 0; i <= sides; ++i) {
  ctx[i === 0 ? 'moveTo' : 'lineTo'](Math.cos(i * coeff + angle) * radius, Math.sin(i * coeff + angle) * radius);
  }
  //ctx.arc(0, 0, radius, 0, Math.PI*2, true);
   
  ctx.stroke();
  }
  else {
  ctx.translate(leftOffset, topOffset);
   
  // Draw axis/grid border.
  var lw = grid.outlineWidth,
  orig = 0.5 - lw + ((lw + 1) % 2 / 2),
  lineTo = 'lineTo',
  moveTo = 'moveTo';
  ctx.lineWidth = lw;
  ctx.strokeStyle = grid.color;
  ctx.lineJoin = 'miter';
  ctx.beginPath();
  ctx.moveTo(orig, orig);
  plotWidth = plotWidth - (lw / 2) % 1;
  plotHeight = plotHeight + lw / 2;
  ctx[outline.indexOf('n') !== -1 ? lineTo : moveTo](plotWidth, orig);
  ctx[outline.indexOf('e') !== -1 ? lineTo : moveTo](plotWidth, plotHeight);
  ctx[outline.indexOf('s') !== -1 ? lineTo : moveTo](orig, plotHeight);
  ctx[outline.indexOf('w') !== -1 ? lineTo : moveTo](orig, orig);
  ctx.stroke();
  ctx.closePath();
  }
   
  ctx.restore();
   
  if (backgroundImage) {
   
  src = backgroundImage.src || backgroundImage;
  left = (parseInt(backgroundImage.left, 10) || 0) + plotOffset.left;
  top = (parseInt(backgroundImage.top, 10) || 0) + plotOffset.top;
  img = new Image();
   
  img.onload = function () {
  ctx.save();
  if (backgroundImage.alpha) ctx.globalAlpha = backgroundImage.alpha;
  ctx.globalCompositeOperation = 'destination-over';
  ctx.drawImage(img, 0, 0, img.width, img.height, left, top, plotWidth, plotHeight);
  ctx.restore();
  };
   
  img.src = src;
  }
  }
  });
   
  })();
   
  (function () {
   
var var
data = options.data, D = Flotr.DOM,
context = options.context, _ = Flotr._,
radius = Math.min(options.height, options.width) * options.radiusRatio / 2, flotr = Flotr,
step = 2 * Math.PI / data.length, S_MOUSETRACK = 'opacity:0.7;background-color:#000;color:#fff;display:none;position:absolute;padding:2px 8px;-moz-border-radius:4px;border-radius:4px;white-space:nowrap;';
angle = -Math.PI / 2,  
i, ratio; Flotr.addPlugin('hit', {
  callbacks: {
offset = offset || 0; 'flotr:mousemove': function (e, pos) {
  this.hit.track(pos);
context.beginPath(); },
for (i = 0; i < data.length; ++i) { 'flotr:click': function (pos) {
ratio = data[i][1] / this.max; this.hit.track(pos);
  },
context[i === 0 ? 'moveTo' : 'lineTo']( 'flotr:mouseout': function () {
Math.cos(i * step + angle) * radius * ratio + offset, this.hit.clearHit();
Math.sin(i * step + angle) * radius * ratio + offset }
); },
} track: function (pos) {
context.closePath(); if (this.options.mouse.track || _.any(this.series, function (s) {
if (options.fill) context.fill(); return s.mouse && s.mouse.track;
context.stroke(); })) {
}, this.hit.hit(pos);
extendYRange : function (axis, data) { }
this.max = Math.max(axis.max, this.max || -Number.MAX_VALUE); },
} /**
}); * Try a method on a graph type. If the method exists, execute it.
  * @param {Object} series
Flotr.addType('timeline', { * @param {String} method Method name.
options: { * @param {Array} args Arguments applied to method.
show: false, * @return executed successfully or failed.
lineWidth: 1, */
barWidth: 0.2, executeOnType: function (s, method, args) {
fill: true, var
fillColor: null, success = false,
fillOpacity: 0.4, options;
centered: true  
}, if (!_.isArray(s)) s = [s];
   
draw : function (options) { function e(s, index) {
  _.each(_.keys(flotr.graphTypes), function (type) {
var if (s[type] && s[type].show && this[type][method]) {
context = options.context; options = this.getOptions(s, type);
   
context.save(); options.fill = !!s.mouse.fillColor;
context.lineJoin = 'miter'; options.fillStyle = this.processColor(s.mouse.fillColor || '#ffffff', {opacity: s.mouse.fillOpacity});
context.lineWidth = options.lineWidth; options.color = s.mouse.lineColor;
context.strokeStyle = options.color; options.context = this.octx;
context.fillStyle = options.fillStyle; options.index = index;
   
this.plot(options); if (args) options.args = args;
  this[type][method].call(this[type], options);
context.restore(); success = true;
}, }
  }, this);
plot : function (options) { }
   
var _.each(s, e, this);
data = options.data,  
context = options.context, return success;
xScale = options.xScale, },
yScale = options.yScale, /**
barWidth = options.barWidth, * Updates the mouse tracking point on the overlay.
lineWidth = options.lineWidth, */
i; drawHit: function (n) {
  var octx = this.octx,
Flotr._.each(data, function (timeline) { s = n.series;
   
var if (s.mouse.lineColor) {
x = timeline[0], octx.save();
y = timeline[1], octx.lineWidth = (s.points ? s.points.lineWidth : 1);
w = timeline[2], octx.strokeStyle = s.mouse.lineColor;
h = barWidth, octx.fillStyle = this.processColor(s.mouse.fillColor || '#ffffff', {opacity: s.mouse.fillOpacity});
  octx.translate(this.plotOffset.left, this.plotOffset.top);
xt = Math.ceil(xScale(x)),  
wt = Math.ceil(xScale(x + w)) - xt, if (!this.hit.executeOnType(s, 'drawHit', n)) {
yt = Math.round(yScale(y)), var xa = n.xaxis,
ht = Math.round(yScale(y - h)) - yt, ya = n.yaxis;
   
x0 = xt - lineWidth / 2, octx.beginPath();
y0 = Math.round(yt - ht / 2) - lineWidth / 2; // TODO fix this (points) should move to general testable graph mixin
  octx.arc(xa.d2p(n.x), ya.d2p(n.y), s.points.radius || s.mouse.radius, 0, 2 * Math.PI, true);
context.strokeRect(x0, y0, wt, ht); octx.fill();
context.fillRect(x0, y0, wt, ht); octx.stroke();
  octx.closePath();
  }
  octx.restore();
  this.clip(octx);
  }
  this.prevHit = n;
  },
  /**
  * Removes the mouse tracking point from the overlay.
  */
  clearHit: function () {
  var prev = this.prevHit,
  octx = this.octx,
  plotOffset = this.plotOffset;
  octx.save();
  octx.translate(plotOffset.left, plotOffset.top);
  if (prev) {
  if (!this.hit.executeOnType(prev.series, 'clearHit', this.prevHit)) {
  // TODO fix this (points) should move to general testable graph mixin
  var
  s = prev.series,
  lw = (s.points ? s.points.lineWidth : 1);
  offset = (s.points.radius || s.mouse.radius) + lw;
  octx.clearRect(
  prev.xaxis.d2p(prev.x) - offset,
  prev.yaxis.d2p(prev.y) - offset,
  offset * 2,
  offset * 2
  );
  }
  D.hide(this.mouseTrack);
  this.prevHit = null;
  }
  octx.restore();
  },
  /**
  * Retrieves the nearest data point from the mouse cursor. If it's within
  * a certain range, draw a point on the overlay canvas and display the x and y
  * value of the data.
  * @param {Object} mouse - Object that holds the relative x and y coordinates of the cursor.
  */
  hit: function (mouse) {
   
  var
  options = this.options,
  prevHit = this.prevHit,
  closest, sensibility, dataIndex, seriesIndex, series, value, xaxis, yaxis;
   
  if (this.series.length === 0) return;
   
  // Nearest data element.
  // dist, x, y, relX, relY, absX, absY, sAngle, eAngle, fraction, mouse,
  // xaxis, yaxis, series, index, seriesIndex
  n = {
  relX: mouse.relX,
  relY: mouse.relY,
  absX: mouse.absX,
  absY: mouse.absY
  };
   
  if (options.mouse.trackY && !options.mouse.trackAll &&
  this.hit.executeOnType(this.series, 'hit', [mouse, n])) {
   
  if (!_.isUndefined(n.seriesIndex)) {
  series = this.series[n.seriesIndex];
  n.series = series;
  n.mouse = series.mouse;
  n.xaxis = series.xaxis;
  n.yaxis = series.yaxis;
  }
  } else {
   
  closest = this.hit.closest(mouse);
   
  if (closest) {
   
  closest = options.mouse.trackY ? closest.point : closest.x;
  seriesIndex = closest.seriesIndex;
  series = this.series[seriesIndex];
  xaxis = series.xaxis;
  yaxis = series.yaxis;
  sensibility = 2 * series.mouse.sensibility;
   
  if
  (options.mouse.trackAll ||
  (closest.distanceX < sensibility / xaxis.scale &&
  (!options.mouse.trackY || closest.distanceY < sensibility / yaxis.scale))) {
  n.series = series;
  n.xaxis = series.xaxis;
  n.yaxis = series.yaxis;
  n.mouse = series.mouse;
  n.x = closest.x;
  n.y = closest.y;
  n.dist = closest.distance;
  n.index = closest.dataIndex;
  n.seriesIndex = seriesIndex;
  }
  }
  }
   
  if (!prevHit || (prevHit.index !== n.index || prevHit.seriesIndex !== n.seriesIndex)) {
  this.hit.clearHit();
  if (n.series && n.mouse && n.mouse.track) {
  this.hit.drawMouseTrack(n);
  this.hit.drawHit(n);
  Flotr.EventAdapter.fire(this.el, 'flotr:hit', [n, this]);
  }
  }
  },
   
  closest: function (mouse) {
   
  var
  series = this.series,
  options = this.options,
  relX = mouse.relX,
  relY = mouse.relY,
  compare = Number.MAX_VALUE,
  compareX = Number.MAX_VALUE,
  closest = {},
  closestX = {},
  check = false,
  serie, data,
  distance, distanceX, distanceY,
  mouseX, mouseY,
  x, y, i, j;
   
  function setClosest(o) {
  o.distance = distance;
  o.distanceX = distanceX;
  o.distanceY = distanceY;
  o.seriesIndex = i;
  o.dataIndex = j;
  o.x = x;
  o.y = y;
  }
   
  for (i = 0; i < series.length; i++) {
   
  serie = series[i];
  data = serie.data;
  mouseX = serie.xaxis.p2d(relX);
  mouseY = serie.yaxis.p2d(relY);
   
  if (data.length) check = true;
   
  for (j = data.length; j--;) {
   
  x = data[j][0];
  y = data[j][1];
   
  if (x === null || y === null) continue;
   
  // don't check if the point isn't visible in the current range
  if (x < serie.xaxis.min || x > serie.xaxis.max) continue;
   
  distanceX = Math.abs(x - mouseX);
  distanceY = Math.abs(y - mouseY);
   
  // Skip square root for speed
  distance = distanceX * distanceX + distanceY * distanceY;
   
  if (distance < compare) {
  compare = distance;
  setClosest(closest);
  }
   
  if (distanceX < compareX) {
  compareX = distanceX;
  setClosest(closestX);
  }
  }
  }
   
  return check ? {
  point: closest,
  x: closestX
  } : false;
  },
   
  drawMouseTrack: function (n) {
   
  var
  pos = '',
  s = n.series,
  p = n.mouse.position,
  m = n.mouse.margin,
  elStyle = S_MOUSETRACK,
  mouseTrack = this.mouseTrack,
  plotOffset = this.plotOffset,
  left = plotOffset.left,
  right = plotOffset.right,
  bottom = plotOffset.bottom,
  top = plotOffset.top,
  decimals = n.mouse.trackDecimals,
  options = this.options;
   
  // Create
  if (!mouseTrack) {
  mouseTrack = D.node('<div class="flotr-mouse-value"></div>');
  this.mouseTrack = mouseTrack;
  D.insert(this.el, mouseTrack);
  }
   
  if (!n.mouse.relative) { // absolute to the canvas
   
  if (p.charAt(0) == 'n') pos += 'top:' + (m + top) + 'px;bottom:auto;';
  else if (p.charAt(0) == 's') pos += 'bottom:' + (m + bottom) + 'px;top:auto;';
  if (p.charAt(1) == 'e') pos += 'right:' + (m + right) + 'px;left:auto;';
  else if (p.charAt(1) == 'w') pos += 'left:' + (m + left) + 'px;right:auto;';
   
  // Bars
  } else if (s.bars.show) {
  pos += 'bottom:' + (m - top - n.yaxis.d2p(n.y / 2) + this.canvasHeight) + 'px;top:auto;';
  pos += 'left:' + (m + left + n.xaxis.d2p(n.x - options.bars.barWidth / 2)) + 'px;right:auto;';
   
  // Pie
  } else if (s.pie.show) {
  var center = {
  x: (this.plotWidth) / 2,
  y: (this.plotHeight) / 2
  },
  radius = (Math.min(this.canvasWidth, this.canvasHeight) * s.pie.sizeRatio) / 2,
  bisection = n.sAngle < n.eAngle ? (n.sAngle + n.eAngle) / 2 : (n.sAngle + n.eAngle + 2 * Math.PI) / 2;
   
  pos += 'bottom:' + (m - top - center.y - Math.sin(bisection) * radius / 2 + this.canvasHeight) + 'px;top:auto;';
  pos += 'left:' + (m + left + center.x + Math.cos(bisection) * radius / 2) + 'px;right:auto;';
   
  // Default
  } else {
  if (p.charAt(0) == 'n') pos += 'bottom:' + (m - top - n.yaxis.d2p(n.y) + this.canvasHeight) + 'px;top:auto;';
  else if (p.charAt(0) == 's') pos += 'top:' + (m + top + n.yaxis.d2p(n.y)) + 'px;bottom:auto;';
  if (p.charAt(1) == 'e') pos += 'left:' + (m + left + n.xaxis.d2p(n.x)) + 'px;right:auto;';
  else if (p.charAt(1) == 'w') pos += 'right:' + (m - left - n.xaxis.d2p(n.x) + this.canvasWidth) + 'px;left:auto;';
  }
   
  elStyle += pos;
  mouseTrack.style.cssText = elStyle;
   
  if (!decimals || decimals < 0) decimals = 0;
   
  mouseTrack.innerHTML = n.mouse.trackFormatter({
  x: n.x.toFixed(decimals),
  y: n.y.toFixed(decimals),
  series: n.series,
  index: n.index,
  nearest: n,
  fraction: n.fraction
  });
   
  D.show(mouseTrack);
  }
   
}); });
},  
   
extendRange : function (series) {  
   
var  
data = series.data,  
xa = series.xaxis,  
ya = series.yaxis,  
w = series.timeline.barWidth;  
   
if (xa.options.min === null)  
xa.min = xa.datamin - w / 2;  
   
if (xa.options.max === null) {  
   
var  
max = xa.max;  
   
Flotr._.each(data, function (timeline) {  
max = Math.max(max, timeline[0] + timeline[2]);  
}, this);  
   
xa.max = max + w / 2;  
}  
   
if (ya.options.min === null)  
ya.min = ya.datamin - w;  
if (ya.options.min === null)  
ya.max = ya.datamax + w;  
}  
   
});  
   
(function () {  
   
var D = Flotr.DOM;  
   
Flotr.addPlugin('crosshair', {  
options: {  
mode: null, // => one of null, 'x', 'y' or 'xy'  
color: '#FF0000', // => crosshair color  
hideCursor: true // => hide the cursor when the crosshair is shown  
},  
callbacks: {  
'flotr:mousemove': function(e, pos) {  
if (this.options.crosshair.mode) {  
this.crosshair.clearCrosshair();  
this.crosshair.drawCrosshair(pos);  
}  
}  
},  
/**  
* Draws the selection box.  
*/  
drawCrosshair: function(pos) {  
var octx = this.octx,  
options = this.options.crosshair,  
plotOffset = this.plotOffset,  
x = plotOffset.left + pos.relX + 0.5,  
y = plotOffset.top + pos.relY + 0.5;  
   
if (pos.relX < 0 || pos.relY < 0 || pos.relX > this.plotWidth || pos.relY > this.plotHeight) {  
this.el.style.cursor = null;  
D.removeClass(this.el, 'flotr-crosshair');  
return;  
}  
   
if (options.hideCursor) {  
this.el.style.cursor = 'none';  
D.addClass(this.el, 'flotr-crosshair');  
}  
   
octx.save();  
octx.strokeStyle = options.color;  
octx.lineWidth = 1;  
octx.beginPath();  
   
if (options.mode.indexOf('x') != -1) {  
octx.moveTo(x, plotOffset.top);  
octx.lineTo(x, plotOffset.top + this.plotHeight);  
}  
   
if (options.mode.indexOf('y') != -1) {  
octx.moveTo(plotOffset.left, y);  
octx.lineTo(plotOffset.left + this.plotWidth, y);  
}  
   
octx.stroke();  
octx.restore();  
},  
/**  
* Removes the selection box from the overlay canvas.  
*/  
clearCrosshair: function() {  
   
var  
plotOffset = this.plotOffset,  
position = this.lastMousePos,  
context = this.octx;  
   
if (position) {  
context.clearRect(  
position.relX + plotOffset.left,  
plotOffset.top,  
1,  
this.plotHeight + 1  
);  
context.clearRect(  
plotOffset.left,  
position.relY + plotOffset.top,  
this.plotWidth + 1,  
1  
);  
}  
}  
});  
})(); })();
   
(function() { /**
   
var  
D = Flotr.DOM,  
_ = Flotr._;  
   
function getImage (type, canvas, width, height) {  
   
// TODO add scaling for w / h  
var  
mime = 'image/'+type,  
data = canvas.toDataURL(mime),  
image = new Image();  
image.src = data;  
return image;  
}  
   
Flotr.addPlugin('download', {  
   
saveImage: function (type, width, height, replaceCanvas) {  
var image = null;  
if (Flotr.isIE && Flotr.isIE < 9) {  
image = '<html><body>'+this.canvas.firstChild.innerHTML+'</body></html>';  
return window.open().document.write(image);  
}  
   
if (type !== 'jpeg' && type !== 'png') return;  
   
image = getImage(type, this.canvas, width, height);  
   
if (_.isElement(image) && replaceCanvas) {  
this.download.restoreCanvas();  
D.hide(this.canvas);  
D.hide(this.overlay);  
D.setStyles({position: 'absolute'});  
D.insert(this.el, image);  
this.saveImageElement = image;  
} else {  
return window.open(image.src);  
}  
},  
   
restoreCanvas: function() {  
D.show(this.canvas);  
D.show(this.overlay);  
if (this.saveImageElement) this.el.removeChild(this.saveImageElement);  
this.saveImageElement = null;  
}  
});  
   
})();  
   
(function () {  
   
var E = Flotr.EventAdapter,  
_ = Flotr._;  
   
Flotr.addPlugin('graphGrid', {  
   
callbacks: {  
'flotr:beforedraw' : function () {  
this.graphGrid.drawGrid();  
},  
'flotr:afterdraw' : function () {  
this.graphGrid.drawOutline();  
}  
},  
   
drawGrid: function(){  
   
var  
ctx = this.ctx,  
options = this.options,  
grid = options.grid,  
verticalLines = grid.verticalLines,  
horizontalLines = grid.horizontalLines,  
minorVerticalLines = grid.minorVerticalLines,  
minorHorizontalLines = grid.minorHorizontalLines,  
plotHeight = this.plotHeight,  
plotWidth = this.plotWidth,  
a, v, i, j;  
   
if(verticalLines || minorVerticalLines ||  
horizontalLines || minorHorizontalLines){  
E.fire(this.el, 'flotr:beforegrid', [this.axes.x, this.axes.y, options, this]);  
}  
ctx.save();  
ctx.lineWidth = 1;  
ctx.strokeStyle = grid.tickColor;  
   
function circularHorizontalTicks (ticks) {  
for(i = 0; i < ticks.length; ++i){  
var ratio = ticks[i].v / a.max;  
for(j = 0; j <= sides; ++j){  
ctx[j === 0 ? 'moveTo' : 'lineTo'](  
Math.cos(j*coeff+angle)*radius*ratio,  
Math.sin(j*coeff+angle)*radius*ratio  
);  
}  
}  
}  
function drawGridLines (ticks, callback) {  
_.each(_.pluck(ticks, 'v'), function(v){  
// Don't show lines on upper and lower bounds.  
if ((v <= a.min || v >= a.max) ||  
(v == a.min || v == a.max) && grid.outlineWidth)  
return;  
callback(Math.floor(a.d2p(v)) + ctx.lineWidth/2);  
});  
}  
function drawVerticalLines (x) {  
ctx.moveTo(x, 0);  
ctx.lineTo(x, plotHeight);  
}  
function drawHorizontalLines (y) {  
ctx.moveTo(0, y);  
ctx.lineTo(plotWidth, y);  
}  
   
if (grid.circular) {  
ctx.translate(this.plotOffset.left+plotWidth/2, this.plotOffset.top+plotHeight/2);  
var radius = Math.min(plotHeight, plotWidth)*options.radar.radiusRatio/2,  
sides = this.axes.x.ticks.length,  
coeff = 2*(Math.PI/sides),  
angle = -Math.PI/2;  
   
// Draw grid lines in vertical direction.  
ctx.beginPath();  
   
a = this.axes.y;  
   
if(horizontalLines){  
circularHorizontalTicks(a.ticks);  
}  
if(minorHorizontalLines){  
circularHorizontalTicks(a.minorTicks);  
}  
   
if(verticalLines){  
_.times(sides, function(i){  
ctx.moveTo(0, 0);  
ctx.lineTo(Math.cos(i*coeff+angle)*radius, Math.sin(i*coeff+angle)*radius);  
});  
}  
ctx.stroke();  
}  
else {  
ctx.translate(this.plotOffset.left, this.plotOffset.top);  
   
// Draw grid background, if present in options.  
if(grid.backgroundColor){  
ctx.fillStyle = this.processColor(grid.backgroundColor, {x1: 0, y1: 0, x2: plotWidth, y2: plotHeight});  
ctx.fillRect(0, 0, plotWidth, plotHeight);  
}  
   
ctx.beginPath();  
   
a = this.axes.x;  
if (verticalLines) drawGridLines(a.ticks, drawVerticalLines);  
if (minorVerticalLines) drawGridLines(a.minorTicks, drawVerticalLines);  
   
a = this.axes.y;  
if (horizontalLines) drawGridLines(a.ticks, drawHorizontalLines);  
if (minorHorizontalLines) drawGridLines(a.minorTicks, drawHorizontalLines);  
   
ctx.stroke();  
}  
   
ctx.restore();  
if(verticalLines || minorVerticalLines ||  
horizontalLines || minorHorizontalLines){  
E.fire(this.el, 'flotr:aftergrid', [this.axes.x, this.axes.y, options, this]);  
}  
},  
   
drawOutline: function(){  
var  
that = this,  
options = that.options,  
grid = options.grid,  
outline = grid.outline,  
ctx = that.ctx,  
backgroundImage = grid.backgroundImage,  
plotOffset = that.plotOffset,  
leftOffset = plotOffset.left,  
topOffset = plotOffset.top,  
plotWidth = that.plotWidth,  
plotHeight = that.plotHeight,  
v, img, src, left, top, globalAlpha;  
   
if (!grid.outlineWidth) return;  
   
ctx.save();  
   
if (grid.circular) {  
ctx.translate(leftOffset + plotWidth / 2, topOffset + plotHeight / 2);  
var radius = Math.min(plotHeight, plotWidth) * options.radar.radiusRatio / 2,  
sides = this.axes.x.ticks.length,  
coeff = 2*(Math.PI/sides),  
angle = -Math.PI/2;  
   
// Draw axis/grid border.  
ctx.beginPath();  
ctx.lineWidth = grid.outlineWidth;  
ctx.strokeStyle = grid.color;  
ctx.lineJoin = 'round';  
   
for(i = 0; i <= sides; ++i){  
ctx[i === 0 ? 'moveTo' : 'lineTo'](Math.cos(i*coeff+angle)*radius, Math.sin(i*coeff+angle)*radius);  
}  
//ctx.arc(0, 0, radius, 0, Math.PI*2, true);  
   
ctx.stroke();  
}  
else {  
ctx.translate(leftOffset, topOffset);  
   
// Draw axis/grid border.  
var lw = grid.outlineWidth,  
orig = 0.5-lw+((lw+1)%2/2),  
lineTo = 'lineTo',  
moveTo = 'moveTo';  
ctx.lineWidth = lw;  
ctx.strokeStyle = grid.color;  
ctx.lineJoin = 'miter';  
ctx.beginPath();  
ctx.moveTo(orig, orig);  
plotWidth = plotWidth - (lw / 2) % 1;  
plotHeight = plotHeight + lw / 2;  
ctx[outline.indexOf('n') !== -1 ? lineTo : moveTo](plotWidth, orig);  
ctx[outline.indexOf('e') !== -1 ? lineTo : moveTo](plotWidth, plotHeight);  
ctx[outline.indexOf('s') !== -1 ? lineTo : moveTo](orig, plotHeight);  
ctx[outline.indexOf('w') !== -1 ? lineTo : moveTo](orig, orig);  
ctx.stroke();  
ctx.closePath();  
}  
   
ctx.restore();  
   
if (backgroundImage) {  
   
src = backgroundImage.src || backgroundImage;  
left = (parseInt(backgroundImage.left, 10) || 0) + plotOffset.left;  
top = (parseInt(backgroundImage.top, 10) || 0) + plotOffset.top;  
img = new Image();  
   
img.onload = function() {  
ctx.save();  
if (backgroundImage.alpha) ctx.globalAlpha = backgroundImage.alpha;  
ctx.globalCompositeOperation = 'destination-over';  
ctx.drawImage(img, 0, 0, img.width, img.height, left, top, plotWidth, plotHeight);  
ctx.restore();  
};  
   
img.src = src;  
}  
}  
});  
   
})();  
   
(function () {  
   
var  
D = Flotr.DOM,  
_ = Flotr._,  
flotr = Flotr,  
S_MOUSETRACK = 'opacity:0.7;background-color:#000;color:#fff;display:none;position:absolute;padding:2px 8px;-moz-border-radius:4px;border-radius:4px;white-space:nowrap;';  
   
Flotr.addPlugin('hit', {  
callbacks: {  
'flotr:mousemove': function(e, pos) {  
this.hit.track(pos);  
},  
'flotr:click': function(pos) {  
this.hit.track(pos);  
},  
'flotr:mouseout': function() {  
this.hit.clearHit();  
}  
},  
track : function (pos) {  
if (this.options.mouse.track || _.any(this.series, function(s){return s.mouse && s.mouse.track;})) {  
this.hit.hit(pos);  
}  
},  
/**  
* Try a method on a graph type. If the method exists, execute it.  
* @param {Object} series  
* @param {String} method Method name.  
* @param {Array} args Arguments applied to method.  
* @return executed successfully or failed.  
*/  
executeOnType: function(s, method, args){  
var  
success = false,  
options;  
   
if (!_.isArray(s)) s = [s];  
   
function e(s, index) {  
_.each(_.keys(flotr.graphTypes), function (type) {  
if (s[type] && s[type].show && this[type][method]) {  
options = this.getOptions(s, type);  
   
options.fill = !!s.mouse.fillColor;  
options.fillStyle = this.processColor(s.mouse.fillColor || '#ffffff', {opacity: s.mouse.fillOpacity});  
options.color = s.mouse.lineColor;  
options.context = this.octx;  
options.index = index;  
   
if (args) options.args = args;  
this[type][method].call(this[type], options);  
success = true;  
}  
}, this);  
}  
_.each(s, e, this);  
   
return success;  
},  
/**  
* Updates the mouse tracking point on the overlay.  
*/  
drawHit: function(n){  
var octx = this.octx,  
s = n.series;  
   
if (s.mouse.lineColor) {  
octx.save();  
octx.lineWidth = (s.points ? s.points.lineWidth : 1);  
octx.strokeStyle = s.mouse.lineColor;  
octx.fillStyle = this.processColor(s.mouse.fillColor || '#ffffff', {opacity: s.mouse.fillOpacity});  
octx.translate(this.plotOffset.left, this.plotOffset.top);  
   
if (!this.hit.executeOnType(s, 'drawHit', n)) {  
var xa = n.xaxis,  
ya = n.yaxis;  
   
octx.beginPath();  
// TODO fix this (points) should move to general testable graph mixin  
octx.arc(xa.d2p(n.x), ya.d2p(n.y), s.points.radius || s.mouse.radius, 0, 2 * Math.PI, true);  
octx.fill();  
octx.stroke();  
octx.closePath();  
}  
octx.restore();  
this.clip(octx);  
}  
this.prevHit = n;  
},  
/**  
* Removes the mouse tracking point from the overlay.  
*/  
clearHit: function(){  
var prev = this.prevHit,  
octx = this.octx,  
plotOffset = this.plotOffset;  
octx.save();  
octx.translate(plotOffset.left, plotOffset.top);  
if (prev) {  
if (!this.hit.executeOnType(prev.series, 'clearHit', this.prevHit)) {  
// TODO fix this (points) should move to general testable graph mixin  
var  
s = prev.series,  
lw = (s.points ? s.points.lineWidth : 1);  
offset = (s.points.radius || s.mouse.radius) + lw;  
octx.clearRect(  
prev.xaxis.d2p(prev.x) - offset,  
prev.yaxis.d2p(prev.y) - offset,  
offset*2,  
offset*2  
);  
}  
D.hide(this.mouseTrack);  
this.prevHit = null;  
}  
octx.restore();  
},  
/**  
* Retrieves the nearest data point from the mouse cursor. If it's within  
* a certain range, draw a point on the overlay canvas and display the x and y  
* value of the data.  
* @param {Object} mouse - Object that holds the relative x and y coordinates of the cursor.  
*/  
hit: function(mouse){  
   
var  
options = this.options,  
prevHit = this.prevHit,  
closest, sensibility, dataIndex, seriesIndex, series, value, xaxis, yaxis;  
   
if (this.series.length === 0) return;  
   
// Nearest data element.  
// dist, x, y, relX, relY, absX, absY, sAngle, eAngle, fraction, mouse,  
// xaxis, yaxis, series, index, seriesIndex  
n = {  
relX : mouse.relX,  
relY : mouse.relY,  
absX : mouse.absX,  
absY : mouse.absY  
};  
   
if (options.mouse.trackY &&  
!options.mouse.trackAll &&  
this.hit.executeOnType(this.series, 'hit', [mouse, n]))  
{  
   
if (!_.isUndefined(n.seriesIndex)) {  
series = this.series[n.seriesIndex];  
n.series = series;  
n.mouse = series.mouse;  
n.xaxis = series.xaxis;  
n.yaxis = series.yaxis;  
}  
} else {  
   
closest = this.hit.closest(mouse);  
   
if (closest) {  
   
closest = options.mouse.trackY ? closest.point : closest.x;  
seriesIndex = closest.seriesIndex;  
series = this.series[seriesIndex];  
xaxis = series.xaxis;  
yaxis = series.yaxis;  
sensibility = 2 * series.mouse.sensibility;  
   
if  
(options.mouse.trackAll ||  
(closest.distanceX < sensibility / xaxis.scale &&  
(!options.mouse.trackY || closest.distanceY < sensibility / yaxis.scale)))  
{  
n.series = series;  
n.xaxis = series.xaxis;  
n.yaxis = series.yaxis;  
n.mouse = series.mouse;  
n.x = closest.x;  
n.y = closest.y;  
n.dist = closest.distance;  
n.index = closest.dataIndex;  
n.seriesIndex = seriesIndex;  
}  
}  
}  
   
if (!prevHit || (prevHit.index !== n.index || prevHit.seriesIndex !== n.seriesIndex)) {  
this.hit.clearHit();  
if (n.series && n.mouse && n.mouse.track) {  
this.hit.drawMouseTrack(n);  
this.hit.drawHit(n);  
Flotr.EventAdapter.fire(this.el, 'flotr:hit', [n, this]);  
}  
}  
},  
   
closest : function (mouse) {  
   
var  
series = this.series,  
options = this.options,  
relX = mouse.relX,  
relY = mouse.relY,  
compare = Number.MAX_VALUE,  
compareX = Number.MAX_VALUE,  
closest = {},  
closestX = {},  
check = false,  
serie, data,  
distance, distanceX, distanceY,  
mouseX, mouseY,  
x, y, i, j;  
   
function setClosest (o) {  
o.distance = distance;  
o.distanceX = distanceX;  
o.distanceY = distanceY;  
o.seriesIndex = i;  
o.dataIndex = j;  
o.x = x;  
o.y = y;  
}  
   
for (i = 0; i < series.length; i++) {  
   
serie = series[i];  
data = serie.data;  
mouseX = serie.xaxis.p2d(relX);  
mouseY = serie.yaxis.p2d(relY);  
   
if (data.length) check = true;  
   
for (j = data.length; j--;) {  
   
x = data[j][0];  
y = data[j][1];  
   
if (x === null || y === null) continue;  
   
// don't check if the point isn't visible in the current range  
if (x < serie.xaxis.min || x > serie.xaxis.max) continue;  
   
distanceX = Math.abs(x - mouseX);  
distanceY = Math.abs(y - mouseY);  
   
// Skip square root for speed  
distance = distanceX * distanceX + distanceY * distanceY;  
   
if (distance < compare) {  
compare = distance;  
setClosest(closest);  
}  
   
if (distanceX < compareX) {  
compareX = distanceX;  
setClosest(closestX);  
}  
}  
}  
   
return check ? {  
point : closest,  
x : closestX  
} : false;  
},  
   
drawMouseTrack : function (n) {  
   
var  
pos = '',  
s = n.series,  
p = n.mouse.position,  
m = n.mouse.margin,  
elStyle = S_MOUSETRACK,  
mouseTrack = this.mouseTrack,  
plotOffset = this.plotOffset,  
left = plotOffset.left,  
right = plotOffset.right,  
bottom = plotOffset.bottom,  
top = plotOffset.top,  
decimals = n.mouse.trackDecimals,  
options = this.options;  
   
// Create  
if (!mouseTrack) {  
mouseTrack = D.node('<div class="flotr-mouse-value"></div>');  
this.mouseTrack = mouseTrack;  
D.insert(this.el, mouseTrack);  
}  
   
if (!n.mouse.relative) { // absolute to the canvas  
   
if (p.charAt(0) == 'n') pos += 'top:' + (m + top) + 'px;bottom:auto;';  
else if (p.charAt(0) == 's') pos += 'bottom:' + (m + bottom) + 'px;top:auto;';  
if (p.charAt(1) == 'e') pos += 'right:' + (m + right) + 'px;left:auto;';  
else if (p.charAt(1) == 'w') pos += 'left:' + (m + left) + 'px;right:auto;';  
   
// Bars  
} else if (s.bars.show) {  
pos += 'bottom:' + (m - top - n.yaxis.d2p(n.y/2) + this.canvasHeight) + 'px;top:auto;';  
pos += 'left:' + (m + left + n.xaxis.d2p(n.x - options.bars.barWidth/2)) + 'px;right:auto;';  
   
// Pie  
} else if (s.pie.show) {  
var center = {  
x: (this.plotWidth)/2,  
y: (this.plotHeight)/2  
},  
radius = (Math.min(this.canvasWidth, this.canvasHeight) * s.pie.sizeRatio) / 2,  
bisection = n.sAngle<n.eAngle ? (n.sAngle + n.eAngle) / 2: (n.sAngle + n.eAngle + 2* Math.PI) / 2;  
   
pos += 'bottom:' + (m - top - center.y - Math.sin(bisection) * radius/2 + this.canvasHeight) + 'px;top:auto;';  
pos += 'left:' + (m + left + center.x + Math.cos(bisection) * radius/2) + 'px;right:auto;';  
   
// Default  
} else {  
if (p.charAt(0) == 'n') pos += 'bottom:' + (m - top - n.yaxis.d2p(n.y) + this.canvasHeight) + 'px;top:auto;';  
else if (p.charAt(0) == 's') pos += 'top:' + (m + top + n.yaxis.d2p(n.y)) + 'px;bottom:auto;';  
if (p.charAt(1) == 'e') pos += 'left:' + (m + left + n.xaxis.d2p(n.x)) + 'px;right:auto;';  
else if (p.charAt(1) == 'w') pos += 'right:' + (m - left - n.xaxis.d2p(n.x) + this.canvasWidth) + 'px;left:auto;';  
}  
   
elStyle += pos;  
mouseTrack.style.cssText = elStyle;  
   
if (!decimals || decimals < 0) decimals = 0;  
   
mouseTrack.innerHTML = n.mouse.trackFormatter({  
x: n.x.toFixed(decimals),  
y: n.y.toFixed(decimals),  
series: n.series,  
index: n.index,  
nearest: n,  
fraction: n.fraction  
});  
   
D.show(mouseTrack);  
}  
   
});  
})();  
   
/**  
* Selection Handles Plugin * Selection Handles Plugin
* *
* *
* Options * Options
* show - True enables the handles plugin. * show - True enables the handles plugin.
* drag - Left and Right drag handles * drag - Left and Right drag handles
* scroll - Scrolling handle * scroll - Scrolling handle
*/ */
(function () { (function () {
   
function isLeftClick (e, type) { function isLeftClick(e, type) {
return (e.which ? (e.which === 1) : (e.button === 0 || e.button === 1)); return (e.which ? (e.which === 1) : (e.button === 0 || e.button === 1));
}  
   
function boundX(x, graph) {  
return Math.min(Math.max(0, x), graph.plotWidth - 1);  
}  
   
function boundY(y, graph) {  
return Math.min(Math.max(0, y), graph.plotHeight);  
}  
   
var  
D = Flotr.DOM,  
E = Flotr.EventAdapter,  
_ = Flotr._;  
   
   
Flotr.addPlugin('selection', {  
   
options: {  
pinchOnly: null, // Only select on pinch  
mode: null, // => one of null, 'x', 'y' or 'xy'  
color: '#B6D9FF', // => selection box color  
fps: 20 // => frames-per-second  
},  
   
callbacks: {  
'flotr:mouseup' : function (event) {  
   
var  
options = this.options.selection,  
selection = this.selection,  
pointer = this.getEventPosition(event);  
   
if (!options || !options.mode) return;  
if (selection.interval) clearInterval(selection.interval);  
   
if (this.multitouches) {  
selection.updateSelection();  
} else  
if (!options.pinchOnly) {  
selection.setSelectionPos(selection.selection.second, pointer);  
}  
selection.clearSelection();  
   
if(selection.selecting && selection.selectionIsSane()){  
selection.drawSelection();  
selection.fireSelectEvent();  
this.ignoreClick = true;  
}  
},  
'flotr:mousedown' : function (event) {  
   
var  
options = this.options.selection,  
selection = this.selection,  
pointer = this.getEventPosition(event);  
   
if (!options || !options.mode) return;  
if (!options.mode || (!isLeftClick(event) && _.isUndefined(event.touches))) return;  
if (!options.pinchOnly) selection.setSelectionPos(selection.selection.first, pointer);  
if (selection.interval) clearInterval(selection.interval);  
   
this.lastMousePos.pageX = null;  
selection.selecting = false;  
selection.interval = setInterval(  
_.bind(selection.updateSelection, this),  
1000 / options.fps  
);  
},  
'flotr:destroy' : function (event) {  
clearInterval(this.selection.interval);  
} }
},  
  function boundX(x, graph) {
// TODO This isn't used. Maybe it belongs in the draw area and fire select event methods? return Math.min(Math.max(0, x), graph.plotWidth - 1);
getArea: function() {  
   
var s = this.selection.selection,  
first = s.first,  
second = s.second;  
   
return {  
x1: Math.min(first.x, second.x),  
x2: Math.max(first.x, second.x),  
y1: Math.min(first.y, second.y),  
y2: Math.max(first.y, second.y)  
};  
},  
   
selection: {first: {x: -1, y: -1}, second: {x: -1, y: -1}},  
prevSelection: null,  
interval: null,  
   
/**  
* Fires the 'flotr:select' event when the user made a selection.  
*/  
fireSelectEvent: function(name){  
var a = this.axes,  
s = this.selection.selection,  
x1, x2, y1, y2;  
   
name = name || 'select';  
   
x1 = a.x.p2d(s.first.x);  
x2 = a.x.p2d(s.second.x);  
y1 = a.y.p2d(s.first.y);  
y2 = a.y.p2d(s.second.y);  
   
E.fire(this.el, 'flotr:'+name, [{  
x1:Math.min(x1, x2),  
y1:Math.min(y1, y2),  
x2:Math.max(x1, x2),  
y2:Math.max(y1, y2),  
xfirst:x1, xsecond:x2, yfirst:y1, ysecond:y2  
}, this]);  
},  
   
/**  
* Allows the user the manually select an area.  
* @param {Object} area - Object with coordinates to select.  
*/  
setSelection: function(area, preventEvent){  
var options = this.options,  
xa = this.axes.x,  
ya = this.axes.y,  
vertScale = ya.scale,  
hozScale = xa.scale,  
selX = options.selection.mode.indexOf('x') != -1,  
selY = options.selection.mode.indexOf('y') != -1,  
s = this.selection.selection;  
   
this.selection.clearSelection();  
   
s.first.y = boundY((selX && !selY) ? 0 : (ya.max - area.y1) * vertScale, this);  
s.second.y = boundY((selX && !selY) ? this.plotHeight - 1: (ya.max - area.y2) * vertScale, this);  
s.first.x = boundX((selY && !selX) ? 0 : area.x1, this);  
s.second.x = boundX((selY && !selX) ? this.plotWidth : area.x2, this);  
   
this.selection.drawSelection();  
if (!preventEvent)  
this.selection.fireSelectEvent();  
},  
   
/**  
* Calculates the position of the selection.  
* @param {Object} pos - Position object.  
* @param {Event} event - Event object.  
*/  
setSelectionPos: function(pos, pointer) {  
var mode = this.options.selection.mode,  
selection = this.selection.selection;  
   
if(mode.indexOf('x') == -1) {  
pos.x = (pos == selection.first) ? 0 : this.plotWidth;  
}else{  
pos.x = boundX(pointer.relX, this);  
} }
   
if (mode.indexOf('y') == -1) { function boundY(y, graph) {
pos.y = (pos == selection.first) ? 0 : this.plotHeight - 1; return Math.min(Math.max(0, y), graph.plotHeight);
}else{  
pos.y = boundY(pointer.relY, this);  
} }
},  
/** var
* Draws the selection box. D = Flotr.DOM,
*/ E = Flotr.EventAdapter,
drawSelection: function() { _ = Flotr._;
   
this.selection.fireSelectEvent('selecting');  
  Flotr.addPlugin('selection', {
var s = this.selection.selection,  
octx = this.octx, options: {
options = this.options, pinchOnly: null, // Only select on pinch
plotOffset = this.plotOffset, mode: null, // => one of null, 'x', 'y' or 'xy'
prevSelection = this.selection.prevSelection; color: '#B6D9FF', // => selection box color
  fps: 20 // => frames-per-second
if (prevSelection && },
s.first.x == prevSelection.first.x &&  
s.first.y == prevSelection.first.y && callbacks: {
s.second.x == prevSelection.second.x && 'flotr:mouseup': function (event) {
s.second.y == prevSelection.second.y) {  
return; var
  options = this.options.selection,
  selection = this.selection,
  pointer = this.getEventPosition(event);
   
  if (!options || !options.mode) return;
  if (selection.interval) clearInterval(selection.interval);
   
  if (this.multitouches) {
  selection.updateSelection();
  } else if (!options.pinchOnly) {
  selection.setSelectionPos(selection.selection.second, pointer);
  }
  selection.clearSelection();
   
  if (selection.selecting && selection.selectionIsSane()) {
  selection.drawSelection();
  selection.fireSelectEvent();
  this.ignoreClick = true;
  }
  },
  'flotr:mousedown': function (event) {
   
  var
  options = this.options.selection,
  selection = this.selection,
  pointer = this.getEventPosition(event);
   
  if (!options || !options.mode) return;
  if (!options.mode || (!isLeftClick(event) && _.isUndefined(event.touches))) return;
  if (!options.pinchOnly) selection.setSelectionPos(selection.selection.first, pointer);
  if (selection.interval) clearInterval(selection.interval);
   
  this.lastMousePos.pageX = null;
  selection.selecting = false;
  selection.interval = setInterval(
  _.bind(selection.updateSelection, this),
  1000 / options.fps
  );
  },
  'flotr:destroy': function (event) {
  clearInterval(this.selection.interval);
  }
  },
   
  // TODO This isn't used. Maybe it belongs in the draw area and fire select event methods?
  getArea: function () {
   
  var s = this.selection.selection,
  first = s.first,
  second = s.second;
   
  return {
  x1: Math.min(first.x, second.x),
  x2: Math.max(first.x, second.x),
  y1: Math.min(first.y, second.y),
  y2: Math.max(first.y, second.y)
  };
  },
   
  selection: {first: {x: -1, y: -1}, second: {x: -1, y: -1}},
  prevSelection: null,
  interval: null,
   
  /**
  * Fires the 'flotr:select' event when the user made a selection.
  */
  fireSelectEvent: function (name) {
  var a = this.axes,
  s = this.selection.selection,
  x1, x2, y1, y2;
   
  name = name || 'select';
   
  x1 = a.x.p2d(s.first.x);
  x2 = a.x.p2d(s.second.x);
  y1 = a.y.p2d(s.first.y);
  y2 = a.y.p2d(s.second.y);
   
  E.fire(this.el, 'flotr:' + name, [
  {
  x1: Math.min(x1, x2),
  y1: Math.min(y1, y2),
  x2: Math.max(x1, x2),
  y2: Math.max(y1, y2),
  xfirst: x1, xsecond: x2, yfirst: y1, ysecond: y2
  },
  this
  ]);
  },
   
  /**
  * Allows the user the manually select an area.
  * @param {Object} area - Object with coordinates to select.
  */
  setSelection: function (area, preventEvent) {
  var options = this.options,
  xa = this.axes.x,
  ya = this.axes.y,
  vertScale = ya.scale,
  hozScale = xa.scale,
  selX = options.selection.mode.indexOf('x') != -1,
  selY = options.selection.mode.indexOf('y') != -1,
  s = this.selection.selection;
   
  this.selection.clearSelection();
   
  s.first.y = boundY((selX && !selY) ? 0 : (ya.max - area.y1) * vertScale, this);
  s.second.y = boundY((selX && !selY) ? this.plotHeight - 1 : (ya.max - area.y2) * vertScale, this);
  s.first.x = boundX((selY && !selX) ? 0 : area.x1, this);
  s.second.x = boundX((selY && !selX) ? this.plotWidth : area.x2, this);
   
  this.selection.drawSelection();
  if (!preventEvent)
  this.selection.fireSelectEvent();
  },
   
  /**
  * Calculates the position of the selection.
  * @param {Object} pos - Position object.
  * @param {Event} event - Event object.
  */
  setSelectionPos: function (pos, pointer) {
  var mode = this.options.selection.mode,
  selection = this.selection.selection;
   
  if (mode.indexOf('x') == -1) {
  pos.x = (pos == selection.first) ? 0 : this.plotWidth;
  } else {
  pos.x = boundX(pointer.relX, this);
  }
   
  if (mode.indexOf('y') == -1) {
  pos.y = (pos == selection.first) ? 0 : this.plotHeight - 1;
  } else {
  pos.y = boundY(pointer.relY, this);
  }
  },
  /**
  * Draws the selection box.
  */
  drawSelection: function () {
   
  this.selection.fireSelectEvent('selecting');
   
  var s = this.selection.selection,
  octx = this.octx,
  options = this.options,
  plotOffset = this.plotOffset,
  prevSelection = this.selection.prevSelection;
   
  if (prevSelection &&
  s.first.x == prevSelection.first.x &&
  s.first.y == prevSelection.first.y &&
  s.second.x == prevSelection.second.x &&
  s.second.y == prevSelection.second.y) {
  return;
  }
   
  octx.save();
  octx.strokeStyle = this.processColor(options.selection.color, {opacity: 0.8});
  octx.lineWidth = 1;
  octx.lineJoin = 'miter';
  octx.fillStyle = this.processColor(options.selection.color, {opacity: 0.4});
   
  this.selection.prevSelection = {
  first: { x: s.first.x, y: s.first.y },
  second: { x: s.second.x, y: s.second.y }
  };
   
  var x = Math.min(s.first.x, s.second.x),
  y = Math.min(s.first.y, s.second.y),
  w = Math.abs(s.second.x - s.first.x),
  h = Math.abs(s.second.y - s.first.y);
   
  octx.fillRect(x + plotOffset.left + 0.5, y + plotOffset.top + 0.5, w, h);
  octx.strokeRect(x + plotOffset.left + 0.5, y + plotOffset.top + 0.5, w, h);
  octx.restore();
  },
   
  /**
  * Updates (draws) the selection box.
  */
  updateSelection: function () {
  if (!this.lastMousePos.pageX) return;
   
  this.selection.selecting = true;
   
  if (this.multitouches) {
  this.selection.setSelectionPos(this.selection.selection.first, this.getEventPosition(this.multitouches[0]));
  this.selection.setSelectionPos(this.selection.selection.second, this.getEventPosition(this.multitouches[1]));
  } else if (this.options.selection.pinchOnly) {
  return;
  } else {
  this.selection.setSelectionPos(this.selection.selection.second, this.lastMousePos);
  }
   
  this.selection.clearSelection();
   
  if (this.selection.selectionIsSane()) {
  this.selection.drawSelection();
  }
  },
   
  /**
  * Removes the selection box from the overlay canvas.
  */
  clearSelection: function () {
  if (!this.selection.prevSelection) return;
   
  var prevSelection = this.selection.prevSelection,
  lw = 1,
  plotOffset = this.plotOffset,
  x = Math.min(prevSelection.first.x, prevSelection.second.x),
  y = Math.min(prevSelection.first.y, prevSelection.second.y),
  w = Math.abs(prevSelection.second.x - prevSelection.first.x),
  h = Math.abs(prevSelection.second.y - prevSelection.first.y);
   
  this.octx.clearRect(x + plotOffset.left - lw + 0.5,
  y + plotOffset.top - lw,
  w + 2 * lw + 0.5,
  h + 2 * lw + 0.5);
   
  this.selection.prevSelection = null;
  },
  /**
  * Determines whether or not the selection is sane and should be drawn.
  * @return {Boolean} - True when sane, false otherwise.
  */
  selectionIsSane: function () {
  var s = this.selection.selection;
  return Math.abs(s.second.x - s.first.x) >= 5 ||
  Math.abs(s.second.y - s.first.y) >= 5;
  }
   
  });
   
  })();
   
  (function () {
   
  var D = Flotr.DOM;
   
  Flotr.addPlugin('labels', {
   
  callbacks: {
  'flotr:afterdraw': function () {
  this.labels.draw();
  }
  },
   
  draw: function () {
  // Construct fixed width label boxes, which can be styled easily.
  var
  axis, tick, left, top, xBoxWidth,
  radius, sides, coeff, angle,
  div, i, html = '',
  noLabels = 0,
  options = this.options,
  ctx = this.ctx,
  a = this.axes,
  style = { size: options.fontSize };
   
  for (i = 0; i < a.x.ticks.length; ++i) {
  if (a.x.ticks[i].label) {
  ++noLabels;
  }
  }
  xBoxWidth = this.plotWidth / noLabels;
   
  if (options.grid.circular) {
  ctx.save();
  ctx.translate(this.plotOffset.left + this.plotWidth / 2,
  this.plotOffset.top + this.plotHeight / 2);
   
  radius = this.plotHeight * options.radar.radiusRatio / 2 + options.fontSize;
  sides = this.axes.x.ticks.length;
  coeff = 2 * (Math.PI / sides);
  angle = -Math.PI / 2;
   
  drawLabelCircular(this, a.x, false);
  drawLabelCircular(this, a.x, true);
  drawLabelCircular(this, a.y, false);
  drawLabelCircular(this, a.y, true);
  ctx.restore();
  }
   
  if (!options.HtmlText && this.textEnabled) {
  drawLabelNoHtmlText(this, a.x, 'center', 'top');
  drawLabelNoHtmlText(this, a.x2, 'center', 'bottom');
  drawLabelNoHtmlText(this, a.y, 'right', 'middle');
  drawLabelNoHtmlText(this, a.y2, 'left', 'middle');
   
  } else if ((
  a.x.options.showLabels ||
  a.x2.options.showLabels ||
  a.y.options.showLabels ||
  a.y2.options.showLabels) && !options.grid.circular
  ) {
   
  html = '';
   
  drawLabelHtml(this, a.x);
  drawLabelHtml(this, a.x2);
  drawLabelHtml(this, a.y);
  drawLabelHtml(this, a.y2);
   
  ctx.stroke();
  ctx.restore();
  div = D.create('div');
  D.setStyles(div, {
  fontSize: 'smaller',
  color: options.grid.color
  });
  div.className = 'flotr-labels';
  D.insert(this.el, div);
  D.insert(div, html);
  }
   
  function drawLabelCircular(graph, axis, minorTicks) {
  var
  ticks = minorTicks ? axis.minorTicks : axis.ticks,
  isX = axis.orientation === 1,
  isFirst = axis.n === 1,
  style, offset;
   
  style = {
  color: axis.options.color || options.grid.color,
  angle: Flotr.toRad(axis.options.labelsAngle),
  textBaseline: 'middle'
  };
   
  for (i = 0; i < ticks.length &&
  (minorTicks ? axis.options.showMinorLabels : axis.options.showLabels); ++i) {
  tick = ticks[i];
  tick.label += '';
  if (!tick.label || !tick.label.length) {
  continue;
  }
   
  x = Math.cos(i * coeff + angle) * radius;
  y = Math.sin(i * coeff + angle) * radius;
   
  style.textAlign = isX ? (Math.abs(x) < 0.1 ? 'center' : (x < 0 ? 'right' : 'left')) : 'left';
   
  Flotr.drawText(
  ctx, tick.label,
  isX ? x : 3,
  isX ? y : -(axis.ticks[i].v / axis.max) * (radius - options.fontSize),
  style
  );
  }
  }
   
  function drawLabelNoHtmlText(graph, axis, textAlign, textBaseline) {
  var
  isX = axis.orientation === 1,
  isFirst = axis.n === 1,
  style, offset;
   
  style = {
  color: axis.options.color || options.grid.color,
  textAlign: textAlign,
  textBaseline: textBaseline,
  angle: Flotr.toRad(axis.options.labelsAngle)
  };
  style = Flotr.getBestTextAlign(style.angle, style);
   
  for (i = 0; i < axis.ticks.length && continueShowingLabels(axis); ++i) {
   
  tick = axis.ticks[i];
  if (!tick.label || !tick.label.length) {
  continue;
  }
   
  offset = axis.d2p(tick.v);
  if (offset < 0 ||
  offset > (isX ? graph.plotWidth : graph.plotHeight)) {
  continue;
  }
   
  Flotr.drawText(
  ctx, tick.label,
  leftOffset(graph, isX, isFirst, offset),
  topOffset(graph, isX, isFirst, offset),
  style
  );
   
  // Only draw on axis y2
  if (!isX && !isFirst) {
  ctx.save();
  ctx.strokeStyle = style.color;
  ctx.beginPath();
  ctx.moveTo(graph.plotOffset.left + graph.plotWidth - 8, graph.plotOffset.top + axis.d2p(tick.v));
  ctx.lineTo(graph.plotOffset.left + graph.plotWidth, graph.plotOffset.top + axis.d2p(tick.v));
  ctx.stroke();
  ctx.restore();
  }
  }
   
  function continueShowingLabels(axis) {
  return axis.options.showLabels && axis.used;
  }
   
  function leftOffset(graph, isX, isFirst, offset) {
  return graph.plotOffset.left +
  (isX ? offset :
  (isFirst ?
  -options.grid.labelMargin :
  options.grid.labelMargin + graph.plotWidth));
  }
   
  function topOffset(graph, isX, isFirst, offset) {
  return graph.plotOffset.top +
  (isX ? options.grid.labelMargin : offset) +
  ((isX && isFirst) ? graph.plotHeight : 0);
  }
  }
   
  function drawLabelHtml(graph, axis) {
  var
  isX = axis.orientation === 1,
  isFirst = axis.n === 1,
  name = '',
  left, style, top,
  offset = graph.plotOffset;
   
  if (!isX && !isFirst) {
  ctx.save();
  ctx.strokeStyle = axis.options.color || options.grid.color;
  ctx.beginPath();
  }
   
  if (axis.options.showLabels && (isFirst ? true : axis.used)) {
  for (i = 0; i < axis.ticks.length; ++i) {
  tick = axis.ticks[i];
  if (!tick.label || !tick.label.length ||
  ((isX ? offset.left : offset.top) + axis.d2p(tick.v) < 0) ||
  ((isX ? offset.left : offset.top) + axis.d2p(tick.v) > (isX ? graph.canvasWidth : graph.canvasHeight))) {
  continue;
  }
  top = offset.top +
  (isX ?
  ((isFirst ? 1 : -1 ) * (graph.plotHeight + options.grid.labelMargin)) :
  axis.d2p(tick.v) - axis.maxLabel.height / 2);
  left = isX ? (offset.left + axis.d2p(tick.v) - xBoxWidth / 2) : 0;
   
  name = '';
  if (i === 0) {
  name = ' first';
  } else if (i === axis.ticks.length - 1) {
  name = ' last';
  }
  name += isX ? ' flotr-grid-label-x' : ' flotr-grid-label-y';
   
  html += [
  '<div style="position:absolute; text-align:' + (isX ? 'center' : 'right') + '; ',
  'top:' + top + 'px; ',
  ((!isX && !isFirst) ? 'right:' : 'left:') + left + 'px; ',
  'width:' + (isX ? xBoxWidth : ((isFirst ? offset.left : offset.right) - options.grid.labelMargin)) + 'px; ',
  axis.options.color ? ('color:' + axis.options.color + '; ') : ' ',
  '" class="flotr-grid-label' + name + '">' + tick.label + '</div>'
  ].join(' ');
   
  if (!isX && !isFirst) {
  ctx.moveTo(offset.left + graph.plotWidth - 8, offset.top + axis.d2p(tick.v));
  ctx.lineTo(offset.left + graph.plotWidth, offset.top + axis.d2p(tick.v));
  }
  }
  }
  }
  }
   
  });
  })();
   
  (function () {
   
  var
  D = Flotr.DOM,
  _ = Flotr._;
   
  Flotr.addPlugin('legend', {
  options: {
  show: true, // => setting to true will show the legend, hide otherwise
  noColumns: 1, // => number of colums in legend table // @todo: doesn't work for HtmlText = false
  labelFormatter: function (v) {
  return v;
  }, // => fn: string -> string
  labelBoxBorderColor: '#CCCCCC', // => border color for the little label boxes
  labelBoxWidth: 14,
  labelBoxHeight: 10,
  labelBoxMargin: 5,
  labelBoxOpacity: 0.4,
  container: null, // => container (as jQuery object) to put legend in, null means default on top of graph
  position: 'nw', // => position of default legend container within plot
  margin: 5, // => distance from grid edge to default legend container within plot
  backgroundColor: null, // => null means auto-detect
  backgroundOpacity: 0.85// => set to 0 to avoid background, set to 1 for a solid background
  },
  callbacks: {
  'flotr:afterinit': function () {
  this.legend.insertLegend();
  }
  },
  /**
  * Adds a legend div to the canvas container or draws it on the canvas.
  */
  insertLegend: function () {
   
  if (!this.options.legend.show)
  return;
   
  var series = this.series,
  plotOffset = this.plotOffset,
  options = this.options,
  legend = options.legend,
  fragments = [],
  rowStarted = false,
  ctx = this.ctx,
  itemCount = _.filter(series,function (s) {
  return (s.label && !s.hide);
  }).length,
  p = legend.position,
  m = legend.margin,
  i, label, color;
   
  if (itemCount) {
  if (!options.HtmlText && this.textEnabled && !legend.container) {
  var style = {
  size: options.fontSize * 1.1,
  color: options.grid.color
  };
   
  var lbw = legend.labelBoxWidth,
  lbh = legend.labelBoxHeight,
  lbm = legend.labelBoxMargin,
  offsetX = plotOffset.left + m,
  offsetY = plotOffset.top + m;
   
  // We calculate the labels' max width
  var labelMaxWidth = 0;
  for (i = series.length - 1; i > -1; --i) {
  if (!series[i].label || series[i].hide) continue;
  label = legend.labelFormatter(series[i].label);
  labelMaxWidth = Math.max(labelMaxWidth, this._text.measureText(label, style).width);
  }
   
  var legendWidth = Math.round(lbw + lbm * 3 + labelMaxWidth),
  legendHeight = Math.round(itemCount * (lbm + lbh) + lbm);
   
  if (p.charAt(0) == 's') offsetY = plotOffset.top + this.plotHeight - (m + legendHeight);
  if (p.charAt(1) == 'e') offsetX = plotOffset.left + this.plotWidth - (m + legendWidth);
   
  // Legend box
  color = this.processColor(legend.backgroundColor || 'rgb(240,240,240)', {opacity: legend.backgroundOpacity || 0.1});
   
  ctx.fillStyle = color;
  ctx.fillRect(offsetX, offsetY, legendWidth, legendHeight);
  ctx.strokeStyle = legend.labelBoxBorderColor;
  ctx.strokeRect(Flotr.toPixel(offsetX), Flotr.toPixel(offsetY), legendWidth, legendHeight);
   
  // Legend labels
  var x = offsetX + lbm;
  var y = offsetY + lbm;
  for (i = 0; i < series.length; i++) {
  if (!series[i].label || series[i].hide) continue;
  label = legend.labelFormatter(series[i].label);
   
  ctx.fillStyle = series[i].color;
  ctx.fillRect(x, y, lbw - 1, lbh - 1);
   
  ctx.strokeStyle = legend.labelBoxBorderColor;
  ctx.lineWidth = 1;
  ctx.strokeRect(Math.ceil(x) - 1.5, Math.ceil(y) - 1.5, lbw + 2, lbh + 2);
   
  // Legend text
  Flotr.drawText(ctx, label, x + lbw + lbm, y + lbh, style);
   
  y += lbh + lbm;
  }
  }
  else {
  for (i = 0; i < series.length; ++i) {
  if (!series[i].label || series[i].hide) continue;
   
  if (i % legend.noColumns === 0) {
  fragments.push(rowStarted ? '</tr><tr>' : '<tr>');
  rowStarted = true;
  }
   
  // @TODO remove requirement on bars
  var s = series[i],
  boxWidth = legend.labelBoxWidth,
  boxHeight = legend.labelBoxHeight,
  opacityValue = (s.bars ? s.bars.fillOpacity : legend.labelBoxOpacity),
  opacity = 'opacity:' + opacityValue + ';filter:alpha(opacity=' + opacityValue * 100 + ');';
   
  label = legend.labelFormatter(s.label);
  color = 'background-color:' + ((s.bars && s.bars.show && s.bars.fillColor && s.bars.fill) ? s.bars.fillColor : s.color) + ';';
   
  fragments.push(
  '<td class="flotr-legend-color-box">',
  '<div style="border:1px solid ', legend.labelBoxBorderColor, ';padding:1px">',
  '<div style="width:', (boxWidth - 1), 'px;height:', (boxHeight - 1), 'px;border:1px solid ', series[i].color, '">', // Border
  '<div style="width:', boxWidth, 'px;height:', boxHeight, 'px;', 'opacity:.4;', color, '"></div>', // Background
  '</div>',
  '</div>',
  '</td>',
  '<td class="flotr-legend-label">', label, '</td>'
  );
  }
  if (rowStarted) fragments.push('</tr>');
   
  if (fragments.length > 0) {
  var table = '<table style="font-size:smaller;color:' + options.grid.color + '">' + fragments.join('') + '</table>';
  if (legend.container) {
  D.insert(legend.container, table);
  }
  else {
  var styles = {position: 'absolute', 'z-index': 2};
   
  if (p.charAt(0) == 'n') {
  styles.top = (m + plotOffset.top) + 'px';
  styles.bottom = 'auto';
  }
  else if (p.charAt(0) == 's') {
  styles.bottom = (m + plotOffset.bottom) + 'px';
  styles.top = 'auto';
  }
  if (p.charAt(1) == 'e') {
  styles.right = (m + plotOffset.right) + 'px';
  styles.left = 'auto';
  }
  else if (p.charAt(1) == 'w') {
  styles.left = (m + plotOffset.left) + 'px';
  styles.right = 'auto';
  }
   
  var div = D.create('div'), size;
  div.className = 'flotr-legend';
  D.setStyles(div, styles);
  D.insert(div, table);
  D.insert(this.el, div);
   
  if (!legend.backgroundOpacity)
  return;
   
  var c = legend.backgroundColor || options.grid.backgroundColor || '#ffffff';
   
  _.extend(styles, D.size(div), {
  'backgroundColor': c,
  'z-index': 1
  });
  styles.width += 'px';
  styles.height += 'px';
   
  // Put in the transparent background separately to avoid blended labels and
  div = D.create('div');
  div.className = 'flotr-legend-bg';
  D.setStyles(div, styles);
  D.opacity(div, legend.backgroundOpacity);
  D.insert(div, ' ');
  D.insert(this.el, div);
  }
  }
  }
  }
  }
  });
  })();
   
  /** Spreadsheet **/
  (function () {
   
  function getRowLabel(value) {
  if (this.options.spreadsheet.tickFormatter) {
  //TODO maybe pass the xaxis formatter to the custom tick formatter as an opt-out?
  return this.options.spreadsheet.tickFormatter(value);
  }
  else {
  var t = _.find(this.axes.x.ticks, function (t) {
  return t.v == value;
  });
  if (t) {
  return t.label;
  }
  return value;
  }
} }
   
octx.save();  
octx.strokeStyle = this.processColor(options.selection.color, {opacity: 0.8});  
octx.lineWidth = 1;  
octx.lineJoin = 'miter';  
octx.fillStyle = this.processColor(options.selection.color, {opacity: 0.4});  
   
this.selection.prevSelection = {  
first: { x: s.first.x, y: s.first.y },  
second: { x: s.second.x, y: s.second.y }  
};  
   
var x = Math.min(s.first.x, s.second.x),  
y = Math.min(s.first.y, s.second.y),  
w = Math.abs(s.second.x - s.first.x),  
h = Math.abs(s.second.y - s.first.y);  
   
octx.fillRect(x + plotOffset.left+0.5, y + plotOffset.top+0.5, w, h);  
octx.strokeRect(x + plotOffset.left+0.5, y + plotOffset.top+0.5, w, h);  
octx.restore();  
},  
   
/**  
* Updates (draws) the selection box.  
*/  
updateSelection: function(){  
if (!this.lastMousePos.pageX) return;  
   
this.selection.selecting = true;  
   
if (this.multitouches) {  
this.selection.setSelectionPos(this.selection.selection.first, this.getEventPosition(this.multitouches[0]));  
this.selection.setSelectionPos(this.selection.selection.second, this.getEventPosition(this.multitouches[1]));  
} else  
if (this.options.selection.pinchOnly) {  
return;  
} else {  
this.selection.setSelectionPos(this.selection.selection.second, this.lastMousePos);  
}  
   
this.selection.clearSelection();  
   
if(this.selection.selectionIsSane()) {  
this.selection.drawSelection();  
}  
},  
   
/**  
* Removes the selection box from the overlay canvas.  
*/  
clearSelection: function() {  
if (!this.selection.prevSelection) return;  
   
var prevSelection = this.selection.prevSelection,  
lw = 1,  
plotOffset = this.plotOffset,  
x = Math.min(prevSelection.first.x, prevSelection.second.x),  
y = Math.min(prevSelection.first.y, prevSelection.second.y),  
w = Math.abs(prevSelection.second.x - prevSelection.first.x),  
h = Math.abs(prevSelection.second.y - prevSelection.first.y);  
   
this.octx.clearRect(x + plotOffset.left - lw + 0.5,  
y + plotOffset.top - lw,  
w + 2 * lw + 0.5,  
h + 2 * lw + 0.5);  
   
this.selection.prevSelection = null;  
},  
/**  
* Determines whether or not the selection is sane and should be drawn.  
* @return {Boolean} - True when sane, false otherwise.  
*/  
selectionIsSane: function(){  
var s = this.selection.selection;  
return Math.abs(s.second.x - s.first.x) >= 5 ||  
Math.abs(s.second.y - s.first.y) >= 5;  
}  
   
});  
   
})();  
   
(function () {  
   
var D = Flotr.DOM;  
   
Flotr.addPlugin('labels', {  
   
callbacks : {  
'flotr:afterdraw' : function () {  
this.labels.draw();  
}  
},  
   
draw: function(){  
// Construct fixed width label boxes, which can be styled easily.  
var var
axis, tick, left, top, xBoxWidth, D = Flotr.DOM,
radius, sides, coeff, angle, _ = Flotr._;
div, i, html = '',  
noLabels = 0, Flotr.addPlugin('spreadsheet', {
options = this.options, options: {
ctx = this.ctx, show: false, // => show the data grid using two tabs
a = this.axes, tabGraphLabel: 'Graph',
style = { size: options.fontSize }; tabDataLabel: 'Data',
  toolbarDownload: 'Download CSV', // @todo: add better language support
for (i = 0; i < a.x.ticks.length; ++i){ toolbarSelectAll: 'Select all',
if (a.x.ticks[i].label) { ++noLabels; } csvFileSeparator: ',',
} decimalSeparator: '.',
xBoxWidth = this.plotWidth / noLabels; tickFormatter: null,
  initialTab: 'graph'
if (options.grid.circular) { },
ctx.save(); /**
ctx.translate(this.plotOffset.left + this.plotWidth / 2, * Builds the tabs in the DOM
this.plotOffset.top + this.plotHeight / 2); */
  callbacks: {
radius = this.plotHeight * options.radar.radiusRatio / 2 + options.fontSize; 'flotr:afterconstruct': function () {
sides = this.axes.x.ticks.length; // @TODO necessary?
coeff = 2 * (Math.PI / sides); //this.el.select('.flotr-tabs-group,.flotr-datagrid-container').invoke('remove');
angle = -Math.PI / 2;  
  if (!this.options.spreadsheet.show) return;
drawLabelCircular(this, a.x, false);  
drawLabelCircular(this, a.x, true); var ss = this.spreadsheet,
drawLabelCircular(this, a.y, false); container = D.node('<div class="flotr-tabs-group" style="position:absolute;left:0px;width:' + this.canvasWidth + 'px"></div>'),
drawLabelCircular(this, a.y, true); graph = D.node('<div style="float:left" class="flotr-tab selected">' + this.options.spreadsheet.tabGraphLabel + '</div>'),
ctx.restore(); data = D.node('<div style="float:left" class="flotr-tab">' + this.options.spreadsheet.tabDataLabel + '</div>'),
} offset;
   
if (!options.HtmlText && this.textEnabled) { ss.tabsContainer = container;
drawLabelNoHtmlText(this, a.x, 'center', 'top'); ss.tabs = { graph: graph, data: data };
drawLabelNoHtmlText(this, a.x2, 'center', 'bottom');  
drawLabelNoHtmlText(this, a.y, 'right', 'middle'); D.insert(container, graph);
drawLabelNoHtmlText(this, a.y2, 'left', 'middle'); D.insert(container, data);
  D.insert(this.el, container);
} else if ((  
a.x.options.showLabels || offset = D.size(data).height + 2;
a.x2.options.showLabels || this.plotOffset.bottom += offset;
a.y.options.showLabels ||  
a.y2.options.showLabels) && D.setStyles(container, {top: this.canvasHeight - offset + 'px'});
!options.grid.circular  
) { this.
  observe(graph, 'click',function () {
html = ''; ss.showTab('graph');
  }).
drawLabelHtml(this, a.x); observe(data, 'click', function () {
drawLabelHtml(this, a.x2); ss.showTab('data');
drawLabelHtml(this, a.y); });
drawLabelHtml(this, a.y2); if (this.options.spreadsheet.initialTab !== 'graph') {
  ss.showTab(this.options.spreadsheet.initialTab);
ctx.stroke(); }
ctx.restore(); }
div = D.create('div'); },
D.setStyles(div, { /**
fontSize: 'smaller', * Builds a matrix of the data to make the correspondance between the x values and the y values :
color: options.grid.color * X value => Y values from the axes
}); * @return {Array} The data grid
div.className = 'flotr-labels'; */
D.insert(this.el, div); loadDataGrid: function () {
D.insert(div, html); if (this.seriesData) return this.seriesData;
}  
  var s = this.series,
function drawLabelCircular (graph, axis, minorTicks) { rows = {};
var  
ticks = minorTicks ? axis.minorTicks : axis.ticks, /* The data grid is a 2 dimensions array. There is a row for each X value.
isX = axis.orientation === 1, * Each row contains the x value and the corresponding y value for each serie ('undefined' if there isn't one)
isFirst = axis.n === 1, **/
style, offset; _.each(s, function (serie, i) {
  _.each(serie.data, function (v) {
style = { var x = v[0],
color : axis.options.color || options.grid.color, y = v[1],
angle : Flotr.toRad(axis.options.labelsAngle), r = rows[x];
textBaseline : 'middle' if (r) {
}; r[i + 1] = y;
  } else {
for (i = 0; i < ticks.length && var newRow = [];
(minorTicks ? axis.options.showMinorLabels : axis.options.showLabels); ++i){ newRow[0] = x;
tick = ticks[i]; newRow[i + 1] = y;
tick.label += ''; rows[x] = newRow;
if (!tick.label || !tick.label.length) { continue; } }
  });
x = Math.cos(i * coeff + angle) * radius;  
y = Math.sin(i * coeff + angle) * radius;  
   
style.textAlign = isX ? (Math.abs(x) < 0.1 ? 'center' : (x < 0 ? 'right' : 'left')) : 'left';  
   
Flotr.drawText(  
ctx, tick.label,  
isX ? x : 3,  
isX ? y : -(axis.ticks[i].v / axis.max) * (radius - options.fontSize),  
style  
);  
}  
}  
   
function drawLabelNoHtmlText (graph, axis, textAlign, textBaseline) {  
var  
isX = axis.orientation === 1,  
isFirst = axis.n === 1,  
style, offset;  
   
style = {  
color : axis.options.color || options.grid.color,  
textAlign : textAlign,  
textBaseline : textBaseline,  
angle : Flotr.toRad(axis.options.labelsAngle)  
};  
style = Flotr.getBestTextAlign(style.angle, style);  
   
for (i = 0; i < axis.ticks.length && continueShowingLabels(axis); ++i) {  
   
tick = axis.ticks[i];  
if (!tick.label || !tick.label.length) { continue; }  
   
offset = axis.d2p(tick.v);  
if (offset < 0 ||  
offset > (isX ? graph.plotWidth : graph.plotHeight)) { continue; }  
   
Flotr.drawText(  
ctx, tick.label,  
leftOffset(graph, isX, isFirst, offset),  
topOffset(graph, isX, isFirst, offset),  
style  
);  
   
// Only draw on axis y2  
if (!isX && !isFirst) {  
ctx.save();  
ctx.strokeStyle = style.color;  
ctx.beginPath();  
ctx.moveTo(graph.plotOffset.left + graph.plotWidth - 8, graph.plotOffset.top + axis.d2p(tick.v));  
ctx.lineTo(graph.plotOffset.left + graph.plotWidth, graph.plotOffset.top + axis.d2p(tick.v));  
ctx.stroke();  
ctx.restore();  
}  
}  
   
function continueShowingLabels (axis) {  
return axis.options.showLabels && axis.used;  
}  
function leftOffset (graph, isX, isFirst, offset) {  
return graph.plotOffset.left +  
(isX ? offset :  
(isFirst ?  
-options.grid.labelMargin :  
options.grid.labelMargin + graph.plotWidth));  
}  
function topOffset (graph, isX, isFirst, offset) {  
return graph.plotOffset.top +  
(isX ? options.grid.labelMargin : offset) +  
((isX && isFirst) ? graph.plotHeight : 0);  
}  
}  
   
function drawLabelHtml (graph, axis) {  
var  
isX = axis.orientation === 1,  
isFirst = axis.n === 1,  
name = '',  
left, style, top,  
offset = graph.plotOffset;  
   
if (!isX && !isFirst) {  
ctx.save();  
ctx.strokeStyle = axis.options.color || options.grid.color;  
ctx.beginPath();  
}  
   
if (axis.options.showLabels && (isFirst ? true : axis.used)) {  
for (i = 0; i < axis.ticks.length; ++i) {  
tick = axis.ticks[i];  
if (!tick.label || !tick.label.length ||  
((isX ? offset.left : offset.top) + axis.d2p(tick.v) < 0) ||  
((isX ? offset.left : offset.top) + axis.d2p(tick.v) > (isX ? graph.canvasWidth : graph.canvasHeight))) {  
continue;  
}  
top = offset.top +  
(isX ?  
((isFirst ? 1 : -1 ) * (graph.plotHeight + options.grid.labelMargin)) :  
axis.d2p(tick.v) - axis.maxLabel.height / 2);  
left = isX ? (offset.left + axis.d2p(tick.v) - xBoxWidth / 2) : 0;  
   
name = '';  
if (i === 0) {  
name = ' first';  
} else if (i === axis.ticks.length - 1) {  
name = ' last';  
}  
name += isX ? ' flotr-grid-label-x' : ' flotr-grid-label-y';  
   
html += [  
'<div style="position:absolute; text-align:' + (isX ? 'center' : 'right') + '; ',  
'top:' + top + 'px; ',  
((!isX && !isFirst) ? 'right:' : 'left:') + left + 'px; ',  
'width:' + (isX ? xBoxWidth : ((isFirst ? offset.left : offset.right) - options.grid.labelMargin)) + 'px; ',  
axis.options.color ? ('color:' + axis.options.color + '; ') : ' ',  
'" class="flotr-grid-label' + name + '">' + tick.label + '</div>'  
].join(' ');  
   
if (!isX && !isFirst) {  
ctx.moveTo(offset.left + graph.plotWidth - 8, offset.top + axis.d2p(tick.v));  
ctx.lineTo(offset.left + graph.plotWidth, offset.top + axis.d2p(tick.v));  
}  
}  
}  
}  
}  
   
});  
})();  
   
(function () {  
   
var  
D = Flotr.DOM,  
_ = Flotr._;  
   
Flotr.addPlugin('legend', {  
options: {  
show: true, // => setting to true will show the legend, hide otherwise  
noColumns: 1, // => number of colums in legend table // @todo: doesn't work for HtmlText = false  
labelFormatter: function(v){return v;}, // => fn: string -> string  
labelBoxBorderColor: '#CCCCCC', // => border color for the little label boxes  
labelBoxWidth: 14,  
labelBoxHeight: 10,  
labelBoxMargin: 5,  
labelBoxOpacity: 0.4,  
container: null, // => container (as jQuery object) to put legend in, null means default on top of graph  
position: 'nw', // => position of default legend container within plot  
margin: 5, // => distance from grid edge to default legend container within plot  
backgroundColor: null, // => null means auto-detect  
backgroundOpacity: 0.85// => set to 0 to avoid background, set to 1 for a solid background  
},  
callbacks: {  
'flotr:afterinit': function() {  
this.legend.insertLegend();  
}  
},  
/**  
* Adds a legend div to the canvas container or draws it on the canvas.  
*/  
insertLegend: function(){  
   
if(!this.options.legend.show)  
return;  
   
var series = this.series,  
plotOffset = this.plotOffset,  
options = this.options,  
legend = options.legend,  
fragments = [],  
rowStarted = false,  
ctx = this.ctx,  
itemCount = _.filter(series, function(s) {return (s.label && !s.hide);}).length,  
p = legend.position,  
m = legend.margin,  
i, label, color;  
   
if (itemCount) {  
if (!options.HtmlText && this.textEnabled && !legend.container) {  
var style = {  
size: options.fontSize*1.1,  
color: options.grid.color  
};  
   
var lbw = legend.labelBoxWidth,  
lbh = legend.labelBoxHeight,  
lbm = legend.labelBoxMargin,  
offsetX = plotOffset.left + m,  
offsetY = plotOffset.top + m;  
   
// We calculate the labels' max width  
var labelMaxWidth = 0;  
for(i = series.length - 1; i > -1; --i){  
if(!series[i].label || series[i].hide) continue;  
label = legend.labelFormatter(series[i].label);  
labelMaxWidth = Math.max(labelMaxWidth, this._text.measureText(label, style).width);  
}  
   
var legendWidth = Math.round(lbw + lbm*3 + labelMaxWidth),  
legendHeight = Math.round(itemCount*(lbm+lbh) + lbm);  
   
if(p.charAt(0) == 's') offsetY = plotOffset.top + this.plotHeight - (m + legendHeight);  
if(p.charAt(1) == 'e') offsetX = plotOffset.left + this.plotWidth - (m + legendWidth);  
   
// Legend box  
color = this.processColor(legend.backgroundColor || 'rgb(240,240,240)', {opacity: legend.backgroundOpacity || 0.1});  
   
ctx.fillStyle = color;  
ctx.fillRect(offsetX, offsetY, legendWidth, legendHeight);  
ctx.strokeStyle = legend.labelBoxBorderColor;  
ctx.strokeRect(Flotr.toPixel(offsetX), Flotr.toPixel(offsetY), legendWidth, legendHeight);  
   
// Legend labels  
var x = offsetX + lbm;  
var y = offsetY + lbm;  
for(i = 0; i < series.length; i++){  
if(!series[i].label || series[i].hide) continue;  
label = legend.labelFormatter(series[i].label);  
   
ctx.fillStyle = series[i].color;  
ctx.fillRect(x, y, lbw-1, lbh-1);  
   
ctx.strokeStyle = legend.labelBoxBorderColor;  
ctx.lineWidth = 1;  
ctx.strokeRect(Math.ceil(x)-1.5, Math.ceil(y)-1.5, lbw+2, lbh+2);  
   
// Legend text  
Flotr.drawText(ctx, label, x + lbw + lbm, y + lbh, style);  
   
y += lbh + lbm;  
}  
}  
else {  
for(i = 0; i < series.length; ++i){  
if(!series[i].label || series[i].hide) continue;  
   
if(i % legend.noColumns === 0){  
fragments.push(rowStarted ? '</tr><tr>' : '<tr>');  
rowStarted = true;  
}  
   
// @TODO remove requirement on bars  
var s = series[i],  
boxWidth = legend.labelBoxWidth,  
boxHeight = legend.labelBoxHeight,  
opacityValue = (s.bars ? s.bars.fillOpacity : legend.labelBoxOpacity),  
opacity = 'opacity:' + opacityValue + ';filter:alpha(opacity=' + opacityValue*100 + ');';  
   
label = legend.labelFormatter(s.label);  
color = 'background-color:' + ((s.bars && s.bars.show && s.bars.fillColor && s.bars.fill) ? s.bars.fillColor : s.color) + ';';  
   
fragments.push(  
'<td class="flotr-legend-color-box">',  
'<div style="border:1px solid ', legend.labelBoxBorderColor, ';padding:1px">',  
'<div style="width:', (boxWidth-1), 'px;height:', (boxHeight-1), 'px;border:1px solid ', series[i].color, '">', // Border  
'<div style="width:', boxWidth, 'px;height:', boxHeight, 'px;', 'opacity:.4;', color, '"></div>', // Background  
'</div>',  
'</div>',  
'</td>',  
'<td class="flotr-legend-label">', label, '</td>'  
);  
}  
if(rowStarted) fragments.push('</tr>');  
   
if(fragments.length > 0){  
var table = '<table style="font-size:smaller;color:' + options.grid.color + '">' + fragments.join('') + '</table>';  
if(legend.container){  
D.insert(legend.container, table);  
}  
else {  
var styles = {position: 'absolute', 'z-index': 2};  
   
if(p.charAt(0) == 'n') { styles.top = (m + plotOffset.top) + 'px'; styles.bottom = 'auto'; }  
else if(p.charAt(0) == 's') { styles.bottom = (m + plotOffset.bottom) + 'px'; styles.top = 'auto'; }  
if(p.charAt(1) == 'e') { styles.right = (m + plotOffset.right) + 'px'; styles.left = 'auto'; }  
else if(p.charAt(1) == 'w') { styles.left = (m + plotOffset.left) + 'px'; styles.right = 'auto'; }  
   
var div = D.create('div'), size;  
div.className = 'flotr-legend';  
D.setStyles(div, styles);  
D.insert(div, table);  
D.insert(this.el, div);  
   
if(!legend.backgroundOpacity)  
return;  
   
var c = legend.backgroundColor || options.grid.backgroundColor || '#ffffff';  
   
_.extend(styles, D.size(div), {  
'backgroundColor': c,  
'z-index': 1  
}); });
styles.width += 'px';  
styles.height += 'px'; // The data grid is sorted by x value
  this.seriesData = _.sortBy(rows, function (row, x) {
// Put in the transparent background separately to avoid blended labels and return parseInt(x, 10);
div = D.create('div'); });
div.className = 'flotr-legend-bg'; return this.seriesData;
D.setStyles(div, styles); },
D.opacity(div, legend.backgroundOpacity); /**
D.insert(div, ' '); * Constructs the data table for the spreadsheet
D.insert(this.el, div); * @todo make a spreadsheet manager (Flotr.Spreadsheet)
} * @return {Element} The resulting table element
} */
} constructDataGrid: function () {
} // If the data grid has already been built, nothing to do here
} if (this.spreadsheet.datagrid) return this.spreadsheet.datagrid;
});  
})(); var s = this.series,
  datagrid = this.spreadsheet.loadDataGrid(),
/** Spreadsheet **/ colgroup = ['<colgroup><col />'],
(function() { buttonDownload, buttonSelect, t;
   
function getRowLabel(value){ // First row : series' labels
if (this.options.spreadsheet.tickFormatter){ var html = ['<table class="flotr-datagrid"><tr class="first-row">'];
//TODO maybe pass the xaxis formatter to the custom tick formatter as an opt-out? html.push('<th>&nbsp;</th>');
return this.options.spreadsheet.tickFormatter(value); _.each(s, function (serie, i) {
} html.push('<th scope="col">' + (serie.label || String.fromCharCode(65 + i)) + '</th>');
else { colgroup.push('<col />');
var t = _.find(this.axes.x.ticks, function(t){return t.v == value;}); });
if (t) { html.push('</tr>');
return t.label; // Data rows
} _.each(datagrid, function (row) {
return value; html.push('<tr>');
} _.times(s.length + 1, function (i) {
} var tag = 'td',
  value = row[i],
var // TODO: do we really want to handle problems with floating point
D = Flotr.DOM, // precision here?
_ = Flotr._; content = (!_.isUndefined(value) ? Math.round(value * 100000) / 100000 : '');
  if (i === 0) {
Flotr.addPlugin('spreadsheet', { tag = 'th';
options: { var label = getRowLabel.call(this, content);
show: false, // => show the data grid using two tabs if (label) content = label;
tabGraphLabel: 'Graph', }
tabDataLabel: 'Data',  
toolbarDownload: 'Download CSV', // @todo: add better language support html.push('<' + tag + (tag == 'th' ? ' scope="row"' : '') + '>' + content + '</' + tag + '>');
toolbarSelectAll: 'Select all', }, this);
csvFileSeparator: ',', html.push('</tr>');
decimalSeparator: '.', }, this);
tickFormatter: null, colgroup.push('</colgroup>');
initialTab: 'graph' t = D.node(html.join(''));
},  
/** /**
* Builds the tabs in the DOM * @TODO disabled this
*/ if (!Flotr.isIE || Flotr.isIE == 9) {
callbacks: {  
'flotr:afterconstruct': function(){  
// @TODO necessary?  
//this.el.select('.flotr-tabs-group,.flotr-datagrid-container').invoke('remove');  
   
if (!this.options.spreadsheet.show) return;  
   
var ss = this.spreadsheet,  
container = D.node('<div class="flotr-tabs-group" style="position:absolute;left:0px;width:'+this.canvasWidth+'px"></div>'),  
graph = D.node('<div style="float:left" class="flotr-tab selected">'+this.options.spreadsheet.tabGraphLabel+'</div>'),  
data = D.node('<div style="float:left" class="flotr-tab">'+this.options.spreadsheet.tabDataLabel+'</div>'),  
offset;  
   
ss.tabsContainer = container;  
ss.tabs = { graph : graph, data : data };  
   
D.insert(container, graph);  
D.insert(container, data);  
D.insert(this.el, container);  
   
offset = D.size(data).height + 2;  
this.plotOffset.bottom += offset;  
   
D.setStyles(container, {top: this.canvasHeight-offset+'px'});  
   
this.  
observe(graph, 'click', function(){ss.showTab('graph');}).  
observe(data, 'click', function(){ss.showTab('data');});  
if (this.options.spreadsheet.initialTab !== 'graph'){  
ss.showTab(this.options.spreadsheet.initialTab);  
}  
}  
},  
/**  
* Builds a matrix of the data to make the correspondance between the x values and the y values :  
* X value => Y values from the axes  
* @return {Array} The data grid  
*/  
loadDataGrid: function(){  
if (this.seriesData) return this.seriesData;  
   
var s = this.series,  
rows = {};  
   
/* The data grid is a 2 dimensions array. There is a row for each X value.  
* Each row contains the x value and the corresponding y value for each serie ('undefined' if there isn't one)  
**/  
_.each(s, function(serie, i){  
_.each(serie.data, function (v) {  
var x = v[0],  
y = v[1],  
r = rows[x];  
if (r) {  
r[i+1] = y;  
} else {  
var newRow = [];  
newRow[0] = x;  
newRow[i+1] = y;  
rows[x] = newRow;  
}  
});  
});  
   
// The data grid is sorted by x value  
this.seriesData = _.sortBy(rows, function(row, x){  
return parseInt(x, 10);  
});  
return this.seriesData;  
},  
/**  
* Constructs the data table for the spreadsheet  
* @todo make a spreadsheet manager (Flotr.Spreadsheet)  
* @return {Element} The resulting table element  
*/  
constructDataGrid: function(){  
// If the data grid has already been built, nothing to do here  
if (this.spreadsheet.datagrid) return this.spreadsheet.datagrid;  
   
var s = this.series,  
datagrid = this.spreadsheet.loadDataGrid(),  
colgroup = ['<colgroup><col />'],  
buttonDownload, buttonSelect, t;  
   
// First row : series' labels  
var html = ['<table class="flotr-datagrid"><tr class="first-row">'];  
html.push('<th>&nbsp;</th>');  
_.each(s, function(serie,i){  
html.push('<th scope="col">'+(serie.label || String.fromCharCode(65+i))+'</th>');  
colgroup.push('<col />');  
});  
html.push('</tr>');  
// Data rows  
_.each(datagrid, function(row){  
html.push('<tr>');  
_.times(s.length+1, function(i){  
var tag = 'td',  
value = row[i],  
// TODO: do we really want to handle problems with floating point  
// precision here?  
content = (!_.isUndefined(value) ? Math.round(value*100000)/100000 : '');  
if (i === 0) {  
tag = 'th';  
var label = getRowLabel.call(this, content);  
if (label) content = label;  
}  
   
html.push('<'+tag+(tag=='th'?' scope="row"':'')+'>'+content+'</'+tag+'>');  
}, this);  
html.push('</tr>');  
}, this);  
colgroup.push('</colgroup>');  
t = D.node(html.join(''));  
   
/**  
* @TODO disabled this  
if (!Flotr.isIE || Flotr.isIE == 9) {  
function handleMouseout(){ function handleMouseout(){
t.select('colgroup col.hover, th.hover').invoke('removeClassName', 'hover'); t.select('colgroup col.hover, th.hover').invoke('removeClassName', 'hover');
} }
function handleMouseover(e){ function handleMouseover(e){
var td = e.element(), var td = e.element(),
siblings = td.previousSiblings(); siblings = td.previousSiblings();
t.select('th[scope=col]')[siblings.length-1].addClassName('hover'); t.select('th[scope=col]')[siblings.length-1].addClassName('hover');
t.select('colgroup col')[siblings.length].addClassName('hover'); t.select('colgroup col')[siblings.length].addClassName('hover');
} }
_.each(t.select('td'), function(td) { _.each(t.select('td'), function(td) {
Flotr.EventAdapter. Flotr.EventAdapter.
observe(td, 'mouseover', handleMouseover). observe(td, 'mouseover', handleMouseover).
observe(td, 'mouseout', handleMouseout); observe(td, 'mouseout', handleMouseout);
}); });
} }
*/ */
   
buttonDownload = D.node( buttonDownload = D.node(
'<button type="button" class="flotr-datagrid-toolbar-button">' + '<button type="button" class="flotr-datagrid-toolbar-button">' +
this.options.spreadsheet.toolbarDownload + this.options.spreadsheet.toolbarDownload +
'</button>'); '</button>');
   
buttonSelect = D.node( buttonSelect = D.node(
'<button type="button" class="flotr-datagrid-toolbar-button">' + '<button type="button" class="flotr-datagrid-toolbar-button">' +
this.options.spreadsheet.toolbarSelectAll+ this.options.spreadsheet.toolbarSelectAll +
'</button>'); '</button>');
   
this. this.
observe(buttonDownload, 'click', _.bind(this.spreadsheet.downloadCSV, this)). observe(buttonDownload, 'click', _.bind(this.spreadsheet.downloadCSV, this)).
observe(buttonSelect, 'click', _.bind(this.spreadsheet.selectAllData, this)); observe(buttonSelect, 'click', _.bind(this.spreadsheet.selectAllData, this));
   
var toolbar = D.node('<div class="flotr-datagrid-toolbar"></div>'); var toolbar = D.node('<div class="flotr-datagrid-toolbar"></div>');
D.insert(toolbar, buttonDownload); D.insert(toolbar, buttonDownload);
D.insert(toolbar, buttonSelect); D.insert(toolbar, buttonSelect);
   
var containerHeight =this.canvasHeight - D.size(this.spreadsheet.tabsContainer).height-2, var containerHeight = this.canvasHeight - D.size(this.spreadsheet.tabsContainer).height - 2,
container = D.node('<div class="flotr-datagrid-container" style="position:absolute;left:0px;top:0px;width:'+ container = D.node('<div class="flotr-datagrid-container" style="position:absolute;left:0px;top:0px;width:' +
this.canvasWidth+'px;height:'+containerHeight+'px;overflow:auto;z-index:10"></div>'); this.canvasWidth + 'px;height:' + containerHeight + 'px;overflow:auto;z-index:10"></div>');
   
D.insert(container, toolbar); D.insert(container, toolbar);
D.insert(container, t); D.insert(container, t);
D.insert(this.el, container); D.insert(this.el, container);
this.spreadsheet.datagrid = t; this.spreadsheet.datagrid = t;
this.spreadsheet.container = container; this.spreadsheet.container = container;
   
return t; return t;
}, },
/** /**
* Shows the specified tab, by its name * Shows the specified tab, by its name
* @todo make a tab manager (Flotr.Tabs) * @todo make a tab manager (Flotr.Tabs)
* @param {String} tabName - The tab name * @param {String} tabName - The tab name
*/ */
showTab: function(tabName){ showTab: function (tabName) {
if (this.spreadsheet.activeTab === tabName){ if (this.spreadsheet.activeTab === tabName) {
return; return;
} }
switch(tabName) { switch (tabName) {
case 'graph': case 'graph':
D.hide(this.spreadsheet.container); D.hide(this.spreadsheet.container);
D.removeClass(this.spreadsheet.tabs.data, 'selected'); D.removeClass(this.spreadsheet.tabs.data, 'selected');
D.addClass(this.spreadsheet.tabs.graph, 'selected'); D.addClass(this.spreadsheet.tabs.graph, 'selected');
break; break;
case 'data': case 'data':
if (!this.spreadsheet.datagrid) if (!this.spreadsheet.datagrid)
this.spreadsheet.constructDataGrid(); this.spreadsheet.constructDataGrid();
D.show(this.spreadsheet.container); D.show(this.spreadsheet.container);
D.addClass(this.spreadsheet.tabs.data, 'selected'); D.addClass(this.spreadsheet.tabs.data, 'selected');
D.removeClass(this.spreadsheet.tabs.graph, 'selected'); D.removeClass(this.spreadsheet.tabs.graph, 'selected');
break; break;
default: default:
throw 'Illegal tab name: ' + tabName; throw 'Illegal tab name: ' + tabName;
} }
this.spreadsheet.activeTab = tabName; this.spreadsheet.activeTab = tabName;
}, },
/** /**
* Selects the data table in the DOM for copy/paste * Selects the data table in the DOM for copy/paste
*/ */
selectAllData: function(){ selectAllData: function () {
if (this.spreadsheet.tabs) { if (this.spreadsheet.tabs) {
var selection, range, doc, win, node = this.spreadsheet.constructDataGrid(); var selection, range, doc, win, node = this.spreadsheet.constructDataGrid();
   
this.spreadsheet.showTab('data'); this.spreadsheet.showTab('data');
   
// deferred to be able to select the table // deferred to be able to select the table
setTimeout(function () { setTimeout(function () {
if ((doc = node.ownerDocument) && (win = doc.defaultView) && if ((doc = node.ownerDocument) && (win = doc.defaultView) &&
win.getSelection && doc.createRange && win.getSelection && doc.createRange &&
(selection = window.getSelection()) && (selection = window.getSelection()) &&
selection.removeAllRanges) { selection.removeAllRanges) {
range = doc.createRange(); range = doc.createRange();
range.selectNode(node); range.selectNode(node);
selection.removeAllRanges(); selection.removeAllRanges();
selection.addRange(range); selection.addRange(range);
} }
else if (document.body && document.body.createTextRange && else if (document.body && document.body.createTextRange &&
(range = document.body.createTextRange())) { (range = document.body.createTextRange())) {
range.moveToElementText(node); range.moveToElementText(node);
range.select(); range.select();
} }
}, 0); }, 0);
return true; return true;
} }
else return false; else return false;
}, },
/** /**
* Converts the data into CSV in order to download a file * Converts the data into CSV in order to download a file
*/ */
downloadCSV: function(){ downloadCSV: function () {
var csv = '', var csv = '',
series = this.series, series = this.series,
options = this.options, options = this.options,
dg = this.spreadsheet.loadDataGrid(), dg = this.spreadsheet.loadDataGrid(),
separator = encodeURIComponent(options.spreadsheet.csvFileSeparator); separator = encodeURIComponent(options.spreadsheet.csvFileSeparator);
   
if (options.spreadsheet.decimalSeparator === options.spreadsheet.csvFileSeparator) { if (options.spreadsheet.decimalSeparator === options.spreadsheet.csvFileSeparator) {
throw "The decimal separator is the same as the column separator ("+options.spreadsheet.decimalSeparator+")"; throw "The decimal separator is the same as the column separator (" + options.spreadsheet.decimalSeparator + ")";
} }
   
// The first row // The first row
_.each(series, function(serie, i){ _.each(series, function (serie, i) {
csv += separator+'"'+(serie.label || String.fromCharCode(65+i)).replace(/\"/g, '\\"')+'"'; csv += separator + '"' + (serie.label || String.fromCharCode(65 + i)).replace(/\"/g, '\\"') + '"';
  });
   
  csv += "%0D%0A"; // \r\n
   
  // For each row
  csv += _.reduce(dg, function (memo, row) {
  var rowLabel = getRowLabel.call(this, row[0]) || '';
  rowLabel = '"' + (rowLabel + '').replace(/\"/g, '\\"') + '"';
  var numbers = row.slice(1).join(separator);
  if (options.spreadsheet.decimalSeparator !== '.') {
  numbers = numbers.replace(/\./g, options.spreadsheet.decimalSeparator);
  }
  return memo + rowLabel + separator + numbers + "%0D%0A"; // \t and \r\n
  }, '', this);
   
  if (Flotr.isIE && Flotr.isIE < 9) {
  csv = csv.replace(new RegExp(separator, 'g'), decodeURIComponent(separator)).replace(/%0A/g, '\n').replace(/%0D/g, '\r');
  window.open().document.write(csv);
  }
  else window.open('data:text/csv,' + csv);
  }
}); });
   
csv += "%0D%0A"; // \r\n  
   
// For each row  
csv += _.reduce(dg, function(memo, row){  
var rowLabel = getRowLabel.call(this, row[0]) || '';  
rowLabel = '"'+(rowLabel+'').replace(/\"/g, '\\"')+'"';  
var numbers = row.slice(1).join(separator);  
if (options.spreadsheet.decimalSeparator !== '.') {  
numbers = numbers.replace(/\./g, options.spreadsheet.decimalSeparator);  
}  
return memo + rowLabel+separator+numbers+"%0D%0A"; // \t and \r\n  
}, '', this);  
   
if (Flotr.isIE && Flotr.isIE < 9) {  
csv = csv.replace(new RegExp(separator, 'g'), decodeURIComponent(separator)).replace(/%0A/g, '\n').replace(/%0D/g, '\r');  
window.open().document.write(csv);  
}  
else window.open('data:text/csv,'+csv);  
}  
});  
})(); })();
   
(function () { (function () {
   
var D = Flotr.DOM; var D = Flotr.DOM;
   
Flotr.addPlugin('titles', { Flotr.addPlugin('titles', {
callbacks: { callbacks: {
'flotr:afterdraw': function() { 'flotr:afterdraw': function () {
this.titles.drawTitles(); this.titles.drawTitles();
} }
}, },
/** /**
* Draws the title and the subtitle * Draws the title and the subtitle
*/ */
drawTitles : function () { drawTitles: function () {
var html, var html,
options = this.options, options = this.options,
margin = options.grid.labelMargin, margin = options.grid.labelMargin,
ctx = this.ctx, ctx = this.ctx,
a = this.axes; a = this.axes;
   
if (!options.HtmlText && this.textEnabled) { if (!options.HtmlText && this.textEnabled) {
var style = { var style = {
size: options.fontSize, size: options.fontSize,
color: options.grid.color, color: options.grid.color,
textAlign: 'center' textAlign: 'center'
}; };
   
// Add subtitle // Add subtitle
if (options.subtitle){ if (options.subtitle) {
Flotr.drawText( Flotr.drawText(
ctx, options.subtitle, ctx, options.subtitle,
this.plotOffset.left + this.plotWidth/2, this.plotOffset.left + this.plotWidth / 2,
this.titleHeight + this.subtitleHeight - 2, this.titleHeight + this.subtitleHeight - 2,
style style
); );
} }
   
style.weight = 1.5; style.weight = 1.5;
style.size *= 1.5; style.size *= 1.5;
   
// Add title // Add title
if (options.title){ if (options.title) {
Flotr.drawText( Flotr.drawText(
ctx, options.title, ctx, options.title,
this.plotOffset.left + this.plotWidth/2, this.plotOffset.left + this.plotWidth / 2,
this.titleHeight - 2, this.titleHeight - 2,
style style
); );
} }
   
style.weight = 1.8; style.weight = 1.8;
style.size *= 0.8; style.size *= 0.8;
   
// Add x axis title // Add x axis title
if (a.x.options.title && a.x.used){ if (a.x.options.title && a.x.used) {
style.textAlign = a.x.options.titleAlign || 'center'; style.textAlign = a.x.options.titleAlign || 'center';
style.textBaseline = 'top'; style.textBaseline = 'top';
style.angle = Flotr.toRad(a.x.options.titleAngle); style.angle = Flotr.toRad(a.x.options.titleAngle);
style = Flotr.getBestTextAlign(style.angle, style); style = Flotr.getBestTextAlign(style.angle, style);
Flotr.drawText( Flotr.drawText(
ctx, a.x.options.title, ctx, a.x.options.title,
this.plotOffset.left + this.plotWidth/2, this.plotOffset.left + this.plotWidth / 2,
this.plotOffset.top + a.x.maxLabel.height + this.plotHeight + 2 * margin, this.plotOffset.top + a.x.maxLabel.height + this.plotHeight + 2 * margin,
style style
); );
} }
   
// Add x2 axis title // Add x2 axis title
if (a.x2.options.title && a.x2.used){ if (a.x2.options.title && a.x2.used) {
style.textAlign = a.x2.options.titleAlign || 'center'; style.textAlign = a.x2.options.titleAlign || 'center';
style.textBaseline = 'bottom'; style.textBaseline = 'bottom';
style.angle = Flotr.toRad(a.x2.options.titleAngle); style.angle = Flotr.toRad(a.x2.options.titleAngle);
style = Flotr.getBestTextAlign(style.angle, style); style = Flotr.getBestTextAlign(style.angle, style);
Flotr.drawText( Flotr.drawText(
ctx, a.x2.options.title, ctx, a.x2.options.title,
this.plotOffset.left + this.plotWidth/2, this.plotOffset.left + this.plotWidth / 2,
this.plotOffset.top - a.x2.maxLabel.height - 2 * margin, this.plotOffset.top - a.x2.maxLabel.height - 2 * margin,
style style
); );
} }
   
// Add y axis title // Add y axis title
if (a.y.options.title && a.y.used){ if (a.y.options.title && a.y.used) {
style.textAlign = a.y.options.titleAlign || 'right'; style.textAlign = a.y.options.titleAlign || 'right';
style.textBaseline = 'middle'; style.textBaseline = 'middle';
style.angle = Flotr.toRad(a.y.options.titleAngle); style.angle = Flotr.toRad(a.y.options.titleAngle);
style = Flotr.getBestTextAlign(style.angle, style); style = Flotr.getBestTextAlign(style.angle, style);
Flotr.drawText( Flotr.drawText(
ctx, a.y.options.title, ctx, a.y.options.title,
this.plotOffset.left - a.y.maxLabel.width - 2 * margin, this.plotOffset.left - a.y.maxLabel.width - 2 * margin,
this.plotOffset.top + this.plotHeight / 2, this.plotOffset.top + this.plotHeight / 2,
style style
); );
} }
   
// Add y2 axis title // Add y2 axis title
if (a.y2.options.title && a.y2.used){ if (a.y2.options.title && a.y2.used) {
style.textAlign = a.y2.options.titleAlign || 'left'; style.textAlign = a.y2.options.titleAlign || 'left';
style.textBaseline = 'middle'; style.textBaseline = 'middle';
style.angle = Flotr.toRad(a.y2.options.titleAngle); style.angle = Flotr.toRad(a.y2.options.titleAngle);
style = Flotr.getBestTextAlign(style.angle, style); style = Flotr.getBestTextAlign(style.angle, style);
Flotr.drawText( Flotr.drawText(
ctx, a.y2.options.title, ctx, a.y2.options.title,
this.plotOffset.left + this.plotWidth + a.y2.maxLabel.width + 2 * margin, this.plotOffset.left + this.plotWidth + a.y2.maxLabel.width + 2 * margin,
this.plotOffset.top + this.plotHeight / 2, this.plotOffset.top + this.plotHeight / 2,
style style
); );
} }
} }
else { else {
html = []; html = [];
   
// Add title // Add title
if (options.title) if (options.title)
html.push( html.push(
'<div style="position:absolute;top:0;left:', '<div style="position:absolute;top:0;left:',
this.plotOffset.left, 'px;font-size:1em;font-weight:bold;text-align:center;width:', this.plotOffset.left, 'px;font-size:1em;font-weight:bold;text-align:center;width:',
this.plotWidth,'px;" class="flotr-title">', options.title, '</div>' this.plotWidth, 'px;" class="flotr-title">', options.title, '</div>'
); );
   
// Add subtitle // Add subtitle
if (options.subtitle) if (options.subtitle)
html.push( html.push(
'<div style="position:absolute;top:', this.titleHeight, 'px;left:', '<div style="position:absolute;top:', this.titleHeight, 'px;left:',
this.plotOffset.left, 'px;font-size:smaller;text-align:center;width:', this.plotOffset.left, 'px;font-size:smaller;text-align:center;width:',
this.plotWidth, 'px;" class="flotr-subtitle">', options.subtitle, '</div>' this.plotWidth, 'px;" class="flotr-subtitle">', options.subtitle, '</div>'
); );
   
html.push('</div>'); html.push('</div>');
   
html.push('<div class="flotr-axis-title" style="font-weight:bold;">'); html.push('<div class="flotr-axis-title" style="font-weight:bold;">');
   
// Add x axis title // Add x axis title
if (a.x.options.title && a.x.used) if (a.x.options.title && a.x.used)
html.push( html.push(
'<div style="position:absolute;top:', '<div style="position:absolute;top:',
(this.plotOffset.top + this.plotHeight + options.grid.labelMargin + a.x.titleSize.height), (this.plotOffset.top + this.plotHeight + options.grid.labelMargin + a.x.titleSize.height),
'px;left:', this.plotOffset.left, 'px;width:', this.plotWidth, 'px;left:', this.plotOffset.left, 'px;width:', this.plotWidth,
'px;text-align:', a.x.options.titleAlign, ';" class="flotr-axis-title flotr-axis-title-x1">', a.x.options.title, '</div>' 'px;text-align:', a.x.options.titleAlign, ';" class="flotr-axis-title flotr-axis-title-x1">', a.x.options.title, '</div>'
); );
   
// Add x2 axis title // Add x2 axis title
if (a.x2.options.title && a.x2.used) if (a.x2.options.title && a.x2.used)
html.push( html.push(
'<div style="position:absolute;top:0;left:', this.plotOffset.left, 'px;width:', '<div style="position:absolute;top:0;left:', this.plotOffset.left, 'px;width:',
this.plotWidth, 'px;text-align:', a.x2.options.titleAlign, ';" class="flotr-axis-title flotr-axis-title-x2">', a.x2.options.title, '</div>' this.plotWidth, 'px;text-align:', a.x2.options.titleAlign, ';" class="flotr-axis-title flotr-axis-title-x2">', a.x2.options.title, '</div>'
); );
   
// Add y axis title // Add y axis title
if (a.y.options.title && a.y.used) if (a.y.options.title && a.y.used)
html.push( html.push(
'<div style="position:absolute;top:', '<div style="position:absolute;top:',
(this.plotOffset.top + this.plotHeight/2 - a.y.titleSize.height/2), (this.plotOffset.top + this.plotHeight / 2 - a.y.titleSize.height / 2),
'px;left:0;text-align:', a.y.options.titleAlign, ';" class="flotr-axis-title flotr-axis-title-y1">', a.y.options.title, '</div>' 'px;left:0;text-align:', a.y.options.titleAlign, ';" class="flotr-axis-title flotr-axis-title-y1">', a.y.options.title, '</div>'
); );
   
// Add y2 axis title // Add y2 axis title
if (a.y2.options.title && a.y2.used) if (a.y2.options.title && a.y2.used)
html.push( html.push(
'<div style="position:absolute;top:', '<div style="position:absolute;top:',
(this.plotOffset.top + this.plotHeight/2 - a.y.titleSize.height/2), (this.plotOffset.top + this.plotHeight / 2 - a.y.titleSize.height / 2),
'px;right:0;text-align:', a.y2.options.titleAlign, ';" class="flotr-axis-title flotr-axis-title-y2">', a.y2.options.title, '</div>' 'px;right:0;text-align:', a.y2.options.titleAlign, ';" class="flotr-axis-title flotr-axis-title-y2">', a.y2.options.title, '</div>'
); );
   
html = html.join(''); html = html.join('');
   
var div = D.create('div'); var div = D.create('div');
D.setStyles({ D.setStyles({
color: options.grid.color color: options.grid.color
}); });
div.className = 'flotr-titles'; div.className = 'flotr-titles';
D.insert(this.el, div); D.insert(this.el, div);
D.insert(div, html); D.insert(div, html);
} }
} }
}); });
})(); })();
   
(function () { (function () {
   
Flotr.ExampleList.add({ Flotr.ExampleList.add({
key : 'test-background', key: 'test-background',
name : 'Test Background', name: 'Test Background',
callback : test_background, callback: test_background,
timeout : 100, timeout: 100,
tolerance : 10 tolerance: 10
}); });
   
function test_background (container) { function test_background(container) {
   
var var
d1 = [], d1 = [],
d2 = [], d2 = [],
d3 = [], d3 = [],
d4 = [], d4 = [],
d5 = [], // Data d5 = [], // Data
ticks = [[ 0, "Lower"], 10, 20, 30, [40, "Upper"]], // Ticks for the Y-Axis ticks = [
graph; [ 0, "Lower"],
  10,
for(var i = 0; i <= 10; i += 0.1){ 20,
d1.push([i, 4 + Math.pow(i,1.5)]); 30,
d2.push([i, Math.pow(i,3)]); [40, "Upper"]
d3.push([i, i*5+3*Math.sin(i*4)]); ], // Ticks for the Y-Axis
d4.push([i, i]); graph;
if( i.toFixed(1)%1 == 0 ){  
d5.push([i, 2*i]); for (var i = 0; i <= 10; i += 0.1) {
  d1.push([i, 4 + Math.pow(i, 1.5)]);
  d2.push([i, Math.pow(i, 3)]);
  d3.push([i, i * 5 + 3 * Math.sin(i * 4)]);
  d4.push([i, i]);
  if (i.toFixed(1) % 1 == 0) {
  d5.push([i, 2 * i]);
  }
  }
   
  d3[30][1] = null;
  d3[31][1] = null;
   
  function ticksFn(n) {
  return '(' + n + ')';
  }
   
  graph = Flotr.draw(container, [
  { data: d1, label: 'y = 4 + x^(1.5)', lines: { fill: true } },
  { data: d2, label: 'y = x^3'},
  { data: d3, label: 'y = 5x + 3sin(4x)'},
  { data: d4, label: 'y = x'},
  { data: d5, label: 'y = 2x', lines: { show: true }, points: { show: true } }
  ], {
  xaxis: {
  noTicks: 7, // Display 7 ticks.
  tickFormatter: ticksFn, // Displays tick values between brackets.
  min: 1, // Part of the series is not displayed.
  max: 7.5 // Part of the series is not displayed.
  },
  yaxis: {
  ticks: ticks, // Set Y-Axis ticks
  max: 40 // Maximum value along Y-Axis
  },
  grid: {
  verticalLines: false,
  backgroundImage: {
  src: 'img/test-background.png?' + Math.random()
  }
  },
  legend: {
  position: 'nw'
  },
  title: 'Basic Axis example',
  subtitle: 'This is a subtitle'
  });
} }
}  
   
d3[30][1] = null;  
d3[31][1] = null;  
   
function ticksFn (n) { return '('+n+')'; }  
   
graph = Flotr.draw(container, [  
{ data : d1, label : 'y = 4 + x^(1.5)', lines : { fill : true } },  
{ data : d2, label : 'y = x^3'},  
{ data : d3, label : 'y = 5x + 3sin(4x)'},  
{ data : d4, label : 'y = x'},  
{ data : d5, label : 'y = 2x', lines : { show : true }, points : { show : true } }  
], {  
xaxis : {  
noTicks : 7, // Display 7 ticks.  
tickFormatter : ticksFn, // Displays tick values between brackets.  
min : 1, // Part of the series is not displayed.  
max : 7.5 // Part of the series is not displayed.  
},  
yaxis : {  
ticks : ticks, // Set Y-Axis ticks  
max : 40 // Maximum value along Y-Axis  
},  
grid : {  
verticalLines : false,  
backgroundImage : {  
src : 'img/test-background.png?' + Math.random()  
}  
},  
legend : {  
position : 'nw'  
},  
title : 'Basic Axis example',  
subtitle : 'This is a subtitle'  
});  
}  
   
})(); })();
   
(function () { (function () {
   
Flotr.ExampleList.add({ Flotr.ExampleList.add({
key : 'test-boundaries', key: 'test-boundaries',
name : 'Test Boundaries', name: 'Test Boundaries',
callback : test_boundaries callback: test_boundaries
}); });
   
function test_boundaries (container) { function test_boundaries(container) {
   
var var
d1 = [[0, 0], [5, 0], [6, 10], [9, 10]], // First data series d1 = [
i, graph; [0, 0],
  [5, 0],
  [6, 10],
  [9, 10]
  ], // First data series
  i, graph;
   
// Draw Graph // Draw Graph
graph = Flotr.draw(container, [ d1 ], { graph = Flotr.draw(container, [ d1 ], {
title : 'test', title: 'test',
xaxis: { xaxis: {
minorTickFreq: 4 minorTickFreq: 4
}, },
lines: { lines: {
lineWidth : 2 lineWidth: 2
}, },
grid: { grid: {
outlineWidth : 2, outlineWidth: 2,
minorVerticalLines: true minorVerticalLines: true
  }
  });
} }
});  
}  
   
})(); })();
   
(function () { (function () {
   
Flotr.ExampleList.add({ Flotr.ExampleList.add({
key : 'test-mountain-nulls', key: 'test-mountain-nulls',
name : 'Mountain Nulls', name: 'Mountain Nulls',
callback : function (container) { callback: function (container) {
var var
d1 = [[0, 3], [4, 8], [5, 6], [6, null], [7, 7], [8, 5]], // First data series d1 = [
d2 = [], // Second data series [0, 3],
i, graph; [4, 8],
  [5, 6],
  [6, null],
  [7, 7],
  [8, 5]
  ], // First data series
  d2 = [], // Second data series
  i, graph;
   
// Generate first data set // Generate first data set
for (i = 0; i < 14; i += 0.5) { for (i = 0; i < 14; i += 0.5) {
d2.push([i, Math.sin(i)]); d2.push([i, Math.sin(i)]);
} }
   
// Multiple nulls  
d2[9][1] = null;  
d2[10][1] = null;  
d2[11][1] = null;  
   
// Single not null surrounded by null  
d2[13][1] = null;  
   
// < 0 null  
d2[23][1] = null;  
   
// Draw Graph // Multiple nulls
graph = Flotr.draw(container, [ d1, d2 ], { d2[9][1] = null;
xaxis: { d2[10][1] = null;
minorTickFreq: 4 d2[11][1] = null;
},  
lines: { // Single not null surrounded by null
fill : true d2[13][1] = null;
},  
grid: { // < 0 null
minorVerticalLines: true d2[23][1] = null;
}  
  // Draw Graph
  graph = Flotr.draw(container, [ d1, d2 ], {
  xaxis: {
  minorTickFreq: 4
  },
  lines: {
  fill: true
  },
  grid: {
  minorVerticalLines: true
  }
  });
  },
  type: 'test'
}); });
},  
type : 'test'  
});  
   
})(); })();
   
/*! jQuery v@1.8.0 jquery.com | jquery.org/license */ /*! jQuery v@1.8.0 jquery.com | jquery.org/license */
(function(a,b){function G(a){var b=F[a]={};return p.each(a.split(s),function(a,c){b[c]=!0}),b}function J(a,c,d){if(d===b&&a.nodeType===1){var e="data-"+c.replace(I,"-$1").toLowerCase();d=a.getAttribute(e);if(typeof d=="string"){try{d=d==="true"?!0:d==="false"?!1:d==="null"?null:+d+""===d?+d:H.test(d)?p.parseJSON(d):d}catch(f){}p.data(a,c,d)}else d=b}return d}function K(a){var b;for(b in a){if(b==="data"&&p.isEmptyObject(a[b]))continue;if(b!=="toJSON")return!1}return!0}function ba(){return!1}function bb(){return!0}function bh(a){return!a||!a.parentNode||a.parentNode.nodeType===11}function bi(a,b){do a=a[b];while(a&&a.nodeType!==1);return a}function bj(a,b,c){b=b||0;if(p.isFunction(b))return p.grep(a,function(a,d){var e=!!b.call(a,d,a);return e===c});if(b.nodeType)return p.grep(a,function(a,d){return a===b===c});if(typeof b=="string"){var d=p.grep(a,function(a){return a.nodeType===1});if(be.test(b))return p.filter(b,d,!c);b=p.filter(b,d)}return p.grep(a,function(a,d){return p.inArray(a,b)>=0===c})}function bk(a){var b=bl.split("|"),c=a.createDocumentFragment();if(c.createElement)while(b.length)c.createElement(b.pop());return c}function bC(a,b){return a.getElementsByTagName(b)[0]||a.appendChild(a.ownerDocument.createElement(b))}function bD(a,b){if(b.nodeType!==1||!p.hasData(a))return;var c,d,e,f=p._data(a),g=p._data(b,f),h=f.events;if(h){delete g.handle,g.events={};for(c in h)for(d=0,e=h[c].length;d<e;d++)p.event.add(b,c,h[c][d])}g.data&&(g.data=p.extend({},g.data))}function bE(a,b){var c;if(b.nodeType!==1)return;b.clearAttributes&&b.clearAttributes(),b.mergeAttributes&&b.mergeAttributes(a),c=b.nodeName.toLowerCase(),c==="object"?(b.parentNode&&(b.outerHTML=a.outerHTML),p.support.html5Clone&&a.innerHTML&&!p.trim(b.innerHTML)&&(b.innerHTML=a.innerHTML)):c==="input"&&bv.test(a.type)?(b.defaultChecked=b.checked=a.checked,b.value!==a.value&&(b.value=a.value)):c==="option"?b.selected=a.defaultSelected:c==="input"||c==="textarea"?b.defaultValue=a.defaultValue:c==="script"&&b.text!==a.text&&(b.text=a.text),b.removeAttribute(p.expando)}function bF(a){return typeof a.getElementsByTagName!="undefined"?a.getElementsByTagName("*"):typeof a.querySelectorAll!="undefined"?a.querySelectorAll("*"):[]}function bG(a){bv.test(a.type)&&(a.defaultChecked=a.checked)}function bX(a,b){if(b in a)return b;var c=b.charAt(0).toUpperCase()+b.slice(1),d=b,e=bV.length;while(e--){b=bV[e]+c;if(b in a)return b}return d}function bY(a,b){return a=b||a,p.css(a,"display")==="none"||!p.contains(a.ownerDocument,a)}function bZ(a,b){var c,d,e=[],f=0,g=a.length;for(;f<g;f++){c=a[f];if(!c.style)continue;e[f]=p._data(c,"olddisplay"),b?(!e[f]&&c.style.display==="none"&&(c.style.display=""),c.style.display===""&&bY(c)&&(e[f]=p._data(c,"olddisplay",cb(c.nodeName)))):(d=bH(c,"display"),!e[f]&&d!=="none"&&p._data(c,"olddisplay",d))}for(f=0;f<g;f++){c=a[f];if(!c.style)continue;if(!b||c.style.display==="none"||c.style.display==="")c.style.display=b?e[f]||"":"none"}return a}function b$(a,b,c){var d=bO.exec(b);return d?Math.max(0,d[1]-(c||0))+(d[2]||"px"):b}function b_(a,b,c,d){var e=c===(d?"border":"content")?4:b==="width"?1:0,f=0;for(;e<4;e+=2)c==="margin"&&(f+=p.css(a,c+bU[e],!0)),d?(c==="content"&&(f-=parseFloat(bH(a,"padding"+bU[e]))||0),c!=="margin"&&(f-=parseFloat(bH(a,"border"+bU[e]+"Width"))||0)):(f+=parseFloat(bH(a,"padding"+bU[e]))||0,c!=="padding"&&(f+=parseFloat(bH(a,"border"+bU[e]+"Width"))||0));return f}function ca(a,b,c){var d=b==="width"?a.offsetWidth:a.offsetHeight,e=!0,f=p.support.boxSizing&&p.css(a,"boxSizing")==="border-box";if(d<=0){d=bH(a,b);if(d<0||d==null)d=a.style[b];if(bP.test(d))return d;e=f&&(p.support.boxSizingReliable||d===a.style[b]),d=parseFloat(d)||0}return d+b_(a,b,c||(f?"border":"content"),e)+"px"}function cb(a){if(bR[a])return bR[a];var b=p("<"+a+">").appendTo(e.body),c=b.css("display");b.remove();if(c==="none"||c===""){bI=e.body.appendChild(bI||p.extend(e.createElement("iframe"),{frameBorder:0,width:0,height:0}));if(!bJ||!bI.createElement)bJ=(bI.contentWindow||bI.contentDocument).document,bJ.write("<!doctype html><html><body>"),bJ.close();b=bJ.body.appendChild(bJ.createElement(a)),c=bH(b,"display"),e.body.removeChild(bI)}return bR[a]=c,c}function ch(a,b,c,d){var e;if(p.isArray(b))p.each(b,function(b,e){c||cd.test(a)?d(a,e):ch(a+"["+(typeof e=="object"?b:"")+"]",e,c,d)});else if(!c&&p.type(b)==="object")for(e in b)ch(a+"["+e+"]",b[e],c,d);else d(a,b)}function cy(a){return function(b,c){typeof b!="string"&&(c=b,b="*");var d,e,f,g=b.toLowerCase().split(s),h=0,i=g.length;if(p.isFunction(c))for(;h<i;h++)d=g[h],f=/^\+/.test(d),f&&(d=d.substr(1)||"*"),e=a[d]=a[d]||[],e[f?"unshift":"push"](c)}}function cz(a,c,d,e,f,g){f=f||c.dataTypes[0],g=g||{},g[f]=!0;var h,i=a[f],j=0,k=i?i.length:0,l=a===cu;for(;j<k&&(l||!h);j++)h=i[j](c,d,e),typeof h=="string"&&(!l||g[h]?h=b:(c.dataTypes.unshift(h),h=cz(a,c,d,e,h,g)));return(l||!h)&&!g["*"]&&(h=cz(a,c,d,e,"*",g)),h}function cA(a,c){var d,e,f=p.ajaxSettings.flatOptions||{};for(d in c)c[d]!==b&&((f[d]?a:e||(e={}))[d]=c[d]);e&&p.extend(!0,a,e)}function cB(a,c,d){var e,f,g,h,i=a.contents,j=a.dataTypes,k=a.responseFields;for(f in k)f in d&&(c[k[f]]=d[f]);while(j[0]==="*")j.shift(),e===b&&(e=a.mimeType||c.getResponseHeader("content-type"));if(e)for(f in i)if(i[f]&&i[f].test(e)){j.unshift(f);break}if(j[0]in d)g=j[0];else{for(f in d){if(!j[0]||a.converters[f+" "+j[0]]){g=f;break}h||(h=f)}g=g||h}if(g)return g!==j[0]&&j.unshift(g),d[g]}function cC(a,b){var c,d,e,f,g=a.dataTypes.slice(),h=g[0],i={},j=0;a.dataFilter&&(b=a.dataFilter(b,a.dataType));if(g[1])for(c in a.converters)i[c.toLowerCase()]=a.converters[c];for(;e=g[++j];)if(e!=="*"){if(h!=="*"&&h!==e){c=i[h+" "+e]||i["* "+e];if(!c)for(d in i){f=d.split(" ");if(f[1]===e){c=i[h+" "+f[0]]||i["* "+f[0]];if(c){c===!0?c=i[d]:i[d]!==!0&&(e=f[0],g.splice(j--,0,e));break}}}if(c!==!0)if(c&&a["throws"])b=c(b);else try{b=c(b)}catch(k){return{state:"parsererror",error:c?k:"No conversion from "+h+" to "+e}}}h=e}return{state:"success",data:b}}function cK(){try{return new a.XMLHttpRequest}catch(b){}}function cL(){try{return new a.ActiveXObject("Microsoft.XMLHTTP")}catch(b){}}function cT(){return setTimeout(function(){cM=b},0),cM=p.now()}function cU(a,b){p.each(b,function(b,c){var d=(cS[b]||[]).concat(cS["*"]),e=0,f=d.length;for(;e<f;e++)if(d[e].call(a,b,c))return})}function cV(a,b,c){var d,e=0,f=0,g=cR.length,h=p.Deferred().always(function(){delete i.elem}),i=function(){var b=cM||cT(),c=Math.max(0,j.startTime+j.duration-b),d=1-(c/j.duration||0),e=0,f=j.tweens.length;for(;e<f;e++)j.tweens[e].run(d);return h.notifyWith(a,[j,d,c]),d<1&&f?c:(h.resolveWith(a,[j]),!1)},j=h.promise({elem:a,props:p.extend({},b),opts:p.extend(!0,{specialEasing:{}},c),originalProperties:b,originalOptions:c,startTime:cM||cT(),duration:c.duration,tweens:[],createTween:function(b,c,d){var e=p.Tween(a,j.opts,b,c,j.opts.specialEasing[b]||j.opts.easing);return j.tweens.push(e),e},stop:function(b){var c=0,d=b?j.tweens.length:0;for(;c<d;c++)j.tweens[c].run(1);return b?h.resolveWith(a,[j,b]):h.rejectWith(a,[j,b]),this}}),k=j.props;cW(k,j.opts.specialEasing);for(;e<g;e++){d=cR[e].call(j,a,k,j.opts);if(d)return d}return cU(j,k),p.isFunction(j.opts.start)&&j.opts.start.call(a,j),p.fx.timer(p.extend(i,{anim:j,queue:j.opts.queue,elem:a})),j.progress(j.opts.progress).done(j.opts.done,j.opts.complete).fail(j.opts.fail).always(j.opts.always)}function cW(a,b){var c,d,e,f,g;for(c in a){d=p.camelCase(c),e=b[d],f=a[c],p.isArray(f)&&(e=f[1],f=a[c]=f[0]),c!==d&&(a[d]=f,delete a[c]),g=p.cssHooks[d];if(g&&"expand"in g){f=g.expand(f),delete a[d];for(c in f)c in a||(a[c]=f[c],b[c]=e)}else b[d]=e}}function cX(a,b,c){var d,e,f,g,h,i,j,k,l=this,m=a.style,n={},o=[],q=a.nodeType&&bY(a);c.queue||(j=p._queueHooks(a,"fx"),j.unqueued==null&&(j.unqueued=0,k=j.empty.fire,j.empty.fire=function(){j.unqueued||k()}),j.unqueued++,l.always(function(){l.always(function(){j.unqueued--,p.queue(a,"fx").length||j.empty.fire()})})),a.nodeType===1&&("height"in b||"width"in b)&&(c.overflow=[m.overflow,m.overflowX,m.overflowY],p.css(a,"display")==="inline"&&p.css(a,"float")==="none"&&(!p.support.inlineBlockNeedsLayout||cb(a.nodeName)==="inline"?m.display="inline-block":m.zoom=1)),c.overflow&&(m.overflow="hidden",p.support.shrinkWrapBlocks||l.done(function(){m.overflow=c.overflow[0],m.overflowX=c.overflow[1],m.overflowY=c.overflow[2]}));for(d in b){f=b[d];if(cO.exec(f)){delete b[d];if(f===(q?"hide":"show"))continue;o.push(d)}}g=o.length;if(g){h=p._data(a,"fxshow")||p._data(a,"fxshow",{}),q?p(a).show():l.done(function(){p(a).hide()}),l.done(function(){var b;p.removeData(a,"fxshow",!0);for(b in n)p.style(a,b,n[b])});for(d=0;d<g;d++)e=o[d],i=l.createTween(e,q?h[e]:0),n[e]=h[e]||p.style(a,e),e in h||(h[e]=i.start,q&&(i.end=i.start,i.start=e==="width"||e==="height"?1:0))}}function cY(a,b,c,d,e){return new cY.prototype.init(a,b,c,d,e)}function cZ(a,b){var c,d={height:a},e=0;for(;e<4;e+=2-b)c=bU[e],d["margin"+c]=d["padding"+c]=a;return b&&(d.opacity=d.width=a),d}function c_(a){return p.isWindow(a)?a:a.nodeType===9?a.defaultView||a.parentWindow:!1}var c,d,e=a.document,f=a.location,g=a.navigator,h=a.jQuery,i=a.$,j=Array.prototype.push,k=Array.prototype.slice,l=Array.prototype.indexOf,m=Object.prototype.toString,n=Object.prototype.hasOwnProperty,o=String.prototype.trim,p=function(a,b){return new p.fn.init(a,b,c)},q=/[\-+]?(?:\d*\.|)\d+(?:[eE][\-+]?\d+|)/.source,r=/\S/,s=/\s+/,t=r.test(" ")?/^[\s\xA0]+|[\s\xA0]+$/g:/^\s+|\s+$/g,u=/^(?:[^#<]*(<[\w\W]+>)[^>]*$|#([\w\-]*)$)/,v=/^<(\w+)\s*\/?>(?:<\/\1>|)$/,w=/^[\],:{}\s]*$/,x=/(?:^|:|,)(?:\s*\[)+/g,y=/\\(?:["\\\/bfnrt]|u[\da-fA-F]{4})/g,z=/"[^"\\\r\n]*"|true|false|null|-?(?:\d\d*\.|)\d+(?:[eE][\-+]?\d+|)/g,A=/^-ms-/,B=/-([\da-z])/gi,C=function(a,b){return(b+"").toUpperCase()},D=function(){e.addEventListener?(e.removeEventListener("DOMContentLoaded",D,!1),p.ready()):e.readyState==="complete"&&(e.detachEvent("onreadystatechange",D),p.ready())},E={};p.fn=p.prototype={constructor:p,init:function(a,c,d){var f,g,h,i;if(!a)return this;if(a.nodeType)return this.context=this[0]=a,this.length=1,this;if(typeof a=="string"){a.charAt(0)==="<"&&a.charAt(a.length-1)===">"&&a.length>=3?f=[null,a,null]:f=u.exec(a);if(f&&(f[1]||!c)){if(f[1])return c=c instanceof p?c[0]:c,i=c&&c.nodeType?c.ownerDocument||c:e,a=p.parseHTML(f[1],i,!0),v.test(f[1])&&p.isPlainObject(c)&&this.attr.call(a,c,!0),p.merge(this,a);g=e.getElementById(f[2]);if(g&&g.parentNode){if(g.id!==f[2])return d.find(a);this.length=1,this[0]=g}return this.context=e,this.selector=a,this}return!c||c.jquery?(c||d).find(a):this.constructor(c).find(a)}return p.isFunction(a)?d.ready(a):(a.selector!==b&&(this.selector=a.selector,this.context=a.context),p.makeArray(a,this))},selector:"",jquery:"1.8.0",length:0,size:function(){return this.length},toArray:function(){return k.call(this)},get:function(a){return a==null?this.toArray():a<0?this[this.length+a]:this[a]},pushStack:function(a,b,c){var d=p.merge(this.constructor(),a);return d.prevObject=this,d.context=this.context,b==="find"?d.selector=this.selector+(this.selector?" ":"")+c:b&&(d.selector=this.selector+"."+b+"("+c+")"),d},each:function(a,b){return p.each(this,a,b)},ready:function(a){return p.ready.promise().done(a),this},eq:function(a){return a=+a,a===-1?this.slice(a):this.slice(a,a+1)},first:function(){return this.eq(0)},last:function(){return this.eq(-1)},slice:function(){return this.pushStack(k.apply(this,arguments),"slice",k.call(arguments).join(","))},map:function(a){return this.pushStack(p.map(this,function(b,c){return a.call(b,c,b)}))},end:function(){return this.prevObject||this.constructor(null)},push:j,sort:[].sort,splice:[].splice},p.fn.init.prototype=p.fn,p.extend=p.fn.extend=function(){var a,c,d,e,f,g,h=arguments[0]||{},i=1,j=arguments.length,k=!1;typeof h=="boolean"&&(k=h,h=arguments[1]||{},i=2),typeof h!="object"&&!p.isFunction(h)&&(h={}),j===i&&(h=this,--i);for(;i<j;i++)if((a=arguments[i])!=null)for(c in a){d=h[c],e=a[c];if(h===e)continue;k&&e&&(p.isPlainObject(e)||(f=p.isArray(e)))?(f?(f=!1,g=d&&p.isArray(d)?d:[]):g=d&&p.isPlainObject(d)?d:{},h[c]=p.extend(k,g,e)):e!==b&&(h[c]=e)}return h},p.extend({noConflict:function(b){return a.$===p&&(a.$=i),b&&a.jQuery===p&&(a.jQuery=h),p},isReady:!1,readyWait:1,holdReady:function(a){a?p.readyWait++:p.ready(!0)},ready:function(a){if(a===!0?--p.readyWait:p.isReady)return;if(!e.body)return setTimeout(p.ready,1);p.isReady=!0;if(a!==!0&&--p.readyWait>0)return;d.resolveWith(e,[p]),p.fn.trigger&&p(e).trigger("ready").off("ready")},isFunction:function(a){return p.type(a)==="function"},isArray:Array.isArray||function(a){return p.type(a)==="array"},isWindow:function(a){return a!=null&&a==a.window},isNumeric:function(a){return!isNaN(parseFloat(a))&&isFinite(a)},type:function(a){return a==null?String(a):E[m.call(a)]||"object"},isPlainObject:function(a){if(!a||p.type(a)!=="object"||a.nodeType||p.isWindow(a))return!1;try{if(a.constructor&&!n.call(a,"constructor")&&!n.call(a.constructor.prototype,"isPrototypeOf"))return!1}catch(c){return!1}var d;for(d in a);return d===b||n.call(a,d)},isEmptyObject:function(a){var b;for(b in a)return!1;return!0},error:function(a){throw new Error(a)},parseHTML:function(a,b,c){var d;return!a||typeof a!="string"?null:(typeof b=="boolean"&&(c=b,b=0),b=b||e,(d=v.exec(a))?[b.createElement(d[1])]:(d=p.buildFragment([a],b,c?null:[]),p.merge([],(d.cacheable?p.clone(d.fragment):d.fragment).childNodes)))},parseJSON:function(b){if(!b||typeof b!="string")return null;b=p.trim(b);if(a.JSON&&a.JSON.parse)return a.JSON.parse(b);if(w.test(b.replace(y,"@").replace(z,"]").replace(x,"")))return(new Function("return "+b))();p.error("Invalid JSON: "+b)},parseXML:function(c){var d,e;if(!c||typeof c!="string")return null;try{a.DOMParser?(e=new DOMParser,d=e.parseFromString(c,"text/xml")):(d=new ActiveXObject("Microsoft.XMLDOM"),d.async="false",d.loadXML(c))}catch(f){d=b}return(!d||!d.documentElement||d.getElementsByTagName("parsererror").length)&&p.error("Invalid XML: "+c),d},noop:function(){},globalEval:function(b){b&&r.test(b)&&(a.execScript||function(b){a.eval.call(a,b)})(b)},camelCase:function(a){return a.replace(A,"ms-").replace(B,C)},nodeName:function(a,b){return a.nodeName&&a.nodeName.toUpperCase()===b.toUpperCase()},each:function(a,c,d){var e,f=0,g=a.length,h=g===b||p.isFunction(a);if(d){if(h){for(e in a)if(c.apply(a[e],d)===!1)break}else for(;f<g;)if(c.apply(a[f++],d)===!1)break}else if(h){for(e in a)if(c.call(a[e],e,a[e])===!1)break}else for(;f<g;)if(c.call(a[f],f,a[f++])===!1)break;return a},trim:o?function(a){return a==null?"":o.call(a)}:function(a){return a==null?"":a.toString().replace(t,"")},makeArray:function(a,b){var c,d=b||[];return a!=null&&(c=p.type(a),a.length==null||c==="string"||c==="function"||c==="regexp"||p.isWindow(a)?j.call(d,a):p.merge(d,a)),d},inArray:function(a,b,c){var d;if(b){if(l)return l.call(b,a,c);d=b.length,c=c?c<0?Math.max(0,d+c):c:0;for(;c<d;c++)if(c in b&&b[c]===a)return c}return-1},merge:function(a,c){var d=c.length,e=a.length,f=0;if(typeof d=="number")for(;f<d;f++)a[e++]=c[f];else while(c[f]!==b)a[e++]=c[f++];return a.length=e,a},grep:function(a,b,c){var d,e=[],f=0,g=a.length;c=!!c;for(;f<g;f++)d=!!b(a[f],f),c!==d&&e.push(a[f]);return e},map:function(a,c,d){var e,f,g=[],h=0,i=a.length,j=a instanceof p||i!==b&&typeof i=="number"&&(i>0&&a[0]&&a[i-1]||i===0||p.isArray(a));if(j)for(;h<i;h++)e=c(a[h],h,d),e!=null&&(g[g.length]=e);else for(f in a)e=c(a[f],f,d),e!=null&&(g[g.length]=e);return g.concat.apply([],g)},guid:1,proxy:function(a,c){var d,e,f;return typeof c=="string"&&(d=a[c],c=a,a=d),p.isFunction(a)?(e=k.call(arguments,2),f=function(){return a.apply(c,e.concat(k.call(arguments)))},f.guid=a.guid=a.guid||f.guid||p.guid++,f):b},access:function(a,c,d,e,f,g,h){var i,j=d==null,k=0,l=a.length;if(d&&typeof d=="object"){for(k in d)p.access(a,c,k,d[k],1,g,e);f=1}else if(e!==b){i=h===b&&p.isFunction(e),j&&(i?(i=c,c=function(a,b,c){return i.call(p(a),c)}):(c.call(a,e),c=null));if(c)for(;k<l;k++)c(a[k],d,i?e.call(a[k],k,c(a[k],d)):e,h);f=1}return f?a:j?c.call(a):l?c(a[0],d):g},now:function(){return(new Date).getTime()}}),p.ready.promise=function(b){if(!d){d=p.Deferred();if(e.readyState==="complete"||e.readyState!=="loading"&&e.addEventListener)setTimeout(p.ready,1);else if(e.addEventListener)e.addEventListener("DOMContentLoaded",D,!1),a.addEventListener("load",p.ready,!1);else{e.attachEvent("onreadystatechange",D),a.attachEvent("onload",p.ready);var c=!1;try{c=a.frameElement==null&&e.documentElement}catch(f){}c&&c.doScroll&&function g(){if(!p.isReady){try{c.doScroll("left")}catch(a){return setTimeout(g,50)}p.ready()}}()}}return d.promise(b)},p.each("Boolean Number String Function Array Date RegExp Object".split(" "),function(a,b){E["[object "+b+"]"]=b.toLowerCase()}),c=p(e);var F={};p.Callbacks=function(a){a=typeof a=="string"?F[a]||G(a):p.extend({},a);var c,d,e,f,g,h,i=[],j=!a.once&&[],k=function(b){c=a.memory&&b,d=!0,h=f||0,f=0,g=i.length,e=!0;for(;i&&h<g;h++)if(i[h].apply(b[0],b[1])===!1&&a.stopOnFalse){c=!1;break}e=!1,i&&(j?j.length&&k(j.shift()):c?i=[]:l.disable())},l={add:function(){if(i){var b=i.length;(function d(b){p.each(b,function(b,c){p.isFunction(c)&&(!a.unique||!l.has(c))?i.push(c):c&&c.length&&d(c)})})(arguments),e?g=i.length:c&&(f=b,k(c))}return this},remove:function(){return i&&p.each(arguments,function(a,b){var c;while((c=p.inArray(b,i,c))>-1)i.splice(c,1),e&&(c<=g&&g--,c<=h&&h--)}),this},has:function(a){return p.inArray(a,i)>-1},empty:function(){return i=[],this},disable:function(){return i=j=c=b,this},disabled:function(){return!i},lock:function(){return j=b,c||l.disable(),this},locked:function(){return!j},fireWith:function(a,b){return b=b||[],b=[a,b.slice?b.slice():b],i&&(!d||j)&&(e?j.push(b):k(b)),this},fire:function(){return l.fireWith(this,arguments),this},fired:function(){return!!d}};return l},p.extend({Deferred:function(a){var b=[["resolve","done",p.Callbacks("once memory"),"resolved"],["reject","fail",p.Callbacks("once memory"),"rejected"],["notify","progress",p.Callbacks("memory")]],c="pending",d={state:function(){return c},always:function(){return e.done(arguments).fail(arguments),this},then:function(){var a=arguments;return p.Deferred(function(c){p.each(b,function(b,d){var f=d[0],g=a[b];e[d[1]](p.isFunction(g)?function(){var a=g.apply(this,arguments);a&&p.isFunction(a.promise)?a.promise().done(c.resolve).fail(c.reject).progress(c.notify):c[f+"With"](this===e?c:this,[a])}:c[f])}),a=null}).promise()},promise:function(a){return typeof a=="object"?p.extend(a,d):d}},e={};return d.pipe=d.then,p.each(b,function(a,f){var g=f[2],h=f[3];d[f[1]]=g.add,h&&g.add(function(){c=h},b[a^1][2].disable,b[2][2].lock),e[f[0]]=g.fire,e[f[0]+"With"]=g.fireWith}),d.promise(e),a&&a.call(e,e),e},when:function(a){var b=0,c=k.call(arguments),d=c.length,e=d!==1||a&&p.isFunction(a.promise)?d:0,f=e===1?a:p.Deferred(),g=function(a,b,c){return function(d){b[a]=this,c[a]=arguments.length>1?k.call(arguments):d,c===h?f.notifyWith(b,c):--e||f.resolveWith(b,c)}},h,i,j;if(d>1){h=new Array(d),i=new Array(d),j=new Array(d);for(;b<d;b++)c[b]&&p.isFunction(c[b].promise)?c[b].promise().done(g(b,j,c)).fail(f.reject).progress(g(b,i,h)):--e}return e||f.resolveWith(j,c),f.promise()}}),p.support=function(){var b,c,d,f,g,h,i,j,k,l,m,n=e.createElement("div");n.setAttribute("className","t"),n.innerHTML=" <link/><table></table><a href='/a'>a</a><input type='checkbox'/>",c=n.getElementsByTagName("*"),d=n.getElementsByTagName("a")[0],d.style.cssText="top:1px;float:left;opacity:.5";if(!c||!c.length||!d)return{};f=e.createElement("select"),g=f.appendChild(e.createElement("option")),h=n.getElementsByTagName("input")[0],b={leadingWhitespace:n.firstChild.nodeType===3,tbody:!n.getElementsByTagName("tbody").length,htmlSerialize:!!n.getElementsByTagName("link").length,style:/top/.test(d.getAttribute("style")),hrefNormalized:d.getAttribute("href")==="/a",opacity:/^0.5/.test(d.style.opacity),cssFloat:!!d.style.cssFloat,checkOn:h.value==="on",optSelected:g.selected,getSetAttribute:n.className!=="t",enctype:!!e.createElement("form").enctype,html5Clone:e.createElement("nav").cloneNode(!0).outerHTML!=="<:nav></:nav>",boxModel:e.compatMode==="CSS1Compat",submitBubbles:!0,changeBubbles:!0,focusinBubbles:!1,deleteExpando:!0,noCloneEvent:!0,inlineBlockNeedsLayout:!1,shrinkWrapBlocks:!1,reliableMarginRight:!0,boxSizingReliable:!0,pixelPosition:!1},h.checked=!0,b.noCloneChecked=h.cloneNode(!0).checked,f.disabled=!0,b.optDisabled=!g.disabled;try{delete n.test}catch(o){b.deleteExpando=!1}!n.addEventListener&&n.attachEvent&&n.fireEvent&&(n.attachEvent("onclick",m=function(){b.noCloneEvent=!1}),n.cloneNode(!0).fireEvent("onclick"),n.detachEvent("onclick",m)),h=e.createElement("input"),h.value="t",h.setAttribute("type","radio"),b.radioValue=h.value==="t",h.setAttribute("checked","checked"),h.setAttribute("name","t"),n.appendChild(h),i=e.createDocumentFragment(),i.appendChild(n.lastChild),b.checkClone=i.cloneNode(!0).cloneNode(!0).lastChild.checked,b.appendChecked=h.checked,i.removeChild(h),i.appendChild(n);if(n.attachEvent)for(k in{submit:!0,change:!0,focusin:!0})j="on"+k,l=j in n,l||(n.setAttribute(j,"return;"),l=typeof n[j]=="function"),b[k+"Bubbles"]=l;return p(function(){var c,d,f,g,h="padding:0;margin:0;border:0;display:block;overflow:hidden;",i=e.getElementsByTagName("body")[0];if(!i)return;c=e.createElement("div"),c.style.cssText="visibility:hidden;border:0;width:0;height:0;position:static;top:0;margin-top:1px",i.insertBefore(c,i.firstChild),d=e.createElement("div"),c.appendChild(d),d.innerHTML="<table><tr><td></td><td>t</td></tr></table>",f=d.getElementsByTagName("td"),f[0].style.cssText="padding:0;margin:0;border:0;display:none",l=f[0].offsetHeight===0,f[0].style.display="",f[1].style.display="none",b.reliableHiddenOffsets=l&&f[0].offsetHeight===0,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%;",b.boxSizing=d.offsetWidth===4,b.doesNotIncludeMarginInBodyOffset=i.offsetTop!==1,a.getComputedStyle&&(b.pixelPosition=(a.getComputedStyle(d,null)||{}).top!=="1%",b.boxSizingReliable=(a.getComputedStyle(d,null)||{width:"4px"}).width==="4px",g=e.createElement("div"),g.style.cssText=d.style.cssText=h,g.style.marginRight=g.style.width="0",d.style.width="1px",d.appendChild(g),b.reliableMarginRight=!parseFloat((a.getComputedStyle(g,null)||{}).marginRight)),typeof d.style.zoom!="undefined"&&(d.innerHTML="",d.style.cssText=h+"width:1px;padding:1px;display:inline;zoom:1",b.inlineBlockNeedsLayout=d.offsetWidth===3,d.style.display="block",d.style.overflow="visible",d.innerHTML="<div></div>",d.firstChild.style.width="5px",b.shrinkWrapBlocks=d.offsetWidth!==3,c.style.zoom=1),i.removeChild(c),c=d=f=g=null}),i.removeChild(n),c=d=f=g=h=i=n=null,b}();var H=/^(?:\{.*\}|\[.*\])$/,I=/([A-Z])/g;p.extend({cache:{},deletedIds:[],uuid:0,expando:"jQuery"+(p.fn.jquery+Math.random()).replace(/\D/g,""),noData:{embed:!0,object:"clsid:D27CDB6E-AE6D-11cf-96B8-444553540000",applet:!0},hasData:function(a){return a=a.nodeType?p.cache[a[p.expando]]:a[p.expando],!!a&&!K(a)},data:function(a,c,d,e){if(!p.acceptData(a))return;var f,g,h=p.expando,i=typeof c=="string",j=a.nodeType,k=j?p.cache:a,l=j?a[h]:a[h]&&h;if((!l||!k[l]||!e&&!k[l].data)&&i&&d===b)return;l||(j?a[h]=l=p.deletedIds.pop()||++p.uuid:l=h),k[l]||(k[l]={},j||(k[l].toJSON=p.noop));if(typeof c=="object"||typeof c=="function")e?k[l]=p.extend(k[l],c):k[l].data=p.extend(k[l].data,c);return f=k[l],e||(f.data||(f.data={}),f=f.data),d!==b&&(f[p.camelCase(c)]=d),i?(g=f[c],g==null&&(g=f[p.camelCase(c)])):g=f,g},removeData:function(a,b,c){if(!p.acceptData(a))return;var d,e,f,g=a.nodeType,h=g?p.cache:a,i=g?a[p.expando]:p.expando;if(!h[i])return;if(b){d=c?h[i]:h[i].data;if(d){p.isArray(b)||(b in d?b=[b]:(b=p.camelCase(b),b in d?b=[b]:b=b.split(" ")));for(e=0,f=b.length;e<f;e++)delete d[b[e]];if(!(c?K:p.isEmptyObject)(d))return}}if(!c){delete h[i].data;if(!K(h[i]))return}g?p.cleanData([a],!0):p.support.deleteExpando||h!=h.window?delete h[i]:h[i]=null},_data:function(a,b,c){return p.data(a,b,c,!0)},acceptData:function(a){var b=a.nodeName&&p.noData[a.nodeName.toLowerCase()];return!b||b!==!0&&a.getAttribute("classid")===b}}),p.fn.extend({data:function(a,c){var d,e,f,g,h,i=this[0],j=0,k=null;if(a===b){if(this.length){k=p.data(i);if(i.nodeType===1&&!p._data(i,"parsedAttrs")){f=i.attributes;for(h=f.length;j<h;j++)g=f[j].name,g.indexOf("data-")===0&&(g=p.camelCase(g.substring(5)),J(i,g,k[g]));p._data(i,"parsedAttrs",!0)}}return k}return typeof a=="object"?this.each(function(){p.data(this,a)}):(d=a.split(".",2),d[1]=d[1]?"."+d[1]:"",e=d[1]+"!",p.access(this,function(c){if(c===b)return k=this.triggerHandler("getData"+e,[d[0]]),k===b&&i&&(k=p.data(i,a),k=J(i,a,k)),k===b&&d[1]?this.data(d[0]):k;d[1]=c,this.each(function(){var b=p(this);b.triggerHandler("setData"+e,d),p.data(this,a,c),b.triggerHandler("changeData"+e,d)})},null,c,arguments.length>1,null,!1))},removeData:function(a){return this.each(function(){p.removeData(this,a)})}}),p.extend({queue:function(a,b,c){var d;if(a)return b=(b||"fx")+"queue",d=p._data(a,b),c&&(!d||p.isArray(c)?d=p._data(a,b,p.makeArray(c)):d.push(c)),d||[]},dequeue:function(a,b){b=b||"fx";var c=p.queue(a,b),d=c.shift(),e=p._queueHooks(a,b),f=function(){p.dequeue(a,b)};d==="inprogress"&&(d=c.shift()),d&&(b==="fx"&&c.unshift("inprogress"),delete e.stop,d.call(a,f,e)),!c.length&&e&&e.empty.fire()},_queueHooks:function(a,b){var c=b+"queueHooks";return p._data(a,c)||p._data(a,c,{empty:p.Callbacks("once memory").add(function(){p.removeData(a,b+"queue",!0),p.removeData(a,c,!0)})})}}),p.fn.extend({queue:function(a,c){var d=2;return typeof a!="string"&&(c=a,a="fx",d--),arguments.length<d?p.queue(this[0],a):c===b?this:this.each(function(){var b=p.queue(this,a,c);p._queueHooks(this,a),a==="fx"&&b[0]!=="inprogress"&&p.dequeue(this,a)})},dequeue:function(a){return this.each(function(){p.dequeue(this,a)})},delay:function(a,b){return a=p.fx?p.fx.speeds[a]||a:a,b=b||"fx",this.queue(b,function(b,c){var d=setTimeout(b,a);c.stop=function(){clearTimeout(d)}})},clearQueue:function(a){return this.queue(a||"fx",[])},promise:function(a,c){var d,e=1,f=p.Deferred(),g=this,h=this.length,i=function(){--e||f.resolveWith(g,[g])};typeof a!="string"&&(c=a,a=b),a=a||"fx";while(h--)(d=p._data(g[h],a+"queueHooks"))&&d.empty&&(e++,d.empty.add(i));return i(),f.promise(c)}});var L,M,N,O=/[\t\r\n]/g,P=/\r/g,Q=/^(?:button|input)$/i,R=/^(?:button|input|object|select|textarea)$/i,S=/^a(?:rea|)$/i,T=/^(?:autofocus|autoplay|async|checked|controls|defer|disabled|hidden|loop|multiple|open|readonly|required|scoped|selected)$/i,U=p.support.getSetAttribute;p.fn.extend({attr:function(a,b){return p.access(this,p.attr,a,b,arguments.length>1)},removeAttr:function(a){return this.each(function(){p.removeAttr(this,a)})},prop:function(a,b){return p.access(this,p.prop,a,b,arguments.length>1)},removeProp:function(a){return a=p.propFix[a]||a,this.each(function(){try{this[a]=b,delete this[a]}catch(c){}})},addClass:function(a){var b,c,d,e,f,g,h;if(p.isFunction(a))return this.each(function(b){p(this).addClass(a.call(this,b,this.className))});if(a&&typeof a=="string"){b=a.split(s);for(c=0,d=this.length;c<d;c++){e=this[c];if(e.nodeType===1)if(!e.className&&b.length===1)e.className=a;else{f=" "+e.className+" ";for(g=0,h=b.length;g<h;g++)~f.indexOf(" "+b[g]+" ")||(f+=b[g]+" ");e.className=p.trim(f)}}}return this},removeClass:function(a){var c,d,e,f,g,h,i;if(p.isFunction(a))return this.each(function(b){p(this).removeClass(a.call(this,b,this.className))});if(a&&typeof a=="string"||a===b){c=(a||"").split(s);for(h=0,i=this.length;h<i;h++){e=this[h];if(e.nodeType===1&&e.className){d=(" "+e.className+" ").replace(O," ");for(f=0,g=c.length;f<g;f++)while(d.indexOf(" "+c[f]+" ")>-1)d=d.replace(" "+c[f]+" "," ");e.className=a?p.trim(d):""}}}return this},toggleClass:function(a,b){var c=typeof a,d=typeof b=="boolean";return p.isFunction(a)?this.each(function(c){p(this).toggleClass(a.call(this,c,this.className,b),b)}):this.each(function(){if(c==="string"){var e,f=0,g=p(this),h=b,i=a.split(s);while(e=i[f++])h=d?h:!g.hasClass(e),g[h?"addClass":"removeClass"](e)}else if(c==="undefined"||c==="boolean")this.className&&p._data(this,"__className__",this.className),this.className=this.className||a===!1?"":p._data(this,"__className__")||""})},hasClass:function(a){var b=" "+a+" ",c=0,d=this.length;for(;c<d;c++)if(this[c].nodeType===1&&(" "+this[c].className+" ").replace(O," ").indexOf(b)>-1)return!0;return!1},val:function(a){var c,d,e,f=this[0];if(!arguments.length){if(f)return c=p.valHooks[f.type]||p.valHooks[f.nodeName.toLowerCase()],c&&"get"in c&&(d=c.get(f,"value"))!==b?d:(d=f.value,typeof d=="string"?d.replace(P,""):d==null?"":d);return}return e=p.isFunction(a),this.each(function(d){var f,g=p(this);if(this.nodeType!==1)return;e?f=a.call(this,d,g.val()):f=a,f==null?f="":typeof f=="number"?f+="":p.isArray(f)&&(f=p.map(f,function(a){return a==null?"":a+""})),c=p.valHooks[this.type]||p.valHooks[this.nodeName.toLowerCase()];if(!c||!("set"in c)||c.set(this,f,"value")===b)this.value=f})}}),p.extend({valHooks:{option:{get:function(a){var b=a.attributes.value;return!b||b.specified?a.value:a.text}},select:{get:function(a){var b,c,d,e,f=a.selectedIndex,g=[],h=a.options,i=a.type==="select-one";if(f<0)return null;c=i?f:0,d=i?f+1:h.length;for(;c<d;c++){e=h[c];if(e.selected&&(p.support.optDisabled?!e.disabled:e.getAttribute("disabled")===null)&&(!e.parentNode.disabled||!p.nodeName(e.parentNode,"optgroup"))){b=p(e).val();if(i)return b;g.push(b)}}return i&&!g.length&&h.length?p(h[f]).val():g},set:function(a,b){var c=p.makeArray(b);return p(a).find("option").each(function(){this.selected=p.inArray(p(this).val(),c)>=0}),c.length||(a.selectedIndex=-1),c}}},attrFn:{},attr:function(a,c,d,e){var f,g,h,i=a.nodeType;if(!a||i===3||i===8||i===2)return;if(e&&p.isFunction(p.fn[c]))return p(a)[c](d);if(typeof a.getAttribute=="undefined")return p.prop(a,c,d);h=i!==1||!p.isXMLDoc(a),h&&(c=c.toLowerCase(),g=p.attrHooks[c]||(T.test(c)?M:L));if(d!==b){if(d===null){p.removeAttr(a,c);return}return g&&"set"in g&&h&&(f=g.set(a,d,c))!==b?f:(a.setAttribute(c,""+d),d)}return g&&"get"in g&&h&&(f=g.get(a,c))!==null?f:(f=a.getAttribute(c),f===null?b:f)},removeAttr:function(a,b){var c,d,e,f,g=0;if(b&&a.nodeType===1){d=b.split(s);for(;g<d.length;g++)e=d[g],e&&(c=p.propFix[e]||e,f=T.test(e),f||p.attr(a,e,""),a.removeAttribute(U?e:c),f&&c in a&&(a[c]=!1))}},attrHooks:{type:{set:function(a,b){if(Q.test(a.nodeName)&&a.parentNode)p.error("type property can't be changed");else if(!p.support.radioValue&&b==="radio"&&p.nodeName(a,"input")){var c=a.value;return a.setAttribute("type",b),c&&(a.value=c),b}}},value:{get:function(a,b){return L&&p.nodeName(a,"button")?L.get(a,b):b in a?a.value:null},set:function(a,b,c){if(L&&p.nodeName(a,"button"))return L.set(a,b,c);a.value=b}}},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(a,c,d){var e,f,g,h=a.nodeType;if(!a||h===3||h===8||h===2)return;return g=h!==1||!p.isXMLDoc(a),g&&(c=p.propFix[c]||c,f=p.propHooks[c]),d!==b?f&&"set"in f&&(e=f.set(a,d,c))!==b?e:a[c]=d:f&&"get"in f&&(e=f.get(a,c))!==null?e:a[c]},propHooks:{tabIndex:{get:function(a){var c=a.getAttributeNode("tabindex");return c&&c.specified?parseInt(c.value,10):R.test(a.nodeName)||S.test(a.nodeName)&&a.href?0:b}}}}),M={get:function(a,c){var d,e=p.prop(a,c);return e===!0||typeof e!="boolean"&&(d=a.getAttributeNode(c))&&d.nodeValue!==!1?c.toLowerCase():b},set:function(a,b,c){var d;return b===!1?p.removeAttr(a,c):(d=p.propFix[c]||c,d in a&&(a[d]=!0),a.setAttribute(c,c.toLowerCase())),c}},U||(N={name:!0,id:!0,coords:!0},L=p.valHooks.button={get:function(a,c){var d;return d=a.getAttributeNode(c),d&&(N[c]?d.value!=="":d.specified)?d.value:b},set:function(a,b,c){var d=a.getAttributeNode(c);return d||(d=e.createAttribute(c),a.setAttributeNode(d)),d.value=b+""}},p.each(["width","height"],function(a,b){p.attrHooks[b]=p.extend(p.attrHooks[b],{set:function(a,c){if(c==="")return a.setAttribute(b,"auto"),c}})}),p.attrHooks.contenteditable={get:L.get,set:function(a,b,c){b===""&&(b="false"),L.set(a,b,c)}}),p.support.hrefNormalized||p.each(["href","src","width","height"],function(a,c){p.attrHooks[c]=p.extend(p.attrHooks[c],{get:function(a){var d=a.getAttribute(c,2);return d===null?b:d}})}),p.support.style||(p.attrHooks.style={get:function(a){return a.style.cssText.toLowerCase()||b},set:function(a,b){return a.style.cssText=""+b}}),p.support.optSelected||(p.propHooks.selected=p.extend(p.propHooks.selected,{get:function(a){var b=a.parentNode;return b&&(b.selectedIndex,b.parentNode&&b.parentNode.selectedIndex),null}})),p.support.enctype||(p.propFix.enctype="encoding"),p.support.checkOn||p.each(["radio","checkbox"],function(){p.valHooks[this]={get:function(a){return a.getAttribute("value")===null?"on":a.value}}}),p.each(["radio","checkbox"],function(){p.valHooks[this]=p.extend(p.valHooks[this],{set:function(a,b){if(p.isArray(b))return a.checked=p.inArray(p(a).val(),b)>=0}})});var V=/^(?:textarea|input|select)$/i,W=/^([^\.]*|)(?:\.(.+)|)$/,X=/(?:^|\s)hover(\.\S+|)\b/,Y=/^key/,Z=/^(?:mouse|contextmenu)|click/,$=/^(?:focusinfocus|focusoutblur)$/,_=function(a){return p.event.special.hover?a:a.replace(X,"mouseenter$1 mouseleave$1")};p.event={add:function(a,c,d,e,f){var g,h,i,j,k,l,m,n,o,q,r;if(a.nodeType===3||a.nodeType===8||!c||!d||!(g=p._data(a)))return;d.handler&&(o=d,d=o.handler,f=o.selector),d.guid||(d.guid=p.guid++),i=g.events,i||(g.events=i={}),h=g.handle,h||(g.handle=h=function(a){return typeof p!="undefined"&&(!a||p.event.triggered!==a.type)?p.event.dispatch.apply(h.elem,arguments):b},h.elem=a),c=p.trim(_(c)).split(" ");for(j=0;j<c.length;j++){k=W.exec(c[j])||[],l=k[1],m=(k[2]||"").split(".").sort(),r=p.event.special[l]||{},l=(f?r.delegateType:r.bindType)||l,r=p.event.special[l]||{},n=p.extend({type:l,origType:k[1],data:e,handler:d,guid:d.guid,selector:f,namespace:m.join(".")},o),q=i[l];if(!q){q=i[l]=[],q.delegateCount=0;if(!r.setup||r.setup.call(a,e,m,h)===!1)a.addEventListener?a.addEventListener(l,h,!1):a.attachEvent&&a.attachEvent("on"+l,h)}r.add&&(r.add.call(a,n),n.handler.guid||(n.handler.guid=d.guid)),f?q.splice(q.delegateCount++,0,n):q.push(n),p.event.global[l]=!0}a=null},global:{},remove:function(a,b,c,d,e){var f,g,h,i,j,k,l,m,n,o,q,r=p.hasData(a)&&p._data(a);if(!r||!(m=r.events))return;b=p.trim(_(b||"")).split(" ");for(f=0;f<b.length;f++){g=W.exec(b[f])||[],h=i=g[1],j=g[2];if(!h){for(h in m)p.event.remove(a,h+b[f],c,d,!0);continue}n=p.event.special[h]||{},h=(d?n.delegateType:n.bindType)||h,o=m[h]||[],k=o.length,j=j?new RegExp("(^|\\.)"+j.split(".").sort().join("\\.(?:.*\\.|)")+"(\\.|$)"):null;for(l=0;l<o.length;l++)q=o[l],(e||i===q.origType)&&(!c||c.guid===q.guid)&&(!j||j.test(q.namespace))&&(!d||d===q.selector||d==="**"&&q.selector)&&(o.splice(l--,1),q.selector&&o.delegateCount--,n.remove&&n.remove.call(a,q));o.length===0&&k!==o.length&&((!n.teardown||n.teardown.call(a,j,r.handle)===!1)&&p.removeEvent(a,h,r.handle),delete m[h])}p.isEmptyObject(m)&&(delete r.handle,p.removeData(a,"events",!0))},customEvent:{getData:!0,setData:!0,changeData:!0},trigger:function(c,d,f,g){if(!f||f.nodeType!==3&&f.nodeType!==8){var h,i,j,k,l,m,n,o,q,r,s=c.type||c,t=[];if($.test(s+p.event.triggered))return;s.indexOf("!")>=0&&(s=s.slice(0,-1),i=!0),s.indexOf(".")>=0&&(t=s.split("."),s=t.shift(),t.sort());if((!f||p.event.customEvent[s])&&!p.event.global[s])return;c=typeof c=="object"?c[p.expando]?c:new p.Event(s,c):new p.Event(s),c.type=s,c.isTrigger=!0,c.exclusive=i,c.namespace=t.join("."),c.namespace_re=c.namespace?new RegExp("(^|\\.)"+t.join("\\.(?:.*\\.|)")+"(\\.|$)"):null,m=s.indexOf(":")<0?"on"+s:"";if(!f){h=p.cache;for(j in h)h[j].events&&h[j].events[s]&&p.event.trigger(c,d,h[j].handle.elem,!0);return}c.result=b,c.target||(c.target=f),d=d!=null?p.makeArray(d):[],d.unshift(c),n=p.event.special[s]||{};if(n.trigger&&n.trigger.apply(f,d)===!1)return;q=[[f,n.bindType||s]];if(!g&&!n.noBubble&&!p.isWindow(f)){r=n.delegateType||s,k=$.test(r+s)?f:f.parentNode;for(l=f;k;k=k.parentNode)q.push([k,r]),l=k;l===(f.ownerDocument||e)&&q.push([l.defaultView||l.parentWindow||a,r])}for(j=0;j<q.length&&!c.isPropagationStopped();j++)k=q[j][0],c.type=q[j][1],o=(p._data(k,"events")||{})[c.type]&&p._data(k,"handle"),o&&o.apply(k,d),o=m&&k[m],o&&p.acceptData(k)&&o.apply(k,d)===!1&&c.preventDefault();return c.type=s,!g&&!c.isDefaultPrevented()&&(!n._default||n._default.apply(f.ownerDocument,d)===!1)&&(s!=="click"||!p.nodeName(f,"a"))&&p.acceptData(f)&&m&&f[s]&&(s!=="focus"&&s!=="blur"||c.target.offsetWidth!==0)&&!p.isWindow(f)&&(l=f[m],l&&(f[m]=null),p.event.triggered=s,f[s](),p.event.triggered=b,l&&(f[m]=l)),c.result}return},dispatch:function(c){c=p.event.fix(c||a.event);var d,e,f,g,h,i,j,k,l,m,n,o=(p._data(this,"events")||{})[c.type]||[],q=o.delegateCount,r=[].slice.call(arguments),s=!c.exclusive&&!c.namespace,t=p.event.special[c.type]||{},u=[];r[0]=c,c.delegateTarget=this;if(t.preDispatch&&t.preDispatch.call(this,c)===!1)return;if(q&&(!c.button||c.type!=="click")){g=p(this),g.context=this;for(f=c.target;f!=this;f=f.parentNode||this)if(f.disabled!==!0||c.type!=="click"){i={},k=[],g[0]=f;for(d=0;d<q;d++)l=o[d],m=l.selector,i[m]===b&&(i[m]=g.is(m)),i[m]&&k.push(l);k.length&&u.push({elem:f,matches:k})}}o.length>q&&u.push({elem:this,matches:o.slice(q)});for(d=0;d<u.length&&!c.isPropagationStopped();d++){j=u[d],c.currentTarget=j.elem;for(e=0;e<j.matches.length&&!c.isImmediatePropagationStopped();e++){l=j.matches[e];if(s||!c.namespace&&!l.namespace||c.namespace_re&&c.namespace_re.test(l.namespace))c.data=l.data,c.handleObj=l,h=((p.event.special[l.origType]||{}).handle||l.handler).apply(j.elem,r),h!==b&&(c.result=h,h===!1&&(c.preventDefault(),c.stopPropagation()))}}return t.postDispatch&&t.postDispatch.call(this,c),c.result},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(a,b){return a.which==null&&(a.which=b.charCode!=null?b.charCode:b.keyCode),a}},mouseHooks:{props:"button buttons clientX clientY fromElement offsetX offsetY pageX pageY screenX screenY toElement".split(" "),filter:function(a,c){var d,f,g,h=c.button,i=c.fromElement;return a.pageX==null&&c.clientX!=null&&(d=a.target.ownerDocument||e,f=d.documentElement,g=d.body,a.pageX=c.clientX+(f&&f.scrollLeft||g&&g.scrollLeft||0)-(f&&f.clientLeft||g&&g.clientLeft||0),a.pageY=c.clientY+(f&&f.scrollTop||g&&g.scrollTop||0)-(f&&f.clientTop||g&&g.clientTop||0)),!a.relatedTarget&&i&&(a.relatedTarget=i===a.target?c.toElement:i),!a.which&&h!==b&&(a.which=h&1?1:h&2?3:h&4?2:0),a}},fix:function(a){if(a[p.expando])return a;var b,c,d=a,f=p.event.fixHooks[a.type]||{},g=f.props?this.props.concat(f.props):this.props;a=p.Event(d);for(b=g.length;b;)c=g[--b],a[c]=d[c];return a.target||(a.target=d.srcElement||e),a.target.nodeType===3&&(a.target=a.target.parentNode),a.metaKey=!!a.metaKey,f.filter?f.filter(a,d):a},special:{ready:{setup:p.bindReady},load:{noBubble:!0},focus:{delegateType:"focusin"},blur:{delegateType:"focusout"},beforeunload:{setup:function(a,b,c){p.isWindow(this)&&(this.onbeforeunload=c)},teardown:function(a,b){this.onbeforeunload===b&&(this.onbeforeunload=null)}}},simulate:function(a,b,c,d){var e=p.extend(new p.Event,c,{type:a,isSimulated:!0,originalEvent:{}});d?p.event.trigger(e,null,b):p.event.dispatch.call(b,e),e.isDefaultPrevented()&&c.preventDefault()}},p.event.handle=p.event.dispatch,p.removeEvent=e.removeEventListener?function(a,b,c){a.removeEventListener&&a.removeEventListener(b,c,!1)}:function(a,b,c){var d="on"+b;a.detachEvent&&(typeof a[d]=="undefined"&&(a[d]=null),a.detachEvent(d,c))},p.Event=function(a,b){if(this instanceof p.Event)a&&a.type?(this.originalEvent=a,this.type=a.type,this.isDefaultPrevented=a.defaultPrevented||a.returnValue===!1||a.getPreventDefault&&a.getPreventDefault()?bb:ba):this.type=a,b&&p.extend(this,b),this.timeStamp=a&&a.timeStamp||p.now(),this[p.expando]=!0;else return new p.Event(a,b)},p.Event.prototype={preventDefault:function(){this.isDefaultPrevented=bb;var a=this.originalEvent;if(!a)return;a.preventDefault?a.preventDefault():a.returnValue=!1},stopPropagation:function(){this.isPropagationStopped=bb;var a=this.originalEvent;if(!a)return;a.stopPropagation&&a.stopPropagation(),a.cancelBubble=!0},stopImmediatePropagation:function(){this.isImmediatePropagationStopped=bb,this.stopPropagation()},isDefaultPrevented:ba,isPropagationStopped:ba,isImmediatePropagationStopped:ba},p.each({mouseenter:"mouseover",mouseleave:"mouseout"},function(a,b){p.event.special[a]={delegateType:b,bindType:b,handle:function(a){var c,d=this,e=a.relatedTarget,f=a.handleObj,g=f.selector;if(!e||e!==d&&!p.contains(d,e))a.type=f.origType,c=f.handler.apply(this,arguments),a.type=b;return c}}}),p.support.submitBubbles||(p.event.special.submit={setup:function(){if(p.nodeName(this,"form"))return!1;p.event.add(this,"click._submit keypress._submit",function(a){var c=a.target,d=p.nodeName(c,"input")||p.nodeName(c,"button")?c.form:b;d&&!p._data(d,"_submit_attached")&&(p.event.add(d,"submit._submit",function(a){a._submit_bubble=!0}),p._data(d,"_submit_attached",!0))})},postDispatch:function(a){a._submit_bubble&&(delete a._submit_bubble,this.parentNode&&!a.isTrigger&&p.event.simulate("submit",this.parentNode,a,!0))},teardown:function(){if(p.nodeName(this,"form"))return!1;p.event.remove(this,"._submit")}}),p.support.changeBubbles||(p.event.special.change={setup:function(){if(V.test(this.nodeName)){if(this.type==="checkbox"||this.type==="radio")p.event.add(this,"propertychange._change",function(a){a.originalEvent.propertyName==="checked"&&(this._just_changed=!0)}),p.event.add(this,"click._change",function(a){this._just_changed&&!a.isTrigger&&(this._just_changed=!1),p.event.simulate("change",this,a,!0)});return!1}p.event.add(this,"beforeactivate._change",function(a){var b=a.target;V.test(b.nodeName)&&!p._data(b,"_change_attached")&&(p.event.add(b,"change._change",function(a){this.parentNode&&!a.isSimulated&&!a.isTrigger&&p.event.simulate("change",this.parentNode,a,!0)}),p._data(b,"_change_attached",!0))})},handle:function(a){var b=a.target;if(this!==b||a.isSimulated||a.isTrigger||b.type!=="radio"&&b.type!=="checkbox")return a.handleObj.handler.apply(this,arguments)},teardown:function(){return p.event.remove(this,"._change"),V.test(this.nodeName)}}),p.support.focusinBubbles||p.each({focus:"focusin",blur:"focusout"},function(a,b){var c=0,d=function(a){p.event.simulate(b,a.target,p.event.fix(a),!0)};p.event.special[b]={setup:function(){c++===0&&e.addEventListener(a,d,!0)},teardown:function(){--c===0&&e.removeEventListener(a,d,!0)}}}),p.fn.extend({on:function(a,c,d,e,f){var g,h;if(typeof a=="object"){typeof c!="string"&&(d=d||c,c=b);for(h in a)this.on(h,c,d,a[h],f);return this}d==null&&e==null?(e=c,d=c=b):e==null&&(typeof c=="string"?(e=d,d=b):(e=d,d=c,c=b));if(e===!1)e=ba;else if(!e)return this;return f===1&&(g=e,e=function(a){return p().off(a),g.apply(this,arguments)},e.guid=g.guid||(g.guid=p.guid++)),this.each(function(){p.event.add(this,a,e,d,c)})},one:function(a,b,c,d){return this.on(a,b,c,d,1)},off:function(a,c,d){var e,f;if(a&&a.preventDefault&&a.handleObj)return e=a.handleObj,p(a.delegateTarget).off(e.namespace?e.origType+"."+e.namespace:e.origType,e.selector,e.handler),this;if(typeof a=="object"){for(f in a)this.off(f,c,a[f]);return this}if(c===!1||typeof c=="function")d=c,c=b;return d===!1&&(d=ba),this.each(function(){p.event.remove(this,a,d,c)})},bind:function(a,b,c){return this.on(a,null,b,c)},unbind:function(a,b){return this.off(a,null,b)},live:function(a,b,c){return p(this.context).on(a,this.selector,b,c),this},die:function(a,b){return p(this.context).off(a,this.selector||"**",b),this},delegate:function(a,b,c,d){return this.on(b,a,c,d)},undelegate:function(a,b,c){return arguments.length==1?this.off(a,"**"):this.off(b,a||"**",c)},trigger:function(a,b){return this.each(function(){p.event.trigger(a,b,this)})},triggerHandler:function(a,b){if(this[0])return p.event.trigger(a,b,this[0],!0)},toggle:function(a){var b=arguments,c=a.guid||p.guid++,d=0,e=function(c){var e=(p._data(this,"lastToggle"+a.guid)||0)%d;return p._data(this,"lastToggle"+a.guid,e+1),c.preventDefault(),b[e].apply(this,arguments)||!1};e.guid=c;while(d<b.length)b[d++].guid=c;return this.click(e)},hover:function(a,b){return this.mouseenter(a).mouseleave(b||a)}}),p.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(a,b){p.fn[b]=function(a,c){return c==null&&(c=a,a=null),arguments.length>0?this.on(b,null,a,c):this.trigger(b)},Y.test(b)&&(p.event.fixHooks[b]=p.event.keyHooks),Z.test(b)&&(p.event.fixHooks[b]=p.event.mouseHooks)}),function(a,b){function bd(a,b,c,d){var e=0,f=b.length;for(;e<f;e++)Z(a,b[e],c,d)}function be(a,b,c,d,e,f){var g,h=$.setFilters[b.toLowerCase()];return h||Z.error(b),(a||!(g=e))&&bd(a||"*",d,g=[],e),g.length>0?h(g,c,f):[]}function bf(a,c,d,e,f){var g,h,i,j,k,l,m,n,p=0,q=f.length,s=L.POS,t=new RegExp("^"+s.source+"(?!"+r+")","i"),u=function(){var a=1,c=arguments.length-2;for(;a<c;a++)arguments[a]===b&&(g[a]=b)};for(;p<q;p++){s.exec(""),a=f[p],j=[],i=0,k=e;while(g=s.exec(a)){n=s.lastIndex=g.index+g[0].length;if(n>i){m=a.slice(i,g.index),i=n,l=[c],B.test(m)&&(k&&(l=k),k=e);if(h=H.test(m))m=m.slice(0,-5).replace(B,"$&*");g.length>1&&g[0].replace(t,u),k=be(m,g[1],g[2],l,k,h)}}k?(j=j.concat(k),(m=a.slice(i))&&m!==")"?B.test(m)?bd(m,j,d,e):Z(m,c,d,e?e.concat(k):k):o.apply(d,j)):Z(a,c,d,e)}return q===1?d:Z.uniqueSort(d)}function bg(a,b,c){var d,e,f,g=[],i=0,j=D.exec(a),k=!j.pop()&&!j.pop(),l=k&&a.match(C)||[""],m=$.preFilter,n=$.filter,o=!c&&b!==h;for(;(e=l[i])!=null&&k;i++){g.push(d=[]),o&&(e=" "+e);while(e){k=!1;if(j=B.exec(e))e=e.slice(j[0].length),k=d.push({part:j.pop().replace(A," "),captures:j});for(f in n)(j=L[f].exec(e))&&(!m[f]||(j=m[f](j,b,c)))&&(e=e.slice(j.shift().length),k=d.push({part:f,captures:j}));if(!k)break}}return k||Z.error(a),g}function bh(a,b,e){var f=b.dir,g=m++;return a||(a=function(a){return a===e}),b.first?function(b,c){while(b=b[f])if(b.nodeType===1)return a(b,c)&&b}:function(b,e){var h,i=g+"."+d,j=i+"."+c;while(b=b[f])if(b.nodeType===1){if((h=b[q])===j)return b.sizset;if(typeof h=="string"&&h.indexOf(i)===0){if(b.sizset)return b}else{b[q]=j;if(a(b,e))return b.sizset=!0,b;b.sizset=!1}}}}function bi(a,b){return a?function(c,d){var e=b(c,d);return e&&a(e===!0?c:e,d)}:b}function bj(a,b,c){var d,e,f=0;for(;d=a[f];f++)$.relative[d.part]?e=bh(e,$.relative[d.part],b):(d.captures.push(b,c),e=bi(e,$.filter[d.part].apply(null,d.captures)));return e}function bk(a){return function(b,c){var d,e=0;for(;d=a[e];e++)if(d(b,c))return!0;return!1}}var c,d,e,f,g,h=a.document,i=h.documentElement,j="undefined",k=!1,l=!0,m=0,n=[].slice,o=[].push,q=("sizcache"+Math.random()).replace(".",""),r="[\\x20\\t\\r\\n\\f]",s="(?:\\\\.|[-\\w]|[^\\x00-\\xa0])+",t=s.replace("w","w#"),u="([*^$|!~]?=)",v="\\["+r+"*("+s+")"+r+"*(?:"+u+r+"*(?:(['\"])((?:\\\\.|[^\\\\])*?)\\3|("+t+")|)|)"+r+"*\\]",w=":("+s+")(?:\\((?:(['\"])((?:\\\\.|[^\\\\])*?)\\2|((?:[^,]|\\\\,|(?:,(?=[^\\[]*\\]))|(?:,(?=[^\\(]*\\))))*))\\)|)",x=":(nth|eq|gt|lt|first|last|even|odd)(?:\\((\\d*)\\)|)(?=[^-]|$)",y=r+"*([\\x20\\t\\r\\n\\f>+~])"+r+"*",z="(?=[^\\x20\\t\\r\\n\\f])(?:\\\\.|"+v+"|"+w.replace(2,7)+"|[^\\\\(),])+",A=new RegExp("^"+r+"+|((?:^|[^\\\\])(?:\\\\.)*)"+r+"+$","g"),B=new RegExp("^"+y),C=new RegExp(z+"?(?="+r+"*,|$)","g"),D=new RegExp("^(?:(?!,)(?:(?:^|,)"+r+"*"+z+")*?|"+r+"*(.*?))(\\)|$)"),E=new RegExp(z.slice(19,-6)+"\\x20\\t\\r\\n\\f>+~])+|"+y,"g"),F=/^(?:#([\w\-]+)|(\w+)|\.([\w\-]+))$/,G=/[\x20\t\r\n\f]*[+~]/,H=/:not\($/,I=/h\d/i,J=/input|select|textarea|button/i,K=/\\(?!\\)/g,L={ID:new RegExp("^#("+s+")"),CLASS:new RegExp("^\\.("+s+")"),NAME:new RegExp("^\\[name=['\"]?("+s+")['\"]?\\]"),TAG:new RegExp("^("+s.replace("[-","[-\\*")+")"),ATTR:new RegExp("^"+v),PSEUDO:new RegExp("^"+w),CHILD:new RegExp("^:(only|nth|last|first)-child(?:\\("+r+"*(even|odd|(([+-]|)(\\d*)n|)"+r+"*(?:([+-]|)"+r+"*(\\d+)|))"+r+"*\\)|)","i"),POS:new RegExp(x,"ig"),needsContext:new RegExp("^"+r+"*[>+~]|"+x,"i")},M={},N=[],O={},P=[],Q=function(a){return a.sizzleFilter=!0,a},R=function(a){return function(b){return b.nodeName.toLowerCase()==="input"&&b.type===a}},S=function(a){return function(b){var c=b.nodeName.toLowerCase();return(c==="input"||c==="button")&&b.type===a}},T=function(a){var b=!1,c=h.createElement("div");try{b=a(c)}catch(d){}return c=null,b},U=T(function(a){a.innerHTML="<select></select>";var b=typeof a.lastChild.getAttribute("multiple");return b!=="boolean"&&b!=="string"}),V=T(function(a){a.id=q+0,a.innerHTML="<a name='"+q+"'></a><div name='"+q+"'></div>",i.insertBefore(a,i.firstChild);var b=h.getElementsByName&&h.getElementsByName(q).length===2+h.getElementsByName(q+0).length;return g=!h.getElementById(q),i.removeChild(a),b}),W=T(function(a){return a.appendChild(h.createComment("")),a.getElementsByTagName("*").length===0}),X=T(function(a){return a.innerHTML="<a href='#'></a>",a.firstChild&&typeof a.firstChild.getAttribute!==j&&a.firstChild.getAttribute("href")==="#"}),Y=T(function(a){return a.innerHTML="<div class='hidden e'></div><div class='hidden'></div>",!a.getElementsByClassName||a.getElementsByClassName("e").length===0?!1:(a.lastChild.className="e",a.getElementsByClassName("e").length!==1)}),Z=function(a,b,c,d){c=c||[],b=b||h;var e,f,g,i,j=b.nodeType;if(j!==1&&j!==9)return[];if(!a||typeof a!="string")return c;g=ba(b);if(!g&&!d)if(e=F.exec(a))if(i=e[1]){if(j===9){f=b.getElementById(i);if(!f||!f.parentNode)return c;if(f.id===i)return c.push(f),c}else if(b.ownerDocument&&(f=b.ownerDocument.getElementById(i))&&bb(b,f)&&f.id===i)return c.push(f),c}else{if(e[2])return o.apply(c,n.call(b.getElementsByTagName(a),0)),c;if((i=e[3])&&Y&&b.getElementsByClassName)return o.apply(c,n.call(b.getElementsByClassName(i),0)),c}return bm(a,b,c,d,g)},$=Z.selectors={cacheLength:50,match:L,order:["ID","TAG"],attrHandle:{},createPseudo:Q,find:{ID:g?function(a,b,c){if(typeof b.getElementById!==j&&!c){var d=b.getElementById(a);return d&&d.parentNode?[d]:[]}}:function(a,c,d){if(typeof c.getElementById!==j&&!d){var e=c.getElementById(a);return e?e.id===a||typeof e.getAttributeNode!==j&&e.getAttributeNode("id").value===a?[e]:b:[]}},TAG:W?function(a,b){if(typeof b.getElementsByTagName!==j)return b.getElementsByTagName(a)}:function(a,b){var c=b.getElementsByTagName(a);if(a==="*"){var d,e=[],f=0;for(;d=c[f];f++)d.nodeType===1&&e.push(d);return e}return c}},relative:{">":{dir:"parentNode",first:!0}," ":{dir:"parentNode"},"+":{dir:"previousSibling",first:!0},"~":{dir:"previousSibling"}},preFilter:{ATTR:function(a){return a[1]=a[1].replace(K,""),a[3]=(a[4]||a[5]||"").replace(K,""),a[2]==="~="&&(a[3]=" "+a[3]+" "),a.slice(0,4)},CHILD:function(a){return a[1]=a[1].toLowerCase(),a[1]==="nth"?(a[2]||Z.error(a[0]),a[3]=+(a[3]?a[4]+(a[5]||1):2*(a[2]==="even"||a[2]==="odd")),a[4]=+(a[6]+a[7]||a[2]==="odd")):a[2]&&Z.error(a[0]),a},PSEUDO:function(a){var b,c=a[4];return L.CHILD.test(a[0])?null:(c&&(b=D.exec(c))&&b.pop()&&(a[0]=a[0].slice(0,b[0].length-c.length-1),c=b[0].slice(0,-1)),a.splice(2,3,c||a[3]),a)}},filter:{ID:g?function(a){return a=a.replace(K,""),function(b){return b.getAttribute("id")===a}}:function(a){return a=a.replace(K,""),function(b){var c=typeof b.getAttributeNode!==j&&b.getAttributeNode("id");return c&&c.value===a}},TAG:function(a){return a==="*"?function(){return!0}:(a=a.replace(K,"").toLowerCase(),function(b){return b.nodeName&&b.nodeName.toLowerCase()===a})},CLASS:function(a){var b=M[a];return b||(b=M[a]=new RegExp("(^|"+r+")"+a+"("+r+"|$)"),N.push(a),N.length>$.cacheLength&&delete M[N.shift()]),function(a){return b.test(a.className||typeof a.getAttribute!==j&&a.getAttribute("class")||"")}},ATTR:function(a,b,c){return b?function(d){var e=Z.attr(d,a),f=e+"";if(e==null)return b==="!=";switch(b){case"=":return f===c;case"!=":return f!==c;case"^=":return c&&f.indexOf(c)===0;case"*=":return c&&f.indexOf(c)>-1;case"$=":return c&&f.substr(f.length-c.length)===c;case"~=":return(" "+f+" ").indexOf(c)>-1;case"|=":return f===c||f.substr(0,c.length+1)===c+"-"}}:function(b){return Z.attr(b,a)!=null}},CHILD:function(a,b,c,d){if(a==="nth"){var e=m++;return function(a){var b,f,g=0,h=a;if(c===1&&d===0)return!0;b=a.parentNode;if(b&&(b[q]!==e||!a.sizset)){for(h=b.firstChild;h;h=h.nextSibling)if(h.nodeType===1){h.sizset=++g;if(h===a)break}b[q]=e}return f=a.sizset-d,c===0?f===0:f%c===0&&f/c>=0}}return function(b){var c=b;switch(a){case"only":case"first":while(c=c.previousSibling)if(c.nodeType===1)return!1;if(a==="first")return!0;c=b;case"last":while(c=c.nextSibling)if(c.nodeType===1)return!1;return!0}}},PSEUDO:function(a,b,c,d){var e=$.pseudos[a]||$.pseudos[a.toLowerCase()];return e||Z.error("unsupported pseudo: "+a),e.sizzleFilter?e(b,c,d):e}},pseudos:{not:Q(function(a,b,c){var d=bl(a.replace(A,"$1"),b,c);return function(a){return!d(a)}}),enabled:function(a){return a.disabled===!1},disabled:function(a){return a.disabled===!0},checked:function(a){var b=a.nodeName.toLowerCase();return b==="input"&&!!a.checked||b==="option"&&!!a.selected},selected:function(a){return a.parentNode&&a.parentNode.selectedIndex,a.selected===!0},parent:function(a){return!$.pseudos.empty(a)},empty:function(a){var b;a=a.firstChild;while(a){if(a.nodeName>"@"||(b=a.nodeType)===3||b===4)return!1;a=a.nextSibling}return!0},contains:Q(function(a){return function(b){return(b.textContent||b.innerText||bc(b)).indexOf(a)>-1}}),has:Q(function(a){return function(b){return Z(a,b).length>0}}),header:function(a){return I.test(a.nodeName)},text:function(a){var b,c;return a.nodeName.toLowerCase()==="input"&&(b=a.type)==="text"&&((c=a.getAttribute("type"))==null||c.toLowerCase()===b)},radio:R("radio"),checkbox:R("checkbox"),file:R("file"),password:R("password"),image:R("image"),submit:S("submit"),reset:S("reset"),button:function(a){var b=a.nodeName.toLowerCase();return b==="input"&&a.type==="button"||b==="button"},input:function(a){return J.test(a.nodeName)},focus:function(a){var b=a.ownerDocument;return a===b.activeElement&&(!b.hasFocus||b.hasFocus())&&(!!a.type||!!a.href)},active:function(a){return a===a.ownerDocument.activeElement}},setFilters:{first:function(a,b,c){return c?a.slice(1):[a[0]]},last:function(a,b,c){var d=a.pop();return c?a:[d]},even:function(a,b,c){var d=[],e=c?1:0,f=a.length;for(;e<f;e=e+2)d.push(a[e]);return d},odd:function(a,b,c){var d=[],e=c?0:1,f=a.length;for(;e<f;e=e+2)d.push(a[e]);return d},lt:function(a,b,c){return c?a.slice(+b):a.slice(0,+b)},gt:function(a,b,c){return c?a.slice(0,+b+1):a.slice(+b+1)},eq:function(a,b,c){var d=a.splice(+b,1);return c?a:d}}};$.setFilters.nth=$.setFilters.eq,$.filters=$.pseudos,X||($.attrHandle={href:function(a){return a.getAttribute("href",2)},type:function(a){return a.getAttribute("type")}}),V&&($.order.push("NAME"),$.find.NAME=function(a,b){if(typeof b.getElementsByName!==j)return b.getElementsByName(a)}),Y&&($.order.splice(1,0,"CLASS"),$.find.CLASS=function(a,b,c){if(typeof b.getElementsByClassName!==j&&!c)return b.getElementsByClassName(a)});try{n.call(i.childNodes,0)[0].nodeType}catch(_){n=function(a){var b,c=[];for(;b=this[a];a++)c.push(b);return c}}var ba=Z.isXML=function(a){var b=a&&(a.ownerDocument||a).documentElement;return b?b.nodeName!=="HTML":!1},bb=Z.contains=i.compareDocumentPosition?function(a,b){return!!(a.compareDocumentPosition(b)&16)}:i.contains?function(a,b){var c=a.nodeType===9?a.documentElement:a,d=b.parentNode;return a===d||!!(d&&d.nodeType===1&&c.contains&&c.contains(d))}:function(a,b){while(b=b.parentNode)if(b===a)return!0;return!1},bc=Z.getText=function(a){var b,c="",d=0,e=a.nodeType;if(e){if(e===1||e===9||e===11){if(typeof a.textContent=="string")return a.textContent;for(a=a.firstChild;a;a=a.nextSibling)c+=bc(a)}else if(e===3||e===4)return a.nodeValue}else for(;b=a[d];d++)c+=bc(b);return c};Z.attr=function(a,b){var c,d=ba(a);return d||(b=b.toLowerCase()),$.attrHandle[b]?$.attrHandle[b](a):U||d?a.getAttribute(b):(c=a.getAttributeNode(b),c?typeof a[b]=="boolean"?a[b]?b:null:c.specified?c.value:null:null)},Z.error=function(a){throw new Error("Syntax error, unrecognized expression: "+a)},[0,0].sort(function(){return l=0}),i.compareDocumentPosition?e=function(a,b){return a===b?(k=!0,0):(!a.compareDocumentPosition||!b.compareDocumentPosition?a.compareDocumentPosition:a.compareDocumentPosition(b)&4)?-1:1}:(e=function(a,b){if(a===b)return k=!0,0;if(a.sourceIndex&&b.sourceIndex)return a.sourceIndex-b.sourceIndex;var c,d,e=[],g=[],h=a.parentNode,i=b.parentNode,j=h;if(h===i)return f(a,b);if(!h)return-1;if(!i)return 1;while(j)e.unshift(j),j=j.parentNode;j=i;while(j)g.unshift(j),j=j.parentNode;c=e.length,d=g.length;for(var l=0;l<c&&l<d;l++)if(e[l]!==g[l])return f(e[l],g[l]);return l===c?f(a,g[l],-1):f(e[l],b,1)},f=function(a,b,c){if(a===b)return c;var d=a.nextSibling;while(d){if(d===b)return-1;d=d.nextSibling}return 1}),Z.uniqueSort=function(a){var b,c=1;if(e){k=l,a.sort(e);if(k)for(;b=a[c];c++)b===a[c-1]&&a.splice(c--,1)}return a};var bl=Z.compile=function(a,b,c){var d,e,f,g=O[a];if(g&&g.context===b)return g;e=bg(a,b,c);for(f=0;d=e[f];f++)e[f]=bj(d,b,c);return g=O[a]=bk(e),g.context=b,g.runs=g.dirruns=0,P.push(a),P.length>$.cacheLength&&delete O[P.shift()],g};Z.matches=function(a,b){return Z(a,null,null,b)},Z.matchesSelector=function(a,b){return Z(b,null,null,[a]).length>0};var bm=function(a,b,e,f,g){a=a.replace(A,"$1");var h,i,j,k,l,m,p,q,r,s=a.match(C),t=a.match(E),u=b.nodeType;if(L.POS.test(a))return bf(a,b,e,f,s);if(f)h=n.call(f,0);else if(s&&s.length===1){if(t.length>1&&u===9&&!g&&(s=L.ID.exec(t[0]))){b=$.find.ID(s[1],b,g)[0];if(!b)return e;a=a.slice(t.shift().length)}q=(s=G.exec(t[0]))&&!s.index&&b.parentNode||b,r=t.pop(),m=r.split(":not")[0];for(j=0,k=$.order.length;j<k;j++){p=$.order[j];if(s=L[p].exec(m)){h=$.find[p]((s[1]||"").replace(K,""),q,g);if(h==null)continue;m===r&&(a=a.slice(0,a.length-r.length)+m.replace(L[p],""),a||o.apply(e,n.call(h,0)));break}}}if(a){i=bl(a,b,g),d=i.dirruns++,h==null&&(h=$.find.TAG("*",G.test(a)&&b.parentNode||b));for(j=0;l=h[j];j++)c=i.runs++,i(l,b)&&e.push(l)}return e};h.querySelectorAll&&function(){var a,b=bm,c=/'|\\/g,d=/\=[\x20\t\r\n\f]*([^'"\]]*)[\x20\t\r\n\f]*\]/g,e=[],f=[":active"],g=i.matchesSelector||i.mozMatchesSelector||i.webkitMatchesSelector||i.oMatchesSelector||i.msMatchesSelector;T(function(a){a.innerHTML="<select><option selected></option></select>",a.querySelectorAll("[selected]").length||e.push("\\["+r+"*(?:checked|disabled|ismap|multiple|readonly|selected|value)"),a.querySelectorAll(":checked").length||e.push(":checked")}),T(function(a){a.innerHTML="<p test=''></p>",a.querySelectorAll("[test^='']").length&&e.push("[*^$]="+r+"*(?:\"\"|'')"),a.innerHTML="<input type='hidden'>",a.querySelectorAll(":enabled").length||e.push(":enabled",":disabled")}),e=e.length&&new RegExp(e.join("|")),bm=function(a,d,f,g,h){if(!g&&!h&&(!e||!e.test(a)))if(d.nodeType===9)try{return o.apply(f,n.call(d.querySelectorAll(a),0)),f}catch(i){}else if(d.nodeType===1&&d.nodeName.toLowerCase()!=="object"){var j=d.getAttribute("id"),k=j||q,l=G.test(a)&&d.parentNode||d;j?k=k.replace(c,"\\$&"):d.setAttribute("id",k);try{return o.apply(f,n.call(l.querySelectorAll(a.replace(C,"[id='"+k+"'] $&")),0)),f}catch(i){}finally{j||d.removeAttribute("id")}}return b(a,d,f,g,h)},g&&(T(function(b){a=g.call(b,"div");try{g.call(b,"[test!='']:sizzle"),f.push($.match.PSEUDO)}catch(c){}}),f=new RegExp(f.join("|")),Z.matchesSelector=function(b,c){c=c.replace(d,"='$1']");if(!ba(b)&&!f.test(c)&&(!e||!e.test(c)))try{var h=g.call(b,c);if(h||a||b.document&&b.document.nodeType!==11)return h}catch(i){}return Z(c,null,null,[b]).length>0})}(),Z.attr=p.attr,p.find=Z,p.expr=Z.selectors,p.expr[":"]=p.expr.pseudos,p.unique=Z.uniqueSort,p.text=Z.getText,p.isXMLDoc=Z.isXML,p.contains=Z.contains}(a);var bc=/Until$/,bd=/^(?:parents|prev(?:Until|All))/,be=/^.[^:#\[\.,]*$/,bf=p.expr.match.needsContext,bg={children:!0,contents:!0,next:!0,prev:!0};p.fn.extend({find:function(a){var b,c,d,e,f,g,h=this;if(typeof a!="string")return p(a).filter(function(){for(b=0,c=h.length;b<c;b++)if(p.contains(h[b],this))return!0});g=this.pushStack("","find",a);for(b=0,c=this.length;b<c;b++){d=g.length,p.find(a,this[b],g);if(b>0)for(e=d;e<g.length;e++)for(f=0;f<d;f++)if(g[f]===g[e]){g.splice(e--,1);break}}return g},has:function(a){var b,c=p(a,this),d=c.length;return this.filter(function(){for(b=0;b<d;b++)if(p.contains(this,c[b]))return!0})},not:function(a){return this.pushStack(bj(this,a,!1),"not",a)},filter:function(a){return this.pushStack(bj(this,a,!0),"filter",a)},is:function(a){return!!a&&(typeof a=="string"?bf.test(a)?p(a,this.context).index(this[0])>=0:p.filter(a,this).length>0:this.filter(a).length>0)},closest:function(a,b){var c,d=0,e=this.length,f=[],g=bf.test(a)||typeof a!="string"?p(a,b||this.context):0;for(;d<e;d++){c=this[d];while(c&&c.ownerDocument&&c!==b&&c.nodeType!==11){if(g?g.index(c)>-1:p.find.matchesSelector(c,a)){f.push(c);break}c=c.parentNode}}return f=f.length>1?p.unique(f):f,this.pushStack(f,"closest",a)},index:function(a){return a?typeof a=="string"?p.inArray(this[0],p(a)):p.inArray(a.jquery?a[0]:a,this):this[0]&&this[0].parentNode?this.prevAll().length:-1},add:function(a,b){var c=typeof a=="string"?p(a,b):p.makeArray(a&&a.nodeType?[a]:a),d=p.merge(this.get(),c);return this.pushStack(bh(c[0])||bh(d[0])?d:p.unique(d))},addBack:function(a){return this.add(a==null?this.prevObject:this.prevObject.filter(a))}}),p.fn.andSelf=p.fn.addBack,p.each({parent:function(a){var b=a.parentNode;return b&&b.nodeType!==11?b:null},parents:function(a){return p.dir(a,"parentNode")},parentsUntil:function(a,b,c){return p.dir(a,"parentNode",c)},next:function(a){return bi(a,"nextSibling")},prev:function(a){return bi(a,"previousSibling")},nextAll:function(a){return p.dir(a,"nextSibling")},prevAll:function(a){return p.dir(a,"previousSibling")},nextUntil:function(a,b,c){return p.dir(a,"nextSibling",c)},prevUntil:function(a,b,c){return p.dir(a,"previousSibling",c)},siblings:function(a){return p.sibling((a.parentNode||{}).firstChild,a)},children:function(a){return p.sibling(a.firstChild)},contents:function(a){return p.nodeName(a,"iframe")?a.contentDocument||a.contentWindow.document:p.merge([],a.childNodes)}},function(a,b){p.fn[a]=function(c,d){var e=p.map(this,b,c);return bc.test(a)||(d=c),d&&typeof d=="string"&&(e=p.filter(d,e)),e=this.length>1&&!bg[a]?p.unique(e):e,this.length>1&&bd.test(a)&&(e=e.reverse()),this.pushStack(e,a,k.call(arguments).join(","))}}),p.extend({filter:function(a,b,c){return c&&(a=":not("+a+")"),b.length===1?p.find.matchesSelector(b[0],a)?[b[0]]:[]:p.find.matches(a,b)},dir:function(a,c,d){var e=[],f=a[c];while(f&&f.nodeType!==9&&(d===b||f.nodeType!==1||!p(f).is(d)))f.nodeType===1&&e.push(f),f=f[c];return e},sibling:function(a,b){var c=[];for(;a;a=a.nextSibling)a.nodeType===1&&a!==b&&c.push(a);return c}});var bl="abbr|article|aside|audio|bdi|canvas|data|datalist|details|figcaption|figure|footer|header|hgroup|mark|meter|nav|output|progress|section|summary|time|video",bm=/ jQuery\d+="(?:null|\d+)"/g,bn=/^\s+/,bo=/<(?!area|br|col|embed|hr|img|input|link|meta|param)(([\w:]+)[^>]*)\/>/gi,bp=/<([\w:]+)/,bq=/<tbody/i,br=/<|&#?\w+;/,bs=/<(?:script|style|link)/i,bt=/<(?:script|object|embed|option|style)/i,bu=new RegExp("<(?:"+bl+")[\\s/>]","i"),bv=/^(?:checkbox|radio)$/,bw=/checked\s*(?:[^=]|=\s*.checked.)/i,bx=/\/(java|ecma)script/i,by=/^\s*<!(?:\[CDATA\[|\-\-)|[\]\-]{2}>\s*$/g,bz={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,"",""]},bA=bk(e),bB=bA.appendChild(e.createElement("div"));bz.optgroup=bz.option,bz.tbody=bz.tfoot=bz.colgroup=bz.caption=bz.thead,bz.th=bz.td,p.support.htmlSerialize||(bz._default=[1,"X<div>","</div>"]),p.fn.extend({text:function(a){return p.access(this,function(a){return a===b?p.text(this):this.empty().append((this[0]&&this[0].ownerDocument||e).createTextNode(a))},null,a,arguments.length)},wrapAll:function(a){if(p.isFunction(a))return this.each(function(b){p(this).wrapAll(a.call(this,b))});if(this[0]){var b=p(a,this[0].ownerDocument).eq(0).clone(!0);this[0].parentNode&&b.insertBefore(this[0]),b.map(function(){var a=this;while(a.firstChild&&a.firstChild.nodeType===1)a=a.firstChild;return a}).append(this)}return this},wrapInner:function(a){return p.isFunction(a)?this.each(function(b){p(this).wrapInner(a.call(this,b))}):this.each(function(){var b=p(this),c=b.contents();c.length?c.wrapAll(a):b.append(a)})},wrap:function(a){var b=p.isFunction(a);return this.each(function(c){p(this).wrapAll(b?a.call(this,c):a)})},unwrap:function(){return this.parent().each(function(){p.nodeName(this,"body")||p(this).replaceWith(this.childNodes)}).end()},append:function(){return this.domManip(arguments,!0,function(a){(this.nodeType===1||this.nodeType===11)&&this.appendChild(a)})},prepend:function(){return this.domManip(arguments,!0,function(a){(this.nodeType===1||this.nodeType===11)&&this.insertBefore(a,this.firstChild)})},before:function(){if(!bh(this[0]))return this.domManip(arguments,!1,function(a){this.parentNode.insertBefore(a,this)});if(arguments.length){var a=p.clean(arguments);return this.pushStack(p.merge(a,this),"before",this.selector)}},after:function(){if(!bh(this[0]))return this.domManip(arguments,!1,function(a){this.parentNode.insertBefore(a,this.nextSibling)});if(arguments.length){var a=p.clean(arguments);return this.pushStack(p.merge(this,a),"after",this.selector)}},remove:function(a,b){var c,d=0;for(;(c=this[d])!=null;d++)if(!a||p.filter(a,[c]).length)!b&&c.nodeType===1&&(p.cleanData(c.getElementsByTagName("*")),p.cleanData([c])),c.parentNode&&c.parentNode.removeChild(c);return this},empty:function(){var a,b=0;for(;(a=this[b])!=null;b++){a.nodeType===1&&p.cleanData(a.getElementsByTagName("*"));while(a.firstChild)a.removeChild(a.firstChild)}return this},clone:function(a,b){return a=a==null?!1:a,b=b==null?a:b,this.map(function(){return p.clone(this,a,b)})},html:function(a){return p.access(this,function(a){var c=this[0]||{},d=0,e=this.length;if(a===b)return c.nodeType===1?c.innerHTML.replace(bm,""):b;if(typeof a=="string"&&!bs.test(a)&&(p.support.htmlSerialize||!bu.test(a))&&(p.support.leadingWhitespace||!bn.test(a))&&!bz[(bp.exec(a)||["",""])[1].toLowerCase()]){a=a.replace(bo,"<$1></$2>");try{for(;d<e;d++)c=this[d]||{},c.nodeType===1&&(p.cleanData(c.getElementsByTagName("*")),c.innerHTML=a);c=0}catch(f){}}c&&this.empty().append(a)},null,a,arguments.length)},replaceWith:function(a){return bh(this[0])?this.length?this.pushStack(p(p.isFunction(a)?a():a),"replaceWith",a):this:p.isFunction(a)?this.each(function(b){var c=p(this),d=c.html();c.replaceWith(a.call(this,b,d))}):(typeof a!="string"&&(a=p(a).detach()),this.each(function(){var b=this.nextSibling,c=this.parentNode;p(this).remove(),b?p(b).before(a):p(c).append(a)}))},detach:function(a){return this.remove(a,!0)},domManip:function(a,c,d){a=[].concat.apply([],a);var e,f,g,h,i=0,j=a[0],k=[],l=this.length;if(!p.support.checkClone&&l>1&&typeof j=="string"&&bw.test(j))return this.each(function(){p(this).domManip(a,c,d)});if(p.isFunction(j))return this.each(function(e){var f=p(this);a[0]=j.call(this,e,c?f.html():b),f.domManip(a,c,d)});if(this[0]){e=p.buildFragment(a,this,k),g=e.fragment,f=g.firstChild,g.childNodes.length===1&&(g=f);if(f){c=c&&p.nodeName(f,"tr");for(h=e.cacheable||l-1;i<l;i++)d.call(c&&p.nodeName(this[i],"table")?bC(this[i],"tbody"):this[i],i===h?g:p.clone(g,!0,!0))}g=f=null,k.length&&p.each(k,function(a,b){b.src?p.ajax?p.ajax({url:b.src,type:"GET",dataType:"script",async:!1,global:!1,"throws":!0}):p.error("no ajax"):p.globalEval((b.text||b.textContent||b.innerHTML||"").replace(by,"")),b.parentNode&&b.parentNode.removeChild(b)})}return this}}),p.buildFragment=function(a,c,d){var f,g,h,i=a[0];return c=c||e,c=(c[0]||c).ownerDocument||c[0]||c,typeof c.createDocumentFragment=="undefined"&&(c=e),a.length===1&&typeof i=="string"&&i.length<512&&c===e&&i.charAt(0)==="<"&&!bt.test(i)&&(p.support.checkClone||!bw.test(i))&&(p.support.html5Clone||!bu.test(i))&&(g=!0,f=p.fragments[i],h=f!==b),f||(f=c.createDocumentFragment(),p.clean(a,c,f,d),g&&(p.fragments[i]=h&&f)),{fragment:f,cacheable:g}},p.fragments={},p.each({appendTo:"append",prependTo:"prepend",insertBefore:"before",insertAfter:"after",replaceAll:"replaceWith"},function(a,b){p.fn[a]=function(c){var d,e=0,f=[],g=p(c),h=g.length,i=this.length===1&&this[0].parentNode;if((i==null||i&&i.nodeType===11&&i.childNodes.length===1)&&h===1)return g[b](this[0]),this;for(;e<h;e++)d=(e>0?this.clone(!0):this).get(),p(g[e])[b](d),f=f.concat(d);return this.pushStack(f,a,g.selector)}}),p.extend({clone:function(a,b,c){var d,e,f,g;p.support.html5Clone||p.isXMLDoc(a)||!bu.test("<"+a.nodeName+">")?g=a.cloneNode(!0):(bB.innerHTML=a.outerHTML,bB.removeChild(g=bB.firstChild));if((!p.support.noCloneEvent||!p.support.noCloneChecked)&&(a.nodeType===1||a.nodeType===11)&&!p.isXMLDoc(a)){bE(a,g),d=bF(a),e=bF(g);for(f=0;d[f];++f)e[f]&&bE(d[f],e[f])}if(b){bD(a,g);if(c){d=bF(a),e=bF(g);for(f=0;d[f];++f)bD(d[f],e[f])}}return d=e=null,g},clean:function(a,b,c,d){var f,g,h,i,j,k,l,m,n,o,q,r,s=0,t=[];if(!b||typeof b.createDocumentFragment=="undefined")b=e;for(g=b===e&&bA;(h=a[s])!=null;s++){typeof h=="number"&&(h+="");if(!h)continue;if(typeof h=="string")if(!br.test(h))h=b.createTextNode(h);else{g=g||bk(b),l=l||g.appendChild(b.createElement("div")),h=h.replace(bo,"<$1></$2>"),i=(bp.exec(h)||["",""])[1].toLowerCase(),j=bz[i]||bz._default,k=j[0],l.innerHTML=j[1]+h+j[2];while(k--)l=l.lastChild;if(!p.support.tbody){m=bq.test(h),n=i==="table"&&!m?l.firstChild&&l.firstChild.childNodes:j[1]==="<table>"&&!m?l.childNodes:[];for(f=n.length-1;f>=0;--f)p.nodeName(n[f],"tbody")&&!n[f].childNodes.length&&n[f].parentNode.removeChild(n[f])}!p.support.leadingWhitespace&&bn.test(h)&&l.insertBefore(b.createTextNode(bn.exec(h)[0]),l.firstChild),h=l.childNodes,l=g.lastChild}h.nodeType?t.push(h):t=p.merge(t,h)}l&&(g.removeChild(l),h=l=g=null);if(!p.support.appendChecked)for(s=0;(h=t[s])!=null;s++)p.nodeName(h,"input")?bG(h):typeof h.getElementsByTagName!="undefined"&&p.grep(h.getElementsByTagName("input"),bG);if(c){q=function(a){if(!a.type||bx.test(a.type))return d?d.push(a.parentNode?a.parentNode.removeChild(a):a):c.appendChild(a)};for(s=0;(h=t[s])!=null;s++)if(!p.nodeName(h,"script")||!q(h))c.appendChild(h),typeof h.getElementsByTagName!="undefined"&&(r=p.grep(p.merge([],h.getElementsByTagName("script")),q),t.splice.apply(t,[s+1,0].concat(r)),s+=r.length)}return t},cleanData:function(a,b){var c,d,e,f,g=0,h=p.expando,i=p.cache,j=p.support.deleteExpando,k=p.event.special;for(;(e=a[g])!=null;g++)if(b||p.acceptData(e)){d=e[h],c=d&&i[d];if(c){if(c.events)for(f in c.events)k[f]?p.event.remove(e,f):p.removeEvent(e,f,c.handle);i[d]&&(delete i[d],j?delete e[h]:e.removeAttribute?e.removeAttribute(h):e[h]=null,p.deletedIds.push(d))}}}}),function(){var a,b;p.uaMatch=function(a){a=a.toLowerCase();var b=/(chrome)[ \/]([\w.]+)/.exec(a)||/(webkit)[ \/]([\w.]+)/.exec(a)||/(opera)(?:.*version|)[ \/]([\w.]+)/.exec(a)||/(msie) ([\w.]+)/.exec(a)||a.indexOf("compatible")<0&&/(mozilla)(?:.*? rv:([\w.]+)|)/.exec(a)||[];return{browser:b[1]||"",version:b[2]||"0"}},a=p.uaMatch(g.userAgent),b={},a.browser&&(b[a.browser]=!0,b.version=a.version),b.webkit&&(b.safari=!0),p.browser=b,p.sub=function(){function a(b,c){return new a.fn.init(b,c)}p.extend(!0,a,this),a.superclass=this,a.fn=a.prototype=this(),a.fn.constructor=a,a.sub=this.sub,a.fn.init=function c(c,d){return d&&d instanceof p&&!(d instanceof a)&&(d=a(d)),p.fn.init.call(this,c,d,b)},a.fn.init.prototype=a.fn;var b=a(e);return a}}();var bH,bI,bJ,bK=/alpha\([^)]*\)/i,bL=/opacity=([^)]*)/,bM=/^(top|right|bottom|left)$/,bN=/^margin/,bO=new RegExp("^("+q+")(.*)$","i"),bP=new RegExp("^("+q+")(?!px)[a-z%]+$","i"),bQ=new RegExp("^([-+])=("+q+")","i"),bR={},bS={position:"absolute",visibility:"hidden",display:"block"},bT={letterSpacing:0,fontWeight:400,lineHeight:1},bU=["Top","Right","Bottom","Left"],bV=["Webkit","O","Moz","ms"],bW=p.fn.toggle;p.fn.extend({css:function(a,c){return p.access(this,function(a,c,d){return d!==b?p.style(a,c,d):p.css(a,c)},a,c,arguments.length>1)},show:function(){return bZ(this,!0)},hide:function(){return bZ(this)},toggle:function(a,b){var c=typeof a=="boolean";return p.isFunction(a)&&p.isFunction(b)?bW.apply(this,arguments):this.each(function(){(c?a:bY(this))?p(this).show():p(this).hide()})}}),p.extend({cssHooks:{opacity:{get:function(a,b){if(b){var c=bH(a,"opacity");return c===""?"1":c}}}},cssNumber:{fillOpacity:!0,fontWeight:!0,lineHeight:!0,opacity:!0,orphans:!0,widows:!0,zIndex:!0,zoom:!0},cssProps:{"float":p.support.cssFloat?"cssFloat":"styleFloat"},style:function(a,c,d,e){if(!a||a.nodeType===3||a.nodeType===8||!a.style)return;var f,g,h,i=p.camelCase(c),j=a.style;c=p.cssProps[i]||(p.cssProps[i]=bX(j,i)),h=p.cssHooks[c]||p.cssHooks[i];if(d===b)return h&&"get"in h&&(f=h.get(a,!1,e))!==b?f:j[c];g=typeof d,g==="string"&&(f=bQ.exec(d))&&(d=(f[1]+1)*f[2]+parseFloat(p.css(a,c)),g="number");if(d==null||g==="number"&&isNaN(d))return;g==="number"&&!p.cssNumber[i]&&(d+="px");if(!h||!("set"in h)||(d=h.set(a,d,e))!==b)try{j[c]=d}catch(k){}},css:function(a,c,d,e){var f,g,h,i=p.camelCase(c);return c=p.cssProps[i]||(p.cssProps[i]=bX(a.style,i)),h=p.cssHooks[c]||p.cssHooks[i],h&&"get"in h&&(f=h.get(a,!0,e)),f===b&&(f=bH(a,c)),f==="normal"&&c in bT&&(f=bT[c]),d||e!==b?(g=parseFloat(f),d||p.isNumeric(g)?g||0:f):f},swap:function(a,b,c){var d,e,f={};for(e in b)f[e]=a.style[e],a.style[e]=b[e];d=c.call(a);for(e in b)a.style[e]=f[e];return d}}),a.getComputedStyle?bH=function(a,b){var c,d,e,f,g=getComputedStyle(a,null),h=a.style;return g&&(c=g[b],c===""&&!p.contains(a.ownerDocument.documentElement,a)&&(c=p.style(a,b)),bP.test(c)&&bN.test(b)&&(d=h.width,e=h.minWidth,f=h.maxWidth,h.minWidth=h.maxWidth=h.width=c,c=g.width,h.width=d,h.minWidth=e,h.maxWidth=f)),c}:e.documentElement.currentStyle&&(bH=function(a,b){var c,d,e=a.currentStyle&&a.currentStyle[b],f=a.style;return e==null&&f&&f[b]&&(e=f[b]),bP.test(e)&&!bM.test(b)&&(c=f.left,d=a.runtimeStyle&&a.runtimeStyle.left,d&&(a.runtimeStyle.left=a.currentStyle.left),f.left=b==="fontSize"?"1em":e,e=f.pixelLeft+"px",f.left=c,d&&(a.runtimeStyle.left=d)),e===""?"auto":e}),p.each(["height","width"],function(a,b){p.cssHooks[b]={get:function(a,c,d){if(c)return a.offsetWidth!==0||bH(a,"display")!=="none"?ca(a,b,d):p.swap(a,bS,function(){return ca(a,b,d)})},set:function(a,c,d){return b$(a,c,d?b_(a,b,d,p.support.boxSizing&&p.css(a,"boxSizing")==="border-box"):0)}}}),p.support.opacity||(p.cssHooks.opacity={get:function(a,b){return bL.test((b&&a.currentStyle?a.currentStyle.filter:a.style.filter)||"")?.01*parseFloat(RegExp.$1)+"":b?"1":""},set:function(a,b){var c=a.style,d=a.currentStyle,e=p.isNumeric(b)?"alpha(opacity="+b*100+")":"",f=d&&d.filter||c.filter||"";c.zoom=1;if(b>=1&&p.trim(f.replace(bK,""))===""&&c.removeAttribute){c.removeAttribute("filter");if(d&&!d.filter)return}c.filter=bK.test(f)?f.replace(bK,e):f+" "+e}}),p(function(){p.support.reliableMarginRight||(p.cssHooks.marginRight={get:function(a,b){return p.swap(a,{display:"inline-block"},function(){if(b)return bH(a,"marginRight")})}}),!p.support.pixelPosition&&p.fn.position&&p.each(["top","left"],function(a,b){p.cssHooks[b]={get:function(a,c){if(c){var d=bH(a,b);return bP.test(d)?p(a).position()[b]+"px":d}}}})}),p.expr&&p.expr.filters&&(p.expr.filters.hidden=function(a){return a.offsetWidth===0&&a.offsetHeight===0||!p.support.reliableHiddenOffsets&&(a.style&&a.style.display||bH(a,"display"))==="none"},p.expr.filters.visible=function(a){return!p.expr.filters.hidden(a)}),p.each({margin:"",padding:"",border:"Width"},function(a,b){p.cssHooks[a+b]={expand:function(c){var d,e=typeof c=="string"?c.split(" "):[c],f={};for(d=0;d<4;d++)f[a+bU[d]+b]=e[d]||e[d-2]||e[0];return f}},bN.test(a)||(p.cssHooks[a+b].set=b$)});var cc=/%20/g,cd=/\[\]$/,ce=/\r?\n/g,cf=/^(?:color|date|datetime|datetime-local|email|hidden|month|number|password|range|search|tel|text|time|url|week)$/i,cg=/^(?:select|textarea)/i;p.fn.extend({serialize:function(){return p.param(this.serializeArray())},serializeArray:function(){return this.map(function(){return this.elements?p.makeArray(this.elements):this}).filter(function(){return this.name&&!this.disabled&&(this.checked||cg.test(this.nodeName)||cf.test(this.type))}).map(function(a,b){var c=p(this).val();return c==null?null:p.isArray(c)?p.map(c,function(a,c){return{name:b.name,value:a.replace(ce,"\r\n")}}):{name:b.name,value:c.replace(ce,"\r\n")}}).get()}}),p.param=function(a,c){var d,e=[],f=function(a,b){b=p.isFunction(b)?b():b==null?"":b,e[e.length]=encodeURIComponent(a)+"="+encodeURIComponent(b)};c===b&&(c=p.ajaxSettings&&p.ajaxSettings.traditional);if(p.isArray(a)||a.jquery&&!p.isPlainObject(a))p.each(a,function(){f(this.name,this.value)});else for(d in a)ch(d,a[d],c,f);return e.join("&").replace(cc,"+")};var ci,cj,ck=/#.*$/,cl=/^(.*?):[ \t]*([^\r\n]*)\r?$/mg,cm=/^(?:about|app|app\-storage|.+\-extension|file|res|widget):$/,cn=/^(?:GET|HEAD)$/,co=/^\/\//,cp=/\?/,cq=/<script\b[^<]*(?:(?!<\/script>)<[^<]*)*<\/script>/gi,cr=/([?&])_=[^&]*/,cs=/^([\w\+\.\-]+:)(?:\/\/([^\/?#:]*)(?::(\d+)|)|)/,ct=p.fn.load,cu={},cv={},cw=["*/"]+["*"];try{ci=f.href}catch(cx){ci=e.createElement("a"),ci.href="",ci=ci.href}cj=cs.exec(ci.toLowerCase())||[],p.fn.load=function(a,c,d){if(typeof a!="string"&&ct)return ct.apply(this,arguments);if(!this.length)return this;var e,f,g,h=this,i=a.indexOf(" ");return i>=0&&(e=a.slice(i,a.length),a=a.slice(0,i)),p.isFunction(c)?(d=c,c=b):typeof c=="object"&&(f="POST"),p.ajax({url:a,type:f,dataType:"html",data:c,complete:function(a,b){d&&h.each(d,g||[a.responseText,b,a])}}).done(function(a){g=arguments,h.html(e?p("<div>").append(a.replace(cq,"")).find(e):a)}),this},p.each("ajaxStart ajaxStop ajaxComplete ajaxError ajaxSuccess ajaxSend".split(" "),function(a,b){p.fn[b]=function(a){return this.on(b,a)}}),p.each(["get","post"],function(a,c){p[c]=function(a,d,e,f){return p.isFunction(d)&&(f=f||e,e=d,d=b),p.ajax({type:c,url:a,data:d,success:e,dataType:f})}}),p.extend({getScript:function(a,c){return p.get(a,b,c,"script")},getJSON:function(a,b,c){return p.get(a,b,c,"json")},ajaxSetup:function(a,b){return b?cA(a,p.ajaxSettings):(b=a,a=p.ajaxSettings),cA(a,b),a},ajaxSettings:{url:ci,isLocal:cm.test(cj[1]),global:!0,type:"GET",contentType:"application/x-www-form-urlencoded; charset=UTF-8",processData:!0,async:!0,accepts:{xml:"application/xml, text/xml",html:"text/html",text:"text/plain",json:"application/json, text/javascript","*":cw},contents:{xml:/xml/,html:/html/,json:/json/},responseFields:{xml:"responseXML",text:"responseText"},converters:{"* text":a.String,"text html":!0,"text json":p.parseJSON,"text xml":p.parseXML},flatOptions:{context:!0,url:!0}},ajaxPrefilter:cy(cu),ajaxTransport:cy(cv),ajax:function(a,c){function y(a,c,f,i){var k,s,t,u,w,y=c;if(v===2)return;v=2,h&&clearTimeout(h),g=b,e=i||"",x.readyState=a>0?4:0,f&&(u=cB(l,x,f));if(a>=200&&a<300||a===304)l.ifModified&&(w=x.getResponseHeader("Last-Modified"),w&&(p.lastModified[d]=w),w=x.getResponseHeader("Etag"),w&&(p.etag[d]=w)),a===304?(y="notmodified",k=!0):(k=cC(l,u),y=k.state,s=k.data,t=k.error,k=!t);else{t=y;if(!y||a)y="error",a<0&&(a=0)}x.status=a,x.statusText=""+(c||y),k?o.resolveWith(m,[s,y,x]):o.rejectWith(m,[x,y,t]),x.statusCode(r),r=b,j&&n.trigger("ajax"+(k?"Success":"Error"),[x,l,k?s:t]),q.fireWith(m,[x,y]),j&&(n.trigger("ajaxComplete",[x,l]),--p.active||p.event.trigger("ajaxStop"))}typeof a=="object"&&(c=a,a=b),c=c||{};var d,e,f,g,h,i,j,k,l=p.ajaxSetup({},c),m=l.context||l,n=m!==l&&(m.nodeType||m instanceof p)?p(m):p.event,o=p.Deferred(),q=p.Callbacks("once memory"),r=l.statusCode||{},t={},u={},v=0,w="canceled",x={readyState:0,setRequestHeader:function(a,b){if(!v){var c=a.toLowerCase();a=u[c]=u[c]||a,t[a]=b}return this},getAllResponseHeaders:function(){return v===2?e:null},getResponseHeader:function(a){var c;if(v===2){if(!f){f={};while(c=cl.exec(e))f[c[1].toLowerCase()]=c[2]}c=f[a.toLowerCase()]}return c===b?null:c},overrideMimeType:function(a){return v||(l.mimeType=a),this},abort:function(a){return a=a||w,g&&g.abort(a),y(0,a),this}};o.promise(x),x.success=x.done,x.error=x.fail,x.complete=q.add,x.statusCode=function(a){if(a){var b;if(v<2)for(b in a)r[b]=[r[b],a[b]];else b=a[x.status],x.always(b)}return this},l.url=((a||l.url)+"").replace(ck,"").replace(co,cj[1]+"//"),l.dataTypes=p.trim(l.dataType||"*").toLowerCase().split(s),l.crossDomain==null&&(i=cs.exec(l.url.toLowerCase()),l.crossDomain=!(!i||i[1]==cj[1]&&i[2]==cj[2]&&(i[3]||(i[1]==="http:"?80:443))==(cj[3]||(cj[1]==="http:"?80:443)))),l.data&&l.processData&&typeof l.data!="string"&&(l.data=p.param(l.data,l.traditional)),cz(cu,l,c,x);if(v===2)return x;j=l.global,l.type=l.type.toUpperCase(),l.hasContent=!cn.test(l.type),j&&p.active++===0&&p.event.trigger("ajaxStart");if(!l.hasContent){l.data&&(l.url+=(cp.test(l.url)?"&":"?")+l.data,delete l.data),d=l.url;if(l.cache===!1){var z=p.now(),A=l.url.replace(cr,"$1_="+z);l.url=A+(A===l.url?(cp.test(l.url)?"&":"?")+"_="+z:"")}}(l.data&&l.hasContent&&l.contentType!==!1||c.contentType)&&x.setRequestHeader("Content-Type",l.contentType),l.ifModified&&(d=d||l.url,p.lastModified[d]&&x.setRequestHeader("If-Modified-Since",p.lastModified[d]),p.etag[d]&&x.setRequestHeader("If-None-Match",p.etag[d])),x.setRequestHeader("Accept",l.dataTypes[0]&&l.accepts[l.dataTypes[0]]?l.accepts[l.dataTypes[0]]+(l.dataTypes[0]!=="*"?", "+cw+"; q=0.01":""):l.accepts["*"]);for(k in l.headers)x.setRequestHeader(k,l.headers[k]);if(!l.beforeSend||l.beforeSend.call(m,x,l)!==!1&&v!==2){w="abort";for(k in{success:1,error:1,complete:1})x[k](l[k]);g=cz(cv,l,c,x);if(!g)y(-1,"No Transport");else{x.readyState=1,j&&n.trigger("ajaxSend",[x,l]),l.async&&l.timeout>0&&(h=setTimeout(function(){x.abort("timeout")},l.timeout));try{v=1,g.send(t,y)}catch(B){if(v<2)y(-1,B);else throw B}}return x}return x.abort()},active:0,lastModified:{},etag:{}});var cD=[],cE=/\?/,cF=/(=)\?(?=&|$)|\?\?/,cG=p.now();p.ajaxSetup({jsonp:"callback",jsonpCallback:function(){var a=cD.pop()||p.expando+"_"+cG++;return this[a]=!0,a}}),p.ajaxPrefilter("json jsonp",function(c,d,e){var f,g,h,i=c.data,j=c.url,k=c.jsonp!==!1,l=k&&cF.test(j),m=k&&!l&&typeof i=="string"&&!(c.contentType||"").indexOf("application/x-www-form-urlencoded")&&cF.test(i);if(c.dataTypes[0]==="jsonp"||l||m)return f=c.jsonpCallback=p.isFunction(c.jsonpCallback)?c.jsonpCallback():c.jsonpCallback,g=a[f],l?c.url=j.replace(cF,"$1"+f):m?c.data=i.replace(cF,"$1"+f):k&&(c.url+=(cE.test(j)?"&":"?")+c.jsonp+"="+f),c.converters["script json"]=function(){return h||p.error(f+" was not called"),h[0]},c.dataTypes[0]="json",a[f]=function(){h=arguments},e.always(function(){a[f]=g,c[f]&&(c.jsonpCallback=d.jsonpCallback,cD.push(f)),h&&p.isFunction(g)&&g(h[0]),h=g=b}),"script"}),p.ajaxSetup({accepts:{script:"text/javascript, application/javascript, application/ecmascript, application/x-ecmascript"},contents:{script:/javascript|ecmascript/},converters:{"text script":function(a){return p.globalEval(a),a}}}),p.ajaxPrefilter("script",function(a){a.cache===b&&(a.cache=!1),a.crossDomain&&(a.type="GET",a.global=!1)}),p.ajaxTransport("script",function(a){if(a.crossDomain){var c,d=e.head||e.getElementsByTagName("head")[0]||e.documentElement;return{send:function(f,g){c=e.createElement("script"),c.async="async",a.scriptCharset&&(c.charset=a.scriptCharset),c.src=a.url,c.onload=c.onreadystatechange=function(a,e){if(e||!c.readyState||/loaded|complete/.test(c.readyState))c.onload=c.onreadystatechange=null,d&&c.parentNode&&d.removeChild(c),c=b,e||g(200,"success")},d.insertBefore(c,d.firstChild)},abort:function(){c&&c.onload(0,1)}}}});var cH,cI=a.ActiveXObject?function(){for(var a in cH)cH[a](0,1)}:!1,cJ=0;p.ajaxSettings.xhr=a.ActiveXObject?function(){return!this.isLocal&&cK()||cL()}:cK,function(a){p.extend(p.support,{ajax:!!a,cors:!!a&&"withCredentials"in a})}(p.ajaxSettings.xhr()),p.support.ajax&&p.ajaxTransport(function(c){if(!c.crossDomain||p.support.cors){var d;return{send:function(e,f){var g,h,i=c.xhr();c.username?i.open(c.type,c.url,c.async,c.username,c.password):i.open(c.type,c.url,c.async);if(c.xhrFields)for(h in c.xhrFields)i[h]=c.xhrFields[h];c.mimeType&&i.overrideMimeType&&i.overrideMimeType(c.mimeType),!c.crossDomain&&!e["X-Requested-With"]&&(e["X-Requested-With"]="XMLHttpRequest");try{for(h in e)i.setRequestHeader(h,e[h])}catch(j){}i.send(c.hasContent&&c.data||null),d=function(a,e){var h,j,k,l,m;try{if(d&&(e||i.readyState===4)){d=b,g&&(i.onreadystatechange=p.noop,cI&&delete cH[g]);if(e)i.readyState!==4&&i.abort();else{h=i.status,k=i.getAllResponseHeaders(),l={},m=i.responseXML,m&&m.documentElement&&(l.xml=m);try{l.text=i.responseText}catch(a){}try{j=i.statusText}catch(n){j=""}!h&&c.isLocal&&!c.crossDomain?h=l.text?200:404:h===1223&&(h=204)}}}catch(o){e||f(-1,o)}l&&f(h,j,l,k)},c.async?i.readyState===4?setTimeout(d,0):(g=++cJ,cI&&(cH||(cH={},p(a).unload(cI)),cH[g]=d),i.onreadystatechange=d):d()},abort:function(){d&&d(0,1)}}}});var cM,cN,cO=/^(?:toggle|show|hide)$/,cP=new RegExp("^(?:([-+])=|)("+q+")([a-z%]*)$","i"),cQ=/queueHooks$/,cR=[cX],cS={"*":[function(a,b){var c,d,e,f=this.createTween(a,b),g=cP.exec(b),h=f.cur(),i=+h||0,j=1;if(g){c=+g[2],d=g[3]||(p.cssNumber[a]?"":"px");if(d!=="px"&&i){i=p.css(f.elem,a,!0)||c||1;do e=j=j||".5",i=i/j,p.style(f.elem,a,i+d),j=f.cur()/h;while(j!==1&&j!==e)}f.unit=d,f.start=i,f.end=g[1]?i+(g[1]+1)*c:c}return f}]};p.Animation=p.extend(cV,{tweener:function(a,b){p.isFunction(a)?(b=a,a=["*"]):a=a.split(" ");var c,d=0,e=a.length;for(;d<e;d++)c=a[d],cS[c]=cS[c]||[],cS[c].unshift(b)},prefilter:function(a,b){b?cR.unshift(a):cR.push(a)}}),p.Tween=cY,cY.prototype={constructor:cY,init:function(a,b,c,d,e,f){this.elem=a,this.prop=c,this.easing=e||"swing",this.options=b,this.start=this.now=this.cur(),this.end=d,this.unit=f||(p.cssNumber[c]?"":"px")},cur:function(){var a=cY.propHooks[this.prop];return a&&a.get?a.get(this):cY.propHooks._default.get(this)},run:function(a){var b,c=cY.propHooks[this.prop];return this.pos=b=p.easing[this.easing](a,this.options.duration*a,0,1,this.options.duration),this.now=(this.end-this.start)*b+this.start,this.options.step&&this.options.step.call(this.elem,this.now,this),c&&c.set?c.set(this):cY.propHooks._default.set(this),this}},cY.prototype.init.prototype=cY.prototype,cY.propHooks={_default:{get:function(a){var b;return a.elem[a.prop]==null||!!a.elem.style&&a.elem.style[a.prop]!=null?(b=p.css(a.elem,a.prop,!1,""),!b||b==="auto"?0:b):a.elem[a.prop]},set:function(a){p.fx.step[a.prop]?p.fx.step[a.prop](a):a.elem.style&&(a.elem.style[p.cssProps[a.prop]]!=null||p.cssHooks[a.prop])?p.style(a.elem,a.prop,a.now+a.unit):a.elem[a.prop]=a.now}}},cY.propHooks.scrollTop=cY.propHooks.scrollLeft={set:function(a){a.elem.nodeType&&a.elem.parentNode&&(a.elem[a.prop]=a.now)}},p.each(["toggle","show","hide"],function(a,b){var c=p.fn[b];p.fn[b]=function(d,e,f){return d==null||typeof d=="boolean"||!a&&p.isFunction(d)&&p.isFunction(e)?c.apply(this,arguments):this.animate(cZ(b,!0),d,e,f)}}),p.fn.extend({fadeTo:function(a,b,c,d){return this.filter(bY).css("opacity",0).show().end().animate({opacity:b},a,c,d)},animate:function(a,b,c,d){var e=p.isEmptyObject(a),f=p.speed(b,c,d),g=function(){var b=cV(this,p.extend({},a),f);e&&b.stop(!0)};return e||f.queue===!1?this.each(g):this.queue(f.queue,g)},stop:function(a,c,d){var e=function(a){var b=a.stop;delete a.stop,b(d)};return typeof a!="string"&&(d=c,c=a,a=b),c&&a!==!1&&this.queue(a||"fx",[]),this.each(function(){var b=!0,c=a!=null&&a+"queueHooks",f=p.timers,g=p._data(this);if(c)g[c]&&g[c].stop&&e(g[c]);else for(c in g)g[c]&&g[c].stop&&cQ.test(c)&&e(g[c]);for(c=f.length;c--;)f[c].elem===this&&(a==null||f[c].queue===a)&&(f[c].anim.stop(d),b=!1,f.splice(c,1));(b||!d)&&p.dequeue(this,a)})}}),p.each({slideDown:cZ("show"),slideUp:cZ("hide"),slideToggle:cZ("toggle"),fadeIn:{opacity:"show"},fadeOut:{opacity:"hide"},fadeToggle:{opacity:"toggle"}},function(a,b){p.fn[a]=function(a,c,d){return this.animate(b,a,c,d)}}),p.speed=function(a,b,c){var d=a&&typeof a=="object"?p.extend({},a):{complete:c||!c&&b||p.isFunction(a)&&a,duration:a,easing:c&&b||b&&!p.isFunction(b)&&b};d.duration=p.fx.off?0:typeof d.duration=="number"?d.duration:d.duration in p.fx.speeds?p.fx.speeds[d.duration]:p.fx.speeds._default;if(d.queue==null||d.queue===!0)d.queue="fx";return d.old=d.complete,d.complete=function(){p.isFunction(d.old)&&d.old.call(this),d.queue&&p.dequeue(this,d.queue)},d},p.easing={linear:function(a){return a},swing:function(a){return.5-Math.cos(a*Math.PI)/2}},p.timers=[],p.fx=cY.prototype.init,p.fx.tick=function(){var a,b=p.timers,c=0;for(;c<b.length;c++)a=b[c],!a()&&b[c]===a&&b.splice(c--,1);b.length||p.fx.stop()},p.fx.timer=function(a){a()&&p.timers.push(a)&&!cN&&(cN=setInterval(p.fx.tick,p.fx.interval))},p.fx.interval=13,p.fx.stop=function(){clearInterval(cN),cN=null},p.fx.speeds={slow:600,fast:200,_default:400},p.fx.step={},p.expr&&p.expr.filters&&(p.expr.filters.animated=function(a){return p.grep(p.timers,function(b){return a===b.elem}).length});var c$=/^(?:body|html)$/i;p.fn.offset=function(a){if(arguments.length)return a===b?this:this.each(function(b){p.offset.setOffset(this,a,b)});var c,d,e,f,g,h,i,j,k,l,m=this[0],n=m&&m.ownerDocument;if(!n)return;return(e=n.body)===m?p.offset.bodyOffset(m):(d=n.documentElement,p.contains(d,m)?(c=m.getBoundingClientRect(),f=c_(n),g=d.clientTop||e.clientTop||0,h=d.clientLeft||e.clientLeft||0,i=f.pageYOffset||d.scrollTop,j=f.pageXOffset||d.scrollLeft,k=c.top+i-g,l=c.left+j-h,{top:k,left:l}):{top:0,left:0})},p.offset={bodyOffset:function(a){var b=a.offsetTop,c=a.offsetLeft;return p.support.doesNotIncludeMarginInBodyOffset&&(b+=parseFloat(p.css(a,"marginTop"))||0,c+=parseFloat(p.css(a,"marginLeft"))||0),{top:b,left:c}},setOffset:function(a,b,c){var d=p.css(a,"position");d==="static"&&(a.style.position="relative");var e=p(a),f=e.offset(),g=p.css(a,"top"),h=p.css(a,"left"),i=(d==="absolute"||d==="fixed")&&p.inArray("auto",[g,h])>-1,j={},k={},l,m;i?(k=e.position(),l=k.top,m=k.left):(l=parseFloat(g)||0,m=parseFloat(h)||0),p.isFunction(b)&&(b=b.call(a,c,f)),b.top!=null&&(j.top=b.top-f.top+l),b.left!=null&&(j.left=b.left-f.left+m),"using"in b?b.using.call(a,j):e.css(j)}},p.fn.extend({position:function(){if(!this[0])return;var a=this[0],b=this.offsetParent(),c=this.offset(),d=c$.test(b[0].nodeName)?{top:0,left:0}:b.offset();return c.top-=parseFloat(p.css(a,"marginTop"))||0,c.left-=parseFloat(p.css(a,"marginLeft"))||0,d.top+=parseFloat(p.css(b[0],"borderTopWidth"))||0,d.left+=parseFloat(p.css(b[0],"borderLeftWidth"))||0,{top:c.top-d.top,left:c.left-d.left}},offsetParent:function(){return this.map(function(){var a=this.offsetParent||e.body;while(a&&!c$.test(a.nodeName)&&p.css(a,"position")==="static")a=a.offsetParent;return a||e.body})}}),p.each({scrollLeft:"pageXOffset",scrollTop:"pageYOffset"},function(a,c){var d=/Y/.test(c);p.fn[a]=function(e){return p.access(this,function(a,e,f){var g=c_(a);if(f===b)return g?c in g?g[c]:g.document.documentElement[e]:a[e];g?g.scrollTo(d?p(g).scrollLeft():f,d?f:p(g).scrollTop()):a[e]=f},a,e,arguments.length,null)}}),p.each({Height:"height",Width:"width"},function(a,c){p.each({padding:"inner"+a,content:c,"":"outer"+a},function(d,e){p.fn[e]=function(e,f){var g=arguments.length&&(d||typeof e!="boolean"),h=d||(e===!0||f===!0?"margin":"border");return p.access(this,function(c,d,e){var f;return p.isWindow(c)?c.document.documentElement["client"+a]:c.nodeType===9?(f=c.documentElement,Math.max(c.body["scroll"+a],f["scroll"+a],c.body["offset"+a],f["offset"+a],f["client"+a])):e===b?p.css(c,d,e,h):p.style(c,d,e,h)},c,g?e:b,g)}})}),a.jQuery=a.$=p,typeof define=="function"&&define.amd&&define.amd.jQuery&&define("jquery",[],function(){return p})})(window); (function (a, b) {
  function G(a) {
  var b = F[a] = {};
  return p.each(a.split(s), function (a, c) {
  b[c] = !0
  }), b
  }
   
  function J(a, c, d) {
  if (d === b && a.nodeType === 1) {
  var e = "data-" + c.replace(I, "-$1").toLowerCase();
  d = a.getAttribute(e);
  if (typeof d == "string") {
  try {
  d = d === "true" ? !0 : d === "false" ? !1 : d === "null" ? null : +d + "" === d ? +d : H.test(d) ? p.parseJSON(d) : d
  } catch (f) {
  }
  p.data(a, c, d)
  } else d = b
  }
  return d
  }
   
  function K(a) {
  var b;
  for (b in a) {
  if (b === "data" && p.isEmptyObject(a[b]))continue;
  if (b !== "toJSON")return!1
  }
  return!0
  }
   
  function ba() {
  return!1
  }
   
  function bb() {
  return!0
  }
   
  function bh(a) {
  return!a || !a.parentNode || a.parentNode.nodeType === 11
  }
   
  function bi(a, b) {
  do a = a[b]; while (a && a.nodeType !== 1);
  return a
  }
   
  function bj(a, b, c) {
  b = b || 0;
  if (p.isFunction(b))return p.grep(a, function (a, d) {
  var e = !!b.call(a, d, a);
  return e === c
  });
  if (b.nodeType)return p.grep(a, function (a, d) {
  return a === b === c
  });
  if (typeof b == "string") {
  var d = p.grep(a, function (a) {
  return a.nodeType === 1
  });
  if (be.test(b))return p.filter(b, d, !c);
  b = p.filter(b, d)
  }
  return p.grep(a, function (a, d) {
  return p.inArray(a, b) >= 0 === c
  })
  }
   
  function bk(a) {
  var b = bl.split("|"), c = a.createDocumentFragment();
  if (c.createElement)while (b.length)c.createElement(b.pop());
  return c
  }
   
  function bC(a, b) {
  return a.getElementsByTagName(b)[0] || a.appendChild(a.ownerDocument.createElement(b))
  }
   
  function bD(a, b) {
  if (b.nodeType !== 1 || !p.hasData(a))return;
  var c, d, e, f = p._data(a), g = p._data(b, f), h = f.events;
  if (h) {
  delete g.handle, g.events = {};
  for (c in h)for (d = 0, e = h[c].length; d < e; d++)p.event.add(b, c, h[c][d])
  }
  g.data && (g.data = p.extend({}, g.data))
  }
   
  function bE(a, b) {
  var c;
  if (b.nodeType !== 1)return;
  b.clearAttributes && b.clearAttributes(), b.mergeAttributes && b.mergeAttributes(a), c = b.nodeName.toLowerCase(), c === "object" ? (b.parentNode && (b.outerHTML = a.outerHTML), p.support.html5Clone && a.innerHTML && !p.trim(b.innerHTML) && (b.innerHTML = a.innerHTML)) : c === "input" && bv.test(a.type) ? (b.defaultChecked = b.checked = a.checked, b.value !== a.value && (b.value = a.value)) : c === "option" ? b.selected = a.defaultSelected : c === "input" || c === "textarea" ? b.defaultValue = a.defaultValue : c === "script" && b.text !== a.text && (b.text = a.text), b.removeAttribute(p.expando)
  }
   
  function bF(a) {
  return typeof a.getElementsByTagName != "undefined" ? a.getElementsByTagName("*") : typeof a.querySelectorAll != "undefined" ? a.querySelectorAll("*") : []
  }
   
  function bG(a) {
  bv.test(a.type) && (a.defaultChecked = a.checked)
  }
   
  function bX(a, b) {
  if (b in a)return b;
  var c = b.charAt(0).toUpperCase() + b.slice(1), d = b, e = bV.length;
  while (e--) {
  b = bV[e] + c;
  if (b in a)return b
  }
  return d
  }
   
  function bY(a, b) {
  return a = b || a, p.css(a, "display") === "none" || !p.contains(a.ownerDocument, a)
  }
   
  function bZ(a, b) {
  var c, d, e = [], f = 0, g = a.length;
  for (; f < g; f++) {
  c = a[f];
  if (!c.style)continue;
  e[f] = p._data(c, "olddisplay"), b ? (!e[f] && c.style.display === "none" && (c.style.display = ""), c.style.display === "" && bY(c) && (e[f] = p._data(c, "olddisplay", cb(c.nodeName)))) : (d = bH(c, "display"), !e[f] && d !== "none" && p._data(c, "olddisplay", d))
  }
  for (f = 0; f < g; f++) {
  c = a[f];
  if (!c.style)continue;
  if (!b || c.style.display === "none" || c.style.display === "")c.style.display = b ? e[f] || "" : "none"
  }
  return a
  }
   
  function b$(a, b, c) {
  var d = bO.exec(b);
  return d ? Math.max(0, d[1] - (c || 0)) + (d[2] || "px") : b
  }
   
  function b_(a, b, c, d) {
  var e = c === (d ? "border" : "content") ? 4 : b === "width" ? 1 : 0, f = 0;
  for (; e < 4; e += 2)c === "margin" && (f += p.css(a, c + bU[e], !0)), d ? (c === "content" && (f -= parseFloat(bH(a, "padding" + bU[e])) || 0), c !== "margin" && (f -= parseFloat(bH(a, "border" + bU[e] + "Width")) || 0)) : (f += parseFloat(bH(a, "padding" + bU[e])) || 0, c !== "padding" && (f += parseFloat(bH(a, "border" + bU[e] + "Width")) || 0));
  return f
  }
   
  function ca(a, b, c) {
  var d = b === "width" ? a.offsetWidth : a.offsetHeight, e = !0, f = p.support.boxSizing && p.css(a, "boxSizing") === "border-box";
  if (d <= 0) {
  d = bH(a, b);
  if (d < 0 || d == null)d = a.style[b];
  if (bP.test(d))return d;
  e = f && (p.support.boxSizingReliable || d === a.style[b]), d = parseFloat(d) || 0
  }
  return d + b_(a, b, c || (f ? "border" : "content"), e) + "px"
  }
   
  function cb(a) {
  if (bR[a])return bR[a];
  var b = p("<" + a + ">").appendTo(e.body), c = b.css("display");
  b.remove();
  if (c === "none" || c === "") {
  bI = e.body.appendChild(bI || p.extend(e.createElement("iframe"), {frameBorder: 0, width: 0, height: 0}));
  if (!bJ || !bI.createElement)bJ = (bI.contentWindow || bI.contentDocument).document, bJ.write("<!doctype html><html><body>"), bJ.close();
  b = bJ.body.appendChild(bJ.createElement(a)), c = bH(b, "display"), e.body.removeChild(bI)
  }
  return bR[a] = c, c
  }
   
  function ch(a, b, c, d) {
  var e;
  if (p.isArray(b))p.each(b, function (b, e) {
  c || cd.test(a) ? d(a, e) : ch(a + "[" + (typeof e == "object" ? b : "") + "]", e, c, d)
  }); else if (!c && p.type(b) === "object")for (e in b)ch(a + "[" + e + "]", b[e], c, d); else d(a, b)
  }
   
  function cy(a) {
  return function (b, c) {
  typeof b != "string" && (c = b, b = "*");
  var d, e, f, g = b.toLowerCase().split(s), h = 0, i = g.length;
  if (p.isFunction(c))for (; h < i; h++)d = g[h], f = /^\+/.test(d), f && (d = d.substr(1) || "*"), e = a[d] = a[d] || [], e[f ? "unshift" : "push"](c)
  }
  }
   
  function cz(a, c, d, e, f, g) {
  f = f || c.dataTypes[0], g = g || {}, g[f] = !0;
  var h, i = a[f], j = 0, k = i ? i.length : 0, l = a === cu;
  for (; j < k && (l || !h); j++)h = i[j](c, d, e), typeof h == "string" && (!l || g[h] ? h = b : (c.dataTypes.unshift(h), h = cz(a, c, d, e, h, g)));
  return(l || !h) && !g["*"] && (h = cz(a, c, d, e, "*", g)), h
  }
   
  function cA(a, c) {
  var d, e, f = p.ajaxSettings.flatOptions || {};
  for (d in c)c[d] !== b && ((f[d] ? a : e || (e = {}))[d] = c[d]);
  e && p.extend(!0, a, e)
  }
   
  function cB(a, c, d) {
  var e, f, g, h, i = a.contents, j = a.dataTypes, k = a.responseFields;
  for (f in k)f in d && (c[k[f]] = d[f]);
  while (j[0] === "*")j.shift(), e === b && (e = a.mimeType || c.getResponseHeader("content-type"));
  if (e)for (f in i)if (i[f] && i[f].test(e)) {
  j.unshift(f);
  break
  }
  if (j[0]in d)g = j[0]; else {
  for (f in d) {
  if (!j[0] || a.converters[f + " " + j[0]]) {
  g = f;
  break
  }
  h || (h = f)
  }
  g = g || h
  }
  if (g)return g !== j[0] && j.unshift(g), d[g]
  }
   
  function cC(a, b) {
  var c, d, e, f, g = a.dataTypes.slice(), h = g[0], i = {}, j = 0;
  a.dataFilter && (b = a.dataFilter(b, a.dataType));
  if (g[1])for (c in a.converters)i[c.toLowerCase()] = a.converters[c];
  for (; e = g[++j];)if (e !== "*") {
  if (h !== "*" && h !== e) {
  c = i[h + " " + e] || i["* " + e];
  if (!c)for (d in i) {
  f = d.split(" ");
  if (f[1] === e) {
  c = i[h + " " + f[0]] || i["* " + f[0]];
  if (c) {
  c === !0 ? c = i[d] : i[d] !== !0 && (e = f[0], g.splice(j--, 0, e));
  break
  }
  }
  }
  if (c !== !0)if (c && a["throws"])b = c(b); else try {
  b = c(b)
  } catch (k) {
  return{state: "parsererror", error: c ? k : "No conversion from " + h + " to " + e}
  }
  }
  h = e
  }
  return{state: "success", data: b}
  }
   
  function cK() {
  try {
  return new a.XMLHttpRequest
  } catch (b) {
  }
  }
   
  function cL() {
  try {
  return new a.ActiveXObject("Microsoft.XMLHTTP")
  } catch (b) {
  }
  }
   
  function cT() {
  return setTimeout(function () {
  cM = b
  }, 0), cM = p.now()
  }
   
  function cU(a, b) {
  p.each(b, function (b, c) {
  var d = (cS[b] || []).concat(cS["*"]), e = 0, f = d.length;
  for (; e < f; e++)if (d[e].call(a, b, c))return
  })
  }
   
  function cV(a, b, c) {
  var d, e = 0, f = 0, g = cR.length, h = p.Deferred().always(function () {
  delete i.elem
  }), i = function () {
  var b = cM || cT(), c = Math.max(0, j.startTime + j.duration - b), d = 1 - (c / j.duration || 0), e = 0, f = j.tweens.length;
  for (; e < f; e++)j.tweens[e].run(d);
  return h.notifyWith(a, [j, d, c]), d < 1 && f ? c : (h.resolveWith(a, [j]), !1)
  }, j = h.promise({elem: a, props: p.extend({}, b), opts: p.extend(!0, {specialEasing: {}}, c), originalProperties: b, originalOptions: c, startTime: cM || cT(), duration: c.duration, tweens: [], createTween: function (b, c, d) {
  var e = p.Tween(a, j.opts, b, c, j.opts.specialEasing[b] || j.opts.easing);
  return j.tweens.push(e), e
  }, stop: function (b) {
  var c = 0, d = b ? j.tweens.length : 0;
  for (; c < d; c++)j.tweens[c].run(1);
  return b ? h.resolveWith(a, [j, b]) : h.rejectWith(a, [j, b]), this
  }}), k = j.props;
  cW(k, j.opts.specialEasing);
  for (; e < g; e++) {
  d = cR[e].call(j, a, k, j.opts);
  if (d)return d
  }
  return cU(j, k), p.isFunction(j.opts.start) && j.opts.start.call(a, j), p.fx.timer(p.extend(i, {anim: j, queue: j.opts.queue, elem: a})), j.progress(j.opts.progress).done(j.opts.done, j.opts.complete).fail(j.opts.fail).always(j.opts.always)
  }
   
  function cW(a, b) {
  var c, d, e, f, g;
  for (c in a) {
  d = p.camelCase(c), e = b[d], f = a[c], p.isArray(f) && (e = f[1], f = a[c] = f[0]), c !== d && (a[d] = f, delete a[c]), g = p.cssHooks[d];
  if (g && "expand"in g) {
  f = g.expand(f), delete a[d];
  for (c in f)c in a || (a[c] = f[c], b[c] = e)
  } else b[d] = e
  }
  }
   
  function cX(a, b, c) {
  var d, e, f, g, h, i, j, k, l = this, m = a.style, n = {}, o = [], q = a.nodeType && bY(a);
  c.queue || (j = p._queueHooks(a, "fx"), j.unqueued == null && (j.unqueued = 0, k = j.empty.fire, j.empty.fire = function () {
  j.unqueued || k()
  }), j.unqueued++, l.always(function () {
  l.always(function () {
  j.unqueued--, p.queue(a, "fx").length || j.empty.fire()
  })
  })), a.nodeType === 1 && ("height"in b || "width"in b) && (c.overflow = [m.overflow, m.overflowX, m.overflowY], p.css(a, "display") === "inline" && p.css(a, "float") === "none" && (!p.support.inlineBlockNeedsLayout || cb(a.nodeName) === "inline" ? m.display = "inline-block" : m.zoom = 1)), c.overflow && (m.overflow = "hidden", p.support.shrinkWrapBlocks || l.done(function () {
  m.overflow = c.overflow[0], m.overflowX = c.overflow[1], m.overflowY = c.overflow[2]
  }));
  for (d in b) {
  f = b[d];
  if (cO.exec(f)) {
  delete b[d];
  if (f === (q ? "hide" : "show"))continue;
  o.push(d)
  }
  }
  g = o.length;
  if (g) {
  h = p._data(a, "fxshow") || p._data(a, "fxshow", {}), q ? p(a).show() : l.done(function () {
  p(a).hide()
  }), l.done(function () {
  var b;
  p.removeData(a, "fxshow", !0);
  for (b in n)p.style(a, b, n[b])
  });
  for (d = 0; d < g; d++)e = o[d], i = l.createTween(e, q ? h[e] : 0), n[e] = h[e] || p.style(a, e), e in h || (h[e] = i.start, q && (i.end = i.start, i.start = e === "width" || e === "height" ? 1 : 0))
  }
  }
   
  function cY(a, b, c, d, e) {
  return new cY.prototype.init(a, b, c, d, e)
  }
   
  function cZ(a, b) {
  var c, d = {height: a}, e = 0;
  for (; e < 4; e += 2 - b)c = bU[e], d["margin" + c] = d["padding" + c] = a;
  return b && (d.opacity = d.width = a), d
  }
   
  function c_(a) {
  return p.isWindow(a) ? a : a.nodeType === 9 ? a.defaultView || a.parentWindow : !1
  }
   
  var c, d, e = a.document, f = a.location, g = a.navigator, h = a.jQuery, i = a.$, j = Array.prototype.push, k = Array.prototype.slice, l = Array.prototype.indexOf, m = Object.prototype.toString, n = Object.prototype.hasOwnProperty, o = String.prototype.trim, p = function (a, b) {
  return new p.fn.init(a, b, c)
  }, q = /[\-+]?(?:\d*\.|)\d+(?:[eE][\-+]?\d+|)/.source, r = /\S/, s = /\s+/, t = r.test(" ") ? /^[\s\xA0]+|[\s\xA0]+$/g : /^\s+|\s+$/g, u = /^(?:[^#<]*(<[\w\W]+>)[^>]*$|#([\w\-]*)$)/, v = /^<(\w+)\s*\/?>(?:<\/\1>|)$/, w = /^[\],:{}\s]*$/, x = /(?:^|:|,)(?:\s*\[)+/g, y = /\\(?:["\\\/bfnrt]|u[\da-fA-F]{4})/g, z = /"[^"\\\r\n]*"|true|false|null|-?(?:\d\d*\.|)\d+(?:[eE][\-+]?\d+|)/g, A = /^-ms-/, B = /-([\da-z])/gi, C = function (a, b) {
  return(b + "").toUpperCase()
  }, D = function () {
  e.addEventListener ? (e.removeEventListener("DOMContentLoaded", D, !1), p.ready()) : e.readyState === "complete" && (e.detachEvent("onreadystatechange", D), p.ready())
  }, E = {};
  p.fn = p.prototype = {constructor: p, init: function (a, c, d) {
  var f, g, h, i;
  if (!a)return this;
  if (a.nodeType)return this.context = this[0] = a, this.length = 1, this;
  if (typeof a == "string") {
  a.charAt(0) === "<" && a.charAt(a.length - 1) === ">" && a.length >= 3 ? f = [null, a, null] : f = u.exec(a);
  if (f && (f[1] || !c)) {
  if (f[1])return c = c instanceof p ? c[0] : c, i = c && c.nodeType ? c.ownerDocument || c : e, a = p.parseHTML(f[1], i, !0), v.test(f[1]) && p.isPlainObject(c) && this.attr.call(a, c, !0), p.merge(this, a);
  g = e.getElementById(f[2]);
  if (g && g.parentNode) {
  if (g.id !== f[2])return d.find(a);
  this.length = 1, this[0] = g
  }
  return this.context = e, this.selector = a, this
  }
  return!c || c.jquery ? (c || d).find(a) : this.constructor(c).find(a)
  }
  return p.isFunction(a) ? d.ready(a) : (a.selector !== b && (this.selector = a.selector, this.context = a.context), p.makeArray(a, this))
  }, selector: "", jquery: "1.8.0", length: 0, size: function () {
  return this.length
  }, toArray: function () {
  return k.call(this)
  }, get: function (a) {
  return a == null ? this.toArray() : a < 0 ? this[this.length + a] : this[a]
  }, pushStack: function (a, b, c) {
  var d = p.merge(this.constructor(), a);
  return d.prevObject = this, d.context = this.context, b === "find" ? d.selector = this.selector + (this.selector ? " " : "") + c : b && (d.selector = this.selector + "." + b + "(" + c + ")"), d
  }, each: function (a, b) {
  return p.each(this, a, b)
  }, ready: function (a) {
  return p.ready.promise().done(a), this
  }, eq: function (a) {
  return a = +a, a === -1 ? this.slice(a) : this.slice(a, a + 1)
  }, first: function () {
  return this.eq(0)
  }, last: function () {
  return this.eq(-1)
  }, slice: function () {
  return this.pushStack(k.apply(this, arguments), "slice", k.call(arguments).join(","))
  }, map: function (a) {
  return this.pushStack(p.map(this, function (b, c) {
  return a.call(b, c, b)
  }))
  }, end: function () {
  return this.prevObject || this.constructor(null)
  }, push: j, sort: [].sort, splice: [].splice}, p.fn.init.prototype = p.fn, p.extend = p.fn.extend = function () {
  var a, c, d, e, f, g, h = arguments[0] || {}, i = 1, j = arguments.length, k = !1;
  typeof h == "boolean" && (k = h, h = arguments[1] || {}, i = 2), typeof h != "object" && !p.isFunction(h) && (h = {}), j === i && (h = this, --i);
  for (; i < j; i++)if ((a = arguments[i]) != null)for (c in a) {
  d = h[c], e = a[c];
  if (h === e)continue;
  k && e && (p.isPlainObject(e) || (f = p.isArray(e))) ? (f ? (f = !1, g = d && p.isArray(d) ? d : []) : g = d && p.isPlainObject(d) ? d : {}, h[c] = p.extend(k, g, e)) : e !== b && (h[c] = e)
  }
  return h
  }, p.extend({noConflict: function (b) {
  return a.$ === p && (a.$ = i), b && a.jQuery === p && (a.jQuery = h), p
  }, isReady: !1, readyWait: 1, holdReady: function (a) {
  a ? p.readyWait++ : p.ready(!0)
  }, ready: function (a) {
  if (a === !0 ? --p.readyWait : p.isReady)return;
  if (!e.body)return setTimeout(p.ready, 1);
  p.isReady = !0;
  if (a !== !0 && --p.readyWait > 0)return;
  d.resolveWith(e, [p]), p.fn.trigger && p(e).trigger("ready").off("ready")
  }, isFunction: function (a) {
  return p.type(a) === "function"
  }, isArray: Array.isArray || function (a) {
  return p.type(a) === "array"
  }, isWindow: function (a) {
  return a != null && a == a.window
  }, isNumeric: function (a) {
  return!isNaN(parseFloat(a)) && isFinite(a)
  }, type: function (a) {
  return a == null ? String(a) : E[m.call(a)] || "object"
  }, isPlainObject: function (a) {
  if (!a || p.type(a) !== "object" || a.nodeType || p.isWindow(a))return!1;
  try {
  if (a.constructor && !n.call(a, "constructor") && !n.call(a.constructor.prototype, "isPrototypeOf"))return!1
  } catch (c) {
  return!1
  }
  var d;
  for (d in a);
  return d === b || n.call(a, d)
  }, isEmptyObject: function (a) {
  var b;
  for (b in a)return!1;
  return!0
  }, error: function (a) {
  throw new Error(a)
  }, parseHTML: function (a, b, c) {
  var d;
  return!a || typeof a != "string" ? null : (typeof b == "boolean" && (c = b, b = 0), b = b || e, (d = v.exec(a)) ? [b.createElement(d[1])] : (d = p.buildFragment([a], b, c ? null : []), p.merge([], (d.cacheable ? p.clone(d.fragment) : d.fragment).childNodes)))
  }, parseJSON: function (b) {
  if (!b || typeof b != "string")return null;
  b = p.trim(b);
  if (a.JSON && a.JSON.parse)return a.JSON.parse(b);
  if (w.test(b.replace(y, "@").replace(z, "]").replace(x, "")))return(new Function("return " + b))();
  p.error("Invalid JSON: " + b)
  }, parseXML: function (c) {
  var d, e;
  if (!c || typeof c != "string")return null;
  try {
  a.DOMParser ? (e = new DOMParser, d = e.parseFromString(c, "text/xml")) : (d = new ActiveXObject("Microsoft.XMLDOM"), d.async = "false", d.loadXML(c))
  } catch (f) {
  d = b
  }
  return(!d || !d.documentElement || d.getElementsByTagName("parsererror").length) && p.error("Invalid XML: " + c), d
  }, noop: function () {
  }, globalEval: function (b) {
  b && r.test(b) && (a.execScript || function (b) {
  a.eval.call(a, b)
  })(b)
  }, camelCase: function (a) {
  return a.replace(A, "ms-").replace(B, C)
  }, nodeName: function (a, b) {
  return a.nodeName && a.nodeName.toUpperCase() === b.toUpperCase()
  }, each: function (a, c, d) {
  var e, f = 0, g = a.length, h = g === b || p.isFunction(a);
  if (d) {
  if (h) {
  for (e in a)if (c.apply(a[e], d) === !1)break
  } else for (; f < g;)if (c.apply(a[f++], d) === !1)break
  } else if (h) {
  for (e in a)if (c.call(a[e], e, a[e]) === !1)break
  } else for (; f < g;)if (c.call(a[f], f, a[f++]) === !1)break;
  return a
  }, trim: o ? function (a) {
  return a == null ? "" : o.call(a)
  } : function (a) {
  return a == null ? "" : a.toString().replace(t, "")
  }, makeArray: function (a, b) {
  var c, d = b || [];
  return a != null && (c = p.type(a), a.length == null || c === "string" || c === "function" || c === "regexp" || p.isWindow(a) ? j.call(d, a) : p.merge(d, a)), d
  }, inArray: function (a, b, c) {
  var d;
  if (b) {
  if (l)return l.call(b, a, c);
  d = b.length, c = c ? c < 0 ? Math.max(0, d + c) : c : 0;
  for (; c < d; c++)if (c in b && b[c] === a)return c
  }
  return-1
  }, merge: function (a, c) {
  var d = c.length, e = a.length, f = 0;
  if (typeof d == "number")for (; f < d; f++)a[e++] = c[f]; else while (c[f] !== b)a[e++] = c[f++];
  return a.length = e, a
  }, grep: function (a, b, c) {
  var d, e = [], f = 0, g = a.length;
  c = !!c;
  for (; f < g; f++)d = !!b(a[f], f), c !== d && e.push(a[f]);
  return e
  }, map: function (a, c, d) {
  var e, f, g = [], h = 0, i = a.length, j = a instanceof p || i !== b && typeof i == "number" && (i > 0 && a[0] && a[i - 1] || i === 0 || p.isArray(a));
  if (j)for (; h < i; h++)e = c(a[h], h, d), e != null && (g[g.length] = e); else for (f in a)e = c(a[f], f, d), e != null && (g[g.length] = e);
  return g.concat.apply([], g)
  }, guid: 1, proxy: function (a, c) {
  var d, e, f;
  return typeof c == "string" && (d = a[c], c = a, a = d), p.isFunction(a) ? (e = k.call(arguments, 2), f = function () {
  return a.apply(c, e.concat(k.call(arguments)))
  }, f.guid = a.guid = a.guid || f.guid || p.guid++, f) : b
  }, access: function (a, c, d, e, f, g, h) {
  var i, j = d == null, k = 0, l = a.length;
  if (d && typeof d == "object") {
  for (k in d)p.access(a, c, k, d[k], 1, g, e);
  f = 1
  } else if (e !== b) {
  i = h === b && p.isFunction(e), j && (i ? (i = c, c = function (a, b, c) {
  return i.call(p(a), c)
  }) : (c.call(a, e), c = null));
  if (c)for (; k < l; k++)c(a[k], d, i ? e.call(a[k], k, c(a[k], d)) : e, h);
  f = 1
  }
  return f ? a : j ? c.call(a) : l ? c(a[0], d) : g
  }, now: function () {
  return(new Date).getTime()
  }}), p.ready.promise = function (b) {
  if (!d) {
  d = p.Deferred();
  if (e.readyState === "complete" || e.readyState !== "loading" && e.addEventListener)setTimeout(p.ready, 1); else if (e.addEventListener)e.addEventListener("DOMContentLoaded", D, !1), a.addEventListener("load", p.ready, !1); else {
  e.attachEvent("onreadystatechange", D), a.attachEvent("onload", p.ready);
  var c = !1;
  try {
  c = a.frameElement == null && e.documentElement
  } catch (f) {
  }
  c && c.doScroll && function g() {
  if (!p.isReady) {
  try {
  c.doScroll("left")
  } catch (a) {
  return setTimeout(g, 50)
  }
  p.ready()
  }
  }()
  }
  }
  return d.promise(b)
  }, p.each("Boolean Number String Function Array Date RegExp Object".split(" "), function (a, b) {
  E["[object " + b + "]"] = b.toLowerCase()
  }), c = p(e);
  var F = {};
  p.Callbacks = function (a) {
  a = typeof a == "string" ? F[a] || G(a) : p.extend({}, a);
  var c, d, e, f, g, h, i = [], j = !a.once && [], k = function (b) {
  c = a.memory && b, d = !0, h = f || 0, f = 0, g = i.length, e = !0;
  for (; i && h < g; h++)if (i[h].apply(b[0], b[1]) === !1 && a.stopOnFalse) {
  c = !1;
  break
  }
  e = !1, i && (j ? j.length && k(j.shift()) : c ? i = [] : l.disable())
  }, l = {add: function () {
  if (i) {
  var b = i.length;
  (function d(b) {
  p.each(b, function (b, c) {
  p.isFunction(c) && (!a.unique || !l.has(c)) ? i.push(c) : c && c.length && d(c)
  })
  })(arguments), e ? g = i.length : c && (f = b, k(c))
  }
  return this
  }, remove: function () {
  return i && p.each(arguments, function (a, b) {
  var c;
  while ((c = p.inArray(b, i, c)) > -1)i.splice(c, 1), e && (c <= g && g--, c <= h && h--)
  }), this
  }, has: function (a) {
  return p.inArray(a, i) > -1
  }, empty: function () {
  return i = [], this
  }, disable: function () {
  return i = j = c = b, this
  }, disabled: function () {
  return!i
  }, lock: function () {
  return j = b, c || l.disable(), this
  }, locked: function () {
  return!j
  }, fireWith: function (a, b) {
  return b = b || [], b = [a, b.slice ? b.slice() : b], i && (!d || j) && (e ? j.push(b) : k(b)), this
  }, fire: function () {
  return l.fireWith(this, arguments), this
  }, fired: function () {
  return!!d
  }};
  return l
  }, p.extend({Deferred: function (a) {
  var b = [
  ["resolve", "done", p.Callbacks("once memory"), "resolved"],
  ["reject", "fail", p.Callbacks("once memory"), "rejected"],
  ["notify", "progress", p.Callbacks("memory")]
  ], c = "pending", d = {state: function () {
  return c
  }, always: function () {
  return e.done(arguments).fail(arguments), this
  }, then: function () {
  var a = arguments;
  return p.Deferred(function (c) {
  p.each(b, function (b, d) {
  var f = d[0], g = a[b];
  e[d[1]](p.isFunction(g) ? function () {
  var a = g.apply(this, arguments);
  a && p.isFunction(a.promise) ? a.promise().done(c.resolve).fail(c.reject).progress(c.notify) : c[f + "With"](this === e ? c : this, [a])
  } : c[f])
  }), a = null
  }).promise()
  }, promise: function (a) {
  return typeof a == "object" ? p.extend(a, d) : d
  }}, e = {};
  return d.pipe = d.then, p.each(b, function (a, f) {
  var g = f[2], h = f[3];
  d[f[1]] = g.add, h && g.add(function () {
  c = h
  }, b[a ^ 1][2].disable, b[2][2].lock), e[f[0]] = g.fire, e[f[0] + "With"] = g.fireWith
  }), d.promise(e), a && a.call(e, e), e
  }, when: function (a) {
  var b = 0, c = k.call(arguments), d = c.length, e = d !== 1 || a && p.isFunction(a.promise) ? d : 0, f = e === 1 ? a : p.Deferred(), g = function (a, b, c) {
  return function (d) {
  b[a] = this, c[a] = arguments.length > 1 ? k.call(arguments) : d, c === h ? f.notifyWith(b, c) : --e || f.resolveWith(b, c)
  }
  }, h, i, j;
  if (d > 1) {
  h = new Array(d), i = new Array(d), j = new Array(d);
  for (; b < d; b++)c[b] && p.isFunction(c[b].promise) ? c[b].promise().done(g(b, j, c)).fail(f.reject).progress(g(b, i, h)) : --e
  }
  return e || f.resolveWith(j, c), f.promise()
  }}), p.support = function () {
  var b, c, d, f, g, h, i, j, k, l, m, n = e.createElement("div");
  n.setAttribute("className", "t"), n.innerHTML = " <link/><table></table><a href='/a'>a</a><input type='checkbox'/>", c = n.getElementsByTagName("*"), d = n.getElementsByTagName("a")[0], d.style.cssText = "top:1px;float:left;opacity:.5";
  if (!c || !c.length || !d)return{};
  f = e.createElement("select"), g = f.appendChild(e.createElement("option")), h = n.getElementsByTagName("input")[0], b = {leadingWhitespace: n.firstChild.nodeType === 3, tbody: !n.getElementsByTagName("tbody").length, htmlSerialize: !!n.getElementsByTagName("link").length, style: /top/.test(d.getAttribute("style")), hrefNormalized: d.getAttribute("href") === "/a", opacity: /^0.5/.test(d.style.opacity), cssFloat: !!d.style.cssFloat, checkOn: h.value === "on", optSelected: g.selected, getSetAttribute: n.className !== "t", enctype: !!e.createElement("form").enctype, html5Clone: e.createElement("nav").cloneNode(!0).outerHTML !== "<:nav></:nav>", boxModel: e.compatMode === "CSS1Compat", submitBubbles: !0, changeBubbles: !0, focusinBubbles: !1, deleteExpando: !0, noCloneEvent: !0, inlineBlockNeedsLayout: !1, shrinkWrapBlocks: !1, reliableMarginRight: !0, boxSizingReliable: !0, pixelPosition: !1}, h.checked = !0, b.noCloneChecked = h.cloneNode(!0).checked, f.disabled = !0, b.optDisabled = !g.disabled;
  try {
  delete n.test
  } catch (o) {
  b.deleteExpando = !1
  }
  !n.addEventListener && n.attachEvent && n.fireEvent && (n.attachEvent("onclick", m = function () {
  b.noCloneEvent = !1
  }), n.cloneNode(!0).fireEvent("onclick"), n.detachEvent("onclick", m)), h = e.createElement("input"), h.value = "t", h.setAttribute("type", "radio"), b.radioValue = h.value === "t", h.setAttribute("checked", "checked"), h.setAttribute("name", "t"), n.appendChild(h), i = e.createDocumentFragment(), i.appendChild(n.lastChild), b.checkClone = i.cloneNode(!0).cloneNode(!0).lastChild.checked, b.appendChecked = h.checked, i.removeChild(h), i.appendChild(n);
  if (n.attachEvent)for (k in{submit: !0, change: !0, focusin: !0})j = "on" + k, l = j in n, l || (n.setAttribute(j, "return;"), l = typeof n[j] == "function"), b[k + "Bubbles"] = l;
  return p(function () {
  var c, d, f, g, h = "padding:0;margin:0;border:0;display:block;overflow:hidden;", i = e.getElementsByTagName("body")[0];
  if (!i)return;
  c = e.createElement("div"), c.style.cssText = "visibility:hidden;border:0;width:0;height:0;position:static;top:0;margin-top:1px", i.insertBefore(c, i.firstChild), d = e.createElement("div"), c.appendChild(d), d.innerHTML = "<table><tr><td></td><td>t</td></tr></table>", f = d.getElementsByTagName("td"), f[0].style.cssText = "padding:0;margin:0;border:0;display:none", l = f[0].offsetHeight === 0, f[0].style.display = "", f[1].style.display = "none", b.reliableHiddenOffsets = l && f[0].offsetHeight === 0, 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%;", b.boxSizing = d.offsetWidth === 4, b.doesNotIncludeMarginInBodyOffset = i.offsetTop !== 1, a.getComputedStyle && (b.pixelPosition = (a.getComputedStyle(d, null) || {}).top !== "1%", b.boxSizingReliable = (a.getComputedStyle(d, null) || {width: "4px"}).width === "4px", g = e.createElement("div"), g.style.cssText = d.style.cssText = h, g.style.marginRight = g.style.width = "0", d.style.width = "1px", d.appendChild(g), b.reliableMarginRight = !parseFloat((a.getComputedStyle(g, null) || {}).marginRight)), typeof d.style.zoom != "undefined" && (d.innerHTML = "", d.style.cssText = h + "width:1px;padding:1px;display:inline;zoom:1", b.inlineBlockNeedsLayout = d.offsetWidth === 3, d.style.display = "block", d.style.overflow = "visible", d.innerHTML = "<div></div>", d.firstChild.style.width = "5px", b.shrinkWrapBlocks = d.offsetWidth !== 3, c.style.zoom = 1), i.removeChild(c), c = d = f = g = null
  }), i.removeChild(n), c = d = f = g = h = i = n = null, b
  }();
  var H = /^(?:\{.*\}|\[.*\])$/, I = /([A-Z])/g;
  p.extend({cache: {}, deletedIds: [], uuid: 0, expando: "jQuery" + (p.fn.jquery + Math.random()).replace(/\D/g, ""), noData: {embed: !0, object: "clsid:D27CDB6E-AE6D-11cf-96B8-444553540000", applet: !0}, hasData: function (a) {
  return a = a.nodeType ? p.cache[a[p.expando]] : a[p.expando], !!a && !K(a)
  }, data: function (a, c, d, e) {
  if (!p.acceptData(a))return;
  var f, g, h = p.expando, i = typeof c == "string", j = a.nodeType, k = j ? p.cache : a, l = j ? a[h] : a[h] && h;
  if ((!l || !k[l] || !e && !k[l].data) && i && d === b)return;
  l || (j ? a[h] = l = p.deletedIds.pop() || ++p.uuid : l = h), k[l] || (k[l] = {}, j || (k[l].toJSON = p.noop));
  if (typeof c == "object" || typeof c == "function")e ? k[l] = p.extend(k[l], c) : k[l].data = p.extend(k[l].data, c);
  return f = k[l], e || (f.data || (f.data = {}), f = f.data), d !== b && (f[p.camelCase(c)] = d), i ? (g = f[c], g == null && (g = f[p.camelCase(c)])) : g = f, g
  }, removeData: function (a, b, c) {
  if (!p.acceptData(a))return;
  var d, e, f, g = a.nodeType, h = g ? p.cache : a, i = g ? a[p.expando] : p.expando;
  if (!h[i])return;
  if (b) {
  d = c ? h[i] : h[i].data;
  if (d) {
  p.isArray(b) || (b in d ? b = [b] : (b = p.camelCase(b), b in d ? b = [b] : b = b.split(" ")));
  for (e = 0, f = b.length; e < f; e++)delete d[b[e]];
  if (!(c ? K : p.isEmptyObject)(d))return
  }
  }
  if (!c) {
  delete h[i].data;
  if (!K(h[i]))return
  }
  g ? p.cleanData([a], !0) : p.support.deleteExpando || h != h.window ? delete h[i] : h[i] = null
  }, _data: function (a, b, c) {
  return p.data(a, b, c, !0)
  }, acceptData: function (a) {
  var b = a.nodeName && p.noData[a.nodeName.toLowerCase()];
  return!b || b !== !0 && a.getAttribute("classid") === b
  }}), p.fn.extend({data: function (a, c) {
  var d, e, f, g, h, i = this[0], j = 0, k = null;
  if (a === b) {
  if (this.length) {
  k = p.data(i);
  if (i.nodeType === 1 && !p._data(i, "parsedAttrs")) {
  f = i.attributes;
  for (h = f.length; j < h; j++)g = f[j].name, g.indexOf("data-") === 0 && (g = p.camelCase(g.substring(5)), J(i, g, k[g]));
  p._data(i, "parsedAttrs", !0)
  }
  }
  return k
  }
  return typeof a == "object" ? this.each(function () {
  p.data(this, a)
  }) : (d = a.split(".", 2), d[1] = d[1] ? "." + d[1] : "", e = d[1] + "!", p.access(this, function (c) {
  if (c === b)return k = this.triggerHandler("getData" + e, [d[0]]), k === b && i && (k = p.data(i, a), k = J(i, a, k)), k === b && d[1] ? this.data(d[0]) : k;
  d[1] = c, this.each(function () {
  var b = p(this);
  b.triggerHandler("setData" + e, d), p.data(this, a, c), b.triggerHandler("changeData" + e, d)
  })
  }, null, c, arguments.length > 1, null, !1))
  }, removeData: function (a) {
  return this.each(function () {
  p.removeData(this, a)
  })
  }}), p.extend({queue: function (a, b, c) {
  var d;
  if (a)return b = (b || "fx") + "queue", d = p._data(a, b), c && (!d || p.isArray(c) ? d = p._data(a, b, p.makeArray(c)) : d.push(c)), d || []
  }, dequeue: function (a, b) {
  b = b || "fx";
  var c = p.queue(a, b), d = c.shift(), e = p._queueHooks(a, b), f = function () {
  p.dequeue(a, b)
  };
  d === "inprogress" && (d = c.shift()), d && (b === "fx" && c.unshift("inprogress"), delete e.stop, d.call(a, f, e)), !c.length && e && e.empty.fire()
  }, _queueHooks: function (a, b) {
  var c = b + "queueHooks";
  return p._data(a, c) || p._data(a, c, {empty: p.Callbacks("once memory").add(function () {
  p.removeData(a, b + "queue", !0), p.removeData(a, c, !0)
  })})
  }}), p.fn.extend({queue: function (a, c) {
  var d = 2;
  return typeof a != "string" && (c = a, a = "fx", d--), arguments.length < d ? p.queue(this[0], a) : c === b ? this : this.each(function () {
  var b = p.queue(this, a, c);
  p._queueHooks(this, a), a === "fx" && b[0] !== "inprogress" && p.dequeue(this, a)
  })
  }, dequeue: function (a) {
  return this.each(function () {
  p.dequeue(this, a)
  })
  }, delay: function (a, b) {
  return a = p.fx ? p.fx.speeds[a] || a : a, b = b || "fx", this.queue(b, function (b, c) {
  var d = setTimeout(b, a);
  c.stop = function () {
  clearTimeout(d)
  }
  })
  }, clearQueue: function (a) {
  return this.queue(a || "fx", [])
  }, promise: function (a, c) {
  var d, e = 1, f = p.Deferred(), g = this, h = this.length, i = function () {
  --e || f.resolveWith(g, [g])
  };
  typeof a != "string" && (c = a, a = b), a = a || "fx";
  while (h--)(d = p._data(g[h], a + "queueHooks")) && d.empty && (e++, d.empty.add(i));
  return i(), f.promise(c)
  }});
  var L, M, N, O = /[\t\r\n]/g, P = /\r/g, Q = /^(?:button|input)$/i, R = /^(?:button|input|object|select|textarea)$/i, S = /^a(?:rea|)$/i, T = /^(?:autofocus|autoplay|async|checked|controls|defer|disabled|hidden|loop|multiple|open|readonly|required|scoped|selected)$/i, U = p.support.getSetAttribute;
  p.fn.extend({attr: function (a, b) {
  return p.access(this, p.attr, a, b, arguments.length > 1)
  }, removeAttr: function (a) {
  return this.each(function () {
  p.removeAttr(this, a)
  })
  }, prop: function (a, b) {
  return p.access(this, p.prop, a, b, arguments.length > 1)
  }, removeProp: function (a) {
  return a = p.propFix[a] || a, this.each(function () {
  try {
  this[a] = b, delete this[a]
  } catch (c) {
  }
  })
  }, addClass: function (a) {
  var b, c, d, e, f, g, h;
  if (p.isFunction(a))return this.each(function (b) {
  p(this).addClass(a.call(this, b, this.className))
  });
  if (a && typeof a == "string") {
  b = a.split(s);
  for (c = 0, d = this.length; c < d; c++) {
  e = this[c];
  if (e.nodeType === 1)if (!e.className && b.length === 1)e.className = a; else {
  f = " " + e.className + " ";
  for (g = 0, h = b.length; g < h; g++)~f.indexOf(" " + b[g] + " ") || (f += b[g] + " ");
  e.className = p.trim(f)
  }
  }
  }
  return this
  }, removeClass: function (a) {
  var c, d, e, f, g, h, i;
  if (p.isFunction(a))return this.each(function (b) {
  p(this).removeClass(a.call(this, b, this.className))
  });
  if (a && typeof a == "string" || a === b) {
  c = (a || "").split(s);
  for (h = 0, i = this.length; h < i; h++) {
  e = this[h];
  if (e.nodeType === 1 && e.className) {
  d = (" " + e.className + " ").replace(O, " ");
  for (f = 0, g = c.length; f < g; f++)while (d.indexOf(" " + c[f] + " ") > -1)d = d.replace(" " + c[f] + " ", " ");
  e.className = a ? p.trim(d) : ""
  }
  }
  }
  return this
  }, toggleClass: function (a, b) {
  var c = typeof a, d = typeof b == "boolean";
  return p.isFunction(a) ? this.each(function (c) {
  p(this).toggleClass(a.call(this, c, this.className, b), b)
  }) : this.each(function () {
  if (c === "string") {
  var e, f = 0, g = p(this), h = b, i = a.split(s);
  while (e = i[f++])h = d ? h : !g.hasClass(e), g[h ? "addClass" : "removeClass"](e)
  } else if (c === "undefined" || c === "boolean")this.className && p._data(this, "__className__", this.className), this.className = this.className || a === !1 ? "" : p._data(this, "__className__") || ""
  })
  }, hasClass: function (a) {
  var b = " " + a + " ", c = 0, d = this.length;
  for (; c < d; c++)if (this[c].nodeType === 1 && (" " + this[c].className + " ").replace(O, " ").indexOf(b) > -1)return!0;
  return!1
  }, val: function (a) {
  var c, d, e, f = this[0];
  if (!arguments.length) {
  if (f)return c = p.valHooks[f.type] || p.valHooks[f.nodeName.toLowerCase()], c && "get"in c && (d = c.get(f, "value")) !== b ? d : (d = f.value, typeof d == "string" ? d.replace(P, "") : d == null ? "" : d);
  return
  }
  return e = p.isFunction(a), this.each(function (d) {
  var f, g = p(this);
  if (this.nodeType !== 1)return;
  e ? f = a.call(this, d, g.val()) : f = a, f == null ? f = "" : typeof f == "number" ? f += "" : p.isArray(f) && (f = p.map(f, function (a) {
  return a == null ? "" : a + ""
  })), c = p.valHooks[this.type] || p.valHooks[this.nodeName.toLowerCase()];
  if (!c || !("set"in c) || c.set(this, f, "value") === b)this.value = f
  })
  }}), p.extend({valHooks: {option: {get: function (a) {
  var b = a.attributes.value;
  return!b || b.specified ? a.value : a.text
  }}, select: {get: function (a) {
  var b, c, d, e, f = a.selectedIndex, g = [], h = a.options, i = a.type === "select-one";
  if (f < 0)return null;
  c = i ? f : 0, d = i ? f + 1 : h.length;
  for (; c < d; c++) {
  e = h[c];
  if (e.selected && (p.support.optDisabled ? !e.disabled : e.getAttribute("disabled") === null) && (!e.parentNode.disabled || !p.nodeName(e.parentNode, "optgroup"))) {
  b = p(e).val();
  if (i)return b;
  g.push(b)
  }
  }
  return i && !g.length && h.length ? p(h[f]).val() : g
  }, set: function (a, b) {
  var c = p.makeArray(b);
  return p(a).find("option").each(function () {
  this.selected = p.inArray(p(this).val(), c) >= 0
  }), c.length || (a.selectedIndex = -1), c
  }}}, attrFn: {}, attr: function (a, c, d, e) {
  var f, g, h, i = a.nodeType;
  if (!a || i === 3 || i === 8 || i === 2)return;
  if (e && p.isFunction(p.fn[c]))return p(a)[c](d);
  if (typeof a.getAttribute == "undefined")return p.prop(a, c, d);
  h = i !== 1 || !p.isXMLDoc(a), h && (c = c.toLowerCase(), g = p.attrHooks[c] || (T.test(c) ? M : L));
  if (d !== b) {
  if (d === null) {
  p.removeAttr(a, c);
  return
  }
  return g && "set"in g && h && (f = g.set(a, d, c)) !== b ? f : (a.setAttribute(c, "" + d), d)
  }
  return g && "get"in g && h && (f = g.get(a, c)) !== null ? f : (f = a.getAttribute(c), f === null ? b : f)
  }, removeAttr: function (a, b) {
  var c, d, e, f, g = 0;
  if (b && a.nodeType === 1) {
  d = b.split(s);
  for (; g < d.length; g++)e = d[g], e && (c = p.propFix[e] || e, f = T.test(e), f || p.attr(a, e, ""), a.removeAttribute(U ? e : c), f && c in a && (a[c] = !1))
  }
  }, attrHooks: {type: {set: function (a, b) {
  if (Q.test(a.nodeName) && a.parentNode)p.error("type property can't be changed"); else if (!p.support.radioValue && b === "radio" && p.nodeName(a, "input")) {
  var c = a.value;
  return a.setAttribute("type", b), c && (a.value = c), b
  }
  }}, value: {get: function (a, b) {
  return L && p.nodeName(a, "button") ? L.get(a, b) : b in a ? a.value : null
  }, set: function (a, b, c) {
  if (L && p.nodeName(a, "button"))return L.set(a, b, c);
  a.value = b
  }}}, 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 (a, c, d) {
  var e, f, g, h = a.nodeType;
  if (!a || h === 3 || h === 8 || h === 2)return;
  return g = h !== 1 || !p.isXMLDoc(a), g && (c = p.propFix[c] || c, f = p.propHooks[c]), d !== b ? f && "set"in f && (e = f.set(a, d, c)) !== b ? e : a[c] = d : f && "get"in f && (e = f.get(a, c)) !== null ? e : a[c]
  }, propHooks: {tabIndex: {get: function (a) {
  var c = a.getAttributeNode("tabindex");
  return c && c.specified ? parseInt(c.value, 10) : R.test(a.nodeName) || S.test(a.nodeName) && a.href ? 0 : b
  }}}}), M = {get: function (a, c) {
  var d, e = p.prop(a, c);
  return e === !0 || typeof e != "boolean" && (d = a.getAttributeNode(c)) && d.nodeValue !== !1 ? c.toLowerCase() : b
  }, set: function (a, b, c) {
  var d;
  return b === !1 ? p.removeAttr(a, c) : (d = p.propFix[c] || c, d in a && (a[d] = !0), a.setAttribute(c, c.toLowerCase())), c
  }}, U || (N = {name: !0, id: !0, coords: !0}, L = p.valHooks.button = {get: function (a, c) {
  var d;
  return d = a.getAttributeNode(c), d && (N[c] ? d.value !== "" : d.specified) ? d.value : b
  }, set: function (a, b, c) {
  var d = a.getAttributeNode(c);
  return d || (d = e.createAttribute(c), a.setAttributeNode(d)), d.value = b + ""
  }}, p.each(["width", "height"], function (a, b) {
  p.attrHooks[b] = p.extend(p.attrHooks[b], {set: function (a, c) {
  if (c === "")return a.setAttribute(b, "auto"), c
  }})
  }), p.attrHooks.contenteditable = {get: L.get, set: function (a, b, c) {
  b === "" && (b = "false"), L.set(a, b, c)
  }}), p.support.hrefNormalized || p.each(["href", "src", "width", "height"], function (a, c) {
  p.attrHooks[c] = p.extend(p.attrHooks[c], {get: function (a) {
  var d = a.getAttribute(c, 2);
  return d === null ? b : d
  }})
  }), p.support.style || (p.attrHooks.style = {get: function (a) {
  return a.style.cssText.toLowerCase() || b
  }, set: function (a, b) {
  return a.style.cssText = "" + b
  }}), p.support.optSelected || (p.propHooks.selected = p.extend(p.propHooks.selected, {get: function (a) {
  var b = a.parentNode;
  return b && (b.selectedIndex, b.parentNode && b.parentNode.selectedIndex), null
  }})), p.support.enctype || (p.propFix.enctype = "encoding"), p.support.checkOn || p.each(["radio", "checkbox"], function () {
  p.valHooks[this] = {get: function (a) {
  return a.getAttribute("value") === null ? "on" : a.value
  }}
  }), p.each(["radio", "checkbox"], function () {
  p.valHooks[this] = p.extend(p.valHooks[this], {set: function (a, b) {
  if (p.isArray(b))return a.checked = p.inArray(p(a).val(), b) >= 0
  }})
  });
  var V = /^(?:textarea|input|select)$/i, W = /^([^\.]*|)(?:\.(.+)|)$/, X = /(?:^|\s)hover(\.\S+|)\b/, Y = /^key/, Z = /^(?:mouse|contextmenu)|click/, $ = /^(?:focusinfocus|focusoutblur)$/, _ = function (a) {
  return p.event.special.hover ? a : a.replace(X, "mouseenter$1 mouseleave$1")
  };
  p.event = {add: function (a, c, d, e, f) {
  var g, h, i, j, k, l, m, n, o, q, r;
  if (a.nodeType === 3 || a.nodeType === 8 || !c || !d || !(g = p._data(a)))return;
  d.handler && (o = d, d = o.handler, f = o.selector), d.guid || (d.guid = p.guid++), i = g.events, i || (g.events = i = {}), h = g.handle, h || (g.handle = h = function (a) {
  return typeof p != "undefined" && (!a || p.event.triggered !== a.type) ? p.event.dispatch.apply(h.elem, arguments) : b
  }, h.elem = a), c = p.trim(_(c)).split(" ");
  for (j = 0; j < c.length; j++) {
  k = W.exec(c[j]) || [], l = k[1], m = (k[2] || "").split(".").sort(), r = p.event.special[l] || {}, l = (f ? r.delegateType : r.bindType) || l, r = p.event.special[l] || {}, n = p.extend({type: l, origType: k[1], data: e, handler: d, guid: d.guid, selector: f, namespace: m.join(".")}, o), q = i[l];
  if (!q) {
  q = i[l] = [], q.delegateCount = 0;
  if (!r.setup || r.setup.call(a, e, m, h) === !1)a.addEventListener ? a.addEventListener(l, h, !1) : a.attachEvent && a.attachEvent("on" + l, h)
  }
  r.add && (r.add.call(a, n), n.handler.guid || (n.handler.guid = d.guid)), f ? q.splice(q.delegateCount++, 0, n) : q.push(n), p.event.global[l] = !0
  }
  a = null
  }, global: {}, remove: function (a, b, c, d, e) {
  var f, g, h, i, j, k, l, m, n, o, q, r = p.hasData(a) && p._data(a);
  if (!r || !(m = r.events))return;
  b = p.trim(_(b || "")).split(" ");
  for (f = 0; f < b.length; f++) {
  g = W.exec(b[f]) || [], h = i = g[1], j = g[2];
  if (!h) {
  for (h in m)p.event.remove(a, h + b[f], c, d, !0);
  continue
  }
  n = p.event.special[h] || {}, h = (d ? n.delegateType : n.bindType) || h, o = m[h] || [], k = o.length, j = j ? new RegExp("(^|\\.)" + j.split(".").sort().join("\\.(?:.*\\.|)") + "(\\.|$)") : null;
  for (l = 0; l < o.length; l++)q = o[l], (e || i === q.origType) && (!c || c.guid === q.guid) && (!j || j.test(q.namespace)) && (!d || d === q.selector || d === "**" && q.selector) && (o.splice(l--, 1), q.selector && o.delegateCount--, n.remove && n.remove.call(a, q));
  o.length === 0 && k !== o.length && ((!n.teardown || n.teardown.call(a, j, r.handle) === !1) && p.removeEvent(a, h, r.handle), delete m[h])
  }
  p.isEmptyObject(m) && (delete r.handle, p.removeData(a, "events", !0))
  }, customEvent: {getData: !0, setData: !0, changeData: !0}, trigger: function (c, d, f, g) {
  if (!f || f.nodeType !== 3 && f.nodeType !== 8) {
  var h, i, j, k, l, m, n, o, q, r, s = c.type || c, t = [];
  if ($.test(s + p.event.triggered))return;
  s.indexOf("!") >= 0 && (s = s.slice(0, -1), i = !0), s.indexOf(".") >= 0 && (t = s.split("."), s = t.shift(), t.sort());
  if ((!f || p.event.customEvent[s]) && !p.event.global[s])return;
  c = typeof c == "object" ? c[p.expando] ? c : new p.Event(s, c) : new p.Event(s), c.type = s, c.isTrigger = !0, c.exclusive = i, c.namespace = t.join("."), c.namespace_re = c.namespace ? new RegExp("(^|\\.)" + t.join("\\.(?:.*\\.|)") + "(\\.|$)") : null, m = s.indexOf(":") < 0 ? "on" + s : "";
  if (!f) {
  h = p.cache;
  for (j in h)h[j].events && h[j].events[s] && p.event.trigger(c, d, h[j].handle.elem, !0);
  return
  }
  c.result = b, c.target || (c.target = f), d = d != null ? p.makeArray(d) : [], d.unshift(c), n = p.event.special[s] || {};
  if (n.trigger && n.trigger.apply(f, d) === !1)return;
  q = [
  [f, n.bindType || s]
  ];
  if (!g && !n.noBubble && !p.isWindow(f)) {
  r = n.delegateType || s, k = $.test(r + s) ? f : f.parentNode;
  for (l = f; k; k = k.parentNode)q.push([k, r]), l = k;
  l === (f.ownerDocument || e) && q.push([l.defaultView || l.parentWindow || a, r])
  }
  for (j = 0; j < q.length && !c.isPropagationStopped(); j++)k = q[j][0], c.type = q[j][1], o = (p._data(k, "events") || {})[c.type] && p._data(k, "handle"), o && o.apply(k, d), o = m && k[m], o && p.acceptData(k) && o.apply(k, d) === !1 && c.preventDefault();
  return c.type = s, !g && !c.isDefaultPrevented() && (!n._default || n._default.apply(f.ownerDocument, d) === !1) && (s !== "click" || !p.nodeName(f, "a")) && p.acceptData(f) && m && f[s] && (s !== "focus" && s !== "blur" || c.target.offsetWidth !== 0) && !p.isWindow(f) && (l = f[m], l && (f[m] = null), p.event.triggered = s, f[s](), p.event.triggered = b, l && (f[m] = l)), c.result
  }
  return
  }, dispatch: function (c) {
  c = p.event.fix(c || a.event);
  var d, e, f, g, h, i, j, k, l, m, n, o = (p._data(this, "events") || {})[c.type] || [], q = o.delegateCount, r = [].slice.call(arguments), s = !c.exclusive && !c.namespace, t = p.event.special[c.type] || {}, u = [];
  r[0] = c, c.delegateTarget = this;
  if (t.preDispatch && t.preDispatch.call(this, c) === !1)return;
  if (q && (!c.button || c.type !== "click")) {
  g = p(this), g.context = this;
  for (f = c.target; f != this; f = f.parentNode || this)if (f.disabled !== !0 || c.type !== "click") {
  i = {}, k = [], g[0] = f;
  for (d = 0; d < q; d++)l = o[d], m = l.selector, i[m] === b && (i[m] = g.is(m)), i[m] && k.push(l);
  k.length && u.push({elem: f, matches: k})
  }
  }
  o.length > q && u.push({elem: this, matches: o.slice(q)});
  for (d = 0; d < u.length && !c.isPropagationStopped(); d++) {
  j = u[d], c.currentTarget = j.elem;
  for (e = 0; e < j.matches.length && !c.isImmediatePropagationStopped(); e++) {
  l = j.matches[e];
  if (s || !c.namespace && !l.namespace || c.namespace_re && c.namespace_re.test(l.namespace))c.data = l.data, c.handleObj = l, h = ((p.event.special[l.origType] || {}).handle || l.handler).apply(j.elem, r), h !== b && (c.result = h, h === !1 && (c.preventDefault(), c.stopPropagation()))
  }
  }
  return t.postDispatch && t.postDispatch.call(this, c), c.result
  }, 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 (a, b) {
  return a.which == null && (a.which = b.charCode != null ? b.charCode : b.keyCode), a
  }}, mouseHooks: {props: "button buttons clientX clientY fromElement offsetX offsetY pageX pageY screenX screenY toElement".split(" "), filter: function (a, c) {
  var d, f, g, h = c.button, i = c.fromElement;
  return a.pageX == null && c.clientX != null && (d = a.target.ownerDocument || e, f = d.documentElement, g = d.body, a.pageX = c.clientX + (f && f.scrollLeft || g && g.scrollLeft || 0) - (f && f.clientLeft || g && g.clientLeft || 0), a.pageY = c.clientY + (f && f.scrollTop || g && g.scrollTop || 0) - (f && f.clientTop || g && g.clientTop || 0)), !a.relatedTarget && i && (a.relatedTarget = i === a.target ? c.toElement : i), !a.which && h !== b && (a.which = h & 1 ? 1 : h & 2 ? 3 : h & 4 ? 2 : 0), a
  }}, fix: function (a) {
  if (a[p.expando])return a;
  var b, c, d = a, f = p.event.fixHooks[a.type] || {}, g = f.props ? this.props.concat(f.props) : this.props;
  a = p.Event(d);
  for (b = g.length; b;)c = g[--b], a[c] = d[c];
  return a.target || (a.target = d.srcElement || e), a.target.nodeType === 3 && (a.target = a.target.parentNode), a.metaKey = !!a.metaKey, f.filter ? f.filter(a, d) : a
  }, special: {ready: {setup: p.bindReady}, load: {noBubble: !0}, focus: {delegateType: "focusin"}, blur: {delegateType: "focusout"}, beforeunload: {setup: function (a, b, c) {
  p.isWindow(this) && (this.onbeforeunload = c)
  }, teardown: function (a, b) {
  this.onbeforeunload === b && (this.onbeforeunload = null)
  }}}, simulate: function (a, b, c, d) {
  var e = p.extend(new p.Event, c, {type: a, isSimulated: !0, originalEvent: {}});
  d ? p.event.trigger(e, null, b) : p.event.dispatch.call(b, e), e.isDefaultPrevented() && c.preventDefault()
  }}, p.event.handle = p.event.dispatch, p.removeEvent = e.removeEventListener ? function (a, b, c) {
  a.removeEventListener && a.removeEventListener(b, c, !1)
  } : function (a, b, c) {
  var d = "on" + b;
  a.detachEvent && (typeof a[d] == "undefined" && (a[d] = null), a.detachEvent(d, c))
  }, p.Event = function (a, b) {
  if (this instanceof p.Event)a && a.type ? (this.originalEvent = a, this.type = a.type, this.isDefaultPrevented = a.defaultPrevented || a.returnValue === !1 || a.getPreventDefault && a.getPreventDefault() ? bb : ba) : this.type = a, b && p.extend(this, b), this.timeStamp = a && a.timeStamp || p.now(), this[p.expando] = !0; else return new p.Event(a, b)
  }, p.Event.prototype = {preventDefault: function () {
  this.isDefaultPrevented = bb;
  var a = this.originalEvent;
  if (!a)return;
  a.preventDefault ? a.preventDefault() : a.returnValue = !1
  }, stopPropagation: function () {
  this.isPropagationStopped = bb;
  var a = this.originalEvent;
  if (!a)return;
  a.stopPropagation && a.stopPropagation(), a.cancelBubble = !0
  }, stopImmediatePropagation: function () {
  this.isImmediatePropagationStopped = bb, this.stopPropagation()
  }, isDefaultPrevented: ba, isPropagationStopped: ba, isImmediatePropagationStopped: ba}, p.each({mouseenter: "mouseover", mouseleave: "mouseout"}, function (a, b) {
  p.event.special[a] = {delegateType: b, bindType: b, handle: function (a) {
  var c, d = this, e = a.relatedTarget, f = a.handleObj, g = f.selector;
  if (!e || e !== d && !p.contains(d, e))a.type = f.origType, c = f.handler.apply(this, arguments), a.type = b;
  return c
  }}
  }), p.support.submitBubbles || (p.event.special.submit = {setup: function () {
  if (p.nodeName(this, "form"))return!1;
  p.event.add(this, "click._submit keypress._submit", function (a) {
  var c = a.target, d = p.nodeName(c, "input") || p.nodeName(c, "button") ? c.form : b;
  d && !p._data(d, "_submit_attached") && (p.event.add(d, "submit._submit", function (a) {
  a._submit_bubble = !0
  }), p._data(d, "_submit_attached", !0))
  })
  }, postDispatch: function (a) {
  a._submit_bubble && (delete a._submit_bubble, this.parentNode && !a.isTrigger && p.event.simulate("submit", this.parentNode, a, !0))
  }, teardown: function () {
  if (p.nodeName(this, "form"))return!1;
  p.event.remove(this, "._submit")
  }}), p.support.changeBubbles || (p.event.special.change = {setup: function () {
  if (V.test(this.nodeName)) {
  if (this.type === "checkbox" || this.type === "radio")p.event.add(this, "propertychange._change", function (a) {
  a.originalEvent.propertyName === "checked" && (this._just_changed = !0)
  }), p.event.add(this, "click._change", function (a) {
  this._just_changed && !a.isTrigger && (this._just_changed = !1), p.event.simulate("change", this, a, !0)
  });
  return!1
  }
  p.event.add(this, "beforeactivate._change", function (a) {
  var b = a.target;
  V.test(b.nodeName) && !p._data(b, "_change_attached") && (p.event.add(b, "change._change", function (a) {
  this.parentNode && !a.isSimulated && !a.isTrigger && p.event.simulate("change", this.parentNode, a, !0)
  }), p._data(b, "_change_attached", !0))
  })
  }, handle: function (a) {
  var b = a.target;
  if (this !== b || a.isSimulated || a.isTrigger || b.type !== "radio" && b.type !== "checkbox")return a.handleObj.handler.apply(this, arguments)
  }, teardown: function () {
  return p.event.remove(this, "._change"), V.test(this.nodeName)
  }}), p.support.focusinBubbles || p.each({focus: "focusin", blur: "focusout"}, function (a, b) {
  var c = 0, d = function (a) {
  p.event.simulate(b, a.target, p.event.fix(a), !0)
  };
  p.event.special[b] = {setup: function () {
  c++ === 0 && e.addEventListener(a, d, !0)
  }, teardown: function () {
  --c === 0 && e.removeEventListener(a, d, !0)
  }}
  }), p.fn.extend({on: function (a, c, d, e, f) {
  var g, h;
  if (typeof a == "object") {
  typeof c != "string" && (d = d || c, c = b);
  for (h in a)this.on(h, c, d, a[h], f);
  return this
  }
  d == null && e == null ? (e = c, d = c = b) : e == null && (typeof c == "string" ? (e = d, d = b) : (e = d, d = c, c = b));
  if (e === !1)e = ba; else if (!e)return this;
  return f === 1 && (g = e, e = function (a) {
  return p().off(a), g.apply(this, arguments)
  }, e.guid = g.guid || (g.guid = p.guid++)), this.each(function () {
  p.event.add(this, a, e, d, c)
  })
  }, one: function (a, b, c, d) {
  return this.on(a, b, c, d, 1)
  }, off: function (a, c, d) {
  var e, f;
  if (a && a.preventDefault && a.handleObj)return e = a.handleObj, p(a.delegateTarget).off(e.namespace ? e.origType + "." + e.namespace : e.origType, e.selector, e.handler), this;
  if (typeof a == "object") {
  for (f in a)this.off(f, c, a[f]);
  return this
  }
  if (c === !1 || typeof c == "function")d = c, c = b;
  return d === !1 && (d = ba), this.each(function () {
  p.event.remove(this, a, d, c)
  })
  }, bind: function (a, b, c) {
  return this.on(a, null, b, c)
  }, unbind: function (a, b) {
  return this.off(a, null, b)
  }, live: function (a, b, c) {
  return p(this.context).on(a, this.selector, b, c), this
  }, die: function (a, b) {
  return p(this.context).off(a, this.selector || "**", b), this
  }, delegate: function (a, b, c, d) {
  return this.on(b, a, c, d)
  }, undelegate: function (a, b, c) {
  return arguments.length == 1 ? this.off(a, "**") : this.off(b, a || "**", c)
  }, trigger: function (a, b) {
  return this.each(function () {
  p.event.trigger(a, b, this)
  })
  }, triggerHandler: function (a, b) {
  if (this[0])return p.event.trigger(a, b, this[0], !0)
  }, toggle: function (a) {
  var b = arguments, c = a.guid || p.guid++, d = 0, e = function (c) {
  var e = (p._data(this, "lastToggle" + a.guid) || 0) % d;
  return p._data(this, "lastToggle" + a.guid, e + 1), c.preventDefault(), b[e].apply(this, arguments) || !1
  };
  e.guid = c;
  while (d < b.length)b[d++].guid = c;
  return this.click(e)
  }, hover: function (a, b) {
  return this.mouseenter(a).mouseleave(b || a)
  }}), p.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 (a, b) {
  p.fn[b] = function (a, c) {
  return c == null && (c = a, a = null), arguments.length > 0 ? this.on(b, null, a, c) : this.trigger(b)
  }, Y.test(b) && (p.event.fixHooks[b] = p.event.keyHooks), Z.test(b) && (p.event.fixHooks[b] = p.event.mouseHooks)
  }), function (a, b) {
  function bd(a, b, c, d) {
  var e = 0, f = b.length;
  for (; e < f; e++)Z(a, b[e], c, d)
  }
   
  function be(a, b, c, d, e, f) {
  var g, h = $.setFilters[b.toLowerCase()];
  return h || Z.error(b), (a || !(g = e)) && bd(a || "*", d, g = [], e), g.length > 0 ? h(g, c, f) : []
  }
   
  function bf(a, c, d, e, f) {
  var g, h, i, j, k, l, m, n, p = 0, q = f.length, s = L.POS, t = new RegExp("^" + s.source + "(?!" + r + ")", "i"), u = function () {
  var a = 1, c = arguments.length - 2;
  for (; a < c; a++)arguments[a] === b && (g[a] = b)
  };
  for (; p < q; p++) {
  s.exec(""), a = f[p], j = [], i = 0, k = e;
  while (g = s.exec(a)) {
  n = s.lastIndex = g.index + g[0].length;
  if (n > i) {
  m = a.slice(i, g.index), i = n, l = [c], B.test(m) && (k && (l = k), k = e);
  if (h = H.test(m))m = m.slice(0, -5).replace(B, "$&*");
  g.length > 1 && g[0].replace(t, u), k = be(m, g[1], g[2], l, k, h)
  }
  }
  k ? (j = j.concat(k), (m = a.slice(i)) && m !== ")" ? B.test(m) ? bd(m, j, d, e) : Z(m, c, d, e ? e.concat(k) : k) : o.apply(d, j)) : Z(a, c, d, e)
  }
  return q === 1 ? d : Z.uniqueSort(d)
  }
   
  function bg(a, b, c) {
  var d, e, f, g = [], i = 0, j = D.exec(a), k = !j.pop() && !j.pop(), l = k && a.match(C) || [""], m = $.preFilter, n = $.filter, o = !c && b !== h;
  for (; (e = l[i]) != null && k; i++) {
  g.push(d = []), o && (e = " " + e);
  while (e) {
  k = !1;
  if (j = B.exec(e))e = e.slice(j[0].length), k = d.push({part: j.pop().replace(A, " "), captures: j});
  for (f in n)(j = L[f].exec(e)) && (!m[f] || (j = m[f](j, b, c))) && (e = e.slice(j.shift().length), k = d.push({part: f, captures: j}));
  if (!k)break
  }
  }
  return k || Z.error(a), g
  }
   
  function bh(a, b, e) {
  var f = b.dir, g = m++;
  return a || (a = function (a) {
  return a === e
  }), b.first ? function (b, c) {
  while (b = b[f])if (b.nodeType === 1)return a(b, c) && b
  } : function (b, e) {
  var h, i = g + "." + d, j = i + "." + c;
  while (b = b[f])if (b.nodeType === 1) {
  if ((h = b[q]) === j)return b.sizset;
  if (typeof h == "string" && h.indexOf(i) === 0) {
  if (b.sizset)return b
  } else {
  b[q] = j;
  if (a(b, e))return b.sizset = !0, b;
  b.sizset = !1
  }
  }
  }
  }
   
  function bi(a, b) {
  return a ? function (c, d) {
  var e = b(c, d);
  return e && a(e === !0 ? c : e, d)
  } : b
  }
   
  function bj(a, b, c) {
  var d, e, f = 0;
  for (; d = a[f]; f++)$.relative[d.part] ? e = bh(e, $.relative[d.part], b) : (d.captures.push(b, c), e = bi(e, $.filter[d.part].apply(null, d.captures)));
  return e
  }
   
  function bk(a) {
  return function (b, c) {
  var d, e = 0;
  for (; d = a[e]; e++)if (d(b, c))return!0;
  return!1
  }
  }
   
  var c, d, e, f, g, h = a.document, i = h.documentElement, j = "undefined", k = !1, l = !0, m = 0, n = [].slice, o = [].push, q = ("sizcache" + Math.random()).replace(".", ""), r = "[\\x20\\t\\r\\n\\f]", s = "(?:\\\\.|[-\\w]|[^\\x00-\\xa0])+", t = s.replace("w", "w#"), u = "([*^$|!~]?=)", v = "\\[" + r + "*(" + s + ")" + r + "*(?:" + u + r + "*(?:(['\"])((?:\\\\.|[^\\\\])*?)\\3|(" + t + ")|)|)" + r + "*\\]", w = ":(" + s + ")(?:\\((?:(['\"])((?:\\\\.|[^\\\\])*?)\\2|((?:[^,]|\\\\,|(?:,(?=[^\\[]*\\]))|(?:,(?=[^\\(]*\\))))*))\\)|)", x = ":(nth|eq|gt|lt|first|last|even|odd)(?:\\((\\d*)\\)|)(?=[^-]|$)", y = r + "*([\\x20\\t\\r\\n\\f>+~])" + r + "*", z = "(?=[^\\x20\\t\\r\\n\\f])(?:\\\\.|" + v + "|" + w.replace(2, 7) + "|[^\\\\(),])+", A = new RegExp("^" + r + "+|((?:^|[^\\\\])(?:\\\\.)*)" + r + "+$", "g"), B = new RegExp("^" + y), C = new RegExp(z + "?(?=" + r + "*,|$)", "g"), D = new RegExp("^(?:(?!,)(?:(?:^|,)" + r + "*" + z + ")*?|" + r + "*(.*?))(\\)|$)"), E = new RegExp(z.slice(19, -6) + "\\x20\\t\\r\\n\\f>+~])+|" + y, "g"), F = /^(?:#([\w\-]+)|(\w+)|\.([\w\-]+))$/, G = /[\x20\t\r\n\f]*[+~]/, H = /:not\($/, I = /h\d/i, J = /input|select|textarea|button/i, K = /\\(?!\\)/g, L = {ID: new RegExp("^#(" + s + ")"), CLASS: new RegExp("^\\.(" + s + ")"), NAME: new RegExp("^\\[name=['\"]?(" + s + ")['\"]?\\]"), TAG: new RegExp("^(" + s.replace("[-", "[-\\*") + ")"), ATTR: new RegExp("^" + v), PSEUDO: new RegExp("^" + w), CHILD: new RegExp("^:(only|nth|last|first)-child(?:\\(" + r + "*(even|odd|(([+-]|)(\\d*)n|)" + r + "*(?:([+-]|)" + r + "*(\\d+)|))" + r + "*\\)|)", "i"), POS: new RegExp(x, "ig"), needsContext: new RegExp("^" + r + "*[>+~]|" + x, "i")}, M = {}, N = [], O = {}, P = [], Q = function (a) {
  return a.sizzleFilter = !0, a
  }, R = function (a) {
  return function (b) {
  return b.nodeName.toLowerCase() === "input" && b.type === a
  }
  }, S = function (a) {
  return function (b) {
  var c = b.nodeName.toLowerCase();
  return(c === "input" || c === "button") && b.type === a
  }
  }, T = function (a) {
  var b = !1, c = h.createElement("div");
  try {
  b = a(c)
  } catch (d) {
  }
  return c = null, b
  }, U = T(function (a) {
  a.innerHTML = "<select></select>";
  var b = typeof a.lastChild.getAttribute("multiple");
  return b !== "boolean" && b !== "string"
  }), V = T(function (a) {
  a.id = q + 0, a.innerHTML = "<a name='" + q + "'></a><div name='" + q + "'></div>", i.insertBefore(a, i.firstChild);
  var b = h.getElementsByName && h.getElementsByName(q).length === 2 + h.getElementsByName(q + 0).length;
  return g = !h.getElementById(q), i.removeChild(a), b
  }), W = T(function (a) {
  return a.appendChild(h.createComment("")), a.getElementsByTagName("*").length === 0
  }), X = T(function (a) {
  return a.innerHTML = "<a href='#'></a>", a.firstChild && typeof a.firstChild.getAttribute !== j && a.firstChild.getAttribute("href") === "#"
  }), Y = T(function (a) {
  return a.innerHTML = "<div class='hidden e'></div><div class='hidden'></div>", !a.getElementsByClassName || a.getElementsByClassName("e").length === 0 ? !1 : (a.lastChild.className = "e", a.getElementsByClassName("e").length !== 1)
  }), Z = function (a, b, c, d) {
  c = c || [], b = b || h;
  var e, f, g, i, j = b.nodeType;
  if (j !== 1 && j !== 9)return[];
  if (!a || typeof a != "string")return c;
  g = ba(b);
  if (!g && !d)if (e = F.exec(a))if (i = e[1]) {
  if (j === 9) {
  f = b.getElementById(i);
  if (!f || !f.parentNode)return c;
  if (f.id === i)return c.push(f), c
  } else if (b.ownerDocument && (f = b.ownerDocument.getElementById(i)) && bb(b, f) && f.id === i)return c.push(f), c
  } else {
  if (e[2])return o.apply(c, n.call(b.getElementsByTagName(a), 0)), c;
  if ((i = e[3]) && Y && b.getElementsByClassName)return o.apply(c, n.call(b.getElementsByClassName(i), 0)), c
  }
  return bm(a, b, c, d, g)
  }, $ = Z.selectors = {cacheLength: 50, match: L, order: ["ID", "TAG"], attrHandle: {}, createPseudo: Q, find: {ID: g ? function (a, b, c) {
  if (typeof b.getElementById !== j && !c) {
  var d = b.getElementById(a);
  return d && d.parentNode ? [d] : []
  }
  } : function (a, c, d) {
  if (typeof c.getElementById !== j && !d) {
  var e = c.getElementById(a);
  return e ? e.id === a || typeof e.getAttributeNode !== j && e.getAttributeNode("id").value === a ? [e] : b : []
  }
  }, TAG: W ? function (a, b) {
  if (typeof b.getElementsByTagName !== j)return b.getElementsByTagName(a)
  } : function (a, b) {
  var c = b.getElementsByTagName(a);
  if (a === "*") {
  var d, e = [], f = 0;
  for (; d = c[f]; f++)d.nodeType === 1 && e.push(d);
  return e
  }
  return c
  }}, relative: {">": {dir: "parentNode", first: !0}, " ": {dir: "parentNode"}, "+": {dir: "previousSibling", first: !0}, "~": {dir: "previousSibling"}}, preFilter: {ATTR: function (a) {
  return a[1] = a[1].replace(K, ""), a[3] = (a[4] || a[5] || "").replace(K, ""), a[2] === "~=" && (a[3] = " " + a[3] + " "), a.slice(0, 4)
  }, CHILD: function (a) {
  return a[1] = a[1].toLowerCase(), a[1] === "nth" ? (a[2] || Z.error(a[0]), a[3] = +(a[3] ? a[4] + (a[5] || 1) : 2 * (a[2] === "even" || a[2] === "odd")), a[4] = +(a[6] + a[7] || a[2] === "odd")) : a[2] && Z.error(a[0]), a
  }, PSEUDO: function (a) {
  var b, c = a[4];
  return L.CHILD.test(a[0]) ? null : (c && (b = D.exec(c)) && b.pop() && (a[0] = a[0].slice(0, b[0].length - c.length - 1), c = b[0].slice(0, -1)), a.splice(2, 3, c || a[3]), a)
  }}, filter: {ID: g ? function (a) {
  return a = a.replace(K, ""), function (b) {
  return b.getAttribute("id") === a
  }
  } : function (a) {
  return a = a.replace(K, ""), function (b) {
  var c = typeof b.getAttributeNode !== j && b.getAttributeNode("id");
  return c && c.value === a
  }
  }, TAG: function (a) {
  return a === "*" ? function () {
  return!0
  } : (a = a.replace(K, "").toLowerCase(), function (b) {
  return b.nodeName && b.nodeName.toLowerCase() === a
  })
  }, CLASS: function (a) {
  var b = M[a];
  return b || (b = M[a] = new RegExp("(^|" + r + ")" + a + "(" + r + "|$)"), N.push(a), N.length > $.cacheLength && delete M[N.shift()]), function (a) {
  return b.test(a.className || typeof a.getAttribute !== j && a.getAttribute("class") || "")
  }
  }, ATTR: function (a, b, c) {
  return b ? function (d) {
  var e = Z.attr(d, a), f = e + "";
  if (e == null)return b === "!=";
  switch (b) {
  case"=":
  return f === c;
  case"!=":
  return f !== c;
  case"^=":
  return c && f.indexOf(c) === 0;
  case"*=":
  return c && f.indexOf(c) > -1;
  case"$=":
  return c && f.substr(f.length - c.length) === c;
  case"~=":
  return(" " + f + " ").indexOf(c) > -1;
  case"|=":
  return f === c || f.substr(0, c.length + 1) === c + "-"
  }
  } : function (b) {
  return Z.attr(b, a) != null
  }
  }, CHILD: function (a, b, c, d) {
  if (a === "nth") {
  var e = m++;
  return function (a) {
  var b, f, g = 0, h = a;
  if (c === 1 && d === 0)return!0;
  b = a.parentNode;
  if (b && (b[q] !== e || !a.sizset)) {
  for (h = b.firstChild; h; h = h.nextSibling)if (h.nodeType === 1) {
  h.sizset = ++g;
  if (h === a)break
  }
  b[q] = e
  }
  return f = a.sizset - d, c === 0 ? f === 0 : f % c === 0 && f / c >= 0
  }
  }
  return function (b) {
  var c = b;
  switch (a) {
  case"only":
  case"first":
  while (c = c.previousSibling)if (c.nodeType === 1)return!1;
  if (a === "first")return!0;
  c = b;
  case"last":
  while (c = c.nextSibling)if (c.nodeType === 1)return!1;
  return!0
  }
  }
  }, PSEUDO: function (a, b, c, d) {
  var e = $.pseudos[a] || $.pseudos[a.toLowerCase()];
  return e || Z.error("unsupported pseudo: " + a), e.sizzleFilter ? e(b, c, d) : e
  }}, pseudos: {not: Q(function (a, b, c) {
  var d = bl(a.replace(A, "$1"), b, c);
  return function (a) {
  return!d(a)
  }
  }), enabled: function (a) {
  return a.disabled === !1
  }, disabled: function (a) {
  return a.disabled === !0
  }, checked: function (a) {
  var b = a.nodeName.toLowerCase();
  return b === "input" && !!a.checked || b === "option" && !!a.selected
  }, selected: function (a) {
  return a.parentNode && a.parentNode.selectedIndex, a.selected === !0
  }, parent: function (a) {
  return!$.pseudos.empty(a)
  }, empty: function (a) {
  var b;
  a = a.firstChild;
  while (a) {
  if (a.nodeName > "@" || (b = a.nodeType) === 3 || b === 4)return!1;
  a = a.nextSibling
  }
  return!0
  }, contains: Q(function (a) {
  return function (b) {
  return(b.textContent || b.innerText || bc(b)).indexOf(a) > -1
  }
  }), has: Q(function (a) {
  return function (b) {
  return Z(a, b).length > 0
  }
  }), header: function (a) {
  return I.test(a.nodeName)
  }, text: function (a) {
  var b, c;
  return a.nodeName.toLowerCase() === "input" && (b = a.type) === "text" && ((c = a.getAttribute("type")) == null || c.toLowerCase() === b)
  }, radio: R("radio"), checkbox: R("checkbox"), file: R("file"), password: R("password"), image: R("image"), submit: S("submit"), reset: S("reset"), button: function (a) {
  var b = a.nodeName.toLowerCase();
  return b === "input" && a.type === "button" || b === "button"
  }, input: function (a) {
  return J.test(a.nodeName)
  }, focus: function (a) {
  var b = a.ownerDocument;
  return a === b.activeElement && (!b.hasFocus || b.hasFocus()) && (!!a.type || !!a.href)
  }, active: function (a) {
  return a === a.ownerDocument.activeElement
  }}, setFilters: {first: function (a, b, c) {
  return c ? a.slice(1) : [a[0]]
  }, last: function (a, b, c) {
  var d = a.pop();
  return c ? a : [d]
  }, even: function (a, b, c) {
  var d = [], e = c ? 1 : 0, f = a.length;
  for (; e < f; e = e + 2)d.push(a[e]);
  return d
  }, odd: function (a, b, c) {
  var d = [], e = c ? 0 : 1, f = a.length;
  for (; e < f; e = e + 2)d.push(a[e]);
  return d
  }, lt: function (a, b, c) {
  return c ? a.slice(+b) : a.slice(0, +b)
  }, gt: function (a, b, c) {
  return c ? a.slice(0, +b + 1) : a.slice(+b + 1)
  }, eq: function (a, b, c) {
  var d = a.splice(+b, 1);
  return c ? a : d
  }}};
  $.setFilters.nth = $.setFilters.eq, $.filters = $.pseudos, X || ($.attrHandle = {href: function (a) {
  return a.getAttribute("href", 2)
  }, type: function (a) {
  return a.getAttribute("type")
  }}), V && ($.order.push("NAME"), $.find.NAME = function (a, b) {
  if (typeof b.getElementsByName !== j)return b.getElementsByName(a)
  }), Y && ($.order.splice(1, 0, "CLASS"), $.find.CLASS = function (a, b, c) {
  if (typeof b.getElementsByClassName !== j && !c)return b.getElementsByClassName(a)
  });
  try {
  n.call(i.childNodes, 0)[0].nodeType
  } catch (_) {
  n = function (a) {
  var b, c = [];
  for (; b = this[a]; a++)c.push(b);
  return c
  }
  }
  var ba = Z.isXML = function (a) {
  var b = a && (a.ownerDocument || a).documentElement;
  return b ? b.nodeName !== "HTML" : !1
  }, bb = Z.contains = i.compareDocumentPosition ? function (a, b) {
  return!!(a.compareDocumentPosition(b) & 16)
  } : i.contains ? function (a, b) {
  var c = a.nodeType === 9 ? a.documentElement : a, d = b.parentNode;
  return a === d || !!(d && d.nodeType === 1 && c.contains && c.contains(d))
  } : function (a, b) {
  while (b = b.parentNode)if (b === a)return!0;
  return!1
  }, bc = Z.getText = function (a) {
  var b, c = "", d = 0, e = a.nodeType;
  if (e) {
  if (e === 1 || e === 9 || e === 11) {
  if (typeof a.textContent == "string")return a.textContent;
  for (a = a.firstChild; a; a = a.nextSibling)c += bc(a)
  } else if (e === 3 || e === 4)return a.nodeValue
  } else for (; b = a[d]; d++)c += bc(b);
  return c
  };
  Z.attr = function (a, b) {
  var c, d = ba(a);
  return d || (b = b.toLowerCase()), $.attrHandle[b] ? $.attrHandle[b](a) : U || d ? a.getAttribute(b) : (c = a.getAttributeNode(b), c ? typeof a[b] == "boolean" ? a[b] ? b : null : c.specified ? c.value : null : null)
  }, Z.error = function (a) {
  throw new Error("Syntax error, unrecognized expression: " + a)
  }, [0, 0].sort(function () {
  return l = 0
  }), i.compareDocumentPosition ? e = function (a, b) {
  return a === b ? (k = !0, 0) : (!a.compareDocumentPosition || !b.compareDocumentPosition ? a.compareDocumentPosition : a.compareDocumentPosition(b) & 4) ? -1 : 1
  } : (e = function (a, b) {
  if (a === b)return k = !0, 0;
  if (a.sourceIndex && b.sourceIndex)return a.sourceIndex - b.sourceIndex;
  var c, d, e = [], g = [], h = a.parentNode, i = b.parentNode, j = h;
  if (h === i)return f(a, b);
  if (!h)return-1;
  if (!i)return 1;
  while (j)e.unshift(j), j = j.parentNode;
  j = i;
  while (j)g.unshift(j), j = j.parentNode;
  c = e.length, d = g.length;
  for (var l = 0; l < c && l < d; l++)if (e[l] !== g[l])return f(e[l], g[l]);
  return l === c ? f(a, g[l], -1) : f(e[l], b, 1)
  }, f = function (a, b, c) {
  if (a === b)return c;
  var d = a.nextSibling;
  while (d) {
  if (d === b)return-1;
  d = d.nextSibling
  }
  return 1
  }), Z.uniqueSort = function (a) {
  var b, c = 1;
  if (e) {
  k = l, a.sort(e);
  if (k)for (; b = a[c]; c++)b === a[c - 1] && a.splice(c--, 1)
  }
  return a
  };
  var bl = Z.compile = function (a, b, c) {
  var d, e, f, g = O[a];
  if (g && g.context === b)return g;
  e = bg(a, b, c);
  for (f = 0; d = e[f]; f++)e[f] = bj(d, b, c);
  return g = O[a] = bk(e), g.context = b, g.runs = g.dirruns = 0, P.push(a), P.length > $.cacheLength && delete O[P.shift()], g
  };
  Z.matches = function (a, b) {
  return Z(a, null, null, b)
  }, Z.matchesSelector = function (a, b) {
  return Z(b, null, null, [a]).length > 0
  };
  var bm = function (a, b, e, f, g) {
  a = a.replace(A, "$1");
  var h, i, j, k, l, m, p, q, r, s = a.match(C), t = a.match(E), u = b.nodeType;
  if (L.POS.test(a))return bf(a, b, e, f, s);
  if (f)h = n.call(f, 0); else if (s && s.length === 1) {
  if (t.length > 1 && u === 9 && !g && (s = L.ID.exec(t[0]))) {
  b = $.find.ID(s[1], b, g)[0];
  if (!b)return e;
  a = a.slice(t.shift().length)
  }
  q = (s = G.exec(t[0])) && !s.index && b.parentNode || b, r = t.pop(), m = r.split(":not")[0];
  for (j = 0, k = $.order.length; j < k; j++) {
  p = $.order[j];
  if (s = L[p].exec(m)) {
  h = $.find[p]((s[1] || "").replace(K, ""), q, g);
  if (h == null)continue;
  m === r && (a = a.slice(0, a.length - r.length) + m.replace(L[p], ""), a || o.apply(e, n.call(h, 0)));
  break
  }
  }
  }
  if (a) {
  i = bl(a, b, g), d = i.dirruns++, h == null && (h = $.find.TAG("*", G.test(a) && b.parentNode || b));
  for (j = 0; l = h[j]; j++)c = i.runs++, i(l, b) && e.push(l)
  }
  return e
  };
  h.querySelectorAll && function () {
  var a, b = bm, c = /'|\\/g, d = /\=[\x20\t\r\n\f]*([^'"\]]*)[\x20\t\r\n\f]*\]/g, e = [], f = [":active"], g = i.matchesSelector || i.mozMatchesSelector || i.webkitMatchesSelector || i.oMatchesSelector || i.msMatchesSelector;
  T(function (a) {
  a.innerHTML = "<select><option selected></option></select>", a.querySelectorAll("[selected]").length || e.push("\\[" + r + "*(?:checked|disabled|ismap|multiple|readonly|selected|value)"), a.querySelectorAll(":checked").length || e.push(":checked")
  }), T(function (a) {
  a.innerHTML = "<p test=''></p>", a.querySelectorAll("[test^='']").length && e.push("[*^$]=" + r + "*(?:\"\"|'')"), a.innerHTML = "<input type='hidden'>", a.querySelectorAll(":enabled").length || e.push(":enabled", ":disabled")
  }), e = e.length && new RegExp(e.join("|")), bm = function (a, d, f, g, h) {
  if (!g && !h && (!e || !e.test(a)))if (d.nodeType === 9)try {
  return o.apply(f, n.call(d.querySelectorAll(a), 0)), f
  } catch (i) {
  } else if (d.nodeType === 1 && d.nodeName.toLowerCase() !== "object") {
  var j = d.getAttribute("id"), k = j || q, l = G.test(a) && d.parentNode || d;
  j ? k = k.replace(c, "\\$&") : d.setAttribute("id", k);
  try {
  return o.apply(f, n.call(l.querySelectorAll(a.replace(C, "[id='" + k + "'] $&")), 0)), f
  } catch (i) {
  } finally {
  j || d.removeAttribute("id")
  }
  }
  return b(a, d, f, g, h)
  }, g && (T(function (b) {
  a = g.call(b, "div");
  try {
  g.call(b, "[test!='']:sizzle"), f.push($.match.PSEUDO)
  } catch (c) {
  }
  }), f = new RegExp(f.join("|")), Z.matchesSelector = function (b, c) {
  c = c.replace(d, "='$1']");
  if (!ba(b) && !f.test(c) && (!e || !e.test(c)))try {
  var h = g.call(b, c);
  if (h || a || b.document && b.document.nodeType !== 11)return h
  } catch (i) {
  }
  return Z(c, null, null, [b]).length > 0
  })
  }(), Z.attr = p.attr, p.find = Z, p.expr = Z.selectors, p.expr[":"] = p.expr.pseudos, p.unique = Z.uniqueSort, p.text = Z.getText, p.isXMLDoc = Z.isXML, p.contains = Z.contains
  }(a);
  var bc = /Until$/, bd = /^(?:parents|prev(?:Until|All))/, be = /^.[^:#\[\.,]*$/, bf = p.expr.match.needsContext, bg = {children: !0, contents: !0, next: !0, prev: !0};
  p.fn.extend({find: function (a) {
  var b, c, d, e, f, g, h = this;
  if (typeof a != "string")return p(a).filter(function () {
  for (b = 0, c = h.length; b < c; b++)if (p.contains(h[b], this))return!0
  });
  g = this.pushStack("", "find", a);
  for (b = 0, c = this.length; b < c; b++) {
  d = g.length, p.find(a, this[b], g);
  if (b > 0)for (e = d; e < g.length; e++)for (f = 0; f < d; f++)if (g[f] === g[e]) {
  g.splice(e--, 1);
  break
  }
  }
  return g
  }, has: function (a) {
  var b, c = p(a, this), d = c.length;
  return this.filter(function () {
  for (b = 0; b < d; b++)if (p.contains(this, c[b]))return!0
  })
  }, not: function (a) {
  return this.pushStack(bj(this, a, !1), "not", a)
  }, filter: function (a) {
  return this.pushStack(bj(this, a, !0), "filter", a)
  }, is: function (a) {
  return!!a && (typeof a == "string" ? bf.test(a) ? p(a, this.context).index(this[0]) >= 0 : p.filter(a, this).length > 0 : this.filter(a).length > 0)
  }, closest: function (a, b) {
  var c, d = 0, e = this.length, f = [], g = bf.test(a) || typeof a != "string" ? p(a, b || this.context) : 0;
  for (; d < e; d++) {
  c = this[d];
  while (c && c.ownerDocument && c !== b && c.nodeType !== 11) {
  if (g ? g.index(c) > -1 : p.find.matchesSelector(c, a)) {
  f.push(c);
  break
  }
  c = c.parentNode
  }
  }
  return f = f.length > 1 ? p.unique(f) : f, this.pushStack(f, "closest", a)
  }, index: function (a) {
  return a ? typeof a == "string" ? p.inArray(this[0], p(a)) : p.inArray(a.jquery ? a[0] : a, this) : this[0] && this[0].parentNode ? this.prevAll().length : -1
  }, add: function (a, b) {
  var c = typeof a == "string" ? p(a, b) : p.makeArray(a && a.nodeType ? [a] : a), d = p.merge(this.get(), c);
  return this.pushStack(bh(c[0]) || bh(d[0]) ? d : p.unique(d))
  }, addBack: function (a) {
  return this.add(a == null ? this.prevObject : this.prevObject.filter(a))
  }}), p.fn.andSelf = p.fn.addBack, p.each({parent: function (a) {
  var b = a.parentNode;
  return b && b.nodeType !== 11 ? b : null
  }, parents: function (a) {
  return p.dir(a, "parentNode")
  }, parentsUntil: function (a, b, c) {
  return p.dir(a, "parentNode", c)
  }, next: function (a) {
  return bi(a, "nextSibling")
  }, prev: function (a) {
  return bi(a, "previousSibling")
  }, nextAll: function (a) {
  return p.dir(a, "nextSibling")
  }, prevAll: function (a) {
  return p.dir(a, "previousSibling")
  }, nextUntil: function (a, b, c) {
  return p.dir(a, "nextSibling", c)
  }, prevUntil: function (a, b, c) {
  return p.dir(a, "previousSibling", c)
  }, siblings: function (a) {
  return p.sibling((a.parentNode || {}).firstChild, a)
  }, children: function (a) {
  return p.sibling(a.firstChild)
  }, contents: function (a) {
  return p.nodeName(a, "iframe") ? a.contentDocument || a.contentWindow.document : p.merge([], a.childNodes)
  }}, function (a, b) {
  p.fn[a] = function (c, d) {
  var e = p.map(this, b, c);
  return bc.test(a) || (d = c), d && typeof d == "string" && (e = p.filter(d, e)), e = this.length > 1 && !bg[a] ? p.unique(e) : e, this.length > 1 && bd.test(a) && (e = e.reverse()), this.pushStack(e, a, k.call(arguments).join(","))
  }
  }), p.extend({filter: function (a, b, c) {
  return c && (a = ":not(" + a + ")"), b.length === 1 ? p.find.matchesSelector(b[0], a) ? [b[0]] : [] : p.find.matches(a, b)
  }, dir: function (a, c, d) {
  var e = [], f = a[c];
  while (f && f.nodeType !== 9 && (d === b || f.nodeType !== 1 || !p(f).is(d)))f.nodeType === 1 && e.push(f), f = f[c];
  return e
  }, sibling: function (a, b) {
  var c = [];
  for (; a; a = a.nextSibling)a.nodeType === 1 && a !== b && c.push(a);
  return c
  }});
  var bl = "abbr|article|aside|audio|bdi|canvas|data|datalist|details|figcaption|figure|footer|header|hgroup|mark|meter|nav|output|progress|section|summary|time|video", bm = / jQuery\d+="(?:null|\d+)"/g, bn = /^\s+/, bo = /<(?!area|br|col|embed|hr|img|input|link|meta|param)(([\w:]+)[^>]*)\/>/gi, bp = /<([\w:]+)/, bq = /<tbody/i, br = /<|&#?\w+;/, bs = /<(?:script|style|link)/i, bt = /<(?:script|object|embed|option|style)/i, bu = new RegExp("<(?:" + bl + ")[\\s/>]", "i"), bv = /^(?:checkbox|radio)$/, bw = /checked\s*(?:[^=]|=\s*.checked.)/i, bx = /\/(java|ecma)script/i, by = /^\s*<!(?:\[CDATA\[|\-\-)|[\]\-]{2}>\s*$/g, bz = {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, "", ""]}, bA = bk(e), bB = bA.appendChild(e.createElement("div"));
  bz.optgroup = bz.option, bz.tbody = bz.tfoot = bz.colgroup = bz.caption = bz.thead, bz.th = bz.td, p.support.htmlSerialize || (bz._default = [1, "X<div>", "</div>"]), p.fn.extend({text: function (a) {
  return p.access(this, function (a) {
  return a === b ? p.text(this) : this.empty().append((this[0] && this[0].ownerDocument || e).createTextNode(a))
  }, null, a, arguments.length)
  }, wrapAll: function (a) {
  if (p.isFunction(a))return this.each(function (b) {
  p(this).wrapAll(a.call(this, b))
  });
  if (this[0]) {
  var b = p(a, this[0].ownerDocument).eq(0).clone(!0);
  this[0].parentNode && b.insertBefore(this[0]), b.map(function () {
  var a = this;
  while (a.firstChild && a.firstChild.nodeType === 1)a = a.firstChild;
  return a
  }).append(this)
  }
  return this
  }, wrapInner: function (a) {
  return p.isFunction(a) ? this.each(function (b) {
  p(this).wrapInner(a.call(this, b))
  }) : this.each(function () {
  var b = p(this), c = b.contents();
  c.length ? c.wrapAll(a) : b.append(a)
  })
  }, wrap: function (a) {
  var b = p.isFunction(a);
  return this.each(function (c) {
  p(this).wrapAll(b ? a.call(this, c) : a)
  })
  }, unwrap: function () {
  return this.parent().each(function () {
  p.nodeName(this, "body") || p(this).replaceWith(this.childNodes)
  }).end()
  }, append: function () {
  return this.domManip(arguments, !0, function (a) {
  (this.nodeType === 1 || this.nodeType === 11) && this.appendChild(a)
  })
  }, prepend: function () {
  return this.domManip(arguments, !0, function (a) {
  (this.nodeType === 1 || this.nodeType === 11) && this.insertBefore(a, this.firstChild)
  })
  }, before: function () {
  if (!bh(this[0]))return this.domManip(arguments, !1, function (a) {
  this.parentNode.insertBefore(a, this)
  });
  if (arguments.length) {
  var a = p.clean(arguments);
  return this.pushStack(p.merge(a, this), "before", this.selector)
  }
  }, after: function () {
  if (!bh(this[0]))return this.domManip(arguments, !1, function (a) {
  this.parentNode.insertBefore(a, this.nextSibling)
  });
  if (arguments.length) {
  var a = p.clean(arguments);
  return this.pushStack(p.merge(this, a), "after", this.selector)
  }
  }, remove: function (a, b) {
  var c, d = 0;
  for (; (c = this[d]) != null; d++)if (!a || p.filter(a, [c]).length)!b && c.nodeType === 1 && (p.cleanData(c.getElementsByTagName("*")), p.cleanData([c])), c.parentNode && c.parentNode.removeChild(c);
  return this
  }, empty: function () {
  var a, b = 0;
  for (; (a = this[b]) != null; b++) {
  a.nodeType === 1 && p.cleanData(a.getElementsByTagName("*"));
  while (a.firstChild)a.removeChild(a.firstChild)
  }
  return this
  }, clone: function (a, b) {
  return a = a == null ? !1 : a, b = b == null ? a : b, this.map(function () {
  return p.clone(this, a, b)
  })
  }, html: function (a) {
  return p.access(this, function (a) {
  var c = this[0] || {}, d = 0, e = this.length;
  if (a === b)return c.nodeType === 1 ? c.innerHTML.replace(bm, "") : b;
  if (typeof a == "string" && !bs.test(a) && (p.support.htmlSerialize || !bu.test(a)) && (p.support.leadingWhitespace || !bn.test(a)) && !bz[(bp.exec(a) || ["", ""])[1].toLowerCase()]) {
  a = a.replace(bo, "<$1></$2>");
  try {
  for (; d < e; d++)c = this[d] || {}, c.nodeType === 1 && (p.cleanData(c.getElementsByTagName("*")), c.innerHTML = a);
  c = 0
  } catch (f) {
  }
  }
  c && this.empty().append(a)
  }, null, a, arguments.length)
  }, replaceWith: function (a) {
  return bh(this[0]) ? this.length ? this.pushStack(p(p.isFunction(a) ? a() : a), "replaceWith", a) : this : p.isFunction(a) ? this.each(function (b) {
  var c = p(this), d = c.html();
  c.replaceWith(a.call(this, b, d))
  }) : (typeof a != "string" && (a = p(a).detach()), this.each(function () {
  var b = this.nextSibling, c = this.parentNode;
  p(this).remove(), b ? p(b).before(a) : p(c).append(a)
  }))
  }, detach: function (a) {
  return this.remove(a, !0)
  }, domManip: function (a, c, d) {
  a = [].concat.apply([], a);
  var e, f, g, h, i = 0, j = a[0], k = [], l = this.length;
  if (!p.support.checkClone && l > 1 && typeof j == "string" && bw.test(j))return this.each(function () {
  p(this).domManip(a, c, d)
  });
  if (p.isFunction(j))return this.each(function (e) {
  var f = p(this);
  a[0] = j.call(this, e, c ? f.html() : b), f.domManip(a, c, d)
  });
  if (this[0]) {
  e = p.buildFragment(a, this, k), g = e.fragment, f = g.firstChild, g.childNodes.length === 1 && (g = f);
  if (f) {
  c = c && p.nodeName(f, "tr");
  for (h = e.cacheable || l - 1; i < l; i++)d.call(c && p.nodeName(this[i], "table") ? bC(this[i], "tbody") : this[i], i === h ? g : p.clone(g, !0, !0))
  }
  g = f = null, k.length && p.each(k, function (a, b) {
  b.src ? p.ajax ? p.ajax({url: b.src, type: "GET", dataType: "script", async: !1, global: !1, "throws": !0}) : p.error("no ajax") : p.globalEval((b.text || b.textContent || b.innerHTML || "").replace(by, "")), b.parentNode && b.parentNode.removeChild(b)
  })
  }
  return this
  }}), p.buildFragment = function (a, c, d) {
  var f, g, h, i = a[0];
  return c = c || e, c = (c[0] || c).ownerDocument || c[0] || c, typeof c.createDocumentFragment == "undefined" && (c = e), a.length === 1 && typeof i == "string" && i.length < 512 && c === e && i.charAt(0) === "<" && !bt.test(i) && (p.support.checkClone || !bw.test(i)) && (p.support.html5Clone || !bu.test(i)) && (g = !0, f = p.fragments[i], h = f !== b), f || (f = c.createDocumentFragment(), p.clean(a, c, f, d), g && (p.fragments[i] = h && f)), {fragment: f, cacheable: g}
  }, p.fragments = {}, p.each({appendTo: "append", prependTo: "prepend", insertBefore: "before", insertAfter: "after", replaceAll: "replaceWith"}, function (a, b) {
  p.fn[a] = function (c) {
  var d, e = 0, f = [], g = p(c), h = g.length, i = this.length === 1 && this[0].parentNode;
  if ((i == null || i && i.nodeType === 11 && i.childNodes.length === 1) && h === 1)return g[b](this[0]), this;
  for (; e < h; e++)d = (e > 0 ? this.clone(!0) : this).get(), p(g[e])[b](d), f = f.concat(d);
  return this.pushStack(f, a, g.selector)
  }
  }), p.extend({clone: function (a, b, c) {
  var d, e, f, g;
  p.support.html5Clone || p.isXMLDoc(a) || !bu.test("<" + a.nodeName + ">") ? g = a.cloneNode(!0) : (bB.innerHTML = a.outerHTML, bB.removeChild(g = bB.firstChild));
  if ((!p.support.noCloneEvent || !p.support.noCloneChecked) && (a.nodeType === 1 || a.nodeType === 11) && !p.isXMLDoc(a)) {
  bE(a, g), d = bF(a), e = bF(g);
  for (f = 0; d[f]; ++f)e[f] && bE(d[f], e[f])
  }
  if (b) {
  bD(a, g);
  if (c) {
  d = bF(a), e = bF(g);
  for (f = 0; d[f]; ++f)bD(d[f], e[f])
  }
  }
  return d = e = null, g
  }, clean: function (a, b, c, d) {
  var f, g, h, i, j, k, l, m, n, o, q, r, s = 0, t = [];
  if (!b || typeof b.createDocumentFragment == "undefined")b = e;
  for (g = b === e && bA; (h = a[s]) != null; s++) {
  typeof h == "number" && (h += "");
  if (!h)continue;
  if (typeof h == "string")if (!br.test(h))h = b.createTextNode(h); else {
  g = g || bk(b), l = l || g.appendChild(b.createElement("div")), h = h.replace(bo, "<$1></$2>"), i = (bp.exec(h) || ["", ""])[1].toLowerCase(), j = bz[i] || bz._default, k = j[0], l.innerHTML = j[1] + h + j[2];
  while (k--)l = l.lastChild;
  if (!p.support.tbody) {
  m = bq.test(h), n = i === "table" && !m ? l.firstChild && l.firstChild.childNodes : j[1] === "<table>" && !m ? l.childNodes : [];
  for (f = n.length - 1; f >= 0; --f)p.nodeName(n[f], "tbody") && !n[f].childNodes.length && n[f].parentNode.removeChild(n[f])
  }
  !p.support.leadingWhitespace && bn.test(h) && l.insertBefore(b.createTextNode(bn.exec(h)[0]), l.firstChild), h = l.childNodes, l = g.lastChild
  }
  h.nodeType ? t.push(h) : t = p.merge(t, h)
  }
  l && (g.removeChild(l), h = l = g = null);
  if (!p.support.appendChecked)for (s = 0; (h = t[s]) != null; s++)p.nodeName(h, "input") ? bG(h) : typeof h.getElementsByTagName != "undefined" && p.grep(h.getElementsByTagName("input"), bG);
  if (c) {
  q = function (a) {
  if (!a.type || bx.test(a.type))return d ? d.push(a.parentNode ? a.parentNode.removeChild(a) : a) : c.appendChild(a)
  };
  for (s = 0; (h = t[s]) != null; s++)if (!p.nodeName(h, "script") || !q(h))c.appendChild(h), typeof h.getElementsByTagName != "undefined" && (r = p.grep(p.merge([], h.getElementsByTagName("script")), q), t.splice.apply(t, [s + 1, 0].concat(r)), s += r.length)
  }
  return t
  }, cleanData: function (a, b) {
  var c, d, e, f, g = 0, h = p.expando, i = p.cache, j = p.support.deleteExpando, k = p.event.special;
  for (; (e = a[g]) != null; g++)if (b || p.acceptData(e)) {
  d = e[h], c = d && i[d];
  if (c) {
  if (c.events)for (f in c.events)k[f] ? p.event.remove(e, f) : p.removeEvent(e, f, c.handle);
  i[d] && (delete i[d], j ? delete e[h] : e.removeAttribute ? e.removeAttribute(h) : e[h] = null, p.deletedIds.push(d))
  }
  }
  }}), function () {
  var a, b;
  p.uaMatch = function (a) {
  a = a.toLowerCase();
  var b = /(chrome)[ \/]([\w.]+)/.exec(a) || /(webkit)[ \/]([\w.]+)/.exec(a) || /(opera)(?:.*version|)[ \/]([\w.]+)/.exec(a) || /(msie) ([\w.]+)/.exec(a) || a.indexOf("compatible") < 0 && /(mozilla)(?:.*? rv:([\w.]+)|)/.exec(a) || [];
  return{browser: b[1] || "", version: b[2] || "0"}
  }, a = p.uaMatch(g.userAgent), b = {}, a.browser && (b[a.browser] = !0, b.version = a.version), b.webkit && (b.safari = !0), p.browser = b, p.sub = function () {
  function a(b, c) {
  return new a.fn.init(b, c)
  }
   
  p.extend(!0, a, this), a.superclass = this, a.fn = a.prototype = this(), a.fn.constructor = a, a.sub = this.sub, a.fn.init = function c(c, d) {
  return d && d instanceof p && !(d instanceof a) && (d = a(d)), p.fn.init.call(this, c, d, b)
  }, a.fn.init.prototype = a.fn;
  var b = a(e);
  return a
  }
  }();
  var bH, bI, bJ, bK = /alpha\([^)]*\)/i, bL = /opacity=([^)]*)/, bM = /^(top|right|bottom|left)$/, bN = /^margin/, bO = new RegExp("^(" + q + ")(.*)$", "i"), bP = new RegExp("^(" + q + ")(?!px)[a-z%]+$", "i"), bQ = new RegExp("^([-+])=(" + q + ")", "i"), bR = {}, bS = {position: "absolute", visibility: "hidden", display: "block"}, bT = {letterSpacing: 0, fontWeight: 400, lineHeight: 1}, bU = ["Top", "Right", "Bottom", "Left"], bV = ["Webkit", "O", "Moz", "ms"], bW = p.fn.toggle;
  p.fn.extend({css: function (a, c) {
  return p.access(this, function (a, c, d) {
  return d !== b ? p.style(a, c, d) : p.css(a, c)
  }, a, c, arguments.length > 1)
  }, show: function () {
  return bZ(this, !0)
  }, hide: function () {
  return bZ(this)
  }, toggle: function (a, b) {
  var c = typeof a == "boolean";
  return p.isFunction(a) && p.isFunction(b) ? bW.apply(this, arguments) : this.each(function () {
  (c ? a : bY(this)) ? p(this).show() : p(this).hide()
  })
  }}), p.extend({cssHooks: {opacity: {get: function (a, b) {
  if (b) {
  var c = bH(a, "opacity");
  return c === "" ? "1" : c
  }
  }}}, cssNumber: {fillOpacity: !0, fontWeight: !0, lineHeight: !0, opacity: !0, orphans: !0, widows: !0, zIndex: !0, zoom: !0}, cssProps: {"float": p.support.cssFloat ? "cssFloat" : "styleFloat"}, style: function (a, c, d, e) {
  if (!a || a.nodeType === 3 || a.nodeType === 8 || !a.style)return;
  var f, g, h, i = p.camelCase(c), j = a.style;
  c = p.cssProps[i] || (p.cssProps[i] = bX(j, i)), h = p.cssHooks[c] || p.cssHooks[i];
  if (d === b)return h && "get"in h && (f = h.get(a, !1, e)) !== b ? f : j[c];
  g = typeof d, g === "string" && (f = bQ.exec(d)) && (d = (f[1] + 1) * f[2] + parseFloat(p.css(a, c)), g = "number");
  if (d == null || g === "number" && isNaN(d))return;
  g === "number" && !p.cssNumber[i] && (d += "px");
  if (!h || !("set"in h) || (d = h.set(a, d, e)) !== b)try {
  j[c] = d
  } catch (k) {
  }
  }, css: function (a, c, d, e) {
  var f, g, h, i = p.camelCase(c);
  return c = p.cssProps[i] || (p.cssProps[i] = bX(a.style, i)), h = p.cssHooks[c] || p.cssHooks[i], h && "get"in h && (f = h.get(a, !0, e)), f === b && (f = bH(a, c)), f === "normal" && c in bT && (f = bT[c]), d || e !== b ? (g = parseFloat(f), d || p.isNumeric(g) ? g || 0 : f) : f
  }, swap: function (a, b, c) {
  var d, e, f = {};
  for (e in b)f[e] = a.style[e], a.style[e] = b[e];
  d = c.call(a);
  for (e in b)a.style[e] = f[e];
  return d
  }}), a.getComputedStyle ? bH = function (a, b) {
  var c, d, e, f, g = getComputedStyle(a, null), h = a.style;
  return g && (c = g[b], c === "" && !p.contains(a.ownerDocument.documentElement, a) && (c = p.style(a, b)), bP.test(c) && bN.test(b) && (d = h.width, e = h.minWidth, f = h.maxWidth, h.minWidth = h.maxWidth = h.width = c, c = g.width, h.width = d, h.minWidth = e, h.maxWidth = f)), c
  } : e.documentElement.currentStyle && (bH = function (a, b) {
  var c, d, e = a.currentStyle && a.currentStyle[b], f = a.style;
  return e == null && f && f[b] && (e = f[b]), bP.test(e) && !bM.test(b) && (c = f.left, d = a.runtimeStyle && a.runtimeStyle.left, d && (a.runtimeStyle.left = a.currentStyle.left), f.left = b === "fontSize" ? "1em" : e, e = f.pixelLeft + "px", f.left = c, d && (a.runtimeStyle.left = d)), e === "" ? "auto" : e
  }), p.each(["height", "width"], function (a, b) {
  p.cssHooks[b] = {get: function (a, c, d) {
  if (c)return a.offsetWidth !== 0 || bH(a, "display") !== "none" ? ca(a, b, d) : p.swap(a, bS, function () {
  return ca(a, b, d)
  })
  }, set: function (a, c, d) {
  return b$(a, c, d ? b_(a, b, d, p.support.boxSizing && p.css(a, "boxSizing") === "border-box") : 0)
  }}
  }), p.support.opacity || (p.cssHooks.opacity = {get: function (a, b) {
  return bL.test((b && a.currentStyle ? a.currentStyle.filter : a.style.filter) || "") ? .01 * parseFloat(RegExp.$1) + "" : b ? "1" : ""
  }, set: function (a, b) {
  var c = a.style, d = a.currentStyle, e = p.isNumeric(b) ? "alpha(opacity=" + b * 100 + ")" : "", f = d && d.filter || c.filter || "";
  c.zoom = 1;
  if (b >= 1 && p.trim(f.replace(bK, "")) === "" && c.removeAttribute) {
  c.removeAttribute("filter");
  if (d && !d.filter)return
  }
  c.filter = bK.test(f) ? f.replace(bK, e) : f + " " + e
  }}), p(function () {
  p.support.reliableMarginRight || (p.cssHooks.marginRight = {get: function (a, b) {
  return p.swap(a, {display: "inline-block"}, function () {
  if (b)return bH(a, "marginRight")
  })
  }}), !p.support.pixelPosition && p.fn.position && p.each(["top", "left"], function (a, b) {
  p.cssHooks[b] = {get: function (a, c) {
  if (c) {
  var d = bH(a, b);
  return bP.test(d) ? p(a).position()[b] + "px" : d
  }
  }}
  })
  }), p.expr && p.expr.filters && (p.expr.filters.hidden = function (a) {
  return a.offsetWidth === 0 && a.offsetHeight === 0 || !p.support.reliableHiddenOffsets && (a.style && a.style.display || bH(a, "display")) === "none"
  }, p.expr.filters.visible = function (a) {
  return!p.expr.filters.hidden(a)
  }), p.each({margin: "", padding: "", border: "Width"}, function (a, b) {
  p.cssHooks[a + b] = {expand: function (c) {
  var d, e = typeof c == "string" ? c.split(" ") : [c], f = {};
  for (d = 0; d < 4; d++)f[a + bU[d] + b] = e[d] || e[d - 2] || e[0];
  return f
  }}, bN.test(a) || (p.cssHooks[a + b].set = b$)
  });
  var cc = /%20/g, cd = /\[\]$/, ce = /\r?\n/g, cf = /^(?:color|date|datetime|datetime-local|email|hidden|month|number|password|range|search|tel|text|time|url|week)$/i, cg = /^(?:select|textarea)/i;
  p.fn.extend({serialize: function () {
  return p.param(this.serializeArray())
  }, serializeArray: function () {
  return this.map(function () {
  return this.elements ? p.makeArray(this.elements) : this
  }).filter(function () {
  return this.name && !this.disabled && (this.checked || cg.test(this.nodeName) || cf.test(this.type))
  }).map(function (a, b) {
  var c = p(this).val();
  return c == null ? null : p.isArray(c) ? p.map(c, function (a, c) {
  return{name: b.name, value: a.replace(ce, "\r\n")}
  }) : {name: b.name, value: c.replace(ce, "\r\n")}
  }).get()
  }}), p.param = function (a, c) {
  var d, e = [], f = function (a, b) {
  b = p.isFunction(b) ? b() : b == null ? "" : b, e[e.length] = encodeURIComponent(a) + "=" + encodeURIComponent(b)
  };
  c === b && (c = p.ajaxSettings && p.ajaxSettings.traditional);
  if (p.isArray(a) || a.jquery && !p.isPlainObject(a))p.each(a, function () {
  f(this.name, this.value)
  }); else for (d in a)ch(d, a[d], c, f);
  return e.join("&").replace(cc, "+")
  };
  var ci, cj, ck = /#.*$/, cl = /^(.*?):[ \t]*([^\r\n]*)\r?$/mg, cm = /^(?:about|app|app\-storage|.+\-extension|file|res|widget):$/, cn = /^(?:GET|HEAD)$/, co = /^\/\//, cp = /\?/, cq = /<script\b[^<]*(?:(?!<\/script>)<[^<]*)*<\/script>/gi, cr = /([?&])_=[^&]*/, cs = /^([\w\+\.\-]+:)(?:\/\/([^\/?#:]*)(?::(\d+)|)|)/, ct = p.fn.load, cu = {}, cv = {}, cw = ["*/"] + ["*"];
  try {
  ci = f.href
  } catch (cx) {
  ci = e.createElement("a"), ci.href = "", ci = ci.href
  }
  cj = cs.exec(ci.toLowerCase()) || [], p.fn.load = function (a, c, d) {
  if (typeof a != "string" && ct)return ct.apply(this, arguments);
  if (!this.length)return this;
  var e, f, g, h = this, i = a.indexOf(" ");
  return i >= 0 && (e = a.slice(i, a.length), a = a.slice(0, i)), p.isFunction(c) ? (d = c, c = b) : typeof c == "object" && (f = "POST"), p.ajax({url: a, type: f, dataType: "html", data: c, complete: function (a, b) {
  d && h.each(d, g || [a.responseText, b, a])
  }}).done(function (a) {
  g = arguments, h.html(e ? p("<div>").append(a.replace(cq, "")).find(e) : a)
  }), this
  }, p.each("ajaxStart ajaxStop ajaxComplete ajaxError ajaxSuccess ajaxSend".split(" "), function (a, b) {
  p.fn[b] = function (a) {
  return this.on(b, a)
  }
  }), p.each(["get", "post"], function (a, c) {
  p[c] = function (a, d, e, f) {
  return p.isFunction(d) && (f = f || e, e = d, d = b), p.ajax({type: c, url: a, data: d, success: e, dataType: f})
  }
  }), p.extend({getScript: function (a, c) {
  return p.get(a, b, c, "script")
  }, getJSON: function (a, b, c) {
  return p.get(a, b, c, "json")
  }, ajaxSetup: function (a, b) {
  return b ? cA(a, p.ajaxSettings) : (b = a, a = p.ajaxSettings), cA(a, b), a
  }, ajaxSettings: {url: ci, isLocal: cm.test(cj[1]), global: !0, type: "GET", contentType: "application/x-www-form-urlencoded; charset=UTF-8", processData: !0, async: !0, accepts: {xml: "application/xml, text/xml", html: "text/html", text: "text/plain", json: "application/json, text/javascript", "*": cw}, contents: {xml: /xml/, html: /html/, json: /json/}, responseFields: {xml: "responseXML", text: "responseText"}, converters: {"* text": a.String, "text html": !0, "text json": p.parseJSON, "text xml": p.parseXML}, flatOptions: {context: !0, url: !0}}, ajaxPrefilter: cy(cu), ajaxTransport: cy(cv), ajax: function (a, c) {
  function y(a, c, f, i) {
  var k, s, t, u, w, y = c;
  if (v === 2)return;
  v = 2, h && clearTimeout(h), g = b, e = i || "", x.readyState = a > 0 ? 4 : 0, f && (u = cB(l, x, f));
  if (a >= 200 && a < 300 || a === 304)l.ifModified && (w = x.getResponseHeader("Last-Modified"), w && (p.lastModified[d] = w), w = x.getResponseHeader("Etag"), w && (p.etag[d] = w)), a === 304 ? (y = "notmodified", k = !0) : (k = cC(l, u), y = k.state, s = k.data, t = k.error, k = !t); else {
  t = y;
  if (!y || a)y = "error", a < 0 && (a = 0)
  }
  x.status = a, x.statusText = "" + (c || y), k ? o.resolveWith(m, [s, y, x]) : o.rejectWith(m, [x, y, t]), x.statusCode(r), r = b, j && n.trigger("ajax" + (k ? "Success" : "Error"), [x, l, k ? s : t]), q.fireWith(m, [x, y]), j && (n.trigger("ajaxComplete", [x, l]), --p.active || p.event.trigger("ajaxStop"))
  }
   
  typeof a == "object" && (c = a, a = b), c = c || {};
  var d, e, f, g, h, i, j, k, l = p.ajaxSetup({}, c), m = l.context || l, n = m !== l && (m.nodeType || m instanceof p) ? p(m) : p.event, o = p.Deferred(), q = p.Callbacks("once memory"), r = l.statusCode || {}, t = {}, u = {}, v = 0, w = "canceled", x = {readyState: 0, setRequestHeader: function (a, b) {
  if (!v) {
  var c = a.toLowerCase();
  a = u[c] = u[c] || a, t[a] = b
  }
  return this
  }, getAllResponseHeaders: function () {
  return v === 2 ? e : null
  }, getResponseHeader: function (a) {
  var c;
  if (v === 2) {
  if (!f) {
  f = {};
  while (c = cl.exec(e))f[c[1].toLowerCase()] = c[2]
  }
  c = f[a.toLowerCase()]
  }
  return c === b ? null : c
  }, overrideMimeType: function (a) {
  return v || (l.mimeType = a), this
  }, abort: function (a) {
  return a = a || w, g && g.abort(a), y(0, a), this
  }};
  o.promise(x), x.success = x.done, x.error = x.fail, x.complete = q.add, x.statusCode = function (a) {
  if (a) {
  var b;
  if (v < 2)for (b in a)r[b] = [r[b], a[b]]; else b = a[x.status], x.always(b)
  }
  return this
  }, l.url = ((a || l.url) + "").replace(ck, "").replace(co, cj[1] + "//"), l.dataTypes = p.trim(l.dataType || "*").toLowerCase().split(s), l.crossDomain == null && (i = cs.exec(l.url.toLowerCase()), l.crossDomain = !(!i || i[1] == cj[1] && i[2] == cj[2] && (i[3] || (i[1] === "http:" ? 80 : 443)) == (cj[3] || (cj[1] === "http:" ? 80 : 443)))), l.data && l.processData && typeof l.data != "string" && (l.data = p.param(l.data, l.traditional)), cz(cu, l, c, x);
  if (v === 2)return x;
  j = l.global, l.type = l.type.toUpperCase(), l.hasContent = !cn.test(l.type), j && p.active++ === 0 && p.event.trigger("ajaxStart");
  if (!l.hasContent) {
  l.data && (l.url += (cp.test(l.url) ? "&" : "?") + l.data, delete l.data), d = l.url;
  if (l.cache === !1) {
  var z = p.now(), A = l.url.replace(cr, "$1_=" + z);
  l.url = A + (A === l.url ? (cp.test(l.url) ? "&" : "?") + "_=" + z : "")
  }
  }
  (l.data && l.hasContent && l.contentType !== !1 || c.contentType) && x.setRequestHeader("Content-Type", l.contentType), l.ifModified && (d = d || l.url, p.lastModified[d] && x.setRequestHeader("If-Modified-Since", p.lastModified[d]), p.etag[d] && x.setRequestHeader("If-None-Match", p.etag[d])), x.setRequestHeader("Accept", l.dataTypes[0] && l.accepts[l.dataTypes[0]] ? l.accepts[l.dataTypes[0]] + (l.dataTypes[0] !== "*" ? ", " + cw + "; q=0.01" : "") : l.accepts["*"]);
  for (k in l.headers)x.setRequestHeader(k, l.headers[k]);
  if (!l.beforeSend || l.beforeSend.call(m, x, l) !== !1 && v !== 2) {
  w = "abort";
  for (k in{success: 1, error: 1, complete: 1})x[k](l[k]);
  g = cz(cv, l, c, x);
  if (!g)y(-1, "No Transport"); else {
  x.readyState = 1, j && n.trigger("ajaxSend", [x, l]), l.async && l.timeout > 0 && (h = setTimeout(function () {
  x.abort("timeout")
  }, l.timeout));
  try {
  v = 1, g.send(t, y)
  } catch (B) {
  if (v < 2)y(-1, B); else throw B
  }
  }
  return x
  }
  return x.abort()
  }, active: 0, lastModified: {}, etag: {}});
  var cD = [], cE = /\?/, cF = /(=)\?(?=&|$)|\?\?/, cG = p.now();
  p.ajaxSetup({jsonp: "callback", jsonpCallback: function () {
  var a = cD.pop() || p.expando + "_" + cG++;
  return this[a] = !0, a
  }}), p.ajaxPrefilter("json jsonp", function (c, d, e) {
  var f, g, h, i = c.data, j = c.url, k = c.jsonp !== !1, l = k && cF.test(j), m = k && !l && typeof i == "string" && !(c.contentType || "").indexOf("application/x-www-form-urlencoded") && cF.test(i);
  if (c.dataTypes[0] === "jsonp" || l || m)return f = c.jsonpCallback = p.isFunction(c.jsonpCallback) ? c.jsonpCallback() : c.jsonpCallback, g = a[f], l ? c.url = j.replace(cF, "$1" + f) : m ? c.data = i.replace(cF, "$1" + f) : k && (c.url += (cE.test(j) ? "&" : "?") + c.jsonp + "=" + f), c.converters["script json"] = function () {
  return h || p.error(f + " was not called"), h[0]
  }, c.dataTypes[0] = "json", a[f] = function () {
  h = arguments
  }, e.always(function () {
  a[f] = g, c[f] && (c.jsonpCallback = d.jsonpCallback, cD.push(f)), h && p.isFunction(g) && g(h[0]), h = g = b
  }), "script"
  }), p.ajaxSetup({accepts: {script: "text/javascript, application/javascript, application/ecmascript, application/x-ecmascript"}, contents: {script: /javascript|ecmascript/}, converters: {"text script": function (a) {
  return p.globalEval(a), a
  }}}), p.ajaxPrefilter("script", function (a) {
  a.cache === b && (a.cache = !1), a.crossDomain && (a.type = "GET", a.global = !1)
  }), p.ajaxTransport("script", function (a) {
  if (a.crossDomain) {
  var c, d = e.head || e.getElementsByTagName("head")[0] || e.documentElement;
  return{send: function (f, g) {
  c = e.createElement("script"), c.async = "async", a.scriptCharset && (c.charset = a.scriptCharset), c.src = a.url, c.onload = c.onreadystatechange = function (a, e) {
  if (e || !c.readyState || /loaded|complete/.test(c.readyState))c.onload = c.onreadystatechange = null, d && c.parentNode && d.removeChild(c), c = b, e || g(200, "success")
  }, d.insertBefore(c, d.firstChild)
  }, abort: function () {
  c && c.onload(0, 1)
  }}
  }
  });
  var cH, cI = a.ActiveXObject ? function () {
  for (var a in cH)cH[a](0, 1)
  } : !1, cJ = 0;
  p.ajaxSettings.xhr = a.ActiveXObject ? function () {
  return!this.isLocal && cK() || cL()
  } : cK, function (a) {
  p.extend(p.support, {ajax: !!a, cors: !!a && "withCredentials"in a})
  }(p.ajaxSettings.xhr()), p.support.ajax && p.ajaxTransport(function (c) {
  if (!c.crossDomain || p.support.cors) {
  var d;
  return{send: function (e, f) {
  var g, h, i = c.xhr();
  c.username ? i.open(c.type, c.url, c.async, c.username, c.password) : i.open(c.type, c.url, c.async);
  if (c.xhrFields)for (h in c.xhrFields)i[h] = c.xhrFields[h];
  c.mimeType && i.overrideMimeType && i.overrideMimeType(c.mimeType), !c.crossDomain && !e["X-Requested-With"] && (e["X-Requested-With"] = "XMLHttpRequest");
  try {
  for (h in e)i.setRequestHeader(h, e[h])
  } catch (j) {
  }
  i.send(c.hasContent && c.data || null), d = function (a, e) {
  var h, j, k, l, m;
  try {
  if (d && (e || i.readyState === 4)) {
  d = b, g && (i.onreadystatechange = p.noop, cI && delete cH[g]);
  if (e)i.readyState !== 4 && i.abort(); else {
  h = i.status, k = i.getAllResponseHeaders(), l = {}, m = i.responseXML, m && m.documentElement && (l.xml = m);
  try {
  l.text = i.responseText
  } catch (a) {
  }
  try {
  j = i.statusText
  } catch (n) {
  j = ""
  }
  !h && c.isLocal && !c.crossDomain ? h = l.text ? 200 : 404 : h === 1223 && (h = 204)
  }
  }
  } catch (o) {
  e || f(-1, o)
  }
  l && f(h, j, l, k)
  }, c.async ? i.readyState === 4 ? setTimeout(d, 0) : (g = ++cJ, cI && (cH || (cH = {}, p(a).unload(cI)), cH[g] = d), i.onreadystatechange = d) : d()
  }, abort: function () {
  d && d(0, 1)
  }}
  }
  });
  var cM, cN, cO = /^(?:toggle|show|hide)$/, cP = new RegExp("^(?:([-+])=|)(" + q + ")([a-z%]*)$", "i"), cQ = /queueHooks$/, cR = [cX], cS = {"*": [function (a, b) {
  var c, d, e, f = this.createTween(a, b), g = cP.exec(b), h = f.cur(), i = +h || 0, j = 1;
  if (g) {
  c = +g[2], d = g[3] || (p.cssNumber[a] ? "" : "px");
  if (d !== "px" && i) {
  i = p.css(f.elem, a, !0) || c || 1;
  do e = j = j || ".5", i = i / j, p.style(f.elem, a, i + d), j = f.cur() / h; while (j !== 1 && j !== e)
  }
  f.unit = d, f.start = i, f.end = g[1] ? i + (g[1] + 1) * c : c
  }
  return f
  }]};
  p.Animation = p.extend(cV, {tweener: function (a, b) {
  p.isFunction(a) ? (b = a, a = ["*"]) : a = a.split(" ");
  var c, d = 0, e = a.length;
  for (; d < e; d++)c = a[d], cS[c] = cS[c] || [], cS[c].unshift(b)
  }, prefilter: function (a, b) {
  b ? cR.unshift(a) : cR.push(a)
  }}), p.Tween = cY, cY.prototype = {constructor: cY, init: function (a, b, c, d, e, f) {
  this.elem = a, this.prop = c, this.easing = e || "swing", this.options = b, this.start = this.now = this.cur(), this.end = d, this.unit = f || (p.cssNumber[c] ? "" : "px")
  }, cur: function () {
  var a = cY.propHooks[this.prop];
  return a && a.get ? a.get(this) : cY.propHooks._default.get(this)
  }, run: function (a) {
  var b, c = cY.propHooks[this.prop];
  return this.pos = b = p.easing[this.easing](a, this.options.duration * a, 0, 1, this.options.duration), this.now = (this.end - this.start) * b + this.start, this.options.step && this.options.step.call(this.elem, this.now, this), c && c.set ? c.set(this) : cY.propHooks._default.set(this), this
  }}, cY.prototype.init.prototype = cY.prototype, cY.propHooks = {_default: {get: function (a) {
  var b;
  return a.elem[a.prop] == null || !!a.elem.style && a.elem.style[a.prop] != null ? (b = p.css(a.elem, a.prop, !1, ""), !b || b === "auto" ? 0 : b) : a.elem[a.prop]
  }, set: function (a) {
  p.fx.step[a.prop] ? p.fx.step[a.prop](a) : a.elem.style && (a.elem.style[p.cssProps[a.prop]] != null || p.cssHooks[a.prop]) ? p.style(a.elem, a.prop, a.now + a.unit) : a.elem[a.prop] = a.now
  }}}, cY.propHooks.scrollTop = cY.propHooks.scrollLeft = {set: function (a) {
  a.elem.nodeType && a.elem.parentNode && (a.elem[a.prop] = a.now)
  }}, p.each(["toggle", "show", "hide"], function (a, b) {
  var c = p.fn[b];
  p.fn[b] = function (d, e, f) {
  return d == null || typeof d == "boolean" || !a && p.isFunction(d) && p.isFunction(e) ? c.apply(this, arguments) : this.animate(cZ(b, !0), d, e, f)
  }
  }), p.fn.extend({fadeTo: function (a, b, c, d) {
  return this.filter(bY).css("opacity", 0).show().end().animate({opacity: b}, a, c, d)
  }, animate: function (a, b, c, d) {
  var e = p.isEmptyObject(a), f = p.speed(b, c, d), g = function () {
  var b = cV(this, p.extend({}, a), f);
  e && b.stop(!0)
  };
  return e || f.queue === !1 ? this.each(g) : this.queue(f.queue, g)
  }, stop: function (a, c, d) {
  var e = function (a) {
  var b = a.stop;
  delete a.stop, b(d)
  };
  return typeof a != "string" && (d = c, c = a, a = b), c && a !== !1 && this.queue(a || "fx", []), this.each(function () {
  var b = !0, c = a != null && a + "queueHooks", f = p.timers, g = p._data(this);
  if (c)g[c] && g[c].stop && e(g[c]); else for (c in g)g[c] && g[c].stop && cQ.test(c) && e(g[c]);
  for (c = f.length; c--;)f[c].elem === this && (a == null || f[c].queue === a) && (f[c].anim.stop(d), b = !1, f.splice(c, 1));
  (b || !d) && p.dequeue(this, a)
  })
  }}), p.each({slideDown: cZ("show"), slideUp: cZ("hide"), slideToggle: cZ("toggle"), fadeIn: {opacity: "show"}, fadeOut: {opacity: "hide"}, fadeToggle: {opacity: "toggle"}}, function (a, b) {
  p.fn[a] = function (a, c, d) {
  return this.animate(b, a, c, d)
  }
  }), p.speed = function (a, b, c) {
  var d = a && typeof a == "object" ? p.extend({}, a) : {complete: c || !c && b || p.isFunction(a) && a, duration: a, easing: c && b || b && !p.isFunction(b) && b};
  d.duration = p.fx.off ? 0 : typeof d.duration == "number" ? d.duration : d.duration in p.fx.speeds ? p.fx.speeds[d.duration] : p.fx.speeds._default;
  if (d.queue == null || d.queue === !0)d.queue = "fx";
  return d.old = d.complete, d.complete = function () {
  p.isFunction(d.old) && d.old.call(this), d.queue && p.dequeue(this, d.queue)
  }, d
  }, p.easing = {linear: function (a) {
  return a
  }, swing: function (a) {
  return.5 - Math.cos(a * Math.PI) / 2
  }}, p.timers = [], p.fx = cY.prototype.init, p.fx.tick = function () {
  var a, b = p.timers, c = 0;
  for (; c < b.length; c++)a = b[c], !a() && b[c] === a && b.splice(c--, 1);
  b.length || p.fx.stop()
  }, p.fx.timer = function (a) {
  a() && p.timers.push(a) && !cN && (cN = setInterval(p.fx.tick, p.fx.interval))
  }, p.fx.interval = 13, p.fx.stop = function () {
  clearInterval(cN), cN = null
  }, p.fx.speeds = {slow: 600, fast: 200, _default: 400}, p.fx.step = {}, p.expr && p.expr.filters && (p.expr.filters.animated = function (a) {
  return p.grep(p.timers,function (b) {
  return a === b.elem
  }).length
  });
  var c$ = /^(?:body|html)$/i;
  p.fn.offset = function (a) {
  if (arguments.length)return a === b ? this : this.each(function (b) {
  p.offset.setOffset(this, a, b)
  });
  var c, d, e, f, g, h, i, j, k, l, m = this[0], n = m && m.ownerDocument;
  if (!n)return;
  return(e = n.body) === m ? p.offset.bodyOffset(m) : (d = n.documentElement, p.contains(d, m) ? (c = m.getBoundingClientRect(), f = c_(n), g = d.clientTop || e.clientTop || 0, h = d.clientLeft || e.clientLeft || 0, i = f.pageYOffset || d.scrollTop, j = f.pageXOffset || d.scrollLeft, k = c.top + i - g, l = c.left + j - h, {top: k, left: l}) : {top: 0, left: 0})
  }, p.offset = {bodyOffset: function (a) {
  var b = a.offsetTop, c = a.offsetLeft;
  return p.support.doesNotIncludeMarginInBodyOffset && (b += parseFloat(p.css(a, "marginTop")) || 0, c += parseFloat(p.css(a, "marginLeft")) || 0), {top: b, left: c}
  }, setOffset: function (a, b, c) {
  var d = p.css(a, "position");
  d === "static" && (a.style.position = "relative");
  var e = p(a), f = e.offset(), g = p.css(a, "top"), h = p.css(a, "left"), i = (d === "absolute" || d === "fixed") && p.inArray("auto", [g, h]) > -1, j = {}, k = {}, l, m;
  i ? (k = e.position(), l = k.top, m = k.left) : (l = parseFloat(g) || 0, m = parseFloat(h) || 0), p.isFunction(b) && (b = b.call(a, c, f)), b.top != null && (j.top = b.top - f.top + l), b.left != null && (j.left = b.left - f.left + m), "using"in b ? b.using.call(a, j) : e.css(j)
  }}, p.fn.extend({position: function () {
  if (!this[0])return;
  var a = this[0], b = this.offsetParent(), c = this.offset(), d = c$.test(b[0].nodeName) ? {top: 0, left: 0} : b.offset();
  return c.top -= parseFloat(p.css(a, "marginTop")) || 0, c.left -= parseFloat(p.css(a, "marginLeft")) || 0, d.top += parseFloat(p.css(b[0], "borderTopWidth")) || 0, d.left += parseFloat(p.css(b[0], "borderLeftWidth")) || 0, {top: c.top - d.top, left: c.left - d.left}
  }, offsetParent: function () {
  return this.map(function () {
  var a = this.offsetParent || e.body;
  while (a && !c$.test(a.nodeName) && p.css(a, "position") === "static")a = a.offsetParent;
  return a || e.body
  })
  }}), p.each({scrollLeft: "pageXOffset", scrollTop: "pageYOffset"}, function (a, c) {
  var d = /Y/.test(c);
  p.fn[a] = function (e) {
  return p.access(this, function (a, e, f) {
  var g = c_(a);
  if (f === b)return g ? c in g ? g[c] : g.document.documentElement[e] : a[e];
  g ? g.scrollTo(d ? p(g).scrollLeft() : f, d ? f : p(g).scrollTop()) : a[e] = f
  }, a, e, arguments.length, null)
  }
  }), p.each({Height: "height", Width: "width"}, function (a, c) {
  p.each({padding: "inner" + a, content: c, "": "outer" + a}, function (d, e) {
  p.fn[e] = function (e, f) {
  var g = arguments.length && (d || typeof e != "boolean"), h = d || (e === !0 || f === !0 ? "margin" : "border");
  return p.access(this, function (c, d, e) {
  var f;
  return p.isWindow(c) ? c.document.documentElement["client" + a] : c.nodeType === 9 ? (f = c.documentElement, Math.max(c.body["scroll" + a], f["scroll" + a], c.body["offset" + a], f["offset" + a], f["client" + a])) : e === b ? p.css(c, d, e, h) : p.style(c, d, e, h)
  }, c, g ? e : b, g)
  }
  })
  }), a.jQuery = a.$ = p, typeof define == "function" && define.amd && define.amd.jQuery && define("jquery", [], function () {
  return p
  })
  })(window);
   
/* Modernizr 2.6.1 (Custom Build) | MIT & BSD /* Modernizr 2.6.1 (Custom Build) | MIT & BSD
* Build: http://modernizr.com/download/#-fontface-backgroundsize-borderimage-borderradius-boxshadow-flexbox-hsla-multiplebgs-opacity-rgba-textshadow-cssanimations-csscolumns-generatedcontent-cssgradients-cssreflections-csstransforms-csstransforms3d-csstransitions-applicationcache-canvas-canvastext-draganddrop-hashchange-history-audio-video-indexeddb-input-inputtypes-localstorage-postmessage-sessionstorage-websockets-websqldatabase-webworkers-geolocation-inlinesvg-smil-svg-svgclippaths-touch-webgl-shiv-mq-cssclasses-addtest-prefixed-teststyles-testprop-testallprops-hasevent-prefixes-domprefixes-load * Build: http://modernizr.com/download/#-fontface-backgroundsize-borderimage-borderradius-boxshadow-flexbox-hsla-multiplebgs-opacity-rgba-textshadow-cssanimations-csscolumns-generatedcontent-cssgradients-cssreflections-csstransforms-csstransforms3d-csstransitions-applicationcache-canvas-canvastext-draganddrop-hashchange-history-audio-video-indexeddb-input-inputtypes-localstorage-postmessage-sessionstorage-websockets-websqldatabase-webworkers-geolocation-inlinesvg-smil-svg-svgclippaths-touch-webgl-shiv-mq-cssclasses-addtest-prefixed-teststyles-testprop-testallprops-hasevent-prefixes-domprefixes-load
*/ */
;window.Modernizr=function(a,b,c){function D(a){j.cssText=a}function E(a,b){return D(n.join(a+";")+(b||""))}function F(a,b){return typeof a===b}function G(a,b){return!!~(""+a).indexOf(b)}function H(a,b){for(var d in a){var e=a[d];if(!G(e,"-")&&j[e]!==c)return b=="pfx"?e:!0}return!1}function I(a,b,d){for(var e in a){var f=b[a[e]];if(f!==c)return d===!1?a[e]:F(f,"function")?f.bind(d||b):f}return!1}function J(a,b,c){var d=a.charAt(0).toUpperCase()+a.slice(1),e=(a+" "+p.join(d+" ")+d).split(" ");return F(b,"string")||F(b,"undefined")?H(e,b):(e=(a+" "+q.join(d+" ")+d).split(" "),I(e,b,c))}function K(){e.input=function(c){for(var d=0,e=c.length;d<e;d++)u[c[d]]=c[d]in k;return u.list&&(u.list=!!b.createElement("datalist")&&!!a.HTMLDataListElement),u}("autocomplete autofocus list placeholder max min multiple pattern required step".split(" ")),e.inputtypes=function(a){for(var d=0,e,f,h,i=a.length;d<i;d++)k.setAttribute("type",f=a[d]),e=k.type!=="text",e&&(k.value=l,k.style.cssText="position:absolute;visibility:hidden;",/^range$/.test(f)&&k.style.WebkitAppearance!==c?(g.appendChild(k),h=b.defaultView,e=h.getComputedStyle&&h.getComputedStyle(k,null).WebkitAppearance!=="textfield"&&k.offsetHeight!==0,g.removeChild(k)):/^(search|tel)$/.test(f)||(/^(url|email)$/.test(f)?e=k.checkValidity&&k.checkValidity()===!1:e=k.value!=l)),t[a[d]]=!!e;return t}("search tel url email datetime date month week time datetime-local number range color".split(" "))}var d="2.6.1",e={},f=!0,g=b.documentElement,h="modernizr",i=b.createElement(h),j=i.style,k=b.createElement("input"),l=":)",m={}.toString,n=" -webkit- -moz- -o- -ms- ".split(" "),o="Webkit Moz O ms",p=o.split(" "),q=o.toLowerCase().split(" "),r={svg:"http://www.w3.org/2000/svg"},s={},t={},u={},v=[],w=v.slice,x,y=function(a,c,d,e){var f,i,j,k=b.createElement("div"),l=b.body,m=l?l:b.createElement("body");if(parseInt(d,10))while(d--)j=b.createElement("div"),j.id=e?e[d]:h+(d+1),k.appendChild(j);return f=["&#173;",'<style id="s',h,'">',a,"</style>"].join(""),k.id=h,(l?k:m).innerHTML+=f,m.appendChild(k),l||(m.style.background="",g.appendChild(m)),i=c(k,a),l?k.parentNode.removeChild(k):m.parentNode.removeChild(m),!!i},z=function(b){var c=a.matchMedia||a.msMatchMedia;if(c)return c(b).matches;var d;return y("@media "+b+" { #"+h+" { position: absolute; } }",function(b){d=(a.getComputedStyle?getComputedStyle(b,null):b.currentStyle)["position"]=="absolute"}),d},A=function(){function d(d,e){e=e||b.createElement(a[d]||"div"),d="on"+d;var f=d in e;return f||(e.setAttribute||(e=b.createElement("div")),e.setAttribute&&e.removeAttribute&&(e.setAttribute(d,""),f=F(e[d],"function"),F(e[d],"undefined")||(e[d]=c),e.removeAttribute(d))),e=null,f}var a={select:"input",change:"input",submit:"form",reset:"form",error:"img",load:"img",abort:"img"};return d}(),B={}.hasOwnProperty,C;!F(B,"undefined")&&!F(B.call,"undefined")?C=function(a,b){return B.call(a,b)}:C=function(a,b){return b in a&&F(a.constructor.prototype[b],"undefined")},Function.prototype.bind||(Function.prototype.bind=function(b){var c=this;if(typeof c!="function")throw new TypeError;var d=w.call(arguments,1),e=function(){if(this instanceof e){var a=function(){};a.prototype=c.prototype;var f=new a,g=c.apply(f,d.concat(w.call(arguments)));return Object(g)===g?g:f}return c.apply(b,d.concat(w.call(arguments)))};return e}),s.flexbox=function(){return J("flexWrap")},s.canvas=function(){var a=b.createElement("canvas");return!!a.getContext&&!!a.getContext("2d")},s.canvastext=function(){return!!e.canvas&&!!F(b.createElement("canvas").getContext("2d").fillText,"function")},s.webgl=function(){return!!a.WebGLRenderingContext},s.touch=function(){var c;return"ontouchstart"in a||a.DocumentTouch&&b instanceof DocumentTouch?c=!0:y(["@media (",n.join("touch-enabled),("),h,")","{#modernizr{top:9px;position:absolute}}"].join(""),function(a){c=a.offsetTop===9}),c},s.geolocation=function(){return"geolocation"in navigator},s.postmessage=function(){return!!a.postMessage},s.websqldatabase=function(){return!!a.openDatabase},s.indexedDB=function(){return!!J("indexedDB",a)},s.hashchange=function(){return A("hashchange",a)&&(b.documentMode===c||b.documentMode>7)},s.history=function(){return!!a.history&&!!history.pushState},s.draganddrop=function(){var a=b.createElement("div");return"draggable"in a||"ondragstart"in a&&"ondrop"in a},s.websockets=function(){return"WebSocket"in a||"MozWebSocket"in a},s.rgba=function(){return D("background-color:rgba(150,255,150,.5)"),G(j.backgroundColor,"rgba")},s.hsla=function(){return D("background-color:hsla(120,40%,100%,.5)"),G(j.backgroundColor,"rgba")||G(j.backgroundColor,"hsla")},s.multiplebgs=function(){return D("background:url(https://),url(https://),red url(https://)"),/(url\s*\(.*?){3}/.test(j.background)},s.backgroundsize=function(){return J("backgroundSize")},s.borderimage=function(){return J("borderImage")},s.borderradius=function(){return J("borderRadius")},s.boxshadow=function(){return J("boxShadow")},s.textshadow=function(){return b.createElement("div").style.textShadow===""},s.opacity=function(){return E("opacity:.55"),/^0.55$/.test(j.opacity)},s.cssanimations=function(){return J("animationName")},s.csscolumns=function(){return J("columnCount")},s.cssgradients=function(){var a="background-image:",b="gradient(linear,left top,right bottom,from(#9f9),to(white));",c="linear-gradient(left top,#9f9, white);";return D((a+"-webkit- ".split(" ").join(b+a)+n.join(c+a)).slice(0,-a.length)),G(j.backgroundImage,"gradient")},s.cssreflections=function(){return J("boxReflect")},s.csstransforms=function(){return!!J("transform")},s.csstransforms3d=function(){var a=!!J("perspective");return a&&"webkitPerspective"in g.style&&y("@media (transform-3d),(-webkit-transform-3d){#modernizr{left:9px;position:absolute;height:3px;}}",function(b,c){a=b.offsetLeft===9&&b.offsetHeight===3}),a},s.csstransitions=function(){return J("transition")},s.fontface=function(){var a;return y('@font-face {font-family:"font";src:url("https://")}',function(c,d){var e=b.getElementById("smodernizr"),f=e.sheet||e.styleSheet,g=f?f.cssRules&&f.cssRules[0]?f.cssRules[0].cssText:f.cssText||"":"";a=/src/i.test(g)&&g.indexOf(d.split(" ")[0])===0}),a},s.generatedcontent=function(){var a;return y(['#modernizr:after{content:"',l,'";visibility:hidden}'].join(""),function(b){a=b.offsetHeight>=1}),a},s.video=function(){var a=b.createElement("video"),c=!1;try{if(c=!!a.canPlayType)c=new Boolean(c),c.ogg=a.canPlayType('video/ogg; codecs="theora"').replace(/^no$/,""),c.h264=a.canPlayType('video/mp4; codecs="avc1.42E01E"').replace(/^no$/,""),c.webm=a.canPlayType('video/webm; codecs="vp8, vorbis"').replace(/^no$/,"")}catch(d){}return c},s.audio=function(){var a=b.createElement("audio"),c=!1;try{if(c=!!a.canPlayType)c=new Boolean(c),c.ogg=a.canPlayType('audio/ogg; codecs="vorbis"').replace(/^no$/,""),c.mp3=a.canPlayType("audio/mpeg;").replace(/^no$/,""),c.wav=a.canPlayType('audio/wav; codecs="1"').replace(/^no$/,""),c.m4a=(a.canPlayType("audio/x-m4a;")||a.canPlayType("audio/aac;")).replace(/^no$/,"")}catch(d){}return c},s.localstorage=function(){try{return localStorage.setItem(h,h),localStorage.removeItem(h),!0}catch(a){return!1}},s.sessionstorage=function(){try{return sessionStorage.setItem(h,h),sessionStorage.removeItem(h),!0}catch(a){return!1}},s.webworkers=function(){return!!a.Worker},s.applicationcache=function(){return!!a.applicationCache},s.svg=function(){return!!b.createElementNS&&!!b.createElementNS(r.svg,"svg").createSVGRect},s.inlinesvg=function(){var a=b.createElement("div");return a.innerHTML="<svg/>",(a.firstChild&&a.firstChild.namespaceURI)==r.svg},s.smil=function(){return!!b.createElementNS&&/SVGAnimate/.test(m.call(b.createElementNS(r.svg,"animate")))},s.svgclippaths=function(){return!!b.createElementNS&&/SVGClipPath/.test(m.call(b.createElementNS(r.svg,"clipPath")))};for(var L in s)C(s,L)&&(x=L.toLowerCase(),e[x]=s[L](),v.push((e[x]?"":"no-")+x));return e.input||K(),e.addTest=function(a,b){if(typeof a=="object")for(var d in a)C(a,d)&&e.addTest(d,a[d]);else{a=a.toLowerCase();if(e[a]!==c)return e;b=typeof b=="function"?b():b,f&&(g.className+=" "+(b?"":"no-")+a),e[a]=b}return e},D(""),i=k=null,function(a,b){function k(a,b){var c=a.createElement("p"),d=a.getElementsByTagName("head")[0]||a.documentElement;return c.innerHTML="x<style>"+b+"</style>",d.insertBefore(c.lastChild,d.firstChild)}function l(){var a=r.elements;return typeof a=="string"?a.split(" "):a}function m(a){var b=i[a[g]];return b||(b={},h++,a[g]=h,i[h]=b),b}function n(a,c,f){c||(c=b);if(j)return c.createElement(a);f||(f=m(c));var g;return f.cache[a]?g=f.cache[a].cloneNode():e.test(a)?g=(f.cache[a]=f.createElem(a)).cloneNode():g=f.createElem(a),g.canHaveChildren&&!d.test(a)?f.frag.appendChild(g):g}function o(a,c){a||(a=b);if(j)return a.createDocumentFragment();c=c||m(a);var d=c.frag.cloneNode(),e=0,f=l(),g=f.length;for(;e<g;e++)d.createElement(f[e]);return d}function p(a,b){b.cache||(b.cache={},b.createElem=a.createElement,b.createFrag=a.createDocumentFragment,b.frag=b.createFrag()),a.createElement=function(c){return r.shivMethods?n(c,a,b):b.createElem(c)},a.createDocumentFragment=Function("h,f","return function(){var n=f.cloneNode(),c=n.createElement;h.shivMethods&&("+l().join().replace(/\w+/g,function(a){return b.createElem(a),b.frag.createElement(a),'c("'+a+'")'})+");return n}")(r,b.frag)}function q(a){a||(a=b);var c=m(a);return r.shivCSS&&!f&&!c.hasCSS&&(c.hasCSS=!!k(a,"article,aside,figcaption,figure,footer,header,hgroup,nav,section{display:block}mark{background:#FF0;color:#000}")),j||p(a,c),a}var c=a.html5||{},d=/^<|^(?:button|map|select|textarea|object|iframe|option|optgroup)$/i,e=/^<|^(?:a|b|button|code|div|fieldset|form|h1|h2|h3|h4|h5|h6|i|iframe|img|input|label|li|link|ol|option|p|param|q|script|select|span|strong|style|table|tbody|td|textarea|tfoot|th|thead|tr|ul)$/i,f,g="_html5shiv",h=0,i={},j;(function(){try{var a=b.createElement("a");a.innerHTML="<xyz></xyz>",f="hidden"in a,j=a.childNodes.length==1||function(){b.createElement("a");var a=b.createDocumentFragment();return typeof a.cloneNode=="undefined"||typeof a.createDocumentFragment=="undefined"||typeof a.createElement=="undefined"}()}catch(c){f=!0,j=!0}})();var r={elements:c.elements||"abbr article aside audio bdi canvas data datalist details figcaption figure footer header hgroup mark meter nav output progress section summary time video",shivCSS:c.shivCSS!==!1,supportsUnknownElements:j,shivMethods:c.shivMethods!==!1,type:"default",shivDocument:q,createElement:n,createDocumentFragment:o};a.html5=r,q(b)}(this,b),e._version=d,e._prefixes=n,e._domPrefixes=q,e._cssomPrefixes=p,e.mq=z,e.hasEvent=A,e.testProp=function(a){return H([a])},e.testAllProps=J,e.testStyles=y,e.prefixed=function(a,b,c){return b?J(a,b,c):J(a,"pfx")},g.className=g.className.replace(/(^|\s)no-js(\s|$)/,"$1$2")+(f?" js "+v.join(" "):""),e}(this,this.document),function(a,b,c){function d(a){return o.call(a)=="[object Function]"}function e(a){return typeof a=="string"}function f(){}function g(a){return!a||a=="loaded"||a=="complete"||a=="uninitialized"}function h(){var a=p.shift();q=1,a?a.t?m(function(){(a.t=="c"?B.injectCss:B.injectJs)(a.s,0,a.a,a.x,a.e,1)},0):(a(),h()):q=0}function i(a,c,d,e,f,i,j){function k(b){if(!o&&g(l.readyState)&&(u.r=o=1,!q&&h(),l.onload=l.onreadystatechange=null,b)){a!="img"&&m(function(){t.removeChild(l)},50);for(var d in y[c])y[c].hasOwnProperty(d)&&y[c][d].onload()}}var j=j||B.errorTimeout,l={},o=0,r=0,u={t:d,s:c,e:f,a:i,x:j};y[c]===1&&(r=1,y[c]=[],l=b.createElement(a)),a=="object"?l.data=c:(l.src=c,l.type=a),l.width=l.height="0",l.onerror=l.onload=l.onreadystatechange=function(){k.call(this,r)},p.splice(e,0,u),a!="img"&&(r||y[c]===2?(t.insertBefore(l,s?null:n),m(k,j)):y[c].push(l))}function j(a,b,c,d,f){return q=0,b=b||"j",e(a)?i(b=="c"?v:u,a,b,this.i++,c,d,f):(p.splice(this.i++,0,a),p.length==1&&h()),this}function k(){var a=B;return a.loader={load:j,i:0},a}var l=b.documentElement,m=a.setTimeout,n=b.getElementsByTagName("script")[0],o={}.toString,p=[],q=0,r="MozAppearance"in l.style,s=r&&!!b.createRange().compareNode,t=s?l:n.parentNode,l=a.opera&&o.call(a.opera)=="[object Opera]",l=!!b.attachEvent&&!l,u=r?"object":l?"script":"img",v=l?"script":u,w=Array.isArray||function(a){return o.call(a)=="[object Array]"},x=[],y={},z={timeout:function(a,b){return b.length&&(a.timeout=b[0]),a}},A,B;B=function(a){function b(a){var a=a.split("!"),b=x.length,c=a.pop(),d=a.length,c={url:c,origUrl:c,prefixes:a},e,f,g;for(f=0;f<d;f++)g=a[f].split("="),(e=z[g.shift()])&&(c=e(c,g));for(f=0;f<b;f++)c=x[f](c);return c}function g(a,e,f,g,i){var j=b(a),l=j.autoCallback;j.url.split(".").pop().split("?").shift(),j.bypass||(e&&(e=d(e)?e:e[a]||e[g]||e[a.split("/").pop().split("?")[0]]||h),j.instead?j.instead(a,e,f,g,i):(y[j.url]?j.noexec=!0:y[j.url]=1,f.load(j.url,j.forceCSS||!j.forceJS&&"css"==j.url.split(".").pop().split("?").shift()?"c":c,j.noexec,j.attrs,j.timeout),(d(e)||d(l))&&f.load(function(){k(),e&&e(j.origUrl,i,g),l&&l(j.origUrl,i,g),y[j.url]=2})))}function i(a,b){function c(a,c){if(a){if(e(a))c||(j=function(){var a=[].slice.call(arguments);k.apply(this,a),l()}),g(a,j,b,0,h);else if(Object(a)===a)for(n in m=function(){var b=0,c;for(c in a)a.hasOwnProperty(c)&&b++;return b}(),a)a.hasOwnProperty(n)&&(!c&&!--m&&(d(j)?j=function(){var a=[].slice.call(arguments);k.apply(this,a),l()}:j[n]=function(a){return function(){var b=[].slice.call(arguments);a&&a.apply(this,b),l()}}(k[n])),g(a[n],j,b,n,h))}else!c&&l()}var h=!!a.test,i=a.load||a.both,j=a.callback||f,k=j,l=a.complete||f,m,n;c(h?a.yep:a.nope,!!i),i&&c(i)}var j,l,m=this.yepnope.loader;if(e(a))g(a,0,m,0);else if(w(a))for(j=0;j<a.length;j++)l=a[j],e(l)?g(l,0,m,0):w(l)?B(l):Object(l)===l&&i(l,m);else Object(a)===a&&i(a,m)},B.addPrefix=function(a,b){z[a]=b},B.addFilter=function(a){x.push(a)},B.errorTimeout=1e4,b.readyState==null&&b.addEventListener&&(b.readyState="loading",b.addEventListener("DOMContentLoaded",A=function(){b.removeEventListener("DOMContentLoaded",A,0),b.readyState="complete"},0)),a.yepnope=k(),a.yepnope.executeStack=h,a.yepnope.injectJs=function(a,c,d,e,i,j){var k=b.createElement("script"),l,o,e=e||B.errorTimeout;k.src=a;for(o in d)k.setAttribute(o,d[o]);c=j?h:c||f,k.onreadystatechange=k.onload=function(){!l&&g(k.readyState)&&(l=1,c(),k.onload=k.onreadystatechange=null)},m(function(){l||(l=1,c(1))},e),i?k.onload():n.parentNode.insertBefore(k,n)},a.yepnope.injectCss=function(a,c,d,e,g,i){var e=b.createElement("link"),j,c=i?h:c||f;e.href=a,e.rel="stylesheet",e.type="text/css";for(j in d)e.setAttribute(j,d[j]);g||(n.parentNode.insertBefore(e,n),m(c,0))}}(this,document),Modernizr.load=function(){yepnope.apply(window,[].slice.call(arguments,0))}; ;
  window.Modernizr = function (a, b, c) {
  function D(a) {
  j.cssText = a
  }
   
  function E(a, b) {
  return D(n.join(a + ";") + (b || ""))
  }
   
  function F(a, b) {
  return typeof a === b
  }
   
  function G(a, b) {
  return!!~("" + a).indexOf(b)
  }
   
  function H(a, b) {
  for (var d in a) {
  var e = a[d];
  if (!G(e, "-") && j[e] !== c)return b == "pfx" ? e : !0
  }
  return!1
  }
   
  function I(a, b, d) {
  for (var e in a) {
  var f = b[a[e]];
  if (f !== c)return d === !1 ? a[e] : F(f, "function") ? f.bind(d || b) : f
  }
  return!1
  }
   
  function J(a, b, c) {
  var d = a.charAt(0).toUpperCase() + a.slice(1), e = (a + " " + p.join(d + " ") + d).split(" ");
  return F(b, "string") || F(b, "undefined") ? H(e, b) : (e = (a + " " + q.join(d + " ") + d).split(" "), I(e, b, c))
  }
   
  function K() {
  e.input = function (c) {
  for (var d = 0, e = c.length; d < e; d++)u[c[d]] = c[d]in k;
  return u.list && (u.list = !!b.createElement("datalist") && !!a.HTMLDataListElement), u
  }("autocomplete autofocus list placeholder max min multiple pattern required step".split(" ")), e.inputtypes = function (a) {
  for (var d = 0, e, f, h, i = a.length; d < i; d++)k.setAttribute("type", f = a[d]), e = k.type !== "text", e && (k.value = l, k.style.cssText = "position:absolute;visibility:hidden;", /^range$/.test(f) && k.style.WebkitAppearance !== c ? (g.appendChild(k), h = b.defaultView, e = h.getComputedStyle && h.getComputedStyle(k, null).WebkitAppearance !== "textfield" && k.offsetHeight !== 0, g.removeChild(k)) : /^(search|tel)$/.test(f) || (/^(url|email)$/.test(f) ? e = k.checkValidity && k.checkValidity() === !1 : e = k.value != l)), t[a[d]] = !!e;
  return t
  }("search tel url email datetime date month week time datetime-local number range color".split(" "))
  }
   
  var d = "2.6.1", e = {}, f = !0, g = b.documentElement, h = "modernizr", i = b.createElement(h), j = i.style, k = b.createElement("input"), l = ":)", m = {}.toString, n = " -webkit- -moz- -o- -ms- ".split(" "), o = "Webkit Moz O ms", p = o.split(" "), q = o.toLowerCase().split(" "), r = {svg: "http://www.w3.org/2000/svg"}, s = {}, t = {}, u = {}, v = [], w = v.slice, x, y = function (a, c, d, e) {
  var f, i, j, k = b.createElement("div"), l = b.body, m = l ? l : b.createElement("body");
  if (parseInt(d, 10))while (d--)j = b.createElement("div"), j.id = e ? e[d] : h + (d + 1), k.appendChild(j);
  return f = ["&#173;", '<style id="s', h, '">', a, "</style>"].join(""), k.id = h, (l ? k : m).innerHTML += f, m.appendChild(k), l || (m.style.background = "", g.appendChild(m)), i = c(k, a), l ? k.parentNode.removeChild(k) : m.parentNode.removeChild(m), !!i
  }, z = function (b) {
  var c = a.matchMedia || a.msMatchMedia;
  if (c)return c(b).matches;
  var d;
  return y("@media " + b + " { #" + h + " { position: absolute; } }", function (b) {
  d = (a.getComputedStyle ? getComputedStyle(b, null) : b.currentStyle)["position"] == "absolute"
  }), d
  }, A = function () {
  function d(d, e) {
  e = e || b.createElement(a[d] || "div"), d = "on" + d;
  var f = d in e;
  return f || (e.setAttribute || (e = b.createElement("div")), e.setAttribute && e.removeAttribute && (e.setAttribute(d, ""), f = F(e[d], "function"), F(e[d], "undefined") || (e[d] = c), e.removeAttribute(d))), e = null, f
  }
   
  var a = {select: "input", change: "input", submit: "form", reset: "form", error: "img", load: "img", abort: "img"};
  return d
  }(), B = {}.hasOwnProperty, C;
  !F(B, "undefined") && !F(B.call, "undefined") ? C = function (a, b) {
  return B.call(a, b)
  } : C = function (a, b) {
  return b in a && F(a.constructor.prototype[b], "undefined")
  }, Function.prototype.bind || (Function.prototype.bind = function (b) {
  var c = this;
  if (typeof c != "function")throw new TypeError;
  var d = w.call(arguments, 1), e = function () {
  if (this instanceof e) {
  var a = function () {
  };
  a.prototype = c.prototype;
  var f = new a, g = c.apply(f, d.concat(w.call(arguments)));
  return Object(g) === g ? g : f
  }
  return c.apply(b, d.concat(w.call(arguments)))
  };
  return e
  }), s.flexbox = function () {
  return J("flexWrap")
  }, s.canvas = function () {
  var a = b.createElement("canvas");
  return!!a.getContext && !!a.getContext("2d")
  }, s.canvastext = function () {
  return!!e.canvas && !!F(b.createElement("canvas").getContext("2d").fillText, "function")
  }, s.webgl = function () {
  return!!a.WebGLRenderingContext
  }, s.touch = function () {
  var c;
  return"ontouchstart"in a || a.DocumentTouch && b instanceof DocumentTouch ? c = !0 : y(["@media (", n.join("touch-enabled),("), h, ")", "{#modernizr{top:9px;position:absolute}}"].join(""), function (a) {
  c = a.offsetTop === 9
  }), c
  }, s.geolocation = function () {
  return"geolocation"in navigator
  }, s.postmessage = function () {
  return!!a.postMessage
  }, s.websqldatabase = function () {
  return!!a.openDatabase
  }, s.indexedDB = function () {
  return!!J("indexedDB", a)
  }, s.hashchange = function () {
  return A("hashchange", a) && (b.documentMode === c || b.documentMode > 7)
  }, s.history = function () {
  return!!a.history && !!history.pushState
  }, s.draganddrop = function () {
  var a = b.createElement("div");
  return"draggable"in a || "ondragstart"in a && "ondrop"in a
  }, s.websockets = function () {
  return"WebSocket"in a || "MozWebSocket"in a
  }, s.rgba = function () {
  return D("background-color:rgba(150,255,150,.5)"), G(j.backgroundColor, "rgba")
  }, s.hsla = function () {
  return D("background-color:hsla(120,40%,100%,.5)"), G(j.backgroundColor, "rgba") || G(j.backgroundColor, "hsla")
  }, s.multiplebgs = function () {
  return D("background:url(https://),url(https://),red url(https://)"), /(url\s*\(.*?){3}/.test(j.background)
  }, s.backgroundsize = function () {
  return J("backgroundSize")
  }, s.borderimage = function () {
  return J("borderImage")
  }, s.borderradius = function () {
  return J("borderRadius")
  }, s.boxshadow = function () {
  return J("boxShadow")
  }, s.textshadow = function () {
  return b.createElement("div").style.textShadow === ""
  }, s.opacity = function () {
  return E("opacity:.55"), /^0.55$/.test(j.opacity)
  }, s.cssanimations = function () {
  return J("animationName")
  }, s.csscolumns = function () {
  return J("columnCount")
  }, s.cssgradients = function () {
  var a = "background-image:", b = "gradient(linear,left top,right bottom,from(#9f9),to(white));", c = "linear-gradient(left top,#9f9, white);";
  return D((a + "-webkit- ".split(" ").join(b + a) + n.join(c + a)).slice(0, -a.length)), G(j.backgroundImage, "gradient")
  }, s.cssreflections = function () {
  return J("boxReflect")
  }, s.csstransforms = function () {
  return!!J("transform")
  }, s.csstransforms3d = function () {
  var a = !!J("perspective");
  return a && "webkitPerspective"in g.style && y("@media (transform-3d),(-webkit-transform-3d){#modernizr{left:9px;position:absolute;height:3px;}}", function (b, c) {
  a = b.offsetLeft === 9 && b.offsetHeight === 3
  }), a
  }, s.csstransitions = function () {
  return J("transition")
  }, s.fontface = function () {
  var a;
  return y('@font-face {font-family:"font";src:url("https://")}', function (c, d) {
  var e = b.getElementById("smodernizr"), f = e.sheet || e.styleSheet, g = f ? f.cssRules && f.cssRules[0] ? f.cssRules[0].cssText : f.cssText || "" : "";
  a = /src/i.test(g) && g.indexOf(d.split(" ")[0]) === 0
  }), a
  }, s.generatedcontent = function () {
  var a;
  return y(['#modernizr:after{content:"', l, '";visibility:hidden}'].join(""), function (b) {
  a = b.offsetHeight >= 1
  }), a
  }, s.video = function () {
  var a = b.createElement("video"), c = !1;
  try {
  if (c = !!a.canPlayType)c = new Boolean(c), c.ogg = a.canPlayType('video/ogg; codecs="theora"').replace(/^no$/, ""), c.h264 = a.canPlayType('video/mp4; codecs="avc1.42E01E"').replace(/^no$/, ""), c.webm = a.canPlayType('video/webm; codecs="vp8, vorbis"').replace(/^no$/, "")
  } catch (d) {
  }
  return c
  }, s.audio = function () {
  var a = b.createElement("audio"), c = !1;
  try {
  if (c = !!a.canPlayType)c = new Boolean(c), c.ogg = a.canPlayType('audio/ogg; codecs="vorbis"').replace(/^no$/, ""), c.mp3 = a.canPlayType("audio/mpeg;").replace(/^no$/, ""), c.wav = a.canPlayType('audio/wav; codecs="1"').replace(/^no$/, ""), c.m4a = (a.canPlayType("audio/x-m4a;") || a.canPlayType("audio/aac;")).replace(/^no$/, "")
  } catch (d) {
  }
  return c
  }, s.localstorage = function () {
  try {
  return localStorage.setItem(h, h), localStorage.removeItem(h), !0
  } catch (a) {
  return!1
  }
  }, s.sessionstorage = function () {
  try {
  return sessionStorage.setItem(h, h), sessionStorage.removeItem(h), !0
  } catch (a) {
  return!1
  }
  }, s.webworkers = function () {
  return!!a.Worker
  }, s.applicationcache = function () {
  return!!a.applicationCache
  }, s.svg = function () {
  return!!b.createElementNS && !!b.createElementNS(r.svg, "svg").createSVGRect
  }, s.inlinesvg = function () {
  var a = b.createElement("div");
  return a.innerHTML = "<svg/>", (a.firstChild && a.firstChild.namespaceURI) == r.svg
  }, s.smil = function () {
  return!!b.createElementNS && /SVGAnimate/.test(m.call(b.createElementNS(r.svg, "animate")))
  }, s.svgclippaths = function () {
  return!!b.createElementNS && /SVGClipPath/.test(m.call(b.createElementNS(r.svg, "clipPath")))
  };
  for (var L in s)C(s, L) && (x = L.toLowerCase(), e[x] = s[L](), v.push((e[x] ? "" : "no-") + x));
  return e.input || K(), e.addTest = function (a, b) {
  if (typeof a == "object")for (var d in a)C(a, d) && e.addTest(d, a[d]); else {
  a = a.toLowerCase();
  if (e[a] !== c)return e;
  b = typeof b == "function" ? b() : b, f && (g.className += " " + (b ? "" : "no-") + a), e[a] = b
  }
  return e
  }, D(""), i = k = null, function (a, b) {
  function k(a, b) {
  var c = a.createElement("p"), d = a.getElementsByTagName("head")[0] || a.documentElement;
  return c.innerHTML = "x<style>" + b + "</style>", d.insertBefore(c.lastChild, d.firstChild)
  }
   
  function l() {
  var a = r.elements;
  return typeof a == "string" ? a.split(" ") : a
  }
   
  function m(a) {
  var b = i[a[g]];
  return b || (b = {}, h++, a[g] = h, i[h] = b), b
  }
   
  function n(a, c, f) {
  c || (c = b);
  if (j)return c.createElement(a);
  f || (f = m(c));
  var g;
  return f.cache[a] ? g = f.cache[a].cloneNode() : e.test(a) ? g = (f.cache[a] = f.createElem(a)).cloneNode() : g = f.createElem(a), g.canHaveChildren && !d.test(a) ? f.frag.appendChild(g) : g
  }
   
  function o(a, c) {
  a || (a = b);
  if (j)return a.createDocumentFragment();
  c = c || m(a);
  var d = c.frag.cloneNode(), e = 0, f = l(), g = f.length;
  for (; e < g; e++)d.createElement(f[e]);
  return d
  }
   
  function p(a, b) {
  b.cache || (b.cache = {}, b.createElem = a.createElement, b.createFrag = a.createDocumentFragment, b.frag = b.createFrag()), a.createElement = function (c) {
  return r.shivMethods ? n(c, a, b) : b.createElem(c)
  }, a.createDocumentFragment = Function("h,f", "return function(){var n=f.cloneNode(),c=n.createElement;h.shivMethods&&(" + l().join().replace(/\w+/g, function (a) {
  return b.createElem(a), b.frag.createElement(a), 'c("' + a + '")'
  }) + ");return n}")(r, b.frag)
  }
   
  function q(a) {
  a || (a = b);
  var c = m(a);
  return r.shivCSS && !f && !c.hasCSS && (c.hasCSS = !!k(a, "article,aside,figcaption,figure,footer,header,hgroup,nav,section{display:block}mark{background:#FF0;color:#000}")), j || p(a, c), a
  }
   
  var c = a.html5 || {}, d = /^<|^(?:button|map|select|textarea|object|iframe|option|optgroup)$/i, e = /^<|^(?:a|b|button|code|div|fieldset|form|h1|h2|h3|h4|h5|h6|i|iframe|img|input|label|li|link|ol|option|p|param|q|script|select|span|strong|style|table|tbody|td|textarea|tfoot|th|thead|tr|ul)$/i, f, g = "_html5shiv", h = 0, i = {}, j;
  (function () {
  try {
  var a = b.createElement("a");
  a.innerHTML = "<xyz></xyz>", f = "hidden"in a, j = a.childNodes.length == 1 || function () {
  b.createElement("a");
  var a = b.createDocumentFragment();
  return typeof a.cloneNode == "undefined" || typeof a.createDocumentFragment == "undefined" || typeof a.createElement == "undefined"
  }()
  } catch (c) {
  f = !0, j = !0
  }
  })();
  var r = {elements: c.elements || "abbr article aside audio bdi canvas data datalist details figcaption figure footer header hgroup mark meter nav output progress section summary time video", shivCSS: c.shivCSS !== !1, supportsUnknownElements: j, shivMethods: c.shivMethods !== !1, type: "default", shivDocument: q, createElement: n, createDocumentFragment: o};
  a.html5 = r, q(b)
  }(this, b), e._version = d, e._prefixes = n, e._domPrefixes = q, e._cssomPrefixes = p, e.mq = z, e.hasEvent = A, e.testProp = function (a) {
  return H([a])
  }, e.testAllProps = J, e.testStyles = y, e.prefixed = function (a, b, c) {
  return b ? J(a, b, c) : J(a, "pfx")
  }, g.className = g.className.replace(/(^|\s)no-js(\s|$)/, "$1$2") + (f ? " js " + v.join(" ") : ""), e
  }(this, this.document), function (a, b, c) {
  function d(a) {
  return o.call(a) == "[object Function]"
  }
   
  function e(a) {
  return typeof a == "string"
  }
   
  function f() {
  }
   
  function g(a) {
  return!a || a == "loaded" || a == "complete" || a == "uninitialized"
  }
   
  function h() {
  var a = p.shift();
  q = 1, a ? a.t ? m(function () {
  (a.t == "c" ? B.injectCss : B.injectJs)(a.s, 0, a.a, a.x, a.e, 1)
  }, 0) : (a(), h()) : q = 0
  }
   
  function i(a, c, d, e, f, i, j) {
  function k(b) {
  if (!o && g(l.readyState) && (u.r = o = 1, !q && h(), l.onload = l.onreadystatechange = null, b)) {
  a != "img" && m(function () {
  t.removeChild(l)
  }, 50);
  for (var d in y[c])y[c].hasOwnProperty(d) && y[c][d].onload()
  }
  }
   
  var j = j || B.errorTimeout, l = {}, o = 0, r = 0, u = {t: d, s: c, e: f, a: i, x: j};
  y[c] === 1 && (r = 1, y[c] = [], l = b.createElement(a)), a == "object" ? l.data = c : (l.src = c, l.type = a), l.width = l.height = "0", l.onerror = l.onload = l.onreadystatechange = function () {
  k.call(this, r)
  }, p.splice(e, 0, u), a != "img" && (r || y[c] === 2 ? (t.insertBefore(l, s ? null : n), m(k, j)) : y[c].push(l))
  }
   
  function j(a, b, c, d, f) {
  return q = 0, b = b || "j", e(a) ? i(b == "c" ? v : u, a, b, this.i++, c, d, f) : (p.splice(this.i++, 0, a), p.length == 1 && h()), this
  }
   
  function k() {
  var a = B;
  return a.loader = {load: j, i: 0}, a
  }
   
  var l = b.documentElement, m = a.setTimeout, n = b.getElementsByTagName("script")[0], o = {}.toString, p = [], q = 0, r = "MozAppearance"in l.style, s = r && !!b.createRange().compareNode, t = s ? l : n.parentNode, l = a.opera && o.call(a.opera) == "[object Opera]", l = !!b.attachEvent && !l, u = r ? "object" : l ? "script" : "img", v = l ? "script" : u, w = Array.isArray || function (a) {
  return o.call(a) == "[object Array]"
  }, x = [], y = {}, z = {timeout: function (a, b) {
  return b.length && (a.timeout = b[0]), a
  }}, A, B;
  B = function (a) {
  function b(a) {
  var a = a.split("!"), b = x.length, c = a.pop(), d = a.length, c = {url: c, origUrl: c, prefixes: a}, e, f, g;
  for (f = 0; f < d; f++)g = a[f].split("="), (e = z[g.shift()]) && (c = e(c, g));
  for (f = 0; f < b; f++)c = x[f](c);
  return c
  }
   
  function g(a, e, f, g, i) {
  var j = b(a), l = j.autoCallback;
  j.url.split(".").pop().split("?").shift(), j.bypass || (e && (e = d(e) ? e : e[a] || e[g] || e[a.split("/").pop().split("?")[0]] || h), j.instead ? j.instead(a, e, f, g, i) : (y[j.url] ? j.noexec = !0 : y[j.url] = 1, f.load(j.url, j.forceCSS || !j.forceJS && "css" == j.url.split(".").pop().split("?").shift() ? "c" : c, j.noexec, j.attrs, j.timeout), (d(e) || d(l)) && f.load(function () {
  k(), e && e(j.origUrl, i, g), l && l(j.origUrl, i, g), y[j.url] = 2
  })))
  }
   
  function i(a, b) {
  function c(a, c) {
  if (a) {
  if (e(a))c || (j = function () {
  var a = [].slice.call(arguments);
  k.apply(this, a), l()
  }), g(a, j, b, 0, h); else if (Object(a) === a)for (n in m = function () {
  var b = 0, c;
  for (c in a)a.hasOwnProperty(c) && b++;
  return b
  }(), a)a.hasOwnProperty(n) && (!c && !--m && (d(j) ? j = function () {
  var a = [].slice.call(arguments);
  k.apply(this, a), l()
  } : j[n] = function (a) {
  return function () {
  var b = [].slice.call(arguments);
  a && a.apply(this, b), l()
  }
  }(k[n])), g(a[n], j, b, n, h))
  } else!c && l()
  }
   
  var h = !!a.test, i = a.load || a.both, j = a.callback || f, k = j, l = a.complete || f, m, n;
  c(h ? a.yep : a.nope, !!i), i && c(i)
  }
   
  var j, l, m = this.yepnope.loader;
  if (e(a))g(a, 0, m, 0); else if (w(a))for (j = 0; j < a.length; j++)l = a[j], e(l) ? g(l, 0, m, 0) : w(l) ? B(l) : Object(l) === l && i(l, m); else Object(a) === a && i(a, m)
  }, B.addPrefix = function (a, b) {
  z[a] = b
  }, B.addFilter = function (a) {
  x.push(a)
  }, B.errorTimeout = 1e4, b.readyState == null && b.addEventListener && (b.readyState = "loading", b.addEventListener("DOMContentLoaded", A = function () {
  b.removeEventListener("DOMContentLoaded", A, 0), b.readyState = "complete"
  }, 0)), a.yepnope = k(), a.yepnope.executeStack = h, a.yepnope.injectJs = function (a, c, d, e, i, j) {
  var k = b.createElement("script"), l, o, e = e || B.errorTimeout;
  k.src = a;
  for (o in d)k.setAttribute(o, d[o]);
  c = j ? h : c || f, k.onreadystatechange = k.onload = function () {
  !l && g(k.readyState) && (l = 1, c(), k.onload = k.onreadystatechange = null)
  }, m(function () {
  l || (l = 1, c(1))
  }, e), i ? k.onload() : n.parentNode.insertBefore(k, n)
  }, a.yepnope.injectCss = function (a, c, d, e, g, i) {
  var e = b.createElement("link"), j, c = i ? h : c || f;
  e.href = a, e.rel = "stylesheet", e.type = "text/css";
  for (j in d)e.setAttribute(j, d[j]);
  g || (n.parentNode.insertBefore(e, n), m(c, 0))
  }
  }(this, document), Modernizr.load = function () {
  yepnope.apply(window, [].slice.call(arguments, 0))
  };
   
<?xml version="1.0" encoding="UTF-8"?><project xmlns="http://www.netbeans.org/ns/project/1"> <?xml version="1.0" encoding="UTF-8"?>
  <project xmlns="http://www.netbeans.org/ns/project/1">
<type>org.netbeans.modules.php.project</type> <type>org.netbeans.modules.php.project</type>
<configuration> <configuration>
<data xmlns="http://www.netbeans.org/ns/php-project/1"> <data xmlns="http://www.netbeans.org/ns/php-project/1">
<name>scannr</name> <name>scannr</name>
</data> </data>
</configuration> </configuration>
</project> </project>
file:a/scannr.py -> file:b/scannr.py
import logging import logging
   
logging.basicConfig(level=logging.DEBUG, logging.basicConfig(level=logging.DEBUG,
format='%(asctime)s\t%(levelname)s\t%(message)s') format='%(asctime)s\t%(levelname)s\t%(message)s')
   
import snd import snd
import time import time
from datetime import date from datetime import date
import threading import threading
from pydispatch import dispatcher from pydispatch import dispatcher
import wave import wave
import serial import serial
#python -m serial.tools.miniterm -p COM20 -e -b 115200 --cr #python -m serial.tools.miniterm -p COM20 -e -b 115200 --cr
import psycopg2 import psycopg2
import csv import csv
import sys,os import sys
  import os
   
   
sys.path.insert(0, os.path.join(os.path.dirname(__file__) or '.', 'pynma')) sys.path.insert(0, os.path.join(os.path.dirname(__file__) or '.', 'pynma'))
import pynma import pynma
   
filename = "demo.wav" filename = "demo.wav"
  last_call = (None, None, None)
MIN_LENGTH = 90000 MIN_LENGTH = 90000
  lock = threading.RLock()
   
   
def worker(filename, length): def get_call():
  global lock
  with lock:
  ser.write("GLG\r")
  line = ser.readline() # read a '\n' terminated line
  print line
  reader = csv.reader([line])
  for row in reader:
  #GLG,40078,NFM,0,0,CanberraBlackMnt,AustralianCapita,SES Ops 1,1,0
  #,NONE,NONE,NONE
  if (row[0] != 'GLG' or row[1] == ''):
  print "uh oh"
  return (None, None, None)
  if (row[1] != ''):
  tgid = row[1]
  #nma.push("scannr", "ping", filename, "http://www.google.com")
  tgname = row[7]
  sitename = row[5]
  return (tgid, tgname, sitename)
   
   
  def log_recording(tgid, tgname, sitename, filename, length):
  cur = conn.cursor()
  cur.execute("INSERT INTO recordings \
  (tgid, tgname, sitename, filename, length)\
  VALUES (%s, %s, %s, %s, %s)", ( tgid, tgname, sitename, filename, length))
  conn.commit()
  cur.close()
   
   
  def tgid_worker():
  global last_call
  last_call = get_call()
   
   
  def save_worker(filename, length):
  global last_call
"""thread worker function """thread worker function
http://www.doughellmann.com/PyMOTW/threading/ http://www.doughellmann.com/PyMOTW/threading/
https://github.com/uskr/pynma https://github.com/uskr/pynma
ffmpeg -i 2012-09-29-1348911268.34-demo.wav -ar 8000 -ab 4.75k test.3gp ffmpeg -i 2012-09-29-1348911268.34-demo.wav -ar 8000 -ab 4.75k test.3gp
http://stackoverflow.com/questions/2559746/getting-error-while-converting-wav-to-amr-using-ffmpeg http://stackoverflow.com/questions/2559746/getting-error-while-converting-
  wav-to-amr-using-ffmpeg
""" """
print 'Worker for '+filename print 'Worker for ' + filename
ser.write("GLG\r") (oldtgid, oldtgname, oldsitename) = last_call
line = ser.readline() # read a '\n' terminated line (tgid, tgname, sitename) = get_call()
print line if oldtgid == tgid:
reader = csv.reader([line]) if tgid is None or tgid == '':
for row in reader: print filename + " has no TGID"
#GLG,40078,NFM,0,0,CanberraBlackMnt,AustralianCapita,SES Ops 1,1,0,NONE,NONE,NONE  
if (row[0] != 'GLG'):  
print "uh oh"  
if (row[1] != ''):  
tgid = row[1]  
#nma.push("scannr", "ping", filename, "http://www.google.com")  
tgname = row[7]  
sitename = row[5]  
"""http://initd.org/psycopg/docs/usage.html"""  
cur = conn.cursor()  
cur.execute("INSERT INTO recordings (filename,tgid,tgname,sitename,length) VALUES (%s, %s,%s, %s, %s)",(filename,tgid,tgname,sitename, length))  
conn.commit()  
cur.close()  
else: else:
print filename+" has no TGID" log_recording(tgid, tgname, sitename, filename, length)
  else:
  if tgid is None or tgid == '':
  print filename + " has no TGID"
  else:
  log_recording(tgid, tgname, sitename, filename, length)
  if oldtgid is None or oldtgid == '':
  print filename + " has no old TGID"
  else:
  log_recording(oldtgid, oldtgname, oldsitename, filename, length)
  return
   
return  
   
def filenameMaker(): def filenameMaker():
global filename global filename
filename = date.today().isoformat()+'-'+str(time.time())+'-demo.wav' filename = date.today().isoformat() + '-' + str(time.time()) + '-demo.wav'
   
def record_to_async_file():  
"Records from the microphone and outputs the resulting data to `path`" def do_record():
  #global filename
sample_width, data = snd.record() sample_width, data = snd.record()
print str(len(data)) thr = threading.Thread(target=record_to_async_file, args=(sample_width, data, filename))
  thr.start()
   
   
  def record_to_async_file(sample_width, data, filename):
  "Records from the microphone and outputs the resulting data to `filename`"
  print "Recording complete"
if len(data) > MIN_LENGTH: if len(data) > MIN_LENGTH:
data = snd.pack('<' + ('h'*len(data)), *data) print "Recording being saved..."
path = "./data/"+filename dispatcher.send(signal='FILE_CREATED', sender=filename, filename=filename, length=len(data))
dispatcher.send( signal='FILE_CREATED', sender=filename, filename=filename, length=len(data)) print str(len(data))
  data = snd.pack('<' + ('h' * len(data)), *data)
  path = "./data/" + filename
wf = wave.open(path, 'wb') wf = wave.open(path, 'wb')
wf.setnchannels(2) wf.setnchannels(2)
wf.setsampwidth(sample_width) wf.setsampwidth(sample_width)
wf.setframerate(snd.RATE) wf.setframerate(snd.RATE)
wf.writeframes(data) wf.writeframes(data)
wf.close() wf.close()
del wf del wf
print("done - result "+str(len(data))+" frames written to "+path) print("done - result " + str(len(data)) + " frames written to " + path)
del data del data
   
dispatcher.connect( filenameMaker, signal='SND_STARTED', sender=dispatcher.Any ) dispatcher.connect(filenameMaker, signal='SND_STARTED', sender=dispatcher.Any)
dispatcher.connect( worker, signal='FILE_CREATED', sender=dispatcher.Any ) dispatcher.connect(tgid_worker, signal='SND_STARTED', sender=dispatcher.Any)
  dispatcher.connect(save_worker, signal='FILE_CREATED', sender=dispatcher.Any)
   
print "Opening serial port..." print "Opening serial port..."
if sys.platform.startswith('darwin'): if sys.platform.startswith('darwin'):
ser = serial.Serial('/dev/tty.usbserial-FTB3VL83', 112500, timeout=1) ser = serial.Serial('/dev/tty.usbserial-FTB3VL83', 112500, timeout=1)
elif sys.platform.startswith('win32'): elif sys.platform.startswith('win32'):
ser = serial.Serial('COM20', 112500, timeout=1) ser = serial.Serial('COM20', 112500, timeout=1)
print "Loading notifymyandroid..." print "Loading notifymyandroid..."
nma = pynma.PyNMA( "a6f50f76119eda33befe4325b4b9e1dd25eef7bad2868e4f") nma = pynma.PyNMA("a6f50f76119eda33befe4325b4b9e1dd25eef7bad2868e4f")
print "Connecting database..." print "Connecting database..."
conn = psycopg2.connect("dbname=scannr user=postgres password=snmc") conn = psycopg2.connect("dbname=scannr user=postgres password=snmc")
print "Scannr started." print "Scannr started."
while True: while True:
print "ready to record again" print "ready to record again"
record_to_async_file() do_record()
ser.close() ser.close()
   
   
   
file:a/snd.py -> file:b/snd.py
""" Record a few seconds of audio and save to a WAVE file. """ Record a few seconds of audio and save to a WAVE file.
Based on http://stackoverflow.com/questions/892199/detect-record-audio-in-python/6743593#6743593 Based on http://stackoverflow.com/questions/892199/detect-record-audio-in-python/6743593#6743593
""" """
   
import pyaudio import pyaudio
import wave import wave
import sys import sys
import audioop # http://docs.python.org/library/audioop  
from os.path import exists  
from array import array from array import array
from struct import unpack, pack from struct import unpack, pack
import threading  
from pydispatch import dispatcher from pydispatch import dispatcher
   
THRESHOLD = 500 THRESHOLD = 500
CHUNK_SIZE = 1024 CHUNK_SIZE = 1024
FORMAT = pyaudio.paInt16 FORMAT = pyaudio.paInt16
RATE = 44100 RATE = 44100
if sys.platform.startswith('darwin'): if sys.platform.startswith('darwin'):
CHANNELS = 2 CHANNELS = 2
elif sys.platform.startswith('win32'): elif sys.platform.startswith('win32'):
CHANNELS = 1 CHANNELS = 1
   
MAX_SILENT = 60 MAX_SILENT = 80
   
def is_silent(L): def is_silent(L):
"Returns `True` if below the 'silent' threshold" "Returns `True` if below the 'silent' threshold"
"print max(L)" "print max(L)"
"print max(L) < THRESHOLD" "print max(L) < THRESHOLD"
return max(L) < THRESHOLD return max(L) < THRESHOLD
   
   
def normalize(L): def normalize(L):
"Average the volume out" "Average the volume out"
MAXIMUM = 16384 MAXIMUM = 16384
times = float(MAXIMUM)/max(abs(i) for i in L) times = float(MAXIMUM) / max(abs(i) for i in L)
   
LRtn = array('h') LRtn = array('h')
for i in L: for i in L:
LRtn.append(int(i*times)) LRtn.append(int(i * times))
return LRtn return LRtn
   
   
def trim(L): def trim(L):
"Trim the blank spots at the start and end" "Trim the blank spots at the start and end"
   
def _trim(L): def _trim(L):
snd_started = False snd_started = False
LRtn = array('h') LRtn = array('h')
   
for i in L: for i in L:
if not snd_started and abs(i)>THRESHOLD: if not snd_started and abs(i) > THRESHOLD:
snd_started = True snd_started = True
LRtn.append(i) LRtn.append(i)
   
elif snd_started: elif snd_started:
LRtn.append(i) LRtn.append(i)
return LRtn return LRtn
   
# Trim to the left # Trim to the left
L = _trim(L) L = _trim(L)
   
# Trim to the right # Trim to the right
L.reverse() L.reverse()
L = _trim(L) L = _trim(L)
L.reverse() L.reverse()
return L return L
   
   
def add_silence(L, seconds): def add_silence(L, seconds):
"Add silence to the start and end of `L` of length `seconds` (float)" "Add silence to the start and end of `L` of length `seconds` (float)"
LRtn = array('h', [0 for i in xrange(int(seconds*RATE))]) LRtn = array('h', [0 for i in xrange(int(seconds * RATE))])
LRtn.extend(L) LRtn.extend(L)
LRtn.extend([0 for i in xrange(int(seconds*RATE))]) LRtn.extend([0 for i in xrange(int(seconds * RATE))])
return LRtn return LRtn
   
   
def record(): def record():
""" """
Record a word or words from the microphone and Record a word or words from the microphone and
return the data as an array of signed shorts. return the data as an array of signed shorts.
   
Normalizes the audio, trims silence from the Normalizes the audio, trims silence from the
start and end, and pads with 0.5 seconds of start and end, and pads with 0.5 seconds of
blank sound to make sure VLC et al can play blank sound to make sure VLC et al can play
it without getting chopped off. it without getting chopped off.
""" """
p = pyaudio.PyAudio() p = pyaudio.PyAudio()
stream = p.open(format=FORMAT, channels=CHANNELS, rate=RATE, stream = p.open(format=FORMAT, channels=CHANNELS, rate=RATE,
input=True, input=True,
frames_per_buffer=CHUNK_SIZE) frames_per_buffer=CHUNK_SIZE)
   
num_silent = 0 num_silent = 0
snd_started = False snd_started = False
   
LRtn = array('h') LRtn = array('h')
   
while 1: while 1:
try: try:
data = stream.read(CHUNK_SIZE) data = stream.read(CHUNK_SIZE)
except IOError as ex: except IOError as ex:
if ex[1] != pyaudio.paInputOverflowed: if ex[1] != pyaudio.paInputOverflowed:
raise raise
data = '\x00' * CHUNK_SIZE data = '\x00' * CHUNK_SIZE
L = unpack('<' + ('h'*(len(data)/2)), data) # little endian, signed short L = unpack('<' + ('h' * (len(data) / 2)), data) # little endian, signed short
L = array('h', L) L = array('h', L)
   
silent = is_silent(L) silent = is_silent(L)
#print silent, num_silent, L[:10] #print silent, num_silent, L[:10]
   
if silent and snd_started: if silent and snd_started:
num_silent += 1 num_silent += 1
#print num_silent #print num_silent
elif not silent and not snd_started: elif not silent and not snd_started:
dispatcher.send( signal='SND_STARTED') dispatcher.send(signal='SND_STARTED')
snd_started = True snd_started = True
print snd_started print snd_started
  if snd_started and not silent:
  num_silent = 0
if snd_started: if snd_started:
LRtn.extend(L) LRtn.extend(L)
if snd_started and num_silent > MAX_SILENT: if snd_started and num_silent > MAX_SILENT:
break break
   
sample_width = p.get_sample_size(FORMAT) sample_width = p.get_sample_size(FORMAT)
stream.stop_stream() stream.stop_stream()
stream.close() stream.close()
p.terminate() p.terminate()
   
LRtn = normalize(LRtn) LRtn = normalize(LRtn)
LRtn = trim(LRtn) LRtn = trim(LRtn)
LRtn = add_silence(LRtn, 0.5) LRtn = add_silence(LRtn, 0.5)
return sample_width, LRtn return sample_width, LRtn
   
   
def record_to_file(path): def record_to_file(path):
"Records from the microphone and outputs the resulting data to `path`" "Records from the microphone and outputs the resulting data to `path`"
sample_width, data = record() sample_width, data = record()
data = pack('<' + ('h'*len(data)), *data) data = pack('<' + ('h' * len(data)), *data)
   
wf = wave.open(path, 'wb') wf = wave.open(path, 'wb')
wf.setnchannels(CHANNELS) wf.setnchannels(CHANNELS)
wf.setsampwidth(sample_width) wf.setsampwidth(sample_width)
wf.setframerate(RATE) wf.setframerate(RATE)
wf.writeframes(data) wf.writeframes(data)
wf.close() wf.close()
print("done - result written to "+path) print("done - result written to " + path)
del data del data
   
   
  if __name__ == '__main__':
  filename = 'demo.wav'
  record_to_file(filename)
  print("done - result written to " + filename)
   
   
if __name__ == '__main__':  
filename = 'demo.wav'  
record_to_file(filename)  
print("done - result written to "+filename)  
   
file:a/test.py -> file:b/test.py
"""PyAudio example: Record a few seconds of audio and save to a WAVE file.""" """PyAudio example: Record a few seconds of audio and save to a WAVE file."""
   
import pyaudio import pyaudio
import wave import wave
   
CHUNK = 1024 CHUNK = 1024
FORMAT = pyaudio.paInt16 FORMAT = pyaudio.paInt16
CHANNELS = 2 CHANNELS = 2
RATE = 44100 RATE = 44100
RECORD_SECONDS = 5 RECORD_SECONDS = 5
WAVE_OUTPUT_FILENAME = "output.wav" WAVE_OUTPUT_FILENAME = "output.wav"
   
p = pyaudio.PyAudio() p = pyaudio.PyAudio()
   
stream = p.open(format=FORMAT, stream = p.open(format=FORMAT,
channels=CHANNELS, channels=CHANNELS,
rate=RATE, rate=RATE,
input=True, input=True,
frames_per_buffer=CHUNK) frames_per_buffer=CHUNK)
   
print("* recording") print("* recording")
   
frames = [] frames = []
   
for i in range(0, int(RATE / CHUNK * RECORD_SECONDS)): for i in range(0, int(RATE / CHUNK * RECORD_SECONDS)):
data = stream.read(CHUNK) data = stream.read(CHUNK)
frames.append(data) frames.append(data)
   
print("* done recording") print("* done recording")
   
stream.stop_stream() stream.stop_stream()
stream.close() stream.close()
p.terminate() p.terminate()
   
wf = wave.open(WAVE_OUTPUT_FILENAME, 'wb') wf = wave.open(WAVE_OUTPUT_FILENAME, 'wb')
wf.setnchannels(CHANNELS) wf.setnchannels(CHANNELS)
wf.setsampwidth(p.get_sample_size(FORMAT)) wf.setsampwidth(p.get_sample_size(FORMAT))
wf.setframerate(RATE) wf.setframerate(RATE)
wf.writeframes(b''.join(frames)) wf.writeframes(b''.join(frames))
wf.close() wf.close()
   
file:a/test2.py -> file:b/test2.py
from os.path import exists from os.path import exists
from array import array from array import array
from struct import unpack, pack from struct import unpack, pack
   
import pyaudio import pyaudio
import wave import wave
   
THRESHOLD = 500 THRESHOLD = 500
CHUNK_SIZE = 1024 CHUNK_SIZE = 1024
FORMAT = pyaudio.paInt16 FORMAT = pyaudio.paInt16
RATE = 44100 RATE = 44100
   
def is_silent(L): def is_silent(L):
"Returns `True` if below the 'silent' threshold" "Returns `True` if below the 'silent' threshold"
return max(L) < THRESHOLD return max(L) < THRESHOLD
   
   
def normalize(L): def normalize(L):
"Average the volume out" "Average the volume out"
MAXIMUM = 16384 MAXIMUM = 16384
times = float(MAXIMUM)/max(abs(i) for i in L) times = float(MAXIMUM) / max(abs(i) for i in L)
   
LRtn = array('h') LRtn = array('h')
for i in L: for i in L:
LRtn.append(int(i*times)) LRtn.append(int(i * times))
return LRtn return LRtn
   
   
def trim(L): def trim(L):
"Trim the blank spots at the start and end" "Trim the blank spots at the start and end"
   
def _trim(L): def _trim(L):
snd_started = False snd_started = False
LRtn = array('h') LRtn = array('h')
   
for i in L: for i in L:
if not snd_started and abs(i)>THRESHOLD: if not snd_started and abs(i) > THRESHOLD:
snd_started = True snd_started = True
LRtn.append(i) LRtn.append(i)
   
elif snd_started: elif snd_started:
LRtn.append(i) LRtn.append(i)
return LRtn return LRtn
   
# Trim to the left # Trim to the left
L = _trim(L) L = _trim(L)
   
# Trim to the right # Trim to the right
L.reverse() L.reverse()
L = _trim(L) L = _trim(L)
L.reverse() L.reverse()
return L return L
   
   
def add_silence(L, seconds): def add_silence(L, seconds):
"Add silence to the start and end of `L` of length `seconds` (float)" "Add silence to the start and end of `L` of length `seconds` (float)"
LRtn = array('h', [0 for i in xrange(int(seconds*RATE))]) LRtn = array('h', [0 for i in xrange(int(seconds * RATE))])
LRtn.extend(L) LRtn.extend(L)
LRtn.extend([0 for i in xrange(int(seconds*RATE))]) LRtn.extend([0 for i in xrange(int(seconds * RATE))])
return LRtn return LRtn
   
   
def record(): def record():
""" """
Record a word or words from the microphone and Record a word or words from the microphone and
return the data as an array of signed shorts. return the data as an array of signed shorts.
   
Normalizes the audio, trims silence from the Normalizes the audio, trims silence from the
start and end, and pads with 0.5 seconds of start and end, and pads with 0.5 seconds of
blank sound to make sure VLC et al can play blank sound to make sure VLC et al can play
it without getting chopped off. it without getting chopped off.
""" """
p = pyaudio.PyAudio() p = pyaudio.PyAudio()
stream = p.open(format=FORMAT, channels=2, rate=RATE, stream = p.open(format=FORMAT, channels=2, rate=RATE,
input=True, input=True,
frames_per_buffer=CHUNK_SIZE) frames_per_buffer=CHUNK_SIZE)
   
num_silent = 0 num_silent = 0
snd_started = False snd_started = False
   
LRtn = array('h') LRtn = array('h')
   
while 1: while 1:
data = stream.read(CHUNK_SIZE) data = stream.read(CHUNK_SIZE)
L = unpack('<' + ('h'*(len(data)/2)), data) # little endian, signed short L = unpack('<' + ('h' * (len(data) / 2)), data) # little endian, signed short
L = array('h', L) L = array('h', L)
LRtn.extend(L) LRtn.extend(L)
   
silent = is_silent(L) silent = is_silent(L)
#print silent, num_silent, L[:10] #print silent, num_silent, L[:10]
   
if silent and snd_started: if silent and snd_started:
num_silent += 1 num_silent += 1
elif not silent and not snd_started: elif not silent and not snd_started:
snd_started = True snd_started = True
   
if snd_started and num_silent > 30: if snd_started and num_silent > 30:
break break
   
sample_width = p.get_sample_size(FORMAT) sample_width = p.get_sample_size(FORMAT)
stream.stop_stream() stream.stop_stream()
stream.close() stream.close()
p.terminate() p.terminate()
   
LRtn = normalize(LRtn) LRtn = normalize(LRtn)
LRtn = trim(LRtn) LRtn = trim(LRtn)
LRtn = add_silence(LRtn, 0.5) LRtn = add_silence(LRtn, 0.5)
return sample_width, LRtn return sample_width, LRtn
   
   
def record_to_file(path): def record_to_file(path):
"Records from the microphone and outputs the resulting data to `path`" "Records from the microphone and outputs the resulting data to `path`"
sample_width, data = record() sample_width, data = record()
data = pack('<' + ('h'*len(data)), *data) data = pack('<' + ('h' * len(data)), *data)
   
wf = wave.open(path, 'wb') wf = wave.open(path, 'wb')
wf.setnchannels(2) wf.setnchannels(2)
wf.setsampwidth(sample_width) wf.setsampwidth(sample_width)
wf.setframerate(RATE) wf.setframerate(RATE)
wf.writeframes(data) wf.writeframes(data)
wf.close() wf.close()
   
if __name__ == '__main__': if __name__ == '__main__':
print("please speak a word into the microphone") print("please speak a word into the microphone")
record_to_file('demo.wav') record_to_file('demo.wav')
print("done - result written to demo.wav") print("done - result written to demo.wav")
   
<?php <?php
// http://www.unitrunker.com/logs.html // http://www.unitrunker.com/logs.html
/* /*
Timestamp as YYYYMMDDHHMMSS Timestamp as YYYYMMDDHHMMSS
Site number in unformatted decimal Site number in unformatted decimal
Action: Call, Leaves, Joins, Login, Logout, Drop, Add, Busy, Deny, Kill Action: Call, Leaves, Joins, Login, Logout, Drop, Add, Busy, Deny, Kill
Source type I or G Source type I or G
Source ID in unformatted decimal Source ID in unformatted decimal
Target type I or G Target type I or G
Target ID in unformatted decimal Target ID in unformatted decimal
Channel number in unformatted decimal Channel number in unformatted decimal
Call Type*/ Call Type*/
$row = 0; $row = 0;
echo "<table>"; echo "<table>";
if (($handle = fopen("C:\Users\Madoka\AppData\Roaming\UniTrunker\S00000001\UniTrunker-20120411.LOG", "r")) !== FALSE) { if (($handle = fopen("C:\Users\Madoka\AppData\Roaming\UniTrunker\S00000001\UniTrunker-20120411.LOG", "r")) !== FALSE) {
while (($data = fgetcsv($handle, 1000, ",")) !== FALSE) { while (($data = fgetcsv($handle, 1000, ",")) !== FALSE) {
   
if ($row > 0 && count($data) == 9) { if ($row > 0 && count($data) == 9) {
   
echo "<tr>"; echo "<tr>";
for ($c=0; $c < count($data); $c++) { for ($c = 0; $c < count($data); $c++) {
echo '<td>'.$data[$c] . "</td>\n"; echo '<td>' . $data[$c] . "</td>\n";
} }
echo "</tr>"; echo "</tr>";
} }
$row++; $row++;
} }
fclose($handle); fclose($handle);
} }
echo "</table>"; echo "</table>";
?> ?>
<?php <?php
include('common.inc.php'); include('common.inc.php');
$tgid = 44028; $tgid = 44028;
  $from = (isset($_REQUEST['from']) ? $_REQUEST['from'] : strtotime("2012-09-12"));
  $to = (isset($_REQUEST['to']) ? $_REQUEST['to'] : strtotime("2012-12-12"));
include_header("fdds"); include_header("fdds");
  $sth = $conn->prepare('select distinct date_trunc(\'day\', call_timestamp) as rdate from recordings order by rdate');
   
  $sth->execute();
  foreach ($sth->fetchAll() as $row) {
  echo '<a href="?from=' . strtotime($row['rdate']) . '&amp;to=' . strtotime($row['rdate'] . ' +1 day') . '">' . $row['rdate'] . '</a> <br>';
  }
   
?> ?>
<div class="span12"> <div class="span12">
   
<table width="100%" height="775px"><tr><td valign="middle"><span class="arrow-w" style="font-size:2em;">&lt;</span></td><td width="95%"><div id="placeholder" style="width:100%;height:575px;"></div></td><td valign="middle"><span class="arrow-e" style="font-size:2em;">&gt;</span></td></tr></table> <table width="100%" height="775px">
<script> <tr>
  <td valign="middle"><span class="arrow-w" style="font-size:2em;">&lt;</span></td>
  <td width="95%">
  <div id="placeholder" style="width:100%;height:575px;"></div>
  </td>
  <td valign="middle"><span class="arrow-e" style="font-size:2em;">&gt;</span></td>
  </tr>
  </table>
  <script>
var data = []; var data = [];
var plot; var plot;
var options = { var options = {
lines: { show: true }, lines: { show: true },
points: { show: true }, points: { show: true },
xaxis : { xaxis: {
mode : 'time', mode: 'time',
labelsAngle : 45 labelsAngle: 45
}, },
selection : { mode : 'x', fps : 30 }, selection: { mode: 'x', fps: 30 },
series: { series: {
lines: { show: true }, lines: { show: true },
points: { show: true } points: { show: true }
}, },
mouse : { mouse: {
track : true, track: true,
relative : true relative: true
} }
}; };
$(function () { $(function () {
// graph // graph
   
   
var placeholder = document.getElementById("placeholder");  
   
drawGraph (options);  
  var placeholder = document.getElementById("placeholder");
   
  drawGraph(options);
   
// Hook into the 'flotr:select' event. // Hook into the 'flotr:select' event.
Flotr.EventAdapter.observe(placeholder, 'flotr:select', function (area) { Flotr.EventAdapter.observe(placeholder, 'flotr:select', function (area) {
   
// Draw graph with new area // Draw graph with new area
graph = drawGraph({ graph = drawGraph({
xaxis: {min:area.x1, max:area.x2, mode : 'time', labelsAngle : 45}, xaxis: {min: area.x1, max: area.x2, mode: 'time', labelsAngle: 45},
yaxis: {min:area.y1, max:area.y2} yaxis: {min: area.y1, max: area.y2}
}); });
}); });
   
// When graph is clicked, draw the graph with default area. // When graph is clicked, draw the graph with default area.
Flotr.EventAdapter.observe(placeholder, 'flotr:click', function () { drawGraph(); }); Flotr.EventAdapter.observe(placeholder, 'flotr:click', function () {
  drawGraph();
  });
   
   
getData('<?php echo $tgid; ?>','<?php echo strtotime("10/09/2012") ?>','<?php echo strtotime("10/11/2012") ?>'); getData('<?php echo $tgid; ?>', '<?php echo $from ?>', '<?php echo $to ?>');
   
}); });
   
// Draw graph with default options, overwriting with passed options // Draw graph with default options, overwriting with passed options
function drawGraph (opts) { function drawGraph(opts) {
   
// Clone the options, so the 'options' variable always keeps intact. // Clone the options, so the 'options' variable always keeps intact.
var o = Flotr._.extend(Flotr._.clone(options), opts || {}); var o = Flotr._.extend(Flotr._.clone(options), opts || {});
   
// Return a new graph. // Return a new graph.
return Flotr.draw( return Flotr.draw(
placeholder, placeholder,
data, data,
o o
); );
} }
   
   
function onDataReceived(series) { function onDataReceived(series) {
data =[] data = []
for (var key in series.data) { for (var key in series.data) {
data[data.length] = {label: key, data: series.data[key]}; data[data.length] = {label: key, data: series.data[key]};
  }
  drawGraph(options);
} }
drawGraph (options); function getData(sensorID, from, to) {
} $.ajax({
function getData(sensorID,from,to) { url: "<?php echo $basePath; ?>calls.json.php?action=graphcount&tgid=" + sensorID + "&from=" + from + "&to=" + to,
$.ajax({ method: 'GET',
url: "<?php echo $basePath; ?>calls.json.php?action=graphcount&tgid="+sensorID+"&from="+from+"&to="+to, dataType: 'json',
method: 'GET', success: onDataReceived
dataType: 'json', });
success: onDataReceived }
});  
}  
   
   
  </script>
   
</script>  
</div> </div>
<?php <?php
include_footer(); include_footer();
?> ?>