From a6331f7fedf088c11ddb9c03d0916b2573ad3187 Mon Sep 17 00:00:00 2001 From: Rod Wright Date: Sat, 14 Feb 2026 11:07:25 -0500 Subject: [PATCH] Initial commit as release 1.1.0 --- CHANGELOG | 43 + INSTALL | 28 + RPiSetup/brlaser-master/CMakeLists.txt | 126 + RPiSetup/brlaser-master/COPYING | 339 + RPiSetup/brlaser-master/ChangeLog | 9 + RPiSetup/brlaser-master/README.md | 85 + RPiSetup/brlaser-master/brlaser.drv.in | 143 + RPiSetup/brlaser-master/config.h.in | 2 + RPiSetup/brlaser-master/src/block.h | 68 + RPiSetup/brlaser-master/src/brdecode.cc | 225 + RPiSetup/brlaser-master/src/debug.cc | 107 + RPiSetup/brlaser-master/src/debug.h | 25 + RPiSetup/brlaser-master/src/job.cc | 118 + RPiSetup/brlaser-master/src/job.h | 68 + RPiSetup/brlaser-master/src/line.cc | 191 + RPiSetup/brlaser-master/src/line.h | 31 + RPiSetup/brlaser-master/src/main.cc | 205 + RPiSetup/brlaser-master/test/lest.hpp | 199 + RPiSetup/brlaser-master/test/tempfile.h | 54 + RPiSetup/brlaser-master/test/test_block.cc | 91 + RPiSetup/brlaser-master/test/test_lest.cc | 291 + RPiSetup/brlaser-master/test/test_line.cc | 150 + .../etc/apache2/conf-available/opendrs.conf | 10 + .../etc/apache2/mods-available/alias.conf | 24 + .../apache2/sites-available/000-default.conf | 31 + RPiSetup/etc/apt/sources.list.d/webmin.list | 1 + RPiSetup/etc/cups/cupsd.conf | 146 + RPiSetup/etc/drs.conf | 2 + .../lib/systemd/system/getty@tty1.service | 53 + RPiSetup/server-setup.sh | 84 + RPiSetup/terminal-setup.sh | 123 + RPiSetup/usr/local/bin/cloneuser | 18 + RPiSetup/usr/local/bin/rpi-clone | 1642 ++ RPiSetup/usr/local/bin/rpi-clone-setup | 144 + RPiSetup/usr/local/bin/startdrs.sh | 37 + RPiSetup/usr/local/bin/updatedrs | 5 + RaspberryPiSetup.txt | 43 + boilerplate.php | 84 + db.php.template | 42 + distfiles/CHANGELOG | 43 + distfiles/INSTALL | 28 + distfiles/LICENSE | 341 + distfiles/about.php | 139 + distfiles/colorpicker/css/screen.css | 149 + distfiles/colorpicker/demo.htm | 196 + distfiles/colorpicker/img/bullet.gif | Bin 0 -> 172 bytes distfiles/colorpicker/img/greybg.png | Bin 0 -> 197 bytes distfiles/colorpicker/img/header.gif | Bin 0 -> 203 bytes distfiles/colorpicker/img/hline.png | Bin 0 -> 416 bytes distfiles/colorpicker/img/hue.png | Bin 0 -> 1344 bytes distfiles/colorpicker/img/navHover2.png | Bin 0 -> 875 bytes distfiles/colorpicker/img/pickerbg.png | Bin 0 -> 12421 bytes distfiles/colorpicker/img/qbottom.png | Bin 0 -> 1351 bytes distfiles/colorpicker/img/qmiddle.png | Bin 0 -> 385 bytes distfiles/colorpicker/img/qtop.png | Bin 0 -> 2030 bytes distfiles/colorpicker/img/select.gif | Bin 0 -> 78 bytes distfiles/colorpicker/img/select.png | Bin 0 -> 192 bytes distfiles/colorpicker/img/vline.png | Bin 0 -> 481 bytes distfiles/colorpicker/js/YAHOO.js | 60 + distfiles/colorpicker/js/animation.js | 39 + distfiles/colorpicker/js/color.js | 101 + distfiles/colorpicker/js/ddcolorposter.js | 43 + distfiles/colorpicker/js/dom.js | 26 + distfiles/colorpicker/js/dragdrop.js | 1 + distfiles/colorpicker/js/event.js | 1 + distfiles/colorpicker/js/key.js | 34 + distfiles/colorpicker/js/log.js | 89 + distfiles/colorpicker/js/slider.js | 1 + distfiles/common.php | 810 + distfiles/config.php | 480 + distfiles/create.php | 238 + distfiles/dbadmin.php | 1096 + distfiles/gpl.php | 431 + distfiles/groups.php | 896 + distfiles/icons/block.png | Bin 0 -> 630 bytes distfiles/icons/calendar.gif | Bin 0 -> 258 bytes distfiles/icons/chain--arrow.png | Bin 0 -> 584 bytes distfiles/icons/chain--plus.png | Bin 0 -> 586 bytes distfiles/icons/control-180.png | Bin 0 -> 458 bytes distfiles/icons/control-double-180.png | Bin 0 -> 551 bytes distfiles/icons/control-double.png | Bin 0 -> 564 bytes distfiles/icons/control.png | Bin 0 -> 470 bytes distfiles/icons/cross.png | Bin 0 -> 544 bytes distfiles/icons/exclamation-red.png | Bin 0 -> 696 bytes distfiles/icons/magnifier--arrow.png | Bin 0 -> 811 bytes distfiles/icons/minus-button.png | Bin 0 -> 479 bytes distfiles/icons/minus-white.png | Bin 0 -> 682 bytes distfiles/icons/node-magnifier.png | Bin 0 -> 811 bytes distfiles/icons/node.png | Bin 0 -> 548 bytes distfiles/icons/notebook--pencil.png | Bin 0 -> 757 bytes distfiles/icons/plus-button.png | Bin 0 -> 583 bytes distfiles/icons/plus-white.png | Bin 0 -> 703 bytes distfiles/icons/plus.png | Bin 0 -> 521 bytes distfiles/icons/question-white.png | Bin 0 -> 761 bytes distfiles/icons/question.png | Bin 0 -> 766 bytes distfiles/icons/sym-dash.png | Bin 0 -> 562 bytes distfiles/icons/sym-diag.png | Bin 0 -> 592 bytes distfiles/icons/sym-x.png | Bin 0 -> 481 bytes distfiles/icons/tick.png | Bin 0 -> 634 bytes distfiles/images/Kamiyamane.png | Bin 0 -> 10223 bytes distfiles/images/apache_pb.gif | Bin 0 -> 1127 bytes distfiles/images/color-palette_1.png | Bin 0 -> 1062 bytes distfiles/images/linux.png | Bin 0 -> 2044 bytes distfiles/images/mariadb-badge-180x60.png | Bin 0 -> 43451 bytes distfiles/images/php-small-trans-dark.gif | Bin 0 -> 1363 bytes .../jquery-ui-1.12.1.opendrs/AUTHORS.txt | 333 + .../jquery-ui-1.12.1.opendrs/LICENSE.txt | 43 + .../external/jquery/jquery.js | 11008 +++++++++ .../images/ui-bg_glass_40_ffc73d_1x400.png | Bin 0 -> 316 bytes .../ui-bg_gloss-wave_25_333333_500x100.png | Bin 0 -> 3816 bytes .../ui-bg_highlight-soft_80_eeeeee_1x100.png | Bin 0 -> 276 bytes .../ui-bg_inset-soft_30_afe4e3_1x100.png | Bin 0 -> 338 bytes .../images/ui-icons_4b8e0b_256x240.png | Bin 0 -> 4549 bytes .../images/ui-icons_a83300_256x240.png | Bin 0 -> 4549 bytes .../images/ui-icons_cccccc_256x240.png | Bin 0 -> 6975 bytes .../images/ui-icons_ffffff_256x240.png | Bin 0 -> 6299 bytes distfiles/jquery-ui-1.12.1.opendrs/index.html | 559 + .../jquery-ui-1.12.1.opendrs/jquery-ui.css | 1312 ++ .../jquery-ui-1.12.1.opendrs/jquery-ui.js | 18706 ++++++++++++++++ .../jquery-ui.min.css | 7 + .../jquery-ui-1.12.1.opendrs/jquery-ui.min.js | 13 + .../jquery-ui.structure.css | 886 + .../jquery-ui.structure.min.css | 5 + .../jquery-ui.theme.css | 443 + .../jquery-ui.theme.min.css | 5 + .../jquery.multiselect.css | 26 + .../jquery.multiselect.js | 986 + .../jquery.multiselect.min.js | 21 + .../jquery-ui-1.12.1.opendrs/package.json | 74 + .../jquery-ui-1.12.1.opendrs/spectrum.css | 507 + .../jquery-ui-1.12.1.opendrs/spectrum.js | 2255 ++ distfiles/login.php | 122 + distfiles/profile.php | 331 + distfiles/search.php | 785 + distfiles/settings.php | 51 + distfiles/writeupdetail.php | 536 + distfiles/writeups.php | 422 + install.sh | 272 + opendrs-initial.sql | 316 + opendrs-update.sh | 8 + opendrs-update.sql | 22 + 141 files changed, 50383 insertions(+) create mode 100644 CHANGELOG create mode 100644 INSTALL create mode 100644 RPiSetup/brlaser-master/CMakeLists.txt create mode 100644 RPiSetup/brlaser-master/COPYING create mode 100644 RPiSetup/brlaser-master/ChangeLog create mode 100644 RPiSetup/brlaser-master/README.md create mode 100644 RPiSetup/brlaser-master/brlaser.drv.in create mode 100644 RPiSetup/brlaser-master/config.h.in create mode 100644 RPiSetup/brlaser-master/src/block.h create mode 100644 RPiSetup/brlaser-master/src/brdecode.cc create mode 100644 RPiSetup/brlaser-master/src/debug.cc create mode 100644 RPiSetup/brlaser-master/src/debug.h create mode 100644 RPiSetup/brlaser-master/src/job.cc create mode 100644 RPiSetup/brlaser-master/src/job.h create mode 100644 RPiSetup/brlaser-master/src/line.cc create mode 100644 RPiSetup/brlaser-master/src/line.h create mode 100644 RPiSetup/brlaser-master/src/main.cc create mode 100644 RPiSetup/brlaser-master/test/lest.hpp create mode 100644 RPiSetup/brlaser-master/test/tempfile.h create mode 100644 RPiSetup/brlaser-master/test/test_block.cc create mode 100644 RPiSetup/brlaser-master/test/test_lest.cc create mode 100644 RPiSetup/brlaser-master/test/test_line.cc create mode 100644 RPiSetup/etc/apache2/conf-available/opendrs.conf create mode 100644 RPiSetup/etc/apache2/mods-available/alias.conf create mode 100644 RPiSetup/etc/apache2/sites-available/000-default.conf create mode 100644 RPiSetup/etc/apt/sources.list.d/webmin.list create mode 100644 RPiSetup/etc/cups/cupsd.conf create mode 100644 RPiSetup/etc/drs.conf create mode 100644 RPiSetup/lib/systemd/system/getty@tty1.service create mode 100644 RPiSetup/server-setup.sh create mode 100644 RPiSetup/terminal-setup.sh create mode 100644 RPiSetup/usr/local/bin/cloneuser create mode 100644 RPiSetup/usr/local/bin/rpi-clone create mode 100644 RPiSetup/usr/local/bin/rpi-clone-setup create mode 100644 RPiSetup/usr/local/bin/startdrs.sh create mode 100644 RPiSetup/usr/local/bin/updatedrs create mode 100644 RaspberryPiSetup.txt create mode 100644 boilerplate.php create mode 100644 db.php.template create mode 100644 distfiles/CHANGELOG create mode 100644 distfiles/INSTALL create mode 100644 distfiles/LICENSE create mode 100644 distfiles/about.php create mode 100644 distfiles/colorpicker/css/screen.css create mode 100644 distfiles/colorpicker/demo.htm create mode 100644 distfiles/colorpicker/img/bullet.gif create mode 100644 distfiles/colorpicker/img/greybg.png create mode 100644 distfiles/colorpicker/img/header.gif create mode 100644 distfiles/colorpicker/img/hline.png create mode 100644 distfiles/colorpicker/img/hue.png create mode 100644 distfiles/colorpicker/img/navHover2.png create mode 100644 distfiles/colorpicker/img/pickerbg.png create mode 100644 distfiles/colorpicker/img/qbottom.png create mode 100644 distfiles/colorpicker/img/qmiddle.png create mode 100644 distfiles/colorpicker/img/qtop.png create mode 100644 distfiles/colorpicker/img/select.gif create mode 100644 distfiles/colorpicker/img/select.png create mode 100644 distfiles/colorpicker/img/vline.png create mode 100644 distfiles/colorpicker/js/YAHOO.js create mode 100644 distfiles/colorpicker/js/animation.js create mode 100644 distfiles/colorpicker/js/color.js create mode 100644 distfiles/colorpicker/js/ddcolorposter.js create mode 100644 distfiles/colorpicker/js/dom.js create mode 100644 distfiles/colorpicker/js/dragdrop.js create mode 100644 distfiles/colorpicker/js/event.js create mode 100644 distfiles/colorpicker/js/key.js create mode 100644 distfiles/colorpicker/js/log.js create mode 100644 distfiles/colorpicker/js/slider.js create mode 100644 distfiles/common.php create mode 100644 distfiles/config.php create mode 100644 distfiles/create.php create mode 100644 distfiles/dbadmin.php create mode 100644 distfiles/gpl.php create mode 100644 distfiles/groups.php create mode 100644 distfiles/icons/block.png create mode 100644 distfiles/icons/calendar.gif create mode 100644 distfiles/icons/chain--arrow.png create mode 100644 distfiles/icons/chain--plus.png create mode 100644 distfiles/icons/control-180.png create mode 100644 distfiles/icons/control-double-180.png create mode 100644 distfiles/icons/control-double.png create mode 100644 distfiles/icons/control.png create mode 100644 distfiles/icons/cross.png create mode 100644 distfiles/icons/exclamation-red.png create mode 100644 distfiles/icons/magnifier--arrow.png create mode 100644 distfiles/icons/minus-button.png create mode 100644 distfiles/icons/minus-white.png create mode 100644 distfiles/icons/node-magnifier.png create mode 100644 distfiles/icons/node.png create mode 100644 distfiles/icons/notebook--pencil.png create mode 100644 distfiles/icons/plus-button.png create mode 100644 distfiles/icons/plus-white.png create mode 100644 distfiles/icons/plus.png create mode 100644 distfiles/icons/question-white.png create mode 100644 distfiles/icons/question.png create mode 100644 distfiles/icons/sym-dash.png create mode 100644 distfiles/icons/sym-diag.png create mode 100644 distfiles/icons/sym-x.png create mode 100644 distfiles/icons/tick.png create mode 100644 distfiles/images/Kamiyamane.png create mode 100644 distfiles/images/apache_pb.gif create mode 100644 distfiles/images/color-palette_1.png create mode 100644 distfiles/images/linux.png create mode 100644 distfiles/images/mariadb-badge-180x60.png create mode 100644 distfiles/images/php-small-trans-dark.gif create mode 100644 distfiles/jquery-ui-1.12.1.opendrs/AUTHORS.txt create mode 100644 distfiles/jquery-ui-1.12.1.opendrs/LICENSE.txt create mode 100644 distfiles/jquery-ui-1.12.1.opendrs/external/jquery/jquery.js create mode 100644 distfiles/jquery-ui-1.12.1.opendrs/images/ui-bg_glass_40_ffc73d_1x400.png create mode 100644 distfiles/jquery-ui-1.12.1.opendrs/images/ui-bg_gloss-wave_25_333333_500x100.png create mode 100644 distfiles/jquery-ui-1.12.1.opendrs/images/ui-bg_highlight-soft_80_eeeeee_1x100.png create mode 100644 distfiles/jquery-ui-1.12.1.opendrs/images/ui-bg_inset-soft_30_afe4e3_1x100.png create mode 100644 distfiles/jquery-ui-1.12.1.opendrs/images/ui-icons_4b8e0b_256x240.png create mode 100644 distfiles/jquery-ui-1.12.1.opendrs/images/ui-icons_a83300_256x240.png create mode 100644 distfiles/jquery-ui-1.12.1.opendrs/images/ui-icons_cccccc_256x240.png create mode 100644 distfiles/jquery-ui-1.12.1.opendrs/images/ui-icons_ffffff_256x240.png create mode 100644 distfiles/jquery-ui-1.12.1.opendrs/index.html create mode 100644 distfiles/jquery-ui-1.12.1.opendrs/jquery-ui.css create mode 100644 distfiles/jquery-ui-1.12.1.opendrs/jquery-ui.js create mode 100644 distfiles/jquery-ui-1.12.1.opendrs/jquery-ui.min.css create mode 100644 distfiles/jquery-ui-1.12.1.opendrs/jquery-ui.min.js create mode 100644 distfiles/jquery-ui-1.12.1.opendrs/jquery-ui.structure.css create mode 100644 distfiles/jquery-ui-1.12.1.opendrs/jquery-ui.structure.min.css create mode 100644 distfiles/jquery-ui-1.12.1.opendrs/jquery-ui.theme.css create mode 100644 distfiles/jquery-ui-1.12.1.opendrs/jquery-ui.theme.min.css create mode 100644 distfiles/jquery-ui-1.12.1.opendrs/jquery.multiselect.css create mode 100644 distfiles/jquery-ui-1.12.1.opendrs/jquery.multiselect.js create mode 100644 distfiles/jquery-ui-1.12.1.opendrs/jquery.multiselect.min.js create mode 100644 distfiles/jquery-ui-1.12.1.opendrs/package.json create mode 100644 distfiles/jquery-ui-1.12.1.opendrs/spectrum.css create mode 100644 distfiles/jquery-ui-1.12.1.opendrs/spectrum.js create mode 100644 distfiles/login.php create mode 100644 distfiles/profile.php create mode 100644 distfiles/search.php create mode 100644 distfiles/settings.php create mode 100644 distfiles/writeupdetail.php create mode 100644 distfiles/writeups.php create mode 100644 install.sh create mode 100644 opendrs-initial.sql create mode 100644 opendrs-update.sh create mode 100644 opendrs-update.sql diff --git a/CHANGELOG b/CHANGELOG new file mode 100644 index 0000000..00d79cb --- /dev/null +++ b/CHANGELOG @@ -0,0 +1,43 @@ + OpenDRS - Online Discrepancy Reporting System + Copyright (C) 2018 Rod Wright + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + +Changelog + +1.0.0 - 2018-03-13 + - Initial release as OpenMTS. + +1.1.0 - 2018-05-10 + - Name change from OpenMTS to OpenDRS. + - Added "Go Back" button to left side menu to make up for loss of browser + back button in terminal mode. + - Added configurable help text for period effective selection. + - Changed Sim period effective from selection to yes/no radio buttons. + - Writeup groups page shows collapsed list of groups. Click the expand icon + to expand each group. + - Fixed bug where entries made on stale create page had inaccurate date/time. + - For logged in users, added the ability to select writeups directly on the + View All Writeups page and assign/create a group. + - On the Create New Writeup page, removed the default selections for Device, + Subsystem, and Period dropdowns, forcing user to make a selection. + - Added a Today button to the View All Writeups page + - Changed the Period dropdowns on the Create, Search, and Group pages to show + the times instead of the period numbers. Period numbers are still shown + in the results tables with mouse hover showing times. + - Moved all installation/upgrade functions to the install.sh script. + + diff --git a/INSTALL b/INSTALL new file mode 100644 index 0000000..bff829e --- /dev/null +++ b/INSTALL @@ -0,0 +1,28 @@ +Installation or upgrade of OpenDRS is straightforward. + +1. Unpack +Unpack the distribution in a convenient place. A directory will +created called OpenDRS-x.x.x/. + +2. Review License +Change to the OpenDRS-x.x.x/ directory and review the LICENSE file. + +3. Run the install script +In the same directory, type ./install.sh at a command prompt and +follow the prompts. + +4. Log in +Click on the Log In menu option on the main page. If this is a fresh +installation The initial user is drsadmin and the initial password +is OpenDRS-1. It is highly recommended that you changethis password +to a strong one by going to the Manage Database page under the +Admin Functions menu. If this an upgrade, your current usernames and +passwords are unchanged. + +5. Configure +The Admin Functions menu will be shown when logged in as an admin user. +Click on Global Settings to set display options, page colors, banners, and +miscellaneous options. Click on Manage Database to set users, devices, +subsystems, action reasons and periods. + +6. Start making writeups diff --git a/RPiSetup/brlaser-master/CMakeLists.txt b/RPiSetup/brlaser-master/CMakeLists.txt new file mode 100644 index 0000000..15f430c --- /dev/null +++ b/RPiSetup/brlaser-master/CMakeLists.txt @@ -0,0 +1,126 @@ +cmake_minimum_required(VERSION 3.1) +project(brlaser CXX) +set(BRLASER_VERSION "4") + +if (NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES) + message(STATUS "No build type selected, default to RelWithDebInfo") + set(CMAKE_BUILD_TYPE "RelWithDebInfo" CACHE STRING "Build type (default RelWithDebInfo)" FORCE) +endif() + +include(CMakePushCheckState) +include(CheckCXXCompilerFlag) +include(CheckIncludeFileCXX) + + +## Enable assertions for all builds +## (cmake by default sets NDEBUG for release builds) + +foreach(var + CMAKE_CXX_FLAGS_RELEASE + CMAKE_CXX_FLAGS_RELWITHDEBINFO + CMAKE_CXX_FLAGS_MINSIZEREL) + string(REGEX REPLACE "(^| )[/-]D *NDEBUG($| )" " " "${var}" "${${var}}") +endforeach() + + +## Configure the compiler + +set(CMAKE_CXX_STANDARD 11) +set(CMAKE_CXX_STANDARD_REQUIRED ON) + +macro(extra_cxx_compiler_flag FLAG) + string(REGEX REPLACE "[^A-Za-z_0-9]" "_" SFLAG ${FLAG}) + check_cxx_compiler_flag(${FLAG} COMPILER_SUPPORT_${SFLAG}) + if(COMPILER_SUPPORT_${SFLAG}) + set(EXTRA_CXX_FLAGS "${EXTRA_CXX_FLAGS} ${FLAG}") + endif() +endmacro(extra_cxx_compiler_flag) + +# Compiler warnings +extra_cxx_compiler_flag("-Wall") +extra_cxx_compiler_flag("-Wno-missing-braces") +# Some security flags +extra_cxx_compiler_flag("-fstack-protector-strong") +extra_cxx_compiler_flag("-Wformat") +extra_cxx_compiler_flag("-Werror=format-security") +extra_cxx_compiler_flag("-D_FORTIFY_SOURCE=2") +# Enable the supported flags, but give priority to CXXFLAGS env var +set(CMAKE_CXX_FLAGS "${EXTRA_CXX_FLAGS} ${CMAKE_CXX_FLAGS}") + + +## Configure CUPS + +find_program(CUPS_CONFIG NAMES cups-config) +if(NOT CUPS_CONFIG) + message(FATAL_ERROR "cups-config command not found. Are the CUPS development packages installed?") +endif() +execute_process( + COMMAND "${CUPS_CONFIG}" --datadir + OUTPUT_VARIABLE CUPS_DATA_DIR + OUTPUT_STRIP_TRAILING_WHITESPACE) +execute_process( + COMMAND "${CUPS_CONFIG}" --serverbin + OUTPUT_VARIABLE CUPS_SERVER_BIN + OUTPUT_STRIP_TRAILING_WHITESPACE) +execute_process( + COMMAND "${CUPS_CONFIG}" --cflags + OUTPUT_VARIABLE CUPS_CFLAGS + OUTPUT_STRIP_TRAILING_WHITESPACE) +execute_process( + COMMAND "${CUPS_CONFIG}" --ldflags + OUTPUT_VARIABLE CUPS_LDFLAGS + OUTPUT_STRIP_TRAILING_WHITESPACE) +execute_process( + COMMAND "${CUPS_CONFIG}" --image --libs + OUTPUT_VARIABLE CUPS_LIBS + OUTPUT_STRIP_TRAILING_WHITESPACE) + +cmake_push_check_state() +set(CMAKE_REQUIRED_FLAGS "${CMAKE_REQUIRED_FLAGS} ${CUPS_CFLAGS}") +check_include_file_cxx(cups/raster.h HAVE_CUPS_RASTER_H) +cmake_pop_check_state() +if(NOT HAVE_CUPS_RASTER_H) + message(SEND_ERROR " header not found. Are the CUPS development packages installed?") +endif() + + +## Build instructions + +configure_file( + "${PROJECT_SOURCE_DIR}/brlaser.drv.in" + "${PROJECT_BINARY_DIR}/brlaser.drv") + +configure_file( + "${PROJECT_SOURCE_DIR}/config.h.in" + "${PROJECT_BINARY_DIR}/config.h") + +include_directories("${PROJECT_BINARY_DIR}") + +add_executable(rastertobrlaser src/main.cc src/job.cc src/line.cc src/debug.cc) +target_compile_options(rastertobrlaser PRIVATE ${CUPS_CFLAGS}) +target_link_libraries(rastertobrlaser ${CUPS_LIBS}) +target_link_libraries(rastertobrlaser ${CUPS_LDFLAGS}) + +add_executable(brdecode src/brdecode.cc) +add_executable(test_lest test/test_lest.cc) +add_executable(test_line test/test_line.cc src/line.cc) +add_executable(test_block test/test_block.cc) + +enable_testing() +add_test(test_lest test_lest) +add_test(test_line test_line) +add_test(test_block test_block) + +# Autotools-style "make check" command +add_custom_target(check COMMAND ${CMAKE_CTEST_COMMAND}) +add_dependencies(check test_lest test_line test_block) + + +# Installation + +install( + TARGETS rastertobrlaser + DESTINATION "${CUPS_SERVER_BIN}/filter") +install( + FILES "${PROJECT_BINARY_DIR}/brlaser.drv" + DESTINATION "${CUPS_DATA_DIR}/drv") diff --git a/RPiSetup/brlaser-master/COPYING b/RPiSetup/brlaser-master/COPYING new file mode 100644 index 0000000..d159169 --- /dev/null +++ b/RPiSetup/brlaser-master/COPYING @@ -0,0 +1,339 @@ + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +License is intended to guarantee your freedom to share and change free +software--to make sure the software is free for all its users. This +General Public License applies to most of the Free Software +Foundation's software and to any other program whose authors commit to +using it. (Some other Free Software Foundation software is covered by +the GNU Lesser General Public License instead.) You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +this service if you wish), that you receive source code or can get it +if you want it, that you can change the software or use pieces of it +in new free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. You must make sure that they, too, receive or can get the +source code. And you must show them these terms so they know their +rights. + + We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + + Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, so +that any problems introduced by others will not reflect on the original +authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that redistributors of a free +program will individually obtain patent licenses, in effect making the +program proprietary. To prevent this, we have made it clear that any +patent must be licensed for everyone's free use or not licensed at all. + + The precise terms and conditions for copying, distribution and +modification follow. + + GNU GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License applies to any program or other work which contains +a notice placed by the copyright holder saying it may be distributed +under the terms of this General Public License. The "Program", below, +refers to any such program or work, and a "work based on the Program" +means either the Program or any derivative work under copyright law: +that is to say, a work containing the Program or a portion of it, +either verbatim or with modifications and/or translated into another +language. (Hereinafter, translation is included without limitation in +the term "modification".) Each licensee is addressed as "you". + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running the Program is not restricted, and the output from the Program +is covered only if its contents constitute a work based on the +Program (independent of having been made by running the Program). +Whether that is true depends on what the Program does. + + 1. You may copy and distribute verbatim copies of the Program's +source code as you receive it, in any medium, provided that you +conspicuously and appropriately publish on each copy an appropriate +copyright notice and disclaimer of warranty; keep intact all the +notices that refer to this License and to the absence of any warranty; +and give any other recipients of the Program a copy of this License +along with the Program. + +You may charge a fee for the physical act of transferring a copy, and +you may at your option offer warranty protection in exchange for a fee. + + 2. You may modify your copy or copies of the Program or any portion +of it, thus forming a work based on the Program, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) You must cause the modified files to carry prominent notices + stating that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in + whole or in part contains or is derived from the Program or any + part thereof, to be licensed as a whole at no charge to all third + parties under the terms of this License. + + c) If the modified program normally reads commands interactively + when run, you must cause it, when started running for such + interactive use in the most ordinary way, to print or display an + announcement including an appropriate copyright notice and a + notice that there is no warranty (or else, saying that you provide + a warranty) and that users may redistribute the program under + these conditions, and telling the user how to view a copy of this + License. (Exception: if the Program itself is interactive but + does not normally print such an announcement, your work based on + the Program is not required to print an announcement.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Program, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Program, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Program. + +In addition, mere aggregation of another work not based on the Program +with the Program (or with a work based on the Program) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may copy and distribute the Program (or a work based on it, +under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you also do one of the following: + + a) Accompany it with the complete corresponding machine-readable + source code, which must be distributed under the terms of Sections + 1 and 2 above on a medium customarily used for software interchange; or, + + b) Accompany it with a written offer, valid for at least three + years, to give any third party, for a charge no more than your + cost of physically performing source distribution, a complete + machine-readable copy of the corresponding source code, to be + distributed under the terms of Sections 1 and 2 above on a medium + customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer + to distribute corresponding source code. (This alternative is + allowed only for noncommercial distribution and only if you + received the program in object code or executable form with such + an offer, in accord with Subsection b above.) + +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, complete source +code means all the source code for all modules it contains, plus any +associated interface definition files, plus the scripts used to +control compilation and installation of the executable. However, as a +special exception, the source code distributed need not include +anything that is normally distributed (in either source or binary +form) with the major components (compiler, kernel, and so on) of the +operating system on which the executable runs, unless that component +itself accompanies the executable. + +If distribution of executable or object code is made by offering +access to copy from a designated place, then offering equivalent +access to copy the source code from the same place counts as +distribution of the source code, even though third parties are not +compelled to copy the source along with the object code. + + 4. You may not copy, modify, sublicense, or distribute the Program +except as expressly provided under this License. Any attempt +otherwise to copy, modify, sublicense or distribute the Program is +void, and will automatically terminate your rights under this License. +However, parties who have received copies, or rights, from you under +this License will not have their licenses terminated so long as such +parties remain in full compliance. + + 5. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Program (or any work based on the +Program), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Program or works based on it. + + 6. Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the +original licensor to copy, distribute or modify the Program subject to +these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + + 7. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Program at all. For example, if a patent +license would not permit royalty-free redistribution of the Program by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Program. + +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system, which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 8. If the distribution and/or use of the Program is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Program under this License +may add an explicit geographical distribution limitation excluding +those countries, so that distribution is permitted only in or among +countries not thus excluded. In such case, this License incorporates +the limitation as if written in the body of this License. + + 9. The Free Software Foundation may publish revised and/or new versions +of the General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + +Each version is given a distinguishing version number. If the Program +specifies a version number of this License which applies to it and "any +later version", you have the option of following the terms and conditions +either of that version or of any later version published by the Free +Software Foundation. If the Program does not specify a version number of +this License, you may choose any version ever published by the Free Software +Foundation. + + 10. If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, write to the author +to ask for permission. For software which is copyrighted by the Free +Software Foundation, write to the Free Software Foundation; we sometimes +make exceptions for this. Our decision will be guided by the two goals +of preserving the free status of all derivatives of our free software and +of promoting the sharing and reuse of software generally. + + NO WARRANTY + + 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY +FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN +OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES +PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED +OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS +TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE +PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, +REPAIR OR CORRECTION. + + 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR +REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, +INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING +OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED +TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY +YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER +PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE +POSSIBILITY OF SUCH DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along + with this program; if not, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + +Also add information on how to contact you by electronic and paper mail. + +If the program is interactive, make it output a short notice like this +when it starts in an interactive mode: + + Gnomovision version 69, Copyright (C) year name of author + Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, the commands you use may +be called something other than `show w' and `show c'; they could even be +mouse-clicks or menu items--whatever suits your program. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the program, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the program + `Gnomovision' (which makes passes at compilers) written by James Hacker. + + , 1 April 1989 + Ty Coon, President of Vice + +This General Public License does not permit incorporating your program into +proprietary programs. If your program is a subroutine library, you may +consider it more useful to permit linking proprietary applications with the +library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License. diff --git a/RPiSetup/brlaser-master/ChangeLog b/RPiSetup/brlaser-master/ChangeLog new file mode 100644 index 0000000..4fbd9a2 --- /dev/null +++ b/RPiSetup/brlaser-master/ChangeLog @@ -0,0 +1,9 @@ +brlaser v3 (2014-07-07) + Added DCP-7065DN description. + +brlaser v2 (2014-06-29) + Suppport compilation with GCC 4.6. + Add a basic test suite. + +brlaser v1 (2013-12-30) + Initial release. diff --git a/RPiSetup/brlaser-master/README.md b/RPiSetup/brlaser-master/README.md new file mode 100644 index 0000000..7b79fea --- /dev/null +++ b/RPiSetup/brlaser-master/README.md @@ -0,0 +1,85 @@ +brlaser: Brother laser printer driver +===================================== + +brlaser is a CUPS driver for Brother laser printers. + +Although most Brother printers support a standard printer language +such as PCL or PostScript, not all do. If you have a monochrome +Brother laser printer (or multi-function device) and the other open +source drivers don't work, this one might help. + +This driver is known to work with these printers: + +* Brother DCP-1510 +* Brother DCP-7030 +* Brother DCP-7040 +* Brother DCP-7055 +* Brother DCP-7055W +* Brother DCP-7065DN +* Brother HL-L2300D +* Brother MFC-7360N + + +Other printers +-------------- + +If your printer isn't included in the list above, just try selecting +any entry marked 'brlaser' and see if it works. + +If it does, please create a new issue here in Github and include the +output of this command: + + sudo lpinfo --include-schemes usb -l -v + +Then I'll be able to add a proper entry for your printer. + + +Installation +------------ + +Some operating systems already ship this driver. This is the case for +at least Debian, Ubuntu, Raspbian, openSUSE and Arch Linux. Look for a +package named `printer-driver-brlaser`. + +You'll also need Ghostscript, in case that's not installed +automatically. + +Once brlaser is installed, you can add your printer using the usual +CUPS interface. + + +Building from source +-------------------- + +To compile brlaser you'll need CMake and the CUPS development packages +(libcups2-dev, libcupsimage2-dev or similar). + +Get the code by cloning the git repo . Compile and install with these commands: + + cmake . + make + sudo make install + +It might be needed to restart CUPS after this. + +[latest release]: https://github.com/pdewacht/brlaser/releases/latest + + +Copyright +--------- + +Copyright © 2013 Peter De Wachter + +brlaser is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 2 of the License, or +(at your option) any later version. + +brlaser is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with brlaser. If not, see . diff --git a/RPiSetup/brlaser-master/brlaser.drv.in b/RPiSetup/brlaser-master/brlaser.drv.in new file mode 100644 index 0000000..84542cc --- /dev/null +++ b/RPiSetup/brlaser-master/brlaser.drv.in @@ -0,0 +1,143 @@ +// This file is part of the brlaser printer driver. +// +// Copyright 2013 Peter De Wachter +// +// brlaser is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 2 of the License, or +// (at your option) any later version. +// +// brlaser is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with brlaser. If not, see . + +#define USING "using @CMAKE_PROJECT_NAME@ v@BRLASER_VERSION@" + +// Include standard font and media definitions +#include +#include + +// List the fonts that are supported, in this case all standard fonts... +Font * + +// Manufacturer and driver version. +Manufacturer "Brother" +Version "@BRLASER_VERSION@" + +// Each filter provided by the driver... +Filter application/vnd.cups-raster 33 rastertobrlaser + +// Supported resolutions. +// The 1200dpi mode is weird: we need to send 1200x1200dpi raster +// data, but Brother only advertises 1200x600dpi. I wonder what +// is going on there. +// The 300dpi mode is reportedly not supported on all printers, so +// it's listed in individual printer blocks where we believe it works. +// Resolution k 1 0 0 0 "300dpi/300 DPI" +*Resolution k 1 0 0 0 "600dpi/600 DPI" +Resolution k 1 0 0 0 "1200dpi/1200HQ" + +// Supported page sizes. +HWMargins 8 8 8 16 +*MediaSize A4 +MediaSize A5 +MediaSize A6 +MediaSize B5 +MediaSize B6 +MediaSize EnvC5 +MediaSize EnvMonarch +MediaSize EnvDL +MediaSize Executive +MediaSize Legal +MediaSize Letter + +// Input trays. Numbers must match the filter source code. +*InputSlot 0 "Auto/Auto-select" +InputSlot 1 "Tray1/Tray 1" +InputSlot 2 "Tray2/Tray 2" +InputSlot 3 "Tray3/Tray 3" +InputSlot 4 "MPTray/MP Tray" +InputSlot 5 "Manual/Manual" + +// Media types. +*MediaType 0 "PLAIN/Plain paper" +MediaType 1 "THIN/Thin paper" +MediaType 2 "THICK/Thick paper" +MediaType 3 "THICKER/Thicker paper" +MediaType 4 "BOND/Bond paper" +MediaType 5 "TRANS/Transparencies" +MediaType 6 "ENV/Envelopes" +MediaType 7 "ENV-THICK/Thick envelopes" +MediaType 8 "ENV-THIN/Thin envelopes" + +Option "brlaserEconomode/Toner save mode" Boolean AnySetup 10 + *Choice False/Off "<>setpagedevice" + Choice True/On "<>setpagedevice" + + +{ + ModelName "DCP-1510" + Attribute "NickName" "" "Brother DCP-1510 series, $USING" + Attribute "1284DeviceID" "" "MFG:Brother;CMD:PJL,XL2HB;MDL:DCP-1510 series;CLS:PRINTER;CID:Brother Laser Type1;" + Resolution k 1 0 0 0 "300dpi/300 DPI" + PCFileName "br1510.ppd" +} + +{ + ModelName "DCP-7030" + Attribute "NickName" "" "Brother DCP-7030, $USING" + Attribute "1284DeviceID" "" "MFG:Brother;CMD:PJL,HBP;MDL:DCP-7030;CLS:PRINTER;" + Resolution k 1 0 0 0 "300dpi/300 DPI" + PCFileName "br7030.ppd" +} + +{ + ModelName "DCP-7040" + Attribute "NickName" "" "Brother DCP-7040, $USING" + Attribute "1284DeviceID" "" "MFG:Brother;CMD:PJL,HBP;MDL:DCP-7040;CLS:PRINTER;" + Resolution k 1 0 0 0 "300dpi/300 DPI" + PCFileName "br7040.ppd" +} + +{ + ModelName "DCP-7055" + Attribute "NickName" "" "Brother DCP-7055, $USING" + Attribute "1284DeviceID" "" "MFG:Brother;CMD:PJL,HBP;MDL:DCP-7055;CLS:PRINTER;CID:Brother Laser Type1;" + Resolution k 1 0 0 0 "300dpi/300 DPI" + PCFileName "br7055.ppd" +} + +{ + ModelName "DCP-7055W" + Attribute "NickName" "" "Brother DCP-7055W, $USING" + Attribute "1284DeviceID" "" "MFG:Brother;CMD:PJL,HBP;MDL:DCP-7055W;CLS:PRINTER;CID:Brother Laser Type1;" + Resolution k 1 0 0 0 "300dpi/300 DPI" + PCFileName "br7055w.ppd" +} + +{ + ModelName "DCP-7065DN" + Attribute "NickName" "" "Brother DCP-7065DN, $USING" + Attribute "1284DeviceID" "" "MFG:Brother;CMD:PJL,HBP;MDL:DCP-7065DN;CLS:PRINTER;CID:Brother Laser Type1;" + Duplex rotated + PCFileName "br7065dn.ppd" +} + +{ + ModelName "HL-L2300D" + Attribute "NickName" "" "Brother HL-L2300D, $USING" + Attribute "1284DeviceID" "" " MFG:Brother;CMD:PJL,HBP;MDL:HL-L2300D series;CLS:PRINTER;CID:Brother Laser Type1;" + Duplex rotated + PCFileName "brl2300d.ppd" +} + +{ + ModelName "MFC-7360N" + Attribute "NickName" "" "Brother MFC-7360N, $USING" + Attribute "1284DeviceID" "" "MFG:Brother;CMD:PJL,HBP;MDL:MFC-7360N;CLS:PRINTER;CID:Brother Laser Type1;" + PCFileName "br7360n.ppd" +} diff --git a/RPiSetup/brlaser-master/config.h.in b/RPiSetup/brlaser-master/config.h.in new file mode 100644 index 0000000..601ac5e --- /dev/null +++ b/RPiSetup/brlaser-master/config.h.in @@ -0,0 +1,2 @@ +#define PACKAGE "@CMAKE_PROJECT_NAME@" +#define VERSION "@BRLASER_VERSION@" diff --git a/RPiSetup/brlaser-master/src/block.h b/RPiSetup/brlaser-master/src/block.h new file mode 100644 index 0000000..13daea1 --- /dev/null +++ b/RPiSetup/brlaser-master/src/block.h @@ -0,0 +1,68 @@ +// This file is part of the brlaser printer driver. +// +// Copyright 2013 Peter De Wachter +// +// brlaser is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 2 of the License, or +// (at your option) any later version. +// +// brlaser is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with brlaser. If not, see . + +#ifndef BLOCK_H +#define BLOCK_H + +#include +#include +#include + +class block { + public: + block(): line_bytes_(0) { + lines_.reserve(max_lines_per_block_); + } + + bool empty() const { + return line_bytes_ == 0; + } + + void add_line(std::vector &&line) { + assert(!line.empty()); + assert(line_fits(line.size())); + line_bytes_ += line.size(); + lines_.emplace_back(line); + } + + bool line_fits(unsigned size) { + return lines_.size() != max_lines_per_block_ + && line_bytes_ + size < max_block_size_; + } + + void flush(FILE *f) { + if (!empty()) { + fprintf(f, "%dw%c%c", + line_bytes_ + 2, 0, + static_cast(lines_.size())); + for (auto &line : lines_) { + fwrite(line.data(), 1, line.size(), f); + } + line_bytes_ = 0; + lines_.clear(); + } + } + + private: + static const unsigned max_block_size_ = 16350; + static const unsigned max_lines_per_block_ = 128; + + std::vector> lines_; + int line_bytes_; +}; + +#endif // BLOCK_H diff --git a/RPiSetup/brlaser-master/src/brdecode.cc b/RPiSetup/brlaser-master/src/brdecode.cc new file mode 100644 index 0000000..bcd3ec7 --- /dev/null +++ b/RPiSetup/brlaser-master/src/brdecode.cc @@ -0,0 +1,225 @@ +// A quick-and-dirty tool to convert print files back to pbm images. +// +// Copyright 2013 Peter De Wachter +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 2 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +#include +#include +#include +#include +#include +#include +#include + +namespace { + +const size_t MAX_LINE_SIZE = 2000; + +FILE *in_file; +std::vector> page; +std::vector line; +size_t line_offset; + + +class unexpected_eof: public std::exception { + public: + virtual const char *what() const noexcept { + return "Unexpected EOF"; + } +}; + +class line_overflow: public std::exception { + public: + virtual const char *what() const noexcept { + return "Unreasonable long line, aborting"; + } +}; + + +uint8_t get() { + int ch = getc(in_file); + if (ch < 0) + throw unexpected_eof(); + return ch; +} + +unsigned read_overflow() { + uint8_t ch; + unsigned sum = 0; + do { + ch = get(); + sum += ch; + } while (ch == 255); + return sum; +} + +void read_repeat(uint8_t cmd) { + uint16_t offset = (cmd >> 5) & 3; + if (offset == 3) + offset += read_overflow(); + uint16_t count = cmd & 31; + if (count == 31) + count += read_overflow(); + count += 2; + uint8_t data = get(); + + size_t end = line_offset + offset + count; + if (end > line.size()) { + if (end > MAX_LINE_SIZE) + throw line_overflow(); + line.resize(end); + } + line_offset += offset; + std::fill_n(line.begin() + line_offset, count, data); + line_offset += count; +} + +void read_substitute(uint8_t cmd) { + uint16_t offset = (cmd >> 3) & 15; + if (offset == 15) + offset += read_overflow(); + uint16_t count = cmd & 7; + if (count == 7) + count += read_overflow(); + count += 1; + + size_t end = line_offset + offset + count; + if (end > line.size()) { + if (end > MAX_LINE_SIZE) + throw line_overflow(); + line.resize(end); + } + line_offset += offset; + std::generate_n(line.begin() + line_offset, count, get); + line_offset += count; +} + +void read_edit() { + int8_t cmd = get(); + if (cmd < 0) { + read_repeat(cmd); + } else { + read_substitute(cmd); + } +} + +void read_line() { + uint8_t num_edits = get(); + if (num_edits == 255) { + line.clear(); + } else { + line_offset = 0; + for (int i = 0; i < num_edits; ++i) { + read_edit(); + } + } + page.push_back(line); +} + +void read_block() { + unsigned count = get(); + count = count * 256 + get(); + for (unsigned i = 0; i < count; ++i) { + read_line(); + } +} + +bool read_page() { + bool in_esc = false; + int ch; + + page.clear(); + line.clear(); + while ((ch = getc(in_file)) >= 0) { + if (ch == '\f') { + break; + } else if (ch == 033) { + in_esc = true; + } else if (in_esc && ch == 'w') { + read_block(); + } else if (in_esc && (ch >= 'A' && ch <= 'Z')) { + in_esc = false; + } + } + return !page.empty(); +} + +void write_pnm(FILE *f) { + size_t height = page.size(); + size_t width = 0; + for (auto &l : page) { + width = std::max(width, l.size()); + } + + fprintf(f, "P4 %zd %zd\n", width * 8, height); + std::vector empty(width); + for (auto &l : page) { + fwrite(l.data(), 1, l.size(), f); + fwrite(empty.data(), 1, width - l.size(), f); + } +} + +} // namespace + +int main(int argc, char *argv[]) { + const char *in_filename; + std::string out_prefix; + + if (argc > 2) { + in_filename = argv[1]; + out_prefix = argv[2]; + } else if (argc > 1) { + in_filename = argv[1]; + out_prefix = argv[1]; + } else { + in_filename = nullptr; + out_prefix = "page"; + } + + if (in_filename) { + in_file = fopen(in_filename, "rb"); + if (!in_file) { + fprintf(stderr, "Can't open file \"%s\"\n", in_filename); + return 1; + } + } else { + in_file = stdin; + if (isatty(0)) { + fprintf(stderr, "No filename given and no input on stdin\n"); + return 1; + } + } + + try { + int page_num = 1; + while (read_page()) { + std::string out_filename = out_prefix + + "-" + std::to_string(page_num) + ".pbm"; + FILE *out_file = fopen(out_filename.c_str(), "wb"); + if (!out_file) { + fprintf(stderr, "Can't write file \"%s\"\n", out_filename.c_str()); + return 1; + } + write_pnm(out_file); + fclose(out_file); + fprintf(stderr, "%s\n", out_filename.c_str()); + ++page_num; + } + } catch (std::exception &e) { + fprintf(stderr, "%s\n", e.what()); + return 1; + } + return 0; +} diff --git a/RPiSetup/brlaser-master/src/debug.cc b/RPiSetup/brlaser-master/src/debug.cc new file mode 100644 index 0000000..041b414 --- /dev/null +++ b/RPiSetup/brlaser-master/src/debug.cc @@ -0,0 +1,107 @@ +// This file is part of the brlaser printer driver. +// +// Copyright 2013 Peter De Wachter +// +// brlaser is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 2 of the License, or +// (at your option) any later version. +// +// brlaser is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with brlaser. If not, see . + +#include "config.h" +#include "debug.h" +#include +#include + +namespace { + +template +void dump(const char *name, const T &value) { + std::cerr << "DEBUG: " PACKAGE ": page header: " << name << " = " << value << '\n'; +} + +template +void dump(const char *name, const T (&value)[N]) { + std::cerr << "DEBUG: " PACKAGE ": page header: " << name << " ="; + for (int i = 0; i < N; ++i) { + std::cerr << ' ' << value[i]; + } + std::cerr << '\n'; +} + +void dump(const char *name, const char *value) { + std::cerr << "DEBUG: " PACKAGE ": page header: " << name << " = \"" << value << "\"\n"; +} + +template +void dump(const char *name, const char (&value)[N][M]) { + std::cerr << "DEBUG: " PACKAGE ": page header: " << name << " ="; + for (int i = 0; i < N; ++i) { + std::cerr << " \"" << value[i] << '"'; + } + std::cerr << '\n'; +} + +} // namespace + + +void dump_page_header(const cups_page_header2_t &h) { +#define d(f) dump(#f, h.f) + d(MediaClass); + d(MediaColor); + d(MediaType); + d(OutputType); + d(AdvanceDistance); + d(AdvanceMedia); + d(Collate); + d(CutMedia); + d(Duplex); + d(HWResolution); + d(ImagingBoundingBox); + d(InsertSheet); + d(Jog); + d(LeadingEdge); + d(Margins); + d(ManualFeed); + d(MediaPosition); + d(MediaWeight); + d(MirrorPrint); + d(NegativePrint); + d(NumCopies); + d(Orientation); + d(OutputFaceUp); + d(PageSize); + d(Separations); + d(TraySwitch); + d(Tumble); + d(cupsWidth); + d(cupsHeight); + d(cupsMediaType); + d(cupsBitsPerColor); + d(cupsBitsPerPixel); + d(cupsBytesPerLine); + d(cupsColorOrder); + d(cupsColorSpace); + d(cupsCompression); + d(cupsRowCount); + d(cupsRowFeed); + d(cupsRowStep); + d(cupsNumColors); + d(cupsBorderlessScalingFactor); + d(cupsPageSize); + d(cupsImagingBBox); + d(cupsInteger); + d(cupsReal); + d(cupsString); + d(cupsMarkerType); + d(cupsRenderingIntent); + d(cupsPageSizeName); +#undef d +} diff --git a/RPiSetup/brlaser-master/src/debug.h b/RPiSetup/brlaser-master/src/debug.h new file mode 100644 index 0000000..d1eff52 --- /dev/null +++ b/RPiSetup/brlaser-master/src/debug.h @@ -0,0 +1,25 @@ +// This file is part of the brlaser printer driver. +// +// Copyright 2013 Peter De Wachter +// +// brlaser is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 2 of the License, or +// (at your option) any later version. +// +// brlaser is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with brlaser. If not, see . + +#ifndef DEBUG_H +#define DEBUG_H + +#include + +void dump_page_header(const cups_page_header2_t &h); + +#endif diff --git a/RPiSetup/brlaser-master/src/job.cc b/RPiSetup/brlaser-master/src/job.cc new file mode 100644 index 0000000..440fc6d --- /dev/null +++ b/RPiSetup/brlaser-master/src/job.cc @@ -0,0 +1,118 @@ +// This file is part of the brlaser printer driver. +// +// Copyright 2013 Peter De Wachter +// +// brlaser is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 2 of the License, or +// (at your option) any later version. +// +// brlaser is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with brlaser. If not, see . + +#include "job.h" +#include +#include +#include +#include "line.h" +#include "block.h" + + +job::job(FILE *out, const std::string &job_name) + : out_(out), + job_name_(job_name), + page_params_() { + // Delete dubious characters from job name + std::replace_if(job_name_.begin(), job_name_.end(), [](char c) { + return c < 32 || c >= 127 || c == '"' || c == '\\'; + }, ' '); + + begin_job(); +} + +job::~job() { + end_job(); +} + +void job::begin_job() { + for (int i = 0; i < 128; ++i) { + putc(0, out_); + } + fprintf(out_, "\033%%-12345X@PJL\n"); + fprintf(out_, "@PJL JOB NAME=\"%s\"\n", job_name_.c_str()); +} + +void job::end_job() { + fprintf(out_, "\033%%-12345X@PJL\n"); + fprintf(out_, "@PJL EOJ NAME=\"%s\"\n", job_name_.c_str()); + fprintf(out_, "\033%%-12345X\n"); +} + +void job::write_page_header() { + fprintf(out_, "\033%%-12345X@PJL\n"); + if (page_params_.resolution != 1200) { + fprintf(out_, "@PJL SET RAS1200MODE = FALSE\n"); + fprintf(out_, "@PJL SET RESOLUTION = %d\n", page_params_.resolution); + } else { + fprintf(out_, "@PJL SET RAS1200MODE = TRUE\n"); + fprintf(out_, "@PJL SET RESOLUTION = 600\n"); + } + fprintf(out_, "@PJL SET ECONOMODE = %s\n", + page_params_.economode ? "ON" : "OFF"); + fprintf(out_, "@PJL SET SOURCETRAY = %s\n", + page_params_.sourcetray.c_str()); + fprintf(out_, "@PJL SET MEDIATYPE = %s\n", + page_params_.mediatype.c_str()); + fprintf(out_, "@PJL SET PAPER = %s\n", + page_params_.papersize.c_str()); + fprintf(out_, "@PJL SET PAGEPROTECT = AUTO\n"); + fprintf(out_, "@PJL SET ORIENTATION = PORTRAIT\n"); + fprintf(out_, "@PJL ENTER LANGUAGE = PCL\n"); + + fputs("\033E", out_); + fprintf(out_, "\033&l%dX", std::max(1, page_params_.num_copies)); + + if (page_params_.duplex) { + fputs("\033&l2S", out_); + } +} + +void job::encode_page(const page_params &page_params, + int lines, + int linesize, + nextline_fn nextline) { + if (!(page_params_ == page_params)) { + page_params_ = page_params; + write_page_header(); + } + + std::vector line(linesize); + std::vector reference(linesize); + block block; + + if (!nextline(line)) { + return; + } + block.add_line(encode_line(line)); + std::swap(line, reference); + + fputs("\033*b1030m", out_); + + for (int i = 1; i < lines && nextline(line); ++i) { + std::vector encoded = encode_line(line, reference); + if (!block.line_fits(encoded.size())) { + block.flush(out_); + } + block.add_line(std::move(encoded)); + std::swap(line, reference); + } + + block.flush(out_); + fputs("1030M\f", out_); + fflush(out_); +} diff --git a/RPiSetup/brlaser-master/src/job.h b/RPiSetup/brlaser-master/src/job.h new file mode 100644 index 0000000..2ae7ea5 --- /dev/null +++ b/RPiSetup/brlaser-master/src/job.h @@ -0,0 +1,68 @@ +// This file is part of the brlaser printer driver. +// +// Copyright 2013 Peter De Wachter +// +// brlaser is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 2 of the License, or +// (at your option) any later version. +// +// brlaser is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with brlaser. If not, see . + +#ifndef JOB_H +#define JOB_H + +#include +#include +#include +#include + +struct page_params { + int num_copies; + int resolution; + bool duplex; + bool economode; + std::string sourcetray; + std::string mediatype; + std::string papersize; + + bool operator==(const page_params &o) const { + return num_copies == o.num_copies + && resolution == o.resolution + && duplex == o.duplex + && economode == o.economode + && sourcetray == o.sourcetray + && mediatype == o.mediatype + && papersize == o.papersize; + } +}; + +class job { + public: + typedef bool (*nextline_fn)(std::vector &buf); + + explicit job(FILE *out, const std::string &job_name); + ~job(); + + void encode_page(const page_params ¶ms, + int lines, + int linesize, + nextline_fn nextline); + + private: + void begin_job(); + void end_job(); + void write_page_header(); + + FILE *out_; + std::string job_name_; + page_params page_params_; +}; + +#endif // JOB_H diff --git a/RPiSetup/brlaser-master/src/line.cc b/RPiSetup/brlaser-master/src/line.cc new file mode 100644 index 0000000..6489118 --- /dev/null +++ b/RPiSetup/brlaser-master/src/line.cc @@ -0,0 +1,191 @@ +// This file is part of the brlaser printer driver. +// +// Copyright 2013 Peter De Wachter +// +// brlaser is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 2 of the License, or +// (at your option) any later version. +// +// brlaser is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with brlaser. If not, see . + +#include "line.h" +#include +#include +using std::vector; + +namespace { + +void write_overflow(int value, vector *out) { + if (value >= 0) { + if (value < 255) { + out->push_back(value); + } else { + out->insert(out->end(), value / 255, 255); + out->push_back(value % 255); + } + } +} + +template +void write_substitute(int offset, + Iterator first, + Iterator last, + vector *out) { + assert(offset >= 0); + assert(offset < 10000); + assert(first != last); + + const int offset_max = 15; + const int count_max = 7; + int count = std::distance(first, last) - 1; + + int offset_low = std::min(offset, offset_max); + int count_low = std::min(count, count_max); + out->push_back((offset_low << 3) | count_low); + write_overflow(offset - offset_max, out); + write_overflow(count - count_max, out); + out->insert(out->end(), first, last); +} + +void write_repeat(int offset, int count, int value, vector *out) { + assert(offset >= 0); + assert(offset < 10000); + assert(count >= 2); + assert(count < 10000); + + const int offset_max = 3; + const int count_max = 31; + count -= 2; + + int offset_low = std::min(offset, offset_max); + int count_low = std::min(count, count_max); + out->push_back(128 | (offset_low << 5) | count_low); + write_overflow(offset - offset_max, out); + write_overflow(count - count_max, out); + out->push_back(value); +} + + +bool all_zeros(const vector &buf) { + return std::none_of(buf.begin(), buf.end(), [](uint8_t b) { return b; }); +} + +template +int skip_to_next_mismatch(Iterator1 *first1, + Iterator1 last1, + Iterator2 *first2) { + auto mismatch_it = std::mismatch(*first1, last1, *first2); + int skipped = std::distance(*first1, mismatch_it.first); + *first1 = mismatch_it.first; + *first2 = mismatch_it.second; + return skipped; +} + +template +int repeat_length(Iterator first, Iterator last) { + if (first != last) { + auto k = *first; + auto mismatch = std::find_if(std::next(first), last, + [=](decltype(k) x) { return x != k; }); + return std::distance(first, mismatch); + } + return 0; +} + +template +int substitute_length(Iterator1 first1, Iterator1 last1, Iterator2 first2) { + if (first1 != last1) { + Iterator1 it1 = first1; + Iterator2 it2 = first2; + Iterator1 next1 = std::next(first1); + Iterator2 next2 = std::next(first2); + Iterator1 prev1 = first1; + while (next1 != last1) { + if ((*it1 == *it2 && *next1 == *next2)) { + return std::distance(first1, it1); + } + if (*it1 == *next1 && *it1 == *prev1) { + return std::distance(first1, prev1); + } + prev1 = it1; + it1 = next1; it2 = next2; + ++next1; ++next2; + } + } + return std::distance(first1, last1); +} + +size_t reserve_size(const vector &line) { + // Big enough to store the line uncompressed together with an Substitute + // command with many overflow bytes. + return line.size() + 16; +} + +} // namespace + +vector encode_line(const vector &line, + const vector &reference) { + assert(line.size() == reference.size()); + if (all_zeros(line)) { + return vector(1, 0xFF); + } + + vector output; + output.reserve(reserve_size(line)); + output.push_back(0); // first byte is the edit count + + const uint8_t max_edits = 254; + int num_edits = 0; + + auto line_it = line.begin(); + auto ref_it = reference.begin(); + while (1) { + int offset = skip_to_next_mismatch(&line_it, line.end(), &ref_it); + if (line_it == line.end()) { + // No more differences, we're done. + break; + } + + if (++num_edits == max_edits) { + // We've run out of edits. Just output the rest of the line in a big + // substitute command. + write_substitute(offset, line_it, line.end(), &output); + break; + } + + int s = substitute_length(line_it, line.end(), ref_it); + if (s > 0) { + write_substitute(offset, line_it, std::next(line_it, s), &output); + line_it += s; + ref_it += s; + } else { + int r = repeat_length(line_it, line.end()); + assert(r >= 2); + write_repeat(offset, r, *line_it, &output); + line_it += r; + ref_it += r; + } + } + + assert(num_edits <= max_edits); + output[0] = num_edits; + return output; +} + +vector encode_line(const vector &line) { + if (all_zeros(line)) { + return vector(1, 0xFF); + } + vector buf; + buf.reserve(reserve_size(line)); + buf.push_back(1); + write_substitute(0, line.begin(), line.end(), &buf); + return buf; +} diff --git a/RPiSetup/brlaser-master/src/line.h b/RPiSetup/brlaser-master/src/line.h new file mode 100644 index 0000000..d9d8716 --- /dev/null +++ b/RPiSetup/brlaser-master/src/line.h @@ -0,0 +1,31 @@ +// This file is part of the brlaser printer driver. +// +// Copyright 2013 Peter De Wachter +// +// brlaser is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 2 of the License, or +// (at your option) any later version. +// +// brlaser is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with brlaser. If not, see . + +#ifndef LINE_H +#define LINE_H + +#include +#include + +std::vector encode_line( + const std::vector &line, + const std::vector &reference); + +std::vector encode_line( + const std::vector &line); + +#endif // LINE_H diff --git a/RPiSetup/brlaser-master/src/main.cc b/RPiSetup/brlaser-master/src/main.cc new file mode 100644 index 0000000..6eafcdd --- /dev/null +++ b/RPiSetup/brlaser-master/src/main.cc @@ -0,0 +1,205 @@ +// This file is part of the brlaser printer driver. +// +// Copyright 2013 Peter De Wachter +// +// brlaser is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 2 of the License, or +// (at your option) any later version. +// +// brlaser is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with brlaser. If not, see . + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "config.h" +#include "job.h" +#include "debug.h" + + +namespace { + +cups_raster_t *ras; +volatile sig_atomic_t interrupted = 0; + + +void sigterm_handler(int sig) { + interrupted = 1; +} + + +bool next_line(std::vector &buf) { + if (interrupted) { + return false; + } + return cupsRasterReadPixels(ras, buf.data(), buf.size()) == buf.size(); +} + + +bool plain_ascii_string(const char *str) { + bool result = true; + for (; result && *str; str++) { + result = *str >= 32 && *str <= 126; + } + return result; +} + +std::string ascii_job_name(const char *job_id, const char *job_user, const char *job_name) { + std::array parts = {{ + job_id, + job_user, + job_name + }}; + std::string result; + for (const char *part : parts) { + if (*part && plain_ascii_string(part)) { + if (!result.empty()) { + result += '/'; + } + result += part; + } + } + if (result.empty()) { + result = "brlaser"; + } + const int max_size = 79; + if (result.size() > max_size) { + result.resize(max_size); + } + return result; +} + +page_params build_page_params(const cups_page_header2_t &header) { + static const std::array sources = {{ + "AUTO", "T1", "T2", "T3", "MP", "MANUAL" + }}; + static const std::map sizes = { + { "A4", "A4" }, + { "A5", "A5" }, + { "A6", "A6" }, + { "B5", "B5" }, + { "B6", "B6" }, + { "EnvC5", "C5" }, + { "EnvMonarch", "MONARCH" }, + { "EnvPRC5", "DL" }, + { "EnvDL", "DL" }, + { "Executive", "EXECUTIVE" }, + { "Legal", "LEGAL" }, + { "Letter", "LETTER" } + }; + + page_params p = { }; + p.num_copies = header.NumCopies; + p.resolution = header.HWResolution[0]; + p.economode = header.cupsInteger[10]; + p.mediatype = header.MediaType; + p.duplex = header.Duplex; + + if (header.MediaPosition < sources.size()) + p.sourcetray = sources[header.MediaPosition]; + else + p.sourcetray = sources[0]; + + auto size_it = sizes.find(header.cupsPageSizeName); + if (size_it != sizes.end()) + p.papersize = size_it->second; + else + p.papersize = "A4"; + + return p; +} + +} // namespace + + +int main(int argc, char *argv[]) { + fprintf(stderr, "INFO: %s version %s\n", PACKAGE, VERSION); + + if (argc != 6 && argc != 7) { + fprintf(stderr, "ERROR: rastertobrlaser job-id user title copies options [file]\n"); + fprintf(stderr, "INFO: This program is a CUPS filter. It is not intended to be run manually.\n"); + return 1; + } + const char *job_id = argv[1]; + const char *job_user = argv[2]; + const char *job_name = argv[3]; + // const int job_copies = atoi(argv[4]); + // const char *job_options = argv[5]; + const char *job_filename = argv[6]; + // const char *job_charset = getenv("CHARSET"); + + signal(SIGTERM, sigterm_handler); + signal(SIGPIPE, SIG_IGN); + + int fd = STDIN_FILENO; + if (job_filename) { + fd = open(job_filename, O_RDONLY); + if (fd < 0) { + fprintf(stderr, "ERROR: " PACKAGE ": Unable to open raster file\n"); + return 1; + } + } + +#ifdef __OpenBSD__ + if (pledge("stdio", nullptr) != 0) { + fprintf(stderr, "ERROR: " PACKAGE ": pledge failed\n"); + return 1; + } +#endif + + ras = cupsRasterOpen(fd, CUPS_RASTER_READ); + if (!ras) { + fprintf(stderr, "DEBUG: " PACKAGE ": Cannot read raster data. Most likely an earlier filter in the pipeline failed.\n"); + return 1; + } + + int pages = 0; + { + job job(stdout, ascii_job_name(job_id, job_user, job_name)); + cups_page_header2_t header; + while (!interrupted && cupsRasterReadHeader2(ras, &header)) { + if (header.cupsBitsPerPixel != 1 + || header.cupsBitsPerColor != 1 + || header.cupsNumColors != 1 + || header.cupsBytesPerLine > 10000) { + fprintf(stderr, "ERROR: " PACKAGE ": Page %d: Bogus raster data.\n", pages + 1); + dump_page_header(header); + return 1; + } + if (pages == 0) { + fprintf(stderr, "DEBUG: " PACKAGE ": Page header of first page\n"); + dump_page_header(header); + } + job.encode_page(build_page_params(header), + header.cupsHeight, + header.cupsBytesPerLine, + next_line); + fprintf(stderr, "PAGE: %d %d\n", ++pages, header.NumCopies); + } + } + + if (pages == 0) { + fprintf(stderr, "ERROR: " PACKAGE ": No pages were found.\n"); + return 1; + } + + fflush(stdout); + if (ferror(stdout)) { + fprintf(stderr, "DEBUG: " PACKAGE ": Could not write print data. Most likely the CUPS backend failed.\n"); + return 1; + } + return 0; +} diff --git a/RPiSetup/brlaser-master/test/lest.hpp b/RPiSetup/brlaser-master/test/lest.hpp new file mode 100644 index 0000000..efc9442 --- /dev/null +++ b/RPiSetup/brlaser-master/test/lest.hpp @@ -0,0 +1,199 @@ +// Copyright 2013 by Martin Moene +// +// lest is based on ideas by Kevlin Henney, see video at +// http://skillsmatter.com/podcast/agile-testing/kevlin-henney-rethinking-unit-testing-in-c-plus-plus +// +// Distributed under the Boost Software License, Version 1.0: +// +// Permission is hereby granted, free of charge, to any person or organization +// obtaining a copy of the software and accompanying documentation covered by +// this license (the "Software") to use, reproduce, display, distribute, +// execute, and transmit the Software, and to prepare derivative works of the +// Software, and to permit third-parties to whom the Software is furnished to +// do so, all subject to the following: +// +// The copyright notices in the Software and this entire statement, including +// the above license grant, this restriction and the following disclaimer, +// must be included in all copies of the Software, in whole or in part, and +// all derivative works of the Software, unless such copies or derivative +// works are solely in the form of machine-executable object code generated by +// a source language processor. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT +// SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE +// FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, +// ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. + +#ifndef LEST_LEST_H_INCLUDED +#define LEST_LEST_H_INCLUDED + +#include +#include +#include +#include +#include + +#ifndef lest_NO_SHORT_ASSERTION_NAMES +# define EXPECT lest_EXPECT +# define EXPECT_THROWS lest_EXPECT_THROWS +# define EXPECT_THROWS_AS lest_EXPECT_THROWS_AS +#endif + +#define lest_EXPECT( expr ) \ + try \ + { \ + if ( ! (expr) ) \ + throw lest::failure{ lest_LOCATION, #expr }; \ + } \ + catch( lest::failure const & ) \ + { \ + throw ; \ + } \ + catch( std::exception const & e ) \ + { \ + throw lest::unexpected{ lest_LOCATION, #expr, lest::with_message( e.what() ) }; \ + } \ + catch(...) \ + { \ + throw lest::unexpected{ lest_LOCATION, #expr, "of unknown type" }; \ + } + +#define lest_EXPECT_THROWS( expr ) \ + for (;;) \ + { \ + try { lest::serum( expr ); } catch (...) { break; } \ + throw lest::expected{ lest_LOCATION, #expr }; \ + } + +#define lest_EXPECT_THROWS_AS( expr, excpt ) \ + for (;;) \ + { \ + try { lest::serum( expr ); } catch ( excpt & ) { break; } catch (...) {} \ + throw lest::expected{ lest_LOCATION, #expr, lest::of_type( #excpt ) }; \ + } + +#define lest_LOCATION lest::location{__FILE__, __LINE__} + +namespace lest { + +struct test +{ + const std::string name; + const std::function behaviour; +}; + +struct location +{ + const std::string file; + const int line; + + location( std::string file, int line ) + : file{ file }, line{ line } {} +}; + +struct comment +{ + const std::string text; + + comment( std::string text ) : text{ text } {} + explicit operator bool() { return ! text.empty(); } +}; + +struct message : std::runtime_error +{ + const std::string kind; + const location where; + const comment note; + + ~message() throw() {} // GCC 4.6 + + message( std::string kind, location where, std::string expr, std::string note = "" ) + : std::runtime_error{ expr }, kind{ kind }, where{ where }, note{ note } {} +}; + +struct failure : message +{ + failure( location where, std::string expr ) + : message{ "failed", where, expr } {} +}; + +struct expected : message +{ + expected( location where, std::string expr, std::string excpt = "" ) + : message{ "failed: didn't get exception", where, expr, excpt } {} +}; + +struct unexpected : message +{ + unexpected( location where, std::string expr, std::string note ) + : message{ "failed: got unexpected exception", where, expr, note } {} +}; + +inline bool serum( bool verum ) { return verum; } + +inline std::string with_message( std::string text ) +{ + return "with message \"" + text + "\""; +} + +inline std::string of_type( std::string text ) +{ + return "of type " + text; +} + +inline std::string pluralise( int n, std::string text ) +{ + return n == 1 ? text : text + "s"; +} + +inline std::ostream & operator<<( std::ostream & os, comment note ) +{ + return os << (note ? " " + note.text : "" ); +} + +inline std::ostream & operator<<( std::ostream & os, location where ) +{ +#ifdef __GNUG__ + return os << where.file << ":" << where.line; +#else + return os << where.file << "(" << where.line << ")"; +#endif +} + +inline void report( std::ostream & os, message const & e, std::string test ) +{ + os << e.where << ": " << e.kind << e.note << ": " << test << ": " << e.what() << std::endl; +} + +template +int run( test const (&specification)[N], std::ostream & os = std::cout ) +{ + int failures = 0; + + for ( auto & testing : specification ) + { + try + { + testing.behaviour(); + } + catch( message const & e ) + { + ++failures; + report( os, e, testing.name ); + } + } + + if ( failures > 0 ) + { + os << failures << " out of " << N << " " << pluralise(N, "test") << " failed." << std::endl; + } + + return failures; +} + +} // namespace lest + +#endif // LEST_LEST_H_INCLUDED diff --git a/RPiSetup/brlaser-master/test/tempfile.h b/RPiSetup/brlaser-master/test/tempfile.h new file mode 100644 index 0000000..f83e306 --- /dev/null +++ b/RPiSetup/brlaser-master/test/tempfile.h @@ -0,0 +1,54 @@ +// This file is part of the brlaser printer driver. +// +// Copyright 2014 Peter De Wachter +// +// brlaser is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 2 of the License, or +// (at your option) any later version. +// +// brlaser is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with brlaser. If not, see . + +#ifndef TEMPFILE_H +#define TEMPFILE_H + +#include +#include +#include + +class tempfile { + public: + explicit tempfile() + : ptr_(0), + size_(0), + file_(open_memstream(&ptr_, &size_)) { + } + + ~tempfile() { + fclose(file_); + free(ptr_); + } + + FILE *file() { + return file_; + } + + std::vector data() { + if (fflush(file_)) + return std::vector(); + return std::vector(ptr_, ptr_ + size_); + } + + private: + char *ptr_; + size_t size_; + FILE *file_; +}; + +#endif // TEMPFILE_H diff --git a/RPiSetup/brlaser-master/test/test_block.cc b/RPiSetup/brlaser-master/test/test_block.cc new file mode 100644 index 0000000..e5fd103 --- /dev/null +++ b/RPiSetup/brlaser-master/test/test_block.cc @@ -0,0 +1,91 @@ +// This file is part of the brlaser printer driver. +// +// Copyright 2014 Peter De Wachter +// +// brlaser is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 2 of the License, or +// (at your option) any later version. +// +// brlaser is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with brlaser. If not, see . + +#include "lest.hpp" +#include +#include +#include "tempfile.h" +#include "../src/block.h" + +typedef std::vector vec; + +const lest::test specification[] = { + "A block is created empty", + [] { + block b; + EXPECT(b.empty()); + }, + + "Adding a line makes a block no longer empty", + [] { + block b; + b.add_line(vec{1}); + EXPECT(!b.empty()); + }, + + "A block can contain 128 lines", + [] { + block b; + for (int i = 0; i < 128; ++i) { + EXPECT(b.line_fits(1)); + b.add_line(vec(1)); + } + EXPECT(!b.line_fits(1)); + }, + + "A block has a size limit of about 16 kilobyte", + [] { + block b; + for (int i = 0; i < 16; ++i) { + EXPECT(b.line_fits(1000)); + b.add_line(vec(1000)); + } + EXPECT(!b.line_fits(400)); + }, + + "Flushing an empty block does nothing", + [] { + block b; + tempfile f; + b.flush(f.file()); + EXPECT(f.data().empty()); + }, + + "Flush() writes the lines to a file with a proper header", + [] { + block b; + for (uint8_t n = 1; n < 6; ++n) { + b.add_line(vec{n, n}); + } + tempfile f; + b.flush(f.file()); + EXPECT(( f.data() == vec{'1','2','w',0,5,1,1,2,2,3,3,4,4,5,5} )); + }, + + "After flush() a block is empty again", + [] { + block b; + b.add_line(vec{1}); + tempfile f; + b.flush(f.file()); + EXPECT(b.empty()); + } +}; + +int main() { + return lest::run(specification); +} diff --git a/RPiSetup/brlaser-master/test/test_lest.cc b/RPiSetup/brlaser-master/test/test_lest.cc new file mode 100644 index 0000000..1eb7d6a --- /dev/null +++ b/RPiSetup/brlaser-master/test/test_lest.cc @@ -0,0 +1,291 @@ +// Copyright 2013 by Martin Moene +// +// Distributed under the Boost Software License, Version 1.0: +// +// Permission is hereby granted, free of charge, to any person or organization +// obtaining a copy of the software and accompanying documentation covered by +// this license (the "Software") to use, reproduce, display, distribute, +// execute, and transmit the Software, and to prepare derivative works of the +// Software, and to permit third-parties to whom the Software is furnished to +// do so, all subject to the following: +// +// The copyright notices in the Software and this entire statement, including +// the above license grant, this restriction and the following disclaimer, +// must be included in all copies of the Software, in whole or in part, and +// all derivative works of the Software, unless such copies or derivative +// works are solely in the form of machine-executable object code generated by +// a source language processor. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT +// SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE +// FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, +// ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. + +#include "lest.hpp" +#include + +using namespace lest; + +const lest::test specification[] = +{ + "Function to suppress warning \"expression has no effect\" acts as identity function", [] + { + EXPECT( false == serum( false ) ); + EXPECT( true == serum( true ) ); + }, + + "Function with_message() returns correct string", [] + { + std::string msg = "Let writing tests become irresistibly easy and attractive."; + EXPECT( "with message \"" + msg + "\"" == with_message( msg ) ); + }, + + "Function of_type() returns correct string", [] + { + std::string msg = "this_type"; + EXPECT( "of type " + msg == of_type( msg ) ); + }, + + "Function pluralise() adds 's' except for 1 item", [] + { + std::string word = "hammer"; + EXPECT( word == pluralise( 1, word ) ); + for ( auto i : {0,2,3,4,5,6,7,8,9,10,11,12} ) + EXPECT( word + "s" == pluralise( i, word ) ); + }, + + "Location constructs properly", [] + { + char const * file = __FILE__; int line = __LINE__; + location where{ file, line }; + EXPECT( file == where.file ); + EXPECT( line == where.line ); + }, + + "Comment constructs properly", [] + { + std::string text = __FILE__; + comment note = text; + EXPECT( text == note.text ); + }, + + "Comment converted to bool indicates absence or presence of comment", [] + { + EXPECT( false == bool( comment( "") ) ); + EXPECT( true == bool( comment("x") ) ); + }, + + "Failure exception type constructs and prints properly", [] + { + std::string name = "test-name"; + failure msg( location{"filename.cpp", 765}, "expression" ); + + std::ostringstream os; + report( os, msg, name ); + +#ifndef __GNUG__ + EXPECT( os.str() == "filename.cpp(765): failed: test-name: expression\n" ); +#else + EXPECT( os.str() == "filename.cpp:765: failed: test-name: expression\n" ); +#endif + }, + + "Expected exception type constructs and prints properly", [] + { + std::string name = "test-name"; + expected msg( location{"filename.cpp", 765}, "expression" ); + + std::ostringstream os; + report( os, msg, name ); + +#ifndef __GNUG__ + EXPECT( os.str() == "filename.cpp(765): failed: didn't get exception: test-name: expression\n" ); +#else + EXPECT( os.str() == "filename.cpp:765: failed: didn't get exception: test-name: expression\n" ); +#endif + }, + + "Unexpected exception type constructs and prints properly", [] + { + std::string name = "test-name"; + unexpected msg( location{"filename.cpp", 765}, "expression", "exception-type" ); + + std::ostringstream os; + report( os, msg, name ); + +#ifndef __GNUG__ + EXPECT( os.str() == "filename.cpp(765): failed: got unexpected exception exception-type: test-name: expression\n" ); +#else + EXPECT( os.str() == "filename.cpp:765: failed: got unexpected exception exception-type: test-name: expression\n" ); +#endif + }, + + "Expect generates no message exception for a succeeding test", [] + { + test pass = { "P", [] { EXPECT( true ); } }; + + try { pass.behaviour(); } + catch(...) { throw failure(location{__FILE__,__LINE__}, "unexpected error generated"); } + }, + + "Expect generates a message exception for a failing test", [] + { + test fail = { "F", [] { EXPECT( false ); } }; + + for (;;) + { + try { fail.behaviour(); } catch ( message & ) { break; } + throw failure(location{__FILE__,__LINE__}, "no error generated"); + } + }, + + "Expect succeeds for success (true) and failure (false)", [] + { + test pass[] = {{ "P", [] { EXPECT( true ); } }}; + test fail[] = {{ "F", [] { EXPECT( false ); } }}; + + std::ostringstream os; + + EXPECT( 0 == run( pass, os ) ); + EXPECT( 1 == run( fail, os ) ); + }, + + "Expect succeeds for integer comparation", [] + { + test pass [] = {{ "P" , [] { EXPECT( 7 == 7 ); EXPECT( 7 != 8 ); + EXPECT( 7 >= 6 ); EXPECT( 7 <= 8 ); + EXPECT( 7 > 6 ); EXPECT( 7 < 8 ); } }}; + test fail_1[] = {{ "F1", [] { EXPECT( 7 == 8 ); } }}; + test fail_2[] = {{ "F2", [] { EXPECT( 7 != 7 ); } }}; + test fail_3[] = {{ "F3", [] { EXPECT( 7 <= 6 ); } }}; + test fail_4[] = {{ "F4", [] { EXPECT( 7 >= 8 ); } }}; + test fail_5[] = {{ "F5", [] { EXPECT( 7 < 6 ); } }}; + test fail_6[] = {{ "F6", [] { EXPECT( 7 > 8 ); } }}; + + std::ostringstream os; + + EXPECT( 0 == run( pass , os ) ); + EXPECT( 1 == run( fail_1, os ) ); + EXPECT( 1 == run( fail_2, os ) ); + EXPECT( 1 == run( fail_3, os ) ); + EXPECT( 1 == run( fail_4, os ) ); + EXPECT( 1 == run( fail_5, os ) ); + EXPECT( 1 == run( fail_6, os ) ); + }, + + "Expect succeeds for string comparation", [] + { + std::string a("a"); std::string b("b"); + test pass [] = {{ "P" , [=]() { EXPECT( a == a ); EXPECT( a != b ); + EXPECT( b >= a ); EXPECT( a <= b ); + EXPECT( b > a ); EXPECT( a < b ); } }}; + test fail_1[] = {{ "F1", [=]() { EXPECT( a == b ); } }}; + test fail_2[] = {{ "F2", [=]() { EXPECT( a != a ); } }}; + test fail_3[] = {{ "F3", [=]() { EXPECT( b <= a ); } }}; + test fail_4[] = {{ "F4", [=]() { EXPECT( a >= b ); } }}; + test fail_5[] = {{ "F5", [=]() { EXPECT( b < a ); } }}; + test fail_6[] = {{ "F6", [=]() { EXPECT( a > b ); } }}; + + std::ostringstream os; + + EXPECT( 0 == run( pass , os ) ); + EXPECT( 1 == run( fail_1, os ) ); + EXPECT( 1 == run( fail_2, os ) ); + EXPECT( 1 == run( fail_3, os ) ); + EXPECT( 1 == run( fail_4, os ) ); + EXPECT( 1 == run( fail_5, os ) ); + EXPECT( 1 == run( fail_6, os ) ); + }, + + "Function run() returns the right failure count", [] + { + test pass [] = {{ "P" , [] { EXPECT( 1==1 ); } }}; + test fail_1[] = {{ "F1", [] { EXPECT( 0==1 ); } }}; + test fail_3[] = {{ "F1", [] { EXPECT( 0==1 ); } }, + { "F2", [] { EXPECT( 0==1 ); } }, + { "F3", [] { EXPECT( 0==1 ); } },}; + + std::ostringstream os; + + EXPECT( 0 == run( pass , os ) ); + EXPECT( 1 == run( fail_1, os ) ); + EXPECT( 3 == run( fail_3, os ) ); + }, + + "Expect succeeds with an unexpected standard exception", [] + { + std::string text = "hello-world"; + test pass[] = {{ "P", [=]() { EXPECT( (throw std::runtime_error(text), true) ); } }}; + + std::ostringstream os; + + EXPECT( 1 == run( pass, os ) ); + EXPECT( std::string::npos != os.str().find(text) ); + }, + + "Expect succeeds with an unexpected non-standard exception", [] + { + test pass[] = {{ "P", [] { EXPECT( (throw 77, true) ); } }}; + + std::ostringstream os; + + EXPECT( 1 == run( pass, os ) ); + }, + + "Expect_throws succeeds with an expected standard exception", [] + { + std::string text = "hello-world"; + test pass[] = {{ "P", [=]() { EXPECT_THROWS( (throw std::runtime_error(text), true) ); } }}; + test fail[] = {{ "F", [ ]() { EXPECT_THROWS( true ); } }}; + + std::ostringstream os; + + EXPECT( 0 == run( pass, os ) ); + EXPECT( 1 == run( fail, os ) ); + }, + + "Expect_throws succeeds with an expected non-standard exception", [] + { + test pass[] = {{ "P", [] { EXPECT_THROWS( (throw 77, true) ); } }}; + test fail[] = {{ "F", [] { EXPECT_THROWS( true ); } }}; + + std::ostringstream os; + + EXPECT( 0 == run( pass, os ) ); + EXPECT( 1 == run( fail, os ) ); + }, + + "Expect_throws_as succeeds with a specific expected standard exception", [] + { + test pass[] = {{ "P", [] { EXPECT_THROWS_AS( (throw std::bad_alloc(), true), std::bad_alloc ); } }}; + test fail[] = {{ "F", [] { EXPECT_THROWS_AS( (throw std::bad_alloc(), true), std::runtime_error ); } }}; + + std::ostringstream os; + + EXPECT( 0 == run( pass, os ) ); + EXPECT( 1 == run( fail, os ) ); + }, + + "Expect_throws_as succeeds with a specific expected non-standard exception", [] + { + test pass[] = {{ "P", [] { EXPECT_THROWS_AS( (throw 77, true), int ); } }}; + test fail[] = {{ "F", [] { EXPECT_THROWS_AS( (throw 77, true), std::runtime_error ); } }}; + + std::ostringstream os; + + EXPECT( 0 == run( pass, os ) ); + EXPECT( 1 == run( fail, os ) ); + }, +}; + +int main() +{ + return lest::run( specification ); +} + +// cl -nologo -Wall -EHsc test_lest.cpp && test_lest +// g++ -Wall -Wextra -Weffc++ -std=c++11 -o test_lest.exe test_lest.cpp && test_lest + diff --git a/RPiSetup/brlaser-master/test/test_line.cc b/RPiSetup/brlaser-master/test/test_line.cc new file mode 100644 index 0000000..9ab12c6 --- /dev/null +++ b/RPiSetup/brlaser-master/test/test_line.cc @@ -0,0 +1,150 @@ +// This file is part of the brlaser printer driver. +// +// Copyright 2014 Peter De Wachter +// +// brlaser is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 2 of the License, or +// (at your option) any later version. +// +// brlaser is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with brlaser. If not, see . + +#include "lest.hpp" +#include +#include +#include +#include "../src/line.h" + +typedef std::vector vec; + + +uint8_t sub(uint8_t offset, uint8_t count) { + assert(offset < 16); + assert(count < 8); + return (offset << 3) | count; +} + +uint8_t rep(uint8_t offset, uint8_t count) { + assert(offset < 4); + assert(count < 32); + return 128 | (offset << 5) | count; +} + + +const lest::test specification[] = { + "Don't crash on zero-length lines", + [] { + EXPECT(( encode_line(vec{}) == vec{0xFF} )); + EXPECT(( encode_line(vec{}, vec{}) == vec{0xFF} )); + }, + + "Encoding an initial blank line", + [] { + EXPECT(( encode_line(vec{0,0,0}) == vec{0xFF} )); + }, + + "Encoding an initial non-blank line", + [] { + EXPECT(( encode_line(vec{1,2,3}) == (vec{1,sub(0,2),1,2,3}) )); + }, + + "Encoding a (non-initial) blank line", + [] { + EXPECT(( encode_line(vec{0,0,0}, vec{1,2,3}) == vec{0xFF} )); + }, + + "Encoding a repeated line", + [] { + EXPECT(( encode_line(vec{1,2,3}, vec{1,2,3}) == vec{0} )); + }, + + "Using a subsitute command", + [] { + EXPECT(( encode_line(vec{0,0,1,2,3,0,0}, vec(7)) == vec{1,sub(2,2),1,2,3} )); + }, + + "Using a repeat command", + [] { + EXPECT(( encode_line(vec{0,0,1,1,0,0}, vec(6)) == vec{1,rep(2,0),1} )); + }, + + "Repeat command followed by substitute command", + [] { + EXPECT(( encode_line(vec{1,1,1,2,3}, vec(5)) == vec{2,rep(0,1),1,sub(0,1),2,3} )); + }, + + "Substitute comand followed by repeat command", + [] { + EXPECT(( encode_line(vec{3,2,1,1,1}, vec(5)) == vec{2,sub(0,1),3,2,rep(0,1),1} )); + }, + + "Substitute with an unmodified byte in the middle", + [] { + EXPECT(( encode_line(vec{1,2,3,0,1,2,3}, vec(7)) == vec{1,sub(0,6),1,2,3,0,1,2,3} )); + }, + + "Substitue with two unmodified bytes in the middle", + [] { + EXPECT(( encode_line(vec{1,2,3,0,0,1,2,3}, vec(8)) == vec{2,sub(0,2),1,2,3,sub(2,2),1,2,3} )); + }, + + "Repeat with an unmodified byte in the middle", + [] { + EXPECT(( encode_line(vec{1,1,1,0,1,1,1}, vec(7)) == vec{2,rep(0,1),1,rep(1,1),1} )); + }, + + "254 edits needed for a single line", + [] { + vec line, result; + for (int i = 0; i < 254; ++i) + line.insert(line.end(), {0,0,1}); + result.push_back(254); + for (int i = 0; i < 254; ++i) + result.insert(result.end(), {sub(2,0),1}); + EXPECT(( encode_line(line, vec(line.size())) == result )); + }, + + "Give up if more than 254 edits needed...", + [] { + vec line, result; + for (int i = 0; i < 255; ++i) + line.insert(line.end(), {0,0,1}); + result.push_back(254); + for (int i = 0; i < 253; ++i) + result.insert(result.end(), {sub(2,0),1}); + result.insert(result.end(), {sub(2,3),1,0,0,1}); + EXPECT(( encode_line(line, vec(line.size())) == result )); + }, + + "Repeat command with overflow bytes", + [] { + vec line(3, 0); + line.insert(line.end(), 512, 1); + vec ref(line.size(), 0); + vec expected{1,rep(3,31),0,255,224,1}; + EXPECT(encode_line(line, ref) == expected); + }, + + "Substitute command with overflow bytes", + [] { + vec expected{1,sub(15,7),255,0,255,237}; + vec line(270, 0); + for (int i = 0; i < 250; ++i) { + expected.insert(expected.end(), {1,2}); + line.insert(line.end(), {1,2}); + } + vec ref(line.size(), 0); + EXPECT(encode_line(line, ref) == expected); + }, +}; + + +int main() { + return lest::run(specification); +} diff --git a/RPiSetup/etc/apache2/conf-available/opendrs.conf b/RPiSetup/etc/apache2/conf-available/opendrs.conf new file mode 100644 index 0000000..91d3d92 --- /dev/null +++ b/RPiSetup/etc/apache2/conf-available/opendrs.conf @@ -0,0 +1,10 @@ +# OpenMDRS configuration + +Alias /opendrs /var/www/opendrs + + + Options Indexes FollowSymLinks MultiViews + AllowOverride None + Order allow,deny + allow from all + diff --git a/RPiSetup/etc/apache2/mods-available/alias.conf b/RPiSetup/etc/apache2/mods-available/alias.conf new file mode 100644 index 0000000..e8341b8 --- /dev/null +++ b/RPiSetup/etc/apache2/mods-available/alias.conf @@ -0,0 +1,24 @@ + + # Aliases: Add here as many aliases as you need (with no limit). The format is + # Alias fakename realname + # + # Note that if you include a trailing / on fakename then the server will + # require it to be present in the URL. So "/icons" isn't aliased in this + # example, only "/icons/". If the fakename is slash-terminated, then the + # realname must also be slash terminated, and if the fakename omits the + # trailing slash, the realname must also omit it. + # + # We include the /icons/ alias for FancyIndexed directory listings. If + # you do not use FancyIndexing, you may comment this out. + + #Alias /icons/ "/usr/share/apache2/icons/" + + + Options FollowSymlinks + AllowOverride None + Require all granted + + + + +# vim: syntax=apache ts=4 sw=4 sts=4 sr noet diff --git a/RPiSetup/etc/apache2/sites-available/000-default.conf b/RPiSetup/etc/apache2/sites-available/000-default.conf new file mode 100644 index 0000000..bf8f25a --- /dev/null +++ b/RPiSetup/etc/apache2/sites-available/000-default.conf @@ -0,0 +1,31 @@ + + # The ServerName directive sets the request scheme, hostname and port that + # the server uses to identify itself. This is used when creating + # redirection URLs. In the context of virtual hosts, the ServerName + # specifies what hostname must appear in the request's Host: header to + # match this virtual host. For the default virtual host (this file) this + # value is not decisive as it is used as a last resort host regardless. + # However, you must set it for any further virtual host explicitly. + #ServerName www.example.com + + ServerAdmin webmaster@localhost + DocumentRoot /var/www/opendrs + + # Available loglevels: trace8, ..., trace1, debug, info, notice, warn, + # error, crit, alert, emerg. + # It is also possible to configure the loglevel for particular + # modules, e.g. + #LogLevel info ssl:warn + + ErrorLog ${APACHE_LOG_DIR}/error.log + CustomLog ${APACHE_LOG_DIR}/access.log combined + + # For most configuration files from conf-available/, which are + # enabled or disabled at a global level, it is possible to + # include a line for only one particular virtual host. For example the + # following line enables the CGI configuration for this host only + # after it has been globally disabled with "a2disconf". + #Include conf-available/serve-cgi-bin.conf + + +# vim: syntax=apache ts=4 sw=4 sts=4 sr noet diff --git a/RPiSetup/etc/apt/sources.list.d/webmin.list b/RPiSetup/etc/apt/sources.list.d/webmin.list new file mode 100644 index 0000000..7b1f58d --- /dev/null +++ b/RPiSetup/etc/apt/sources.list.d/webmin.list @@ -0,0 +1 @@ +deb http://download.webmin.com/download/repository sarge contrib diff --git a/RPiSetup/etc/cups/cupsd.conf b/RPiSetup/etc/cups/cupsd.conf new file mode 100644 index 0000000..524fdde --- /dev/null +++ b/RPiSetup/etc/cups/cupsd.conf @@ -0,0 +1,146 @@ +# +# Configuration file for the CUPS scheduler. See "man cupsd.conf" for a +# complete description of this file. +# + +# Log general information in error_log - change "warn" to "debug" +# for troubleshooting... +LogLevel warn +PageLogFormat + +# Deactivate CUPS' internal logrotating, as we provide a better one, especially +# LogLevel debug2 gets usable now +MaxLogSize 0 + +# Only listen for connections from the local machine. +#Listen localhost:631 +Port 631 +Listen 0.0.0.0:631 +Listen /var/run/cups/cups.sock + +# Show shared printers on the local network. +Browsing On +BrowseLocalProtocols dnssd + +# Default authentication type, when authentication is required... +DefaultAuthType Basic + +# Web interface setting... +WebInterface Yes + +# Restrict access to the server... + + Order allow,deny + Allow from 192.168.100.* + + +# Restrict access to the admin pages... + + AuthType Basic + Require valid-user + Allow all + Order allow,deny + + +# Restrict access to configuration files... + + AuthType Default + Require user @SYSTEM + Order allow,deny + + +# Restrict access to log files... + + AuthType Default + Require user @SYSTEM + Order allow,deny + + +# Set the default printer/job policies... + + # Job/subscription privacy... + JobPrivateAccess default + JobPrivateValues default + SubscriptionPrivateAccess default + SubscriptionPrivateValues default + + # Job-related operations must be done by the owner or an administrator... + + Order deny,allow + + + + Require user @OWNER @SYSTEM + Order deny,allow + + + # All administration operations require an administrator to authenticate... + + AuthType Default + Require user @SYSTEM + Order deny,allow + + + # All printer operations require a printer operator to authenticate... + + AuthType Default + Require user @SYSTEM + Order deny,allow + + + # Only the owner or an administrator can cancel or authenticate a job... + + Require user @OWNER @SYSTEM + Order deny,allow + + + + Order deny,allow + + + +# Set the authenticated printer/job policies... + + # Job/subscription privacy... + JobPrivateAccess default + JobPrivateValues default + SubscriptionPrivateAccess default + SubscriptionPrivateValues default + + # Job-related operations must be done by the owner or an administrator... + + AuthType Default + Order deny,allow + + + + AuthType Default + Require user @OWNER @SYSTEM + Order deny,allow + + + # All administration operations require an administrator to authenticate... + + AuthType Default + Require user @SYSTEM + Order deny,allow + + + # All printer operations require a printer operator to authenticate... + + AuthType Default + Require user @SYSTEM + Order deny,allow + + + # Only the owner or an administrator can cancel or authenticate a job... + + AuthType Default + Require user @OWNER @SYSTEM + Order deny,allow + + + + Order deny,allow + + diff --git a/RPiSetup/etc/drs.conf b/RPiSetup/etc/drs.conf new file mode 100644 index 0000000..97dc6af --- /dev/null +++ b/RPiSetup/etc/drs.conf @@ -0,0 +1,2 @@ +DRS_SERVER="http://localhost" + diff --git a/RPiSetup/lib/systemd/system/getty@tty1.service b/RPiSetup/lib/systemd/system/getty@tty1.service new file mode 100644 index 0000000..5e77963 --- /dev/null +++ b/RPiSetup/lib/systemd/system/getty@tty1.service @@ -0,0 +1,53 @@ +# This file is part of systemd. +# +# systemd is free software; you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation; either version 2.1 of the License, or +# (at your option) any later version. + +[Unit] +Description=Getty on %I +Documentation=man:agetty(8) man:systemd-getty-generator(8) +Documentation=http://0pointer.de/blog/projects/serial-console.html +After=systemd-user-sessions.service plymouth-quit-wait.service +After=rc-local.service + +# If additional gettys are spawned during boot then we should make +# sure that this is synchronized before getty.target, even though +# getty.target didn't actually pull it in. +Before=getty.target +IgnoreOnIsolate=yes + +# IgnoreOnIsolate causes issues with sulogin, if someone isolates +# rescue.target or starts rescue.service from multi-user.target or +# graphical.target. +Conflicts=rescue.service +Before=rescue.service + +# On systems without virtual consoles, don't start any getty. Note +# that serial gettys are covered by serial-getty@.service, not this +# unit. +ConditionPathExists=/dev/tty0 + +[Service] +# the VT is cleared by TTYVTDisallocate +ExecStart=-/sbin/agetty -a customer --noclear %I $TERM +Type=idle +Restart=always +RestartSec=0 +UtmpIdentifier=%I +TTYPath=/dev/%I +TTYReset=yes +TTYVHangup=yes +TTYVTDisallocate=yes +KillMode=process +IgnoreSIGPIPE=no +SendSIGHUP=yes + +# Unset locale for the console getty since the console has problems +# displaying some internationalized messages. +Environment=LANG= LANGUAGE= LC_CTYPE= LC_NUMERIC= LC_TIME= LC_COLLATE= LC_MONETARY= LC_MESSAGES= LC_PAPER= LC_NAME= LC_ADDRESS= LC_TELEPHONE= LC_MEASUREMENT= LC_IDENTIFICATION= + +[Install] +WantedBy=getty.target +DefaultInstance=tty1 diff --git a/RPiSetup/server-setup.sh b/RPiSetup/server-setup.sh new file mode 100644 index 0000000..637e3af --- /dev/null +++ b/RPiSetup/server-setup.sh @@ -0,0 +1,84 @@ +#!/bin/bash + +# server-setup.sh +# OpenDRS Online Discrepancy Reporting System +# Copyright (C) 2018 Rod Wright + +# SPDX-License-Identifier: GPL-2.0 + +echo "" +echo "" +echo "" +echo "" +echo "This script will configure a Raspberry Pi to be an OpenDRS server." +echo "" +echo "" +echo "You should have already run the terminal-setup.sh script in this directory." +echo "" +if [ ! -e "/etc/drs.conf" ] +then + echo "It looks like the terminal-setup.sh script has not been run yet." + echo "This is required before running the server-setup.sh script." + echo "Aborting." + exit 1 +fi +echo "It looks like you have. Continuing." +echo "" +echo "" +echo "Updating Raspbian..." +sleep 10 +apt update && apt dist-upgrade -y +echo "...done." +echo "" +echo "" +echo "Installing required server packages. Select apache2 as the web server to configure" +echo "automatically. Choose Yes when prompted to install phpmyadmin database and allow it to" +echo "generate a random password(leave the field blank)." +echo -n "Press enter to continue." +read continueok +sleep 10 +apt install -y apache2 php php-mcrypt mysql-server phpmyadmin +cp etc/apache2/mods-available/alias.conf /etc/apache2/mods-available +service apache2 restart +echo "...done." +echo "" +echo "" +echo "Configuring the mysql installation. Accept the defaults at the following prompts." +echo "Remember the password you set here. You'll need it during the OpenDRS install." +echo -n "Press enter to proceed." +read continueok +mysql_secure_installation +mysql mysql -e "update user set plugin='';flush privileges;" +echo "" +echo "" +echo "Installing OpenDRS." +DRS_VERSION="1.1.0" +install_path="/var/www/opendrs-$DRS_VERSION" +httpd_user_group="www-data:www-data" +new_link_name="/var/www/opendrs" +echo "Creating installation directory . . ." +mkdir $install_path +echo "Installing distribution . . ." +cp -Rv ../distfiles/* $install_path +echo "Setting file ownership . . ." +chown -R $httpd_user_group $install_path +ln -s $install_path $new_link_name +cp etc/apache2/conf-available/opendrs.conf /etc/apache2/conf-available +cp etc/apache2/sites-available/000-default.conf /etc/apache2/sites-available +a2enconf opendrs +echo "DRS_SERVER=\"http://localhost\"" > /etc/drs.conf +service apache2 restart +echo "" +echo "Server configuration is now complete. " +echo "Once the reboot is complete you will need to do the final installation of OpenDRS through" +echo "the browser. You will need the password you set during the mysql installation above." +echo "" +echo "" +echo "It is highly recommended that after rebooting, you press ctrl-alt-F2," +echo "log in as pi, then create new users for yourself and any other" +echo "administrators using the cloneuser command (cloneuser pi newlogin)." +echo "" +echo "" +echo -n "Press enter to reboot now." +read rebootok +reboot diff --git a/RPiSetup/terminal-setup.sh b/RPiSetup/terminal-setup.sh new file mode 100644 index 0000000..ac8a153 --- /dev/null +++ b/RPiSetup/terminal-setup.sh @@ -0,0 +1,123 @@ +#!/bin/bash + +# terminal-setup.sh +# OpenDRS Online Maintenance Tracking System +# Copyright (C) 2018 Rod Wright + +# SPDX-License-Identifier: GPL-2.0 + +echo "" +echo "" +echo "" +echo "" +echo "This script will configure a Raspberry Pi to be an OpenDRS terminal." +echo "" +echo "" +echo "You should have already completed the following steps:" +echo "" +echo "1. Created a Raspbian Lite SD card from the latest release." +echo "2. Installed the card in a Raspberry Pi, booted it, and logged in as pi." +echo "3. Ran sudo raspi-config and :" +echo " a. Set a strong password for the pi user." +echo " b. Set hostname and, if accessing a network through wifi, the SSID and" +echo " passphrase under Network Options." +echo " c. Set locale, timezone, keyboard layout, and wifi country under" +echo " Localisation Options." +echo " d. Enable SSH under Interfacing Options." +echo " e. Set overscan as required to get rid of any black borders around the" +echo " edge of the display." +echo " f. Reboot when prompted and logged back in as pi." +echo "4. Transferred the OpenDRS distribution package to /home/pi on the" +echo " Raspberry Pi and extracted it." +echo "5. Changed directory to RPiSetup in the the extracted distribution directory." +echo "6. Ran sudo ./terminal-setup.sh (this file)." +echo "" +echo "If you have completed these steps, press enter to continue. If not," +echo -n "press ctrl-c to quit." +read continueok +echo "" +echo "" +echo "Updating Raspbian..." +sleep 10 +apt update && apt dist-upgrade -y +echo "...done." +echo "Copying files..." +chmod +x usr/local/bin/* +cp usr/local/bin/* /usr/local/bin +cp etc/apt/sources.list.d/* /etc/apt/sources.list.d +cp lib/systemd/system/getty@tty1.service /lib/systemd/system +rm /lib/systemd/system/ctrl-alt-del.target +ln -s /dev/null /lib/systemd/system/ctrl-alt-del.target +echo "...done." +echo "" +echo "" +echo "Installing required terminal packages..." +sleep 10 +wget http://www.webmin.com/jcameron-key.asc +apt-key add jcameron-key.asc +rm jcameron-key.asc +apt update +apt install -y --no-install-recommends xorg openbox chromium-browser +apt install -y webmin cups libcups2-dev cmake +addgroup lpadmin +usermod -a -G lpadmin pi +systemctl enable getty@tty1.service +service cups-browsed stop +systemctl disable cups-browsed.service +cp etc/cups/cupsd.conf /etc/cups/ +service cups restart +cd brlaser-master +cmake . && make && make install +cd .. +myhn=`hostname` +echo "Drivers have been installed for the Brother HL-L2300D printer that may come" +echo "with the terminal." +echo "After the terminal is set up and working, you can use another network" +echo "computer to point a web browser to $myhn:631 to add this or another printer" +echo "of your choosing." +echo -n "Press enter to continue." +read continueok +echo "" +echo "" +echo "Creating customer user..." +cloneuser pi customer +mkdir -p /home/customer/.config/openbox +cp home/customer/.profile /home/customer +cp home/customer/.config/openbox/rc.xml /home/customer/.config/openbox +chown -R customer:customer /home/customer/.config /home/customer/.profile +echo "...done." +echo "" +echo "" +echo "This terminal will need to communicate with an OpenDRS server. If" +echo "you intend to host the server directly on this terminal, accept the" +echo "default URL shown. Otherwise, enter the server URL." +echo -n "[http://localhost] :" +read server_url +if [[ $server_url == "" ]] +then + server_url="http://localhost" +fi +echo "DRS_SERVER=\"$server_url\"" > /etc/drs.conf +echo "" +echo "" +echo "Terminal configuration is now complete. " +echo "" +echo "" +echo "If this Raspberry Pi will also be used as the OpenDRS server, you will need" +echo "to run the server-setup.sh script in this same directory." +echo "Would you like to do that now? enter Y if so, or press enter to leave this" +echo -n "as a terminal only and reboot :" +read serverchoice +if [[ $serverchoice == "Y" || $serverchoice == "y" ]] +then + ./server-setup.sh +fi +echo "It is highly recommended that after rebooting, you press ctrl-alt-F2," +echo "log in as pi, then create new users for yourself and any other" +echo "administrators using the cloneuser command (cloneuser pi newlogin)." +echo "" +echo "" +echo "Setup complete." +echo -n "Press enter to reboot now." +read rebootok +reboot diff --git a/RPiSetup/usr/local/bin/cloneuser b/RPiSetup/usr/local/bin/cloneuser new file mode 100644 index 0000000..1b129d9 --- /dev/null +++ b/RPiSetup/usr/local/bin/cloneuser @@ -0,0 +1,18 @@ +#!/bin/bash +SRC=$1 +DEST=$2 + +SRC_GROUPS=`id -Gn ${SRC}` +SRC_SHELL=$(awk -F : -v name=${SRC} '(name == $1) { print $7 }' /etc/passwd) + +echo "Creating user $DEST" +useradd --shell ${SRC_SHELL} --create-home ${DEST} +for grp in ${SRC_GROUPS};do + if [ "$grp" != "$SRC" ]; then + echo "Adding user $DEST to group $grp." + usermod -a -G $grp ${DEST} + fi +done +passwd ${DEST} + + diff --git a/RPiSetup/usr/local/bin/rpi-clone b/RPiSetup/usr/local/bin/rpi-clone new file mode 100644 index 0000000..61842be --- /dev/null +++ b/RPiSetup/usr/local/bin/rpi-clone @@ -0,0 +1,1642 @@ +#!/bin/bash + +version=2.0.20 + +# auto run grub-install if grub detected +grub_auto=1 + +PGM=`basename $0` +setup_command="$PGM-setup" + +rsync_options="--force -rltWDEHXAgoptx" + +if [ `id -u` != 0 ] +then + echo -e "$PGM needs to be run as root.\n" + exit 1 +fi + +confirm() + { + if ((unattended || (initialize && Unattended) )) + then + return 0 + fi + printf "\n%s (yes/no): " "$1" + read resp + if [ "$resp" = "y" ] || [ "$resp" = "yes" ] + then + return 0 + fi + if [ "$2" == "abort" ] + then + echo -e "Aborting!\n" + exit 0 + fi + return 1 + } + +commands="rsync parted fdisk findmnt column fsck.vfat" +packages="rsync parted util-linux mount bsdmainutils dosfstools" +need_packages="" + +idx=1 +for cmd in $commands +do + if ! command -v $cmd > /dev/null + then + pkg=$(echo "$packages" | cut -d " " -f $idx) + printf "%-30s %s\n" "Command not found: $cmd" "Package required: $pkg" + need_packages="$need_packages $pkg" + fi + ((++idx)) +done + +if [ "$need_packages" != "" ] +then + confirm "Do you want to apt-get install the packages?" "abort" + apt-get install -y --no-install-recommends $need_packages +fi + +clone=/mnt/clone +clone_src=/mnt/clone-src +clone_log=/var/log/$PGM.log + +HOSTNAME=`hostname` + + +usage() + { + echo $" +usage: $PGM sdN {-v|--verbose} {-f|--force-initialize} {-f2} + {-u|--unattended} {-U|--Unattended} {-q|--quiet} + {-s|--setup host} {-e|--edit-fstab sdX } {-m|--mountdir dir } + {-L|--label-partitions label} {-l|--leave-sd-usb-boot} + {-a|--all-sync} {-F|--Force-sync} {-x} {-V|--version} + {--convert-fstab-to-partuuid} + + -v - verbose rsync, list all files as they are copied. + -f - force initialize the destination disk by imaging the booted disk + partition structure. File systems are then synced or imaged. + -f2 - force initialize only the first 2 partitions to the destination. + So a multi partition USB boot can initialize clone back to + a 2 partition SD card. + -u - unattended clone if not initializing. No confirmations asked, + but abort if disk needs initializing or on error. + -U - unattended even if initializing. No confirmations asked, + but abort only on errors. + -q - quiet mode, no output unless errors or initializing. Implies -u. + -s host - add 'host' to args passed to script rpi-clone-setup and run it + after cloning but before unmounting partitions. For setting + clone disk hostname, but args can be what the script expects. + You can give multiple "-s arg" options. + -e sdX - edit destination fstab to change booted device names to new + device 'sdX'. This is Only for fstabs that use device names. + Used for setting up a USB bootable disk. + -m dir - Add dir to a custom list of mounted directories to sync. Then + the custom list will be synced instead of the default of all + mounted directories. The root directory is always synced. + Not for when initializing. + -L lbl - label for ext type partitions. If ends with '#', replace with + partition number and label all ext partitions. Otherwise, + apply label to root partition only. + -l - leave SD card to USB boot alone when cloning to SD card mmcblk0 + from a USB boot. This preserves a SD card to USB boot setup + by leaving the SD card cmdline.txt using the USB root. When + cloning to USB from SD card this option sets up the SD card + cmdline.txt to boot to the USB disk. + -a - Sync all partitions if types compatible, not just mounted ones. + -F - force file system sync even if errors. + If source used > destination space error, do the sync anyway. + If a source partition mount error, skip it and do other syncs. + -x - use set -x for very verbose bash shell script debugging + -V - print rpi-clone version. + + Clone a booted file system to a destination disk which is bootable. + + The destination disk is a SD card (USB card reader) or USB disk 'sdN' plugged + into a USB port. The 'sdN' name should be a full disk name like sda and not + a partition name like sda1. $PGM works on a Raspberry Pi and can work on + other systems. For a destination disk that shows up as sda, run: + + $ sudo rpi-clone sda + + Clones can be from a booted SD card or USB disk. For a description, example + clone runs and example usage of above options, see the README.md at: + + https://github.com/billw2/rpi-clone + + A line logging a $PGM run is written to $clone_log. + + Download: + git clone https://github.com/billw2/rpi-clone +" + exit 1 + } + +readable() + { + val=$1 + if [ "$val" == "" ] + then + result=" ??" + else + blk_size=$2 + val=$((val / 1000 * blk_size)) + + if ((val < 1000000)) + then + result=$(echo $val \ + | awk '{ byte =$1 /1000; printf "%.1f%s", byte, "MB" }') + elif ((val < 1000000000)) + then + result=$(echo $val \ + | awk '{ byte =$1 /1000/1000; printf "%.1f%s", byte, "GB" }') + else + result=$(echo $val \ + | awk '{ byte =$1 /1000/1000/1000; printf "%.1f%s", byte, "TB" }') + fi + fi + printf -v "${3}" "%s" "$result" + } + +qecho() + { + if ((!quiet)) + then + echo "$@" + fi + } + +qprintf() + { + if ((!quiet)) + then + printf "$@" + fi + } + +unmount_or_abort() + { + if [ "$1" == "" ] + then + return + fi + qprintf "\n $2\n The clone cannot proceed unless it is unmounted." + + if confirm "Do you want to unmount $1?" "abort" + then + if ! umount $1 + then + echo "$PGM could not unmount $1." + echo -e "Aborting!\n" + exit 0 + fi + fi + } + +unmount_list() + { + if [ "$1" == "" ] + then + return + fi + for dir in $1 + do + qecho " unmounting $dir" + if ! umount $dir + then + qecho " Failed to unmount: $dir" + fi + done + } + +mount_partition() + { + qecho " Mounting $1 on $2" + + if ! mount $1 $2 + then + echo " Mount failure of $1 on $2." + if [ "$3" != "" ] + then + unmount_list $3 + fi + echo "Aborting!" + exit 1 + fi + } + +rsync_file_system() + { + src_dir="$1" + dst_dir="$2" + + qprintf " => rsync $1 $2 $3 ..." + + if [ "$3" == "with-root-excludes" ] + then + rsync $rsync_options --delete \ + $exclude_swapfile \ + --exclude '.gvfs' \ + --exclude '/dev/*' \ + --exclude '/mnt/clone/*' \ + --exclude '/proc/*' \ + --exclude '/run/*' \ + --exclude '/sys/*' \ + --exclude '/tmp/*' \ + --exclude 'lost\+found/*' \ + $src_dir \ + $dst_dir + else + rsync $rsync_options --delete \ + --exclude '.gvfs' \ + --exclude 'lost\+found/*' \ + $src_dir \ + $dst_dir + fi + qecho "" + } + +print_partitions() + { + if ((quiet)) && ((!initialize)) + then + return + fi + n_parts=$(( (n_src_parts >= n_dst_parts) ? n_src_parts : n_dst_parts )) + + readable $src_disk_size "512" src_size_readable + readable $dst_disk_size "512" dst_size_readable + + printf "\n%-43s%s" "Booted disk: $src_disk $src_size_readable" \ + "Destination disk: $dst_disk $dst_size_readable" + echo $" +---------------------------------------------------------------------------" + out=$'Part, Size,FS,Label ,Part, Size,FS,Label\n' + for ((p = 1; p <= n_parts; p++)) + do + if ((p <= n_src_parts && src_exists[p])) + then + readable ${src_size_sectors[p]} "512" tmp + printf -v sectors_readable "%7s" $tmp + pname="$p ${src_name[p]}" + out=${out}$"$pname,$sectors_readable,${src_fs_type[p]},${src_label[p]}," + else + out=${out}$" , , , ," + fi + + if ((p <= n_dst_parts && dst_exists[p])) + then + readable ${dst_size_sectors[p]} "512" tmp + printf -v sectors_readable "%7s" $tmp + out=${out}$"$p,$sectors_readable,${dst_fs_type[p]},${dst_label[p]}," + else + out=${out}$" , , , ," + fi + out=${out}$'\n' + done + + echo $"$out" | column -t -s ',' + + if ((alt_root_part_num > 0)) + then + echo $" +** Assuming destination root partition for the clone is $dst_part_base$root_part_num + The root FS mount is not from booted $src_disk. It is ${src_root_dev#/dev/}" + + fi + echo $"---------------------------------------------------------------------------" + } + +print_sync_actions() + { + if ((quiet)) + then + return + fi + for ((p = 1; p <= n_src_parts; p++)) + do + if ((!src_exists[p])) + then + continue + fi + if ((p == root_part_num && alt_root_part_num > 0)) + then + part=${src_root_dev#/dev/} + flow="$part to $dst_part_base$p" + else + flow="to $dst_part_base$p" + fi + if ((src_sync_part[p])) + then + readable ${src_used_sectors[p]} "512" used + readable ${dst_size_sectors[p]} "512" size + printf "%-22s%-14s : SYNC %s\n" \ + "${src_mounted_dir[p]}" "(${used} used)" \ + "$flow (${size} size)" + fi + done + } + +print_image_actions() + { + for ((p = 1; p <= n_src_parts; p++)) + do + if ((!src_exists[p])) + then + continue + fi + pname="$p ${src_name[p]}" + fs_type=${src_fs_type[$p]} + + if ((p == root_part_num && alt_root_part_num > 0)) + then + part=${src_root_dev#/dev/} + flow="$part to $dst_part_base$p" + else + flow="to $dst_part_base$p" + fi + + action="" + if ((p <= n_image_parts)) + then + if [ "${src_mounted_dir[p]}" == "/boot" ] && ((p == 1)) + then + action="IMAGE $flow FSCK" + elif [ "$fs_type" == "swap" ] + then + action="MKSWAP" + elif ((p != ext_part_num)) + then + if [ "${src_mounted_dir[p]}" != "" ] || ((p == n_src_parts)) + then + if ((p < n_src_parts || last_part_space || force_sync)) + then + action="MKFS SYNC $flow" + else + action="MKFS **NO SYNC**" + fi + else + action="IMAGE $flow" + fi + fi + fi + + if ((p == n_image_parts)) + then + readable ${src_used_sectors[n_image_parts]} "512" used + printf "%-22s%-14s : RESIZE($image_space_readable) %s\n" \ + "$pname" "(${used} used)" "$action" + elif ((src_used_sectors[$p] > 0 && p < n_image_parts)) + then + readable ${src_used_sectors[p]} "512" used + printf "%-22s%-14s : $action\n" "$pname" "(${used} used)" + elif [ "$action" != "" ] + then + printf "%-36s : $action\n" "$pname" + fi + done + } + +print_options() + { + if ((quiet)) + then + return + fi + echo $"---------------------------------------------------------------------------" + + if ((force_2_parts)) + then + printf "%-22s : %s\n" "-f2" \ + "force initialize to first two partitions only" + fi + if [ "$edit_fstab_name" != "" ] + then + printf "%-22s : %s\n" "-e clone fstab edit" \ + "edit $src_part_base device entries to $edit_fstab_name" + fi + if [ "$ext_label" != "" ] + then + rep="${ext_label: -1}" + if [ "$rep" == "#" ] + then + msg="all ext partition types" + else + msg="root partition only" + fi + printf "%-22s : %s\n" "-L $ext_label" \ + "volume label for $msg." + fi + if ((leave_sd_usb_boot)) + then + if ((SD_slot_dst)) + then + msg="leave SD card cmdline.txt bootable to USB" + elif ((SD_slot_boot)) + then + msg="install boot to USB cmdline.txt on SD card" + else + msg="-l ignored. Src or dst is not a SD card slot." + fi + printf "%-22s : %s\n" "-l SD to USB boot mode" "$msg" + fi + if [ "$setup_args" != "" ] + then + printf "%-22s : %s\n" "Run setup script" "$setup_command $setup_args" + else + printf "%-22s : no\n" "Run setup script" + fi + if ((have_grub)) + then + printf "%-22s : %s\n" "Run grub" \ + "grub-install --root-directory=$clone /dev/$dst_disk" + fi + printf "%-22s : %s\n" "Verbose mode" "$verbose" + printf "%-23s:\n" "-----------------------" + } + +ext_label() + { + pnum=$1 + fs_type=$2 + flag=$3 + label_arg="" + + if [ "$ext_label" != "" ] && [[ "$fs_type" == *"ext"* ]] + then + rep="${ext_label: -1}" + if [ "$rep" == "#" ] + then + label_arg=${ext_label:: -1} + label_arg="$flag $label_arg$pnum" + elif ((pnum == root_part_num)) + then + label_arg="$flag $ext_label" + fi + fi + printf -v "${4}" "%s" "$label_arg" + } + +get_src_disk() + { + partition=${1#/dev/} + disk=${partition:: -1} + num="${partition: -1}" + if [[ $disk == *"mmcblk"* ]] + then + SD_slot_boot=1 + disk=${disk:0:7} + src_part_base=${disk}p + fi + printf -v "${2}" "%s" "$disk" + printf -v "${3}" "%s" "$num" + } + + +# ==== source (booted) disk info and default mount list +# +src_boot_dev=`findmnt /boot -o source -n` +src_root_dev=`findmnt / -o source -n` +SD_slot_boot=0 +SD_slot_dst=0 +src_part_base="" + +boot_part_num=0 +alt_root_part_num=0 + + +if [ "$src_boot_dev" == "" ] +then + get_src_disk "$src_root_dev" "src_disk" "unused" +else + get_src_disk "$src_boot_dev" "src_disk" "boot_part_num" +fi + +get_src_disk "$src_root_dev" "src_root_disk" "root_part_num" + +if [ "$src_disk" == "" ] +then + echo "Cannot find booted device." + exit 1 +fi + +if [ "$src_part_base" == "" ] +then + src_part_base=$src_disk +fi + +if [ "$src_disk" != "$src_root_disk" ] +then + if ((SD_slot_boot)) + then + # Handle SD card boots with root on different USB disk device. + # But will assume SD card has a root partition just above its root. + # + alt_root_part_num="$root_part_num" + root_part_num=$((boot_part_num + 1)) + else + echo $" +Boot and root are on different disks and it's not a SD card boot. +Don't know how to partition the destination disk!" + exit 1 + fi +fi + +# src_root_dev, if on device other than booted, is not in src_partition_table +# and src_fdisk_table, but is in src_df_table and src_mount_table +# +src_partition_table=$(parted -m "/dev/$src_disk" unit s print | tr -d ';') +src_fdisk_table=$(fdisk -l /dev/$src_disk | grep "^/dev/") + +tmp=$(df | grep -e "^/dev/$src_disk" -e "^/dev/root" -e "$src_root_dev" \ + | tr -s " ") +dev=${src_root_dev#/dev/} +src_df_table=$(echo "$tmp" | sed "s/root/$dev/") + +n_src_parts=$(echo "$src_partition_table" | tail -n 1 | cut -d ":" -f 1) +src_disk_size=$(echo "$src_partition_table" \ + | grep "^/dev/$src_disk" | cut -d ":" -f 2 | tr -d 's') + +line=$(fdisk -l /dev/$src_disk | grep "Disk identifier:") +src_disk_ID=${line#*x} + +src_mount_table=$(findmnt -o source,target -n -l \ + | grep -e "^/dev/$src_disk" -e "^$src_root_dev" | tr -s " ") +n_mounts=$(echo "$src_mount_table" | wc -l) + +if ((alt_root_part_num > 0 && n_src_parts < 2)) +then + echo $" +Booted disk has only one partition and the root is from another device. +Don't know how to partition the destination disk! +" + exit 1 +fi + + +line=$(echo "$src_fdisk_table" | grep "Extended") +if [ "$line" != "" ] +then + dev=$(echo "$line" | cut -d " " -f 1) + ext_part_num="${dev: -1}" +else + ext_part_num=0 +fi + + +for ((p = 1; p <= n_src_parts; p++)) +do + line=$(echo "$src_partition_table" | grep -e "^${p}:") + if [ "$line" == "" ] + then + src_exists[p]=0 + continue + fi + src_exists[p]=1 + + if ((p == root_part_num)) + then + src_partition[p]=${src_root_dev#/dev/} + src_device[p]=$src_root_dev + else + src_partition[p]="${src_part_base}${p}" + src_device[p]="/dev/${src_partition[p]}" + fi + + # parted sectors are 512 bytes + src_start_sector[p]=$(echo "$line" | cut -d ":" -f 2 | tr -d 's') + src_size_sectors[p]=$(echo "$line" | cut -d ":" -f 4 | tr -d 's') + + part_type=$(echo "$line" | cut -d ":" -f 5) + + src_mounted_dir[p]=$(echo "$src_mount_table" \ + | grep -m 1 -e "^${src_device[p]}" | cut -d " " -f 2) + if [ "${src_mounted_dir[p]}" != "" ] + then + src_sync_part[p]=1 + else + src_sync_part[p]=0 + fi + + src_name[p]="" + if [ "$part_type" != "" ] + then + src_fs_type[p]="$part_type" + else + src_fs_type[p]="--" + fi + src_label[p]="--" + + if [ "${src_mounted_dir[p]}" == "/" ] + then + src_name[p]="root" + # + # If root on device other than booted SD card, root_part_num assumed to be + # booted /boot part_num + 1 and alt_root_part_num is from root device. + # + elif ((p == root_part_num)) && ((alt_root_part_num > 0)) + then + src_name[p]="root**" + elif ((p == ext_part_num)) + then + src_fs_type[p]="EXT" + elif [[ "$part_type" == *"linux-swap"* ]] + then + src_fs_type[p]="swap" + elif [ "${src_mounted_dir[p]}" != "" ] + then + src_name[p]="${src_mounted_dir[p]}" + fi + + if [[ "$part_type" == *"ext"* ]] + then + label=`e2label ${src_device[p]} 2> /dev/null` + if [ "$label" != "" ] + then + src_label[p]="$label" + fi + fi +done + + +# command line +# +setup_args="" +edit_fstab_name="" +ext_label="" +verbose="no" + +force_initialize=0 +force_2_parts=0 +force_sync=0 +all_sync=0 +usage_error=0 +unattended=0 +Unattended=0 +quiet=0 +custom_sync=0 +leave_sd_usb_boot=0 +convert_to_partuuid=0 + +while [ "$1" ] +do + case "$1" in + -v|--verbose) + verbose="yes" + rsync_options=${rsync_options}v + ;; + -u|--unattended) + unattended=1 + ;; + -U|--Unattended-init) + unattended=1 + Unattended=1 + ;; + -q|--quiet) + unattended=1 + quiet=1 + rsync_options=${rsync_options}q + ;; + -s|--setup) + shift + if ! command -v $setup_command > /dev/null + then + echo "Cannot find script $setup_command for setup arg \"$1\"." + usage_error=1 + fi + if [ "$setup_args" == "" ] + then + setup_args="$1" + else + setup_args="$setup_args $1" + fi + ;; + -e|--edit-fstab) + shift + edit_fstab_name=$1 + ;; + -f|--force-initialize) + force_initialize=1 + ;; + -f2) + force_initialize=1 + force_2_parts=1 + ;; + -x) + set -x + ;; + -a|--all-sync) + all_sync=1 + ;; + -m|--mountdir) + shift + mount_ok=0 + for ((p = 1; p <= n_src_parts; p++)) + do + if ((!src_exists[p])) + then + continue + fi + if ((!custom_sync)) && ((p != root_part_num)) + then + src_sync_part[p]=0 + fi + if [ "${src_mounted_dir[p]}" == "$1" ] + then + src_sync_part[p]=1 + mount_ok=1 + fi + done + if ((!mount_ok)) + then + echo "Asking to clone directory \"$1\", but it is not mounted." + usage_error=1 + fi + custom_sync=1 + ;; + -L|--label_partitions) + shift + ext_label=$1 + ;; + -l|--leave-sd-usb-boot) + leave_sd_usb_boot=1 + ;; + -F|--Force-sync) + force_sync=1 + ;; + --convert-fstab-to-partuuid) + convert_to_partuuid=1 + ;; + -V|--version) + echo $PGM Version: $version + exit 0 + ;; + -h|--help) + usage + ;; + *) + if [ "$dst_disk" != "" ] + then + echo "Bad args" + usage + fi + dst_disk=$1 + dir=`expr substr $dst_disk 1 5` + if [ "$dir" == "/dev/" ] + then + dst_disk=${dst_disk#/dev/} + fi + ;; + esac + shift +done + +if ((custom_sync)) && ((all_sync)) +then + echo "-m and -a options at the same time conflict." + exit 1 +fi +if ((custom_sync)) && ((force_initialize)) +then + echo "-m and -f options at the same time conflict." + exit 1 +fi +if [[ "$verbose" == "yes" ]] && ((quiet)) +then + echo "-q and -v options at the same time conflict." + exit 1 +fi + +if ((usage_error)) +then + echo "" + exit 1 +fi + +if ((convert_to_partuuid)) +then + unattended=0 + Unattended=0 + + fstab=/etc/fstab + fstab_tmp=/tmp/fstab + fstab_save=${fstab}.${PGM}-save + confirm "This will change your $fstab, are you sure?" "abort" + + cp $fstab $fstab_tmp + printf "\nConverting $fstab from device names to PARTUUID\n" + count=0 + for ((p = 1; p <= n_src_parts; p++)) + do + if grep -q "^/dev/${src_partition[p]}" $fstab_tmp + then + partuuid=$(lsblk -n -o PARTUUID /dev/${src_partition[p]}) + sed -i "s/\/dev\/${src_partition[p]}/PARTUUID=$partuuid/" $fstab_tmp + printf " Editing $fstab, changing /dev/${src_partition[p]} to $partuuid\n" + ((++count)) + fi + done + if ((count)) + then + cp $fstab $fstab_save + cp $fstab_tmp $fstab + printf "Your original fstab is backed up to $fstab_save\n" + + cmdline_txt=/boot/cmdline.txt + cmdline_save=$cmdline_txt.${PGM}-save + if [ -f $cmdline_txt ] && grep -q "$src_root_dev" $cmdline_txt + then + root_part=${src_partition[root_part_num]} + partuuid=$(lsblk -n -o PARTUUID $src_root_dev) + if [ "$partuuid" != "" ] + then + cp $cmdline_txt $cmdline_save + sed -i "s/\/dev\/$root_part/PARTUUID=$partuuid/" $cmdline_txt + printf " Editing $cmdline_txt, changing root=$src_root_dev to root=PARTUUID=$partuuid\n" + printf "Your original cmdline.txt is backed up to $cmdline_save\n" + fi + fi + else + printf "Could not find any $src_disk partition names in $fstab, nothing changed.\n" + fi + rm $fstab_tmp + echo "" + exit 0 +fi + +# dst_mount_flag enumerations: +live=1 +temp=2 +fail=3 + +for ((p = 1; p <= n_src_parts; p++)) +do + if ((!src_exists[p])) + then + continue + fi + blocks=0 + dst_mount_flag[p]=0 + + if [ "${src_mounted_dir[p]}" != "" ] + then # df blocks are 1024 bytes + dst_mount_flag[p]=$live + blocks=$(echo "$src_df_table" \ + | grep -m 1 "^${src_device[p]}" | cut -d " " -f 3) + # in case intializing, get n_src_parts to compare to dest space + elif ((p == n_src_parts)) \ + || ((all_sync)) \ + && [ "${src_fs_type[p]}" != "EXT" ] \ + && [ "${src_fs_type[p]}" != "swap" ] \ + && [ "${src_fs_type[p]}" != "" ] + then + if mount ${src_device[p]} $clone + then + sleep 1 + blocks=$(df | grep "^${src_device[p]}" \ + | tr -s " " | cut -d " " -f 3) + umount $clone + dst_mount_flag[p]=$temp + if ((all_sync)) + then + src_sync_part[p]=1 + fi + else + dst_mount_flag[p]=$fail + fi + fi + src_used_sectors[p]=$((blocks * 2)) +done + +# ==== destination disk checks +# +if [ "$dst_disk" = "" ] +then + echo "No destination disk given." + usage +fi + +chk_disk=`cat /proc/partitions | grep -m 1 $dst_disk` + +if [ "$chk_disk" == "" ] +then + echo $" + Cannot find '$dst_disk' in the partition table. The partition table is:" + cat /proc/partitions + exit 1 +fi + +dst_part_base=$dst_disk + +if [[ ${chk_disk: -1} =~ ^[0-9]$ ]] +then + if [[ $dst_disk == *"mmcblk"* ]] + then + SD_slot_dst=1 + dst_part_base=${dst_disk}p + if [ "$edit_fstab_name" == "" ] \ + && ! grep -q "^PARTUUID=" /etc/fstab + then + edit_fstab_name=$dst_part_base + assumed_fstab_edit=1 + else + assumed_fstab_edit=0 + fi + else + qecho $" + Target disk $dst_disk ends with a digit so may be a partition. + $PGM requires disk names like 'sda' and not partition names like 'sda1'." + + confirm "Continue anyway?" "abort" + fi +fi + +if [ "$src_disk" == "$dst_disk" ] +then + echo "Destination disk $dst_disk is the booted disk. Cannot clone!" + exit 1 +fi + +dst_partition_table=$(parted -m "/dev/$dst_disk" unit s print | tr -d ';') +n_dst_parts=$(echo "$dst_partition_table" | tail -n 1 | cut -d ":" -f 1) +if [ "$n_dst_parts" == "/dev/$dst_disk" ] +then + n_dst_parts=0 +fi + +dst_disk_size=$(echo "$dst_partition_table" \ + | grep "^/dev/$dst_disk" | cut -d ":" -f 2 | tr -d 's') +dst_root_dev=/dev/${dst_part_base}${root_part_num} + +dst_mount_table=$(findmnt -o source,target -n -l \ + | grep "^/dev/$dst_disk" | tr -s " ") + +dst_fdisk_table=$(fdisk -l /dev/$dst_disk | grep "^/dev/") +line=$(echo "$dst_fdisk_table" | grep "Extended") +if [ "$line" != "" ] +then + dev=$(echo "$line" | cut -d " " -f 1) + ext_num="${dev: -1}" +else + ext_num=0 +fi + +for ((p = 1; p <= n_dst_parts; p++)) +do + line=$(echo "$dst_partition_table" | grep -e "^${p}:") + if [ "$line" == "" ] + then + dst_exists[p]=0 + continue + fi + dst_exists[p]=1 + + part="${dst_part_base}${p}" + dst_partition[p]="$part" + dst_device[p]="/dev/$part" + + dst_start_sector[p]=$(echo "$line" | cut -d ":" -f 2 | tr -d 's') + dst_size_sectors[p]=$(echo "$line" | cut -d ":" -f 4 | tr -d 's') + + part_type=$(echo "$line" | cut -d ":" -f 5) + if [ "$part_type" != "" ] + then + dst_fs_type[p]="$part_type" + else + dst_fs_type[p]="--" + fi + dst_label[p]="--" + + if [[ "$part_type" == *"linux-swap"* ]] + then + dst_fs_type[p]="swap" + elif [[ "$part_type" == *"ext"* ]] + then + label=`e2label ${dst_device[p]} 2> /dev/null` + if [ "$label" != "" ] + then + dst_label[p]="$label" + fi + elif ((p == ext_num)) + then + dst_fs_type[p]="EXT" + fi +done + +fs_match=1 +root_part_match=0 +first_part_mismatch=0 + +initialize=$((force_initialize)) + +for ((p = 1; p <= n_src_parts; p++)) +do + if ((!src_exists[p])) + then + continue + fi + stype=${src_fs_type[p]} + dtype=${dst_fs_type[p]} + if [ "$stype" != "$dtype" ] + then + tmp_match=0 + if [[ "$stype" == *"fat"* ]] && [[ "$dtype" == *"fat"* ]] + then + tmp_match=1 + elif [[ "$stype" == *"ext"* ]] && [[ "$dtype" == *"ext"* ]] + then + tmp_match=1 + fi + if ((tmp_match && p == root_part_num)) + then + root_part_match=1 + fi + if ((!tmp_match)) && ((src_sync_part[p])) + then + first_part_mismatch=$p + fs_match=0 + break + fi + fi +done + + +if ((!fs_match)) +then + initialize=1 + fs_match_string="do not match" +else + fs_match_string="OK, they match" +fi + +if ((initialize)) && ((quiet)) +then + echo "Quiet mode asked for but an initialize is required - can't clone!" + exit 1 +fi + +for ((p = n_dst_parts; p >= 1; --p)) +do + if ((!dst_exists[p])) + then + continue + fi + dir=$(echo "$dst_mount_table" \ + | grep -e "^${dst_device[p]}" | cut -d " " -f 2) + unmount_or_abort "$dir" \ +"Destination disk partition ${dst_device[p]} is mounted on $dir." +done + +mounted_dev=$(findmnt $clone -o source -n) +unmount_or_abort "$mounted_dev" \ + "Directory $clone is already mounted with $mounted_dev." + +mounted_dev=$(findmnt $clone_src -o source -n) +unmount_or_abort "$mounted_dev" \ + "Directory $clone_src is already mounted with $mounted_dev." + +mounted_dev=$(findmnt /mnt -o source -n) +unmount_or_abort "$mounted_dev" "$mounted_dev is currently mounted on /mnt." + + +if [ ! -d $clone ] +then + mkdir $clone +fi +if [ ! -d $clone_src ] +then + mkdir $clone_src +fi + +# Do not include a dhpys swapfile in rsync. It regenerates at boot. +# +if [ -f /etc/dphys-swapfile ] +then + swapfile=`cat /etc/dphys-swapfile | grep ^CONF_SWAPFILE | cut -f 2 -d=` + if [ "$swapfile" = "" ] + then + swapfile=/var/swap + fi + exclude_swapfile="--exclude $swapfile" +fi + +if ((grub_auto)) && [ -d /boot/grub ] && command -v grub-install +then + have_grub=1 +else + have_grub=0 +fi + +print_partitions + +if ((initialize)) +then + if ((unattended && !Unattended)) + then + echo $" +Unattended -u option not allowed when initializing. +Use -U for unattended even if initializing. +" + exit 1 + fi + + reason="FS types mismatch" + if ((force_initialize)) + then + reason="forced by option" + fi + + n_image_parts=$((force_2_parts ? 2 : n_src_parts)) + + start_sector=${src_start_sector[$n_image_parts]} + last_part_sectors=$((dst_disk_size - start_sector)) + last_part_used=${src_used_sectors[$n_image_parts]} + last_part_space=$(( (last_part_sectors > last_part_used) ? 1 : 0 )) + + if ((last_part_sectors < 7812)) + then + printf "%-22s : %s\n" "** FATAL **" \ + "Initialize required - $reason." + printf "%-22s : %s\n" "" \ + " But destination is too small. To image partition" + readable $((start_sector + 7812)) "512" min_size + printf "%-22s : %s\n" "" \ + " source sizes, minimum destination size is $min_size." + if ((n_src_parts > 2)) + then + printf "%-22s : %s\n" " " \ + " Options: try a two partition clone with -f2, or" + printf "%-22s : %s\n" " " \ + " manually create custom partitions that can work." + fi + printf "%-23s:\n" "-----------------------" + exit 1 + fi + + readable $((last_part_sectors + 7812)) "512" image_space_readable + + echo "== Initialize: IMAGE $src_disk partition table to $dst_disk - $reason ==" + print_image_actions + print_options + + printf "%-22s : %s\n" "** WARNING **" \ + "All destination disk $dst_disk data will be overwritten!" + printf "%-22s : %s\n" "" \ + " The partition structure will be imaged from $src_disk." + + abort=0 + if ((!last_part_space)) + then + readable $last_part_used "512" used_readable + printf "%-22s : %s\n" "** WARNING **" \ + "Destination last partition resize to $image_space_readable" + printf "%-22s : %s\n" "" \ + " is too small to hold source used $used_readable." + if [ "$n_image_parts" == "$root_part_num" ] && ((!force_sync)) + then + printf "%-22s : %s\n" "** FATAL **" \ + "This is the root partition, so aborting!" + printf "%-22s : %s\n" "" \ + " Use -F to override." + abort=1 + elif ((!force_sync)) + then + printf "%-22s : %s\n" "" \ + " The partition SYNC is skipped, use -F to override." + else + printf "%-22s : %s\n" "" \ + " ** Syncing anyway as you asked with -F. **" + fi + fi + printf "%-23s:\n" "-----------------------" + if ((abort)) + then + exit 1 + fi + confirm "Initialize and clone to the destination disk ${dst_disk}?" "abort" + + if ((!Unattended)) && [ "$ext_label" == "" ] + then + printf "Optional destination ext type file system label (16 chars max): " + read ext_label + fi + + start_time=`date '+%H:%M:%S'` + start_sec=$(date '+%s') + + if [ "${src_mounted_dir[1]}" == "/boot" ] + then + image_to_part=2 + msg="/boot" + else + image_to_part=1 + msg="first" + fi + image_to_sector=${src_start_sector[$image_to_part]} + count=$((image_to_sector / 2 / 1024 + 4)) # in MiB blocks for dd bs=1M + + printf "\nInitializing\n" + printf " Imaging past the start of $msg partition $image_to_part.\n" + sync + printf " => dd if=/dev/$src_disk of=/dev/$dst_disk bs=1M count=$count ..." + dd if=/dev/$src_disk of=/dev/$dst_disk bs=1M count=$count &> /tmp/$PGM-output + if [ "$?" != 0 ] + then + printf "\n dd failed. See /tmp/$PGM-output.\n" + printf " Try running $PGM again.\n\n" + exit 1 + fi + echo "" + sync + sleep 1 + sfd0=$(sfdisk -d /dev/$src_disk) + if ((force_2_parts && (n_src_parts > n_image_parts))) + then + remove_part_start=${src_partition[3]} + sfd0=$(echo "$sfd0" | sed -e "/\/dev\/$remove_part_start/,\$d") + fi + + part="${src_part_base}$n_image_parts" + sfd1=$(echo "$sfd0" | sed "\/dev\/$part/s/size=[^,]*,//") + + if ((ext_part_num > 0 && !force_2_parts)) + then + part="${src_part_base}$ext_part_num" + sfd1=$(echo "$sfd1" | sed "\/dev\/$part/s/size=[^,]*,//") + fi + + printf " Resizing last partition to end of disk ..." + for ((x = 0; x < 3; ++x)) + do + sleep $((x + 1)) + sfdisk --force /dev/$dst_disk &> /tmp/$PGM-output <<< "$sfd1" + if [ "$?" == 0 ] + then + break + fi + if ((x == 2)) + then + printf "\n====$PGM\n==orig:\n%s\n\n==edited:\n%s\n" \ + "$sfd0" "$sfd1" >> /tmp/$PGM-output + printf "\n Resize failed. See /tmp/$PGM-output.\n" + printf " Try running $PGM again.\n\n" + + # Don't let dst disk keep source disk ID. Can lead to remounts. + new_id=$(od -A n -t x -N 4 /dev/urandom | tr -d " ") + qprintf "x\ni\n0x$new_id\nr\nw\nq\n" | fdisk /dev/$dst_disk > /dev/null + exit 1 + fi + done + printf "\n Resize success.\n" + printf " Changing destination Disk ID ..." + sync + sleep 2 + + new_id=$(od -A n -t x -N 4 /dev/urandom | tr -d " ") + qprintf "x\ni\n0x$new_id\nr\nw\nq\n" | fdisk /dev/$dst_disk > /dev/null + printf "\n Delaying so partprobe can update /dev entries ..." + sync + sleep 2 + partprobe "/dev/$dst_disk" + sleep 2 + echo "" + + for ((p = n_image_parts + 1; p <= n_src_parts; p++)) + do + src_sync_part[p]=0 + done + + for ((p = 1; p <= n_image_parts; p++)) + do + if ((!src_exists[p])) + then + continue + fi + dst_dev=/dev/${dst_part_base}${p} + fs_type=${src_fs_type[$p]} + if ((p == ext_part_num)) \ + || [ "$fs_type" == "--" ] + then + continue + fi + + if [ "${src_mounted_dir[p]}" == "/boot" ] && ((p == 1)) + then + # Avoid "Volume not properly unmounted" warnings. + printf " => fsck -p $dst_dev ..." + fsck -p $dst_dev &>> /tmp/$PGM-log + echo "" + ext_label $p $fs_type "" label + if [ "$label" != "" ] + then + e2label $dst_dev $label + fi + else + if [ "$fs_type" == "swap" ] + then + printf " => mkswap $dst_dev\n" + mkswap $dst_dev &>> /tmp/$PGM-log + elif ((p != ext_part_num)) + then + if [ "${src_mounted_dir[p]}" != "" ] || ((p == n_image_parts)) + then + if [ "$fs_type" == "fat16" ] + then + fs_type="vfat" + elif [ "$fs_type" == "fat32" ] + then + fs_type="vfat -F 32" + fi + ext_label $p $fs_type "-L" label + printf " => mkfs -t $fs_type $label $dst_dev ..." + yes | mkfs -t "$fs_type" $label "$dst_dev" &>> /tmp/$PGM-output +# mkfs -t "$fs_type" "$dst_dev" &>> /tmp/$PGM-output <<< "yes" + echo "" + if ((p == n_image_parts)) + then + if ((!last_part_space)) + then + src_sync_part[p]=$force_sync + else + src_sync_part[p]=1 + fi + fi + else + printf " => dd if=${src_device[$p]} of=$dst_dev bs=1M ..." + dd if=${src_device[$p]} of=$dst_dev bs=1M &>> /tmp/$PGM-output + if [ "$?" != 0 ] + then + printf "\n dd failed. See /tmp/$PGM-output.\n" + else + echo "" + fi + ext_label $p $fs_type "" label + if [ "$label" != "" ] + then + echo " e2label $dst_dev $label" + e2label $dst_dev $label + fi + fi + fi + fi + done + ext_label="" +else + qecho "== SYNC $src_disk file systems to $dst_disk ==" + print_sync_actions + print_options + + informed=0 + space_ok=1 + all_sync_mount_ok=1 + if ((force_sync)) + then + err="WARNING" + else + err="FATAL" + fi + for ((p = 1; p <= n_src_parts; p++)) + do + if ((!src_exists[p] || !dst_exists[p])) + then + continue + fi + if ((${dst_size_sectors[p]} < ${src_used_sectors[p]})) \ + && ((src_sync_part[p])) + then + qprintf "%-22s : %s\n" "** $err **" \ + "Partition $p: source used > destination space." + space_ok=$force_sync + informed=1 + fi + if ((all_sync && dst_mount_flag[p] == fail)) + then + qprintf "%-22s : %s\n" "** $err **" \ + "Partition $p: mount failed, cannot sync." + all_sync_mount_ok=$force_sync + informed=1 + fi + done + if ((informed)) + then + qprintf "%-23s:\n" "-----------------------" + fi + + if ((!space_ok || !all_sync_mount_ok)) + then + printf "\nAborting!\n" + if ((!space_ok)) + then + printf " Use -F to override used > space fail or -m to select mounts to clone.\n" + fi + if ((!all_sync_mount_ok)) + then + printf " Use -a -F to sync all except failed mounts.\n" + fi + exit 1 + fi + + confirm "Ok to proceed with the clone?" "abort" + start_time=`date '+%H:%M:%S'` + start_sec=$(date '+%s') +fi + +line=$(fdisk -l /dev/$dst_disk | grep "Disk identifier:") +dst_disk_ID=${line#*x} +if [ "$dst_disk_ID" == "$src_disk_ID" ] +then + qecho "Destination disk has same Disk ID as source, changing it." + new_id=$(od -A n -t x -N 4 /dev/urandom | tr -d " ") + qprintf "x\ni\n0x$new_id\nr\nw\nq\n" | fdisk /dev/$dst_disk | grep changed + sync + sleep 2 + partprobe "/dev/$dst_disk" + sleep 2 + + line=$(fdisk -l /dev/$dst_disk | grep "Disk identifier:") + dst_disk_ID=${line#*x} + if [ "$dst_disk_ID" == "$src_disk_ID" ] + then + qecho " Failed to set a new Disk ID." + fi +fi + +sync +qprintf "\nSyncing file systems (can take a long time)\n" + +sync_msg_done=0 +for ((p = 1; p <= n_src_parts; p++)) +do + if ((!src_exists[p])) + then + continue + fi + if ((src_sync_part[p] && dst_mount_flag[p] == temp)) + then + if ((!sync_msg_done)) + then + qprintf "Syncing unmounted partitions:\n" + fi + sync_msg_done=1 + dst_dev=/dev/${dst_part_base}${p} + fs_type=${src_fs_type[$p]} + ext_label $p $fs_type "" label + if [ "$label" != "" ] + then + qecho " e2label $dst_dev $label" + e2label $dst_dev $label + fi + + mount_partition ${src_device[p]} $clone_src "" + mount_partition $dst_dev $clone "$clone_src" + unmount_list="$clone_src $clone" + rsync_file_system "${clone_src}/" "${clone}" "" + unmount_list "$unmount_list" + fi +done + +qprintf "Syncing mounted partitions:\n" + +fs_type=${src_fs_type[$root_part_num]} +ext_label $root_part_num $fs_type "" label +if [ "$label" != "" ] +then + qecho " e2label $dst_root_dev $label" + e2label $dst_root_dev $label +fi + +mount_partition $dst_root_dev $clone "" +unmount_list="$clone" + +rsync_file_system "//" "$clone" "with-root-excludes" + +for ((p = 1; p <= n_src_parts; p++)) +do + if ((!src_exists[p])) + then + continue + fi + if ((p != root_part_num && src_sync_part[p] && dst_mount_flag[p] == live)) + then + dst_dir=$clone${src_mounted_dir[p]} + if [ ! -d $dst_dir ] + then + mkdir -p $dst_dir + fi + + dst_dev=/dev/${dst_part_base}${p} + fs_type=${src_fs_type[$p]} + ext_label $p $fs_type "" label + if [ "$label" != "" ] + then + qecho " e2label $dst_dev $label" + e2label $dst_dev $label + fi + + mount_partition "$dst_dev" "$dst_dir" "$unmount_list" + rsync_file_system "${src_mounted_dir[p]}/" "${dst_dir}" "" + unmount_list="$dst_dir $unmount_list" + fi +done +qecho "" + +# Fix PARTUUID or device name references in cmdline.txt and fstab +# +fstab=${clone}/etc/fstab +cmdline_txt=${clone}/boot/cmdline.txt + +if [ -f $cmdline_txt ] +then + if ((leave_sd_usb_boot && SD_slot_dst)) + then + qecho "Leaving SD to USB boot alone." + cp $cmdline_txt ${clone}/boot/cmdline.boot + cmdline_txt=${clone}/boot/cmdline.boot + fi + if grep -q $src_disk_ID $cmdline_txt + then + qecho "Editing $cmdline_txt PARTUUID to use $dst_disk_ID" + sed -i "s/${src_disk_ID}/${dst_disk_ID}/" "$cmdline_txt" + elif [ "$edit_fstab_name" != "" ] && grep -q ${src_part_base} $cmdline_txt + then + qecho "Editing $cmdline_txt references from $src_part_base to $edit_fstab_name" + sed -i "s/${src_part_base}/$edit_fstab_name/" "$cmdline_txt" + fi + if ((leave_sd_usb_boot && SD_slot_boot)) + then + qecho "Copying USB cmdline.txt to SD card to set up USB boot." + cp /boot/cmdline.txt /boot/cmdline.boot + cp $cmdline_txt /boot/cmdline.txt + fi +fi + +if grep -q $src_disk_ID $fstab +then + qecho "Editing $fstab PARTUUID to use $dst_disk_ID" + sed -i "s/${src_disk_ID}/${dst_disk_ID}/g" "$fstab" +elif [ "$edit_fstab_name" != "" ] && grep -q ${src_part_base} $fstab +then + qecho "Editing $fstab references from $src_part_base to $edit_fstab_name" + sed -i "s/${src_part_base}/${edit_fstab_name}/" "$fstab" +fi + + +rm -f $clone/etc/udev/rules.d/70-persistent-net.rules + +dst_root_vol_name=`e2label $dst_root_dev` + +if [ "$dst_root_vol_name" = "" ] +then + dst_root_vol_name="no label" +fi + +if ((have_grub)) +then + qecho "grub-install --root-directory=$clone /dev/$dst_disk" + if ((quiet)) + then + grub-install --root-directory=$clone /dev/$dst_disk &> /dev/null + else + grub-install --root-directory=$clone /dev/$dst_disk + fi +fi + +if [ "$setup_args" != "" ] +then + qprintf "\nRunning setup script: $setup_command $setup_args\n" + $setup_command $setup_args +fi + +date=`date '+%F %H:%M'` +echo "$date $HOSTNAME $PGM : clone to $dst_disk ($dst_root_vol_name)" \ + >> $clone_log +echo "$date $HOSTNAME $PGM : clone to $dst_disk ($dst_root_vol_name)" \ + >> ${clone}${clone_log} + + +stop_sec=$(date '+%s') +clone_sec=$((stop_sec - start_sec)) + +stop_time=`date '+%H:%M:%S'` + +qecho "===============================" +qecho "Done with clone to /dev/$dst_disk" +qprintf " Start - %s End - %s Elapsed Time - %d:%02d\n" \ + "$start_time" "$stop_time" "$((clone_sec / 60))" "$((clone_sec % 60))" + +if ((!unattended)) +then + echo -n $" +Cloned partitions are mounted on $clone for inspection or customizing. + +Hit Enter when ready to unmount the /dev/$dst_disk partitions ..." + + read resp +fi + +unmount_list "$unmount_list" +qprintf "===============================\n\n" + +exit 0 diff --git a/RPiSetup/usr/local/bin/rpi-clone-setup b/RPiSetup/usr/local/bin/rpi-clone-setup new file mode 100644 index 0000000..5578c30 --- /dev/null +++ b/RPiSetup/usr/local/bin/rpi-clone-setup @@ -0,0 +1,144 @@ +#!/bin/bash + +# Usage: rpi-clone-setup {-t|--test} hostname +# eg: sudo rpi-clone-setup bozo +# +# This script is automatically run by rpi-clone (when it is given -s options) +# to setup an alternate hostname. A cloned file system mounted on /mnt/clone +# is expected unless testing with the -t option. +# +# Or, this script can be run by hand at the end of a clone when rpi-clone +# pauses with the cloned file systems still mounted on /mnt/clone. +# +# Or, run this script by hand with -t to process files in test directories +# under /tmp/clone-test. Run -t and look at the files to see if the files +# have been edited OK. +# eg: sudo rpi-clone-setup -t bozo +# +# This is a starter script that handles only /etc/hosts and /etc/hostname. +# Make sure the script works correctly for your /etc/hosts file. +# +# If adding a customization for another file: +# Add the file to file_list. +# If needed, add a mkdir -p line to the "if ((testing))" part. +# Add the scripting necessary to customize the file. +# Test new scripting by running: rpi-clone-setup -t newhostname +# + +file_list="etc/hostname etc/hosts" + +clone=/mnt/clone +clone_test=/tmp/clone-test + +PGM=`basename $0` + +if [ `id -u` != 0 ] +then + echo "You must be root to run $PGM" + exit 0 +fi + +function usage + { + echo "Usage: $PGM hostname {-t|--test}" + echo " Eg: $PGM rpi1" + echo " Modify files appropriate to set up for a new host." + echo " Files handled are:" + for file in $file_list + do + echo " $file" + done + echo "" + echo "If testing (-t flag) files are copied and processed to $clone_test" + echo "" + exit 0 + } + +testing=0 +while [ "$1" ] +do + case "$1" in + -t|--test) + testing=1 + ;; + *) + if [ "$newhost" != "" ] + then + echo "Bad args" + usage + fi + newhost=$1 + ;; + esac + shift +done + +if [ "$newhost" = "" ] +then + echo -e "You must specify a target hostname\n" + usage +fi + +echo -e "\t$newhost\t- target hostname" + +if ((!testing)) && [ ! -d /mnt/clone/etc ] +then + echo "A destination clone file system is not mounted on /mnt/clone" + echo "Aborting!" + exit 0 +fi + +if ((testing)) +then + cd /tmp + rm -rf $clone_test + clone=$clone_test + + mkdir -p $clone/etc + + echo "**********************************************" + echo "Testing setup: copying files to $clone" + for file in $file_list + do + echo " cp /$file $clone/$file" + cp /$file $clone/$file + done + echo "This test run will modify those files." + echo "**********************************************" + echo "" +fi + + +## +# Set /etc/hostname +# +cd $clone/etc +echo $newhost > hostname +# +# Read it back to verify. +# +echo "$clone/etc/hostname - set new hostname: " +LINE=`cat hostname` +echo -e "$LINE\n" + + +## +# Edit /etc/hosts - edit the sed command if editing fails for your /etc/hosts. +# +cd $clone/etc +sed -i s/"$HOSTNAME"/"$newhost"/ hosts +# +# Read it back to verify. +# +echo "$clone/etc/hosts - set new hostname \"$newhost\" in lines: " +LINE=`grep $newhost hosts` +echo -e "$LINE\n" + + +## +# Add more customizations if needed. +# + + +exit 0 + diff --git a/RPiSetup/usr/local/bin/startdrs.sh b/RPiSetup/usr/local/bin/startdrs.sh new file mode 100644 index 0000000..67459ce --- /dev/null +++ b/RPiSetup/usr/local/bin/startdrs.sh @@ -0,0 +1,37 @@ +#!/bin/bash + +source /etc/drs.conf + +openbox-session & + +xset s off +xset +dpms +xset dpms 0 600 1800 + +#xset dpms force off + +while true +do + killall chromium-browser + rm -rf ~/.{config,cache}/chromium/ + + # start browser in your web site: here, just plain old localhost + # -test-type will ignore any warnings and + # --ignore-certificate-errors will allow you to kiosk any https-page without displaying certificate errors + chromium-browser -test-type --ignore-certificate-errors --kiosk --no-first-run --incognito $DRS_SERVER & + + sleep 10 + + while true + do + pgrep chromium-browse + if [ "$?" -eq "1" ] + then + # here you could execute something that is supposed to happen after the browser was accidentally quit + chromium-browser -test-type --ignore-certificate-errors --kiosk --no-first-run --incognito $DRS_SERVER & + fi + sleep 1 + done + + exit 0 +done diff --git a/RPiSetup/usr/local/bin/updatedrs b/RPiSetup/usr/local/bin/updatedrs new file mode 100644 index 0000000..87687da --- /dev/null +++ b/RPiSetup/usr/local/bin/updatedrs @@ -0,0 +1,5 @@ +#!/bin/bash +host=$1 + +rsync -rlptDvz rwright@$host:/home/rwright/ownCloud/Software_Projects/OpenDRS-1.1.0/distfiles/ /var/www/opendrs/ && chown -R www-data:www-data /var/www/opendrs/* + diff --git a/RaspberryPiSetup.txt b/RaspberryPiSetup.txt new file mode 100644 index 0000000..9a78dcd --- /dev/null +++ b/RaspberryPiSetup.txt @@ -0,0 +1,43 @@ +Raspberry Pi setup + +1. Create Raspbian Lite SD card from the most recent stable release. + +2. Insert SD card in Raspberry Pi and boot. + +3. Login as pi. + +4. sudo raspi-config and: + + a. Use option 1 to set a strong password for the default user (pi). + + b. Use option 2 Network Options to: + 1. Set the desired hostname. + 2. Set WiFi SSID and passphrase if using WiFi for networking. + + c. Use option 4 Localisation Options to: + 1. Set locale. + 2. Set timezone. + 3. Set keyboard layout (IMPORTANT! Raspbian is made in England, so + unless that's the kind of keyboard you have, the default layout + will make your symbols come out wrong!). + 4. Set WiFi country. + + d. Use option 5 Interfacing Options to enable SSH server. + + e. If you have black borders on the sides of the screen, use option 7 + Advanced Options to select No for Overscan compensation. + + f. When you've finished, tab down to highlight Finish and hit enter. + + g. Reboot when prompted. + + h. Log back in as pi using the password you set in a. above. + +5. Copy OpenDRS distribution package to /home/pi on the Raspberry Pi and extract. + +6. Change directory to the extracted distribution. + +7. Change directory to RPiSetup. + +8. run sudo ./terminal-setup.sh + diff --git a/boilerplate.php b/boilerplate.php new file mode 100644 index 0000000..7593a46 --- /dev/null +++ b/boilerplate.php @@ -0,0 +1,84 @@ +"; + + +echo "
This is the standard template used for all pages in the application.
"; + + +echo "
"; +banner($print); +pageblock("right","end"); +pagetable("end"); + +framework("end","","",$print); + +?> diff --git a/db.php.template b/db.php.template new file mode 100644 index 0000000..0e72665 --- /dev/null +++ b/db.php.template @@ -0,0 +1,42 @@ + + + Database Error! + + + Could not connect to the $dbname database. Please ensure the MySQL server is running. If this is + the first time you have used OpenMTS, please consult the INSTALL file included in the distribution. + + + "; + exit(); +} + +$drs_db=@mysqli_connect("localhost",$username,$dbpass,$dbname); +if (mysqli_errno($drs_db)) { + dberrmsg($dbname); +} + + +?> diff --git a/distfiles/CHANGELOG b/distfiles/CHANGELOG new file mode 100644 index 0000000..00d79cb --- /dev/null +++ b/distfiles/CHANGELOG @@ -0,0 +1,43 @@ + OpenDRS - Online Discrepancy Reporting System + Copyright (C) 2018 Rod Wright + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + +Changelog + +1.0.0 - 2018-03-13 + - Initial release as OpenMTS. + +1.1.0 - 2018-05-10 + - Name change from OpenMTS to OpenDRS. + - Added "Go Back" button to left side menu to make up for loss of browser + back button in terminal mode. + - Added configurable help text for period effective selection. + - Changed Sim period effective from selection to yes/no radio buttons. + - Writeup groups page shows collapsed list of groups. Click the expand icon + to expand each group. + - Fixed bug where entries made on stale create page had inaccurate date/time. + - For logged in users, added the ability to select writeups directly on the + View All Writeups page and assign/create a group. + - On the Create New Writeup page, removed the default selections for Device, + Subsystem, and Period dropdowns, forcing user to make a selection. + - Added a Today button to the View All Writeups page + - Changed the Period dropdowns on the Create, Search, and Group pages to show + the times instead of the period numbers. Period numbers are still shown + in the results tables with mouse hover showing times. + - Moved all installation/upgrade functions to the install.sh script. + + diff --git a/distfiles/INSTALL b/distfiles/INSTALL new file mode 100644 index 0000000..bff829e --- /dev/null +++ b/distfiles/INSTALL @@ -0,0 +1,28 @@ +Installation or upgrade of OpenDRS is straightforward. + +1. Unpack +Unpack the distribution in a convenient place. A directory will +created called OpenDRS-x.x.x/. + +2. Review License +Change to the OpenDRS-x.x.x/ directory and review the LICENSE file. + +3. Run the install script +In the same directory, type ./install.sh at a command prompt and +follow the prompts. + +4. Log in +Click on the Log In menu option on the main page. If this is a fresh +installation The initial user is drsadmin and the initial password +is OpenDRS-1. It is highly recommended that you changethis password +to a strong one by going to the Manage Database page under the +Admin Functions menu. If this an upgrade, your current usernames and +passwords are unchanged. + +5. Configure +The Admin Functions menu will be shown when logged in as an admin user. +Click on Global Settings to set display options, page colors, banners, and +miscellaneous options. Click on Manage Database to set users, devices, +subsystems, action reasons and periods. + +6. Start making writeups diff --git a/distfiles/LICENSE b/distfiles/LICENSE new file mode 100644 index 0000000..9732b82 --- /dev/null +++ b/distfiles/LICENSE @@ -0,0 +1,341 @@ + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc. + 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +License is intended to guarantee your freedom to share and change free +software--to make sure the software is free for all its users. This +General Public License applies to most of the Free Software +Foundation's software and to any other program whose authors commit to +using it. (Some other Free Software Foundation software is covered by +the GNU Library General Public License instead.) You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +this service if you wish), that you receive source code or can get it +if you want it, that you can change the software or use pieces of it +in new free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. You must make sure that they, too, receive or can get the +source code. And you must show them these terms so they know their +rights. + + We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + + Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, so +that any problems introduced by others will not reflect on the original +authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that redistributors of a free +program will individually obtain patent licenses, in effect making the +program proprietary. To prevent this, we have made it clear that any +patent must be licensed for everyone's free use or not licensed at all. + + The precise terms and conditions for copying, distribution and +modification follow. + + GNU GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License applies to any program or other work which contains +a notice placed by the copyright holder saying it may be distributed +under the terms of this General Public License. The \"Program\", below, +refers to any such program or work, and a \"work based on the Program\" +means either the Program or any derivative work under copyright law: +that is to say, a work containing the Program or a portion of it, +either verbatim or with modifications and/or translated into another +language. (Hereinafter, translation is included without limitation in +the term \"modification\".) Each licensee is addressed as \"you\". + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running the Program is not restricted, and the output from the Program +is covered only if its contents constitute a work based on the +Program (independent of having been made by running the Program). +Whether that is true depends on what the Program does. + + 1. You may copy and distribute verbatim copies of the Program's +source code as you receive it, in any medium, provided that you +conspicuously and appropriately publish on each copy an appropriate +copyright notice and disclaimer of warranty; keep intact all the +notices that refer to this License and to the absence of any warranty; +and give any other recipients of the Program a copy of this License +along with the Program. + +You may charge a fee for the physical act of transferring a copy, and +you may at your option offer warranty protection in exchange for a fee. + + 2. You may modify your copy or copies of the Program or any portion +of it, thus forming a work based on the Program, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) You must cause the modified files to carry prominent notices + stating that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in + whole or in part contains or is derived from the Program or any + part thereof, to be licensed as a whole at no charge to all third + parties under the terms of this License. + + c) If the modified program normally reads commands interactively + when run, you must cause it, when started running for such + interactive use in the most ordinary way, to print or display an + announcement including an appropriate copyright notice and a + notice that there is no warranty (or else, saying that you provide + a warranty) and that users may redistribute the program under + these conditions, and telling the user how to view a copy of this + License. (Exception: if the Program itself is interactive but + does not normally print such an announcement, your work based on + the Program is not required to print an announcement.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Program, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Program, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Program. + +In addition, mere aggregation of another work not based on the Program +with the Program (or with a work based on the Program) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may copy and distribute the Program (or a work based on it, +under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you also do one of the following: + + a) Accompany it with the complete corresponding machine-readable + source code, which must be distributed under the terms of Sections + 1 and 2 above on a medium customarily used for software interchange; or, + + b) Accompany it with a written offer, valid for at least three + years, to give any third party, for a charge no more than your + cost of physically performing source distribution, a complete + machine-readable copy of the corresponding source code, to be + distributed under the terms of Sections 1 and 2 above on a medium + customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer + to distribute corresponding source code. (This alternative is + allowed only for noncommercial distribution and only if you + received the program in object code or executable form with such + an offer, in accord with Subsection b above.) + +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, complete source +code means all the source code for all modules it contains, plus any +associated interface definition files, plus the scripts used to +control compilation and installation of the executable. However, as a +special exception, the source code distributed need not include +anything that is normally distributed (in either source or binary +form) with the major components (compiler, kernel, and so on) of the +operating system on which the executable runs, unless that component +itself accompanies the executable. + +If distribution of executable or object code is made by offering +access to copy from a designated place, then offering equivalent +access to copy the source code from the same place counts as +distribution of the source code, even though third parties are not +compelled to copy the source along with the object code. + + 4. You may not copy, modify, sublicense, or distribute the Program +except as expressly provided under this License. Any attempt +otherwise to copy, modify, sublicense or distribute the Program is +void, and will automatically terminate your rights under this License. +However, parties who have received copies, or rights, from you under +this License will not have their licenses terminated so long as such +parties remain in full compliance. + + 5. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Program (or any work based on the +Program), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Program or works based on it. + + 6. Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the +original licensor to copy, distribute or modify the Program subject to +these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + + 7. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Program at all. For example, if a patent +license would not permit royalty-free redistribution of the Program by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Program. + +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system, which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 8. If the distribution and/or use of the Program is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Program under this License +may add an explicit geographical distribution limitation excluding +those countries, so that distribution is permitted only in or among +countries not thus excluded. In such case, this License incorporates +the limitation as if written in the body of this License. + + 9. The Free Software Foundation may publish revised and/or new versions +of the General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + +Each version is given a distinguishing version number. If the Program +specifies a version number of this License which applies to it and \"any +later version\", you have the option of following the terms and conditions +either of that version or of any later version published by the Free +Software Foundation. If the Program does not specify a version number of +this License, you may choose any version ever published by the Free Software +Foundation. + + 10. If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, write to the author +to ask for permission. For software which is copyrighted by the Free +Software Foundation, write to the Free Software Foundation; we sometimes +make exceptions for this. Our decision will be guided by the two goals +of preserving the free status of all derivatives of our free software and +of promoting the sharing and reuse of software generally. + + NO WARRANTY + + 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY +FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN +OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES +PROVIDE THE PROGRAM \"AS IS\" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED +OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS +TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE +PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, +REPAIR OR CORRECTION. + + 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR +REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, +INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING +OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED +TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY +YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER +PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE +POSSIBILITY OF SUCH DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least +the \"copyright\" line and a pointer to where the full notice is found. + + + Copyright (C) + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + +Also add information on how to contact you by electronic and paper mail. + +If the program is interactive, make it output a short notice like this +when it starts in an interactive mode: + + Gnomovision version 69, Copyright (C) year name of author + Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, the commands you use may +be called something other than `show w' and `show c'; they could even be +mouse-clicks or menu items--whatever suits your program. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a \"copyright disclaimer\" for the program, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the program + `Gnomovision' (which makes passes at compilers) written by James Hacker. + + , 1 April 1989 + Ty Coon, President of Vice + +This General Public License does not permit incorporating your program into +proprietary programs. If your program is a subroutine library, you may +consider it more useful to permit linking proprietary applications with the +library. If this is what you want to do, use the GNU Library General +Public License instead of this License. + diff --git a/distfiles/about.php b/distfiles/about.php new file mode 100644 index 0000000..67819df --- /dev/null +++ b/distfiles/about.php @@ -0,0 +1,139 @@ +"; + +echo " +

+OpenDRS $version Discrepancy Reporting System +


+

This program is licensed under the terms of the GNU General Public License. Click on the link +or see the LICENSE file in the distribution to read it. OpenDRS is Copyright (C) 2018 by Rod Wright. +

+

+OpenDRS is a simple online maintenance tracking/discrepancy reporting application. It allows anyone with a web browser to create , + view, and search writeups. The look and feel of the OpenDRS pages are easily changed. +Settings that affect all users can be changed in the Admin Functions menu. User specific settings can be changed in the +My Profile menu. No browser specific HTML is used in OpenDRS, so it should be useable in any browser, and it has been +tested using Google Chrome, Mozilla Firefox and Microsoft Internet Explorer. +

+

+OpenDRS is highly flexible. Instructions are included in the distribution for creating a user terminal with a Raspberry Pi. OpenDRS can be hosted on a +centralized server, accessed by networked PCs and/or Raspberry Pi terminals. It can also be hosted directly on a Raspberry Pi +terminal as a non-networked standalone system or in a small network of Raspberry Pi terminals. +

+

+To read about the changes in this latest version of OpenDRS, click on the link or see the CHANGELOG file in the distribution. +

+ +

Several pieces of Open Source software make OpenDRS possible. +
+ + + + + + + + + + + + + + + + + +
LinuxAll +development and most of the testing of OpenDRS was done on computers running the +Linux operating system.
PHPOpenDRS is written in PHP, a hypertext +preprocessor, which makes possible the dynamic content of the pages.
MariaDBOpenDRS stores its data in a MariaDB +database.
ApacheOpenDRS was developed using the Apache web +server.
Fugue
Icons
The icons used in OpenDRS are from the Fugue Icon set created by Yusuke Kamiyamane. The entire set is available at +p.yusukekamiyamane.com, and are licensed under a +Creative Commons Attribution 3.0 License.
+

+"; + + + +echo "
"; +banner($print); +pageblock("right","end"); +pagetable("end"); + +framework("end","","",$print); + +?> diff --git a/distfiles/colorpicker/css/screen.css b/distfiles/colorpicker/css/screen.css new file mode 100644 index 0000000..6c6f522 --- /dev/null +++ b/distfiles/colorpicker/css/screen.css @@ -0,0 +1,149 @@ +#pickerPanel { width: 360px; height: 240px; font: normal 11px verdana, sans-serif; color: #333; line-height: 19px; margin: 0; } + +/* slider */ + +.dragPanel { + position: relative; + background-color: #eeeeee; + margin: 4px; + width: 260px; + height: 180px; +} + +.dragPanel h4 { + background-color: #bbbbbb; + height: 10px; + margin: 0px; + cursor: move; +} + + +input { font-size: .85em } +.thumb { + cursor:default; + width:18px; + height:18px; + z-index: 9; + position:absolute; +} + +.bg { + position:absolute; + left:10px; + height:18px; + width:146px; + border: 0px solid #aaaaaa; +} + +.bg span, .bg p { + cursor:default; + position: relative; + font-size: 2px; + overflow: hidden; + color: #aaaaaa; + top: 4px; + height: 10px; + width: 4px; + display: block; + float:left; +} + +.bg span { + border-top:1px solid #cccccc; + border-bottom:1px solid #cccccc; +} + +.bg .lb { + border-left:1px solid #cccccc; +} + +.bg .rb { + border-right:1px solid #cccccc; +} +#valdiv { position:absolute; top: 100px; left:10px; } + +#rBG {top:30px} +#gBG {top:50px} +#bBG {top:70px} + +#rgbSwatch { + position:absolute; + left:160px; + top:34px; + height:50px; + width:50px; + border:1px solid #aaaaaa; +} + +#rgbPanel { +/* +top: 400px; +left: 20px; +*/ +width: 360px; +height: 240px; +} + +/* picker */ + +#hueThumb { + cursor:default; + width:18px; + height:18px; + z-index: 9; + position:absolute; +} + +#hueBg { + position:absolute; + left:216px; + height:198px; + width:18px; + background:url(../img/hue.png) no-repeat; + top:18px; +} + +#pickerDiv { + position:absolute; + left:10px; + height:187px; + width:188px; + /* + background:url(../img/pickerbg.png) no-repeat; + */ + top:20px; +} + +#pickerbg { + position:absolute; + z-index: 1; + top:0px; + left:0px; +} +#selector { + cursor:default; + width:11px; + height:11px; + z-index: 9; + position:absolute; + top:0px; + left:0px; +} + +#pickerSwatch { + position:absolute; + left:260px; + top:30px; + height:60px; + width:60px; + border:2px solid #aaaaaa; +} + +#pickervaldiv { text-align:right; position:absolute; top: 86px; left:246px; } + +.colorbox{ +width: 25px; +height: 14px; +border: 1px solid black; +border-bottom-width: 0; +} \ No newline at end of file diff --git a/distfiles/colorpicker/demo.htm b/distfiles/colorpicker/demo.htm new file mode 100644 index 0000000..17ba657 --- /dev/null +++ b/distfiles/colorpicker/demo.htm @@ -0,0 +1,196 @@ + + + +YUI Color Picker + + + + + + + + + + + + + + + + + + + +

Color Picker

+

+ Implements a slider region and a vertical slider to implement an HSV color + picker. +

+ + +
+

 

+
+ +
+
+ +
+
+
+ +
+
+
+ R + H +
+ G + S +
+ B + V +
+
+ # +
+ +
+
+ +
 
+
+ + + + + + + + +
+# ____
+# ____ +
+ +More info: YUI Color Picker script. + + + diff --git a/distfiles/colorpicker/img/bullet.gif b/distfiles/colorpicker/img/bullet.gif new file mode 100644 index 0000000000000000000000000000000000000000..9d28c2f6aa75cfb4dbdff60fbac60130fe88a5d3 GIT binary patch literal 172 zcmZ?wbhEHbiA_^35yCG=nM*V^-Y&L?h$mWymZSRuEmL)9C;?rN?A}amE~Lz2ZJ>L D!_Y=` literal 0 HcmV?d00001 diff --git a/distfiles/colorpicker/img/greybg.png b/distfiles/colorpicker/img/greybg.png new file mode 100644 index 0000000000000000000000000000000000000000..1ff7817010b1327f9174c53743480dbec6e5831a GIT binary patch literal 197 zcmeAS@N?(olHy`uVBq!ia0vp^8Vn39J{-(I)*UWIAj!az?&#~tz;Nxx75@k$AYZb? zHKHUqKdq!Zu_%?nF(p4KRlzeiF+DXXH8G{K@MNkDP!U^zPl&6ru`!UTJT3AnkaG2O zaSW-r_2wWWFOYX=!FT^$es&QclUPtQlYyCGgAN1Z%=cyQK$<*V{an^LB{Ts5arr3K literal 0 HcmV?d00001 diff --git a/distfiles/colorpicker/img/header.gif b/distfiles/colorpicker/img/header.gif new file mode 100644 index 0000000000000000000000000000000000000000..6bb816ce2139fd04128976d48d6b49ea67b86049 GIT binary patch literal 203 zcmV;+05tzcNk%w1VJra~0Du4hYItf(TudSP1ONa4001li0000i0UH1S0-}tMsmtvT zqnxzbi?iMw_z#BSNS5Y_rs~SJ?hD8AOmp^)=lag~{tpZahs2_BLQ5)_%%<}RjjoQ= zs`ZM^YPa03cP9;t$KBRxX9S( z_z2mUI7wM)nG~6+xyjk-d2<;mI!gL!dWxE=`bXO8`ii$IJ4;(@>jRssyX$o8`+HCT F06P{RX6^t0 literal 0 HcmV?d00001 diff --git a/distfiles/colorpicker/img/hline.png b/distfiles/colorpicker/img/hline.png new file mode 100644 index 0000000000000000000000000000000000000000..e9b1649c81b28473569dfc11824d6143d0c47f53 GIT binary patch literal 416 zcmeAS@N?(olHy`uVBq!ia0vp^LLkh+3?vf;>QaFeOS+@4BLl;=8&~`zjDUQ}64!{5 z;QX|b^2DN42FH~Aq*MjZ+{ERS(8U$^J-+C7)2tvF^Mn4j0NanYu;rmk`Qiw~u^p=hI!wPL$S)YkB>^x*Ew%U# z)EejM;uunKE9t^v1|Ab}q0qqaq*N9rUd}ky#0di2($?0}+ROqQJPJ?0`oP?3@>HdA zi|dRAhp2$iHh)QtCCgNg9aKHf!{XGn%Yi}Uk%+)`S2l&mrGc(59w?SD2qZ9UIApAq TB$+J@w28sf)z4*}Q$iB}CQQOW literal 0 HcmV?d00001 diff --git a/distfiles/colorpicker/img/hue.png b/distfiles/colorpicker/img/hue.png new file mode 100644 index 0000000000000000000000000000000000000000..9a2d9c9952432d6faae3af571c047cd6c0a209ec GIT binary patch literal 1344 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 Dz=6CK literal 0 HcmV?d00001 diff --git a/distfiles/colorpicker/img/qbottom.png b/distfiles/colorpicker/img/qbottom.png new file mode 100644 index 0000000000000000000000000000000000000000..aa792d80e215656fecb4508716d368ed9ad5c9e7 GIT binary patch literal 1351 zcmb`H=~I&j5XN7`B7;(xI@T$pVmsQ-v{*U>GfL4E1TigwMT;QC5+R@{MKBIv)!3>< zu{xrnQfWD3AV3Zv;YdP&0D*7?a*>dbgDVM;aD*Ihj<>O&`UmvG?*8_}KF^2U*@vfM zqP)HQyZ`|3MxKa>1AtACi(@?9UHMMHL#?Z9&Imgh1^}W-L2N>htN#cSnSeQ+osP*v z<)VSGwCoG$y~r$7209LnN-M}2M2EUmd{0H6iF7y|RuD99m^xU!gCpbW+4))`b!pik zmdKnCbT}l$X1CAGEyxvWu~gpEKeV7-T+$m>R@cmC%ayAb0*S^Qo!|+?O|48it8d-7 z@i(q9KEa0Znqb| zF6RvKY&LsW?;wdvzhCsUjzoL-q(pC6?ds#c__HcLF^NEKrO~@{t{0TQZRCq2p`l0c zjV%-!V_G>gzn}#{TPJ&9!(`%%r3)I}&0l}7sv|2@Gs}jR8Ukf%T1loce!6=PCtSC|L`H<2$(Jtig}J9uK%^>R|GrC753gRnZ>(SaDV<)gKi~?z{>#F% zY06FMDKUC0y)$aEnHCOa^%1BAiQul1xYgahPvSGUBk%?|G0}=IK!!mTSa6GjkIwFo)N8~5ID3|RQU5qOdq*jOu@-X~@; zuTe7Lm)7}+&6sLt>Xd5CeT+4*QLedhogvDqFBb=Wc(3|UZosYraf15!B8vlqp)U>{ z)UF){BC5SZMh=4wjCNG16Sw;Iu{-5N0&ZI_;2F<~uY?uIsos&_+deCG?(D2Pcrd3m zFRne<{|un^)JYddGCo=GdMrG`404o5=X1YE1#8a!u+n453n{tdSr)@m2vA!y%J=Yx ztpTslpn+-;+(PfTk#|UZ83xsCa$aMF()p^7g#cc~^=`HRQR{AEAk?N6d~IQq$+@`Tnd@j|Ugvw`;e2jkcG}|8 zE6~KAM{*qeToT^&Sa>hU?c;ly1V66Vo50=61bRL*>6rnm;pXT36?Kbbzg;XOxb>za z2Y}MG;2TG88G348RAG^RyIacS)H*QUSj?{Uh)`a|=uK@mw>w_aZ8(ABJO0;!461P_ zmKr8{)30m_*Y=>INiW4{{dgNkDCX8uOY&3UYt7W`h);7 LGA4o;p7Ps2i5x4* literal 0 HcmV?d00001 diff --git a/distfiles/colorpicker/img/qmiddle.png b/distfiles/colorpicker/img/qmiddle.png new file mode 100644 index 0000000000000000000000000000000000000000..331fc6b2d6de89d2ac89128f698ad23732f72e90 GIT binary patch literal 385 zcmeAS@N?(olHy`uVBq!ia0y~yVEPYa`*1J=$+J&19swzqbVpxD28L@luJ}h70r`?8 zt`Q}{`DrEPiAAXljw$&`sS2LCiRr09sfj6-g(p*OfQp;~d_r9R{r|sv|IsgBza2Vu z`ueTAcke&?{{6?BckdrReV$WLVrgZyY5U#_SFWErcj@P^U(a8@{_yeBs`cA`|M~Ox z?_XnMJN@8N2@_WwWL+yDFB|Nq}(`yc%On*To_lY$$wpL`QP{QR$bfT8(Y WivQ20^LGF}#Ng@b=d#Wzp$P!YPS(Z% literal 0 HcmV?d00001 diff --git a/distfiles/colorpicker/img/qtop.png b/distfiles/colorpicker/img/qtop.png new file mode 100644 index 0000000000000000000000000000000000000000..07e845ed1554f058d9d04ed0bce74b66b4359329 GIT binary patch literal 2030 zcmZ`)c~Daa8jqq|r7GRFtE*iFwC)zyT34=7gm4N$Q3pg0MUjF)0wjcl1c*FVEMT<= zB90&sP!J&`$Pump;Rr{n?VZBn3KqL1D(X=X8iNlF!-wKY;bd$G7KgicF3S!eGY6C&I%?nHibIOjdhm@6@!khRsnbRZ5jA2uoPs*zE2b%*vz7SJq}` z=d0_R!^6q!=5_*s7>dJp^zd=m(2kCda3YDv<&sGeDwS$>ZlSYxU|~rX9~Z~%>f2E$ zFlh9$eC1;?6NI43+9o0~oJ@|0jEE|!tm_{d+1lQzZEUS?YRmapEEo|^OXoDSw!RUH z3zg2!#{j@b26I$2(ZcP@&S&Hml{L3@_V9;f%ktH=bw*i@8U(}eVHyh^i0C`TnM(Vscm>{yr8&(UPND#$%W!cQq({5>E*qA!JuHw z)YO#4V$nWTs?};nc@49sp|OS6H!xgQRbO0QL!nS2NaXtF_Kftmfx)3{4!5?UwQq1Z z{Y{$P75iLT>4;FAo>O3CWJHOgOiE@#L-CyUu2x=8KBN3YQJIysO;Kr;cyjut^DVw$ z)WpO@)BjHuwet&0ufBrIUVrr(cwsCt$u_#-v zs%vs~bsH6mQ#0OgZfzMF8p1G4p+wVOrkQD`zXD}zb&EW@Byx3o>+$EfNz1sy+D{Ce zdA$W3bNz33{mf$Nw9v^SktleWY34fw7AgRX(sBFeOX-Gn_gK*E;z9)(8)W zt7EPf&4$9Ei4%_SoHhu+4^&Xx=UGWd%!VKfq`toXU%nV8x0^|kwaT(*3a#yyOb1v9 zz{N7r8{@mt__p=&?#|9}Z_C4xQHEykbzgXy^ZDU{0ixRg$X*W>Ej9=8EB)%#9Rac_ zZp^i_4A9rpF3mKjg})HJUd4uHD}dw_@oI4AoI9FBeOPw!>!jG#*QZX_7@m3e)wx4P z1Lk<%4VbzG*A&hCQaU=|G05c_7^EN~98jpl(U23gl@n)r_K&F^Qz6(LcOJgE(KjGV z{@itQ(<P<1#b#T%=U1<#3yawYR?umbKm)vq6CZb&V zb^Md>=5+B(Nc_DkBPGdBm7=M9xLE?)^O3$vL}CUBb8Shs8m*#c8pT3ZuuvPn1Pqa3 zLy3ez2L}5&j(K*HTf2R5=(9W?C$A7A1G@KsB)VYux%s7FJw0tYqb-16B!4*$&ujf5 zv!E|e2f@i98K)ECwDR+|0FIB5pV`vwy?S5LM9IMyJQE!j*`GdmyQ0cGK~ufM%$jOn zS1YHSSI||t(VO(I2Xyq0WCvV0YVyi)j_qDBc!#U|_)tTk;LNkr8`OYyViNMn@t9MB zr0;TIiyxCSA^jkD>!jrPm&*IEW}R_%`>rN~Pn@jb!{|gon2vg7T!aOdXZy7;MMgf{qLA{?Zbb| zfo<6kiSkpQTM~zr)C8=F7!oI;&Eb~qu{>5c~LSsS6 y*#e69I$%~m4(;;D{+%)jwSRj{Vj=jC-iVSxl;tzlvFnF5w`Ev+KmHe5@TeRB literal 0 HcmV?d00001 diff --git a/distfiles/colorpicker/img/select.gif b/distfiles/colorpicker/img/select.gif new file mode 100644 index 0000000000000000000000000000000000000000..599f7f13a6854d198f501588948ffcf97bf9f365 GIT binary patch literal 78 zcmZ?wbhEHbij|08)1sO8@`> literal 0 HcmV?d00001 diff --git a/distfiles/colorpicker/img/select.png b/distfiles/colorpicker/img/select.png new file mode 100644 index 0000000000000000000000000000000000000000..78445a2fe060047216b3692980ebabfb0683c3c6 GIT binary patch literal 192 zcmeAS@N?(olHy`uVBq!ia0vp^+#t-s3?%0jwTl2LmUKs7M+Sy#H?H_c7yF!NeM|wNi2*CXBkqA8W@y2&T>3yVv%g*IK)!uk)}MmImzx& dTmu6$gLVMJ@>Lg{{{wY0c)I$ztaD0e0szdFIQaFeOS+@4BLl;=8&~`zjDUQ}64!{5 z;QX|b^2DN42FH~Aq*MjZ+{Ecdy=k(LQHiU}m43nu(QndREIi_vqTPSJt&t*0xmEwasZ;A5}8XDYSU{%H!tliLJBu zCfBdtuRc^ejNJhOV&#fE9SV#*h(8@u)`Iuu?wOHSR?E+BWwmUGwcy}W$;xxQ_P zyoOob)SVLYTL1t5-+AZgeW1&&N`m}?fn4$cgZY8~pwKM#ba4!+xRtcw2m_D6sT(cM z&Rnh?Q|C^0_HXBn^k|tH>(p>xB+?@|Ho!an7(=9o&&`h06OJ=QdPIjt1f64Y?dWWr zII}fIu`on2u$WCTHbjxt&eV)MMA0=*)!0NaGcJ0&&isFinite(tweak)){if(tween.currentFrame+tweak>=frames){tweak=frames-(frame+1);} +tween.currentFrame+=tweak;}};} +YAHOO.util.Bezier=new function() +{this.getPosition=function(points,t) +{var n=points.length;var tmp=[];for(var i=0;i0&&control[0].constructor!=Array){control=[control];} +if(YAHOO.util.Dom.getStyle(this.getEl(),'position')=='static'){YAHOO.util.Dom.setStyle(this.getEl(),'position','relative');} +if(typeof attributes['points']['from']!='undefined'){YAHOO.util.Dom.setXY(this.getEl(),attributes['points']['from']);start=this.getAttribute('points');} +else if((start[0]===0||start[1]===0)){YAHOO.util.Dom.setXY(this.getEl(),YAHOO.util.Dom.getXY(this.getEl()));start=this.getAttribute('points');} +var i,len;if(typeof attributes['points']['to']!='undefined'){end=translateValues(attributes['points']['to'],this);for(i=0,len=control.length;i0){translatedPoints=translatedPoints.concat(control);} +translatedPoints[translatedPoints.length]=end;}};this._onStart.subscribe(onStart);};YAHOO.util.Scroll=function(el,attributes,duration,method){if(el){YAHOO.util.Anim.call(this,el,attributes,duration,method);}};YAHOO.util.Scroll.prototype=new YAHOO.util.Anim();YAHOO.util.Scroll.prototype.defaultUnits.scroll=' ';YAHOO.util.Scroll.prototype.doMethod=function(attribute,start,end){var val=null;if(attribute=='scroll'){val=[this.method(this.currentFrame,start[0],end[0]-start[0],this.totalFrames),this.method(this.currentFrame,start[1],end[1]-start[1],this.totalFrames)];}else{val=this.method(this.currentFrame,start,end-start,this.totalFrames);} +return val;} +YAHOO.util.Scroll.prototype.getAttribute=function(attribute){var val=null;var el=this.getEl();if(attribute=='scroll'){val=[el.scrollLeft,el.scrollTop];}else{val=parseFloat(YAHOO.util.Dom.getStyle(el,attribute));} +return val;};YAHOO.util.Scroll.prototype.setAttribute=function(attribute,val,unit){var el=this.getEl();if(attribute=='scroll'){el.scrollLeft=val[0];el.scrollTop=val[1];}else{YAHOO.util.Dom.setStyle(el,attribute,val+unit);}}; \ No newline at end of file diff --git a/distfiles/colorpicker/js/color.js b/distfiles/colorpicker/js/color.js new file mode 100644 index 0000000..05f2a29 --- /dev/null +++ b/distfiles/colorpicker/js/color.js @@ -0,0 +1,101 @@ +/* Copyright (c) 2006 Yahoo! Inc. All rights reserved. */ + +YAHOO.util.Color = new function() { + + // Adapted from http://www.easyrgb.com/math.html + // hsv values = 0 - 1 + // rgb values 0 - 255 + this.hsv2rgb = function (h, s, v) { + var r, g, b; + if ( s == 0 ) { + r = v * 255; + g = v * 255; + b = v * 255; + } else { + + // h must be < 1 + var var_h = h * 6; + if ( var_h == 6 ) { + var_h = 0; + } + + //Or ... var_i = floor( var_h ) + var var_i = Math.floor( var_h ); + var var_1 = v * ( 1 - s ); + var var_2 = v * ( 1 - s * ( var_h - var_i ) ); + var var_3 = v * ( 1 - s * ( 1 - ( var_h - var_i ) ) ); + + if ( var_i == 0 ) { + var_r = v; + var_g = var_3; + var_b = var_1; + } else if ( var_i == 1 ) { + var_r = var_2; + var_g = v; + var_b = var_1; + } else if ( var_i == 2 ) { + var_r = var_1; + var_g = v; + var_b = var_3 + } else if ( var_i == 3 ) { + var_r = var_1; + var_g = var_2; + var_b = v; + } else if ( var_i == 4 ) { + var_r = var_3; + var_g = var_1; + var_b = v; + } else { + var_r = v; + var_g = var_1; + var_b = var_2 + } + + r = var_r * 255 //rgb results = 0 ÷ 255 + g = var_g * 255 + b = var_b * 255 + + } + return [Math.round(r), Math.round(g), Math.round(b)]; + }; + + this.rgb2hex = function (r,g,b) { + return this.toHex(r) + this.toHex(g) + this.toHex(b); + }; + + this.hexchars = "0123456789ABCDEF"; + + this.toHex = function(n) { + n = n || 0; + n = parseInt(n, 10); + if (isNaN(n)) n = 0; + n = Math.round(Math.min(Math.max(0, n), 255)); + + return this.hexchars.charAt((n - n % 16) / 16) + this.hexchars.charAt(n % 16); + }; + + this.toDec = function(hexchar) { + return this.hexchars.indexOf(hexchar.toUpperCase()); + }; + + this.hex2rgb = function(str) { + var rgb = []; + rgb[0] = (this.toDec(str.substr(0, 1)) * 16) + + this.toDec(str.substr(1, 1)); + rgb[1] = (this.toDec(str.substr(2, 1)) * 16) + + this.toDec(str.substr(3, 1)); + rgb[2] = (this.toDec(str.substr(4, 1)) * 16) + + this.toDec(str.substr(5, 1)); + // gLogger.debug("hex2rgb: " + str + ", " + rgb.toString()); + return rgb; + }; + + this.isValidRGB = function(a) { + if ((!a[0] && a[0] !=0) || isNaN(a[0]) || a[0] < 0 || a[0] > 255) return false; + if ((!a[1] && a[1] !=0) || isNaN(a[1]) || a[1] < 0 || a[1] > 255) return false; + if ((!a[2] && a[2] !=0) || isNaN(a[2]) || a[2] < 0 || a[2] > 255) return false; + + return true; + }; +} + diff --git a/distfiles/colorpicker/js/ddcolorposter.js b/distfiles/colorpicker/js/ddcolorposter.js new file mode 100644 index 0000000..445461d --- /dev/null +++ b/distfiles/colorpicker/js/ddcolorposter.js @@ -0,0 +1,43 @@ +/* +====================================================================== +ddcolorposter.js: By Dynamic Drive (http://www.dynamicdrive.com) +Communicates between Yahoo Color Picker and form fields on your page +Created: Feb 20th, 06' +====================================================================== +*/ + +function executeonload(functionref){ + if (window.addEventListener) + window.addEventListener("load", functionref, false) + else if (window.attachEvent) + window.attachEvent("onload", functionref) + else if (document.getElementById) + window.onload=functionref +} + +var ddcolorposter={ +initialize:function(r,g,b, hexvalue){ + this.rvalue=r //store red value + this.gvalue=g //store green value + this.bvalue=b //store blue value + this.hexvalue=hexvalue //store combined hex value + if (typeof this.targetobj!="undefined"){ + this.targetobj.value=this.hexvalue //set field to selected hex color value + if (typeof this.divobj!="undefined") //set adjacent div to selected hex color value + this.divobj.style.backgroundColor="#"+this.hexvalue + } +}, +echocolor:function(inputobj, divID){ + this.targetobj=inputobj + this.divobj=document.getElementById(divID) + this.targetobj.onblur=function(){ + if (inputobj.value.search(/^[a-zA-Z0-9]{6}$/)!=-1) //if field contains valid hex value + document.getElementById(divID).style.backgroundColor="#"+inputobj.value + } +}, +fillcolorbox:function(inputID, divID){ + var inputobj=document.getElementById(inputID) + if (inputobj.value.search(/^[a-zA-Z0-9]{6}$/)!=-1) //if field contains valid hex value + document.getElementById(divID).style.backgroundColor="#"+inputobj.value +} +} \ No newline at end of file diff --git a/distfiles/colorpicker/js/dom.js b/distfiles/colorpicker/js/dom.js new file mode 100644 index 0000000..40ae0ee --- /dev/null +++ b/distfiles/colorpicker/js/dom.js @@ -0,0 +1,26 @@ +/* Copyright (c) 2006 Yahoo! Inc. All rights reserved. */ +YAHOO.util.Dom=new function(){this.get=function(el){if(typeof el=='string'){el=document.getElementById(el);} +return el;};this.getStyle=function(el,property){var value=null;var dv=document.defaultView;el=this.get(el);if(property=='opacity'&&el.filters){value=1;try{value=el.filters.item('DXImageTransform.Microsoft.Alpha').opacity/100;}catch(e){try{value=el.filters.item('alpha').opacity/100;}catch(e){}}} +else if(el.style[property]){value=el.style[property];} +else if(el.currentStyle&&el.currentStyle[property]){value=el.currentStyle[property];} +else if(dv&&dv.getComputedStyle) +{var converted='';for(i=0,len=property.length;i=this.left&®ion.right<=this.right&®ion.top>=this.top&®ion.bottom<=this.bottom);};YAHOO.util.Region.prototype.getArea=function(){return((this.bottom-this.top)*(this.right-this.left));};YAHOO.util.Region.prototype.intersect=function(region){var t=Math.max(this.top,region.top);var r=Math.min(this.right,region.right);var b=Math.min(this.bottom,region.bottom);var l=Math.max(this.left,region.left);if(b>=t&&r>=l){return new YAHOO.util.Region(t,r,b,l);}else{return null;}};YAHOO.util.Region.prototype.union=function(region){var t=Math.min(this.top,region.top);var r=Math.max(this.right,region.right);var b=Math.max(this.bottom,region.bottom);var l=Math.min(this.left,region.left);return new YAHOO.util.Region(t,r,b,l);};YAHOO.util.Region.prototype.toString=function(){return("Region {"+" t: "+this.top+", r: "+this.right+", b: "+this.bottom+", l: "+this.left+"}");} +YAHOO.util.Region.getRegion=function(el){var p=YAHOO.util.Dom.getXY(el);var t=p[1];var r=p[0]+el.offsetWidth;var b=p[1]+el.offsetHeight;var l=p[0];return new YAHOO.util.Region(t,r,b,l);};YAHOO.util.Point=function(x,y){this.x=x;this.y=y;this.top=y;this.right=x;this.bottom=y;this.left=x;};YAHOO.util.Point.prototype=new YAHOO.util.Region(); \ No newline at end of file diff --git a/distfiles/colorpicker/js/dragdrop.js b/distfiles/colorpicker/js/dragdrop.js new file mode 100644 index 0000000..90e1b46 --- /dev/null +++ b/distfiles/colorpicker/js/dragdrop.js @@ -0,0 +1 @@ +/* Copyright (c) 2006 Yahoo! Inc. All rights reserved. */YAHOO.util.DragDrop=function(id,_2){if(id){this.init(id,_2);}};YAHOO.util.DragDrop.prototype={id:null,dragElId:null,handleElId:null,invalidHandleTypes:null,startPageX:0,startPageY:0,groups:null,locked:false,lock:function(){this.locked=true;},unlock:function(){this.locked=false;},isTarget:true,padding:null,_domRef:null,__ygDragDrop:true,constrainX:false,constrainY:false,minX:0,maxX:0,minY:0,maxY:0,maintainOffset:false,xTicks:null,yTicks:null,primaryButtonOnly:true,b4StartDrag:function(x,y){},startDrag:function(x,y){},b4Drag:function(e){},onDrag:function(e){},onDragEnter:function(e,id){},b4DragOver:function(e){},onDragOver:function(e,id){},b4DragOut:function(e){},onDragOut:function(e,id){},b4DragDrop:function(e){},onDragDrop:function(e,id){},b4EndDrag:function(e){},endDrag:function(e){},b4MouseDown:function(e){},onMouseDown:function(e){},onMouseUp:function(e){},getEl:function(){if(!this._domRef){this._domRef=this.DDM.getElement(this.id);}return this._domRef;},getDragEl:function(){return this.DDM.getElement(this.dragElId);},init:function(id,_6){this.initTarget(id,_6);YAHOO.util.Event.addListener(id,"mousedown",this.handleMouseDown,this,true);},initTarget:function(id,_7){this.DDM=YAHOO.util.DDM;this.padding=[0,0,0,0];this.groups={};this.id=id;this.setDragElId(id);this.invalidHandleTypes={a:"a"};this.handleElId=id;if(document&&document.body){this.setInitPosition();}this.addToGroup((_7)?_7:"default");},setPadding:function(_8,_9,_10,_11){if(!_9&&0!==_9){this.padding=[_8,_8,_8,_8];}else{if(!_10&&0!==_10){this.padding=[_8,_9,_8,_9];}else{this.padding=[_8,_9,_10,_11];}}},setInitPosition:function(_12,_13){var el=this.getEl();if(!this.DDM.verifyEl(el)){return;}var dx=_12||0;var dy=_13||0;var p=YAHOO.util.Dom.getXY(el);this.initPageX=p[0]-dx;this.initPageY=p[1]-dy;this.lastPageX=p[0];this.lastPageY=p[1];this.setStartPosition(p);},setStartPosition:function(pos){var p=pos||YAHOO.util.Dom.getXY(this.getEl());this.startPageX=p[0];this.startPageY=p[1];},addToGroup:function(_19){this.groups[_19]=true;this.DDM.regDragDrop(this,_19);},setDragElId:function(id){this.dragElId=id;},setHandleElId:function(id){this.handleElId=id;this.DDM.regHandle(this.id,id);},setOuterHandleElId:function(id){YAHOO.util.Event.addListener(id,"mousedown",this.handleMouseDown,this,true);this.setHandleElId(id);},unreg:function(){YAHOO.util.Event.removeListener(this.id,"mousedown",this.handleMouseDown);this._domRef=null;this.DDM._remove(this);},isLocked:function(){return (this.DDM.isLocked()||this.locked);},handleMouseDown:function(e,oDD){var EU=YAHOO.util.Event;var _22=e.which||e.button;if(this.primaryButtonOnly&&_22>1){return;}if(this.isLocked()){return;}this.DDM.refreshCache(this.groups);var pt=new YAHOO.util.Point(EU.getPageX(e),EU.getPageY(e));if(this.DDM.isOverTarget(pt,this)){var _24=EU.getTarget(e);if(this.isValidHandleChild(_24)&&(this.id==this.handleElId||this.DDM.handleWasClicked(_24,this.id))){this.setStartPosition();this.b4MouseDown(e);this.onMouseDown(e);this.DDM.handleMouseDown(e,this);this.DDM.stopEvent(e);}}},addInvalidHandleType:function(_25){var _26=_25.toUpperCase();this.invalidHandleTypes[_26]=_26;},removeInvalidHandleType:function(_27){var _28=_27.toUpperCase();this.invalidHandleTypes[_28]=null;},isValidHandleChild:function(_29){var _30=_29.nodeName;if(_30=="#text"){_30=_29.parentNode.nodeName;}return (!this.invalidHandleTypes[_30]);},setXTicks:function(_31,_32){this.xTicks=[];this.xTickSize=_32;var _33={};for(var i=this.initPageX;i>=this.minX;i=i-_32){if(!_33[i]){this.xTicks[this.xTicks.length]=i;_33[i]=true;}}for(i=this.initPageX;i<=this.maxX;i=i+_32){if(!_33[i]){this.xTicks[this.xTicks.length]=i;_33[i]=true;}}this.xTicks.sort(this.DDM.numericSort);},setYTicks:function(_35,_36){this.yTicks=[];this.yTickSize=_36;var _37={};for(var i=this.initPageY;i>=this.minY;i=i-_36){if(!_37[i]){this.yTicks[this.yTicks.length]=i;_37[i]=true;}}for(i=this.initPageY;i<=this.maxY;i=i+_36){if(!_37[i]){this.yTicks[this.yTicks.length]=i;_37[i]=true;}}this.yTicks.sort(this.DDM.numericSort);},setXConstraint:function(_38,_39,_40){this.leftConstraint=_38;this.rightConstraint=_39;this.minX=this.initPageX-_38;this.maxX=this.initPageX+_39;if(_40){this.setXTicks(this.initPageX,_40);}this.constrainX=true;},setYConstraint:function(iUp,_42,_43){this.topConstraint=iUp;this.bottomConstraint=_42;this.minY=this.initPageY-iUp;this.maxY=this.initPageY+_42;if(_43){this.setYTicks(this.initPageY,_43);}this.constrainY=true;},resetConstraints:function(){var dx=(this.maintainOffset)?this.lastPageX-this.initPageX:0;var dy=(this.maintainOffset)?this.lastPageY-this.initPageY:0;this.setInitPosition(dx,dy);if(this.constrainX){this.setXConstraint(this.leftConstraint,this.rightConstraint,this.xTickSize);}if(this.constrainY){this.setYConstraint(this.topConstraint,this.bottomConstraint,this.yTickSize);}},getTick:function(val,_45){if(!_45){return val;}else{if(_45[0]>=val){return _45[0];}else{for(var i=0;i<_45.length;++i){var _46=i+1;if(_45[_46]&&_45[_46]>=val){var _47=val-_45[i];var _48=_45[_46]-val;return (_48>_47)?_45[i]:_45[_46];}}return _45[_45.length-1];}}},toString:function(val,_49){return ("YAHOO.util.DragDrop {"+this.id+"}");}};if(!YAHOO.util.DragDropMgr){YAHOO.util.DragDropMgr=new function(){var _50=YAHOO.util;this.ids={};this.handleIds={};this.dragCurrent=null;this.dragOvers={};this.deltaX=0;this.deltaY=0;this.preventDefault=true;this.stopPropagation=true;this.initalized=false;this.locked=false;this.init=function(){};this.POINT=0;this.INTERSECT=1;this.mode=this.POINT;this._execOnAll=function(_51,_52){for(var i in this.ids){for(var j in this.ids[i]){var oDD=this.ids[i][j];if(!this.isTypeOfDD(oDD)){continue;}oDD[_51].apply(oDD,_52);}}};this._onLoad=function(){this._execOnAll("setInitPosition",[]);var EU=_50.Event;EU.addListener(document,"mouseup",this.handleMouseUp,this,true);EU.addListener(document,"mousemove",this.handleMouseMove,this,true);EU.addListener(window,"unload",this._onUnload,this,true);EU.addListener(window,"resize",this._onResize,this,true);this.initalized=true;};this._onResize=function(e){this._execOnAll("resetConstraints",[]);};this.lock=function(){this.locked=true;};this.unlock=function(){this.locked=false;};this.isLocked=function(){return this.locked;};this.locationCache={};this.useCache=true;this.clickPixelThresh=3;this.clickTimeThresh=1000;this.dragThreshMet=false;this.clickTimeout=null;this.startX=0;this.startY=0;this.regDragDrop=function(oDD,_54){if(!this.initialized){this.init();}if(!this.ids[_54]){this.ids[_54]={};}this.ids[_54][oDD.id]=oDD;};this._remove=function(oDD){for(var g in oDD.groups){if(g&&this.ids[g][oDD.id]){delete this.ids[g][oDD.id];}}delete this.handleIds[oDD.id];};this.regHandle=function(_56,_57){if(!this.handleIds[_56]){this.handleIds[_56]={};}this.handleIds[_56][_57]=_57;};this.isDragDrop=function(id){return (this.getDDById(id))?true:false;};this.getRelated=function(_58,_59){var _60=[];for(var i in _58.groups){for(j in this.ids[i]){var dd=this.ids[i][j];if(!this.isTypeOfDD(dd)){continue;}if(!_59||dd.isTarget){_60[_60.length]=dd;}}}return _60;};this.isLegalTarget=function(oDD,_62){var _63=this.getRelated(oDD);for(var i=0;i<_63.length;++i){if(_63[i].id==_62.id){return true;}}return false;};this.isTypeOfDD=function(oDD){return (oDD&&oDD.__ygDragDrop);};this.isHandle=function(_64,_65){return (this.handleIds[_64]&&this.handleIds[_64][_65]);};this.getDDById=function(id){for(var i in this.ids){if(this.ids[i][id]){return this.ids[i][id];}}return null;};this.handleMouseDown=function(e,oDD){this.dragCurrent=oDD;var el=oDD.getEl();this.startX=_50.Event.getPageX(e);this.startY=_50.Event.getPageY(e);this.deltaX=this.startX-el.offsetLeft;this.deltaY=this.startY-el.offsetTop;this.dragThreshMet=false;this.clickTimeout=setTimeout("var DDM=YAHOO.util.DDM;DDM.startDrag(DDM.startX, DDM.startY)",this.clickTimeThresh);};this.startDrag=function(x,y){clearTimeout(this.clickTimeout);if(this.dragCurrent){this.dragCurrent.b4StartDrag(x,y);this.dragCurrent.startDrag(x,y);}this.dragThreshMet=true;};this.handleMouseUp=function(e){if(!this.dragCurrent){return;}clearTimeout(this.clickTimeout);if(this.dragThreshMet){this.fireEvents(e,true);}else{}this.stopDrag(e);this.stopEvent(e);};this.stopEvent=function(e){if(this.stopPropagation){_50.Event.stopPropagation(e);}if(this.preventDefault){_50.Event.preventDefault(e);}};this.stopDrag=function(e){if(this.dragCurrent){if(this.dragThreshMet){this.dragCurrent.b4EndDrag(e);this.dragCurrent.endDrag(e);}this.dragCurrent.onMouseUp(e);}this.dragCurrent=null;this.dragOvers={};};this.handleMouseMove=function(e){if(!this.dragCurrent){return;}if(_50.Event.isIE&&!e.button){this.stopEvent(e);return this.handleMouseUp(e);}if(!this.dragThreshMet){var _66=Math.abs(this.startX-_50.Event.getPageX(e));var _67=Math.abs(this.startY-_50.Event.getPageY(e));if(_66>this.clickPixelThresh||_67>this.clickPixelThresh){this.startDrag(this.startX,this.startY);}}if(this.dragThreshMet){this.dragCurrent.b4Drag(e);this.dragCurrent.onDrag(e);this.fireEvents(e,false);}this.stopEvent(e);};this.fireEvents=function(e,_68){var dc=this.dragCurrent;if(!dc||dc.isLocked()){return;}var x=_50.Event.getPageX(e);var y=_50.Event.getPageY(e);var pt=new YAHOO.util.Point(x,y);var _70=[];var _71=[];var _72=[];var _73=[];var _74=[];for(var i in this.dragOvers){var ddo=this.dragOvers[i];if(!this.isTypeOfDD(ddo)){continue;}if(!this.isOverTarget(pt,ddo,this.mode)){_71.push(ddo);}_70[i]=true;delete this.dragOvers[i];}for(var _76 in dc.groups){if("string"!=typeof _76){continue;}for(i in this.ids[_76]){var oDD=this.ids[_76][i];if(!this.isTypeOfDD(oDD)){continue;}if(oDD.isTarget&&!oDD.isLocked()&&oDD!=dc){if(this.isOverTarget(pt,oDD,this.mode)){if(_68){_73.push(oDD);}else{if(!_70[oDD.id]){_74.push(oDD);}else{_72.push(oDD);}this.dragOvers[oDD.id]=oDD;}}}}}if(this.mode){if(_71.length>0){dc.b4DragOut(e,_71);dc.onDragOut(e,_71);}if(_74.length>0){dc.onDragEnter(e,_74);}if(_72.length>0){dc.b4DragOver(e,_72);dc.onDragOver(e,_72);}if(_73.length>0){dc.b4DragDrop(e,_73);dc.onDragDrop(e,_73);}}else{for(i=0;i<_71.length;++i){dc.b4DragOut(e,_71[i].id);dc.onDragOut(e,_71[i].id);}for(i=0;i<_74.length;++i){dc.onDragEnter(e,_74[i].id);}for(i=0;i<_72.length;++i){dc.b4DragOver(e,_72[i].id);dc.onDragOver(e,_72[i].id);}for(i=0;i<_73.length;++i){dc.b4DragDrop(e,_73[i].id);dc.onDragDrop(e,_73[i].id);}}};this.getBestMatch=function(dds){var _78=null;if(dds.length==1){_78=dds[0];}else{for(var i=0;i500){}else{setTimeout("YAHOO.util.DDM._addListeners()",10);this._timeoutCount+=1;}}};this.handleWasClicked=function(node,id){if(this.isHandle(id,node.id)){return true;}else{var p=node.parentNode;while(p){if(this.isHandle(id,p.id)){return true;}else{p=p.parentNode;}}}return false;};};YAHOO.util.DDM=YAHOO.util.DragDropMgr;YAHOO.util.DDM._addListeners();}YAHOO.util.DD=function(id,_103){if(id){this.init(id,_103);}};YAHOO.util.DD.prototype=new YAHOO.util.DragDrop();YAHOO.util.DD.prototype.scroll=true;YAHOO.util.DD.prototype.autoOffset=function(_104,_105){var el=this.getEl();var _106=YAHOO.util.Dom.getXY(el);var x=_104-_106[0];var y=_105-_106[1];this.setDelta(x,y);};YAHOO.util.DD.prototype.setDelta=function(_107,_108){this.deltaX=_107;this.deltaY=_108;};YAHOO.util.DD.prototype.setDragElPos=function(_109,_110){var el=this.getDragEl();this.alignElWithMouse(el,_109,_110);};YAHOO.util.DD.prototype.alignElWithMouse=function(el,_111,_112){var _113=this.getTargetCoord(_111,_112);var _114=[_113.x,_113.y];YAHOO.util.Dom.setXY(el,_114);this.cachePosition(_113.x,_113.y);this.autoScroll(_113.x,_113.y,el.offsetHeight,el.offsetWidth);};YAHOO.util.DD.prototype.cachePosition=function(_115,_116){if(_115){this.lastPageX=_115;this.lastPageY=_116;}else{var _117=YAHOO.util.Dom.getXY(this.getEl());this.lastPageX=_117[0];this.lastPageY=_117[1];}};YAHOO.util.DD.prototype.autoScroll=function(x,y,h,w){if(this.scroll){var _120=this.DDM.getClientHeight();var _121=this.DDM.getClientWidth();var st=this.DDM.getScrollTop();var sl=this.DDM.getScrollLeft();var bot=h+y;var _125=w+x;var _126=(_120+st-y-this.deltaY);var _127=(_121+sl-x-this.deltaX);var _128=40;var _129=(document.all)?80:30;if(bot>_120&&_126<_128){window.scrollTo(sl,st+_129);}if(y0&&y-st<_128){window.scrollTo(sl,st-_129);}if(_125>_121&&_127<_128){window.scrollTo(sl+_129,st);}if(x0&&x-sl<_128){window.scrollTo(sl-_129,st);}}};YAHOO.util.DD.prototype.getTargetCoord=function(_130,_131){var x=_130-this.deltaX;var y=_131-this.deltaY;if(this.constrainX){if(xthis.maxX){x=this.maxX;}}if(this.constrainY){if(ythis.maxY){y=this.maxY;}}x=this.getTick(x,this.xTicks);y=this.getTick(y,this.yTicks);return {x:x,y:y};};YAHOO.util.DD.prototype.b4MouseDown=function(e){this.autoOffset(YAHOO.util.Event.getPageX(e),YAHOO.util.Event.getPageY(e));};YAHOO.util.DD.prototype.b4Drag=function(e){this.setDragElPos(YAHOO.util.Event.getPageX(e),YAHOO.util.Event.getPageY(e));};YAHOO.util.DDProxy=function(id,_132){if(id){this.forceCssPosition=false;this.init(id,_132);this.initFrame();}};YAHOO.util.DDProxy.prototype=new YAHOO.util.DD();YAHOO.util.DDProxy.frameDiv=null;YAHOO.util.DDProxy.dragElId="ygddfdiv";YAHOO.util.DDProxy.prototype.borderWidth=2;YAHOO.util.DDProxy.prototype.resizeFrame=true;YAHOO.util.DDProxy.prototype.centerFrame=false;YAHOO.util.DDProxy.createFrame=function(){var THIS=YAHOO.util.DDProxy;if(!document||!document.body){setTimeout(THIS.createFrame,50);return;}if(!THIS.frameDiv){THIS.frameDiv=document.createElement("div");THIS.frameDiv.id=THIS.dragElId;var s=THIS.frameDiv.style;s.position="absolute";s.visibility="hidden";s.cursor="move";s.border="2px solid #aaa";s.zIndex=999;document.body.appendChild(THIS.frameDiv);}};YAHOO.util.DDProxy.prototype.initFrame=function(){YAHOO.util.DDProxy.createFrame();this.setDragElId(YAHOO.util.DDProxy.dragElId);this.useAbsMath=true;};YAHOO.util.DDProxy.prototype.showFrame=function(_134,_135){var el=this.getEl();var s=this.getDragEl().style;if(this.resizeFrame){s.width=(parseInt(el.offsetWidth)-(2*this.borderWidth))+"px";s.height=(parseInt(el.offsetHeight)-(2*this.borderWidth))+"px";}if(this.centerFrame){this.setDelta(Math.round(parseInt(s.width)/2),Math.round(parseInt(s.width)/2));}this.setDragElPos(_134,_135);s.visibility="";};YAHOO.util.DDProxy.prototype.b4MouseDown=function(e){var x=YAHOO.util.Event.getPageX(e);var y=YAHOO.util.Event.getPageY(e);this.autoOffset(x,y);this.setDragElPos(x,y);};YAHOO.util.DDProxy.prototype.b4StartDrag=function(x,y){this.showFrame(x,y);};YAHOO.util.DDProxy.prototype.b4EndDrag=function(e){var s=this.getDragEl().style;s.visibility="hidden";};YAHOO.util.DDProxy.prototype.endDrag=function(e){var lel=this.getEl();var del=this.getDragEl();del.style.visibility="";lel.style.visibility="hidden";YAHOO.util.DDM.moveToEl(lel,del);del.style.visibility="hidden";lel.style.visibility="";};YAHOO.util.DDTarget=function(id,_138){if(id){this.initTarget(id,_138);}};YAHOO.util.DDTarget.prototype=new YAHOO.util.DragDrop(); \ No newline at end of file diff --git a/distfiles/colorpicker/js/event.js b/distfiles/colorpicker/js/event.js new file mode 100644 index 0000000..dbdc88b --- /dev/null +++ b/distfiles/colorpicker/js/event.js @@ -0,0 +1 @@ +/* Copyright (c) 2006 Yahoo! Inc. All rights reserved. */YAHOO.util.CustomEvent=function(_1,_2){this.type=_1;this.scope=_2||window;this.subscribers=[];if(YAHOO.util["Event"]){YAHOO.util.Event.regCE(this);}};YAHOO.util.CustomEvent.prototype={subscribe:function(fn,_4,_5){this.subscribers.push(new YAHOO.util.Subscriber(fn,_4,_5));},unsubscribe:function(fn,_6){var _7=false;for(var i=0;i=0){_40=_15[_41];}if(!el||!_40){return false;}if(el.removeEventListener){el.removeEventListener(_39,_40[this.WFN],false);}else{if(el.detachEvent){el.detachEvent("on"+_39,_40[this.WFN]);}}delete _15[_41][this.WFN];delete _15[_41][this.FN];delete _15[_41];return true;},getTarget:function(ev,_43){var t=ev.target||ev.srcElement;if(_43&&t&&"#text"==t.nodeName){return t.parentNode;}else{return t;}},getPageX:function(ev){var x=ev.pageX;if(!x&&0!==x){x=ev.clientX||0;if(this.isIE){x+=this._getScrollLeft();}}return x;},getPageY:function(ev){var y=ev.pageY;if(!y&&0!==y){y=ev.clientY||0;if(this.isIE){y+=this._getScrollTop();}}return y;},getRelatedTarget:function(ev){var t=ev.relatedTarget;if(!t){if(ev.type=="mouseout"){t=ev.toElement;}else{if(ev.type=="mouseover"){t=ev.fromElement;}}}return t;},getTime:function(ev){if(!ev.time){var t=new Date().getTime();try{ev.time=t;}catch(e){return t;}}return ev.time;},stopEvent:function(ev){this.stopPropagation(ev);this.preventDefault(ev);},stopPropagation:function(ev){if(ev.stopPropagation){ev.stopPropagation();}else{ev.cancelBubble=true;}},preventDefault:function(ev){if(ev.preventDefault){ev.preventDefault();}else{ev.returnValue=false;}},getEvent:function(e){var ev=e||window.event;if(!ev){var c=this.getEvent.caller;while(c){ev=c.arguments[0];if(ev&&Event==ev.constructor){break;}c=c.caller;}}return ev;},getCharCode:function(ev){return ev.charCode||(ev.type=="keypress")?ev.keyCode:0;},_getCacheIndex:function(el,_48,fn){for(var i=0;i<_15.length;++i){var li=_15[i];if(li&&li[this.FN]==fn&&li[this.EL]==el&&li[this.TYPE]==_48){return i;}}return -1;},_isValidCollection:function(o){return (o&&o.length&&typeof o!="string"&&!o.tagName&&!o.alert&&typeof o[0]!="undefined");},elCache:{},getEl:function(id){return document.getElementById(id);},clearCache:function(){for(i in this.elCache){delete this.elCache[i];}},regCE:function(ce){_18.push(ce);},_load:function(e){_14=true;},_tryPreloadAttach:function(){var _52=!_14;for(var i=0;i<_16.length;++i){var d=_16[i];if(d){var el=this.getEl(d[this.EL]);if(el){this.on(el,d[this.TYPE],d[this.FN],d[this.SCOPE],d[this.ADJ_SCOPE]);delete _16[i];}}}if(_52){setTimeout("YAHOO.util.Event._tryPreloadAttach()",50);}},_unload:function(e,me){for(var i=0;i<_17.length;++i){var l=_17[i];if(l){var _56=(l[this.ADJ_SCOPE])?l[this.SCOPE]:window;l[this.FN].call(_56,this.getEvent(e),l[this.SCOPE]);}}if(_15&&_15.length>0){for(i=0;i<_15.length;++i){l=_15[i];if(l){this.removeListener(l[this.EL],l[this.TYPE],l[this.FN]);}}this.clearCache();}for(i=0;i<_18.length;++i){_18[i].unsubscribeAll();delete _18[i];}for(i=0;i<_19.length;++i){delete _19[i][0];delete _19[i];}},_getScrollLeft:function(){return this._getScroll()[1];},_getScrollTop:function(){return this._getScroll()[0];},_getScroll:function(){var dd=document.documentElement;db=document.body;if(dd&&dd.scrollTop){return [dd.scrollTop,dd.scrollLeft];}else{if(db){return [db.scrollTop,db.scrollLeft];}else{return [0,0];}}}};}();YAHOO.util.Event.on=YAHOO.util.Event.addListener;if(document&&document.body){YAHOO.util.Event._load();}else{YAHOO.util.Event.on(window,"load",YAHOO.util.Event._load,YAHOO.util.Event,true);}YAHOO.util.Event.on(window,"unload",YAHOO.util.Event._unload,YAHOO.util.Event,true);YAHOO.util.Event._tryPreloadAttach();} \ No newline at end of file diff --git a/distfiles/colorpicker/js/key.js b/distfiles/colorpicker/js/key.js new file mode 100644 index 0000000..f2abf53 --- /dev/null +++ b/distfiles/colorpicker/js/key.js @@ -0,0 +1,34 @@ +/* Copyright (c) 2006 Yahoo! Inc. All rights reserved. */ + +YAHOO.util.Key = new function() { + // this.logger = new ygLogger("ygEventUtil"); + // DOM key constants + this.DOM_VK_UNDEFINED = 0x0; + this.DOM_VK_RIGHT_ALT = 0x12; + this.DOM_VK_LEFT_ALT = 0x12; + this.DOM_VK_LEFT_CONTROL = 0x11; + this.DOM_VK_RIGHT_CONTROL = 0x11; + this.DOM_VK_LEFT_SHIFT = 0x10; + this.DOM_VK_RIGHT_SHIFT = 0x10; + this.DOM_VK_META = 0x9D; + this.DOM_VK_BACK_SPACE = 0x08; + this.DOM_VK_CAPS_LOCK = 0x14; + this.DOM_VK_DELETE = 0x7F; + this.DOM_VK_END = 0x23; + this.DOM_VK_ENTER = 0x0D; + this.DOM_VK_ESCAPE = 0x1B; + this.DOM_VK_HOME = 0x24; + this.DOM_VK_NUM_LOCK = 0x90; + this.DOM_VK_PAUSE = 0x13; + this.DOM_VK_PRINTSCREEN = 0x9A; + this.DOM_VK_SCROLL_LOCK = 0x91; + this.DOM_VK_SPACE = 0x20; + this.DOM_VK_TAB = 0x09; + this.DOM_VK_LEFT = 0x25; + this.DOM_VK_RIGHT = 0x27; + this.DOM_VK_UP = 0x26; + this.DOM_VK_DOWN = 0x28; + this.DOM_VK_PAGE_DOWN = 0x22; + this.DOM_VK_PAGE_UP = 0x21; +}; + diff --git a/distfiles/colorpicker/js/log.js b/distfiles/colorpicker/js/log.js new file mode 100644 index 0000000..ed4f973 --- /dev/null +++ b/distfiles/colorpicker/js/log.js @@ -0,0 +1,89 @@ +/* Copyright (c) 2006 Yahoo! Inc. All rights reserved. */ + +/** + * @class a general logging class. Expects to be initalized with a reference + * to a html element to write to. + * + * @constructor + * @param {String} sModuleName the name of the module this instance belongs + * to. Used in the log message to help id where the msg came from. + */ +function ygLogger(sModuleName) { + if (this.setModuleName) + this.setModuleName(sModuleName); +} + +ygLogger.DEBUG_ENABLED = true; +ygLogger.targetEl = null; +ygLogger.logStack = []; +ygLogger.startLog = new Date().getTime(); +ygLogger.lastLog = new Date().getTime(); +ygLogger.locked = true; +ygLogger.logTimeout = null; + +ygLogger.init = function(oHostElement) { + if (oHostElement) { + ygLogger.targetEl = oHostElement; + } else { + // create element or create window? + } +}; + +ygLogger.prototype.setModuleName = function(sModuleName) { + this.logName = sModuleName; +}; + +ygLogger.prototype.debug = function() { + if (ygLogger.DEBUG_ENABLED) { + var newDate = new Date(); + var newTime = newDate.getTime(); + var timeStamp = newTime - ygLogger.lastLog; + var totalSeconds = (newTime - ygLogger.startLog) / 1000; + + ygLogger.lastLog = newTime; + + for (var i = 0; i < arguments.length; i++) { + ygLogger.logStack[ygLogger.logStack.length] = + timeStamp + " ms(" + totalSeconds + ") " + + newDate.toLocaleTimeString() + "
" + + this.logName + ": " + arguments[i] + ""; + } + + if (ygLogger.logTimeout == null) { + ygLogger.logTimeout = setTimeout("ygLogger._outputMessages()" , 1); + } + + } +}; + +ygLogger.disable = function() { + ygLogger.DEBUG_ENABLED = false; + try { ygLogger.targetEl.style.visibility = "hidden"; } catch(e) {} + +}; + +ygLogger.enable = function() { + ygLogger.DEBUG_ENABLED = true; + try { ygLogger.targetEl.style.visibility = ""; } catch(e) {} +}; + +ygLogger._outputMessages = function() { + if (ygLogger.targetEl != null) { + + for (var i = 0; i < ygLogger.logStack.length; i++) { + var sMsg = ygLogger.logStack[i]; + var oNewElement = ygLogger.targetEl.appendChild( + document.createElement("p")); + oNewElement.innerHTML = sMsg; + } + + ygLogger.logStack = []; + + ygLogger.targetEl.scrollTop = ygLogger.targetEl.scrollHeight; + + // debugger; + } + + ygLogger.logTimeout = null; +}; + diff --git a/distfiles/colorpicker/js/slider.js b/distfiles/colorpicker/js/slider.js new file mode 100644 index 0000000..8bcfbcf --- /dev/null +++ b/distfiles/colorpicker/js/slider.js @@ -0,0 +1 @@ +/* Copyright (c) 2006 Yahoo! Inc. All rights reserved. */YAHOO.widget.Slider=function(_1,_2,_3){if(_1){this.init(_1,_2,true);var _4=this;this.thumb=_3;_3.onChange=function(){_4.onThumbChange();};var el=_3.getEl();this.thumbCenterPoint={x:el.offsetWidth/2,y:el.offsetHeight/2};this.isTarget=false;this.animate=YAHOO.widget.Slider.ANIM_AVAIL;this.baselinePos=YAHOO.util.Dom.getXY(this.getEl());this.tickPause=40;if(_3._isHoriz&&_3.xTicks){this.tickPause=Math.round(360/_3.xTicks.length);}else{if(_3.yTicks){this.tickPause=Math.round(360/_3.yTicks.length);}}_3.onMouseDown=function(){return _4.focus();};_3.b4MouseDown=function(){return _4.b4MouseDown();};_3.onMouseUp=function(){_4.onMouseUp();};_3.onDrag=function(){_4.fireEvents();};if(_3._isRegion){this.setRegionValue(0,0);}else{this.setValue(0);}}};YAHOO.widget.Slider.prototype=new YAHOO.util.DragDrop();YAHOO.widget.Slider.getHorizSlider=function(_6,_7,_8,_9,_10){return new YAHOO.widget.Slider(_6,_6,new YAHOO.widget.SliderThumb(_7,_6,_8,_9,0,0,_10));};YAHOO.widget.Slider.getVertSlider=function(_11,_12,iUp,_14,_15){return new YAHOO.widget.Slider(_11,_11,new YAHOO.widget.SliderThumb(_12,_11,0,0,iUp,_14,_15));};YAHOO.widget.Slider.getSliderRegion=function(_16,_17,_18,_19,iUp,_20,_21){return new YAHOO.widget.Slider(_16,_16,new YAHOO.widget.SliderThumb(_17,_16,_18,_19,iUp,_20,_21));};YAHOO.widget.Slider.ANIM_AVAIL=true;YAHOO.widget.Slider.prototype.lock=function(){this.thumb.lock();this.locked=true;};YAHOO.widget.Slider.prototype.unlock=function(){this.thumb.unlock();this.locked=false;};YAHOO.widget.Slider.prototype.onMouseUp=function(){this._deferSlideEnd=true;this.fireEvents();};YAHOO.widget.Slider.prototype.focus=function(){var el=this.getEl();if(el.focus){el.focus();}this.verifyOffset();if(this.isLocked()){return false;}else{this.onSlideStart();return true;}};YAHOO.widget.Slider.prototype.onChange=function(_22,_23){};YAHOO.widget.Slider.prototype.onSlideStart=function(){};YAHOO.widget.Slider.prototype.onSlideEnd=function(){};YAHOO.widget.Slider.prototype.getValue=function(){return this.thumb.getValue();};YAHOO.widget.Slider.prototype.getXValue=function(){return this.thumb.getXValue();};YAHOO.widget.Slider.prototype.getYValue=function(){return this.thumb.getYValue();};YAHOO.widget.Slider.prototype.onThumbChange=function(){var t=this.thumb;if(t._isRegion){t.onChange(t.getXValue(),t.getYValue());}else{t.onChange(t.getValue());}};YAHOO.widget.Slider.prototype.setValue=function(_25,_26){if(this.isLocked()){return false;}if(isNaN(_25)){return false;}var t=this.thumb;var _27,newY;if(t._isRegion){return false;}else{if(t._isHoriz){_27=t.initPageX+_25+this.thumbCenterPoint.x;this.moveThumb(_27,t.initPageY,_26);}else{newY=t.initPageY+_25+this.thumbCenterPoint.y;this.moveThumb(t.initPageX,newY,_26);}}return true;};YAHOO.widget.Slider.prototype.setRegionValue=function(_28,_29,_30){if(this.isLocked()){return false;}if(isNaN(_28)){return false;}var t=this.thumb;if(t._isRegion){var _31=t.initPageX+_28+this.thumbCenterPoint.x;var _32=t.initPageY+_29+this.thumbCenterPoint.y;this.moveThumb(_31,_32,_30);return true;}return false;};YAHOO.widget.Slider.prototype.verifyOffset=function(){var _33=YAHOO.util.Dom.getXY(this.getEl());if(_33[0]!=this.baselinePos[0]||_33[1]!=this.baselinePos[1]){this.thumb.resetConstraints();this.baselinePos=_33;return false;}return true;};YAHOO.widget.Slider.prototype.moveThumb=function(x,y,_36){this.verifyOffset();var _37=this;var t=this.thumb;t.setDelta(this.thumbCenterPoint.x,this.thumbCenterPoint.y);var _p=t.getTargetCoord(x,y);var p=[_p.x,_p.y];if(this.animate&&YAHOO.widget.Slider.ANIM_AVAIL&&t._graduated&&!_36){this.lock();setTimeout(function(){_37.moveOneTick(p);},this.tickPause);}else{if(this.animate&&YAHOO.widget.Slider.ANIM_AVAIL&&!_36){this.lock();var _40=new YAHOO.util.Motion(t.id,{points:{to:p}},0.4,YAHOO.util.Easing.easeOut);_40.onComplete.subscribe(function(){_37.endAnim();});_40.animate();}else{t.setDragElPos(x,y);this.fireEvents();}}};YAHOO.widget.Slider.prototype.moveOneTick=function(_41){var t=this.thumb;var _42=YAHOO.util.Dom.getXY(t.getEl());var tmp;var _44=null;if(t._isRegion){_44=this._getNextX(_42,_41);var _45=(_44)?_44[0]:_42[0];_44=this._getNextY([_45,_42[1]],_41);}else{if(t._isHoriz){_44=this._getNextX(_42,_41);}else{_44=this._getNextY(_42,_41);}}if(_44){YAHOO.util.Dom.setXY(t.getEl(),_44);if(!(_44[0]==_41[0]&&_44[1]==_41[1])){var _46=this;setTimeout(function(){_46.moveOneTick(_41);},this.tickPause);}else{this.unlock();this.fireEvents();}}else{this.unlock();this.fireEvents();}};YAHOO.widget.Slider.prototype._getNextX=function(_47,_48){var t=this.thumb;var _49;var tmp=[];var _50=null;if(_47[0]>_48[0]){_49=t.tickSize-this.thumbCenterPoint.x;tmp=t.getTargetCoord(_47[0]-_49,_47[1]);_50=[tmp.x,tmp.y];}else{if(_47[0]<_48[0]){_49=t.tickSize+this.thumbCenterPoint.x;tmp=t.getTargetCoord(_47[0]+_49,_47[1]);_50=[tmp.x,tmp.y];}else{}}return _50;};YAHOO.widget.Slider.prototype._getNextY=function(_51,_52){var t=this.thumb;var _53;var tmp=[];var _54=null;if(_51[1]>_52[1]){_53=t.tickSize-this.thumbCenterPoint.y;tmp=t.getTargetCoord(_51[0],_51[1]-_53);_54=[tmp.x,tmp.y];}else{if(_51[1]<_52[1]){_53=t.tickSize+this.thumbCenterPoint.y;tmp=t.getTargetCoord(_51[0],_51[1]+_53);_54=[tmp.x,tmp.y];}else{}}return _54;};YAHOO.widget.Slider.prototype.b4MouseDown=function(e){this.thumb.resetConstraints();};YAHOO.widget.Slider.prototype.onMouseDown=function(e){if(!this.isLocked()){var x=YAHOO.util.Event.getPageX(e);var y=YAHOO.util.Event.getPageY(e);this.moveThumb(x,y);this.focus();}};YAHOO.widget.Slider.prototype.onDrag=function(e){if(!this.isLocked()){var x=YAHOO.util.Event.getPageX(e);var y=YAHOO.util.Event.getPageY(e);this.moveThumb(x,y,true);}};YAHOO.widget.Slider.prototype.endAnim=function(){this.unlock();this.fireEvents();};YAHOO.widget.Slider.prototype.fireEvents=function(){var t=this.thumb;t.cachePosition();if(!this.isLocked()){if(t._isRegion){var _56=t.getXValue();var _57=t.getYValue();if(_56!=this.previousX||_57!=this.previousY){this.onChange(_56,_57);}this.previousX=_56;this.previousY=_57;}else{var _58=t.getValue();if(_58!=this.previousVal){this.onChange(_58);}this.previousVal=_58;}if(this._deferSlideEnd){this.onSlideEnd();this._deferSlideEnd=false;}}};YAHOO.widget.SliderThumb=function(id,_60,_61,_62,iUp,_63,_64){if(id){this.init(id,_60);this.parentElId=_60;}this.isTarget=false;this.tickSize=_64;this.maintainOffset=true;this.initSlider(_61,_62,iUp,_63,_64);};YAHOO.widget.SliderThumb.prototype=new YAHOO.util.DD();YAHOO.widget.SliderThumb.prototype.getOffsetFromParent=function(){var _65=YAHOO.util.Dom.getXY(this.getEl());var _66=YAHOO.util.Dom.getXY(this.parentElId);return [(_65[0]-_66[0]),(_65[1]-_66[1])];};YAHOO.widget.SliderThumb.prototype.startOffset=null;YAHOO.widget.SliderThumb.prototype._isHoriz=false;YAHOO.widget.SliderThumb.prototype._prevVal=0;YAHOO.widget.SliderThumb.prototype._graduated=false;YAHOO.widget.SliderThumb.prototype.initSlider=function(_67,_68,iUp,_69,_70){this.setXConstraint(_67,_68,_70);this.setYConstraint(iUp,_69,_70);if(_70&&_70>1){this._graduated=true;}this._isHoriz=(_67>0||_68>0);this._isVert=(iUp>0||_69>0);this._isRegion=(this._isHoriz&&this._isVert);this.startOffset=this.getOffsetFromParent();};YAHOO.widget.SliderThumb.prototype.getValue=function(){var val=(this._isHoriz)?this.getXValue():this.getYValue();return val;};YAHOO.widget.SliderThumb.prototype.getXValue=function(){var _72=this.getOffsetFromParent();return (_72[0]-this.startOffset[0]);};YAHOO.widget.SliderThumb.prototype.getYValue=function(){var _73=this.getOffsetFromParent();return (_73[1]-this.startOffset[1]);};YAHOO.widget.SliderThumb.prototype.onChange=function(x,y){};if("undefined"==typeof YAHOO.util.Anim){YAHOO.widget.Slider.ANIM_AVAIL=false;} \ No newline at end of file diff --git a/distfiles/common.php b/distfiles/common.php new file mode 100644 index 0000000..9251c9c --- /dev/null +++ b/distfiles/common.php @@ -0,0 +1,810 @@ + + + + Installation error + + + Could not find the db.php file required for this installation. + + + "; + exit(); +} + +include("db.php"); +include("settings.php"); + +// define some constants +define("STAT_OPEN",1); +define("STAT_CLOSED",2); +define("STAT_DEFERRED",3); +define("ADMIN",2); +define("USER",1); + +// build the basic color array +$basic_colors=array( +"White" => "#FFFFFF", +"Silver" => "#C0C0C0", +"Gray" => "#808080", +"Black" => "#000000", +"Red" => "#FF0000", +"Maroon" => "#800000", +"Orange" => "#FFA500", +"Yellow" => "#FFFF00", +"Olive" => "#808000", +"Lime" => "#00FF00", +"Green" => "#008000", +"Aqua" => "#00FFFF", +"Teal" => "#008080", +"Blue" => "#0000FF", +"Navy" => "#000080", +"Fuchsia" => "#FF00FF", +"Purple" => "#800080"); + +// Generic functions + +function framework($option,$htmltitle,$pagetitle,$print) { + // generates html code for the beginning and end of all pages + if ($option=="begin") { + // generates everything down to the sidemenu/pagetable + // htmltitle is the page title that appears in the window titlebar + // pagetitle is what will appear as the title of the page itself + // print is whether to format for printing or not + if ($print) { + $bgcolor=P_BACKGROUND_COLOR; + $textcolor=P_TEXT_COLOR; + $linkcolor=P_LINK_COLOR; + $vlinkcolor=P_VLINK_COLOR; + $hdgcolor=P_HEADING_COLOR; + } else { + $bgcolor=BACKGROUND_COLOR; + $textcolor=TEXT_COLOR; + $linkcolor=LINK_COLOR; + $vlinkcolor=VLINK_COLOR; + $hdgcolor=HEADING_COLOR; + } + + echo " + + + + $htmltitle + + + + + + + + + + + + + + + + + + + + + + + "; + echo " + + "; + if ($htmltitle || $pagetitle) { + echo " $htmltitle - $pagetitle"; + if ($print) { + echo " +     +     + "; + } + echo "
"; + } + } else if ($option=="end") { + echo " + + + + + + + + "; + } else { + echo " +
Invalid option \"$option\" passed to framework(). Valid options are \"begin\" and \"end\".
+ "; + exit(1); + } +} + +function pagetable($option) { + if ($option=="begin") { + echo " +
+ + + "; + } else if ($option=="end") { + echo " +
+ "; + } else { + echo " +
Invalid option \"$option\" passed to pagetable(). Valid options are \"begin\" and \"end\".
+ "; + exit(1); + } +} + +function pageblock($block,$option) { + if ($block=="left") { + if ($option=="begin") { + echo " + + "; + } else if ($option=="end") { + echo " + + "; + } else { + echo " +
Invalid option \"$option\" passed to pageblock(). Valid options are \"begin\" and \"end\".
+ "; + exit(1); + } + } else if ($block=="right") { + if ($option=="begin") { + echo " + + "; + } else if ($option=="end") { + if (defined("FOOTER_MESSAGE")) { + $footer_msg=FOOTER_MESSAGE; + echo " +

+
$footer_msg

+ "; + } + echo " + + "; + } else { + echo " +
Invalid option \"$option\" passed to pageblock(). Valid options are \"begin\" and \"end\".
+ "; + exit(1); + } + } else { + echo " +
Invalid block \"$block\" passed to pageblock(). Valid blocks are \"left\" and \"right\".
+ "; + exit(1); + } +} + +function sidemenu($printable=true) { + // Displays sidebar menu + global $drs_db,$mbgcolor; + echo " +
+ + +
Create New Writeup
+
"; + echo " +
+ "; + echo " +
+ + + + + +
View Open Writeups
View All Writeups
Writeup Groups
Search Writeups
+
+ "; + + if (isset($_SESSION['userid'])) { + $menuuser=$_SESSION['userid']; + if (mysqli_fetch_row(mysqli_query($drs_db,"select role from users where id=\"$menuuser\""))[0] == ADMIN) { + echo " +
+ + + + +
Admin Functions
Global Settings
Manage Database
+
+ "; + } + } + + if (!isset($_SESSION['userid'])) { + echo " +
+ + + +
My Account
Log In
+
"; + } else { + $loginid=$_SESSION['userid']; + $loggedinas=mysqli_fetch_row(mysqli_query($drs_db,"select firstname,lastname from users where id=\"$loginid\"")); + echo " +
+ + + + +
My Account
Logged in as $loggedinas[0] $loggedinas[1]
Log Out
My Profile
+
"; + } + if ($printable) { + $currentparams=arrayCopy($_REQUEST); + echo " +
+
+
+ +
+
+ "; + } + echo " +
+ + +
About
+
+ "; +} + +function banner($print) { + // displays a banner + global $drs_db; + $bannerid=BANNER; + // get banner parameters from banner table + $banrow=mysqli_fetch_assoc(mysqli_query($drs_db,"select * from banners where bannerid=\"$bannerid\"")); + if (!$banrow) { + $bannerid=1; + } else { + $bannertext=$banrow["bannername"]; + if ($print) { + $bannercolor="#FFFFFF"; + $bannertxtcolor="#000000"; + } else { + $bannercolor=$banrow["bannercolor"]; + $bannertxtcolor=$banrow["textcolor"]; + } + } + if ($bannerid != 1) { + echo " + +
$bannertext
+ "; + } +} + +function dblookup($dbhandle,$table,$in_field,$out_field,$in_data) { + // look up a single field in a database given a value for a single field + $out_data=mysqli_fetch_row(mysqli_query($dbhandle,"select $out_field from $table where $in_field=\"$in_data\"")); + return($out_data[0]); +} + +function arrayCopy( array $array ) { + $result = array(); + foreach( $array as $key => $val ) { + if( is_array( $val ) ) { + $result[$key] = arrayCopy( $val ); + } elseif ( is_object( $val ) ) { + $result[$key] = clone $val; + } else { + $result[$key] = $val; + } + } + return $result; +} + +function validateDate($date, $format = 'Y-m-d') { + $d = DateTime::createFromFormat($format, $date); + return $d && $d->format($format) == $date; +} + +function validateTime($time, $format = 'H:i:s') { + $t = DateTime::createFromFormat($format, $time); + return $t && $t->format($format) == $time; +} + +function validate_password($pass,$passconf) { + // run some checks on supplied passwords + // returns a two element array + // first element is true if passwords match, false if not + // second element is true if passwords meet complexity requirements, false if not + + $results=array(); + + // check for match + if ($pass==$passconf) { + $results['match']=true; + } else { + $results['match']=false; + } + + // check for complexity + // length + if (strlen($pass) < 8) { + $passlength=false; + } else { + $passlength=true; + } + // upper case + if (preg_match('/[A-Z]/',$pass)) { + $hasupper=true; + } else { + $hasupper=false; + } + // lower case + if (preg_match('/[a-z]/',$pass)) { + $haslower=true; + } else { + $haslower=false; + } + // number + if (preg_match('/[0-9]/',$pass)) { + $hasnumber=true; + } else { + $hasnumber=false; + } + // special character + if (preg_match('/[[:punct:]]/',$pass)) { + $hasspecial=true; + } else { + $hasspecial=false; + } + if ($passlength && $hasupper && $haslower && $hasnumber && $hasspecial) { + $results['complex']=true; + } else { + $results['complex']=false; + } + return $results; +} + +function displaywriteup($id) { + global $drs_db; + $writeupdata=mysqli_fetch_assoc(mysqli_query($drs_db,"select * from writeups where id=\"$id\"")); + // determine status indicator + if ($writeupdata['status']==1) $statusind="
OPEN
"; + if ($writeupdata['status']==2) $statusind="
CLSD
"; + if ($writeupdata['status']==3) $statusind="
DFRD
"; + // determine effective icon + if($writeupdata["period_effective"]==1) { + $effind="EFF"; + } else { + $effind="NEF"; + } + // look up text data + $devicename=dblookup($drs_db,"devices","id","name",$writeupdata["device"]); + $subsystemname=dblookup($drs_db,"subsystems","id","name",$writeupdata["subsystem"]); + $subsystemdesc=dblookup($drs_db,"subsystems","id","description",$writeupdata["subsystem"]); + $discrtxt=$writeupdata["discrepancy_text"]; + $rdate=$writeupdata["report_date"]; + $rtime=$writeupdata["report_time"]; + $repby=$writeupdata["reported_by"]; + $period=$writeupdata["period"]; + $periodtimes=dblookup($drs_db,"periods","id","times",$period); + $techfname=dblookup($drs_db,"users","id","firstname",$writeupdata["action_by"]); + $techlname=dblookup($drs_db,"users","id","lastname",$writeupdata["action_by"]); + $reasonname=dblookup($drs_db,"reasons","id","text",$writeupdata["action_reason"]); + $actxt=$writeupdata["action_text"]; + $adate=$writeupdata["action_date"]; + if ($adate=="0000-00-00") $adate=""; + $atime=$writeupdata["action_time"]; + + // determine if this is a member of a group + $member_qry=mysqli_query($drs_db,"select linkgroup from links where writeupid=\"{$writeupdata['id']}\""); + $is_member=mysqli_num_rows($member_qry); + if ($is_member) { + $group_count=$is_member; + $member_group=mysqli_fetch_row($member_qry)[0]; + $member_group_name=dblookup($drs_db,"groups","id","name",$member_group); + if ($member_group_name) { + $imgtitle="Member of group: $member_group_name"; + } else { + $imgtitle="Member of group ID: $member_group"; + } + if ($group_count == 1) { + $group_ind="\"GRP\""; + } else { + $group_ind="\"GRP\""; + } + } + echo " +
+ + + + + + + + +
ID: $id   $statusind   Device: $devicename   Subsystem: $subsystemname   Period: $period   $effind
+ + + + + +
Discrepancy:
$discrtxt
   $group_ind
+ + + + + + +
Report date: $rdate   Report time: $rtime   Reported by: $repby
+
+ + + + + +
Action taken by: $techfname $techlname   Status change reason: $reasonname
+ + + + +
Action taken:
$actxt
+ + + + + +
Action date: $adate   Action time: $atime
+
+ "; +} + +function format_message($msgtype,$msgtxt) { + // displays a formatted message + // $msgtype is 0 for success or anything else for failure + // $msgtxt is the string to display + if ($msgtype == 0) { + $state="ui-state-highlight"; + $icon="ui-icon-info"; + } else { + $state="ui-state-error"; + $icon="ui-icon-alert"; + } + if ($msgtxt == "") $msgtxt="PROGRAM ERROR: No message text was supplied."; + echo " +
+

+ $msgtxt

+


+ "; +} + +function getsetting($setting_name,$user_id) { + // get the value of a setting for a user + global $drs_db; + $confvalue=mysqli_fetch_assoc(mysqli_query($drs_db,"select id,value from config where name=\"$setting_name\""))['value']; + $uservalue=mysqli_fetch_assoc(mysqli_query($drs_db,"select value from profile where name=\"$setting_name\" and user=\"$user_id\""))['value']; + if ($uservalue) { + return $uservalue; + } else { + return $confvalue; + } +} + +function putsetting($setting_name,$setting_value,$user_id) { + // set the value of a setting for a tech + global $drs_db; + $confvalue=mysqli_fetch_assoc(mysqli_query($drs_db,"select value from config where name=\"$setting_name\""))['value']; + $uservalue=mysqli_fetch_assoc(mysqli_query($drs_db,"select value from profile where name=\"$setting_name\" and user=\"$user_id\""))['value']; + if ($setting_value==$confvalue) { + mysqli_query($drs_db,"delete from profile where user=\"$user_id\" and name=\"$setting_name\""); + } else { + if ($uservalue) { + if ($uservalue != $setting_value) mysqli_query($drs_db,"update profile set value=\"$setting_value\" where name=\"$setting_name\" and user=\"$user_id\""); + } else { + mysqli_query($drs_db,"insert into profile(user,name,value) values(\"$user_id\",\"$setting_name\",\"$setting_value\")"); + } + } +} + +function group_table($groupid,$role=false,$mode="view",$selection="none",$expanded=false) { + // display a table for a single group + global $drs_db; + // print group id and name + if ($selection == "all") { + $sel_flag="checked"; + } elseif ($selection == "none") { + $sel_flag=""; + } + if ($expanded) { + echo " + + \"Unexpand + + "; + } + $groupname=dblookup($drs_db,"groups","id","name",$groupid); + if ($groupname == "") { + echo "Group ID:  $groupid"; + } else { + echo "Group Name:  $groupname"; + } + echo "
"; + // print table headers + if ($role && $mode=="select") { + echo " + + + + + + + + + + + + + + "; + } else { + echo " +
SelectIDStatusDatePeriodDeviceSubsystemReported byDiscrepancyMsn Eff
+ + + + + + + + + + + + "; + } + + $writeup_qry=mysqli_query($drs_db,"select writeupid from links where linkgroup=$groupid"); + while ($writeup=mysqli_fetch_row($writeup_qry)) { + // get writeup data + $writeupdata=mysqli_fetch_assoc(mysqli_query($drs_db,"select * from writeups where id={$writeup[0]}")); + + // convert device number to name + $tname=dblookup($drs_db,"devices","id","name",$writeupdata["device"]); + // convert subsystem number to name + $ssname=dblookup($drs_db,"subsystems","id","name",$writeupdata["subsystem"]); + // determine status indicator + if ($writeupdata['status']==1) $statusind="
OPEN
"; + if ($writeupdata['status']==2) $statusind="
CLSD
"; + if ($writeupdata['status']==3) $statusind="
DFRD
"; + // determine times for period + $ptimes=mysqli_fetch_row(mysqli_query($drs_db,"select times from periods where id=\"{$writeupdata['period']}\""))[0]; + // determine effective icon + if($writeupdata["period_effective"]==1) { + $eff_icon="\"Period"; + } else { + $eff_icon="\"Period"; + } + // print table row + if ($role && $mode=="select") { + printf( + " + + + + + + + + + + + \n", + $writeupdata["id"], + $writeupdata["id"],$writeupdata["id"], + $statusind, + $writeupdata["report_date"], + $ptimes, $writeupdata["period"], + $tname, + $ssname, + $writeupdata["reported_by"], + $writeupdata["discrepancy_text"], + $eff_icon + ); + } else { + printf( + " + + + + + + + + + + \n", + $writeupdata["id"],$writeupdata["id"], + $statusind, + $writeupdata["report_date"], + $ptimes, $writeupdata["period"], + $tname, + $ssname, + $writeupdata["reported_by"], + $writeupdata["discrepancy_text"], + $eff_icon + ); + } + } + echo "
IDStatusDatePeriodDeviceSubsystemReported byDiscrepancyMsn Eff
%s%s%s%s%s%s%s%s%s
%s%s%s%s%s%s%s%s%s

"; + +} + + +?> diff --git a/distfiles/config.php b/distfiles/config.php new file mode 100644 index 0000000..c6458ad --- /dev/null +++ b/distfiles/config.php @@ -0,0 +1,480 @@ +"; +} + +if ($update_colors) { + if ($newbgc) mysqli_query($drs_db,"update config set value=\"$newbgc\" where name=\"background_color\""); + if ($newmbgc) mysqli_query($drs_db,"update config set value=\"$newmbgc\" where name=\"menubg_color\""); + if ($newtc) mysqli_query($drs_db,"update config set value=\"$newtc\" where name=\"text_color\""); + if ($newlc) mysqli_query($drs_db,"update config set value=\"$newlc\" where name=\"link_color\""); + if ($newvlc) mysqli_query($drs_db,"update config set value=\"$newvlc\" where name=\"vlink_color\""); + if ($newhc) mysqli_query($drs_db,"update config set value=\"$newhc\" where name=\"heading_color\""); + echo ""; +} +if ($reset_colors) { + mysqli_query($drs_db,"update config set value=defaultvalue where name like \"%_color\""); + echo ""; +} + +if ($add_banner) { + // do error checking + // remove html tags from banner name + $bnrname=strip_tags($bnrname); + // test for name supplied + if (strlen(trim($bnrname))) { + $namesuppld=true; + } else { + $namesuppld=false; + $namesuppld_err=true; + } + // test for name unique + if (mysqli_num_rows(mysqli_query($drs_db,"select bannerid from banners where bannername=\"$bnrname\""))) { + $nameunique=false; + $nameunique_err=true; + } else { + $nameunique=true; + } + if ($namesuppld && $nameunique) { + // sanitize banner name + $clean_bnrname=mysqli_real_escape_string($drs_db,$bnrname); + // modify the table + mysqli_query($drs_db,"insert into banners(bannername,bannercolor,textcolor) values(\"$clean_bnrname\",\"$bnrcolor\",\"$bnrtxtcolor\")"); + } +} +if ($edit_banner) { + if ($delete_banner) { + mysqli_query($drs_db,"delete from banners where bannerid=\"$bnrid\""); + unset($edit_banner); + } + if ($update_banner) { + // do error checking + // remove html tags from banner name + $bnrname=strip_tags($bnrname); + // test for name supplied + if (strlen(trim($bnrname))) { + $namesuppld=true; + } else { + $namesuppld=false; + $namesuppld_err=true; + } + // test for name unique + if (mysqli_num_rows(mysqli_query($db,"select bannerid from banners where bannername=\"$bnrname\" and bannerid!=\"$bnrid\""))) { + if (mysqli_num_rows(mysqli_query($drs_db,"select bannerid from banners where bannername=\"$bnrname\""))) { + $nameunique=false; + $nameunique_err=true; + } else { + $nameunique=true; + } + } else { + $nameunique=true; + } + if ($namesuppld && $nameunique) { + // sanitize banner name + $clean_bnrname=mysqli_real_escape_string($drs_db,$bnrname); + // modify the table + mysqli_query($drs_db,"update banners set bannername=\"$bnrname\",bannercolor=\"$bnrcolor\",textcolor=\"$bnrtxtcolor\" where bannerid=\"$bnrid\""); + } + } +} + +if ($update_misc) { + if ($logoutall) { + if ($ostype == 'WIN') { + $sessfileloc=mysqli_fetch_row(mysqli_query($drs_db,"select value from config where name=\"win_server_session_dir\""))[0]; + } else { + $sessfileloc=mysqli_fetch_row(mysqli_query($drs_db,"select value from config where name=\"unix_server_session_dir\""))[0]; + } + foreach (glob($sessfileloc."/*") as $sessfile) { + unlink($sessfile); + } + } + // test for sessttl supplied + if (strlen(trim($sessttl))) { + $ttlsuppld=true; + } else { + $ttlsuppld=false; + $ttlsuppld_err=true; + } + // test for sessdir supplied + if (strlen(trim($sessdir))) { + $dirsuppld=true; + } else { + $dirsuppld=false; + $dirsuppld_err=true; + } + if ($ttlsuppld && $dirsuppld) { + mysqli_query($drs_db,"update config set value=\"$sessttl\" where name=\"session_ttl_days\""); + if ($ostype == 'WIN') { + mysqli_query($drs_db,"update config set value=\"$sessdir\" where name=\"win_server_session_dir\""); + } else { + mysqli_query($drs_db,"update config set value=\"$sessdir\" where name=\"unix_server_session_dir\""); + } + } +} + +// ******** end database manipulation ******** + +pagetable("begin"); +if (!$print) { + pageblock("left","begin"); + sidemenu(); + pageblock("left","end"); +} +pageblock("right","begin"); +banner($print); +echo "
"; +echo " +
+ + +
+
+"; +$curapn=mysqli_fetch_row(mysqli_query($drs_db,"select value from config where name=\"app_name\""))[0]; +$curbnr=mysqli_fetch_row(mysqli_query($drs_db,"select value from config where name=\"banner\""))[0]; +$curftr=mysqli_fetch_row(mysqli_query($drs_db,"select value from config where name=\"footer_message\""))[0]; +$curpereffhlp=mysqli_fetch_row(mysqli_query($drs_db,"select value from config where name=\"period_eff_help_txt\""))[0]; +echo " +Display Configuration Parameters



+"; +echo " +Application Name (will appear at the top of every page):
+ +


+"; +echo " +Banner (will appear at the top and bottom of pages):  


+Footer Message (will appear at the bottom of pages):
+


+Period Effective Help Text :
+



+     +


+
+"; + +echo " +
+ +
+"; +$curbgc=mysqli_fetch_row(mysqli_query($drs_db,"select value from config where name=\"background_color\""))[0]; +$curmbgc=mysqli_fetch_row(mysqli_query($drs_db,"select value from config where name=\"menubg_color\""))[0]; +$curtc=mysqli_fetch_row(mysqli_query($drs_db,"select value from config where name=\"text_color\""))[0]; +$curlc=mysqli_fetch_row(mysqli_query($drs_db,"select value from config where name=\"link_color\""))[0]; +$curvlc=mysqli_fetch_row(mysqli_query($drs_db,"select value from config where name=\"vlink_color\""))[0]; +$curhc=mysqli_fetch_row(mysqli_query($drs_db,"select value from config where name=\"heading_color\""))[0]; + +echo " +Color Configuration Parameters

+ + + +
+
+Select the color you'd like to change then use the color picker to select a new color

+ + + + + + + +
Page background color# ____
Menu background color# ____
Normal text color# ____
Link color# ____
Visited link color# ____
Heading color# ____
+

+    + +
+
+

 

+
+ \"\" +
+
+ +
+
+
+ +
+ + +
+ R + H +
+ G + S +
+ B + V +
+
+ # +
+
+ +
+ +
 
+
+
+"; + +echo " + +
+ +
+"; +if ($nameunique_err) format_message(1,"Banner name already exists. Please try another."); +if ($namesuppld_err) format_message(1,"Banner name cannot be blank."); +if ($edit_banner) { + // set form options for editing + $sectiontitle="Modify Banner

"; + $formtag="
"; + $buttontags=" + +      +      +
+ "; + // pull current values from database + $currentvalues=mysqli_fetch_assoc(mysqli_query($drs_db,"select * from banners where bannerid=$bnrid")); + $bnrname=$currentvalues["bannername"]; + $bnrcolor=$currentvalues["bannercolor"]; + $bnrtextcolor=$currentvalues["textcolor"]; + $bnrcoloropts=""; + foreach($basic_colors as $cname => $chex) { + if ($chex==$bnrcolor) { + $bnrcoloropts=$bnrcoloropts . ""; + } else { + $bnrcoloropts=$bnrcoloropts . ""; + } + } + $bnrtextcoloropts=""; + foreach($basic_colors as $cname => $chex) { + if ($chex==$bnrtextcolor) { + $bnrtextcoloropts=$bnrtextcoloropts . ""; + } else { + $bnrtextcoloropts=$bnrtextcoloropts . ""; + } + } +} else { + // set form options for add + $sectiontitle="Add New Banner

"; + $formtag=""; + $bnrname=""; + $buttontags=" + + "; + // set default values + $bnrcolor="#000000"; + $bnrtextcolor="#FFFFFF"; + $bnrcoloropts=""; + foreach($basic_colors as $cname => $chex) { + if ($chex==$bnrcolor) { + $bnrcoloropts=$bnrcoloropts . ""; + } else { + $bnrcoloropts=$bnrcoloropts . ""; + } + } + $bnrtextcoloropts=""; + foreach($basic_colors as $cname => $chex) { + if ($chex==$bnrtextcolor) { + $bnrtextcoloropts=$bnrtextcoloropts . ""; + } else { + $bnrtextcoloropts=$bnrtextcoloropts . ""; + } + } +} +echo " +$sectiontitle +$formtag + + +
+Banner Name (text displayed in the banner):  

+Banner Color (background color of the banner):   +

+Banner Text Color (text color of the banner, different than banner color):   +

+
+$buttontags +
+

+"; +// show the table of entities +$existingdata=mysqli_query($drs_db,"select * from banners where bannerid!=1 order by bannerid asc"); +echo " +Existing Banners   Click on the Banner ID to edit.

+ + +"; +while ($tablerow=mysqli_fetch_assoc($existingdata)) { + printf(" + + + + ", + $tablerow["bannerid"],$tablerow["bannerid"], + $tablerow["bannercolor"],$tablerow["textcolor"],$tablerow["bannername"]); +} +echo "
Banner IDBanner
%s%s
"; + +echo " +
+ +
+
+"; +if ($ttlsuppld_err) format_message(1,"Session expiration time cannot be blank."); +if ($dirsuppld_err) format_message(1,"Server session file directory cannot be blank."); +$curttl=mysqli_fetch_row(mysqli_query($drs_db,"select value from config where name=\"session_ttl_days\""))[0]; +if ($ostype == 'WIN') { + $curssdir=mysqli_fetch_row(mysqli_query($drs_db,"select value from config where name=\"win_server_session_dir\""))[0]; +} else { + $curssdir=mysqli_fetch_row(mysqli_query($drs_db,"select value from config where name=\"unix_server_session_dir\""))[0]; +} +echo " +Miscellaneous Configuration Parameters



+Session expiration time (users will have to log in again after this long) :   days

+Server session file directory (must be writable by the web server process) :


+Force logout of all users  



+     +
+ +
+ +
+"; + +echo "
"; +banner($print); +pageblock("right","end"); +pagetable("end"); + +framework("end","","",$print); + +?> diff --git a/distfiles/create.php b/distfiles/create.php new file mode 100644 index 0000000..8bbe61a --- /dev/null +++ b/distfiles/create.php @@ -0,0 +1,238 @@ +"; +// display the form +if ($create) { + if ($discrepancy_text && $reported_by) { + format_message(0,"Writeup created. You may create another writeup or return to Open Writeups page."); + $preserve=FALSE; + } + if (!$device) { + format_message(1,"You didn't select a device. Please try again."); + $preserve=TRUE; + } + if (!$subsystem) { + format_message(1,"You didn't select a subsystem. Please try again."); + $preserve=TRUE; + } + if (!$period) { + format_message(1,"You didn't select a period. Please try again."); + $preserve=TRUE; + } + if (!$discrepancy_text) { + format_message(1,"You didn't enter any discrepancy text. Please try again."); + $preserve=TRUE; + } + if (!$reported_by) { + format_message(1,"You didn't enter your name in the Reported by: block. Please try again."); + $preserve=TRUE; + } +} else { + $preserve=FALSE; +} + +echo " + +"; + +// start writeup entry section +if ($status==1) $statusind="OPEN"; +if ($status==2) $statusind="CLSD"; +if ($status==3) $statusind="DFRD"; + +echo " + + + + + + + + + + + + + + + + + + + + +
Device
+
Subsystem
+
Period
+
Report Date
+
Report Time
+
+Discrepancy (required)
+
+
Reported by (required)
+
Period effective?  
+ Yes      + No +
+
+ + +
+"; + +banner($print); +pageblock("right","end"); +pagetable("end"); + +framework("end","","",$print); + +?> diff --git a/distfiles/dbadmin.php b/distfiles/dbadmin.php new file mode 100644 index 0000000..0886c78 --- /dev/null +++ b/distfiles/dbadmin.php @@ -0,0 +1,1096 @@ +"; +echo " +
+ + +
+"; +if ($usernameunique_err) format_message(1,"User name already exists."); +if ($fnamesuppld_err) format_message(1,"First name cannot be blank."); +if ($lnamesuppld_err) format_message(1,"Last name cannot be blank."); +if ($loginsuppld_err) format_message(1,"Login cannot be blank."); +if ($loginunique_err) format_message(1,"Login already exists."); +if ($passcomplex_err) format_message(1,"Password doesn't meet complexity requirements."); +if ($passmatch_err) format_message(1,"The supplied passwords don't match."); +if ($userhaswriteups_err) format_message(1,"User has actions in the database and cannot be deleted."); + +if ($edit_user && !$delete_user) { + // set form options for editing + $sectiontitle="Modify User        Click here to add a new user

"; + $formtag="
"; + $buttontags=" + +      +      +
+ "; + // pull current values from database + $currentvalues=mysqli_fetch_assoc(mysqli_query($drs_db,"select * from users where id=$user_id")); + $user_fname=$currentvalues["firstname"]; + $user_lname=$currentvalues["lastname"]; + $user_login=$currentvalues["login"]; + $user_pass=$user_passconf="password_is_unchanged"; + if ($currentvalues["role"]==ADMIN) { + $roletag=""; + } else { + $roletag=""; + } + if ($currentvalues["active"]==1) { + $activetag=""; + } else { + $activetag=""; + } +} else { + // set form options for add + if (!$user_add_fail) unset($user_id,$user_fname,$user_lname,$user_login,$user_pass,$user_passconf,$user_role,$user_active); + $sectiontitle="Add New User

"; + $formtag=""; + $buttontags=" + + "; + // set default values + $roletag=""; + $activetag=""; +} +$password_help="Password must be at least 8 characters in length and contain at least one upper case letter, one lower case letter, one number, and one special character."; +echo " +$sectiontitle +$formtag + + +
+First Name:  

+Last Name:  

+Login:  

+Password:  

+Repeat Password:  

+Need a random password?   +"; +$rpwok=false; +while (!$rpwok) { + $rpw=substr(str_shuffle(password_hash(microtime(),PASSWORD_DEFAULT)), 10, 12); + $rpwtst=validate_password($rpw,$rpw); + if ($rpwtst['complex']) $rpwok=true; +} +echo "  $rpw

+

+Admin:  $roletag     +Active:   $activetag

+
+$buttontags +
+

+"; + +// show the table of entities +$existingdata=mysqli_query($drs_db,"select id,firstname,lastname,login,role,active from users order by id asc"); +echo " +Existing Users   Click on the User ID to edit.

+ + +"; +while ($tablerow=mysqli_fetch_assoc($existingdata)) { + if ($tablerow["role"]==ADMIN) { + $roledef="Admin"; + } elseif ($tablerow["role"]==USER) { + $roledef="User"; + } + if ($tablerow["active"]) { + $activedef="\"active\""; + } else { + $activedef="\"inactive\""; + } + printf(" + + + + + + + + ", + $tablerow["id"],$tablerow["id"], + $tablerow["firstname"], + $tablerow["lastname"], + $tablerow["login"], + $roledef, + $activedef); +} +echo "
User IDFirst NameLast NameLoginRoleActive
%s%s%s%s%s%s
"; + +echo " +
+ +
+"; +if ($devicenameunique_err) format_message(1,"Device name already exists."); +if ($devicenamesuppld_err) format_message(1,"Device name cannot be blank."); +if ($devicehaswriteups_err) format_message(1,"Device has writeups in the database and cannot be deleted."); + +if ($edit_device && !$delete_device) { + // set form options for editing + $sectiontitle="Modify Device        Click here to add a new device

"; + $formtag="
"; + $buttontags=" + +      +      +
+ "; + // pull current values from database + $currentvalues=mysqli_fetch_assoc(mysqli_query($drs_db,"select * from devices where id=$device_id")); + $device_name=$currentvalues["name"]; + if ($currentvalues["active"]==1) { + $activetag=""; + } else { + $activetag=""; + } +} else { + // set form options for add + if (!$device_add_fail) unset($device_id,$device_name,$device_active); + $sectiontitle="Add New Device

"; + $formtag=""; + $buttontags=" + + "; + // set default values + $activetag=""; +} +echo " +$sectiontitle +$formtag + + +
+Device Name:  

+Active:   $activetag

+
+$buttontags +
+

+"; + +// show the table of entities +$existingdata=mysqli_query($drs_db,"select * from devices order by id asc"); +echo " +Existing Devices   Click on the Device ID to edit.

+ + +"; +while ($tablerow=mysqli_fetch_assoc($existingdata)) { + if ($tablerow["active"]) { + $activedef="\"active\""; + } else { + $activedef="\"inactive\""; + } + printf(" + + + + + ", + $tablerow["id"],$tablerow["id"], + $tablerow["name"], + $activedef); +} +echo "
Device IDDevice NameActive
%s%s%s
"; +echo " +
+ +
+"; +if ($ssnameunique_err) format_message(1,"Subsystem name already exists."); +if ($ssnamesuppld_err) format_message(1,"Subsystem name cannot be blank."); +if ($sshaswriteups_err) format_message(1,"Subsystem has writeups in the database and cannot be deleted."); + +if ($edit_subsystem && !$delete_subsystem) { + // set form options for editing + $sectiontitle="Modify Subsystem        Click here to add a new subsystem

"; + $formtag="
"; + $buttontags=" + +      +      +
+ "; + // pull current values from database + $currentvalues=mysqli_fetch_assoc(mysqli_query($drs_db,"select * from subsystems where id=$subsystem_id")); + $subsystem_name=$currentvalues["name"]; + $subsystem_desc=$currentvalues["description"]; + if ($currentvalues["active"]==1) { + $activetag=""; + } else { + $activetag=""; + } +} else { + // set form options for add + if (!$subsystem_add_fail) unset($subsystem_id,$subsystem_name,$subsystem_desc,$subsystem_active); + $sectiontitle="Add New Subsystem

"; + $formtag=""; + $buttontags=" + + "; + // set default values + $activetag=""; +} +echo " +$sectiontitle +$formtag + + +
+Subsystem Name:  

+Description:


+Active:   $activetag

+
+$buttontags +
+

+"; + +// show the table of entities +$existingdata=mysqli_query($drs_db,"select * from subsystems order by id asc"); +echo " +Existing Subsystems   Click on the Subsystem ID to edit.

+ + +"; +while ($tablerow=mysqli_fetch_assoc($existingdata)) { + if ($tablerow["active"]) { + $activedef="\"active\""; + } else { + $activedef="\"inactive\""; + } + printf(" + + + + + + ", + $tablerow["id"],$tablerow["id"], + $tablerow["name"], + $tablerow["description"], + $activedef); +} +echo "
Subsystem IDSubsystem NameDescriptionActive
%s%s%s%s
"; + +echo " +
+ +
+"; +if ($reastextunique_err) format_message(1,"Reason already exists."); +if ($reastextsuppld_err) format_message(1,"Reason cannot be blank."); +if ($reashaswriteups_err) format_message(1,"Reason has writeups in the database and cannot be deleted."); + +if ($edit_reason && !$delete_reason) { + // set form options for editing + $sectiontitle="Modify Reason        Click here to add a new reason

"; + $formtag="
"; + $buttontags=" + +      +      +
+ "; + // pull current values from database + $currentvalues=mysqli_fetch_assoc(mysqli_query($drs_db,"select * from reasons where id=$reason_id")); + $reason_text=$currentvalues["text"]; + if ($currentvalues["active"]==1) { + $activetag=""; + } else { + $activetag=""; + } +} else { + // set form options for add + if (!$reason_add_fail) unset($reason_id,$reason_text,$reason_active); + $sectiontitle="Add New Reason

"; + $formtag=""; + $buttontags=" + + "; + // set default values + $activetag=""; +} +echo " +$sectiontitle +$formtag + + +
+Reason Text:  

+Active:   $activetag

+
+$buttontags +
+

+"; + +// show the table of entities +$existingdata=mysqli_query($drs_db,"select * from reasons order by id asc"); +echo " +Existing Reasons   Click on the Reason ID to edit.

+ + +"; +while ($tablerow=mysqli_fetch_assoc($existingdata)) { + if ($tablerow["active"]) { + $activedef="\"active\""; + } else { + $activedef="\"inactive\""; + } + printf(" + + + + + ", + $tablerow["id"],$tablerow["id"], + $tablerow["text"], + $activedef); +} +echo "
Reason IDReason TextActive
%s%s%s
"; + +echo " +
+ +
+"; +if ($peridisint_err) format_message(1,"Period number must be an integer."); +if ($peridsuppld_err) format_message(1,"Period number cannot be blank."); +if ($peridunique_err) format_message(1,"Period number already exists."); +if ($pertimesunique_err) format_message(1,"Period already exists."); +if ($pertimessuppld_err) format_message(1,"Period times cannot be blank."); +if ($perhaswriteups_err) format_message(1,"Period has writeups in the database and cannot be deleted."); + +if ($edit_period && !$delete_period) { + // set form options for editing + $sectiontitle="Modify Period        Click here to add a new period

"; + $formtag="
"; + $buttontags=" + +      +      +
+ "; + // pull current values from database + $currentvalues=mysqli_fetch_assoc(mysqli_query($drs_db,"select * from periods where id=$period_id")); + $period_times=$currentvalues["times"]; + if ($currentvalues["active"]==1) { + $activetag=""; + } else { + $activetag=""; + } + $idtag="$period_id"; +} else { + // set form options for add + if (!$period_add_fail) unset($period_id,$period_times,$period_active); + $sectiontitle="Add New Period

"; + $formtag=""; + $buttontags=" + + "; + $idtag=""; + // set default values + $activetag=""; +} +echo " +$sectiontitle +$formtag + + +
+Period Number:  $idtag

+Period Times:  

+Active:   $activetag

+
+$buttontags +
+

+"; + +// show the table of entities +$existingdata=mysqli_query($drs_db,"select * from periods order by id asc"); +echo " +Existing Periods   Click on the Period ID to edit.

+ + +"; +while ($tablerow=mysqli_fetch_assoc($existingdata)) { + if ($tablerow["active"]) { + $activedef="\"active\""; + } else { + $activedef="\"inactive\""; + } + printf(" + + + + + ", + $tablerow["id"],$tablerow["id"], + $tablerow["times"], + $activedef); +} +echo "
Period IDPeriod TimesActive
%s%s%s
"; +echo " +
+ +
+"; + +echo "
"; +banner($print); +pageblock("right","end"); +pagetable("end"); + +framework("end","","",$print); + +?> diff --git a/distfiles/gpl.php b/distfiles/gpl.php new file mode 100644 index 0000000..6e855f3 --- /dev/null +++ b/distfiles/gpl.php @@ -0,0 +1,431 @@ +"; + + +echo " +
+
+            GNU GENERAL PUBLIC LICENSE
+               Version 2, June 1991
+
+ Copyright (C) 1989, 1991 Free Software Foundation, Inc.
+                       59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+                Preamble
+
+  The licenses for most software are designed to take away your
+freedom to share and change it.  By contrast, the GNU General Public
+License is intended to guarantee your freedom to share and change free
+software--to make sure the software is free for all its users.  This
+General Public License applies to most of the Free Software
+Foundation's software and to any other program whose authors commit to
+using it.  (Some other Free Software Foundation software is covered by
+the GNU Library General Public License instead.)  You can apply it to
+your programs, too.
+
+  When we speak of free software, we are referring to freedom, not
+price.  Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+this service if you wish), that you receive source code or can get it
+if you want it, that you can change the software or use pieces of it
+in new free programs; and that you know you can do these things.
+
+  To protect your rights, we need to make restrictions that forbid
+anyone to deny you these rights or to ask you to surrender the rights.
+These restrictions translate to certain responsibilities for you if you
+distribute copies of the software, or if you modify it.
+
+  For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must give the recipients all the rights that
+you have.  You must make sure that they, too, receive or can get the
+source code.  And you must show them these terms so they know their
+rights.
+
+  We protect your rights with two steps: (1) copyright the software, and
+(2) offer you this license which gives you legal permission to copy,
+distribute and/or modify the software.
+
+  Also, for each author's protection and ours, we want to make certain
+that everyone understands that there is no warranty for this free
+software.  If the software is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original, so
+that any problems introduced by others will not reflect on the original
+authors' reputations.
+
+  Finally, any free program is threatened constantly by software
+patents.  We wish to avoid the danger that redistributors of a free
+program will individually obtain patent licenses, in effect making the
+program proprietary.  To prevent this, we have made it clear that any
+patent must be licensed for everyone's free use or not licensed at all.
+
+  The precise terms and conditions for copying, distribution and
+modification follow.
+
+            GNU GENERAL PUBLIC LICENSE
+   TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+  0. This License applies to any program or other work which contains
+a notice placed by the copyright holder saying it may be distributed
+under the terms of this General Public License.  The \"Program\", below,
+refers to any such program or work, and a \"work based on the Program\"
+means either the Program or any derivative work under copyright law:
+that is to say, a work containing the Program or a portion of it,
+either verbatim or with modifications and/or translated into another
+language.  (Hereinafter, translation is included without limitation in
+the term \"modification\".)  Each licensee is addressed as \"you\".
+
+Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope.  The act of
+running the Program is not restricted, and the output from the Program
+is covered only if its contents constitute a work based on the
+Program (independent of having been made by running the Program).
+Whether that is true depends on what the Program does.
+
+  1. You may copy and distribute verbatim copies of the Program's
+source code as you receive it, in any medium, provided that you
+conspicuously and appropriately publish on each copy an appropriate
+copyright notice and disclaimer of warranty; keep intact all the
+notices that refer to this License and to the absence of any warranty;
+and give any other recipients of the Program a copy of this License
+along with the Program.
+
+You may charge a fee for the physical act of transferring a copy, and
+you may at your option offer warranty protection in exchange for a fee.
+
+  2. You may modify your copy or copies of the Program or any portion
+of it, thus forming a work based on the Program, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+    a) You must cause the modified files to carry prominent notices
+    stating that you changed the files and the date of any change.
+
+    b) You must cause any work that you distribute or publish, that in
+    whole or in part contains or is derived from the Program or any
+    part thereof, to be licensed as a whole at no charge to all third
+    parties under the terms of this License.
+
+    c) If the modified program normally reads commands interactively
+    when run, you must cause it, when started running for such
+    interactive use in the most ordinary way, to print or display an
+    announcement including an appropriate copyright notice and a
+    notice that there is no warranty (or else, saying that you provide
+    a warranty) and that users may redistribute the program under
+    these conditions, and telling the user how to view a copy of this
+    License.  (Exception: if the Program itself is interactive but
+    does not normally print such an announcement, your work based on
+    the Program is not required to print an announcement.)
+
+These requirements apply to the modified work as a whole.  If
+identifiable sections of that work are not derived from the Program,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works.  But when you
+distribute the same sections as part of a whole which is a work based
+on the Program, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Program.
+
+In addition, mere aggregation of another work not based on the Program
+with the Program (or with a work based on the Program) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+  3. You may copy and distribute the Program (or a work based on it,
+under Section 2) in object code or executable form under the terms of
+Sections 1 and 2 above provided that you also do one of the following:
+
+    a) Accompany it with the complete corresponding machine-readable
+    source code, which must be distributed under the terms of Sections
+    1 and 2 above on a medium customarily used for software interchange; or,
+
+    b) Accompany it with a written offer, valid for at least three
+    years, to give any third party, for a charge no more than your
+    cost of physically performing source distribution, a complete
+    machine-readable copy of the corresponding source code, to be
+    distributed under the terms of Sections 1 and 2 above on a medium
+    customarily used for software interchange; or,
+
+    c) Accompany it with the information you received as to the offer
+    to distribute corresponding source code.  (This alternative is
+    allowed only for noncommercial distribution and only if you
+    received the program in object code or executable form with such
+    an offer, in accord with Subsection b above.)
+
+The source code for a work means the preferred form of the work for
+making modifications to it.  For an executable work, complete source
+code means all the source code for all modules it contains, plus any
+associated interface definition files, plus the scripts used to
+control compilation and installation of the executable.  However, as a
+special exception, the source code distributed need not include
+anything that is normally distributed (in either source or binary
+form) with the major components (compiler, kernel, and so on) of the
+operating system on which the executable runs, unless that component
+itself accompanies the executable.
+
+If distribution of executable or object code is made by offering
+access to copy from a designated place, then offering equivalent
+access to copy the source code from the same place counts as
+distribution of the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+  4. You may not copy, modify, sublicense, or distribute the Program
+except as expressly provided under this License.  Any attempt
+otherwise to copy, modify, sublicense or distribute the Program is
+void, and will automatically terminate your rights under this License.
+However, parties who have received copies, or rights, from you under
+this License will not have their licenses terminated so long as such
+parties remain in full compliance.
+
+  5. You are not required to accept this License, since you have not
+signed it.  However, nothing else grants you permission to modify or
+distribute the Program or its derivative works.  These actions are
+prohibited by law if you do not accept this License.  Therefore, by
+modifying or distributing the Program (or any work based on the
+Program), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Program or works based on it.
+
+  6. Each time you redistribute the Program (or any work based on the
+Program), the recipient automatically receives a license from the
+original licensor to copy, distribute or modify the Program subject to
+these terms and conditions.  You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties to
+this License.
+
+  7. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License.  If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Program at all.  For example, if a patent
+license would not permit royalty-free redistribution of the Program by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Program.
+
+If any portion of this section is held invalid or unenforceable under
+any particular circumstance, the balance of the section is intended to
+apply and the section as a whole is intended to apply in other
+circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system, which is
+implemented by public license practices.  Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+  8. If the distribution and/or use of the Program is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Program under this License
+may add an explicit geographical distribution limitation excluding
+those countries, so that distribution is permitted only in or among
+countries not thus excluded.  In such case, this License incorporates
+the limitation as if written in the body of this License.
+
+  9. The Free Software Foundation may publish revised and/or new versions
+of the General Public License from time to time.  Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+Each version is given a distinguishing version number.  If the Program
+specifies a version number of this License which applies to it and \"any
+later version\", you have the option of following the terms and conditions
+either of that version or of any later version published by the Free
+Software Foundation.  If the Program does not specify a version number of
+this License, you may choose any version ever published by the Free Software
+Foundation.
+
+  10. If you wish to incorporate parts of the Program into other free
+programs whose distribution conditions are different, write to the author
+to ask for permission.  For software which is copyrighted by the Free
+Software Foundation, write to the Free Software Foundation; we sometimes
+make exceptions for this.  Our decision will be guided by the two goals
+of preserving the free status of all derivatives of our free software and
+of promoting the sharing and reuse of software generally.
+
+                NO WARRANTY
+
+  11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
+FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW.  EXCEPT WHEN
+OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
+PROVIDE THE PROGRAM \"AS IS\" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
+OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.  THE ENTIRE RISK AS
+TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU.  SHOULD THE
+PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
+REPAIR OR CORRECTION.
+
+  12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
+REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
+INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
+OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
+TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
+YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
+PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGES.
+
+             END OF TERMS AND CONDITIONS
+
+        How to Apply These Terms to Your New Programs
+
+  If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+  To do so, attach the following notices to the program.  It is safest
+to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least
+the \"copyright\" line and a pointer to where the full notice is found.
+
+    
+    Copyright (C)   
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program; if not, write to the Free Software
+    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+
+
+Also add information on how to contact you by electronic and paper mail.
+
+If the program is interactive, make it output a short notice like this
+when it starts in an interactive mode:
+
+    Gnomovision version 69, Copyright (C) year name of author
+    Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+    This is free software, and you are welcome to redistribute it
+    under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License.  Of course, the commands you use may
+be called something other than `show w' and `show c'; they could even be
+mouse-clicks or menu items--whatever suits your program.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a \"copyright disclaimer\" for the program, if
+necessary.  Here is a sample; alter the names:
+
+  Yoyodyne, Inc., hereby disclaims all copyright interest in the program
+  `Gnomovision' (which makes passes at compilers) written by James Hacker.
+
+  , 1 April 1989
+  Ty Coon, President of Vice
+
+This General Public License does not permit incorporating your program into
+proprietary programs.  If your program is a subroutine library, you may
+consider it more useful to permit linking proprietary applications with the
+library.  If this is what you want to do, use the GNU Library General
+Public License instead of this License.
+
+
+
+
+"; + + +echo "
"; +banner($print); +pageblock("right","end"); +pagetable("end"); + +framework("end","","",$print); + +?> diff --git a/distfiles/groups.php b/distfiles/groups.php new file mode 100644 index 0000000..bc728cf --- /dev/null +++ b/distfiles/groups.php @@ -0,0 +1,896 @@ +NEF"; + if ($eff_val=="1") $eff_dsp="EFF"; + $eff_param="{$eff_param} {$eff_dsp}, "; + } + $eff_param=rtrim($eff_param,", "); + $query_string=rtrim($query_string," or "); + $query_string="{$query_string})"; + $query_string="{$query_string} and "; + } + // status + if ($s_status) { + $query_string="{$query_string}("; + $status_param="Status:"; + foreach ($s_status as $stat_val) { + $query_string="{$query_string}status=\"{$stat_val}\" or "; + if ($stat_val=="1") $stat_dsp="OPEN"; + if ($stat_val=="2") $stat_dsp="CLSD"; + if ($stat_val=="3") $stat_dsp="DFRD"; + $status_param="{$status_param} {$stat_dsp}, "; + } + $status_param=rtrim($status_param,", "); + $query_string=rtrim($query_string," or "); + $query_string="{$query_string})"; + $query_string="{$query_string} and "; + } + // report date + $query_string="{$query_string}("; + $query_string="{$query_string}report_date between \"{$s_report_date_start}\" and \"{$s_report_date_end}\""; + $query_string="{$query_string})"; + $query_string="{$query_string} and "; + $reportdate_param="Report date between {$s_report_date_start} and {$s_report_date_end}"; + // report time + $query_string="{$query_string}("; + $query_string="{$query_string}report_time between \"{$s_report_time_start}\" and \"{$s_report_time_end}\""; + $query_string="{$query_string})"; + $query_string="{$query_string} and "; + $reporttime_param="Report time between {$s_report_time_start} and {$s_report_time_end}"; + // discrepancy text + if ($s_discrepancy_text) { + $query_string="{$query_string}("; + $query_string="{$query_string}discrepancy_text like \"%{$s_discrepancy_text}%\""; + $query_string="{$query_string})"; + $query_string="{$query_string} and "; + $discrepancytext_param="Text in discrepancy: {$s_discrepancy_text}"; + } else { + $discrepancytext_param="Text in discrepancy: any"; + } + // reported by + if ($s_reported_by) { + $query_string="{$query_string}("; + $query_string="{$query_string}reported_by like \"%{$s_reported_by}%\""; + $query_string="{$query_string})"; + $query_string="{$query_string} and "; + $repby_param="Text in Reported by: {$s_reported_by}"; + } else { + $repby_param="Text in Reported by: any"; + } + + $query_string=rtrim($query_string," and "); + $query_string="select * from writeups where {$query_string} order by report_date asc"; +} + +// ******** end database manipulation ******** + +pagetable("begin"); +if (!$print) { + pageblock("left","begin"); + sidemenu(); + pageblock("left","end"); +} +pageblock("right","begin"); +banner($print); +echo "
"; + +if ($action=="add") { + if (count($targets) > 1) { + $plural="s"; + } else { + $plural=""; + } + if ($save && !$fail) { + format_message(0,"Writeup$plural successfully added. Return to Writeup Groups page."); + } elseif ($save && $fail) { + format_message(2,"An error was encountered when adding writeup$plural. The error was \" $fail \""); + } + if ($save) { + // show the group + $mode="view"; + group_table($group,$role,$mode); + if ($role) { + // show buttons + echo " +
+ + + +       + +       + +       + +       + +
+ "; + } + } else { + if ($role){ + // show mini search with discrepancy data only + echo " +
+ + + Search Parameters     Enter search terms to find writeups to add to the group.
+ + + + + + "; + echo " + + + + + "; + echo " + + + + + + + + + + "; + // **********************************" + echo " + +
+ "; + // Status cell ********************** + echo " + Status
+ "; + if ($search) { + $openstate=$clsdstate=$dfrdstate=""; + if (in_array("1",$s_status,true)) $openstate="selected"; + if (in_array("2",$s_status,true)) $clsdstate="selected"; + if (in_array("3",$s_status,true)) $dfrdstate="selected"; + } else { + $openstate=$clsdstate=$dfrdstate="selected"; + } + echo " + + "; + // ********************************** + echo " +
+ "; + // Device cell ********************* + echo " + Device
+ + "; + // ********************************** + echo " +
+ "; + // Subsystem cell ******************* + echo " + Subsystem
+ + "; + // ********************************** + echo " +
+ "; + // Period cell ********************** + echo " + Period
+ + "; + // ********************************** + echo " +
+ "; + // Report Date cell ***************** + echo " + Report Date Between
+ +  and  + + "; + // ********************************** + echo " +
+ "; + // Report Time cell ***************** + echo " + Report Time Between
+ +  and  + + "; + // ********************************** + echo " +
+ "; + // Discrepancy text cell ************ + echo " + Words in Discrepancy
+
+ "; + // ********************************** + echo " +
+ "; + // Reported by cell ***************** + echo " + Reported by
+ + "; + // ********************************** + echo " +
+ "; + // Period effective cell ************ + echo " + Sim period effective?  
+ "; + if ($search) { + $effstate=$nefstate=""; + if (in_array("1",$s_period_effective,true)) $effstate="selected"; + if (in_array("0",$s_period_effective,true)) $nefstate="selected"; + } else { + $effstate=$nefstate="selected"; + } + echo " + +
+ +
+
"; + // display the results + if ($search) { + $writeup_qry=mysqli_query($drs_db,$query_string); + if (mysqli_num_rows($writeup_qry)) { + echo " +
+ + + + + + + + + + + + + + "; + while ($tablerow = mysqli_fetch_assoc($writeup_qry)) { + // determine effective icon + if($tablerow["period_effective"]==1) { + $eff_icon="\"Period"; + } else { + $eff_icon="\"Period"; + } + + // determine status indicator + if ($tablerow['status']==1) $statusind="
OPEN
"; + if ($tablerow['status']==2) $statusind="
CLSD
"; + if ($tablerow['status']==3) $statusind="
DFRD
"; + + // convert device number to name + $dname=dblookup($drs_db,"devices","id","name",$tablerow["device"]); + $disc_txt="{$tablerow["discrepancy_text"]}"; + // determine if this is a member of a group + $member_qry=mysqli_query($drs_db,"select linkgroup from links where writeupid=\"{$tablerow['id']}\""); + $is_member=mysqli_num_rows($member_qry); + if ($is_member) { + $group_count=$is_member; + $member_group=mysqli_fetch_row($member_qry)[0]; + $member_group_name=dblookup($drs_db,"groups","id","name",$member_group); + if ($member_group_name) { + $imgtitle="Member of group: $member_group_name"; + } else { + $imgtitle="Member of group ID: $member_group"; + } + if ($group_count == 1) { + $disc_txt=$disc_txt."
\"GRP\"
"; + } else { + $disc_txt=$disc_txt."
\"GRP\"
"; + } + } + // print table row + printf( + " + + + + + + + + + \n", + $tablerow["id"], + $tablerow["id"],$tablerow["id"], + $statusind, + $tablerow["report_date"], + $dname, + $tablerow["reported_by"], + $disc_txt, + $eff_icon + ); + } + echo " +
SelectIDStatusDateDeviceReported byDiscrepancyMsn Eff
%s%s%s%s%s%s%s
\n +
+ Select the writeups above you'd like to add to the group and    + +

+ If nothing looks appropriate, you can modify the search parameters above and search again or select another menu option. +
"; + } else { + echo " + No writeups match your search parameters.

+ You can modify the search parameters above and search again or select another menu option. + "; + } + } + } + } +} elseif ($action=="remove") { + if ($save && !$fail) { + format_message(0,"Group successfully modified. Return to Writeup Groups page."); + } elseif ($save && $fail) { + format_message(2,"An error was encountered when modifying group. The error was \" $fail \""); + } + if ($role && mysqli_num_rows(mysqli_query($drs_db,"select id from links where linkgroup=$group"))) { + // show group table with everything selected + echo " +
+ + + "; + group_table($group,$role,"select","all"); + echo " + Deselect the writeups you want to remove from this group.

+ NOTE: Deselecting them all will dissolve the group. +

+ +       + +
+ "; + } +} elseif ($action=="dissolve") { + if ($save && !$fail) { + format_message(0,"Group dissolved. Return to Writeup Groups page."); + } elseif ($save && $fail) { + format_message(2,"An error was encountered when dissolving the group. The error was \" $fail \""); + } else { + if ($role) { + echo " +
+ + + NOTE: Dissolving this group will unassign its member writeups and remove the group.

+ It will not delete any writeups or affect their assignment to other groups.

+ +       + +
+ "; + } + // show the group + $mode="view"; + group_table($group,$role,$mode); + if ($role) { + // show buttons + echo " +
+ + +       + +       + +       + +       + +
+ "; + } + } +} elseif ($action=="setname") { + if ($save && !$fail) { + format_message(0,"Group name updated. Return to Writeup Groups page."); + } elseif ($save && $fail) { + format_message(2,"An error was encountered when updating the group name. The error was \" $fail \""); + } + if ($role) { + // get current group name if there is one and show the form + $curr_name=dblookup($drs_db,"groups","id","name",$group); + echo " +
+ + + + +       + +
+ "; + } + // show the group + $mode="view"; + group_table($group,$role,$mode); + if ($role) { + // show buttons + echo " +
+ + +       + +       + +       + +       + +
+ "; + } +} elseif ($action=="takeaction") { + if ($save && !$fail) { + format_message(0,"Group action completed. Return to Writeup Groups page."); + } elseif ($save && $fail) { + format_message(2,"An error was encountered when taking group action. The error was \" $fail \""); + } + // show the group + $mode="view"; + group_table($group,$role,$mode); + if ($role && ! $save) { + // show action form + echo " +
+ + + + + + + + + + + + + + + + + + + +
Status
+ +
Action taken by
+ "; + if ($role==ADMIN) { + // allow selection of tech name + echo " + + "; + } else { + // limit the choice to the tech in question + echo " + + "; + } + echo " +
Status change reason
+ +
+ Action Taken (required)
+
+
Action Taken Date
+
Action Taken Time
+
+ +
+
+ "; + } +} else { + if ($role) { + echo " +
+ +
+
+ "; + } + if ($group) { + // show only the requested group + $mode="view"; + group_table($group,$role,$mode); + if ($role) { + // show buttons + echo " +
+ + +       + +       + +       + +       + +
+ "; + } + echo "

"; + } else { + // show all groups + $mode="view"; + $groups_qry=mysqli_query($drs_db,"select id from groups"); + if (mysqli_num_rows($groups_qry)) { + while ($grouprow=mysqli_fetch_assoc($groups_qry)) { + if ($expandgrp == $grouprow['id']) { + group_table($grouprow['id'],$role,$mode,"none",$expandgrp); + if ($role) { + // show buttons + echo " +
+ + +       + +       + +       + +       + +
+ "; + } + } else { + echo " + + \"Expand + + "; + $groupname=dblookup($drs_db,"groups","id","name",$grouprow['id']); + if ($groupname == "") { + echo "Group ID:  {$grouprow['id']}"; + } else { + echo "Group Name:  $groupname"; + } + } + echo "

"; + } + } else { + echo "
No writeup groups were found.


"; + } + } +} + + +echo "
"; +banner($print); +pageblock("right","end"); +pagetable("end"); + +framework("end","","",$print); + +?> diff --git a/distfiles/icons/block.png b/distfiles/icons/block.png new file mode 100644 index 0000000000000000000000000000000000000000..ed7ec0e972ed69b08a7c4cf5f2b38111b987d97b GIT binary patch literal 630 zcmV-+0*U>JP)IGgPFXOrD-o74|)X8xN$Z)e`jD$1E_z}f2vLQyVJ`w-o63qrgMvNyAEi*rvF@JW%O$Q==CyW`|5ODT(#XD4eHr-sJMmx8K%KyURF9~R&+wenvFa$R8m@07*qoM6N<$g6cUWI{*Lx literal 0 HcmV?d00001 diff --git a/distfiles/icons/calendar.gif b/distfiles/icons/calendar.gif new file mode 100644 index 0000000000000000000000000000000000000000..52f2863c90764cabb11ac926b3f7e02b9b9e5e49 GIT binary patch literal 258 zcmZ?wbh9u|6kyTUl#W73z|)4nX4`*rp5ZyQ#B-@379)%)KM@Be-M>hGtI|Gs_w|L4#D z|Nj|~fDVWavWtPW{Q*md3PbOU5QXTA``&Hf+-&MQNyN4Pv4TT$qr;4tz7x@TlA2nj z{VVh>rsSCQXN#@AXk#?9q4e^OX(ylcxawre$mLbEZVWhZ^wyuYi=i=Ui~?zCvD{J; zob`>l`Gve(T%4S?jg@f;{OB2!bOQWALGdFOQVvkR?QwQ_|pZzJQT&0DrAGFSru Doau8X literal 0 HcmV?d00001 diff --git a/distfiles/icons/chain--arrow.png b/distfiles/icons/chain--arrow.png new file mode 100644 index 0000000000000000000000000000000000000000..4c8515cb9b345be376528051d4f05be5c92b75f4 GIT binary patch literal 584 zcmV-O0=NB%P)PbXFRCwBA{Qv(y10{fofkH6w2C(?~ z`7yAuu^BTmGU@>N97u9MfZ=lm#K0x@>({TdEI@{-h=_ihTa8ER^3y7~C{So897&77Q^#G9L2I$6Gc{mLsSC?o?kammMz9~*$q?Xt46 zWB|Gfh($rp2Wx)w<_*{&pqfIU+J@lZkP47qkO6=0-+%f%K0eiK(xl#^&!0bk+`4re z)&K)~_aiSaZxKi>NDoLaBT&FeR#rZwsHoCcLqkVWKtPE3>(?)TuU@_Iyr-viGe{${ z-0}x33{3Ctep=g+vGnodhie#t@V{^7r@~nPtMPm&Z{|^Jh%dA8ry=e^W)(U)5L5%nkiB!}i22Ody zGx=FI#y37OFns=lkP~BPVA!zm$XT2Q@S4Xk@S4Sd0&eF2fB%e;dB0000`6pHRCwBA{Qv(y10{fofkH6w2C(?~ z`7yAuu^BTmGU@>N97u9MfZ=lm#K0x@>({TdEI@{-h=_ihTa8ER^3y7~C{So897&77Q^#G9L2I$6Gc{mLsSC?o?kammMz9~*$q?Xt46 zWB|Gfh($rp2Wx)w<_*{&pqfIU+J@lZkP47qkO6=0-+%f%K0eiK(xl#^&!0bk+`4re z)&K)~_aiSaZxKi>NDoLaBT&FeR#rZwsHoCcLqkVWKtPE3>(?)TuU@_Iyr-viGe{#$ zZtCyZ%;!H|V37VJ{`+~|%{h-BKU~8Ilow)VW_ATSRU1o4--O13I!NwM4_{nLT(bC_ zC3BxM7k(@R2Iy`Upu~3|V?U5Sj5D%Jc>NgESTum1c=_?~M{x#@|F8c}=c{74{l}e= zU`i?AVPH^YIpY}QWQ;Dgf8D;b_ziGphv5GVGl3XvfKHgc@s(8<&w}L{8P2j0m0tfd zq%-^h=Gx!?&%FEl4(REB3=FJ{CJZc$3@k(^`j@{LfQFoX)^!V*0T|DMG&4xEGXMk_ Y0QLFc?%bB?od5s;07*qoM6N<$f?YKi0RR91 literal 0 HcmV?d00001 diff --git a/distfiles/icons/control-180.png b/distfiles/icons/control-180.png new file mode 100644 index 0000000000000000000000000000000000000000..8b847a0b1f2be8b46d0db9fa687208e0eccfb590 GIT binary patch literal 458 zcmV;*0X6=KP)0$h?zc(wljAM@w;?}8XFDPthb z0K&u@0Mz{Z*RO(DA35U>AO0}>{`nJ(i8A0nRCAoait&vbUl_i9!)*WyR?R^Kd;@WSsOKPF6`x&JJ$00YCz z_a7KO|NO=9_19m9U*ErjF+l@>URVNj#e@|rPn;D}mt}a#BgpWAl?UtsY_7l+h)aNh zIAPzieP`Ka#2FrQ3Nrix2H-DZV&TaocZTz&baskK*>`=@MwFE?i#6JVof5vM7sQ@6r0H+MgS(LEp;s5{u07*qoM6N<$g14c_ ANdN!< literal 0 HcmV?d00001 diff --git a/distfiles/icons/control-double-180.png b/distfiles/icons/control-double-180.png new file mode 100644 index 0000000000000000000000000000000000000000..a39522fa590e952149b3bf03b8e1da48f8410c42 GIT binary patch literal 551 zcmV+?0@(eDP)(RCwBA{Qv(y10{fofkMCnV`w#< zWnkyzWMJdqU|?luXJBDvjR6YV0$rF6lK=7bD~9;NByofRDB%D9m_NUN7r?lGe*MBK zj@=7DMZbUjDv0xuHvawdCj$uo{Qey-9_4`$$1niste9Uve-BHWj1mT5ML&KNWXBsBU%dE%K~RtdY%@rlk&&^$-&x*x+qR1g;^O=e7oi8@ zukYUr@=~pg&zyV1@aDruhHt-E8Ge5I29hf9c2zSzaqbBN10xf|m!E%N;;3E#+CHIs z(%!QIvfK=J8CV%!FmZrw0*SBNbnz^ovJAsZ9zlj@tUO?Gm@Al&ys!l5iU~_*?mDZe zF3IqSogZuyFtEVl$JQS?`%glg;Sr}0SRCdGxB+k0mNNW$c6kZN#>3Oro@L;KxZvC8 z&v5Z)OV^+M!NG~*0!CO;1_l(EW)|0EVAk-DVG>mb$M^3;ohE-@-M}jT=jC;jgv1KO zoPS?l=LX{A%qFQK4D5VnK+VP=NuU@AUuQN>SIu7LI@f~1s7_glwdTO zxifP!J?AEVCc7-W@Xp7>J?}YZ=3LG9ebln*P~!^lifOrMKwVURaCMzmIC!W3%|Sue3>T#=q4^o=dDloF{>K9~zHxAT)r6 zFS9Y;T)aMv%JhaQGY6=DAFj&pmx@(`bd~PHWojQHmyofggYxh&2k|4~G2(30L&}p1 zTsZrx3aGSJdTA)D+W9DwcwF5l@+j8VUi~!w7hnL9uuA%W$>(GM0000n&vr zqzf4S|BoTb0GM;%zyHG!?&Qae+SdA ze*I_oz|IY3>nI5_tlf0}EHHW}5H}fj9x^|0O@4Tq2wjK`{UpWM^TxK644g+3#*I`EqG3!|&%; zfHEZ*1~4M$1y=0<1||)^Ga&Z=H#aBzyRmu6-&fb+^4RhXiVOZ++sMHrpmL4j$A?S* z&dmi`sR6`4p!huy|HSJ8pp!uc3PBD22Qm{0{{X7}gVz9Z06>5N01C<2jATQ61poj5 M07*qoM6N<$f-i*7RsaA1 literal 0 HcmV?d00001 diff --git a/distfiles/icons/cross.png b/distfiles/icons/cross.png new file mode 100644 index 0000000000000000000000000000000000000000..6b9fa6dd36ee8165272a13dd263f573507c78ca6 GIT binary patch literal 544 zcmV+*0^j|KP)L-ku(! z6_?D-?!0+#=VtbVZQGdTnZt~apI_HPK#=zVadHM((E`e*C+RoFb)Qo8-U{Lb7{`Tz zw4B8FZ|o?Wox+p=1wp47C;7ar*Xu~;a?<=sjPv>+otDjJ6FaGt!YnNyxQQhp)G1V! zk;r6ZtyV)c8pTbiRAnHRNXTxti%=+p$4aG2*+mMM&xrdiU^`VPk^N*+HX98^7!HT% zwA%;A{T)GxeQ|RcxyzV%! z$AbYD+)i1R64zB?q}NmTz}5|04~J#YG_goA*Eq(QJvp7pG14hUj1pZ^t>3S*xqHUO ze~r;}zTY_XkZ*}d@gm!;N90h8nBQenBUZ_ukZKNicn*hc_Pmc!Jn|2=s<~}>!Sb>Qm3!QP46b_JFw5YU70Tu$X}=T}ez@@t%j iG9vD)nDux55?}x$+|UyQVK_bj0000uaKl`6H0#EMPSO}g$+@CEq-R{ewSv#FGPK-H!Tc2R3p2_kJF0cu`0*kJtV zjCsgq(?u;Ed+xn+&YijUnDKod{^#N8^`2pX!!LmWNFh!ze2F+%=KVx`#;wi}cP!HRj?vUiNxuPsBu{O~nOELXjlSf5~J5P%I&A zB-h4c)%3h7afr*1RT(5a!ohQN<71Ys0TMXehLPM^z5@17Z^8fgQ$x#zB eD000&0t^5S;vPyKd%pDm0000z>nv``JZ#1^Bf4aOsCUPHRW;{Z59Q|XG8jmGSBQ?YVxW`bbC!36@B z5f9Jvr3Y-)MM{k}TO*fB2?$`cEW9^l>hmg&o-D?G5|(8_0?XQIwixin;|ql`@eV~L zWjT*~Z)Zt3j!P6koh*}W7sSoyfWjRrRlLKZH$)+=`hOJ!MK0_IWRg0Oc=IU&>qpuk z!7vO+z~bEW2akj#NRE#P@nT$_=!c8lYCIfl{di3WWm5<#HgEO6P|j_AXNV z2FD8GznKY0uB|Nn?77o57YGCClcB(b zZoFMdJ9mB1@1AuHhr{7t&}Op%Q52&TrKHGfnwzg&v)j+M`+VL(6w{2zMc`hn@9CIu zkTQiIK(aHmv2kA#lldS`r&YJKw6)por*0x~pU&!-rz`6lAwgK8eyj+*4Yhfe?!Hf# zBPc1fTCF&9mC2NUq^YT`(QdEpcDr3YDBzUw>T9f-Y`j!q`MeH*pa1*R)Fc3&-W!{O z27>|U_4?NS{%4&I$C=*o@i!09G>+ntPRiU36lHaHsSL9vwok^oW0r3N!&98c)rChi znWpIqB(ScouKqydIfm}=*YR6$F)OF68+ZvIjEGCKw*n&_{kRAG^78UWw7PLCwh$A5 pgNjTrI0;1I9Xrx7fPH@pFaRcvUPbP`Xq^B6002ovPDHLkV1iKYeMbNQ literal 0 HcmV?d00001 diff --git a/distfiles/icons/minus-button.png b/distfiles/icons/minus-button.png new file mode 100644 index 0000000000000000000000000000000000000000..6dc019a60b0ab525b0b136a5bb25a25bfe892f4d GIT binary patch literal 479 zcmV<50U-W~P)IFbV8T*9m3uQ&Y18-Y3~RQmnXF79@XnN7p+!RsH#}Ulx}_3^^L)x z_GTFJ=$41czs^~+S#>;5!1Fe_7N?Cy_0=#s-v#!fQb{boD(?pn-%9rV(N13P7 zX(0hw%8Hi>xUP%YY?fULf&eb)e1bFfIqGo&t3eWD0|9!35l8EGg(`WyoX^DsO=)O4-oBmr{eC<1b{^C3_u+qx$0c>Y2p*3Ye7?#Sb z5TE&ns}SF?Z9CuT=sS(Z5wu!OD7;P%1RNuPGz;Ba>ar(rZSEXIf`fFmW+k@D?L?uZ zKOmS439S!R;E)xelh59ZiSY{}3|i~a+*Fu>*n}4rFG+zXuXghd+XhF3#lVv*wtG@Gr}<*U9xG{l`s9ugs#8&`Y*55 z;lAB&gQn?p-|Ja4&T%{}%&PG6T?Y^OF+b>^ory`sqATq^Qr+K*RGEooO<1c z{n~)z3Mh(y<5S5@+`Xxg3bc^|k|e`cxl1{23zFo`7fMXM)=)^f3MpGY(+~bg@VPA1 zk>d(*oUmh9a^~3^%lLgLQ6SF`JI1Pn{QNB#h(!)^8JxK3`3x`&2mZ)XIy$|SOU;Xc zWI}++5K9ktzt)4|R}%^)v;K2?GxMYLbp5cpNvD+xFbw0IM>rU}H9Ix;@Xq+eLXu&H ziqe3GE{;F}=5RMUn;W>gd`i`hdtV)zhj zl)E^0-?x4J&hnE*9{6$2|9(BpGN?X8NGh>GbCFc|vcj^U3(*d&PNG6j}aMWZn8EiJ2V_{)oIT#FnL>UpI(fFUCEXxEk!nCxs>@vYHp*A0$ z2Y_F29vmDT+=hc*(1!sXuuak#IOnQ=w^YLO2VjHxOagZnvwE{&@ds zZ*RZb)7@n=+X{u;W@z!nmk-Oq4(NvXrj&dP!&n7Dpbd+o3|hPE#F=w_!)JY?X1!Vz zRUBYAG=A~2saCJC?_WNPHyog0Boc{OJ3Bic)7lb=1TmYq&V z9y)t^QyOTDsambZQD+PWs=7>M#eu-F!kf_3C>%uDbUK}!o10sqQRz)-7!s9ArAI@% zhx@v3&IhKyXEqa6QDD_lDVKXRGyQJ*{=KioV(}9LB@?1uP-c%wJn!l0VMI~9GBx$! zq0cvV8}t4QnH@L+(7&+;ySEs3f$%)vSW^Wqm#brBWaL&j{7u$qG}` z&YeAT?zuZN!eX(YeTGPT4mp?09g3oOhPfN)&gb*j*=+V#uh(@_BoZM>l1zD0JRZNy zWHQ&ljnnB=DwRsV+wIoyQ5P5vhcul|4ZP84RFfNx#xX?BnZyzKlcFfuO`GHKm|Crt zZI{R2;7p2nJRT(w2%MHmrMF_S_-V7*9%NZoCzDAMoP59ESFyps(=_c{fE^F)ra|-p zEeICTYbTLNZ~=CbuU4yso=tf&7CY$ganrKb>!rb9pyzJ4o0`q0fd>JS&5p)mu@G$Q zIjmDO8eMK!4>%(J(FOsl)k?iyZ)+_W3=%Hf5jI`09?(JljQC3qhlBs*R&|`h8dxZ mCJ;BkyeZ#uVc+NcFTen2dF_!K8+a)I0000Wb$GF literal 0 HcmV?d00001 diff --git a/distfiles/icons/notebook--pencil.png b/distfiles/icons/notebook--pencil.png new file mode 100644 index 0000000000000000000000000000000000000000..9e025e7c94eb5882b044fcc115507725ba1a01bf GIT binary patch literal 757 zcmVV8x*S5h8-6Rl??_lyV&r~Xf`=Jm{H#a{R z3Wa)U{cRy&>df;okzH_}6M|N&6%+IGzjg`qa)R$W1hqu) zP05uJ?2$00aTER>&^bFxThR4gf%-Z-3gng-1lD3Bt01vhETA6g#^@*xD@uicDXOT# zrA82B|3Sb6!3kOR)+@@$IxuL~%LMA(c5!rBPT(#Q02L$~$s{tGcHGn7-}5mLs45qb zqOlzU7bl4QO`vUt@qQtVp|P=^Zl7=93S%$L1v>8%fv?ss7HdS{bK68+x=foJZ?U?5 z4_D8&pzYM%iJzMG=rXWSu?0>*esB_j9MdYClPd%0IN6G4qhWZPENhzKS(tOVVA@I~ zz(pt}UyTi3myQ1z@YPaH>99nQi4BV8@$p&syLzFh&gdbJ2d^Ctm{z8ykfP&IGZ}L< zu0l>|;Rg^B9$8*MHvsIV>yP2h>o0q!n%Zpo%$lC9N nKT7WnRZXh0{EEu>9{~mc=B~q1eam^+00000NkvXXu0mjfR_9?L literal 0 HcmV?d00001 diff --git a/distfiles/icons/plus-button.png b/distfiles/icons/plus-button.png new file mode 100644 index 0000000000000000000000000000000000000000..10d1f60031732739dc674c8785a5b8027ce17566 GIT binary patch literal 583 zcmV-N0=WH&P)h5s20ovZX^7fD;8b(_RL!{(6WEfiitsiAGQlv=rSefIf- zn+r|Px!_jOrz7Ms7vJ|bXSaJhdilaeYWCX;H|Cq-Pvi(rNZlr$$I5Yz!UhbpQhIFFK8Hy6z^g2-g| zFqTvX4`mmU$GP=ZrZ-OZvgDFVCUr~0!Y+X*8Z(d?b{k9V8LnWF)|bwBOaXpj4Xf%Eeyd%Ur>BN8&;L3S-SK|~7yy`x V;U%6sJ%|7R002ovPDHLkV1fo32DShI literal 0 HcmV?d00001 diff --git a/distfiles/icons/plus-white.png b/distfiles/icons/plus-white.png new file mode 100644 index 0000000000000000000000000000000000000000..c765946e038152fdf3cfa67b54e108b15efb77ca GIT binary patch literal 703 zcmV;w0zmzVP) zB558bX=XCr zks$g@S0valO*7r=b$1$#Luj{Kka-ce64;9fk~|!(B+fd$7pG4^&}SW2szxkV_z}zG zyLX5tMX0skDzMF()Jrel49k(zGFVp|(6jLX2jNi{%uOlY2hVoX4bucW36Bq5*CVFU zSy@hw$sr$)doT=xTy2{IW90A*HK;`oOVDbymliI#y&=EA%!p>QNn!745~8_s-t9#X zyF$0yTU?m)Q-QrOCOnNxr7{c#28{(lgxN_4Jblx{Cf^sW`}5IA&_UjPICo?7SX*g< zaP3}^G3%>Q2zUhQVHJp?17L`i99i+dzPm^oOcL8Vnm|=g7K?kG*eORJa<48i@#Wi} z=zH_|6ys{OI_y_1535nrJR9%xIt~$OT8Dw~6vsJ5Q6X2bJS>5txYC(CSF1KOkYx=d zNu^L#H-)`N9DG+`yCBuj!x9LBw9_}#)apy4|E;c2p(v{G_Hlp$W8^Bt{OC3GQHvh* zayW4lGb!LW0X)I^WN2c3B{3s=<1q=w{5);$esuuZk2+-XdhJVjD^VN_nA-!1pPk~;B}k?4mJRP lFZ@BeIK23yp!cT$0{{vEAhJLvQSAT#002ovPDHLkV1j}6Gh_e& literal 0 HcmV?d00001 diff --git a/distfiles/icons/plus.png b/distfiles/icons/plus.png new file mode 100644 index 0000000000000000000000000000000000000000..4ac1bafbcc6fc7a894a0e7c0a4d58730818477d9 GIT binary patch literal 521 zcmV+k0`~ohP)z{ZCNSP1$Af}nyR+9m-Ti&ToBV4+6k{;p` z-#5Fbj4}9+4e`g_TS0rVQ;aAyIMiRYV6an&AAiA|cmmL#xdS`}j5=9@fxErFguYvkV#=u%aQalROG7#VQWzx$Phm_MZMF~H~fq)W*mE?vYY>(1{m_}{NJt!C_ zL^KkSAP5A2*=d$#1~-wc!J0GuQV>NEwAKOuG=J5$$ajYN`rZ24hRd4y#MIOE>e8y4 z-*+4bH@DY026RFk$JT{_th*mwc%CQnlNd%zakW}aS^WUC&Mr?&`eqmATwT$4dU$M= zg33zh1Mnls_s7^~Hj|Mg>w%+|q3~xDbGJYqX(2Y25SEea?7A3KEJ$%9f&PJl8eND821JVDLIihG+y!Z@4+2UMgjAcB zHZe`4oxGcwOuXNXGgxuwg)@)uJLk;(4tJPFqXGZ3jCdKp6a|Z=1#Grf5Ji)hW!X7W zo+jx1%}Wq0tEw8VRx3$K+Jj=T0FlT3rh&EyA;7|ZXnfG(I5ltxI-JdRDy#Tb<0W4t zR=GkrK~h>eumeqO;;P|=iyq$F&O>vwI_-748Su2V!02(ovG`~sEU7AJCe%QdWv{A~ zLkodpe3z5OcQ||X!+}}v-spo9eKuGKbn@44#zTY?A>|~C%|f9#H#ux`bh!*vtSAc1 zo^Pi(GiZf;J`e9c@6aA{CQjNMs6j0?s8p)clcO$5sMqThGZS{08sWid-N!y;GCPn+ zBw%gr8;taeRHKies%)>f!%Xfq`gKXlL%Cdmk6()L@I@Mi2V8LZ!V!{u5ilCJ-9R?}1XOM0h;Sqd_)KHJX7aS`&%QvhT3DMua>; zGq}}fr|CVH+Xapj^+30>O^u#`$z)%C6O-`}0nZCM&dqOA{8%pFww6eMA1f}y(7{;1$Z0ha;VV_$h^s#A_ng~Rce`Cff9{I5nkPQQ{Xe!Qbc_3 zInQHlanR{vX}DY2Iz&Io5Q)jT?e&%5_t>qa z-P8&_t<->F7%fn@(=*lIJ#cNNwQa=Du-s9ax+<^l{dgbUj6Zmp-FW?#l+pyXzYTC4 r3xQc1?H>z&kP04N{83Q*OMn3Yq{|~z654m?G)iJ4iAVne6SOec!%)^9H%D3;(ku{)oQ@Kmvdu(l^uw2qma-j-sQQ zw@NV$u1gc$-#{F?5x}#FJ%PX0pxPkaJ$t&NI~r+j)qGwbE3j1BH5R^QFMa;9He_1% zQ=0eoKm|2&D5yNVm5BEdhiDs_ENs+cf#|VNAkrBMk5A48ck(9GM5+RASXIQ~p}~`V z1-+>0dI2V$#1EAAU?dG|Ia#}QxvOvd&DUY8?95PiM&ME6BzjMUy90#*V&B zaZDyNJSBEi1{T}3EG-%dbu-R>1+Ih7x3_7ndcLs##j*DWCk*uFphaDW^im#DDIG*f zgkZ3-m2qg=v%Oy+xI+{`bsPsUc0ezbz_iNHA5-A=^+rf7oABzL34-JWcIdW^RmQ3J zfMA3%M%FF{y;uf95Fk;4M|Y0!@Z%R7AbGX@>Ijk$c=wMAfKf}T7nx7$`D`pv|F5uv?mBr;W zt6?dk<9%SKF2WcYO}Xrw>4WY*I&- wVvBNdO@Q@6twQ?84W?HS3|1KBl0O0r0Hc5`CJFBOhyVZp07*qoM6N<$f}U|+z5oCK literal 0 HcmV?d00001 diff --git a/distfiles/icons/sym-dash.png b/distfiles/icons/sym-dash.png new file mode 100644 index 0000000000000000000000000000000000000000..87e4d4650c852401824bc417039e323b33825c4f GIT binary patch literal 562 zcmV-20?qx2P)6{+H$g#+;$Tn;E*;uh8Zt=nz2ts6Bwq5;Eq&n2J;QyTyXU#@35JZ=TLr+A^+*Js zzuO|li1(fl0wDwdq?AZ0QA#0%;QfswsgMM45!xQI6MM1N;+!MK*gq{soO4)f3$Oqb z!0+SNh(d5C+mH5vjm>Sm_d8U-JC@+-hrif-`-l>NF^2lLHMDVzXB8$+9$@iBqtNru z*{PIIJot_@rr(0rIeD(Be_3TzLRo>|UKb*JzysbZ^jZcT%s9=RS7(69Rzs z`)3n;Z1w|mItU@Kwl{~^f03a=vda*IrWgh;0UWxyj8qDMt+|V7!{OVnQ-1Ew1zH{W z@$?M;%Qf1X`Q@V=`{1n&!K zeQnTkilZ~9*mmI7HZYCloio^Tc@ZHc#7JnabSGWOKnP+8sMFKPsVZd!&GUlNAUs-| zCt8bpbqiU^3I#%Qg;H%E|LHU3YL)a=BD*ufKidlCgvEs}-v9sr07*qoM6N<$f`Co( AFaQ7m literal 0 HcmV?d00001 diff --git a/distfiles/icons/sym-diag.png b/distfiles/icons/sym-diag.png new file mode 100644 index 0000000000000000000000000000000000000000..9e3368e3088d06f6fb1903dcda76234fc8de7665 GIT binary patch literal 592 zcmV-W0H9ij|}itb~*bg4se4 z#3CSQp==`rZA>I0A!3gBGywrn0MBcJ z0Iw@6hzKDBVvNKX0nl2bwI)dtrf&|l`Icz_FJJ?Vjtr1x8QyzDB+n*-_r3rtPzk`V z;p>P}oF3hZ36Z7cH9`m*G<$bs0}QA~oeG?D%*<8!_^r_TJ7vNpD-hy+Sy?u$sIxM_+2S5y==OcOlS9tr4 zDKh8CoMZ7fCAfE5xy$$b|?*~D2;lbhD^ZlOh`QGz{1`QxU@Q35$yudx;`0j(vW`j}+ zr4#^CN~DyeQYl6kSr_jDw%NSg(ak$T2()(6Ui^HSkAj8R%7jP`uirwFU#|9JHU7+{ z?i}qZ+4?AUW)wTK#Jf^=9#9q9Q}V4zVqsD)-b768=#`!BTM8tAWt!NQ#j;U!6Jk4nPRLpUz{e9Ft~LZv&8Llp7n>+t@hbzrXVj XF|5?O2DN#v00000NkvXXu0mjfo(9wm literal 0 HcmV?d00001 diff --git a/distfiles/icons/tick.png b/distfiles/icons/tick.png new file mode 100644 index 0000000000000000000000000000000000000000..2414885b8576481fb6350ed9ce01ab033169fd75 GIT binary patch literal 634 zcmV-=0)_pFP)tYd4K$mX5uyr2F@fdffp{&DSHtl4|Bn9=ukpCx`#%PTUr-D(@b7;C zhJXJjrnw{;1KBM=6&|E`fsNrGL!Y6%zUh}QUl`(@V)PmQFtotEKmafTHP0uP}7&%m4pcKQ#X)BiAJ2y+PrtBI>9eEIt2-_c7)?*Lsf z5vX5*Af@m-wBJRV%#Fiz=BdPr0!2^a2d-yv7gSU);Z6{`~Oy?E5qSnHUln4*Y%!){F!Y1~5WXoC44g zb7l_)X~q^V6MmWR7e3wj|L|Wb!|^}Y86N$^h+kv_Kw-fP!~#If$6(B4$)LlK#&G6; zC&ShMSAb$5tA9Xg5dOsg@-z^@3;;QS9f&!gG&3|;{Da~@Q2ZB({s%XJ5&#fj0J|In U+D>(nEdT%j07*qoM6N<$g0@&8X#fBK literal 0 HcmV?d00001 diff --git a/distfiles/images/Kamiyamane.png b/distfiles/images/Kamiyamane.png new file mode 100644 index 0000000000000000000000000000000000000000..634f5a87df28bf7f21b79de6eda5ed8ae038cff0 GIT binary patch literal 10223 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 literal 0 HcmV?d00001 diff --git a/distfiles/images/color-palette_1.png b/distfiles/images/color-palette_1.png new file mode 100644 index 0000000000000000000000000000000000000000..6168280a4e329e2a4739bb4670856fdba4cc4ad7 GIT binary patch literal 1062 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 PPhE4*)*fq{^!)Pwu~k*R literal 0 HcmV?d00001 diff --git a/distfiles/images/php-small-trans-dark.gif b/distfiles/images/php-small-trans-dark.gif new file mode 100644 index 0000000000000000000000000000000000000000..d23e75f22e1254005464e88e08850d5ff4023e2d GIT binary patch literal 1363 zcmV-Z1+4lzx4H#a!3v9erXeKt8th>WFLTxY+*&0lS;A|*9nXOcigUz(!GZEkbc*W6QMqgG|5 zT4||TXs20drCDdCT4<$PX{JU{qolI7MZF;m36C8Pkw;3BGiIcs1 zeT641KR`i40RjU6000FA0v;Y57#J5XFD?rT2M!Gj6cZ5$2M7H8{OIWD&(F^S0s{a4 z{{R30A^8LW001!nEC2ui09XJY06+%+z?g7IEEs5Ew*AsiwEm&Hi~5fv)S zD!|Uqz#b*cuFo8ZE-;KpHBdV*F$z08FA^~eGzkqfJUd%wJM8XYPnJUn2?`RUAsQMR z9^8a*Gl)eqfz~*LFj2ulf&e+1ybxmyi3=0YKIB35ViXD%dH@(`AnV2j6(4N@n2{j? zD;6kFTyP0M01AW?C~P|tpyCn#3KcYMf;D3n1Uf1vYzR@1MF|ja=3G#Kss>UN96mUk z@L|PHpJ0z5pzt7qg9>O(s3pO+EeVDV9YjdLAOMYsVe=kAhXlck7W(#GFc`Ch;lm}~ z!gXNb!2kmlTkJ^H&eo0=I&|#NJn2b`7m_+(ZomTo4+Sv-w1D{v1T;>t0!EL6x*%{WRkIdWVJoF`|XLLj0PUxjSmG55;?)ILuLEzW|$ zY!4tNgrtFiql!Q(Xkw5Mip5evya2qo$w}A&A{aD~!4~1z1C|RFID?;bsMrv}1rR7_ zpGNHgz(5AFF*pMT)c`a9feYFFb_5I-1l7St09dt@ZO~{i6Dk(aW*Z_pg(zYZDLinN z37ROx;*ZyGFv1BBtgy$8NG7S|l1m^UKmr)FumA*9R@p*BCp6~53I^E7yN^|L{3&TOCp1~0s@K!+?U)KCHg6re-{?(Dfk1DGL@!45U}AcYR!VPOxg zsEHFv0xv*N-BKX1DXg$s4HOFiII!SmAVUp1tX8<4zyJa^>TrdU52%nPY%C;D!?)iG z@E1%dRA>OW3`DU1KvE4b0PeR8B+!B#u1zq4PG1s1fCIw;;|T;gAlpI<7({SNiW1a- z3Y1|CpqvC1{zY&C1)vfEe}|k>?;>! zcrm6zLN){jL$4Y`2C{|)vVthG7e)drLn1**H-GTS9yGMXQ5d?RA_IrTZZg3MUdFJQ zZ(tda33;mkFaQIPM|~6u3qU?Kd|5Eif&?oc06FAPLIA4ZClF9Lk`E*(I4A(n5Hp(} z)HbX@9k8iz0Z1Gv1$*F}Ob|ki`4#~{>LyU0fV@=y_>7Af9N&WP&`dBmcBkiSfQ}W6 zV2JV=1@ZxL{Yfvv1JqU@d=UmvV8M<&27{ZA3B4b}2qI+3zzjF^aQ=-2An^DL7KngZ V96&$;3XoTIp@9MPSCJq906SI)^mYIM literal 0 HcmV?d00001 diff --git a/distfiles/jquery-ui-1.12.1.opendrs/AUTHORS.txt b/distfiles/jquery-ui-1.12.1.opendrs/AUTHORS.txt new file mode 100644 index 0000000..a75056b --- /dev/null +++ b/distfiles/jquery-ui-1.12.1.opendrs/AUTHORS.txt @@ -0,0 +1,333 @@ +Authors ordered by first contribution +A list of current team members is available at http://jqueryui.com/about + +Paul Bakaus +Richard Worth +Yehuda Katz +Sean Catchpole +John Resig +Tane Piper +Dmitri Gaskin +Klaus Hartl +Stefan Petre +Gilles van den Hoven +Micheil Bryan Smith +Jörn Zaefferer +Marc Grabanski +Keith Wood +Brandon Aaron +Scott González +Eduardo Lundgren +Aaron Eisenberger +Joan Piedra +Bruno Basto +Remy Sharp +Bohdan Ganicky +David Bolter +Chi Cheng +Ca-Phun Ung +Ariel Flesler +Maggie Wachs +Scott Jehl +Todd Parker +Andrew Powell +Brant Burnett +Douglas Neiner +Paul Irish +Ralph Whitbeck +Thibault Duplessis +Dominique Vincent +Jack Hsu +Adam Sontag +Carl Fürstenberg +Kevin Dalman +Alberto Fernández Capel +Jacek Jędrzejewski (http://jacek.jedrzejewski.name) +Ting Kuei +Samuel Cormier-Iijima +Jon Palmer +Ben Hollis +Justin MacCarthy +Eyal Kobrigo +Tiago Freire +Diego Tres +Holger Rüprich +Ziling Zhao +Mike Alsup +Robson Braga Araujo +Pierre-Henri Ausseil +Christopher McCulloh +Andrew Newcomb +Lim Chee Aun +Jorge Barreiro +Daniel Steigerwald +John Firebaugh +John Enters +Andrey Kapitcyn +Dmitry Petrov +Eric Hynds +Chairat Sunthornwiphat +Josh Varner +Stéphane Raimbault +Jay Merrifield +J. Ryan Stinnett +Peter Heiberg +Alex Dovenmuehle +Jamie Gegerson +Raymond Schwartz +Phillip Barnes +Kyle Wilkinson +Khaled AlHourani +Marian Rudzynski +Jean-Francois Remy +Doug Blood +Filippo Cavallarin +Heiko Henning +Aliaksandr Rahalevich +Mario Visic +Xavi Ramirez +Max Schnur +Saji Nediyanchath +Corey Frang +Aaron Peterson +Ivan Peters +Mohamed Cherif Bouchelaghem +Marcos Sousa +Michael DellaNoce +George Marshall +Tobias Brunner +Martin Solli +David Petersen +Dan Heberden +William Kevin Manire +Gilmore Davidson +Michael Wu +Adam Parod +Guillaume Gautreau +Marcel Toele +Dan Streetman +Matt Hoskins +Giovanni Giacobbi +Kyle Florence +Pavol Hluchý +Hans Hillen +Mark Johnson +Trey Hunner +Shane Whittet +Edward A Faulkner +Adam Baratz +Kato Kazuyoshi +Eike Send +Kris Borchers +Eddie Monge +Israel Tsadok +Carson McDonald +Jason Davies +Garrison Locke +David Murdoch +Benjamin Scott Boyle +Jesse Baird +Jonathan Vingiano +Dylan Just +Hiroshi Tomita +Glenn Goodrich +Tarafder Ashek-E-Elahi +Ryan Neufeld +Marc Neuwirth +Philip Graham +Benjamin Sterling +Wesley Walser +Kouhei Sutou +Karl Kirch +Chris Kelly +Jason Oster +Felix Nagel +Alexander Polomoshnov +David Leal +Igor Milla +Dave Methvin +Florian Gutmann +Marwan Al Jubeh +Milan Broum +Sebastian Sauer +Gaëtan Muller +Michel Weimerskirch +William Griffiths +Stojce Slavkovski +David Soms +David De Sloovere +Michael P. Jung +Shannon Pekary +Dan Wellman +Matthew Edward Hutton +James Khoury +Rob Loach +Alberto Monteiro +Alex Rhea +Krzysztof Rosiński +Ryan Olton +Genie <386@mail.com> +Rick Waldron +Ian Simpson +Lev Kitsis +TJ VanToll +Justin Domnitz +Douglas Cerna +Bert ter Heide +Jasvir Nagra +Yuriy Khabarov <13real008@gmail.com> +Harri Kilpiö +Lado Lomidze +Amir E. Aharoni +Simon Sattes +Jo Liss +Guntupalli Karunakar +Shahyar Ghobadpour +Lukasz Lipinski +Timo Tijhof +Jason Moon +Martin Frost +Eneko Illarramendi +EungJun Yi +Courtland Allen +Viktar Varvanovich +Danny Trunk +Pavel Stetina +Michael Stay +Steven Roussey +Michael Hollis +Lee Rowlands +Timmy Willison +Karl Swedberg +Baoju Yuan +Maciej Mroziński +Luis Dalmolin +Mark Aaron Shirley +Martin Hoch +Jiayi Yang +Philipp Benjamin Köppchen +Sindre Sorhus +Bernhard Sirlinger +Jared A. Scheel +Rafael Xavier de Souza +John Chen +Robert Beuligmann +Dale Kocian +Mike Sherov +Andrew Couch +Marc-Andre Lafortune +Nate Eagle +David Souther +Mathias Stenbom +Sergey Kartashov +Avinash R +Ethan Romba +Cory Gackenheimer +Juan Pablo Kaniefsky +Roman Salnikov +Anika Henke +Samuel Bovée +Fabrício Matté +Viktor Kojouharov +Pawel Maruszczyk (http://hrabstwo.net) +Pavel Selitskas +Bjørn Johansen +Matthieu Penant +Dominic Barnes +David Sullivan +Thomas Jaggi +Vahid Sohrabloo +Travis Carden +Bruno M. Custódio +Nathanael Silverman +Christian Wenz +Steve Urmston +Zaven Muradyan +Woody Gilk +Zbigniew Motyka +Suhail Alkowaileet +Toshi MARUYAMA +David Hansen +Brian Grinstead +Christian Klammer +Steven Luscher +Gan Eng Chin +Gabriel Schulhof +Alexander Schmitz +Vilhjálmur Skúlason +Siebrand Mazeland +Mohsen Ekhtiari +Pere Orga +Jasper de Groot +Stephane Deschamps +Jyoti Deka +Andrei Picus +Ondrej Novy +Jacob McCutcheon +Monika Piotrowicz +Imants Horsts +Eric Dahl +Dave Stein +Dylan Barrell +Daniel DeGroff +Michael Wiencek +Thomas Meyer +Ruslan Yakhyaev +Brian J. Dowling +Ben Higgins +Yermo Lamers +Patrick Stapleton +Trisha Crowley +Usman Akeju +Rodrigo Menezes +Jacques Perrault +Frederik Elvhage +Will Holley +Uri Gilad +Richard Gibson +Simen Bekkhus +Chen Eshchar +Bruno Pérel +Mohammed Alshehri +Lisa Seacat DeLuca +Anne-Gaelle Colom +Adam Foster +Luke Page +Daniel Owens +Michael Orchard +Marcus Warren +Nils Heuermann +Marco Ziech +Patricia Juarez +Ben Mosher +Ablay Keldibek +Thomas Applencourt +Jiabao Wu +Eric Lee Carraway +Victor Homyakov +Myeongjin Lee +Liran Sharir +Weston Ruter +Mani Mishra +Hannah Methvin +Leonardo Balter +Benjamin Albert +Michał Gołębiowski +Alyosha Pushak +Fahad Ahmad +Matt Brundage +Francesc Baeta +Piotr Baran +Mukul Hase +Konstantin Dinev +Rand Scullard +Dan Strohl +Maksim Ryzhikov +Amine HADDAD +Amanpreet Singh +Alexey Balchunas +Peter Kehl +Peter Dave Hello +Johannes Schäfer +Ville Skyttä +Ryan Oriecuia diff --git a/distfiles/jquery-ui-1.12.1.opendrs/LICENSE.txt b/distfiles/jquery-ui-1.12.1.opendrs/LICENSE.txt new file mode 100644 index 0000000..4819e54 --- /dev/null +++ b/distfiles/jquery-ui-1.12.1.opendrs/LICENSE.txt @@ -0,0 +1,43 @@ +Copyright jQuery Foundation and other contributors, https://jquery.org/ + +This software consists of voluntary contributions made by many +individuals. For exact contribution history, see the revision history +available at https://github.com/jquery/jquery-ui + +The following license applies to all parts of this software except as +documented below: + +==== + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +==== + +Copyright and related rights for sample code are waived via CC0. Sample +code is defined as all source code contained within the demos directory. + +CC0: http://creativecommons.org/publicdomain/zero/1.0/ + +==== + +All files located in the node_modules and external directories are +externally maintained libraries used by this software which have their +own licenses; we recommend you read them, as their terms may differ from +the terms above. diff --git a/distfiles/jquery-ui-1.12.1.opendrs/external/jquery/jquery.js b/distfiles/jquery-ui-1.12.1.opendrs/external/jquery/jquery.js new file mode 100644 index 0000000..7fc60fc --- /dev/null +++ b/distfiles/jquery-ui-1.12.1.opendrs/external/jquery/jquery.js @@ -0,0 +1,11008 @@ +/*! + * jQuery JavaScript Library v1.12.4 + * http://jquery.com/ + * + * Includes Sizzle.js + * http://sizzlejs.com/ + * + * Copyright jQuery Foundation and other contributors + * Released under the MIT license + * http://jquery.org/license + * + * Date: 2016-05-20T17:17Z + */ + +(function( global, factory ) { + + if ( typeof module === "object" && typeof module.exports === "object" ) { + // For CommonJS and CommonJS-like environments where a proper `window` + // is present, execute the factory and get jQuery. + // For environments that do not have a `window` with a `document` + // (such as Node.js), expose a factory as module.exports. + // This accentuates the need for the creation of a real `window`. + // e.g. var jQuery = require("jquery")(window); + // See ticket #14549 for more info. + module.exports = global.document ? + factory( global, true ) : + function( w ) { + if ( !w.document ) { + throw new Error( "jQuery requires a window with a document" ); + } + return factory( w ); + }; + } else { + factory( global ); + } + +// Pass this if window is not defined yet +}(typeof window !== "undefined" ? window : this, function( window, noGlobal ) { + +// Support: Firefox 18+ +// Can't be in strict mode, several libs including ASP.NET trace +// the stack via arguments.caller.callee and Firefox dies if +// you try to trace through "use strict" call chains. (#13335) +//"use strict"; +var deletedIds = []; + +var document = window.document; + +var slice = deletedIds.slice; + +var concat = deletedIds.concat; + +var push = deletedIds.push; + +var indexOf = deletedIds.indexOf; + +var class2type = {}; + +var toString = class2type.toString; + +var hasOwn = class2type.hasOwnProperty; + +var support = {}; + + + +var + version = "1.12.4", + + // Define a local copy of jQuery + jQuery = function( selector, context ) { + + // The jQuery object is actually just the init constructor 'enhanced' + // Need init if jQuery is called (just allow error to be thrown if not included) + return new jQuery.fn.init( selector, context ); + }, + + // Support: Android<4.1, IE<9 + // Make sure we trim BOM and NBSP + rtrim = /^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g, + + // Matches dashed string for camelizing + rmsPrefix = /^-ms-/, + rdashAlpha = /-([\da-z])/gi, + + // Used by jQuery.camelCase as callback to replace() + fcamelCase = function( all, letter ) { + return letter.toUpperCase(); + }; + +jQuery.fn = jQuery.prototype = { + + // The current version of jQuery being used + jquery: version, + + constructor: jQuery, + + // Start with an empty selector + selector: "", + + // The default length of a jQuery object is 0 + length: 0, + + toArray: function() { + return slice.call( this ); + }, + + // Get the Nth element in the matched element set OR + // Get the whole matched element set as a clean array + get: function( num ) { + return num != null ? + + // Return just the one element from the set + ( num < 0 ? this[ num + this.length ] : this[ num ] ) : + + // Return all the elements in a clean array + slice.call( this ); + }, + + // Take an array of elements and push it onto the stack + // (returning the new matched element set) + pushStack: function( elems ) { + + // Build a new jQuery matched element set + var ret = jQuery.merge( this.constructor(), elems ); + + // Add the old object onto the stack (as a reference) + ret.prevObject = this; + ret.context = this.context; + + // Return the newly-formed element set + return ret; + }, + + // Execute a callback for every element in the matched set. + each: function( callback ) { + return jQuery.each( this, callback ); + }, + + map: function( callback ) { + return this.pushStack( jQuery.map( this, function( elem, i ) { + return callback.call( elem, i, elem ); + } ) ); + }, + + slice: function() { + return this.pushStack( slice.apply( this, arguments ) ); + }, + + first: function() { + return this.eq( 0 ); + }, + + last: function() { + return this.eq( -1 ); + }, + + eq: function( i ) { + var len = this.length, + j = +i + ( i < 0 ? len : 0 ); + return this.pushStack( j >= 0 && j < len ? [ this[ j ] ] : [] ); + }, + + end: function() { + return this.prevObject || this.constructor(); + }, + + // For internal use only. + // Behaves like an Array's method, not like a jQuery method. + push: push, + sort: deletedIds.sort, + splice: deletedIds.splice +}; + +jQuery.extend = jQuery.fn.extend = function() { + var src, copyIsArray, copy, name, options, clone, + target = arguments[ 0 ] || {}, + i = 1, + length = arguments.length, + deep = false; + + // Handle a deep copy situation + if ( typeof target === "boolean" ) { + deep = target; + + // skip the boolean and the target + target = arguments[ i ] || {}; + i++; + } + + // Handle case when target is a string or something (possible in deep copy) + if ( typeof target !== "object" && !jQuery.isFunction( target ) ) { + target = {}; + } + + // extend jQuery itself if only one argument is passed + if ( i === length ) { + target = this; + i--; + } + + for ( ; i < length; i++ ) { + + // Only deal with non-null/undefined values + if ( ( options = arguments[ i ] ) != null ) { + + // Extend the base object + for ( name in options ) { + src = target[ name ]; + copy = options[ name ]; + + // Prevent never-ending loop + if ( target === copy ) { + continue; + } + + // Recurse if we're merging plain objects or arrays + if ( deep && copy && ( jQuery.isPlainObject( copy ) || + ( copyIsArray = jQuery.isArray( copy ) ) ) ) { + + if ( copyIsArray ) { + copyIsArray = false; + clone = src && jQuery.isArray( src ) ? src : []; + + } else { + clone = src && jQuery.isPlainObject( src ) ? src : {}; + } + + // Never move original objects, clone them + target[ name ] = jQuery.extend( deep, clone, copy ); + + // Don't bring in undefined values + } else if ( copy !== undefined ) { + target[ name ] = copy; + } + } + } + } + + // Return the modified object + return target; +}; + +jQuery.extend( { + + // Unique for each copy of jQuery on the page + expando: "jQuery" + ( version + Math.random() ).replace( /\D/g, "" ), + + // Assume jQuery is ready without the ready module + isReady: true, + + error: function( msg ) { + throw new Error( msg ); + }, + + noop: function() {}, + + // See test/unit/core.js for details concerning isFunction. + // Since version 1.3, DOM methods and functions like alert + // aren't supported. They return false on IE (#2968). + isFunction: function( obj ) { + return jQuery.type( obj ) === "function"; + }, + + isArray: Array.isArray || function( obj ) { + return jQuery.type( obj ) === "array"; + }, + + isWindow: function( obj ) { + /* jshint eqeqeq: false */ + return obj != null && obj == obj.window; + }, + + isNumeric: function( obj ) { + + // parseFloat NaNs numeric-cast false positives (null|true|false|"") + // ...but misinterprets leading-number strings, particularly hex literals ("0x...") + // subtraction forces infinities to NaN + // adding 1 corrects loss of precision from parseFloat (#15100) + var realStringObj = obj && obj.toString(); + return !jQuery.isArray( obj ) && ( realStringObj - parseFloat( realStringObj ) + 1 ) >= 0; + }, + + isEmptyObject: function( obj ) { + var name; + for ( name in obj ) { + return false; + } + return true; + }, + + isPlainObject: function( obj ) { + var key; + + // Must be an Object. + // Because of IE, we also have to check the presence of the constructor property. + // Make sure that DOM nodes and window objects don't pass through, as well + if ( !obj || jQuery.type( obj ) !== "object" || obj.nodeType || jQuery.isWindow( obj ) ) { + return false; + } + + try { + + // Not own constructor property must be Object + if ( obj.constructor && + !hasOwn.call( obj, "constructor" ) && + !hasOwn.call( obj.constructor.prototype, "isPrototypeOf" ) ) { + return false; + } + } catch ( e ) { + + // IE8,9 Will throw exceptions on certain host objects #9897 + return false; + } + + // Support: IE<9 + // Handle iteration over inherited properties before own properties. + if ( !support.ownFirst ) { + for ( key in obj ) { + return hasOwn.call( obj, key ); + } + } + + // Own properties are enumerated firstly, so to speed up, + // if last one is own, then all properties are own. + for ( key in obj ) {} + + return key === undefined || hasOwn.call( obj, key ); + }, + + type: function( obj ) { + if ( obj == null ) { + return obj + ""; + } + return typeof obj === "object" || typeof obj === "function" ? + class2type[ toString.call( obj ) ] || "object" : + typeof obj; + }, + + // Workarounds based on findings by Jim Driscoll + // http://weblogs.java.net/blog/driscoll/archive/2009/09/08/eval-javascript-global-context + globalEval: function( data ) { + if ( data && jQuery.trim( data ) ) { + + // We use execScript on Internet Explorer + // We use an anonymous function so that context is window + // rather than jQuery in Firefox + ( window.execScript || function( data ) { + window[ "eval" ].call( window, data ); // jscs:ignore requireDotNotation + } )( data ); + } + }, + + // Convert dashed to camelCase; used by the css and data modules + // Microsoft forgot to hump their vendor prefix (#9572) + camelCase: function( string ) { + return string.replace( rmsPrefix, "ms-" ).replace( rdashAlpha, fcamelCase ); + }, + + nodeName: function( elem, name ) { + return elem.nodeName && elem.nodeName.toLowerCase() === name.toLowerCase(); + }, + + each: function( obj, callback ) { + var length, i = 0; + + if ( isArrayLike( obj ) ) { + length = obj.length; + for ( ; i < length; i++ ) { + if ( callback.call( obj[ i ], i, obj[ i ] ) === false ) { + break; + } + } + } else { + for ( i in obj ) { + if ( callback.call( obj[ i ], i, obj[ i ] ) === false ) { + break; + } + } + } + + return obj; + }, + + // Support: Android<4.1, IE<9 + trim: function( text ) { + return text == null ? + "" : + ( text + "" ).replace( rtrim, "" ); + }, + + // results is for internal usage only + makeArray: function( arr, results ) { + var ret = results || []; + + if ( arr != null ) { + if ( isArrayLike( Object( arr ) ) ) { + jQuery.merge( ret, + typeof arr === "string" ? + [ arr ] : arr + ); + } else { + push.call( ret, arr ); + } + } + + return ret; + }, + + inArray: function( elem, arr, i ) { + var len; + + if ( arr ) { + if ( indexOf ) { + return indexOf.call( arr, elem, i ); + } + + len = arr.length; + i = i ? i < 0 ? Math.max( 0, len + i ) : i : 0; + + for ( ; i < len; i++ ) { + + // Skip accessing in sparse arrays + if ( i in arr && arr[ i ] === elem ) { + return i; + } + } + } + + return -1; + }, + + merge: function( first, second ) { + var len = +second.length, + j = 0, + i = first.length; + + while ( j < len ) { + first[ i++ ] = second[ j++ ]; + } + + // Support: IE<9 + // Workaround casting of .length to NaN on otherwise arraylike objects (e.g., NodeLists) + if ( len !== len ) { + while ( second[ j ] !== undefined ) { + first[ i++ ] = second[ j++ ]; + } + } + + first.length = i; + + return first; + }, + + grep: function( elems, callback, invert ) { + var callbackInverse, + matches = [], + i = 0, + length = elems.length, + callbackExpect = !invert; + + // Go through the array, only saving the items + // that pass the validator function + for ( ; i < length; i++ ) { + callbackInverse = !callback( elems[ i ], i ); + if ( callbackInverse !== callbackExpect ) { + matches.push( elems[ i ] ); + } + } + + return matches; + }, + + // arg is for internal usage only + map: function( elems, callback, arg ) { + var length, value, + i = 0, + ret = []; + + // Go through the array, translating each of the items to their new values + if ( isArrayLike( elems ) ) { + length = elems.length; + for ( ; i < length; i++ ) { + value = callback( elems[ i ], i, arg ); + + if ( value != null ) { + ret.push( value ); + } + } + + // Go through every key on the object, + } else { + for ( i in elems ) { + value = callback( elems[ i ], i, arg ); + + if ( value != null ) { + ret.push( value ); + } + } + } + + // Flatten any nested arrays + return concat.apply( [], ret ); + }, + + // A global GUID counter for objects + guid: 1, + + // Bind a function to a context, optionally partially applying any + // arguments. + proxy: function( fn, context ) { + var args, proxy, tmp; + + if ( typeof context === "string" ) { + tmp = fn[ context ]; + context = fn; + fn = tmp; + } + + // Quick check to determine if target is callable, in the spec + // this throws a TypeError, but we will just return undefined. + if ( !jQuery.isFunction( fn ) ) { + return undefined; + } + + // Simulated bind + args = slice.call( arguments, 2 ); + proxy = function() { + return fn.apply( context || this, args.concat( slice.call( arguments ) ) ); + }; + + // Set the guid of unique handler to the same of original handler, so it can be removed + proxy.guid = fn.guid = fn.guid || jQuery.guid++; + + return proxy; + }, + + now: function() { + return +( new Date() ); + }, + + // jQuery.support is not used in Core but other projects attach their + // properties to it so it needs to exist. + support: support +} ); + +// JSHint would error on this code due to the Symbol not being defined in ES5. +// Defining this global in .jshintrc would create a danger of using the global +// unguarded in another place, it seems safer to just disable JSHint for these +// three lines. +/* jshint ignore: start */ +if ( typeof Symbol === "function" ) { + jQuery.fn[ Symbol.iterator ] = deletedIds[ Symbol.iterator ]; +} +/* jshint ignore: end */ + +// Populate the class2type map +jQuery.each( "Boolean Number String Function Array Date RegExp Object Error Symbol".split( " " ), +function( i, name ) { + class2type[ "[object " + name + "]" ] = name.toLowerCase(); +} ); + +function isArrayLike( obj ) { + + // Support: iOS 8.2 (not reproducible in simulator) + // `in` check used to prevent JIT error (gh-2145) + // hasOwn isn't used here due to false negatives + // regarding Nodelist length in IE + var length = !!obj && "length" in obj && obj.length, + type = jQuery.type( obj ); + + if ( type === "function" || jQuery.isWindow( obj ) ) { + return false; + } + + return type === "array" || length === 0 || + typeof length === "number" && length > 0 && ( length - 1 ) in obj; +} +var Sizzle = +/*! + * Sizzle CSS Selector Engine v2.2.1 + * http://sizzlejs.com/ + * + * Copyright jQuery Foundation and other contributors + * Released under the MIT license + * http://jquery.org/license + * + * Date: 2015-10-17 + */ +(function( window ) { + +var i, + support, + Expr, + getText, + isXML, + tokenize, + compile, + select, + outermostContext, + sortInput, + hasDuplicate, + + // Local document vars + setDocument, + document, + docElem, + documentIsHTML, + rbuggyQSA, + rbuggyMatches, + matches, + contains, + + // Instance-specific data + expando = "sizzle" + 1 * new Date(), + preferredDoc = window.document, + dirruns = 0, + done = 0, + classCache = createCache(), + tokenCache = createCache(), + compilerCache = createCache(), + sortOrder = function( a, b ) { + if ( a === b ) { + hasDuplicate = true; + } + return 0; + }, + + // General-purpose constants + MAX_NEGATIVE = 1 << 31, + + // Instance methods + hasOwn = ({}).hasOwnProperty, + arr = [], + pop = arr.pop, + push_native = arr.push, + push = arr.push, + slice = arr.slice, + // Use a stripped-down indexOf as it's faster than native + // http://jsperf.com/thor-indexof-vs-for/5 + indexOf = function( list, elem ) { + var i = 0, + len = list.length; + for ( ; i < len; i++ ) { + if ( list[i] === elem ) { + return i; + } + } + return -1; + }, + + booleans = "checked|selected|async|autofocus|autoplay|controls|defer|disabled|hidden|ismap|loop|multiple|open|readonly|required|scoped", + + // Regular expressions + + // http://www.w3.org/TR/css3-selectors/#whitespace + whitespace = "[\\x20\\t\\r\\n\\f]", + + // http://www.w3.org/TR/CSS21/syndata.html#value-def-identifier + identifier = "(?:\\\\.|[\\w-]|[^\\x00-\\xa0])+", + + // Attribute selectors: http://www.w3.org/TR/selectors/#attribute-selectors + attributes = "\\[" + whitespace + "*(" + identifier + ")(?:" + whitespace + + // Operator (capture 2) + "*([*^$|!~]?=)" + whitespace + + // "Attribute values must be CSS identifiers [capture 5] or strings [capture 3 or capture 4]" + "*(?:'((?:\\\\.|[^\\\\'])*)'|\"((?:\\\\.|[^\\\\\"])*)\"|(" + identifier + "))|)" + whitespace + + "*\\]", + + pseudos = ":(" + identifier + ")(?:\\((" + + // To reduce the number of selectors needing tokenize in the preFilter, prefer arguments: + // 1. quoted (capture 3; capture 4 or capture 5) + "('((?:\\\\.|[^\\\\'])*)'|\"((?:\\\\.|[^\\\\\"])*)\")|" + + // 2. simple (capture 6) + "((?:\\\\.|[^\\\\()[\\]]|" + attributes + ")*)|" + + // 3. anything else (capture 2) + ".*" + + ")\\)|)", + + // Leading and non-escaped trailing whitespace, capturing some non-whitespace characters preceding the latter + rwhitespace = new RegExp( whitespace + "+", "g" ), + rtrim = new RegExp( "^" + whitespace + "+|((?:^|[^\\\\])(?:\\\\.)*)" + whitespace + "+$", "g" ), + + rcomma = new RegExp( "^" + whitespace + "*," + whitespace + "*" ), + rcombinators = new RegExp( "^" + whitespace + "*([>+~]|" + whitespace + ")" + whitespace + "*" ), + + rattributeQuotes = new RegExp( "=" + whitespace + "*([^\\]'\"]*?)" + whitespace + "*\\]", "g" ), + + rpseudo = new RegExp( pseudos ), + ridentifier = new RegExp( "^" + identifier + "$" ), + + matchExpr = { + "ID": new RegExp( "^#(" + identifier + ")" ), + "CLASS": new RegExp( "^\\.(" + identifier + ")" ), + "TAG": new RegExp( "^(" + identifier + "|[*])" ), + "ATTR": new RegExp( "^" + attributes ), + "PSEUDO": new RegExp( "^" + pseudos ), + "CHILD": new RegExp( "^:(only|first|last|nth|nth-last)-(child|of-type)(?:\\(" + whitespace + + "*(even|odd|(([+-]|)(\\d*)n|)" + whitespace + "*(?:([+-]|)" + whitespace + + "*(\\d+)|))" + whitespace + "*\\)|)", "i" ), + "bool": new RegExp( "^(?:" + booleans + ")$", "i" ), + // For use in libraries implementing .is() + // We use this for POS matching in `select` + "needsContext": new RegExp( "^" + whitespace + "*[>+~]|:(even|odd|eq|gt|lt|nth|first|last)(?:\\(" + + whitespace + "*((?:-\\d)?\\d*)" + whitespace + "*\\)|)(?=[^-]|$)", "i" ) + }, + + rinputs = /^(?:input|select|textarea|button)$/i, + rheader = /^h\d$/i, + + rnative = /^[^{]+\{\s*\[native \w/, + + // Easily-parseable/retrievable ID or TAG or CLASS selectors + rquickExpr = /^(?:#([\w-]+)|(\w+)|\.([\w-]+))$/, + + rsibling = /[+~]/, + rescape = /'|\\/g, + + // CSS escapes http://www.w3.org/TR/CSS21/syndata.html#escaped-characters + runescape = new RegExp( "\\\\([\\da-f]{1,6}" + whitespace + "?|(" + whitespace + ")|.)", "ig" ), + funescape = function( _, escaped, escapedWhitespace ) { + var high = "0x" + escaped - 0x10000; + // NaN means non-codepoint + // Support: Firefox<24 + // Workaround erroneous numeric interpretation of +"0x" + return high !== high || escapedWhitespace ? + escaped : + high < 0 ? + // BMP codepoint + String.fromCharCode( high + 0x10000 ) : + // Supplemental Plane codepoint (surrogate pair) + String.fromCharCode( high >> 10 | 0xD800, high & 0x3FF | 0xDC00 ); + }, + + // Used for iframes + // See setDocument() + // Removing the function wrapper causes a "Permission Denied" + // error in IE + unloadHandler = function() { + setDocument(); + }; + +// Optimize for push.apply( _, NodeList ) +try { + push.apply( + (arr = slice.call( preferredDoc.childNodes )), + preferredDoc.childNodes + ); + // Support: Android<4.0 + // Detect silently failing push.apply + arr[ preferredDoc.childNodes.length ].nodeType; +} catch ( e ) { + push = { apply: arr.length ? + + // Leverage slice if possible + function( target, els ) { + push_native.apply( target, slice.call(els) ); + } : + + // Support: IE<9 + // Otherwise append directly + function( target, els ) { + var j = target.length, + i = 0; + // Can't trust NodeList.length + while ( (target[j++] = els[i++]) ) {} + target.length = j - 1; + } + }; +} + +function Sizzle( selector, context, results, seed ) { + var m, i, elem, nid, nidselect, match, groups, newSelector, + newContext = context && context.ownerDocument, + + // nodeType defaults to 9, since context defaults to document + nodeType = context ? context.nodeType : 9; + + results = results || []; + + // Return early from calls with invalid selector or context + if ( typeof selector !== "string" || !selector || + nodeType !== 1 && nodeType !== 9 && nodeType !== 11 ) { + + return results; + } + + // Try to shortcut find operations (as opposed to filters) in HTML documents + if ( !seed ) { + + if ( ( context ? context.ownerDocument || context : preferredDoc ) !== document ) { + setDocument( context ); + } + context = context || document; + + if ( documentIsHTML ) { + + // If the selector is sufficiently simple, try using a "get*By*" DOM method + // (excepting DocumentFragment context, where the methods don't exist) + if ( nodeType !== 11 && (match = rquickExpr.exec( selector )) ) { + + // ID selector + if ( (m = match[1]) ) { + + // Document context + if ( nodeType === 9 ) { + if ( (elem = context.getElementById( m )) ) { + + // Support: IE, Opera, Webkit + // TODO: identify versions + // getElementById can match elements by name instead of ID + if ( elem.id === m ) { + results.push( elem ); + return results; + } + } else { + return results; + } + + // Element context + } else { + + // Support: IE, Opera, Webkit + // TODO: identify versions + // getElementById can match elements by name instead of ID + if ( newContext && (elem = newContext.getElementById( m )) && + contains( context, elem ) && + elem.id === m ) { + + results.push( elem ); + return results; + } + } + + // Type selector + } else if ( match[2] ) { + push.apply( results, context.getElementsByTagName( selector ) ); + return results; + + // Class selector + } else if ( (m = match[3]) && support.getElementsByClassName && + context.getElementsByClassName ) { + + push.apply( results, context.getElementsByClassName( m ) ); + return results; + } + } + + // Take advantage of querySelectorAll + if ( support.qsa && + !compilerCache[ selector + " " ] && + (!rbuggyQSA || !rbuggyQSA.test( selector )) ) { + + if ( nodeType !== 1 ) { + newContext = context; + newSelector = selector; + + // qSA looks outside Element context, which is not what we want + // Thanks to Andrew Dupont for this workaround technique + // Support: IE <=8 + // Exclude object elements + } else if ( context.nodeName.toLowerCase() !== "object" ) { + + // Capture the context ID, setting it first if necessary + if ( (nid = context.getAttribute( "id" )) ) { + nid = nid.replace( rescape, "\\$&" ); + } else { + context.setAttribute( "id", (nid = expando) ); + } + + // Prefix every selector in the list + groups = tokenize( selector ); + i = groups.length; + nidselect = ridentifier.test( nid ) ? "#" + nid : "[id='" + nid + "']"; + while ( i-- ) { + groups[i] = nidselect + " " + toSelector( groups[i] ); + } + newSelector = groups.join( "," ); + + // Expand context for sibling selectors + newContext = rsibling.test( selector ) && testContext( context.parentNode ) || + context; + } + + if ( newSelector ) { + try { + push.apply( results, + newContext.querySelectorAll( newSelector ) + ); + return results; + } catch ( qsaError ) { + } finally { + if ( nid === expando ) { + context.removeAttribute( "id" ); + } + } + } + } + } + } + + // All others + return select( selector.replace( rtrim, "$1" ), context, results, seed ); +} + +/** + * Create key-value caches of limited size + * @returns {function(string, object)} Returns the Object data after storing it on itself with + * property name the (space-suffixed) string and (if the cache is larger than Expr.cacheLength) + * deleting the oldest entry + */ +function createCache() { + var keys = []; + + function cache( key, value ) { + // Use (key + " ") to avoid collision with native prototype properties (see Issue #157) + if ( keys.push( key + " " ) > Expr.cacheLength ) { + // Only keep the most recent entries + delete cache[ keys.shift() ]; + } + return (cache[ key + " " ] = value); + } + return cache; +} + +/** + * Mark a function for special use by Sizzle + * @param {Function} fn The function to mark + */ +function markFunction( fn ) { + fn[ expando ] = true; + return fn; +} + +/** + * Support testing using an element + * @param {Function} fn Passed the created div and expects a boolean result + */ +function assert( fn ) { + var div = document.createElement("div"); + + try { + return !!fn( div ); + } catch (e) { + return false; + } finally { + // Remove from its parent by default + if ( div.parentNode ) { + div.parentNode.removeChild( div ); + } + // release memory in IE + div = null; + } +} + +/** + * Adds the same handler for all of the specified attrs + * @param {String} attrs Pipe-separated list of attributes + * @param {Function} handler The method that will be applied + */ +function addHandle( attrs, handler ) { + var arr = attrs.split("|"), + i = arr.length; + + while ( i-- ) { + Expr.attrHandle[ arr[i] ] = handler; + } +} + +/** + * Checks document order of two siblings + * @param {Element} a + * @param {Element} b + * @returns {Number} Returns less than 0 if a precedes b, greater than 0 if a follows b + */ +function siblingCheck( a, b ) { + var cur = b && a, + diff = cur && a.nodeType === 1 && b.nodeType === 1 && + ( ~b.sourceIndex || MAX_NEGATIVE ) - + ( ~a.sourceIndex || MAX_NEGATIVE ); + + // Use IE sourceIndex if available on both nodes + if ( diff ) { + return diff; + } + + // Check if b follows a + if ( cur ) { + while ( (cur = cur.nextSibling) ) { + if ( cur === b ) { + return -1; + } + } + } + + return a ? 1 : -1; +} + +/** + * Returns a function to use in pseudos for input types + * @param {String} type + */ +function createInputPseudo( type ) { + return function( elem ) { + var name = elem.nodeName.toLowerCase(); + return name === "input" && elem.type === type; + }; +} + +/** + * Returns a function to use in pseudos for buttons + * @param {String} type + */ +function createButtonPseudo( type ) { + return function( elem ) { + var name = elem.nodeName.toLowerCase(); + return (name === "input" || name === "button") && elem.type === type; + }; +} + +/** + * Returns a function to use in pseudos for positionals + * @param {Function} fn + */ +function createPositionalPseudo( fn ) { + return markFunction(function( argument ) { + argument = +argument; + return markFunction(function( seed, matches ) { + var j, + matchIndexes = fn( [], seed.length, argument ), + i = matchIndexes.length; + + // Match elements found at the specified indexes + while ( i-- ) { + if ( seed[ (j = matchIndexes[i]) ] ) { + seed[j] = !(matches[j] = seed[j]); + } + } + }); + }); +} + +/** + * Checks a node for validity as a Sizzle context + * @param {Element|Object=} context + * @returns {Element|Object|Boolean} The input node if acceptable, otherwise a falsy value + */ +function testContext( context ) { + return context && typeof context.getElementsByTagName !== "undefined" && context; +} + +// Expose support vars for convenience +support = Sizzle.support = {}; + +/** + * Detects XML nodes + * @param {Element|Object} elem An element or a document + * @returns {Boolean} True iff elem is a non-HTML XML node + */ +isXML = Sizzle.isXML = function( elem ) { + // documentElement is verified for cases where it doesn't yet exist + // (such as loading iframes in IE - #4833) + var documentElement = elem && (elem.ownerDocument || elem).documentElement; + return documentElement ? documentElement.nodeName !== "HTML" : false; +}; + +/** + * Sets document-related variables once based on the current document + * @param {Element|Object} [doc] An element or document object to use to set the document + * @returns {Object} Returns the current document + */ +setDocument = Sizzle.setDocument = function( node ) { + var hasCompare, parent, + doc = node ? node.ownerDocument || node : preferredDoc; + + // Return early if doc is invalid or already selected + if ( doc === document || doc.nodeType !== 9 || !doc.documentElement ) { + return document; + } + + // Update global variables + document = doc; + docElem = document.documentElement; + documentIsHTML = !isXML( document ); + + // Support: IE 9-11, Edge + // Accessing iframe documents after unload throws "permission denied" errors (jQuery #13936) + if ( (parent = document.defaultView) && parent.top !== parent ) { + // Support: IE 11 + if ( parent.addEventListener ) { + parent.addEventListener( "unload", unloadHandler, false ); + + // Support: IE 9 - 10 only + } else if ( parent.attachEvent ) { + parent.attachEvent( "onunload", unloadHandler ); + } + } + + /* Attributes + ---------------------------------------------------------------------- */ + + // Support: IE<8 + // Verify that getAttribute really returns attributes and not properties + // (excepting IE8 booleans) + support.attributes = assert(function( div ) { + div.className = "i"; + return !div.getAttribute("className"); + }); + + /* getElement(s)By* + ---------------------------------------------------------------------- */ + + // Check if getElementsByTagName("*") returns only elements + support.getElementsByTagName = assert(function( div ) { + div.appendChild( document.createComment("") ); + return !div.getElementsByTagName("*").length; + }); + + // Support: IE<9 + support.getElementsByClassName = rnative.test( document.getElementsByClassName ); + + // Support: IE<10 + // Check if getElementById returns elements by name + // The broken getElementById methods don't pick up programatically-set names, + // so use a roundabout getElementsByName test + support.getById = assert(function( div ) { + docElem.appendChild( div ).id = expando; + return !document.getElementsByName || !document.getElementsByName( expando ).length; + }); + + // ID find and filter + if ( support.getById ) { + Expr.find["ID"] = function( id, context ) { + if ( typeof context.getElementById !== "undefined" && documentIsHTML ) { + var m = context.getElementById( id ); + return m ? [ m ] : []; + } + }; + Expr.filter["ID"] = function( id ) { + var attrId = id.replace( runescape, funescape ); + return function( elem ) { + return elem.getAttribute("id") === attrId; + }; + }; + } else { + // Support: IE6/7 + // getElementById is not reliable as a find shortcut + delete Expr.find["ID"]; + + Expr.filter["ID"] = function( id ) { + var attrId = id.replace( runescape, funescape ); + return function( elem ) { + var node = typeof elem.getAttributeNode !== "undefined" && + elem.getAttributeNode("id"); + return node && node.value === attrId; + }; + }; + } + + // Tag + Expr.find["TAG"] = support.getElementsByTagName ? + function( tag, context ) { + if ( typeof context.getElementsByTagName !== "undefined" ) { + return context.getElementsByTagName( tag ); + + // DocumentFragment nodes don't have gEBTN + } else if ( support.qsa ) { + return context.querySelectorAll( tag ); + } + } : + + function( tag, context ) { + var elem, + tmp = [], + i = 0, + // By happy coincidence, a (broken) gEBTN appears on DocumentFragment nodes too + results = context.getElementsByTagName( tag ); + + // Filter out possible comments + if ( tag === "*" ) { + while ( (elem = results[i++]) ) { + if ( elem.nodeType === 1 ) { + tmp.push( elem ); + } + } + + return tmp; + } + return results; + }; + + // Class + Expr.find["CLASS"] = support.getElementsByClassName && function( className, context ) { + if ( typeof context.getElementsByClassName !== "undefined" && documentIsHTML ) { + return context.getElementsByClassName( className ); + } + }; + + /* QSA/matchesSelector + ---------------------------------------------------------------------- */ + + // QSA and matchesSelector support + + // matchesSelector(:active) reports false when true (IE9/Opera 11.5) + rbuggyMatches = []; + + // qSa(:focus) reports false when true (Chrome 21) + // We allow this because of a bug in IE8/9 that throws an error + // whenever `document.activeElement` is accessed on an iframe + // So, we allow :focus to pass through QSA all the time to avoid the IE error + // See http://bugs.jquery.com/ticket/13378 + rbuggyQSA = []; + + if ( (support.qsa = rnative.test( document.querySelectorAll )) ) { + // Build QSA regex + // Regex strategy adopted from Diego Perini + assert(function( div ) { + // Select is set to empty string on purpose + // This is to test IE's treatment of not explicitly + // setting a boolean content attribute, + // since its presence should be enough + // http://bugs.jquery.com/ticket/12359 + docElem.appendChild( div ).innerHTML = "" + + ""; + + // Support: IE8, Opera 11-12.16 + // Nothing should be selected when empty strings follow ^= or $= or *= + // The test attribute must be unknown in Opera but "safe" for WinRT + // http://msdn.microsoft.com/en-us/library/ie/hh465388.aspx#attribute_section + if ( div.querySelectorAll("[msallowcapture^='']").length ) { + rbuggyQSA.push( "[*^$]=" + whitespace + "*(?:''|\"\")" ); + } + + // Support: IE8 + // Boolean attributes and "value" are not treated correctly + if ( !div.querySelectorAll("[selected]").length ) { + rbuggyQSA.push( "\\[" + whitespace + "*(?:value|" + booleans + ")" ); + } + + // Support: Chrome<29, Android<4.4, Safari<7.0+, iOS<7.0+, PhantomJS<1.9.8+ + if ( !div.querySelectorAll( "[id~=" + expando + "-]" ).length ) { + rbuggyQSA.push("~="); + } + + // Webkit/Opera - :checked should return selected option elements + // http://www.w3.org/TR/2011/REC-css3-selectors-20110929/#checked + // IE8 throws error here and will not see later tests + if ( !div.querySelectorAll(":checked").length ) { + rbuggyQSA.push(":checked"); + } + + // Support: Safari 8+, iOS 8+ + // https://bugs.webkit.org/show_bug.cgi?id=136851 + // In-page `selector#id sibing-combinator selector` fails + if ( !div.querySelectorAll( "a#" + expando + "+*" ).length ) { + rbuggyQSA.push(".#.+[+~]"); + } + }); + + assert(function( div ) { + // Support: Windows 8 Native Apps + // The type and name attributes are restricted during .innerHTML assignment + var input = document.createElement("input"); + input.setAttribute( "type", "hidden" ); + div.appendChild( input ).setAttribute( "name", "D" ); + + // Support: IE8 + // Enforce case-sensitivity of name attribute + if ( div.querySelectorAll("[name=d]").length ) { + rbuggyQSA.push( "name" + whitespace + "*[*^$|!~]?=" ); + } + + // FF 3.5 - :enabled/:disabled and hidden elements (hidden elements are still enabled) + // IE8 throws error here and will not see later tests + if ( !div.querySelectorAll(":enabled").length ) { + rbuggyQSA.push( ":enabled", ":disabled" ); + } + + // Opera 10-11 does not throw on post-comma invalid pseudos + div.querySelectorAll("*,:x"); + rbuggyQSA.push(",.*:"); + }); + } + + if ( (support.matchesSelector = rnative.test( (matches = docElem.matches || + docElem.webkitMatchesSelector || + docElem.mozMatchesSelector || + docElem.oMatchesSelector || + docElem.msMatchesSelector) )) ) { + + assert(function( div ) { + // Check to see if it's possible to do matchesSelector + // on a disconnected node (IE 9) + support.disconnectedMatch = matches.call( div, "div" ); + + // This should fail with an exception + // Gecko does not error, returns false instead + matches.call( div, "[s!='']:x" ); + rbuggyMatches.push( "!=", pseudos ); + }); + } + + rbuggyQSA = rbuggyQSA.length && new RegExp( rbuggyQSA.join("|") ); + rbuggyMatches = rbuggyMatches.length && new RegExp( rbuggyMatches.join("|") ); + + /* Contains + ---------------------------------------------------------------------- */ + hasCompare = rnative.test( docElem.compareDocumentPosition ); + + // Element contains another + // Purposefully self-exclusive + // As in, an element does not contain itself + contains = hasCompare || rnative.test( docElem.contains ) ? + function( a, b ) { + var adown = a.nodeType === 9 ? a.documentElement : a, + bup = b && b.parentNode; + return a === bup || !!( bup && bup.nodeType === 1 && ( + adown.contains ? + adown.contains( bup ) : + a.compareDocumentPosition && a.compareDocumentPosition( bup ) & 16 + )); + } : + function( a, b ) { + if ( b ) { + while ( (b = b.parentNode) ) { + if ( b === a ) { + return true; + } + } + } + return false; + }; + + /* Sorting + ---------------------------------------------------------------------- */ + + // Document order sorting + sortOrder = hasCompare ? + function( a, b ) { + + // Flag for duplicate removal + if ( a === b ) { + hasDuplicate = true; + return 0; + } + + // Sort on method existence if only one input has compareDocumentPosition + var compare = !a.compareDocumentPosition - !b.compareDocumentPosition; + if ( compare ) { + return compare; + } + + // Calculate position if both inputs belong to the same document + compare = ( a.ownerDocument || a ) === ( b.ownerDocument || b ) ? + a.compareDocumentPosition( b ) : + + // Otherwise we know they are disconnected + 1; + + // Disconnected nodes + if ( compare & 1 || + (!support.sortDetached && b.compareDocumentPosition( a ) === compare) ) { + + // Choose the first element that is related to our preferred document + if ( a === document || a.ownerDocument === preferredDoc && contains(preferredDoc, a) ) { + return -1; + } + if ( b === document || b.ownerDocument === preferredDoc && contains(preferredDoc, b) ) { + return 1; + } + + // Maintain original order + return sortInput ? + ( indexOf( sortInput, a ) - indexOf( sortInput, b ) ) : + 0; + } + + return compare & 4 ? -1 : 1; + } : + function( a, b ) { + // Exit early if the nodes are identical + if ( a === b ) { + hasDuplicate = true; + return 0; + } + + var cur, + i = 0, + aup = a.parentNode, + bup = b.parentNode, + ap = [ a ], + bp = [ b ]; + + // Parentless nodes are either documents or disconnected + if ( !aup || !bup ) { + return a === document ? -1 : + b === document ? 1 : + aup ? -1 : + bup ? 1 : + sortInput ? + ( indexOf( sortInput, a ) - indexOf( sortInput, b ) ) : + 0; + + // If the nodes are siblings, we can do a quick check + } else if ( aup === bup ) { + return siblingCheck( a, b ); + } + + // Otherwise we need full lists of their ancestors for comparison + cur = a; + while ( (cur = cur.parentNode) ) { + ap.unshift( cur ); + } + cur = b; + while ( (cur = cur.parentNode) ) { + bp.unshift( cur ); + } + + // Walk down the tree looking for a discrepancy + while ( ap[i] === bp[i] ) { + i++; + } + + return i ? + // Do a sibling check if the nodes have a common ancestor + siblingCheck( ap[i], bp[i] ) : + + // Otherwise nodes in our document sort first + ap[i] === preferredDoc ? -1 : + bp[i] === preferredDoc ? 1 : + 0; + }; + + return document; +}; + +Sizzle.matches = function( expr, elements ) { + return Sizzle( expr, null, null, elements ); +}; + +Sizzle.matchesSelector = function( elem, expr ) { + // Set document vars if needed + if ( ( elem.ownerDocument || elem ) !== document ) { + setDocument( elem ); + } + + // Make sure that attribute selectors are quoted + expr = expr.replace( rattributeQuotes, "='$1']" ); + + if ( support.matchesSelector && documentIsHTML && + !compilerCache[ expr + " " ] && + ( !rbuggyMatches || !rbuggyMatches.test( expr ) ) && + ( !rbuggyQSA || !rbuggyQSA.test( expr ) ) ) { + + try { + var ret = matches.call( elem, expr ); + + // IE 9's matchesSelector returns false on disconnected nodes + if ( ret || support.disconnectedMatch || + // As well, disconnected nodes are said to be in a document + // fragment in IE 9 + elem.document && elem.document.nodeType !== 11 ) { + return ret; + } + } catch (e) {} + } + + return Sizzle( expr, document, null, [ elem ] ).length > 0; +}; + +Sizzle.contains = function( context, elem ) { + // Set document vars if needed + if ( ( context.ownerDocument || context ) !== document ) { + setDocument( context ); + } + return contains( context, elem ); +}; + +Sizzle.attr = function( elem, name ) { + // Set document vars if needed + if ( ( elem.ownerDocument || elem ) !== document ) { + setDocument( elem ); + } + + var fn = Expr.attrHandle[ name.toLowerCase() ], + // Don't get fooled by Object.prototype properties (jQuery #13807) + val = fn && hasOwn.call( Expr.attrHandle, name.toLowerCase() ) ? + fn( elem, name, !documentIsHTML ) : + undefined; + + return val !== undefined ? + val : + support.attributes || !documentIsHTML ? + elem.getAttribute( name ) : + (val = elem.getAttributeNode(name)) && val.specified ? + val.value : + null; +}; + +Sizzle.error = function( msg ) { + throw new Error( "Syntax error, unrecognized expression: " + msg ); +}; + +/** + * Document sorting and removing duplicates + * @param {ArrayLike} results + */ +Sizzle.uniqueSort = function( results ) { + var elem, + duplicates = [], + j = 0, + i = 0; + + // Unless we *know* we can detect duplicates, assume their presence + hasDuplicate = !support.detectDuplicates; + sortInput = !support.sortStable && results.slice( 0 ); + results.sort( sortOrder ); + + if ( hasDuplicate ) { + while ( (elem = results[i++]) ) { + if ( elem === results[ i ] ) { + j = duplicates.push( i ); + } + } + while ( j-- ) { + results.splice( duplicates[ j ], 1 ); + } + } + + // Clear input after sorting to release objects + // See https://github.com/jquery/sizzle/pull/225 + sortInput = null; + + return results; +}; + +/** + * Utility function for retrieving the text value of an array of DOM nodes + * @param {Array|Element} elem + */ +getText = Sizzle.getText = function( elem ) { + var node, + ret = "", + i = 0, + nodeType = elem.nodeType; + + if ( !nodeType ) { + // If no nodeType, this is expected to be an array + while ( (node = elem[i++]) ) { + // Do not traverse comment nodes + ret += getText( node ); + } + } else if ( nodeType === 1 || nodeType === 9 || nodeType === 11 ) { + // Use textContent for elements + // innerText usage removed for consistency of new lines (jQuery #11153) + if ( typeof elem.textContent === "string" ) { + return elem.textContent; + } else { + // Traverse its children + for ( elem = elem.firstChild; elem; elem = elem.nextSibling ) { + ret += getText( elem ); + } + } + } else if ( nodeType === 3 || nodeType === 4 ) { + return elem.nodeValue; + } + // Do not include comment or processing instruction nodes + + return ret; +}; + +Expr = Sizzle.selectors = { + + // Can be adjusted by the user + cacheLength: 50, + + createPseudo: markFunction, + + match: matchExpr, + + attrHandle: {}, + + find: {}, + + relative: { + ">": { dir: "parentNode", first: true }, + " ": { dir: "parentNode" }, + "+": { dir: "previousSibling", first: true }, + "~": { dir: "previousSibling" } + }, + + preFilter: { + "ATTR": function( match ) { + match[1] = match[1].replace( runescape, funescape ); + + // Move the given value to match[3] whether quoted or unquoted + match[3] = ( match[3] || match[4] || match[5] || "" ).replace( runescape, funescape ); + + if ( match[2] === "~=" ) { + match[3] = " " + match[3] + " "; + } + + return match.slice( 0, 4 ); + }, + + "CHILD": function( match ) { + /* matches from matchExpr["CHILD"] + 1 type (only|nth|...) + 2 what (child|of-type) + 3 argument (even|odd|\d*|\d*n([+-]\d+)?|...) + 4 xn-component of xn+y argument ([+-]?\d*n|) + 5 sign of xn-component + 6 x of xn-component + 7 sign of y-component + 8 y of y-component + */ + match[1] = match[1].toLowerCase(); + + if ( match[1].slice( 0, 3 ) === "nth" ) { + // nth-* requires argument + if ( !match[3] ) { + Sizzle.error( match[0] ); + } + + // numeric x and y parameters for Expr.filter.CHILD + // remember that false/true cast respectively to 0/1 + match[4] = +( match[4] ? match[5] + (match[6] || 1) : 2 * ( match[3] === "even" || match[3] === "odd" ) ); + match[5] = +( ( match[7] + match[8] ) || match[3] === "odd" ); + + // other types prohibit arguments + } else if ( match[3] ) { + Sizzle.error( match[0] ); + } + + return match; + }, + + "PSEUDO": function( match ) { + var excess, + unquoted = !match[6] && match[2]; + + if ( matchExpr["CHILD"].test( match[0] ) ) { + return null; + } + + // Accept quoted arguments as-is + if ( match[3] ) { + match[2] = match[4] || match[5] || ""; + + // Strip excess characters from unquoted arguments + } else if ( unquoted && rpseudo.test( unquoted ) && + // Get excess from tokenize (recursively) + (excess = tokenize( unquoted, true )) && + // advance to the next closing parenthesis + (excess = unquoted.indexOf( ")", unquoted.length - excess ) - unquoted.length) ) { + + // excess is a negative index + match[0] = match[0].slice( 0, excess ); + match[2] = unquoted.slice( 0, excess ); + } + + // Return only captures needed by the pseudo filter method (type and argument) + return match.slice( 0, 3 ); + } + }, + + filter: { + + "TAG": function( nodeNameSelector ) { + var nodeName = nodeNameSelector.replace( runescape, funescape ).toLowerCase(); + return nodeNameSelector === "*" ? + function() { return true; } : + function( elem ) { + return elem.nodeName && elem.nodeName.toLowerCase() === nodeName; + }; + }, + + "CLASS": function( className ) { + var pattern = classCache[ className + " " ]; + + return pattern || + (pattern = new RegExp( "(^|" + whitespace + ")" + className + "(" + whitespace + "|$)" )) && + classCache( className, function( elem ) { + return pattern.test( typeof elem.className === "string" && elem.className || typeof elem.getAttribute !== "undefined" && elem.getAttribute("class") || "" ); + }); + }, + + "ATTR": function( name, operator, check ) { + return function( elem ) { + var result = Sizzle.attr( elem, name ); + + if ( result == null ) { + return operator === "!="; + } + if ( !operator ) { + return true; + } + + result += ""; + + return operator === "=" ? result === check : + operator === "!=" ? result !== check : + operator === "^=" ? check && result.indexOf( check ) === 0 : + operator === "*=" ? check && result.indexOf( check ) > -1 : + operator === "$=" ? check && result.slice( -check.length ) === check : + operator === "~=" ? ( " " + result.replace( rwhitespace, " " ) + " " ).indexOf( check ) > -1 : + operator === "|=" ? result === check || result.slice( 0, check.length + 1 ) === check + "-" : + false; + }; + }, + + "CHILD": function( type, what, argument, first, last ) { + var simple = type.slice( 0, 3 ) !== "nth", + forward = type.slice( -4 ) !== "last", + ofType = what === "of-type"; + + return first === 1 && last === 0 ? + + // Shortcut for :nth-*(n) + function( elem ) { + return !!elem.parentNode; + } : + + function( elem, context, xml ) { + var cache, uniqueCache, outerCache, node, nodeIndex, start, + dir = simple !== forward ? "nextSibling" : "previousSibling", + parent = elem.parentNode, + name = ofType && elem.nodeName.toLowerCase(), + useCache = !xml && !ofType, + diff = false; + + if ( parent ) { + + // :(first|last|only)-(child|of-type) + if ( simple ) { + while ( dir ) { + node = elem; + while ( (node = node[ dir ]) ) { + if ( ofType ? + node.nodeName.toLowerCase() === name : + node.nodeType === 1 ) { + + return false; + } + } + // Reverse direction for :only-* (if we haven't yet done so) + start = dir = type === "only" && !start && "nextSibling"; + } + return true; + } + + start = [ forward ? parent.firstChild : parent.lastChild ]; + + // non-xml :nth-child(...) stores cache data on `parent` + if ( forward && useCache ) { + + // Seek `elem` from a previously-cached index + + // ...in a gzip-friendly way + node = parent; + outerCache = node[ expando ] || (node[ expando ] = {}); + + // Support: IE <9 only + // Defend against cloned attroperties (jQuery gh-1709) + uniqueCache = outerCache[ node.uniqueID ] || + (outerCache[ node.uniqueID ] = {}); + + cache = uniqueCache[ type ] || []; + nodeIndex = cache[ 0 ] === dirruns && cache[ 1 ]; + diff = nodeIndex && cache[ 2 ]; + node = nodeIndex && parent.childNodes[ nodeIndex ]; + + while ( (node = ++nodeIndex && node && node[ dir ] || + + // Fallback to seeking `elem` from the start + (diff = nodeIndex = 0) || start.pop()) ) { + + // When found, cache indexes on `parent` and break + if ( node.nodeType === 1 && ++diff && node === elem ) { + uniqueCache[ type ] = [ dirruns, nodeIndex, diff ]; + break; + } + } + + } else { + // Use previously-cached element index if available + if ( useCache ) { + // ...in a gzip-friendly way + node = elem; + outerCache = node[ expando ] || (node[ expando ] = {}); + + // Support: IE <9 only + // Defend against cloned attroperties (jQuery gh-1709) + uniqueCache = outerCache[ node.uniqueID ] || + (outerCache[ node.uniqueID ] = {}); + + cache = uniqueCache[ type ] || []; + nodeIndex = cache[ 0 ] === dirruns && cache[ 1 ]; + diff = nodeIndex; + } + + // xml :nth-child(...) + // or :nth-last-child(...) or :nth(-last)?-of-type(...) + if ( diff === false ) { + // Use the same loop as above to seek `elem` from the start + while ( (node = ++nodeIndex && node && node[ dir ] || + (diff = nodeIndex = 0) || start.pop()) ) { + + if ( ( ofType ? + node.nodeName.toLowerCase() === name : + node.nodeType === 1 ) && + ++diff ) { + + // Cache the index of each encountered element + if ( useCache ) { + outerCache = node[ expando ] || (node[ expando ] = {}); + + // Support: IE <9 only + // Defend against cloned attroperties (jQuery gh-1709) + uniqueCache = outerCache[ node.uniqueID ] || + (outerCache[ node.uniqueID ] = {}); + + uniqueCache[ type ] = [ dirruns, diff ]; + } + + if ( node === elem ) { + break; + } + } + } + } + } + + // Incorporate the offset, then check against cycle size + diff -= last; + return diff === first || ( diff % first === 0 && diff / first >= 0 ); + } + }; + }, + + "PSEUDO": function( pseudo, argument ) { + // pseudo-class names are case-insensitive + // http://www.w3.org/TR/selectors/#pseudo-classes + // Prioritize by case sensitivity in case custom pseudos are added with uppercase letters + // Remember that setFilters inherits from pseudos + var args, + fn = Expr.pseudos[ pseudo ] || Expr.setFilters[ pseudo.toLowerCase() ] || + Sizzle.error( "unsupported pseudo: " + pseudo ); + + // The user may use createPseudo to indicate that + // arguments are needed to create the filter function + // just as Sizzle does + if ( fn[ expando ] ) { + return fn( argument ); + } + + // But maintain support for old signatures + if ( fn.length > 1 ) { + args = [ pseudo, pseudo, "", argument ]; + return Expr.setFilters.hasOwnProperty( pseudo.toLowerCase() ) ? + markFunction(function( seed, matches ) { + var idx, + matched = fn( seed, argument ), + i = matched.length; + while ( i-- ) { + idx = indexOf( seed, matched[i] ); + seed[ idx ] = !( matches[ idx ] = matched[i] ); + } + }) : + function( elem ) { + return fn( elem, 0, args ); + }; + } + + return fn; + } + }, + + pseudos: { + // Potentially complex pseudos + "not": markFunction(function( selector ) { + // Trim the selector passed to compile + // to avoid treating leading and trailing + // spaces as combinators + var input = [], + results = [], + matcher = compile( selector.replace( rtrim, "$1" ) ); + + return matcher[ expando ] ? + markFunction(function( seed, matches, context, xml ) { + var elem, + unmatched = matcher( seed, null, xml, [] ), + i = seed.length; + + // Match elements unmatched by `matcher` + while ( i-- ) { + if ( (elem = unmatched[i]) ) { + seed[i] = !(matches[i] = elem); + } + } + }) : + function( elem, context, xml ) { + input[0] = elem; + matcher( input, null, xml, results ); + // Don't keep the element (issue #299) + input[0] = null; + return !results.pop(); + }; + }), + + "has": markFunction(function( selector ) { + return function( elem ) { + return Sizzle( selector, elem ).length > 0; + }; + }), + + "contains": markFunction(function( text ) { + text = text.replace( runescape, funescape ); + return function( elem ) { + return ( elem.textContent || elem.innerText || getText( elem ) ).indexOf( text ) > -1; + }; + }), + + // "Whether an element is represented by a :lang() selector + // is based solely on the element's language value + // being equal to the identifier C, + // or beginning with the identifier C immediately followed by "-". + // The matching of C against the element's language value is performed case-insensitively. + // The identifier C does not have to be a valid language name." + // http://www.w3.org/TR/selectors/#lang-pseudo + "lang": markFunction( function( lang ) { + // lang value must be a valid identifier + if ( !ridentifier.test(lang || "") ) { + Sizzle.error( "unsupported lang: " + lang ); + } + lang = lang.replace( runescape, funescape ).toLowerCase(); + return function( elem ) { + var elemLang; + do { + if ( (elemLang = documentIsHTML ? + elem.lang : + elem.getAttribute("xml:lang") || elem.getAttribute("lang")) ) { + + elemLang = elemLang.toLowerCase(); + return elemLang === lang || elemLang.indexOf( lang + "-" ) === 0; + } + } while ( (elem = elem.parentNode) && elem.nodeType === 1 ); + return false; + }; + }), + + // Miscellaneous + "target": function( elem ) { + var hash = window.location && window.location.hash; + return hash && hash.slice( 1 ) === elem.id; + }, + + "root": function( elem ) { + return elem === docElem; + }, + + "focus": function( elem ) { + return elem === document.activeElement && (!document.hasFocus || document.hasFocus()) && !!(elem.type || elem.href || ~elem.tabIndex); + }, + + // Boolean properties + "enabled": function( elem ) { + return elem.disabled === false; + }, + + "disabled": function( elem ) { + return elem.disabled === true; + }, + + "checked": function( elem ) { + // In CSS3, :checked should return both checked and selected elements + // http://www.w3.org/TR/2011/REC-css3-selectors-20110929/#checked + var nodeName = elem.nodeName.toLowerCase(); + return (nodeName === "input" && !!elem.checked) || (nodeName === "option" && !!elem.selected); + }, + + "selected": function( elem ) { + // Accessing this property makes selected-by-default + // options in Safari work properly + if ( elem.parentNode ) { + elem.parentNode.selectedIndex; + } + + return elem.selected === true; + }, + + // Contents + "empty": function( elem ) { + // http://www.w3.org/TR/selectors/#empty-pseudo + // :empty is negated by element (1) or content nodes (text: 3; cdata: 4; entity ref: 5), + // but not by others (comment: 8; processing instruction: 7; etc.) + // nodeType < 6 works because attributes (2) do not appear as children + for ( elem = elem.firstChild; elem; elem = elem.nextSibling ) { + if ( elem.nodeType < 6 ) { + return false; + } + } + return true; + }, + + "parent": function( elem ) { + return !Expr.pseudos["empty"]( elem ); + }, + + // Element/input types + "header": function( elem ) { + return rheader.test( elem.nodeName ); + }, + + "input": function( elem ) { + return rinputs.test( elem.nodeName ); + }, + + "button": function( elem ) { + var name = elem.nodeName.toLowerCase(); + return name === "input" && elem.type === "button" || name === "button"; + }, + + "text": function( elem ) { + var attr; + return elem.nodeName.toLowerCase() === "input" && + elem.type === "text" && + + // Support: IE<8 + // New HTML5 attribute values (e.g., "search") appear with elem.type === "text" + ( (attr = elem.getAttribute("type")) == null || attr.toLowerCase() === "text" ); + }, + + // Position-in-collection + "first": createPositionalPseudo(function() { + return [ 0 ]; + }), + + "last": createPositionalPseudo(function( matchIndexes, length ) { + return [ length - 1 ]; + }), + + "eq": createPositionalPseudo(function( matchIndexes, length, argument ) { + return [ argument < 0 ? argument + length : argument ]; + }), + + "even": createPositionalPseudo(function( matchIndexes, length ) { + var i = 0; + for ( ; i < length; i += 2 ) { + matchIndexes.push( i ); + } + return matchIndexes; + }), + + "odd": createPositionalPseudo(function( matchIndexes, length ) { + var i = 1; + for ( ; i < length; i += 2 ) { + matchIndexes.push( i ); + } + return matchIndexes; + }), + + "lt": createPositionalPseudo(function( matchIndexes, length, argument ) { + var i = argument < 0 ? argument + length : argument; + for ( ; --i >= 0; ) { + matchIndexes.push( i ); + } + return matchIndexes; + }), + + "gt": createPositionalPseudo(function( matchIndexes, length, argument ) { + var i = argument < 0 ? argument + length : argument; + for ( ; ++i < length; ) { + matchIndexes.push( i ); + } + return matchIndexes; + }) + } +}; + +Expr.pseudos["nth"] = Expr.pseudos["eq"]; + +// Add button/input type pseudos +for ( i in { radio: true, checkbox: true, file: true, password: true, image: true } ) { + Expr.pseudos[ i ] = createInputPseudo( i ); +} +for ( i in { submit: true, reset: true } ) { + Expr.pseudos[ i ] = createButtonPseudo( i ); +} + +// Easy API for creating new setFilters +function setFilters() {} +setFilters.prototype = Expr.filters = Expr.pseudos; +Expr.setFilters = new setFilters(); + +tokenize = Sizzle.tokenize = function( selector, parseOnly ) { + var matched, match, tokens, type, + soFar, groups, preFilters, + cached = tokenCache[ selector + " " ]; + + if ( cached ) { + return parseOnly ? 0 : cached.slice( 0 ); + } + + soFar = selector; + groups = []; + preFilters = Expr.preFilter; + + while ( soFar ) { + + // Comma and first run + if ( !matched || (match = rcomma.exec( soFar )) ) { + if ( match ) { + // Don't consume trailing commas as valid + soFar = soFar.slice( match[0].length ) || soFar; + } + groups.push( (tokens = []) ); + } + + matched = false; + + // Combinators + if ( (match = rcombinators.exec( soFar )) ) { + matched = match.shift(); + tokens.push({ + value: matched, + // Cast descendant combinators to space + type: match[0].replace( rtrim, " " ) + }); + soFar = soFar.slice( matched.length ); + } + + // Filters + for ( type in Expr.filter ) { + if ( (match = matchExpr[ type ].exec( soFar )) && (!preFilters[ type ] || + (match = preFilters[ type ]( match ))) ) { + matched = match.shift(); + tokens.push({ + value: matched, + type: type, + matches: match + }); + soFar = soFar.slice( matched.length ); + } + } + + if ( !matched ) { + break; + } + } + + // Return the length of the invalid excess + // if we're just parsing + // Otherwise, throw an error or return tokens + return parseOnly ? + soFar.length : + soFar ? + Sizzle.error( selector ) : + // Cache the tokens + tokenCache( selector, groups ).slice( 0 ); +}; + +function toSelector( tokens ) { + var i = 0, + len = tokens.length, + selector = ""; + for ( ; i < len; i++ ) { + selector += tokens[i].value; + } + return selector; +} + +function addCombinator( matcher, combinator, base ) { + var dir = combinator.dir, + checkNonElements = base && dir === "parentNode", + doneName = done++; + + return combinator.first ? + // Check against closest ancestor/preceding element + function( elem, context, xml ) { + while ( (elem = elem[ dir ]) ) { + if ( elem.nodeType === 1 || checkNonElements ) { + return matcher( elem, context, xml ); + } + } + } : + + // Check against all ancestor/preceding elements + function( elem, context, xml ) { + var oldCache, uniqueCache, outerCache, + newCache = [ dirruns, doneName ]; + + // We can't set arbitrary data on XML nodes, so they don't benefit from combinator caching + if ( xml ) { + while ( (elem = elem[ dir ]) ) { + if ( elem.nodeType === 1 || checkNonElements ) { + if ( matcher( elem, context, xml ) ) { + return true; + } + } + } + } else { + while ( (elem = elem[ dir ]) ) { + if ( elem.nodeType === 1 || checkNonElements ) { + outerCache = elem[ expando ] || (elem[ expando ] = {}); + + // Support: IE <9 only + // Defend against cloned attroperties (jQuery gh-1709) + uniqueCache = outerCache[ elem.uniqueID ] || (outerCache[ elem.uniqueID ] = {}); + + if ( (oldCache = uniqueCache[ dir ]) && + oldCache[ 0 ] === dirruns && oldCache[ 1 ] === doneName ) { + + // Assign to newCache so results back-propagate to previous elements + return (newCache[ 2 ] = oldCache[ 2 ]); + } else { + // Reuse newcache so results back-propagate to previous elements + uniqueCache[ dir ] = newCache; + + // A match means we're done; a fail means we have to keep checking + if ( (newCache[ 2 ] = matcher( elem, context, xml )) ) { + return true; + } + } + } + } + } + }; +} + +function elementMatcher( matchers ) { + return matchers.length > 1 ? + function( elem, context, xml ) { + var i = matchers.length; + while ( i-- ) { + if ( !matchers[i]( elem, context, xml ) ) { + return false; + } + } + return true; + } : + matchers[0]; +} + +function multipleContexts( selector, contexts, results ) { + var i = 0, + len = contexts.length; + for ( ; i < len; i++ ) { + Sizzle( selector, contexts[i], results ); + } + return results; +} + +function condense( unmatched, map, filter, context, xml ) { + var elem, + newUnmatched = [], + i = 0, + len = unmatched.length, + mapped = map != null; + + for ( ; i < len; i++ ) { + if ( (elem = unmatched[i]) ) { + if ( !filter || filter( elem, context, xml ) ) { + newUnmatched.push( elem ); + if ( mapped ) { + map.push( i ); + } + } + } + } + + return newUnmatched; +} + +function setMatcher( preFilter, selector, matcher, postFilter, postFinder, postSelector ) { + if ( postFilter && !postFilter[ expando ] ) { + postFilter = setMatcher( postFilter ); + } + if ( postFinder && !postFinder[ expando ] ) { + postFinder = setMatcher( postFinder, postSelector ); + } + return markFunction(function( seed, results, context, xml ) { + var temp, i, elem, + preMap = [], + postMap = [], + preexisting = results.length, + + // Get initial elements from seed or context + elems = seed || multipleContexts( selector || "*", context.nodeType ? [ context ] : context, [] ), + + // Prefilter to get matcher input, preserving a map for seed-results synchronization + matcherIn = preFilter && ( seed || !selector ) ? + condense( elems, preMap, preFilter, context, xml ) : + elems, + + matcherOut = matcher ? + // If we have a postFinder, or filtered seed, or non-seed postFilter or preexisting results, + postFinder || ( seed ? preFilter : preexisting || postFilter ) ? + + // ...intermediate processing is necessary + [] : + + // ...otherwise use results directly + results : + matcherIn; + + // Find primary matches + if ( matcher ) { + matcher( matcherIn, matcherOut, context, xml ); + } + + // Apply postFilter + if ( postFilter ) { + temp = condense( matcherOut, postMap ); + postFilter( temp, [], context, xml ); + + // Un-match failing elements by moving them back to matcherIn + i = temp.length; + while ( i-- ) { + if ( (elem = temp[i]) ) { + matcherOut[ postMap[i] ] = !(matcherIn[ postMap[i] ] = elem); + } + } + } + + if ( seed ) { + if ( postFinder || preFilter ) { + if ( postFinder ) { + // Get the final matcherOut by condensing this intermediate into postFinder contexts + temp = []; + i = matcherOut.length; + while ( i-- ) { + if ( (elem = matcherOut[i]) ) { + // Restore matcherIn since elem is not yet a final match + temp.push( (matcherIn[i] = elem) ); + } + } + postFinder( null, (matcherOut = []), temp, xml ); + } + + // Move matched elements from seed to results to keep them synchronized + i = matcherOut.length; + while ( i-- ) { + if ( (elem = matcherOut[i]) && + (temp = postFinder ? indexOf( seed, elem ) : preMap[i]) > -1 ) { + + seed[temp] = !(results[temp] = elem); + } + } + } + + // Add elements to results, through postFinder if defined + } else { + matcherOut = condense( + matcherOut === results ? + matcherOut.splice( preexisting, matcherOut.length ) : + matcherOut + ); + if ( postFinder ) { + postFinder( null, results, matcherOut, xml ); + } else { + push.apply( results, matcherOut ); + } + } + }); +} + +function matcherFromTokens( tokens ) { + var checkContext, matcher, j, + len = tokens.length, + leadingRelative = Expr.relative[ tokens[0].type ], + implicitRelative = leadingRelative || Expr.relative[" "], + i = leadingRelative ? 1 : 0, + + // The foundational matcher ensures that elements are reachable from top-level context(s) + matchContext = addCombinator( function( elem ) { + return elem === checkContext; + }, implicitRelative, true ), + matchAnyContext = addCombinator( function( elem ) { + return indexOf( checkContext, elem ) > -1; + }, implicitRelative, true ), + matchers = [ function( elem, context, xml ) { + var ret = ( !leadingRelative && ( xml || context !== outermostContext ) ) || ( + (checkContext = context).nodeType ? + matchContext( elem, context, xml ) : + matchAnyContext( elem, context, xml ) ); + // Avoid hanging onto element (issue #299) + checkContext = null; + return ret; + } ]; + + for ( ; i < len; i++ ) { + if ( (matcher = Expr.relative[ tokens[i].type ]) ) { + matchers = [ addCombinator(elementMatcher( matchers ), matcher) ]; + } else { + matcher = Expr.filter[ tokens[i].type ].apply( null, tokens[i].matches ); + + // Return special upon seeing a positional matcher + if ( matcher[ expando ] ) { + // Find the next relative operator (if any) for proper handling + j = ++i; + for ( ; j < len; j++ ) { + if ( Expr.relative[ tokens[j].type ] ) { + break; + } + } + return setMatcher( + i > 1 && elementMatcher( matchers ), + i > 1 && toSelector( + // If the preceding token was a descendant combinator, insert an implicit any-element `*` + tokens.slice( 0, i - 1 ).concat({ value: tokens[ i - 2 ].type === " " ? "*" : "" }) + ).replace( rtrim, "$1" ), + matcher, + i < j && matcherFromTokens( tokens.slice( i, j ) ), + j < len && matcherFromTokens( (tokens = tokens.slice( j )) ), + j < len && toSelector( tokens ) + ); + } + matchers.push( matcher ); + } + } + + return elementMatcher( matchers ); +} + +function matcherFromGroupMatchers( elementMatchers, setMatchers ) { + var bySet = setMatchers.length > 0, + byElement = elementMatchers.length > 0, + superMatcher = function( seed, context, xml, results, outermost ) { + var elem, j, matcher, + matchedCount = 0, + i = "0", + unmatched = seed && [], + setMatched = [], + contextBackup = outermostContext, + // We must always have either seed elements or outermost context + elems = seed || byElement && Expr.find["TAG"]( "*", outermost ), + // Use integer dirruns iff this is the outermost matcher + dirrunsUnique = (dirruns += contextBackup == null ? 1 : Math.random() || 0.1), + len = elems.length; + + if ( outermost ) { + outermostContext = context === document || context || outermost; + } + + // Add elements passing elementMatchers directly to results + // Support: IE<9, Safari + // Tolerate NodeList properties (IE: "length"; Safari: ) matching elements by id + for ( ; i !== len && (elem = elems[i]) != null; i++ ) { + if ( byElement && elem ) { + j = 0; + if ( !context && elem.ownerDocument !== document ) { + setDocument( elem ); + xml = !documentIsHTML; + } + while ( (matcher = elementMatchers[j++]) ) { + if ( matcher( elem, context || document, xml) ) { + results.push( elem ); + break; + } + } + if ( outermost ) { + dirruns = dirrunsUnique; + } + } + + // Track unmatched elements for set filters + if ( bySet ) { + // They will have gone through all possible matchers + if ( (elem = !matcher && elem) ) { + matchedCount--; + } + + // Lengthen the array for every element, matched or not + if ( seed ) { + unmatched.push( elem ); + } + } + } + + // `i` is now the count of elements visited above, and adding it to `matchedCount` + // makes the latter nonnegative. + matchedCount += i; + + // Apply set filters to unmatched elements + // NOTE: This can be skipped if there are no unmatched elements (i.e., `matchedCount` + // equals `i`), unless we didn't visit _any_ elements in the above loop because we have + // no element matchers and no seed. + // Incrementing an initially-string "0" `i` allows `i` to remain a string only in that + // case, which will result in a "00" `matchedCount` that differs from `i` but is also + // numerically zero. + if ( bySet && i !== matchedCount ) { + j = 0; + while ( (matcher = setMatchers[j++]) ) { + matcher( unmatched, setMatched, context, xml ); + } + + if ( seed ) { + // Reintegrate element matches to eliminate the need for sorting + if ( matchedCount > 0 ) { + while ( i-- ) { + if ( !(unmatched[i] || setMatched[i]) ) { + setMatched[i] = pop.call( results ); + } + } + } + + // Discard index placeholder values to get only actual matches + setMatched = condense( setMatched ); + } + + // Add matches to results + push.apply( results, setMatched ); + + // Seedless set matches succeeding multiple successful matchers stipulate sorting + if ( outermost && !seed && setMatched.length > 0 && + ( matchedCount + setMatchers.length ) > 1 ) { + + Sizzle.uniqueSort( results ); + } + } + + // Override manipulation of globals by nested matchers + if ( outermost ) { + dirruns = dirrunsUnique; + outermostContext = contextBackup; + } + + return unmatched; + }; + + return bySet ? + markFunction( superMatcher ) : + superMatcher; +} + +compile = Sizzle.compile = function( selector, match /* Internal Use Only */ ) { + var i, + setMatchers = [], + elementMatchers = [], + cached = compilerCache[ selector + " " ]; + + if ( !cached ) { + // Generate a function of recursive functions that can be used to check each element + if ( !match ) { + match = tokenize( selector ); + } + i = match.length; + while ( i-- ) { + cached = matcherFromTokens( match[i] ); + if ( cached[ expando ] ) { + setMatchers.push( cached ); + } else { + elementMatchers.push( cached ); + } + } + + // Cache the compiled function + cached = compilerCache( selector, matcherFromGroupMatchers( elementMatchers, setMatchers ) ); + + // Save selector and tokenization + cached.selector = selector; + } + return cached; +}; + +/** + * A low-level selection function that works with Sizzle's compiled + * selector functions + * @param {String|Function} selector A selector or a pre-compiled + * selector function built with Sizzle.compile + * @param {Element} context + * @param {Array} [results] + * @param {Array} [seed] A set of elements to match against + */ +select = Sizzle.select = function( selector, context, results, seed ) { + var i, tokens, token, type, find, + compiled = typeof selector === "function" && selector, + match = !seed && tokenize( (selector = compiled.selector || selector) ); + + results = results || []; + + // Try to minimize operations if there is only one selector in the list and no seed + // (the latter of which guarantees us context) + if ( match.length === 1 ) { + + // Reduce context if the leading compound selector is an ID + tokens = match[0] = match[0].slice( 0 ); + if ( tokens.length > 2 && (token = tokens[0]).type === "ID" && + support.getById && context.nodeType === 9 && documentIsHTML && + Expr.relative[ tokens[1].type ] ) { + + context = ( Expr.find["ID"]( token.matches[0].replace(runescape, funescape), context ) || [] )[0]; + if ( !context ) { + return results; + + // Precompiled matchers will still verify ancestry, so step up a level + } else if ( compiled ) { + context = context.parentNode; + } + + selector = selector.slice( tokens.shift().value.length ); + } + + // Fetch a seed set for right-to-left matching + i = matchExpr["needsContext"].test( selector ) ? 0 : tokens.length; + while ( i-- ) { + token = tokens[i]; + + // Abort if we hit a combinator + if ( Expr.relative[ (type = token.type) ] ) { + break; + } + if ( (find = Expr.find[ type ]) ) { + // Search, expanding context for leading sibling combinators + if ( (seed = find( + token.matches[0].replace( runescape, funescape ), + rsibling.test( tokens[0].type ) && testContext( context.parentNode ) || context + )) ) { + + // If seed is empty or no tokens remain, we can return early + tokens.splice( i, 1 ); + selector = seed.length && toSelector( tokens ); + if ( !selector ) { + push.apply( results, seed ); + return results; + } + + break; + } + } + } + } + + // Compile and execute a filtering function if one is not provided + // Provide `match` to avoid retokenization if we modified the selector above + ( compiled || compile( selector, match ) )( + seed, + context, + !documentIsHTML, + results, + !context || rsibling.test( selector ) && testContext( context.parentNode ) || context + ); + return results; +}; + +// One-time assignments + +// Sort stability +support.sortStable = expando.split("").sort( sortOrder ).join("") === expando; + +// Support: Chrome 14-35+ +// Always assume duplicates if they aren't passed to the comparison function +support.detectDuplicates = !!hasDuplicate; + +// Initialize against the default document +setDocument(); + +// Support: Webkit<537.32 - Safari 6.0.3/Chrome 25 (fixed in Chrome 27) +// Detached nodes confoundingly follow *each other* +support.sortDetached = assert(function( div1 ) { + // Should return 1, but returns 4 (following) + return div1.compareDocumentPosition( document.createElement("div") ) & 1; +}); + +// Support: IE<8 +// Prevent attribute/property "interpolation" +// http://msdn.microsoft.com/en-us/library/ms536429%28VS.85%29.aspx +if ( !assert(function( div ) { + div.innerHTML = ""; + return div.firstChild.getAttribute("href") === "#" ; +}) ) { + addHandle( "type|href|height|width", function( elem, name, isXML ) { + if ( !isXML ) { + return elem.getAttribute( name, name.toLowerCase() === "type" ? 1 : 2 ); + } + }); +} + +// Support: IE<9 +// Use defaultValue in place of getAttribute("value") +if ( !support.attributes || !assert(function( div ) { + div.innerHTML = ""; + div.firstChild.setAttribute( "value", "" ); + return div.firstChild.getAttribute( "value" ) === ""; +}) ) { + addHandle( "value", function( elem, name, isXML ) { + if ( !isXML && elem.nodeName.toLowerCase() === "input" ) { + return elem.defaultValue; + } + }); +} + +// Support: IE<9 +// Use getAttributeNode to fetch booleans when getAttribute lies +if ( !assert(function( div ) { + return div.getAttribute("disabled") == null; +}) ) { + addHandle( booleans, function( elem, name, isXML ) { + var val; + if ( !isXML ) { + return elem[ name ] === true ? name.toLowerCase() : + (val = elem.getAttributeNode( name )) && val.specified ? + val.value : + null; + } + }); +} + +return Sizzle; + +})( window ); + + + +jQuery.find = Sizzle; +jQuery.expr = Sizzle.selectors; +jQuery.expr[ ":" ] = jQuery.expr.pseudos; +jQuery.uniqueSort = jQuery.unique = Sizzle.uniqueSort; +jQuery.text = Sizzle.getText; +jQuery.isXMLDoc = Sizzle.isXML; +jQuery.contains = Sizzle.contains; + + + +var dir = function( elem, dir, until ) { + var matched = [], + truncate = until !== undefined; + + while ( ( elem = elem[ dir ] ) && elem.nodeType !== 9 ) { + if ( elem.nodeType === 1 ) { + if ( truncate && jQuery( elem ).is( until ) ) { + break; + } + matched.push( elem ); + } + } + return matched; +}; + + +var siblings = function( n, elem ) { + var matched = []; + + for ( ; n; n = n.nextSibling ) { + if ( n.nodeType === 1 && n !== elem ) { + matched.push( n ); + } + } + + return matched; +}; + + +var rneedsContext = jQuery.expr.match.needsContext; + +var rsingleTag = ( /^<([\w-]+)\s*\/?>(?:<\/\1>|)$/ ); + + + +var risSimple = /^.[^:#\[\.,]*$/; + +// Implement the identical functionality for filter and not +function winnow( elements, qualifier, not ) { + if ( jQuery.isFunction( qualifier ) ) { + return jQuery.grep( elements, function( elem, i ) { + /* jshint -W018 */ + return !!qualifier.call( elem, i, elem ) !== not; + } ); + + } + + if ( qualifier.nodeType ) { + return jQuery.grep( elements, function( elem ) { + return ( elem === qualifier ) !== not; + } ); + + } + + if ( typeof qualifier === "string" ) { + if ( risSimple.test( qualifier ) ) { + return jQuery.filter( qualifier, elements, not ); + } + + qualifier = jQuery.filter( qualifier, elements ); + } + + return jQuery.grep( elements, function( elem ) { + return ( jQuery.inArray( elem, qualifier ) > -1 ) !== not; + } ); +} + +jQuery.filter = function( expr, elems, not ) { + var elem = elems[ 0 ]; + + if ( not ) { + expr = ":not(" + expr + ")"; + } + + return elems.length === 1 && elem.nodeType === 1 ? + jQuery.find.matchesSelector( elem, expr ) ? [ elem ] : [] : + jQuery.find.matches( expr, jQuery.grep( elems, function( elem ) { + return elem.nodeType === 1; + } ) ); +}; + +jQuery.fn.extend( { + find: function( selector ) { + var i, + ret = [], + self = this, + len = self.length; + + if ( typeof selector !== "string" ) { + return this.pushStack( jQuery( selector ).filter( function() { + for ( i = 0; i < len; i++ ) { + if ( jQuery.contains( self[ i ], this ) ) { + return true; + } + } + } ) ); + } + + for ( i = 0; i < len; i++ ) { + jQuery.find( selector, self[ i ], ret ); + } + + // Needed because $( selector, context ) becomes $( context ).find( selector ) + ret = this.pushStack( len > 1 ? jQuery.unique( ret ) : ret ); + ret.selector = this.selector ? this.selector + " " + selector : selector; + return ret; + }, + filter: function( selector ) { + return this.pushStack( winnow( this, selector || [], false ) ); + }, + not: function( selector ) { + return this.pushStack( winnow( this, selector || [], true ) ); + }, + is: function( selector ) { + return !!winnow( + this, + + // If this is a positional/relative selector, check membership in the returned set + // so $("p:first").is("p:last") won't return true for a doc with two "p". + typeof selector === "string" && rneedsContext.test( selector ) ? + jQuery( selector ) : + selector || [], + false + ).length; + } +} ); + + +// Initialize a jQuery object + + +// A central reference to the root jQuery(document) +var rootjQuery, + + // A simple way to check for HTML strings + // Prioritize #id over to avoid XSS via location.hash (#9521) + // Strict HTML recognition (#11290: must start with <) + rquickExpr = /^(?:\s*(<[\w\W]+>)[^>]*|#([\w-]*))$/, + + init = jQuery.fn.init = function( selector, context, root ) { + var match, elem; + + // HANDLE: $(""), $(null), $(undefined), $(false) + if ( !selector ) { + return this; + } + + // init accepts an alternate rootjQuery + // so migrate can support jQuery.sub (gh-2101) + root = root || rootjQuery; + + // Handle HTML strings + if ( typeof selector === "string" ) { + if ( selector.charAt( 0 ) === "<" && + selector.charAt( selector.length - 1 ) === ">" && + selector.length >= 3 ) { + + // Assume that strings that start and end with <> are HTML and skip the regex check + match = [ null, selector, null ]; + + } else { + match = rquickExpr.exec( selector ); + } + + // Match html or make sure no context is specified for #id + if ( match && ( match[ 1 ] || !context ) ) { + + // HANDLE: $(html) -> $(array) + if ( match[ 1 ] ) { + context = context instanceof jQuery ? context[ 0 ] : context; + + // scripts is true for back-compat + // Intentionally let the error be thrown if parseHTML is not present + jQuery.merge( this, jQuery.parseHTML( + match[ 1 ], + context && context.nodeType ? context.ownerDocument || context : document, + true + ) ); + + // HANDLE: $(html, props) + if ( rsingleTag.test( match[ 1 ] ) && jQuery.isPlainObject( context ) ) { + for ( match in context ) { + + // Properties of context are called as methods if possible + if ( jQuery.isFunction( this[ match ] ) ) { + this[ match ]( context[ match ] ); + + // ...and otherwise set as attributes + } else { + this.attr( match, context[ match ] ); + } + } + } + + return this; + + // HANDLE: $(#id) + } else { + elem = document.getElementById( match[ 2 ] ); + + // Check parentNode to catch when Blackberry 4.6 returns + // nodes that are no longer in the document #6963 + if ( elem && elem.parentNode ) { + + // Handle the case where IE and Opera return items + // by name instead of ID + if ( elem.id !== match[ 2 ] ) { + return rootjQuery.find( selector ); + } + + // Otherwise, we inject the element directly into the jQuery object + this.length = 1; + this[ 0 ] = elem; + } + + this.context = document; + this.selector = selector; + return this; + } + + // HANDLE: $(expr, $(...)) + } else if ( !context || context.jquery ) { + return ( context || root ).find( selector ); + + // HANDLE: $(expr, context) + // (which is just equivalent to: $(context).find(expr) + } else { + return this.constructor( context ).find( selector ); + } + + // HANDLE: $(DOMElement) + } else if ( selector.nodeType ) { + this.context = this[ 0 ] = selector; + this.length = 1; + return this; + + // HANDLE: $(function) + // Shortcut for document ready + } else if ( jQuery.isFunction( selector ) ) { + return typeof root.ready !== "undefined" ? + root.ready( selector ) : + + // Execute immediately if ready is not present + selector( jQuery ); + } + + if ( selector.selector !== undefined ) { + this.selector = selector.selector; + this.context = selector.context; + } + + return jQuery.makeArray( selector, this ); + }; + +// Give the init function the jQuery prototype for later instantiation +init.prototype = jQuery.fn; + +// Initialize central reference +rootjQuery = jQuery( document ); + + +var rparentsprev = /^(?:parents|prev(?:Until|All))/, + + // methods guaranteed to produce a unique set when starting from a unique set + guaranteedUnique = { + children: true, + contents: true, + next: true, + prev: true + }; + +jQuery.fn.extend( { + has: function( target ) { + var i, + targets = jQuery( target, this ), + len = targets.length; + + return this.filter( function() { + for ( i = 0; i < len; i++ ) { + if ( jQuery.contains( this, targets[ i ] ) ) { + return true; + } + } + } ); + }, + + closest: function( selectors, context ) { + var cur, + i = 0, + l = this.length, + matched = [], + pos = rneedsContext.test( selectors ) || typeof selectors !== "string" ? + jQuery( selectors, context || this.context ) : + 0; + + for ( ; i < l; i++ ) { + for ( cur = this[ i ]; cur && cur !== context; cur = cur.parentNode ) { + + // Always skip document fragments + if ( cur.nodeType < 11 && ( pos ? + pos.index( cur ) > -1 : + + // Don't pass non-elements to Sizzle + cur.nodeType === 1 && + jQuery.find.matchesSelector( cur, selectors ) ) ) { + + matched.push( cur ); + break; + } + } + } + + return this.pushStack( matched.length > 1 ? jQuery.uniqueSort( matched ) : matched ); + }, + + // Determine the position of an element within + // the matched set of elements + index: function( elem ) { + + // No argument, return index in parent + if ( !elem ) { + return ( this[ 0 ] && this[ 0 ].parentNode ) ? this.first().prevAll().length : -1; + } + + // index in selector + if ( typeof elem === "string" ) { + return jQuery.inArray( this[ 0 ], jQuery( elem ) ); + } + + // Locate the position of the desired element + return jQuery.inArray( + + // If it receives a jQuery object, the first element is used + elem.jquery ? elem[ 0 ] : elem, this ); + }, + + add: function( selector, context ) { + return this.pushStack( + jQuery.uniqueSort( + jQuery.merge( this.get(), jQuery( selector, context ) ) + ) + ); + }, + + addBack: function( selector ) { + return this.add( selector == null ? + this.prevObject : this.prevObject.filter( selector ) + ); + } +} ); + +function sibling( cur, dir ) { + do { + cur = cur[ dir ]; + } while ( cur && cur.nodeType !== 1 ); + + return cur; +} + +jQuery.each( { + parent: function( elem ) { + var parent = elem.parentNode; + return parent && parent.nodeType !== 11 ? parent : null; + }, + parents: function( elem ) { + return dir( elem, "parentNode" ); + }, + parentsUntil: function( elem, i, until ) { + return dir( elem, "parentNode", until ); + }, + next: function( elem ) { + return sibling( elem, "nextSibling" ); + }, + prev: function( elem ) { + return sibling( elem, "previousSibling" ); + }, + nextAll: function( elem ) { + return dir( elem, "nextSibling" ); + }, + prevAll: function( elem ) { + return dir( elem, "previousSibling" ); + }, + nextUntil: function( elem, i, until ) { + return dir( elem, "nextSibling", until ); + }, + prevUntil: function( elem, i, until ) { + return dir( elem, "previousSibling", until ); + }, + siblings: function( elem ) { + return siblings( ( elem.parentNode || {} ).firstChild, elem ); + }, + children: function( elem ) { + return siblings( elem.firstChild ); + }, + contents: function( elem ) { + return jQuery.nodeName( elem, "iframe" ) ? + elem.contentDocument || elem.contentWindow.document : + jQuery.merge( [], elem.childNodes ); + } +}, function( name, fn ) { + jQuery.fn[ name ] = function( until, selector ) { + var ret = jQuery.map( this, fn, until ); + + if ( name.slice( -5 ) !== "Until" ) { + selector = until; + } + + if ( selector && typeof selector === "string" ) { + ret = jQuery.filter( selector, ret ); + } + + if ( this.length > 1 ) { + + // Remove duplicates + if ( !guaranteedUnique[ name ] ) { + ret = jQuery.uniqueSort( ret ); + } + + // Reverse order for parents* and prev-derivatives + if ( rparentsprev.test( name ) ) { + ret = ret.reverse(); + } + } + + return this.pushStack( ret ); + }; +} ); +var rnotwhite = ( /\S+/g ); + + + +// Convert String-formatted options into Object-formatted ones +function createOptions( options ) { + var object = {}; + jQuery.each( options.match( rnotwhite ) || [], function( _, flag ) { + object[ flag ] = true; + } ); + return object; +} + +/* + * Create a callback list using the following parameters: + * + * options: an optional list of space-separated options that will change how + * the callback list behaves or a more traditional option object + * + * By default a callback list will act like an event callback list and can be + * "fired" multiple times. + * + * Possible options: + * + * once: will ensure the callback list can only be fired once (like a Deferred) + * + * memory: will keep track of previous values and will call any callback added + * after the list has been fired right away with the latest "memorized" + * values (like a Deferred) + * + * unique: will ensure a callback can only be added once (no duplicate in the list) + * + * stopOnFalse: interrupt callings when a callback returns false + * + */ +jQuery.Callbacks = function( options ) { + + // Convert options from String-formatted to Object-formatted if needed + // (we check in cache first) + options = typeof options === "string" ? + createOptions( options ) : + jQuery.extend( {}, options ); + + var // Flag to know if list is currently firing + firing, + + // Last fire value for non-forgettable lists + memory, + + // Flag to know if list was already fired + fired, + + // Flag to prevent firing + locked, + + // Actual callback list + list = [], + + // Queue of execution data for repeatable lists + queue = [], + + // Index of currently firing callback (modified by add/remove as needed) + firingIndex = -1, + + // Fire callbacks + fire = function() { + + // Enforce single-firing + locked = options.once; + + // Execute callbacks for all pending executions, + // respecting firingIndex overrides and runtime changes + fired = firing = true; + for ( ; queue.length; firingIndex = -1 ) { + memory = queue.shift(); + while ( ++firingIndex < list.length ) { + + // Run callback and check for early termination + if ( list[ firingIndex ].apply( memory[ 0 ], memory[ 1 ] ) === false && + options.stopOnFalse ) { + + // Jump to end and forget the data so .add doesn't re-fire + firingIndex = list.length; + memory = false; + } + } + } + + // Forget the data if we're done with it + if ( !options.memory ) { + memory = false; + } + + firing = false; + + // Clean up if we're done firing for good + if ( locked ) { + + // Keep an empty list if we have data for future add calls + if ( memory ) { + list = []; + + // Otherwise, this object is spent + } else { + list = ""; + } + } + }, + + // Actual Callbacks object + self = { + + // Add a callback or a collection of callbacks to the list + add: function() { + if ( list ) { + + // If we have memory from a past run, we should fire after adding + if ( memory && !firing ) { + firingIndex = list.length - 1; + queue.push( memory ); + } + + ( function add( args ) { + jQuery.each( args, function( _, arg ) { + if ( jQuery.isFunction( arg ) ) { + if ( !options.unique || !self.has( arg ) ) { + list.push( arg ); + } + } else if ( arg && arg.length && jQuery.type( arg ) !== "string" ) { + + // Inspect recursively + add( arg ); + } + } ); + } )( arguments ); + + if ( memory && !firing ) { + fire(); + } + } + return this; + }, + + // Remove a callback from the list + remove: function() { + jQuery.each( arguments, function( _, arg ) { + var index; + while ( ( index = jQuery.inArray( arg, list, index ) ) > -1 ) { + list.splice( index, 1 ); + + // Handle firing indexes + if ( index <= firingIndex ) { + firingIndex--; + } + } + } ); + return this; + }, + + // Check if a given callback is in the list. + // If no argument is given, return whether or not list has callbacks attached. + has: function( fn ) { + return fn ? + jQuery.inArray( fn, list ) > -1 : + list.length > 0; + }, + + // Remove all callbacks from the list + empty: function() { + if ( list ) { + list = []; + } + return this; + }, + + // Disable .fire and .add + // Abort any current/pending executions + // Clear all callbacks and values + disable: function() { + locked = queue = []; + list = memory = ""; + return this; + }, + disabled: function() { + return !list; + }, + + // Disable .fire + // Also disable .add unless we have memory (since it would have no effect) + // Abort any pending executions + lock: function() { + locked = true; + if ( !memory ) { + self.disable(); + } + return this; + }, + locked: function() { + return !!locked; + }, + + // Call all callbacks with the given context and arguments + fireWith: function( context, args ) { + if ( !locked ) { + args = args || []; + args = [ context, args.slice ? args.slice() : args ]; + queue.push( args ); + if ( !firing ) { + fire(); + } + } + return this; + }, + + // Call all the callbacks with the given arguments + fire: function() { + self.fireWith( this, arguments ); + return this; + }, + + // To know if the callbacks have already been called at least once + fired: function() { + return !!fired; + } + }; + + return self; +}; + + +jQuery.extend( { + + Deferred: function( func ) { + var tuples = [ + + // action, add listener, listener list, final state + [ "resolve", "done", jQuery.Callbacks( "once memory" ), "resolved" ], + [ "reject", "fail", jQuery.Callbacks( "once memory" ), "rejected" ], + [ "notify", "progress", jQuery.Callbacks( "memory" ) ] + ], + state = "pending", + promise = { + state: function() { + return state; + }, + always: function() { + deferred.done( arguments ).fail( arguments ); + return this; + }, + then: function( /* fnDone, fnFail, fnProgress */ ) { + var fns = arguments; + return jQuery.Deferred( function( newDefer ) { + jQuery.each( tuples, function( i, tuple ) { + var fn = jQuery.isFunction( fns[ i ] ) && fns[ i ]; + + // deferred[ done | fail | progress ] for forwarding actions to newDefer + deferred[ tuple[ 1 ] ]( function() { + var returned = fn && fn.apply( this, arguments ); + if ( returned && jQuery.isFunction( returned.promise ) ) { + returned.promise() + .progress( newDefer.notify ) + .done( newDefer.resolve ) + .fail( newDefer.reject ); + } else { + newDefer[ tuple[ 0 ] + "With" ]( + this === promise ? newDefer.promise() : this, + fn ? [ returned ] : arguments + ); + } + } ); + } ); + fns = null; + } ).promise(); + }, + + // Get a promise for this deferred + // If obj is provided, the promise aspect is added to the object + promise: function( obj ) { + return obj != null ? jQuery.extend( obj, promise ) : promise; + } + }, + deferred = {}; + + // Keep pipe for back-compat + promise.pipe = promise.then; + + // Add list-specific methods + jQuery.each( tuples, function( i, tuple ) { + var list = tuple[ 2 ], + stateString = tuple[ 3 ]; + + // promise[ done | fail | progress ] = list.add + promise[ tuple[ 1 ] ] = list.add; + + // Handle state + if ( stateString ) { + list.add( function() { + + // state = [ resolved | rejected ] + state = stateString; + + // [ reject_list | resolve_list ].disable; progress_list.lock + }, tuples[ i ^ 1 ][ 2 ].disable, tuples[ 2 ][ 2 ].lock ); + } + + // deferred[ resolve | reject | notify ] + deferred[ tuple[ 0 ] ] = function() { + deferred[ tuple[ 0 ] + "With" ]( this === deferred ? promise : this, arguments ); + return this; + }; + deferred[ tuple[ 0 ] + "With" ] = list.fireWith; + } ); + + // Make the deferred a promise + promise.promise( deferred ); + + // Call given func if any + if ( func ) { + func.call( deferred, deferred ); + } + + // All done! + return deferred; + }, + + // Deferred helper + when: function( subordinate /* , ..., subordinateN */ ) { + var i = 0, + resolveValues = slice.call( arguments ), + length = resolveValues.length, + + // the count of uncompleted subordinates + remaining = length !== 1 || + ( subordinate && jQuery.isFunction( subordinate.promise ) ) ? length : 0, + + // the master Deferred. + // If resolveValues consist of only a single Deferred, just use that. + deferred = remaining === 1 ? subordinate : jQuery.Deferred(), + + // Update function for both resolve and progress values + updateFunc = function( i, contexts, values ) { + return function( value ) { + contexts[ i ] = this; + values[ i ] = arguments.length > 1 ? slice.call( arguments ) : value; + if ( values === progressValues ) { + deferred.notifyWith( contexts, values ); + + } else if ( !( --remaining ) ) { + deferred.resolveWith( contexts, values ); + } + }; + }, + + progressValues, progressContexts, resolveContexts; + + // add listeners to Deferred subordinates; treat others as resolved + if ( length > 1 ) { + progressValues = new Array( length ); + progressContexts = new Array( length ); + resolveContexts = new Array( length ); + for ( ; i < length; i++ ) { + if ( resolveValues[ i ] && jQuery.isFunction( resolveValues[ i ].promise ) ) { + resolveValues[ i ].promise() + .progress( updateFunc( i, progressContexts, progressValues ) ) + .done( updateFunc( i, resolveContexts, resolveValues ) ) + .fail( deferred.reject ); + } else { + --remaining; + } + } + } + + // if we're not waiting on anything, resolve the master + if ( !remaining ) { + deferred.resolveWith( resolveContexts, resolveValues ); + } + + return deferred.promise(); + } +} ); + + +// The deferred used on DOM ready +var readyList; + +jQuery.fn.ready = function( fn ) { + + // Add the callback + jQuery.ready.promise().done( fn ); + + return this; +}; + +jQuery.extend( { + + // Is the DOM ready to be used? Set to true once it occurs. + isReady: false, + + // A counter to track how many items to wait for before + // the ready event fires. See #6781 + readyWait: 1, + + // Hold (or release) the ready event + holdReady: function( hold ) { + if ( hold ) { + jQuery.readyWait++; + } else { + jQuery.ready( true ); + } + }, + + // Handle when the DOM is ready + ready: function( wait ) { + + // Abort if there are pending holds or we're already ready + if ( wait === true ? --jQuery.readyWait : jQuery.isReady ) { + return; + } + + // Remember that the DOM is ready + jQuery.isReady = true; + + // If a normal DOM Ready event fired, decrement, and wait if need be + if ( wait !== true && --jQuery.readyWait > 0 ) { + return; + } + + // If there are functions bound, to execute + readyList.resolveWith( document, [ jQuery ] ); + + // Trigger any bound ready events + if ( jQuery.fn.triggerHandler ) { + jQuery( document ).triggerHandler( "ready" ); + jQuery( document ).off( "ready" ); + } + } +} ); + +/** + * Clean-up method for dom ready events + */ +function detach() { + if ( document.addEventListener ) { + document.removeEventListener( "DOMContentLoaded", completed ); + window.removeEventListener( "load", completed ); + + } else { + document.detachEvent( "onreadystatechange", completed ); + window.detachEvent( "onload", completed ); + } +} + +/** + * The ready event handler and self cleanup method + */ +function completed() { + + // readyState === "complete" is good enough for us to call the dom ready in oldIE + if ( document.addEventListener || + window.event.type === "load" || + document.readyState === "complete" ) { + + detach(); + jQuery.ready(); + } +} + +jQuery.ready.promise = function( obj ) { + if ( !readyList ) { + + readyList = jQuery.Deferred(); + + // Catch cases where $(document).ready() is called + // after the browser event has already occurred. + // Support: IE6-10 + // Older IE sometimes signals "interactive" too soon + if ( document.readyState === "complete" || + ( document.readyState !== "loading" && !document.documentElement.doScroll ) ) { + + // Handle it asynchronously to allow scripts the opportunity to delay ready + window.setTimeout( jQuery.ready ); + + // Standards-based browsers support DOMContentLoaded + } else if ( document.addEventListener ) { + + // Use the handy event callback + document.addEventListener( "DOMContentLoaded", completed ); + + // A fallback to window.onload, that will always work + window.addEventListener( "load", completed ); + + // If IE event model is used + } else { + + // Ensure firing before onload, maybe late but safe also for iframes + document.attachEvent( "onreadystatechange", completed ); + + // A fallback to window.onload, that will always work + window.attachEvent( "onload", completed ); + + // If IE and not a frame + // continually check to see if the document is ready + var top = false; + + try { + top = window.frameElement == null && document.documentElement; + } catch ( e ) {} + + if ( top && top.doScroll ) { + ( function doScrollCheck() { + if ( !jQuery.isReady ) { + + try { + + // Use the trick by Diego Perini + // http://javascript.nwbox.com/IEContentLoaded/ + top.doScroll( "left" ); + } catch ( e ) { + return window.setTimeout( doScrollCheck, 50 ); + } + + // detach all dom ready events + detach(); + + // and execute any waiting functions + jQuery.ready(); + } + } )(); + } + } + } + return readyList.promise( obj ); +}; + +// Kick off the DOM ready check even if the user does not +jQuery.ready.promise(); + + + + +// Support: IE<9 +// Iteration over object's inherited properties before its own +var i; +for ( i in jQuery( support ) ) { + break; +} +support.ownFirst = i === "0"; + +// Note: most support tests are defined in their respective modules. +// false until the test is run +support.inlineBlockNeedsLayout = false; + +// Execute ASAP in case we need to set body.style.zoom +jQuery( function() { + + // Minified: var a,b,c,d + var val, div, body, container; + + body = document.getElementsByTagName( "body" )[ 0 ]; + if ( !body || !body.style ) { + + // Return for frameset docs that don't have a body + return; + } + + // Setup + div = document.createElement( "div" ); + container = document.createElement( "div" ); + container.style.cssText = "position:absolute;border:0;width:0;height:0;top:0;left:-9999px"; + body.appendChild( container ).appendChild( div ); + + if ( typeof div.style.zoom !== "undefined" ) { + + // Support: IE<8 + // Check if natively block-level elements act like inline-block + // elements when setting their display to 'inline' and giving + // them layout + div.style.cssText = "display:inline;margin:0;border:0;padding:1px;width:1px;zoom:1"; + + support.inlineBlockNeedsLayout = val = div.offsetWidth === 3; + if ( val ) { + + // Prevent IE 6 from affecting layout for positioned elements #11048 + // Prevent IE from shrinking the body in IE 7 mode #12869 + // Support: IE<8 + body.style.zoom = 1; + } + } + + body.removeChild( container ); +} ); + + +( function() { + var div = document.createElement( "div" ); + + // Support: IE<9 + support.deleteExpando = true; + try { + delete div.test; + } catch ( e ) { + support.deleteExpando = false; + } + + // Null elements to avoid leaks in IE. + div = null; +} )(); +var acceptData = function( elem ) { + var noData = jQuery.noData[ ( elem.nodeName + " " ).toLowerCase() ], + nodeType = +elem.nodeType || 1; + + // Do not set data on non-element DOM nodes because it will not be cleared (#8335). + return nodeType !== 1 && nodeType !== 9 ? + false : + + // Nodes accept data unless otherwise specified; rejection can be conditional + !noData || noData !== true && elem.getAttribute( "classid" ) === noData; +}; + + + + +var rbrace = /^(?:\{[\w\W]*\}|\[[\w\W]*\])$/, + rmultiDash = /([A-Z])/g; + +function dataAttr( elem, key, data ) { + + // If nothing was found internally, try to fetch any + // data from the HTML5 data-* attribute + if ( data === undefined && elem.nodeType === 1 ) { + + var name = "data-" + key.replace( rmultiDash, "-$1" ).toLowerCase(); + + data = elem.getAttribute( name ); + + if ( typeof data === "string" ) { + try { + data = data === "true" ? true : + data === "false" ? false : + data === "null" ? null : + + // Only convert to a number if it doesn't change the string + +data + "" === data ? +data : + rbrace.test( data ) ? jQuery.parseJSON( data ) : + data; + } catch ( e ) {} + + // Make sure we set the data so it isn't changed later + jQuery.data( elem, key, data ); + + } else { + data = undefined; + } + } + + return data; +} + +// checks a cache object for emptiness +function isEmptyDataObject( obj ) { + var name; + for ( name in obj ) { + + // if the public data object is empty, the private is still empty + if ( name === "data" && jQuery.isEmptyObject( obj[ name ] ) ) { + continue; + } + if ( name !== "toJSON" ) { + return false; + } + } + + return true; +} + +function internalData( elem, name, data, pvt /* Internal Use Only */ ) { + if ( !acceptData( elem ) ) { + return; + } + + var ret, thisCache, + internalKey = jQuery.expando, + + // We have to handle DOM nodes and JS objects differently because IE6-7 + // can't GC object references properly across the DOM-JS boundary + isNode = elem.nodeType, + + // Only DOM nodes need the global jQuery cache; JS object data is + // attached directly to the object so GC can occur automatically + cache = isNode ? jQuery.cache : elem, + + // Only defining an ID for JS objects if its cache already exists allows + // the code to shortcut on the same path as a DOM node with no cache + id = isNode ? elem[ internalKey ] : elem[ internalKey ] && internalKey; + + // Avoid doing any more work than we need to when trying to get data on an + // object that has no data at all + if ( ( !id || !cache[ id ] || ( !pvt && !cache[ id ].data ) ) && + data === undefined && typeof name === "string" ) { + return; + } + + if ( !id ) { + + // Only DOM nodes need a new unique ID for each element since their data + // ends up in the global cache + if ( isNode ) { + id = elem[ internalKey ] = deletedIds.pop() || jQuery.guid++; + } else { + id = internalKey; + } + } + + if ( !cache[ id ] ) { + + // Avoid exposing jQuery metadata on plain JS objects when the object + // is serialized using JSON.stringify + cache[ id ] = isNode ? {} : { toJSON: jQuery.noop }; + } + + // An object can be passed to jQuery.data instead of a key/value pair; this gets + // shallow copied over onto the existing cache + if ( typeof name === "object" || typeof name === "function" ) { + if ( pvt ) { + cache[ id ] = jQuery.extend( cache[ id ], name ); + } else { + cache[ id ].data = jQuery.extend( cache[ id ].data, name ); + } + } + + thisCache = cache[ id ]; + + // jQuery data() is stored in a separate object inside the object's internal data + // cache in order to avoid key collisions between internal data and user-defined + // data. + if ( !pvt ) { + if ( !thisCache.data ) { + thisCache.data = {}; + } + + thisCache = thisCache.data; + } + + if ( data !== undefined ) { + thisCache[ jQuery.camelCase( name ) ] = data; + } + + // Check for both converted-to-camel and non-converted data property names + // If a data property was specified + if ( typeof name === "string" ) { + + // First Try to find as-is property data + ret = thisCache[ name ]; + + // Test for null|undefined property data + if ( ret == null ) { + + // Try to find the camelCased property + ret = thisCache[ jQuery.camelCase( name ) ]; + } + } else { + ret = thisCache; + } + + return ret; +} + +function internalRemoveData( elem, name, pvt ) { + if ( !acceptData( elem ) ) { + return; + } + + var thisCache, i, + isNode = elem.nodeType, + + // See jQuery.data for more information + cache = isNode ? jQuery.cache : elem, + id = isNode ? elem[ jQuery.expando ] : jQuery.expando; + + // If there is already no cache entry for this object, there is no + // purpose in continuing + if ( !cache[ id ] ) { + return; + } + + if ( name ) { + + thisCache = pvt ? cache[ id ] : cache[ id ].data; + + if ( thisCache ) { + + // Support array or space separated string names for data keys + if ( !jQuery.isArray( name ) ) { + + // try the string as a key before any manipulation + if ( name in thisCache ) { + name = [ name ]; + } else { + + // split the camel cased version by spaces unless a key with the spaces exists + name = jQuery.camelCase( name ); + if ( name in thisCache ) { + name = [ name ]; + } else { + name = name.split( " " ); + } + } + } else { + + // If "name" is an array of keys... + // When data is initially created, via ("key", "val") signature, + // keys will be converted to camelCase. + // Since there is no way to tell _how_ a key was added, remove + // both plain key and camelCase key. #12786 + // This will only penalize the array argument path. + name = name.concat( jQuery.map( name, jQuery.camelCase ) ); + } + + i = name.length; + while ( i-- ) { + delete thisCache[ name[ i ] ]; + } + + // If there is no data left in the cache, we want to continue + // and let the cache object itself get destroyed + if ( pvt ? !isEmptyDataObject( thisCache ) : !jQuery.isEmptyObject( thisCache ) ) { + return; + } + } + } + + // See jQuery.data for more information + if ( !pvt ) { + delete cache[ id ].data; + + // Don't destroy the parent cache unless the internal data object + // had been the only thing left in it + if ( !isEmptyDataObject( cache[ id ] ) ) { + return; + } + } + + // Destroy the cache + if ( isNode ) { + jQuery.cleanData( [ elem ], true ); + + // Use delete when supported for expandos or `cache` is not a window per isWindow (#10080) + /* jshint eqeqeq: false */ + } else if ( support.deleteExpando || cache != cache.window ) { + /* jshint eqeqeq: true */ + delete cache[ id ]; + + // When all else fails, undefined + } else { + cache[ id ] = undefined; + } +} + +jQuery.extend( { + cache: {}, + + // The following elements (space-suffixed to avoid Object.prototype collisions) + // throw uncatchable exceptions if you attempt to set expando properties + noData: { + "applet ": true, + "embed ": true, + + // ...but Flash objects (which have this classid) *can* handle expandos + "object ": "clsid:D27CDB6E-AE6D-11cf-96B8-444553540000" + }, + + hasData: function( elem ) { + elem = elem.nodeType ? jQuery.cache[ elem[ jQuery.expando ] ] : elem[ jQuery.expando ]; + return !!elem && !isEmptyDataObject( elem ); + }, + + data: function( elem, name, data ) { + return internalData( elem, name, data ); + }, + + removeData: function( elem, name ) { + return internalRemoveData( elem, name ); + }, + + // For internal use only. + _data: function( elem, name, data ) { + return internalData( elem, name, data, true ); + }, + + _removeData: function( elem, name ) { + return internalRemoveData( elem, name, true ); + } +} ); + +jQuery.fn.extend( { + data: function( key, value ) { + var i, name, data, + elem = this[ 0 ], + attrs = elem && elem.attributes; + + // Special expections of .data basically thwart jQuery.access, + // so implement the relevant behavior ourselves + + // Gets all values + if ( key === undefined ) { + if ( this.length ) { + data = jQuery.data( elem ); + + if ( elem.nodeType === 1 && !jQuery._data( elem, "parsedAttrs" ) ) { + i = attrs.length; + while ( i-- ) { + + // Support: IE11+ + // The attrs elements can be null (#14894) + if ( attrs[ i ] ) { + name = attrs[ i ].name; + if ( name.indexOf( "data-" ) === 0 ) { + name = jQuery.camelCase( name.slice( 5 ) ); + dataAttr( elem, name, data[ name ] ); + } + } + } + jQuery._data( elem, "parsedAttrs", true ); + } + } + + return data; + } + + // Sets multiple values + if ( typeof key === "object" ) { + return this.each( function() { + jQuery.data( this, key ); + } ); + } + + return arguments.length > 1 ? + + // Sets one value + this.each( function() { + jQuery.data( this, key, value ); + } ) : + + // Gets one value + // Try to fetch any internally stored data first + elem ? dataAttr( elem, key, jQuery.data( elem, key ) ) : undefined; + }, + + removeData: function( key ) { + return this.each( function() { + jQuery.removeData( this, key ); + } ); + } +} ); + + +jQuery.extend( { + queue: function( elem, type, data ) { + var queue; + + if ( elem ) { + type = ( type || "fx" ) + "queue"; + queue = jQuery._data( elem, type ); + + // Speed up dequeue by getting out quickly if this is just a lookup + if ( data ) { + if ( !queue || jQuery.isArray( data ) ) { + queue = jQuery._data( elem, type, jQuery.makeArray( data ) ); + } else { + queue.push( data ); + } + } + return queue || []; + } + }, + + dequeue: function( elem, type ) { + type = type || "fx"; + + var queue = jQuery.queue( elem, type ), + startLength = queue.length, + fn = queue.shift(), + hooks = jQuery._queueHooks( elem, type ), + next = function() { + jQuery.dequeue( elem, type ); + }; + + // If the fx queue is dequeued, always remove the progress sentinel + if ( fn === "inprogress" ) { + fn = queue.shift(); + startLength--; + } + + if ( fn ) { + + // Add a progress sentinel to prevent the fx queue from being + // automatically dequeued + if ( type === "fx" ) { + queue.unshift( "inprogress" ); + } + + // clear up the last queue stop function + delete hooks.stop; + fn.call( elem, next, hooks ); + } + + if ( !startLength && hooks ) { + hooks.empty.fire(); + } + }, + + // not intended for public consumption - generates a queueHooks object, + // or returns the current one + _queueHooks: function( elem, type ) { + var key = type + "queueHooks"; + return jQuery._data( elem, key ) || jQuery._data( elem, key, { + empty: jQuery.Callbacks( "once memory" ).add( function() { + jQuery._removeData( elem, type + "queue" ); + jQuery._removeData( elem, key ); + } ) + } ); + } +} ); + +jQuery.fn.extend( { + queue: function( type, data ) { + var setter = 2; + + if ( typeof type !== "string" ) { + data = type; + type = "fx"; + setter--; + } + + if ( arguments.length < setter ) { + return jQuery.queue( this[ 0 ], type ); + } + + return data === undefined ? + this : + this.each( function() { + var queue = jQuery.queue( this, type, data ); + + // ensure a hooks for this queue + jQuery._queueHooks( this, type ); + + if ( type === "fx" && queue[ 0 ] !== "inprogress" ) { + jQuery.dequeue( this, type ); + } + } ); + }, + dequeue: function( type ) { + return this.each( function() { + jQuery.dequeue( this, type ); + } ); + }, + clearQueue: function( type ) { + return this.queue( type || "fx", [] ); + }, + + // Get a promise resolved when queues of a certain type + // are emptied (fx is the type by default) + promise: function( type, obj ) { + var tmp, + count = 1, + defer = jQuery.Deferred(), + elements = this, + i = this.length, + resolve = function() { + if ( !( --count ) ) { + defer.resolveWith( elements, [ elements ] ); + } + }; + + if ( typeof type !== "string" ) { + obj = type; + type = undefined; + } + type = type || "fx"; + + while ( i-- ) { + tmp = jQuery._data( elements[ i ], type + "queueHooks" ); + if ( tmp && tmp.empty ) { + count++; + tmp.empty.add( resolve ); + } + } + resolve(); + return defer.promise( obj ); + } +} ); + + +( function() { + var shrinkWrapBlocksVal; + + support.shrinkWrapBlocks = function() { + if ( shrinkWrapBlocksVal != null ) { + return shrinkWrapBlocksVal; + } + + // Will be changed later if needed. + shrinkWrapBlocksVal = false; + + // Minified: var b,c,d + var div, body, container; + + body = document.getElementsByTagName( "body" )[ 0 ]; + if ( !body || !body.style ) { + + // Test fired too early or in an unsupported environment, exit. + return; + } + + // Setup + div = document.createElement( "div" ); + container = document.createElement( "div" ); + container.style.cssText = "position:absolute;border:0;width:0;height:0;top:0;left:-9999px"; + body.appendChild( container ).appendChild( div ); + + // Support: IE6 + // Check if elements with layout shrink-wrap their children + if ( typeof div.style.zoom !== "undefined" ) { + + // Reset CSS: box-sizing; display; margin; border + div.style.cssText = + + // Support: Firefox<29, Android 2.3 + // Vendor-prefix box-sizing + "-webkit-box-sizing:content-box;-moz-box-sizing:content-box;" + + "box-sizing:content-box;display:block;margin:0;border:0;" + + "padding:1px;width:1px;zoom:1"; + div.appendChild( document.createElement( "div" ) ).style.width = "5px"; + shrinkWrapBlocksVal = div.offsetWidth !== 3; + } + + body.removeChild( container ); + + return shrinkWrapBlocksVal; + }; + +} )(); +var pnum = ( /[+-]?(?:\d*\.|)\d+(?:[eE][+-]?\d+|)/ ).source; + +var rcssNum = new RegExp( "^(?:([+-])=|)(" + pnum + ")([a-z%]*)$", "i" ); + + +var cssExpand = [ "Top", "Right", "Bottom", "Left" ]; + +var isHidden = function( elem, el ) { + + // isHidden might be called from jQuery#filter function; + // in that case, element will be second argument + elem = el || elem; + return jQuery.css( elem, "display" ) === "none" || + !jQuery.contains( elem.ownerDocument, elem ); + }; + + + +function adjustCSS( elem, prop, valueParts, tween ) { + var adjusted, + scale = 1, + maxIterations = 20, + currentValue = tween ? + function() { return tween.cur(); } : + function() { return jQuery.css( elem, prop, "" ); }, + initial = currentValue(), + unit = valueParts && valueParts[ 3 ] || ( jQuery.cssNumber[ prop ] ? "" : "px" ), + + // Starting value computation is required for potential unit mismatches + initialInUnit = ( jQuery.cssNumber[ prop ] || unit !== "px" && +initial ) && + rcssNum.exec( jQuery.css( elem, prop ) ); + + if ( initialInUnit && initialInUnit[ 3 ] !== unit ) { + + // Trust units reported by jQuery.css + unit = unit || initialInUnit[ 3 ]; + + // Make sure we update the tween properties later on + valueParts = valueParts || []; + + // Iteratively approximate from a nonzero starting point + initialInUnit = +initial || 1; + + do { + + // If previous iteration zeroed out, double until we get *something*. + // Use string for doubling so we don't accidentally see scale as unchanged below + scale = scale || ".5"; + + // Adjust and apply + initialInUnit = initialInUnit / scale; + jQuery.style( elem, prop, initialInUnit + unit ); + + // Update scale, tolerating zero or NaN from tween.cur() + // Break the loop if scale is unchanged or perfect, or if we've just had enough. + } while ( + scale !== ( scale = currentValue() / initial ) && scale !== 1 && --maxIterations + ); + } + + if ( valueParts ) { + initialInUnit = +initialInUnit || +initial || 0; + + // Apply relative offset (+=/-=) if specified + adjusted = valueParts[ 1 ] ? + initialInUnit + ( valueParts[ 1 ] + 1 ) * valueParts[ 2 ] : + +valueParts[ 2 ]; + if ( tween ) { + tween.unit = unit; + tween.start = initialInUnit; + tween.end = adjusted; + } + } + return adjusted; +} + + +// Multifunctional method to get and set values of a collection +// The value/s can optionally be executed if it's a function +var access = function( elems, fn, key, value, chainable, emptyGet, raw ) { + var i = 0, + length = elems.length, + bulk = key == null; + + // Sets many values + if ( jQuery.type( key ) === "object" ) { + chainable = true; + for ( i in key ) { + access( elems, fn, i, key[ i ], true, emptyGet, raw ); + } + + // Sets one value + } else if ( value !== undefined ) { + chainable = true; + + if ( !jQuery.isFunction( value ) ) { + raw = true; + } + + if ( bulk ) { + + // Bulk operations run against the entire set + if ( raw ) { + fn.call( elems, value ); + fn = null; + + // ...except when executing function values + } else { + bulk = fn; + fn = function( elem, key, value ) { + return bulk.call( jQuery( elem ), value ); + }; + } + } + + if ( fn ) { + for ( ; i < length; i++ ) { + fn( + elems[ i ], + key, + raw ? value : value.call( elems[ i ], i, fn( elems[ i ], key ) ) + ); + } + } + } + + return chainable ? + elems : + + // Gets + bulk ? + fn.call( elems ) : + length ? fn( elems[ 0 ], key ) : emptyGet; +}; +var rcheckableType = ( /^(?:checkbox|radio)$/i ); + +var rtagName = ( /<([\w:-]+)/ ); + +var rscriptType = ( /^$|\/(?:java|ecma)script/i ); + +var rleadingWhitespace = ( /^\s+/ ); + +var nodeNames = "abbr|article|aside|audio|bdi|canvas|data|datalist|" + + "details|dialog|figcaption|figure|footer|header|hgroup|main|" + + "mark|meter|nav|output|picture|progress|section|summary|template|time|video"; + + + +function createSafeFragment( document ) { + var list = nodeNames.split( "|" ), + safeFrag = document.createDocumentFragment(); + + if ( safeFrag.createElement ) { + while ( list.length ) { + safeFrag.createElement( + list.pop() + ); + } + } + return safeFrag; +} + + +( function() { + var div = document.createElement( "div" ), + fragment = document.createDocumentFragment(), + input = document.createElement( "input" ); + + // Setup + div.innerHTML = "
a"; + + // IE strips leading whitespace when .innerHTML is used + support.leadingWhitespace = div.firstChild.nodeType === 3; + + // Make sure that tbody elements aren't automatically inserted + // IE will insert them into empty tables + support.tbody = !div.getElementsByTagName( "tbody" ).length; + + // Make sure that link elements get serialized correctly by innerHTML + // This requires a wrapper element in IE + support.htmlSerialize = !!div.getElementsByTagName( "link" ).length; + + // Makes sure cloning an html5 element does not cause problems + // Where outerHTML is undefined, this still works + support.html5Clone = + document.createElement( "nav" ).cloneNode( true ).outerHTML !== "<:nav>"; + + // Check if a disconnected checkbox will retain its checked + // value of true after appended to the DOM (IE6/7) + input.type = "checkbox"; + input.checked = true; + fragment.appendChild( input ); + support.appendChecked = input.checked; + + // Make sure textarea (and checkbox) defaultValue is properly cloned + // Support: IE6-IE11+ + div.innerHTML = ""; + support.noCloneChecked = !!div.cloneNode( true ).lastChild.defaultValue; + + // #11217 - WebKit loses check when the name is after the checked attribute + fragment.appendChild( div ); + + // Support: Windows Web Apps (WWA) + // `name` and `type` must use .setAttribute for WWA (#14901) + input = document.createElement( "input" ); + input.setAttribute( "type", "radio" ); + input.setAttribute( "checked", "checked" ); + input.setAttribute( "name", "t" ); + + div.appendChild( input ); + + // Support: Safari 5.1, iOS 5.1, Android 4.x, Android 2.3 + // old WebKit doesn't clone checked state correctly in fragments + support.checkClone = div.cloneNode( true ).cloneNode( true ).lastChild.checked; + + // Support: IE<9 + // Cloned elements keep attachEvent handlers, we use addEventListener on IE9+ + support.noCloneEvent = !!div.addEventListener; + + // Support: IE<9 + // Since attributes and properties are the same in IE, + // cleanData must set properties to undefined rather than use removeAttribute + div[ jQuery.expando ] = 1; + support.attributes = !div.getAttribute( jQuery.expando ); +} )(); + + +// We have to close these tags to support XHTML (#13200) +var wrapMap = { + option: [ 1, "" ], + legend: [ 1, "
", "
" ], + area: [ 1, "", "" ], + + // Support: IE8 + param: [ 1, "", "" ], + thead: [ 1, "", "
" ], + tr: [ 2, "", "
" ], + col: [ 2, "", "
" ], + td: [ 3, "", "
" ], + + // IE6-8 can't serialize link, script, style, or any html5 (NoScope) tags, + // unless wrapped in a div with non-breaking characters in front of it. + _default: support.htmlSerialize ? [ 0, "", "" ] : [ 1, "X
", "
" ] +}; + +// Support: IE8-IE9 +wrapMap.optgroup = wrapMap.option; + +wrapMap.tbody = wrapMap.tfoot = wrapMap.colgroup = wrapMap.caption = wrapMap.thead; +wrapMap.th = wrapMap.td; + + +function getAll( context, tag ) { + var elems, elem, + i = 0, + found = typeof context.getElementsByTagName !== "undefined" ? + context.getElementsByTagName( tag || "*" ) : + typeof context.querySelectorAll !== "undefined" ? + context.querySelectorAll( tag || "*" ) : + undefined; + + if ( !found ) { + for ( found = [], elems = context.childNodes || context; + ( elem = elems[ i ] ) != null; + i++ + ) { + if ( !tag || jQuery.nodeName( elem, tag ) ) { + found.push( elem ); + } else { + jQuery.merge( found, getAll( elem, tag ) ); + } + } + } + + return tag === undefined || tag && jQuery.nodeName( context, tag ) ? + jQuery.merge( [ context ], found ) : + found; +} + + +// Mark scripts as having already been evaluated +function setGlobalEval( elems, refElements ) { + var elem, + i = 0; + for ( ; ( elem = elems[ i ] ) != null; i++ ) { + jQuery._data( + elem, + "globalEval", + !refElements || jQuery._data( refElements[ i ], "globalEval" ) + ); + } +} + + +var rhtml = /<|&#?\w+;/, + rtbody = / from table fragments + if ( !support.tbody ) { + + // String was a , *may* have spurious + elem = tag === "table" && !rtbody.test( elem ) ? + tmp.firstChild : + + // String was a bare or + wrap[ 1 ] === "
" && !rtbody.test( elem ) ? + tmp : + 0; + + j = elem && elem.childNodes.length; + while ( j-- ) { + if ( jQuery.nodeName( ( tbody = elem.childNodes[ j ] ), "tbody" ) && + !tbody.childNodes.length ) { + + elem.removeChild( tbody ); + } + } + } + + jQuery.merge( nodes, tmp.childNodes ); + + // Fix #12392 for WebKit and IE > 9 + tmp.textContent = ""; + + // Fix #12392 for oldIE + while ( tmp.firstChild ) { + tmp.removeChild( tmp.firstChild ); + } + + // Remember the top-level container for proper cleanup + tmp = safe.lastChild; + } + } + } + + // Fix #11356: Clear elements from fragment + if ( tmp ) { + safe.removeChild( tmp ); + } + + // Reset defaultChecked for any radios and checkboxes + // about to be appended to the DOM in IE 6/7 (#8060) + if ( !support.appendChecked ) { + jQuery.grep( getAll( nodes, "input" ), fixDefaultChecked ); + } + + i = 0; + while ( ( elem = nodes[ i++ ] ) ) { + + // Skip elements already in the context collection (trac-4087) + if ( selection && jQuery.inArray( elem, selection ) > -1 ) { + if ( ignored ) { + ignored.push( elem ); + } + + continue; + } + + contains = jQuery.contains( elem.ownerDocument, elem ); + + // Append to fragment + tmp = getAll( safe.appendChild( elem ), "script" ); + + // Preserve script evaluation history + if ( contains ) { + setGlobalEval( tmp ); + } + + // Capture executables + if ( scripts ) { + j = 0; + while ( ( elem = tmp[ j++ ] ) ) { + if ( rscriptType.test( elem.type || "" ) ) { + scripts.push( elem ); + } + } + } + } + + tmp = null; + + return safe; +} + + +( function() { + var i, eventName, + div = document.createElement( "div" ); + + // Support: IE<9 (lack submit/change bubble), Firefox (lack focus(in | out) events) + for ( i in { submit: true, change: true, focusin: true } ) { + eventName = "on" + i; + + if ( !( support[ i ] = eventName in window ) ) { + + // Beware of CSP restrictions (https://developer.mozilla.org/en/Security/CSP) + div.setAttribute( eventName, "t" ); + support[ i ] = div.attributes[ eventName ].expando === false; + } + } + + // Null elements to avoid leaks in IE. + div = null; +} )(); + + +var rformElems = /^(?:input|select|textarea)$/i, + rkeyEvent = /^key/, + rmouseEvent = /^(?:mouse|pointer|contextmenu|drag|drop)|click/, + rfocusMorph = /^(?:focusinfocus|focusoutblur)$/, + rtypenamespace = /^([^.]*)(?:\.(.+)|)/; + +function returnTrue() { + return true; +} + +function returnFalse() { + return false; +} + +// Support: IE9 +// See #13393 for more info +function safeActiveElement() { + try { + return document.activeElement; + } catch ( err ) { } +} + +function on( elem, types, selector, data, fn, one ) { + var origFn, type; + + // Types can be a map of types/handlers + if ( typeof types === "object" ) { + + // ( types-Object, selector, data ) + if ( typeof selector !== "string" ) { + + // ( types-Object, data ) + data = data || selector; + selector = undefined; + } + for ( type in types ) { + on( elem, type, selector, data, types[ type ], one ); + } + return elem; + } + + if ( data == null && fn == null ) { + + // ( types, fn ) + fn = selector; + data = selector = undefined; + } else if ( fn == null ) { + if ( typeof selector === "string" ) { + + // ( types, selector, fn ) + fn = data; + data = undefined; + } else { + + // ( types, data, fn ) + fn = data; + data = selector; + selector = undefined; + } + } + if ( fn === false ) { + fn = returnFalse; + } else if ( !fn ) { + return elem; + } + + if ( one === 1 ) { + origFn = fn; + fn = function( event ) { + + // Can use an empty set, since event contains the info + jQuery().off( event ); + return origFn.apply( this, arguments ); + }; + + // Use same guid so caller can remove using origFn + fn.guid = origFn.guid || ( origFn.guid = jQuery.guid++ ); + } + return elem.each( function() { + jQuery.event.add( this, types, fn, data, selector ); + } ); +} + +/* + * Helper functions for managing events -- not part of the public interface. + * Props to Dean Edwards' addEvent library for many of the ideas. + */ +jQuery.event = { + + global: {}, + + add: function( elem, types, handler, data, selector ) { + var tmp, events, t, handleObjIn, + special, eventHandle, handleObj, + handlers, type, namespaces, origType, + elemData = jQuery._data( elem ); + + // Don't attach events to noData or text/comment nodes (but allow plain objects) + if ( !elemData ) { + return; + } + + // Caller can pass in an object of custom data in lieu of the handler + if ( handler.handler ) { + handleObjIn = handler; + handler = handleObjIn.handler; + selector = handleObjIn.selector; + } + + // Make sure that the handler has a unique ID, used to find/remove it later + if ( !handler.guid ) { + handler.guid = jQuery.guid++; + } + + // Init the element's event structure and main handler, if this is the first + if ( !( events = elemData.events ) ) { + events = elemData.events = {}; + } + if ( !( eventHandle = elemData.handle ) ) { + eventHandle = elemData.handle = function( e ) { + + // Discard the second event of a jQuery.event.trigger() and + // when an event is called after a page has unloaded + return typeof jQuery !== "undefined" && + ( !e || jQuery.event.triggered !== e.type ) ? + jQuery.event.dispatch.apply( eventHandle.elem, arguments ) : + undefined; + }; + + // Add elem as a property of the handle fn to prevent a memory leak + // with IE non-native events + eventHandle.elem = elem; + } + + // Handle multiple events separated by a space + types = ( types || "" ).match( rnotwhite ) || [ "" ]; + t = types.length; + while ( t-- ) { + tmp = rtypenamespace.exec( types[ t ] ) || []; + type = origType = tmp[ 1 ]; + namespaces = ( tmp[ 2 ] || "" ).split( "." ).sort(); + + // There *must* be a type, no attaching namespace-only handlers + if ( !type ) { + continue; + } + + // If event changes its type, use the special event handlers for the changed type + special = jQuery.event.special[ type ] || {}; + + // If selector defined, determine special event api type, otherwise given type + type = ( selector ? special.delegateType : special.bindType ) || type; + + // Update special based on newly reset type + special = jQuery.event.special[ type ] || {}; + + // handleObj is passed to all event handlers + handleObj = jQuery.extend( { + type: type, + origType: origType, + data: data, + handler: handler, + guid: handler.guid, + selector: selector, + needsContext: selector && jQuery.expr.match.needsContext.test( selector ), + namespace: namespaces.join( "." ) + }, handleObjIn ); + + // Init the event handler queue if we're the first + if ( !( handlers = events[ type ] ) ) { + handlers = events[ type ] = []; + handlers.delegateCount = 0; + + // Only use addEventListener/attachEvent if the special events handler returns false + if ( !special.setup || + special.setup.call( elem, data, namespaces, eventHandle ) === false ) { + + // Bind the global event handler to the element + if ( elem.addEventListener ) { + elem.addEventListener( type, eventHandle, false ); + + } else if ( elem.attachEvent ) { + elem.attachEvent( "on" + type, eventHandle ); + } + } + } + + if ( special.add ) { + special.add.call( elem, handleObj ); + + if ( !handleObj.handler.guid ) { + handleObj.handler.guid = handler.guid; + } + } + + // Add to the element's handler list, delegates in front + if ( selector ) { + handlers.splice( handlers.delegateCount++, 0, handleObj ); + } else { + handlers.push( handleObj ); + } + + // Keep track of which events have ever been used, for event optimization + jQuery.event.global[ type ] = true; + } + + // Nullify elem to prevent memory leaks in IE + elem = null; + }, + + // Detach an event or set of events from an element + remove: function( elem, types, handler, selector, mappedTypes ) { + var j, handleObj, tmp, + origCount, t, events, + special, handlers, type, + namespaces, origType, + elemData = jQuery.hasData( elem ) && jQuery._data( elem ); + + if ( !elemData || !( events = elemData.events ) ) { + return; + } + + // Once for each type.namespace in types; type may be omitted + types = ( types || "" ).match( rnotwhite ) || [ "" ]; + t = types.length; + while ( t-- ) { + tmp = rtypenamespace.exec( types[ t ] ) || []; + type = origType = tmp[ 1 ]; + namespaces = ( tmp[ 2 ] || "" ).split( "." ).sort(); + + // Unbind all events (on this namespace, if provided) for the element + if ( !type ) { + for ( type in events ) { + jQuery.event.remove( elem, type + types[ t ], handler, selector, true ); + } + continue; + } + + special = jQuery.event.special[ type ] || {}; + type = ( selector ? special.delegateType : special.bindType ) || type; + handlers = events[ type ] || []; + tmp = tmp[ 2 ] && + new RegExp( "(^|\\.)" + namespaces.join( "\\.(?:.*\\.|)" ) + "(\\.|$)" ); + + // Remove matching events + origCount = j = handlers.length; + while ( j-- ) { + handleObj = handlers[ j ]; + + if ( ( mappedTypes || origType === handleObj.origType ) && + ( !handler || handler.guid === handleObj.guid ) && + ( !tmp || tmp.test( handleObj.namespace ) ) && + ( !selector || selector === handleObj.selector || + selector === "**" && handleObj.selector ) ) { + handlers.splice( j, 1 ); + + if ( handleObj.selector ) { + handlers.delegateCount--; + } + if ( special.remove ) { + special.remove.call( elem, handleObj ); + } + } + } + + // Remove generic event handler if we removed something and no more handlers exist + // (avoids potential for endless recursion during removal of special event handlers) + if ( origCount && !handlers.length ) { + if ( !special.teardown || + special.teardown.call( elem, namespaces, elemData.handle ) === false ) { + + jQuery.removeEvent( elem, type, elemData.handle ); + } + + delete events[ type ]; + } + } + + // Remove the expando if it's no longer used + if ( jQuery.isEmptyObject( events ) ) { + delete elemData.handle; + + // removeData also checks for emptiness and clears the expando if empty + // so use it instead of delete + jQuery._removeData( elem, "events" ); + } + }, + + trigger: function( event, data, elem, onlyHandlers ) { + var handle, ontype, cur, + bubbleType, special, tmp, i, + eventPath = [ elem || document ], + type = hasOwn.call( event, "type" ) ? event.type : event, + namespaces = hasOwn.call( event, "namespace" ) ? event.namespace.split( "." ) : []; + + cur = tmp = elem = elem || document; + + // Don't do events on text and comment nodes + if ( elem.nodeType === 3 || elem.nodeType === 8 ) { + return; + } + + // focus/blur morphs to focusin/out; ensure we're not firing them right now + if ( rfocusMorph.test( type + jQuery.event.triggered ) ) { + return; + } + + if ( type.indexOf( "." ) > -1 ) { + + // Namespaced trigger; create a regexp to match event type in handle() + namespaces = type.split( "." ); + type = namespaces.shift(); + namespaces.sort(); + } + ontype = type.indexOf( ":" ) < 0 && "on" + type; + + // Caller can pass in a jQuery.Event object, Object, or just an event type string + event = event[ jQuery.expando ] ? + event : + new jQuery.Event( type, typeof event === "object" && event ); + + // Trigger bitmask: & 1 for native handlers; & 2 for jQuery (always true) + event.isTrigger = onlyHandlers ? 2 : 3; + event.namespace = namespaces.join( "." ); + event.rnamespace = event.namespace ? + new RegExp( "(^|\\.)" + namespaces.join( "\\.(?:.*\\.|)" ) + "(\\.|$)" ) : + null; + + // Clean up the event in case it is being reused + event.result = undefined; + if ( !event.target ) { + event.target = elem; + } + + // Clone any incoming data and prepend the event, creating the handler arg list + data = data == null ? + [ event ] : + jQuery.makeArray( data, [ event ] ); + + // Allow special events to draw outside the lines + special = jQuery.event.special[ type ] || {}; + if ( !onlyHandlers && special.trigger && special.trigger.apply( elem, data ) === false ) { + return; + } + + // Determine event propagation path in advance, per W3C events spec (#9951) + // Bubble up to document, then to window; watch for a global ownerDocument var (#9724) + if ( !onlyHandlers && !special.noBubble && !jQuery.isWindow( elem ) ) { + + bubbleType = special.delegateType || type; + if ( !rfocusMorph.test( bubbleType + type ) ) { + cur = cur.parentNode; + } + for ( ; cur; cur = cur.parentNode ) { + eventPath.push( cur ); + tmp = cur; + } + + // Only add window if we got to document (e.g., not plain obj or detached DOM) + if ( tmp === ( elem.ownerDocument || document ) ) { + eventPath.push( tmp.defaultView || tmp.parentWindow || window ); + } + } + + // Fire handlers on the event path + i = 0; + while ( ( cur = eventPath[ i++ ] ) && !event.isPropagationStopped() ) { + + event.type = i > 1 ? + bubbleType : + special.bindType || type; + + // jQuery handler + handle = ( jQuery._data( cur, "events" ) || {} )[ event.type ] && + jQuery._data( cur, "handle" ); + + if ( handle ) { + handle.apply( cur, data ); + } + + // Native handler + handle = ontype && cur[ ontype ]; + if ( handle && handle.apply && acceptData( cur ) ) { + event.result = handle.apply( cur, data ); + if ( event.result === false ) { + event.preventDefault(); + } + } + } + event.type = type; + + // If nobody prevented the default action, do it now + if ( !onlyHandlers && !event.isDefaultPrevented() ) { + + if ( + ( !special._default || + special._default.apply( eventPath.pop(), data ) === false + ) && acceptData( elem ) + ) { + + // Call a native DOM method on the target with the same name name as the event. + // Can't use an .isFunction() check here because IE6/7 fails that test. + // Don't do default actions on window, that's where global variables be (#6170) + if ( ontype && elem[ type ] && !jQuery.isWindow( elem ) ) { + + // Don't re-trigger an onFOO event when we call its FOO() method + tmp = elem[ ontype ]; + + if ( tmp ) { + elem[ ontype ] = null; + } + + // Prevent re-triggering of the same event, since we already bubbled it above + jQuery.event.triggered = type; + try { + elem[ type ](); + } catch ( e ) { + + // IE<9 dies on focus/blur to hidden element (#1486,#12518) + // only reproducible on winXP IE8 native, not IE9 in IE8 mode + } + jQuery.event.triggered = undefined; + + if ( tmp ) { + elem[ ontype ] = tmp; + } + } + } + } + + return event.result; + }, + + dispatch: function( event ) { + + // Make a writable jQuery.Event from the native event object + event = jQuery.event.fix( event ); + + var i, j, ret, matched, handleObj, + handlerQueue = [], + args = slice.call( arguments ), + handlers = ( jQuery._data( this, "events" ) || {} )[ event.type ] || [], + special = jQuery.event.special[ event.type ] || {}; + + // Use the fix-ed jQuery.Event rather than the (read-only) native event + args[ 0 ] = event; + event.delegateTarget = this; + + // Call the preDispatch hook for the mapped type, and let it bail if desired + if ( special.preDispatch && special.preDispatch.call( this, event ) === false ) { + return; + } + + // Determine handlers + handlerQueue = jQuery.event.handlers.call( this, event, handlers ); + + // Run delegates first; they may want to stop propagation beneath us + i = 0; + while ( ( matched = handlerQueue[ i++ ] ) && !event.isPropagationStopped() ) { + event.currentTarget = matched.elem; + + j = 0; + while ( ( handleObj = matched.handlers[ j++ ] ) && + !event.isImmediatePropagationStopped() ) { + + // Triggered event must either 1) have no namespace, or 2) have namespace(s) + // a subset or equal to those in the bound event (both can have no namespace). + if ( !event.rnamespace || event.rnamespace.test( handleObj.namespace ) ) { + + event.handleObj = handleObj; + event.data = handleObj.data; + + ret = ( ( jQuery.event.special[ handleObj.origType ] || {} ).handle || + handleObj.handler ).apply( matched.elem, args ); + + if ( ret !== undefined ) { + if ( ( event.result = ret ) === false ) { + event.preventDefault(); + event.stopPropagation(); + } + } + } + } + } + + // Call the postDispatch hook for the mapped type + if ( special.postDispatch ) { + special.postDispatch.call( this, event ); + } + + return event.result; + }, + + handlers: function( event, handlers ) { + var i, matches, sel, handleObj, + handlerQueue = [], + delegateCount = handlers.delegateCount, + cur = event.target; + + // Support (at least): Chrome, IE9 + // Find delegate handlers + // Black-hole SVG instance trees (#13180) + // + // Support: Firefox<=42+ + // Avoid non-left-click in FF but don't block IE radio events (#3861, gh-2343) + if ( delegateCount && cur.nodeType && + ( event.type !== "click" || isNaN( event.button ) || event.button < 1 ) ) { + + /* jshint eqeqeq: false */ + for ( ; cur != this; cur = cur.parentNode || this ) { + /* jshint eqeqeq: true */ + + // Don't check non-elements (#13208) + // Don't process clicks on disabled elements (#6911, #8165, #11382, #11764) + if ( cur.nodeType === 1 && ( cur.disabled !== true || event.type !== "click" ) ) { + matches = []; + for ( i = 0; i < delegateCount; i++ ) { + handleObj = handlers[ i ]; + + // Don't conflict with Object.prototype properties (#13203) + sel = handleObj.selector + " "; + + if ( matches[ sel ] === undefined ) { + matches[ sel ] = handleObj.needsContext ? + jQuery( sel, this ).index( cur ) > -1 : + jQuery.find( sel, this, null, [ cur ] ).length; + } + if ( matches[ sel ] ) { + matches.push( handleObj ); + } + } + if ( matches.length ) { + handlerQueue.push( { elem: cur, handlers: matches } ); + } + } + } + } + + // Add the remaining (directly-bound) handlers + if ( delegateCount < handlers.length ) { + handlerQueue.push( { elem: this, handlers: handlers.slice( delegateCount ) } ); + } + + return handlerQueue; + }, + + fix: function( event ) { + if ( event[ jQuery.expando ] ) { + return event; + } + + // Create a writable copy of the event object and normalize some properties + var i, prop, copy, + type = event.type, + originalEvent = event, + fixHook = this.fixHooks[ type ]; + + if ( !fixHook ) { + this.fixHooks[ type ] = fixHook = + rmouseEvent.test( type ) ? this.mouseHooks : + rkeyEvent.test( type ) ? this.keyHooks : + {}; + } + copy = fixHook.props ? this.props.concat( fixHook.props ) : this.props; + + event = new jQuery.Event( originalEvent ); + + i = copy.length; + while ( i-- ) { + prop = copy[ i ]; + event[ prop ] = originalEvent[ prop ]; + } + + // Support: IE<9 + // Fix target property (#1925) + if ( !event.target ) { + event.target = originalEvent.srcElement || document; + } + + // Support: Safari 6-8+ + // Target should not be a text node (#504, #13143) + if ( event.target.nodeType === 3 ) { + event.target = event.target.parentNode; + } + + // Support: IE<9 + // For mouse/key events, metaKey==false if it's undefined (#3368, #11328) + event.metaKey = !!event.metaKey; + + return fixHook.filter ? fixHook.filter( event, originalEvent ) : event; + }, + + // Includes some event props shared by KeyEvent and MouseEvent + props: ( "altKey bubbles cancelable ctrlKey currentTarget detail eventPhase " + + "metaKey relatedTarget shiftKey target timeStamp view which" ).split( " " ), + + fixHooks: {}, + + keyHooks: { + props: "char charCode key keyCode".split( " " ), + filter: function( event, original ) { + + // Add which for key events + if ( event.which == null ) { + event.which = original.charCode != null ? original.charCode : original.keyCode; + } + + return event; + } + }, + + mouseHooks: { + props: ( "button buttons clientX clientY fromElement offsetX offsetY " + + "pageX pageY screenX screenY toElement" ).split( " " ), + filter: function( event, original ) { + var body, eventDoc, doc, + button = original.button, + fromElement = original.fromElement; + + // Calculate pageX/Y if missing and clientX/Y available + if ( event.pageX == null && original.clientX != null ) { + eventDoc = event.target.ownerDocument || document; + doc = eventDoc.documentElement; + body = eventDoc.body; + + event.pageX = original.clientX + + ( doc && doc.scrollLeft || body && body.scrollLeft || 0 ) - + ( doc && doc.clientLeft || body && body.clientLeft || 0 ); + event.pageY = original.clientY + + ( doc && doc.scrollTop || body && body.scrollTop || 0 ) - + ( doc && doc.clientTop || body && body.clientTop || 0 ); + } + + // Add relatedTarget, if necessary + if ( !event.relatedTarget && fromElement ) { + event.relatedTarget = fromElement === event.target ? + original.toElement : + fromElement; + } + + // Add which for click: 1 === left; 2 === middle; 3 === right + // Note: button is not normalized, so don't use it + if ( !event.which && button !== undefined ) { + event.which = ( button & 1 ? 1 : ( button & 2 ? 3 : ( button & 4 ? 2 : 0 ) ) ); + } + + return event; + } + }, + + special: { + load: { + + // Prevent triggered image.load events from bubbling to window.load + noBubble: true + }, + focus: { + + // Fire native event if possible so blur/focus sequence is correct + trigger: function() { + if ( this !== safeActiveElement() && this.focus ) { + try { + this.focus(); + return false; + } catch ( e ) { + + // Support: IE<9 + // If we error on focus to hidden element (#1486, #12518), + // let .trigger() run the handlers + } + } + }, + delegateType: "focusin" + }, + blur: { + trigger: function() { + if ( this === safeActiveElement() && this.blur ) { + this.blur(); + return false; + } + }, + delegateType: "focusout" + }, + click: { + + // For checkbox, fire native event so checked state will be right + trigger: function() { + if ( jQuery.nodeName( this, "input" ) && this.type === "checkbox" && this.click ) { + this.click(); + return false; + } + }, + + // For cross-browser consistency, don't fire native .click() on links + _default: function( event ) { + return jQuery.nodeName( event.target, "a" ); + } + }, + + beforeunload: { + postDispatch: function( event ) { + + // Support: Firefox 20+ + // Firefox doesn't alert if the returnValue field is not set. + if ( event.result !== undefined && event.originalEvent ) { + event.originalEvent.returnValue = event.result; + } + } + } + }, + + // Piggyback on a donor event to simulate a different one + simulate: function( type, elem, event ) { + var e = jQuery.extend( + new jQuery.Event(), + event, + { + type: type, + isSimulated: true + + // Previously, `originalEvent: {}` was set here, so stopPropagation call + // would not be triggered on donor event, since in our own + // jQuery.event.stopPropagation function we had a check for existence of + // originalEvent.stopPropagation method, so, consequently it would be a noop. + // + // Guard for simulated events was moved to jQuery.event.stopPropagation function + // since `originalEvent` should point to the original event for the + // constancy with other events and for more focused logic + } + ); + + jQuery.event.trigger( e, null, elem ); + + if ( e.isDefaultPrevented() ) { + event.preventDefault(); + } + } +}; + +jQuery.removeEvent = document.removeEventListener ? + function( elem, type, handle ) { + + // This "if" is needed for plain objects + if ( elem.removeEventListener ) { + elem.removeEventListener( type, handle ); + } + } : + function( elem, type, handle ) { + var name = "on" + type; + + if ( elem.detachEvent ) { + + // #8545, #7054, preventing memory leaks for custom events in IE6-8 + // detachEvent needed property on element, by name of that event, + // to properly expose it to GC + if ( typeof elem[ name ] === "undefined" ) { + elem[ name ] = null; + } + + elem.detachEvent( name, handle ); + } + }; + +jQuery.Event = function( src, props ) { + + // Allow instantiation without the 'new' keyword + if ( !( this instanceof jQuery.Event ) ) { + return new jQuery.Event( src, props ); + } + + // Event object + if ( src && src.type ) { + this.originalEvent = src; + this.type = src.type; + + // Events bubbling up the document may have been marked as prevented + // by a handler lower down the tree; reflect the correct value. + this.isDefaultPrevented = src.defaultPrevented || + src.defaultPrevented === undefined && + + // Support: IE < 9, Android < 4.0 + src.returnValue === false ? + returnTrue : + returnFalse; + + // Event type + } else { + this.type = src; + } + + // Put explicitly provided properties onto the event object + if ( props ) { + jQuery.extend( this, props ); + } + + // Create a timestamp if incoming event doesn't have one + this.timeStamp = src && src.timeStamp || jQuery.now(); + + // Mark it as fixed + this[ jQuery.expando ] = true; +}; + +// jQuery.Event is based on DOM3 Events as specified by the ECMAScript Language Binding +// http://www.w3.org/TR/2003/WD-DOM-Level-3-Events-20030331/ecma-script-binding.html +jQuery.Event.prototype = { + constructor: jQuery.Event, + isDefaultPrevented: returnFalse, + isPropagationStopped: returnFalse, + isImmediatePropagationStopped: returnFalse, + + preventDefault: function() { + var e = this.originalEvent; + + this.isDefaultPrevented = returnTrue; + if ( !e ) { + return; + } + + // If preventDefault exists, run it on the original event + if ( e.preventDefault ) { + e.preventDefault(); + + // Support: IE + // Otherwise set the returnValue property of the original event to false + } else { + e.returnValue = false; + } + }, + stopPropagation: function() { + var e = this.originalEvent; + + this.isPropagationStopped = returnTrue; + + if ( !e || this.isSimulated ) { + return; + } + + // If stopPropagation exists, run it on the original event + if ( e.stopPropagation ) { + e.stopPropagation(); + } + + // Support: IE + // Set the cancelBubble property of the original event to true + e.cancelBubble = true; + }, + stopImmediatePropagation: function() { + var e = this.originalEvent; + + this.isImmediatePropagationStopped = returnTrue; + + if ( e && e.stopImmediatePropagation ) { + e.stopImmediatePropagation(); + } + + this.stopPropagation(); + } +}; + +// Create mouseenter/leave events using mouseover/out and event-time checks +// so that event delegation works in jQuery. +// Do the same for pointerenter/pointerleave and pointerover/pointerout +// +// Support: Safari 7 only +// Safari sends mouseenter too often; see: +// https://code.google.com/p/chromium/issues/detail?id=470258 +// for the description of the bug (it existed in older Chrome versions as well). +jQuery.each( { + mouseenter: "mouseover", + mouseleave: "mouseout", + pointerenter: "pointerover", + pointerleave: "pointerout" +}, function( orig, fix ) { + jQuery.event.special[ orig ] = { + delegateType: fix, + bindType: fix, + + handle: function( event ) { + var ret, + target = this, + related = event.relatedTarget, + handleObj = event.handleObj; + + // For mouseenter/leave call the handler if related is outside the target. + // NB: No relatedTarget if the mouse left/entered the browser window + if ( !related || ( related !== target && !jQuery.contains( target, related ) ) ) { + event.type = handleObj.origType; + ret = handleObj.handler.apply( this, arguments ); + event.type = fix; + } + return ret; + } + }; +} ); + +// IE submit delegation +if ( !support.submit ) { + + jQuery.event.special.submit = { + setup: function() { + + // Only need this for delegated form submit events + if ( jQuery.nodeName( this, "form" ) ) { + return false; + } + + // Lazy-add a submit handler when a descendant form may potentially be submitted + jQuery.event.add( this, "click._submit keypress._submit", function( e ) { + + // Node name check avoids a VML-related crash in IE (#9807) + var elem = e.target, + form = jQuery.nodeName( elem, "input" ) || jQuery.nodeName( elem, "button" ) ? + + // Support: IE <=8 + // We use jQuery.prop instead of elem.form + // to allow fixing the IE8 delegated submit issue (gh-2332) + // by 3rd party polyfills/workarounds. + jQuery.prop( elem, "form" ) : + undefined; + + if ( form && !jQuery._data( form, "submit" ) ) { + jQuery.event.add( form, "submit._submit", function( event ) { + event._submitBubble = true; + } ); + jQuery._data( form, "submit", true ); + } + } ); + + // return undefined since we don't need an event listener + }, + + postDispatch: function( event ) { + + // If form was submitted by the user, bubble the event up the tree + if ( event._submitBubble ) { + delete event._submitBubble; + if ( this.parentNode && !event.isTrigger ) { + jQuery.event.simulate( "submit", this.parentNode, event ); + } + } + }, + + teardown: function() { + + // Only need this for delegated form submit events + if ( jQuery.nodeName( this, "form" ) ) { + return false; + } + + // Remove delegated handlers; cleanData eventually reaps submit handlers attached above + jQuery.event.remove( this, "._submit" ); + } + }; +} + +// IE change delegation and checkbox/radio fix +if ( !support.change ) { + + jQuery.event.special.change = { + + setup: function() { + + if ( rformElems.test( this.nodeName ) ) { + + // IE doesn't fire change on a check/radio until blur; trigger it on click + // after a propertychange. Eat the blur-change in special.change.handle. + // This still fires onchange a second time for check/radio after blur. + if ( this.type === "checkbox" || this.type === "radio" ) { + jQuery.event.add( this, "propertychange._change", function( event ) { + if ( event.originalEvent.propertyName === "checked" ) { + this._justChanged = true; + } + } ); + jQuery.event.add( this, "click._change", function( event ) { + if ( this._justChanged && !event.isTrigger ) { + this._justChanged = false; + } + + // Allow triggered, simulated change events (#11500) + jQuery.event.simulate( "change", this, event ); + } ); + } + return false; + } + + // Delegated event; lazy-add a change handler on descendant inputs + jQuery.event.add( this, "beforeactivate._change", function( e ) { + var elem = e.target; + + if ( rformElems.test( elem.nodeName ) && !jQuery._data( elem, "change" ) ) { + jQuery.event.add( elem, "change._change", function( event ) { + if ( this.parentNode && !event.isSimulated && !event.isTrigger ) { + jQuery.event.simulate( "change", this.parentNode, event ); + } + } ); + jQuery._data( elem, "change", true ); + } + } ); + }, + + handle: function( event ) { + var elem = event.target; + + // Swallow native change events from checkbox/radio, we already triggered them above + if ( this !== elem || event.isSimulated || event.isTrigger || + ( elem.type !== "radio" && elem.type !== "checkbox" ) ) { + + return event.handleObj.handler.apply( this, arguments ); + } + }, + + teardown: function() { + jQuery.event.remove( this, "._change" ); + + return !rformElems.test( this.nodeName ); + } + }; +} + +// Support: Firefox +// Firefox doesn't have focus(in | out) events +// Related ticket - https://bugzilla.mozilla.org/show_bug.cgi?id=687787 +// +// Support: Chrome, Safari +// focus(in | out) events fire after focus & blur events, +// which is spec violation - http://www.w3.org/TR/DOM-Level-3-Events/#events-focusevent-event-order +// Related ticket - https://code.google.com/p/chromium/issues/detail?id=449857 +if ( !support.focusin ) { + jQuery.each( { focus: "focusin", blur: "focusout" }, function( orig, fix ) { + + // Attach a single capturing handler on the document while someone wants focusin/focusout + var handler = function( event ) { + jQuery.event.simulate( fix, event.target, jQuery.event.fix( event ) ); + }; + + jQuery.event.special[ fix ] = { + setup: function() { + var doc = this.ownerDocument || this, + attaches = jQuery._data( doc, fix ); + + if ( !attaches ) { + doc.addEventListener( orig, handler, true ); + } + jQuery._data( doc, fix, ( attaches || 0 ) + 1 ); + }, + teardown: function() { + var doc = this.ownerDocument || this, + attaches = jQuery._data( doc, fix ) - 1; + + if ( !attaches ) { + doc.removeEventListener( orig, handler, true ); + jQuery._removeData( doc, fix ); + } else { + jQuery._data( doc, fix, attaches ); + } + } + }; + } ); +} + +jQuery.fn.extend( { + + on: function( types, selector, data, fn ) { + return on( this, types, selector, data, fn ); + }, + one: function( types, selector, data, fn ) { + return on( this, types, selector, data, fn, 1 ); + }, + off: function( types, selector, fn ) { + var handleObj, type; + if ( types && types.preventDefault && types.handleObj ) { + + // ( event ) dispatched jQuery.Event + handleObj = types.handleObj; + jQuery( types.delegateTarget ).off( + handleObj.namespace ? + handleObj.origType + "." + handleObj.namespace : + handleObj.origType, + handleObj.selector, + handleObj.handler + ); + return this; + } + if ( typeof types === "object" ) { + + // ( types-object [, selector] ) + for ( type in types ) { + this.off( type, selector, types[ type ] ); + } + return this; + } + if ( selector === false || typeof selector === "function" ) { + + // ( types [, fn] ) + fn = selector; + selector = undefined; + } + if ( fn === false ) { + fn = returnFalse; + } + return this.each( function() { + jQuery.event.remove( this, types, fn, selector ); + } ); + }, + + trigger: function( type, data ) { + return this.each( function() { + jQuery.event.trigger( type, data, this ); + } ); + }, + triggerHandler: function( type, data ) { + var elem = this[ 0 ]; + if ( elem ) { + return jQuery.event.trigger( type, data, elem, true ); + } + } +} ); + + +var rinlinejQuery = / jQuery\d+="(?:null|\d+)"/g, + rnoshimcache = new RegExp( "<(?:" + nodeNames + ")[\\s/>]", "i" ), + rxhtmlTag = /<(?!area|br|col|embed|hr|img|input|link|meta|param)(([\w:-]+)[^>]*)\/>/gi, + + // Support: IE 10-11, Edge 10240+ + // In IE/Edge using regex groups here causes severe slowdowns. + // See https://connect.microsoft.com/IE/feedback/details/1736512/ + rnoInnerhtml = /\s*$/g, + safeFragment = createSafeFragment( document ), + fragmentDiv = safeFragment.appendChild( document.createElement( "div" ) ); + +// Support: IE<8 +// Manipulating tables requires a tbody +function manipulationTarget( elem, content ) { + return jQuery.nodeName( elem, "table" ) && + jQuery.nodeName( content.nodeType !== 11 ? content : content.firstChild, "tr" ) ? + + elem.getElementsByTagName( "tbody" )[ 0 ] || + elem.appendChild( elem.ownerDocument.createElement( "tbody" ) ) : + elem; +} + +// Replace/restore the type attribute of script elements for safe DOM manipulation +function disableScript( elem ) { + elem.type = ( jQuery.find.attr( elem, "type" ) !== null ) + "/" + elem.type; + return elem; +} +function restoreScript( elem ) { + var match = rscriptTypeMasked.exec( elem.type ); + if ( match ) { + elem.type = match[ 1 ]; + } else { + elem.removeAttribute( "type" ); + } + return elem; +} + +function cloneCopyEvent( src, dest ) { + if ( dest.nodeType !== 1 || !jQuery.hasData( src ) ) { + return; + } + + var type, i, l, + oldData = jQuery._data( src ), + curData = jQuery._data( dest, oldData ), + events = oldData.events; + + if ( events ) { + delete curData.handle; + curData.events = {}; + + for ( type in events ) { + for ( i = 0, l = events[ type ].length; i < l; i++ ) { + jQuery.event.add( dest, type, events[ type ][ i ] ); + } + } + } + + // make the cloned public data object a copy from the original + if ( curData.data ) { + curData.data = jQuery.extend( {}, curData.data ); + } +} + +function fixCloneNodeIssues( src, dest ) { + var nodeName, e, data; + + // We do not need to do anything for non-Elements + if ( dest.nodeType !== 1 ) { + return; + } + + nodeName = dest.nodeName.toLowerCase(); + + // IE6-8 copies events bound via attachEvent when using cloneNode. + if ( !support.noCloneEvent && dest[ jQuery.expando ] ) { + data = jQuery._data( dest ); + + for ( e in data.events ) { + jQuery.removeEvent( dest, e, data.handle ); + } + + // Event data gets referenced instead of copied if the expando gets copied too + dest.removeAttribute( jQuery.expando ); + } + + // IE blanks contents when cloning scripts, and tries to evaluate newly-set text + if ( nodeName === "script" && dest.text !== src.text ) { + disableScript( dest ).text = src.text; + restoreScript( dest ); + + // IE6-10 improperly clones children of object elements using classid. + // IE10 throws NoModificationAllowedError if parent is null, #12132. + } else if ( nodeName === "object" ) { + if ( dest.parentNode ) { + dest.outerHTML = src.outerHTML; + } + + // This path appears unavoidable for IE9. When cloning an object + // element in IE9, the outerHTML strategy above is not sufficient. + // If the src has innerHTML and the destination does not, + // copy the src.innerHTML into the dest.innerHTML. #10324 + if ( support.html5Clone && ( src.innerHTML && !jQuery.trim( dest.innerHTML ) ) ) { + dest.innerHTML = src.innerHTML; + } + + } else if ( nodeName === "input" && rcheckableType.test( src.type ) ) { + + // IE6-8 fails to persist the checked state of a cloned checkbox + // or radio button. Worse, IE6-7 fail to give the cloned element + // a checked appearance if the defaultChecked value isn't also set + + dest.defaultChecked = dest.checked = src.checked; + + // IE6-7 get confused and end up setting the value of a cloned + // checkbox/radio button to an empty string instead of "on" + if ( dest.value !== src.value ) { + dest.value = src.value; + } + + // IE6-8 fails to return the selected option to the default selected + // state when cloning options + } else if ( nodeName === "option" ) { + dest.defaultSelected = dest.selected = src.defaultSelected; + + // IE6-8 fails to set the defaultValue to the correct value when + // cloning other types of input fields + } else if ( nodeName === "input" || nodeName === "textarea" ) { + dest.defaultValue = src.defaultValue; + } +} + +function domManip( collection, args, callback, ignored ) { + + // Flatten any nested arrays + args = concat.apply( [], args ); + + var first, node, hasScripts, + scripts, doc, fragment, + i = 0, + l = collection.length, + iNoClone = l - 1, + value = args[ 0 ], + isFunction = jQuery.isFunction( value ); + + // We can't cloneNode fragments that contain checked, in WebKit + if ( isFunction || + ( l > 1 && typeof value === "string" && + !support.checkClone && rchecked.test( value ) ) ) { + return collection.each( function( index ) { + var self = collection.eq( index ); + if ( isFunction ) { + args[ 0 ] = value.call( this, index, self.html() ); + } + domManip( self, args, callback, ignored ); + } ); + } + + if ( l ) { + fragment = buildFragment( args, collection[ 0 ].ownerDocument, false, collection, ignored ); + first = fragment.firstChild; + + if ( fragment.childNodes.length === 1 ) { + fragment = first; + } + + // Require either new content or an interest in ignored elements to invoke the callback + if ( first || ignored ) { + scripts = jQuery.map( getAll( fragment, "script" ), disableScript ); + hasScripts = scripts.length; + + // Use the original fragment for the last item + // instead of the first because it can end up + // being emptied incorrectly in certain situations (#8070). + for ( ; i < l; i++ ) { + node = fragment; + + if ( i !== iNoClone ) { + node = jQuery.clone( node, true, true ); + + // Keep references to cloned scripts for later restoration + if ( hasScripts ) { + + // Support: Android<4.1, PhantomJS<2 + // push.apply(_, arraylike) throws on ancient WebKit + jQuery.merge( scripts, getAll( node, "script" ) ); + } + } + + callback.call( collection[ i ], node, i ); + } + + if ( hasScripts ) { + doc = scripts[ scripts.length - 1 ].ownerDocument; + + // Reenable scripts + jQuery.map( scripts, restoreScript ); + + // Evaluate executable scripts on first document insertion + for ( i = 0; i < hasScripts; i++ ) { + node = scripts[ i ]; + if ( rscriptType.test( node.type || "" ) && + !jQuery._data( node, "globalEval" ) && + jQuery.contains( doc, node ) ) { + + if ( node.src ) { + + // Optional AJAX dependency, but won't run scripts if not present + if ( jQuery._evalUrl ) { + jQuery._evalUrl( node.src ); + } + } else { + jQuery.globalEval( + ( node.text || node.textContent || node.innerHTML || "" ) + .replace( rcleanScript, "" ) + ); + } + } + } + } + + // Fix #11809: Avoid leaking memory + fragment = first = null; + } + } + + return collection; +} + +function remove( elem, selector, keepData ) { + var node, + elems = selector ? jQuery.filter( selector, elem ) : elem, + i = 0; + + for ( ; ( node = elems[ i ] ) != null; i++ ) { + + if ( !keepData && node.nodeType === 1 ) { + jQuery.cleanData( getAll( node ) ); + } + + if ( node.parentNode ) { + if ( keepData && jQuery.contains( node.ownerDocument, node ) ) { + setGlobalEval( getAll( node, "script" ) ); + } + node.parentNode.removeChild( node ); + } + } + + return elem; +} + +jQuery.extend( { + htmlPrefilter: function( html ) { + return html.replace( rxhtmlTag, "<$1>" ); + }, + + clone: function( elem, dataAndEvents, deepDataAndEvents ) { + var destElements, node, clone, i, srcElements, + inPage = jQuery.contains( elem.ownerDocument, elem ); + + if ( support.html5Clone || jQuery.isXMLDoc( elem ) || + !rnoshimcache.test( "<" + elem.nodeName + ">" ) ) { + + clone = elem.cloneNode( true ); + + // IE<=8 does not properly clone detached, unknown element nodes + } else { + fragmentDiv.innerHTML = elem.outerHTML; + fragmentDiv.removeChild( clone = fragmentDiv.firstChild ); + } + + if ( ( !support.noCloneEvent || !support.noCloneChecked ) && + ( elem.nodeType === 1 || elem.nodeType === 11 ) && !jQuery.isXMLDoc( elem ) ) { + + // We eschew Sizzle here for performance reasons: http://jsperf.com/getall-vs-sizzle/2 + destElements = getAll( clone ); + srcElements = getAll( elem ); + + // Fix all IE cloning issues + for ( i = 0; ( node = srcElements[ i ] ) != null; ++i ) { + + // Ensure that the destination node is not null; Fixes #9587 + if ( destElements[ i ] ) { + fixCloneNodeIssues( node, destElements[ i ] ); + } + } + } + + // Copy the events from the original to the clone + if ( dataAndEvents ) { + if ( deepDataAndEvents ) { + srcElements = srcElements || getAll( elem ); + destElements = destElements || getAll( clone ); + + for ( i = 0; ( node = srcElements[ i ] ) != null; i++ ) { + cloneCopyEvent( node, destElements[ i ] ); + } + } else { + cloneCopyEvent( elem, clone ); + } + } + + // Preserve script evaluation history + destElements = getAll( clone, "script" ); + if ( destElements.length > 0 ) { + setGlobalEval( destElements, !inPage && getAll( elem, "script" ) ); + } + + destElements = srcElements = node = null; + + // Return the cloned set + return clone; + }, + + cleanData: function( elems, /* internal */ forceAcceptData ) { + var elem, type, id, data, + i = 0, + internalKey = jQuery.expando, + cache = jQuery.cache, + attributes = support.attributes, + special = jQuery.event.special; + + for ( ; ( elem = elems[ i ] ) != null; i++ ) { + if ( forceAcceptData || acceptData( elem ) ) { + + id = elem[ internalKey ]; + data = id && cache[ id ]; + + if ( data ) { + if ( data.events ) { + for ( type in data.events ) { + if ( special[ type ] ) { + jQuery.event.remove( elem, type ); + + // This is a shortcut to avoid jQuery.event.remove's overhead + } else { + jQuery.removeEvent( elem, type, data.handle ); + } + } + } + + // Remove cache only if it was not already removed by jQuery.event.remove + if ( cache[ id ] ) { + + delete cache[ id ]; + + // Support: IE<9 + // IE does not allow us to delete expando properties from nodes + // IE creates expando attributes along with the property + // IE does not have a removeAttribute function on Document nodes + if ( !attributes && typeof elem.removeAttribute !== "undefined" ) { + elem.removeAttribute( internalKey ); + + // Webkit & Blink performance suffers when deleting properties + // from DOM nodes, so set to undefined instead + // https://code.google.com/p/chromium/issues/detail?id=378607 + } else { + elem[ internalKey ] = undefined; + } + + deletedIds.push( id ); + } + } + } + } + } +} ); + +jQuery.fn.extend( { + + // Keep domManip exposed until 3.0 (gh-2225) + domManip: domManip, + + detach: function( selector ) { + return remove( this, selector, true ); + }, + + remove: function( selector ) { + return remove( this, selector ); + }, + + text: function( value ) { + return access( this, function( value ) { + return value === undefined ? + jQuery.text( this ) : + this.empty().append( + ( this[ 0 ] && this[ 0 ].ownerDocument || document ).createTextNode( value ) + ); + }, null, value, arguments.length ); + }, + + append: function() { + return domManip( this, arguments, function( elem ) { + if ( this.nodeType === 1 || this.nodeType === 11 || this.nodeType === 9 ) { + var target = manipulationTarget( this, elem ); + target.appendChild( elem ); + } + } ); + }, + + prepend: function() { + return domManip( this, arguments, function( elem ) { + if ( this.nodeType === 1 || this.nodeType === 11 || this.nodeType === 9 ) { + var target = manipulationTarget( this, elem ); + target.insertBefore( elem, target.firstChild ); + } + } ); + }, + + before: function() { + return domManip( this, arguments, function( elem ) { + if ( this.parentNode ) { + this.parentNode.insertBefore( elem, this ); + } + } ); + }, + + after: function() { + return domManip( this, arguments, function( elem ) { + if ( this.parentNode ) { + this.parentNode.insertBefore( elem, this.nextSibling ); + } + } ); + }, + + empty: function() { + var elem, + i = 0; + + for ( ; ( elem = this[ i ] ) != null; i++ ) { + + // Remove element nodes and prevent memory leaks + if ( elem.nodeType === 1 ) { + jQuery.cleanData( getAll( elem, false ) ); + } + + // Remove any remaining nodes + while ( elem.firstChild ) { + elem.removeChild( elem.firstChild ); + } + + // If this is a select, ensure that it displays empty (#12336) + // Support: IE<9 + if ( elem.options && jQuery.nodeName( elem, "select" ) ) { + elem.options.length = 0; + } + } + + return this; + }, + + clone: function( dataAndEvents, deepDataAndEvents ) { + dataAndEvents = dataAndEvents == null ? false : dataAndEvents; + deepDataAndEvents = deepDataAndEvents == null ? dataAndEvents : deepDataAndEvents; + + return this.map( function() { + return jQuery.clone( this, dataAndEvents, deepDataAndEvents ); + } ); + }, + + html: function( value ) { + return access( this, function( value ) { + var elem = this[ 0 ] || {}, + i = 0, + l = this.length; + + if ( value === undefined ) { + return elem.nodeType === 1 ? + elem.innerHTML.replace( rinlinejQuery, "" ) : + undefined; + } + + // See if we can take a shortcut and just use innerHTML + if ( typeof value === "string" && !rnoInnerhtml.test( value ) && + ( support.htmlSerialize || !rnoshimcache.test( value ) ) && + ( support.leadingWhitespace || !rleadingWhitespace.test( value ) ) && + !wrapMap[ ( rtagName.exec( value ) || [ "", "" ] )[ 1 ].toLowerCase() ] ) { + + value = jQuery.htmlPrefilter( value ); + + try { + for ( ; i < l; i++ ) { + + // Remove element nodes and prevent memory leaks + elem = this[ i ] || {}; + if ( elem.nodeType === 1 ) { + jQuery.cleanData( getAll( elem, false ) ); + elem.innerHTML = value; + } + } + + elem = 0; + + // If using innerHTML throws an exception, use the fallback method + } catch ( e ) {} + } + + if ( elem ) { + this.empty().append( value ); + } + }, null, value, arguments.length ); + }, + + replaceWith: function() { + var ignored = []; + + // Make the changes, replacing each non-ignored context element with the new content + return domManip( this, arguments, function( elem ) { + var parent = this.parentNode; + + if ( jQuery.inArray( this, ignored ) < 0 ) { + jQuery.cleanData( getAll( this ) ); + if ( parent ) { + parent.replaceChild( elem, this ); + } + } + + // Force callback invocation + }, ignored ); + } +} ); + +jQuery.each( { + appendTo: "append", + prependTo: "prepend", + insertBefore: "before", + insertAfter: "after", + replaceAll: "replaceWith" +}, function( name, original ) { + jQuery.fn[ name ] = function( selector ) { + var elems, + i = 0, + ret = [], + insert = jQuery( selector ), + last = insert.length - 1; + + for ( ; i <= last; i++ ) { + elems = i === last ? this : this.clone( true ); + jQuery( insert[ i ] )[ original ]( elems ); + + // Modern browsers can apply jQuery collections as arrays, but oldIE needs a .get() + push.apply( ret, elems.get() ); + } + + return this.pushStack( ret ); + }; +} ); + + +var iframe, + elemdisplay = { + + // Support: Firefox + // We have to pre-define these values for FF (#10227) + HTML: "block", + BODY: "block" + }; + +/** + * Retrieve the actual display of a element + * @param {String} name nodeName of the element + * @param {Object} doc Document object + */ + +// Called only from within defaultDisplay +function actualDisplay( name, doc ) { + var elem = jQuery( doc.createElement( name ) ).appendTo( doc.body ), + + display = jQuery.css( elem[ 0 ], "display" ); + + // We don't have any data stored on the element, + // so use "detach" method as fast way to get rid of the element + elem.detach(); + + return display; +} + +/** + * Try to determine the default display value of an element + * @param {String} nodeName + */ +function defaultDisplay( nodeName ) { + var doc = document, + display = elemdisplay[ nodeName ]; + + if ( !display ) { + display = actualDisplay( nodeName, doc ); + + // If the simple way fails, read from inside an iframe + if ( display === "none" || !display ) { + + // Use the already-created iframe if possible + iframe = ( iframe || jQuery( "