summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--.editorconfig15
-rw-r--r--.gitattributes23
-rw-r--r--.gitignore27
-rw-r--r--.travis.yml5
-rw-r--r--Dockerfile11
-rw-r--r--NEWS115
-rw-r--r--README.md10
-rw-r--r--application/.htaccess7
-rw-r--r--application/cache/.htaccess1
-rw-r--r--application/cache/index.html5
-rw-r--r--application/config/autoload.php67
-rw-r--r--application/config/config.php317
-rw-r--r--application/config/constants.php78
-rw-r--r--application/config/doctypes.php35
-rw-r--r--application/config/example/config-local.php3
-rw-r--r--application/config/example/database.php86
-rw-r--r--application/config/foreign_chars.php122
-rw-r--r--application/config/hooks.php11
-rw-r--r--application/config/index.html5
-rw-r--r--application/config/memcached.php19
-rw-r--r--application/config/migration.php64
-rw-r--r--application/config/mimes.php278
-rw-r--r--application/config/profiler.php11
-rw-r--r--application/config/routes.php42
-rw-r--r--application/config/smileys.php66
-rw-r--r--application/config/user_agents.php357
-rw-r--r--application/controllers/Api.php (renamed from application/controllers/api.php)47
-rw-r--r--application/controllers/Main.php (renamed from application/controllers/file/file_default.php)169
-rw-r--r--application/controllers/Tools.php (renamed from application/controllers/tools.php)27
-rw-r--r--application/controllers/User.php (renamed from application/controllers/user.php)90
-rw-r--r--application/controllers/api/api_controller.php7
-rw-r--r--application/controllers/api/v2/api_info.php2
-rw-r--r--application/controllers/api/v2/file.php48
-rw-r--r--application/controllers/api/v2/user.php32
-rw-r--r--application/controllers/file/Multipaste.php (renamed from application/controllers/file/multipaste.php)11
-rw-r--r--application/controllers/index.html5
-rw-r--r--application/core/MY_Controller.php19
-rw-r--r--application/core/MY_Input.php4
-rw-r--r--application/core/index.html5
-rw-r--r--application/errors/error_404.php3
-rw-r--r--application/errors/error_db.php3
-rw-r--r--application/errors/error_general.php104
-rw-r--r--application/errors/error_php.php11
-rw-r--r--application/helpers/filebin_helper.php158
-rw-r--r--application/helpers/index.html5
-rw-r--r--application/hooks/index.html5
-rw-r--r--application/index.html5
-rw-r--r--application/language/english/index.html5
-rw-r--r--application/language/index.html5
-rw-r--r--application/libraries/Customautoloader.php2
-rw-r--r--application/libraries/Ddownload/Ddownload.php2
-rw-r--r--application/libraries/Ddownload/drivers/Ddownload_php.php4
-rw-r--r--application/libraries/Duser/Duser.php2
-rw-r--r--application/libraries/Duser/drivers/Duser_db.php12
-rw-r--r--application/libraries/Duser/drivers/Duser_ldap.php17
-rw-r--r--application/libraries/ExceptionHandler.php12
-rw-r--r--application/libraries/Image/Drivers/GD.php4
-rw-r--r--application/libraries/Image/Drivers/imagemagick.php11
-rw-r--r--application/libraries/Pygments.php9
-rw-r--r--application/libraries/index.html5
-rw-r--r--application/logs/index.html5
-rw-r--r--application/migrations/019_change_filesize_type.php51
-rw-r--r--application/migrations/020_update_session_table.php45
-rw-r--r--application/migrations/021_change_charset.php28
-rw-r--r--application/models/Mfile.php (renamed from application/models/mfile.php)4
-rw-r--r--application/models/Mmultipaste.php (renamed from application/models/mmultipaste.php)1
-rw-r--r--application/models/Muser.php (renamed from application/models/muser.php)76
-rw-r--r--application/models/index.html5
-rw-r--r--application/service/files.php132
-rw-r--r--application/service/multipaste_queue.php6
-rw-r--r--application/service/renderer.php41
-rw-r--r--application/service/user.php48
-rw-r--r--application/test/tests/api_v2/test_api_permissions.php2
-rw-r--r--application/test/tests/api_v2/test_file_create_multipaste.php23
-rw-r--r--application/test/tests/api_v2/test_file_upload.php45
-rw-r--r--application/test/tests/test_database_schema.php38
-rw-r--r--application/test/tests/test_filebin_helper.php59
-rw-r--r--application/test/tests/test_libraries_image.php22
-rw-r--r--application/test/tests/test_libraries_procrunner.php12
-rw-r--r--application/test/tests/test_service_multipaste_queue.php9
-rw-r--r--application/test/tests/test_service_user.php65
-rw-r--r--application/third_party/index.html5
m---------application/third_party/mockery0
-rwxr-xr-xapplication/third_party/test-more-php/Test-More-OO.php9
-rwxr-xr-xapplication/third_party/test-more-php/Test-Simple-OO.php3
-rw-r--r--application/views/errors/cli/error_404.php8
-rw-r--r--application/views/errors/cli/error_db.php8
-rw-r--r--application/views/errors/cli/error_exception.php21
-rw-r--r--application/views/errors/cli/error_general.php8
-rw-r--r--application/views/errors/cli/error_php.php21
-rw-r--r--application/views/errors/cli/index.html (renamed from application/errors/index.html)5
-rw-r--r--application/views/errors/html/error_404.php3
-rw-r--r--application/views/errors/html/error_db.php3
-rw-r--r--application/views/errors/html/error_exception.php32
-rw-r--r--application/views/errors/html/error_general.php127
-rw-r--r--application/views/errors/html/error_php.php33
-rw-r--r--application/views/errors/html/index.html (renamed from system/database/drivers/sqlite/index.html)5
-rw-r--r--application/views/errors/index.html11
-rw-r--r--application/views/file/deleted.php2
-rw-r--r--application/views/file/upload_form.php16
-rw-r--r--application/views/index.html5
-rw-r--r--application/views/user/invite.php6
-rw-r--r--application/views/user/register.php1
-rwxr-xr-xcheck_deps.php1
-rw-r--r--composer.json27
-rw-r--r--composer.lock420
-rw-r--r--contributing.md93
-rw-r--r--doc/api.md2
-rw-r--r--doc/api/file.md47
-rw-r--r--docker/README.md7
-rwxr-xr-xdocker/add_user.sh1
-rw-r--r--docker/docker-compose.yml5
-rwxr-xr-xdocker/filebin_starter.sh81
-rw-r--r--license.txt72
-rw-r--r--public_html/data/css/asciinema-player.css94
-rw-r--r--public_html/data/js/thumbnail-view.js18
-rw-r--r--public_html/data/js/vendor/asciinema-player.js2064
-rw-r--r--public_html/index.php251
-rwxr-xr-xrun-tests.sh9
-rwxr-xr-xscripts/get_lexer_list.py2
-rw-r--r--system/.htaccess7
-rw-r--r--system/core/Benchmark.php106
-rw-r--r--system/core/CodeIgniter.php447
-rw-r--r--system/core/Common.php885
-rw-r--r--system/core/Config.php405
-rw-r--r--system/core/Controller.php83
-rw-r--r--system/core/Exceptions.php280
-rw-r--r--system/core/Hooks.php215
-rw-r--r--system/core/Input.php1045
-rw-r--r--system/core/Lang.php172
-rw-r--r--system/core/Loader.php1253
-rw-r--r--system/core/Log.php296
-rw-r--r--system/core/Model.php78
-rw-r--r--system/core/Output.php716
-rw-r--r--system/core/Router.php604
-rw-r--r--system/core/Security.php923
-rw-r--r--system/core/URI.php635
-rw-r--r--system/core/Utf8.php152
-rw-r--r--system/core/compat/hash.php253
-rw-r--r--system/core/compat/index.html11
-rw-r--r--system/core/compat/mbstring.php150
-rw-r--r--system/core/compat/password.php252
-rw-r--r--system/core/compat/standard.php135
-rw-r--r--system/core/index.html5
-rw-r--r--system/database/DB.php210
-rw-r--r--system/database/DB_active_rec.php2045
-rw-r--r--system/database/DB_cache.php165
-rw-r--r--system/database/DB_driver.php1711
-rw-r--r--system/database/DB_forge.php956
-rw-r--r--system/database/DB_query_builder.php2881
-rw-r--r--system/database/DB_result.php542
-rw-r--r--system/database/DB_utility.php385
-rw-r--r--system/database/drivers/cubrid/cubrid_driver.php714
-rw-r--r--system/database/drivers/cubrid/cubrid_forge.php363
-rw-r--r--system/database/drivers/cubrid/cubrid_result.php148
-rw-r--r--system/database/drivers/cubrid/cubrid_utility.php108
-rw-r--r--system/database/drivers/cubrid/index.html5
-rw-r--r--system/database/drivers/ibase/ibase_driver.php414
-rw-r--r--system/database/drivers/ibase/ibase_forge.php252
-rw-r--r--system/database/drivers/ibase/ibase_result.php162
-rw-r--r--system/database/drivers/ibase/ibase_utility.php70
-rw-r--r--system/database/drivers/ibase/index.html11
-rw-r--r--system/database/drivers/index.html5
-rw-r--r--system/database/drivers/mssql/index.html5
-rw-r--r--system/database/drivers/mssql/mssql_driver.php674
-rw-r--r--system/database/drivers/mssql/mssql_forge.php274
-rw-r--r--system/database/drivers/mssql/mssql_result.php130
-rw-r--r--system/database/drivers/mssql/mssql_utility.php106
-rw-r--r--system/database/drivers/mysql/index.html5
-rw-r--r--system/database/drivers/mysql/mysql_driver.php751
-rw-r--r--system/database/drivers/mysql/mysql_forge.php336
-rw-r--r--system/database/drivers/mysql/mysql_result.php136
-rw-r--r--system/database/drivers/mysql/mysql_utility.php158
-rw-r--r--system/database/drivers/mysqli/index.html5
-rw-r--r--system/database/drivers/mysqli/mysqli_driver.php806
-rw-r--r--system/database/drivers/mysqli/mysqli_forge.php323
-rw-r--r--system/database/drivers/mysqli/mysqli_result.php177
-rw-r--r--system/database/drivers/mysqli/mysqli_utility.php229
-rw-r--r--system/database/drivers/oci8/index.html5
-rw-r--r--system/database/drivers/oci8/oci8_driver.php831
-rw-r--r--system/database/drivers/oci8/oci8_forge.php315
-rw-r--r--system/database/drivers/oci8/oci8_result.php172
-rw-r--r--system/database/drivers/oci8/oci8_utility.php104
-rw-r--r--system/database/drivers/odbc/index.html5
-rw-r--r--system/database/drivers/odbc/odbc_driver.php617
-rw-r--r--system/database/drivers/odbc/odbc_forge.php283
-rw-r--r--system/database/drivers/odbc/odbc_result.php243
-rw-r--r--system/database/drivers/odbc/odbc_utility.php121
-rw-r--r--system/database/drivers/pdo/index.html5
-rw-r--r--system/database/drivers/pdo/pdo_driver.php772
-rw-r--r--system/database/drivers/pdo/pdo_forge.php284
-rw-r--r--system/database/drivers/pdo/pdo_result.php146
-rw-r--r--system/database/drivers/pdo/pdo_utility.php119
-rw-r--r--system/database/drivers/pdo/subdrivers/index.html11
-rw-r--r--system/database/drivers/pdo/subdrivers/pdo_4d_driver.php201
-rw-r--r--system/database/drivers/pdo/subdrivers/pdo_4d_forge.php218
-rw-r--r--system/database/drivers/pdo/subdrivers/pdo_cubrid_driver.php210
-rw-r--r--system/database/drivers/pdo/subdrivers/pdo_cubrid_forge.php231
-rw-r--r--system/database/drivers/pdo/subdrivers/pdo_dblib_driver.php354
-rw-r--r--system/database/drivers/pdo/subdrivers/pdo_dblib_forge.php150
-rw-r--r--system/database/drivers/pdo/subdrivers/pdo_firebird_driver.php280
-rw-r--r--system/database/drivers/pdo/subdrivers/pdo_firebird_forge.php238
-rw-r--r--system/database/drivers/pdo/subdrivers/pdo_ibm_driver.php245
-rw-r--r--system/database/drivers/pdo/subdrivers/pdo_ibm_forge.php155
-rw-r--r--system/database/drivers/pdo/subdrivers/pdo_informix_driver.php310
-rw-r--r--system/database/drivers/pdo/subdrivers/pdo_informix_forge.php164
-rw-r--r--system/database/drivers/pdo/subdrivers/pdo_mysql_driver.php380
-rw-r--r--system/database/drivers/pdo/subdrivers/pdo_mysql_forge.php257
-rw-r--r--system/database/drivers/pdo/subdrivers/pdo_oci_driver.php327
-rw-r--r--system/database/drivers/pdo/subdrivers/pdo_oci_forge.php208
-rw-r--r--system/database/drivers/pdo/subdrivers/pdo_odbc_driver.php230
-rw-r--r--system/database/drivers/pdo/subdrivers/pdo_odbc_forge.php71
-rw-r--r--system/database/drivers/pdo/subdrivers/pdo_pgsql_driver.php385
-rw-r--r--system/database/drivers/pdo/subdrivers/pdo_pgsql_forge.php218
-rw-r--r--system/database/drivers/pdo/subdrivers/pdo_sqlite_driver.php214
-rw-r--r--system/database/drivers/pdo/subdrivers/pdo_sqlite_forge.php239
-rw-r--r--system/database/drivers/pdo/subdrivers/pdo_sqlsrv_driver.php370
-rw-r--r--system/database/drivers/pdo/subdrivers/pdo_sqlsrv_forge.php150
-rw-r--r--system/database/drivers/postgre/index.html5
-rw-r--r--system/database/drivers/postgre/postgre_driver.php735
-rw-r--r--system/database/drivers/postgre/postgre_forge.php352
-rw-r--r--system/database/drivers/postgre/postgre_result.php116
-rw-r--r--system/database/drivers/postgre/postgre_utility.php103
-rw-r--r--system/database/drivers/sqlite/sqlite_driver.php658
-rw-r--r--system/database/drivers/sqlite/sqlite_forge.php265
-rw-r--r--system/database/drivers/sqlite/sqlite_result.php179
-rw-r--r--system/database/drivers/sqlite/sqlite_utility.php96
-rw-r--r--system/database/drivers/sqlite3/index.html11
-rw-r--r--system/database/drivers/sqlite3/sqlite3_driver.php345
-rw-r--r--system/database/drivers/sqlite3/sqlite3_forge.php226
-rw-r--r--system/database/drivers/sqlite3/sqlite3_result.php195
-rw-r--r--system/database/drivers/sqlite3/sqlite3_utility.php62
-rw-r--r--system/database/drivers/sqlsrv/index.html5
-rw-r--r--system/database/drivers/sqlsrv/sqlsrv_driver.php669
-rw-r--r--system/database/drivers/sqlsrv/sqlsrv_forge.php269
-rw-r--r--system/database/drivers/sqlsrv/sqlsrv_result.php151
-rw-r--r--system/database/drivers/sqlsrv/sqlsrv_utility.php102
-rw-r--r--system/database/index.html5
-rw-r--r--system/fonts/index.html5
-rw-r--r--system/helpers/array_helper.php145
-rw-r--r--system/helpers/captcha_helper.php382
-rw-r--r--system/helpers/cookie_helper.php136
-rw-r--r--system/helpers/date_helper.php710
-rw-r--r--system/helpers/directory_helper.php84
-rw-r--r--system/helpers/download_helper.php198
-rw-r--r--system/helpers/email_helper.php62
-rw-r--r--system/helpers/file_helper.php434
-rw-r--r--system/helpers/form_helper.php1092
-rw-r--r--system/helpers/html_helper.php401
-rw-r--r--system/helpers/index.html5
-rw-r--r--system/helpers/inflector_helper.php318
-rw-r--r--system/helpers/language_helper.php80
-rw-r--r--system/helpers/number_helper.php65
-rw-r--r--system/helpers/path_helper.php89
-rw-r--r--system/helpers/security_helper.php142
-rw-r--r--system/helpers/smiley_helper.php281
-rw-r--r--system/helpers/string_helper.php361
-rw-r--r--system/helpers/text_helper.php507
-rw-r--r--system/helpers/typography_helper.php110
-rw-r--r--system/helpers/url_helper.php605
-rw-r--r--system/helpers/xml_helper.php82
-rw-r--r--system/index.html5
-rw-r--r--system/language/english/calendar_lang.php132
-rw-r--r--system/language/english/date_lang.php78
-rw-r--r--system/language/english/db_lang.php45
-rw-r--r--system/language/english/email_lang.php79
-rw-r--r--system/language/english/form_validation_lang.php96
-rw-r--r--system/language/english/ftp_lang.php66
-rw-r--r--system/language/english/imglib_lang.php79
-rw-r--r--system/language/english/index.html5
-rw-r--r--system/language/english/migration_lang.php57
-rw-r--r--system/language/english/number_lang.php51
-rw-r--r--system/language/english/pagination_lang.php44
-rw-r--r--system/language/english/profiler_lang.php80
-rw-r--r--system/language/english/unit_test_lang.php80
-rw-r--r--system/language/english/upload_lang.php74
-rw-r--r--system/language/index.html5
-rw-r--r--system/libraries/Cache/Cache.php243
-rw-r--r--system/libraries/Cache/drivers/Cache_apc.php173
-rw-r--r--system/libraries/Cache/drivers/Cache_apcu.php219
-rw-r--r--system/libraries/Cache/drivers/Cache_dummy.php117
-rw-r--r--system/libraries/Cache/drivers/Cache_file.php224
-rw-r--r--system/libraries/Cache/drivers/Cache_memcached.php300
-rw-r--r--system/libraries/Cache/drivers/Cache_redis.php360
-rw-r--r--system/libraries/Cache/drivers/Cache_wincache.php218
-rw-r--r--system/libraries/Cache/drivers/index.html5
-rw-r--r--system/libraries/Cache/index.html5
-rw-r--r--system/libraries/Calendar.php472
-rw-r--r--system/libraries/Cart.php551
-rw-r--r--system/libraries/Driver.php261
-rw-r--r--system/libraries/Email.php2114
-rw-r--r--system/libraries/Encrypt.php500
-rw-r--r--system/libraries/Encryption.php939
-rw-r--r--system/libraries/Form_validation.php1292
-rw-r--r--system/libraries/Ftp.php414
-rw-r--r--system/libraries/Image_lib.php1544
-rw-r--r--system/libraries/Javascript.php871
-rw-r--r--system/libraries/Log.php114
-rw-r--r--system/libraries/Migration.php401
-rw-r--r--system/libraries/Pagination.php721
-rw-r--r--system/libraries/Parser.php215
-rw-r--r--system/libraries/Profiler.php477
-rw-r--r--system/libraries/Session.php793
-rw-r--r--system/libraries/Session/CI_Session_driver_interface.php60
-rw-r--r--system/libraries/Session/OldSessionWrapper.php98
-rw-r--r--system/libraries/Session/PHP8SessionWrapper.php100
-rw-r--r--system/libraries/Session/Session.php1030
-rw-r--r--system/libraries/Session/SessionUpdateTimestampHandlerInterface.php56
-rw-r--r--system/libraries/Session/Session_driver.php202
-rw-r--r--system/libraries/Session/drivers/Session_database_driver.php471
-rw-r--r--system/libraries/Session/drivers/Session_files_driver.php449
-rw-r--r--system/libraries/Session/drivers/Session_memcached_driver.php414
-rw-r--r--system/libraries/Session/drivers/Session_redis_driver.php502
-rw-r--r--system/libraries/Session/drivers/index.html11
-rw-r--r--system/libraries/Session/index.html11
-rw-r--r--system/libraries/Sha1.php251
-rw-r--r--system/libraries/Table.php431
-rw-r--r--system/libraries/Trackback.php325
-rw-r--r--system/libraries/Typography.php231
-rw-r--r--system/libraries/Unit_test.php306
-rw-r--r--system/libraries/Upload.php1055
-rw-r--r--system/libraries/User_agent.php347
-rw-r--r--system/libraries/Xmlrpc.php1708
-rw-r--r--system/libraries/Xmlrpcs.php380
-rw-r--r--system/libraries/Zip.php382
-rw-r--r--system/libraries/index.html5
-rw-r--r--system/libraries/javascript/Jquery.php1071
327 files changed, 45521 insertions, 30818 deletions
diff --git a/.editorconfig b/.editorconfig
new file mode 100644
index 000000000..39f48d95f
--- /dev/null
+++ b/.editorconfig
@@ -0,0 +1,15 @@
+# top-most EditorConfig file
+root = true
+
+# Unix-style newlines with a newline ending every file
+[*]
+end_of_line = lf
+insert_final_newline = true
+
+# Matches multiple files with brace expansion notation
+# Set default charset
+[*]
+charset = utf-8
+
+# Tab indentation (no size specified)
+indent_style = tab
diff --git a/.gitattributes b/.gitattributes
new file mode 100644
index 000000000..b4f0b6cbb
--- /dev/null
+++ b/.gitattributes
@@ -0,0 +1,23 @@
+# This file tells which files and directories should be ignored and
+# NOT downloaded when using composer to pull down a project with
+# the --prefer-dist option selected. Used to remove development
+# specific files so user has a clean download.
+
+# git files
+.gitattributes export-ignore
+# .gitignore
+
+# helper config files
+.travis.yml export-ignore
+phpdoc.dist.xml export-ignore
+
+# Misc other files
+readme.rst
+
+# They don't want all of our tests...
+tests/codeigniter/ export-ignore
+tests/travis/ export-ignore
+
+# User Guide source files and compiled files
+user_guide_src export-ignore
+user_guide export-ignore
diff --git a/.gitignore b/.gitignore
index c7d565385..95eba43c1 100644
--- a/.gitignore
+++ b/.gitignore
@@ -13,13 +13,36 @@ is_installed
application/cache/*
!application/cache/index.html
-!application/cache/.htaccess
application/logs/*
!application/logs/index.html
-!application/logs/.htaccess
+
+!application/*/.htaccess
public_html/data/js/main.min.js
/vendor
code-coverage-report.xml
code-coverage-report/
+
+composer.lock
+tests/mocks/database/ci_test.sqlite
+
+user_guide_src/build/*
+user_guide_src/cilexer/build/*
+user_guide_src/cilexer/dist/*
+user_guide_src/cilexer/pycilexer.egg-info/*
+/vendor/
+
+# IDE Files
+#-------------------------
+/nbproject/
+.idea/*
+
+## Sublime Text cache files
+*.tmlanguage.cache
+*.tmPreferences.cache
+*.stTheme.cache
+*.sublime-workspace
+*.sublime-project
+/tests/tests/
+/tests/results/
diff --git a/.travis.yml b/.travis.yml
deleted file mode 100644
index 43cfb824e..000000000
--- a/.travis.yml
+++ /dev/null
@@ -1,5 +0,0 @@
-branches:
- except:
- - 2.1-stable
- - 2.2-stable
- - master \ No newline at end of file
diff --git a/Dockerfile b/Dockerfile
index 0b18a4e81..203c36ed3 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -1,7 +1,8 @@
-FROM alpine:edge
-MAINTAINER Sebastian Rakel <sebastian@devunit.eu>
+FROM alpine:3
+LABEL maintainer="sebastian@devunit.eu"
-RUN apk add --no-cache -X http://dl-cdn.alpinelinux.org/alpine/edge/testing bash php5 py-pygments py2-pip imagemagick php5-gd nodejs composer php5-pdo_mysql php5-exif
+RUN apk add --no-cache bash php8 py-pygments py-pip imagemagick php8-gd nodejs composer php8-pdo_mysql php8-exif php8-ctype php8-session git php8-fileinfo msmtp php8-pgsql php8-phar php8-json
+RUN ln -sf /usr/bin/python3 /usr/bin/python
ENV FILEBIN_HOME_DIR /var/lib/filebin
ENV FILEBIN_DIR $FILEBIN_HOME_DIR/filebin
@@ -9,10 +10,12 @@ ENV FILEBIN_DIR $FILEBIN_HOME_DIR/filebin
ADD . $FILEBIN_DIR
RUN adduser -S -h $FILEBIN_HOME_DIR filebin
-RUN chown filebin: -R $FILEBIN_HOME_DIR
+RUN chown filebin: -R $FILEBIN_HOME_DIR
RUN pip install ansi2html
+RUN sed -i 's+.*sendmail_path =.*+sendmail_path = "/usr/bin/msmtp -C ${FILEBIN_HOME_DIR}/msmtprc --logfile ${FILEBIN_HOME_DIR}/msmtp.log -a filebinmail -t"+' /etc/php8/php.ini
+
USER filebin
ADD docker/filebin_starter.sh $FILEBIN_HOME_DIR
diff --git a/NEWS b/NEWS
index d661e9020..b70320d2b 100644
--- a/NEWS
+++ b/NEWS
@@ -1,5 +1,120 @@
This file lists major, incompatible or otherwise important changes, you should look at it after every update.
+NEXT
+ - Fix password reset trying to send mails to deleted users
+
+4.0.1 2024-01-14
+ - Fix PHP 8.2 deprecation warnings
+ - Fix PHP 8.3 deprecation warnings
+ - Update CodeIgniter to development branch
+
+4.0.0 2022-04-10
+ - BREAKING CHANGE: Minimum required PHP version is now >= 7
+ - More PHP 8.1 compatibility fixes
+ - Fix exception in multipaste queue when queued item has been deleted
+
+3.6.2 2022-01-09
+ - Use python3 instead of python for Ubuntu 20.04 and Debian 11 compatibility.
+ If you do not have python 3, you will have to change shebang in
+ `scripts/get_lexer_list.py`. The script is still compatible with python 2.
+ - Update CodeIgniter to latest master for some additional PHP 8.0 and 8.1 fixes
+
+3.6.1 2021-04-11
+ - Copy filename when repasting file
+ - Add link to new android client
+
+3.6.0 2021-02-21
+ - PHP 8.0 comptibility fixes
+ - Add json and crystal filename extensions for highlighting
+
+3.5.0 2020-10-07
+ - API 2.2.0: Add `minimum-id-length` post parameter to
+ `file/create_multipaste` and `file/upload` endpoints
+
+3.4.5 2020-06-12
+ - Fix image orientation/rotation when viewing images with the
+ colorbox/lightbox in Firefox 76/Chromium 83.
+
+3.4.4 2020-04-19
+ - Log PublicApiException to error log
+ - Reclassify various user input/insufficient permission exceptions
+
+3.4.3 2019-12-07
+ - PHP 7.4 compatibility fixes
+
+3.4.2 2019-12-06
+ - PHP 7.4 compatibility fixes
+
+3.4.1 2019-09-27
+ - Update CodeIgniter to 3.1.11,
+ https://codeigniter.com/userguide3/changelog.html#version-3-1-11
+ - Remove (broken) thumbnails for PDF files from upload_history_thumbnails page
+ - Update composer files
+
+3.4.0 2019-05-21
+ - Allow data: URLs in Content-Security-Policy header for images and fonts
+
+3.3.2 2019-05-15
+ - Fix compatability with Pygments 2.4.0
+
+3.3.1 2019-01-28
+ - Fix CSRF issue breaking multipaste creation page
+ - Update codeigniter to 3.1.10
+ - Remove imagemagick PDF thumbnail support due to Arch Linux disabling it in
+ imagemagick due to repeated security concerns.
+
+3.3.0 2018-09-19
+ - LDAP: Allow optional binding/authentication
+ - LDAP: Allow optional filtering of allowed users
+ - Various Dockerfile fixes to make it work again
+ - Document finfo dependency
+
+3.2.0 2018-07-04
+ - Add `php index.php user delete_user` command for admin user deletion
+ - Document add_user admin command
+ - Update codeigniter to 3.1.9
+ - Document mbstring dependency
+
+3.1.1 2018-06-01
+ - Fix call to undefined method clean_multipaste_tarballs in cronjob
+
+3.1.0 2018-06-01
+ - Update asciinema player to support new cast format
+ - Allow users to delete invitation keys
+ - Add support to remove database entries without files to `php index.php file clean_stale_files`
+
+3.0.3 2018-03-23
+ - Update codeiginiter to 3.1.8
+
+3.0.2 2018-02-06
+ - Ensure newly uploaded files do not exist before adding them to the DB
+ - Update file modification time instead of overwriting it with it's own data
+ when a file is uploaded multiple times
+ - Minor changes:
+ + Support values that are not multiples of one day for upload_max_age
+ + Add config settings for testsuite to example configs
+ + Include most php extension dependencies in composer.json
+ + Use dedicated, temporary upload directory for testsuite
+
+3.0.1 2017-12-02
+ - Document different database settings for postgres in example config
+
+3.0.0 2017-12-02
+
+ - This version ensures PHP 7.2 support by updating the underlying framework
+
+ - INCOMPATIBLE: You need to perform the following actions for this update:
+ - Changes in application/config/database.php
+ + Change "$active_record = TRUE" to "$query_builder = TRUE"
+ + Change the pconnect setting to FALSE
+ + Change the stricton setting to TRUE
+ + If you use mysql: (NOT for postgres installations)
+ MYSQL Change the char_set setting to "utf8mb4"
+ MYSQL Change the dbcollat setting to "utf8mb4_bin"
+ - Changes in application/config/config-local.php
+ + Set base_url to your domain and, if used, installation directory.
+ Example: $config['base_url'] = "https://paste.xinu.at/"
+
2.1.3 2017-07-04
- API 2.1.1: Fix incorrect types of empty objects returned by file/history and file/delete
diff --git a/README.md b/README.md
index 6184b6ee3..60927726f 100644
--- a/README.md
+++ b/README.md
@@ -23,6 +23,10 @@ list](https://lists.server-speed.net/listinfo/filebin-general) for everything
(general discussion, patches, announcements, ...). If need be more lists will be
created at a later date.
+### IRC
+
+The official IRC channel is [irc.freenode.net/#filebin](irc://irc.freenode.net/#filebin).
+
### Patches
Please submit patches (preferably git send-email) to the [filebin-general
@@ -34,15 +38,19 @@ You can also open a pull request on [GitHub](https://github.com/Bluewind/filebin
## Dependencies
-* PHP >=5.5 with the following modules
+* PHP >=7.0 with the following modules
* gd for thumbnail generation
* exif for thumbnail generation
* phar for tarball creation
* mysql, mysqli, pgsql, pdo_mysql (any of those) for database access
+ * mbstring for multibyte support
+ * finfo for mimetype detection
+ * xdebug for tests (not required on production)
* [composer](https://getcomposer.org/) for dependency management
* [pygmentize](http://pygments.org/) for code highlighting
* [ansi2html](http://pypi.python.org/pypi/ansi2html) for shell output rendering (ANSI color codes)
* [imagemagick](http://www.imagemagick.org/) for additional thumbnail generation
+ * ghostscript for PDF thumbnail creation via imagemagick
* [node.js](https://nodejs.org/) for javascript minification
diff --git a/application/.htaccess b/application/.htaccess
index 14249c50b..6c63ed4c4 100644
--- a/application/.htaccess
+++ b/application/.htaccess
@@ -1 +1,6 @@
-Deny from all \ No newline at end of file
+<IfModule authz_core_module>
+ Require all denied
+</IfModule>
+<IfModule !authz_core_module>
+ Deny from all
+</IfModule> \ No newline at end of file
diff --git a/application/cache/.htaccess b/application/cache/.htaccess
deleted file mode 100644
index 14249c50b..000000000
--- a/application/cache/.htaccess
+++ /dev/null
@@ -1 +0,0 @@
-Deny from all \ No newline at end of file
diff --git a/application/cache/index.html b/application/cache/index.html
index c942a79ce..bcb7cae34 100644
--- a/application/cache/index.html
+++ b/application/cache/index.html
@@ -1,4 +1,5 @@
-<html>
+<!DOCTYPE html>
+<html lang="en">
<head>
<title>403 Forbidden</title>
</head>
@@ -7,4 +8,4 @@
<p>Directory access is forbidden.</p>
</body>
-</html> \ No newline at end of file
+</html>
diff --git a/application/config/autoload.php b/application/config/autoload.php
index a471f3ab2..a95dc038a 100644
--- a/application/config/autoload.php
+++ b/application/config/autoload.php
@@ -1,4 +1,6 @@
-<?php if ( ! defined('BASEPATH')) exit('No direct script access allowed');
+<?php
+defined('BASEPATH') OR exit('No direct script access allowed');
+
/*
| -------------------------------------------------------------------
| AUTO-LOADER
@@ -20,40 +22,64 @@
|
| 1. Packages
| 2. Libraries
-| 3. Helper files
-| 4. Custom config files
-| 5. Language files
-| 6. Models
+| 3. Drivers
+| 4. Helper files
+| 5. Custom config files
+| 6. Language files
+| 7. Models
|
*/
/*
| -------------------------------------------------------------------
-| Auto-load Packges
+| Auto-load Packages
| -------------------------------------------------------------------
| Prototype:
|
| $autoload['packages'] = array(APPPATH.'third_party', '/usr/local/shared');
|
*/
-
$autoload['packages'] = array();
-
/*
| -------------------------------------------------------------------
| Auto-load Libraries
| -------------------------------------------------------------------
-| These are the classes located in the system/libraries folder
-| or in your application/libraries folder.
+| These are the classes located in system/libraries/ or your
+| application/libraries/ directory, with the addition of the
+| 'database' library, which is somewhat of a special case.
|
| Prototype:
|
-| $autoload['libraries'] = array('database', 'session', 'xmlrpc');
+| $autoload['libraries'] = array('database', 'email', 'session');
+|
+| You can also supply an alternative library name to be assigned
+| in the controller:
+|
+| $autoload['libraries'] = array('user_agent' => 'ua');
*/
-
$autoload['libraries'] = array('database');
+/*
+| -------------------------------------------------------------------
+| Auto-load Drivers
+| -------------------------------------------------------------------
+| These classes are located in system/libraries/ or in your
+| application/libraries/ directory, but are also placed inside their
+| own subdirectory and they extend the CI_Driver_Library class. They
+| offer multiple interchangeable driver options.
+|
+| Prototype:
+|
+| $autoload['drivers'] = array('cache');
+|
+| You can also supply an alternative property name to be assigned in
+| the controller:
+|
+| $autoload['drivers'] = array('cache' => 'cch');
+|
+*/
+$autoload['drivers'] = array();
/*
| -------------------------------------------------------------------
@@ -63,10 +89,8 @@ $autoload['libraries'] = array('database');
|
| $autoload['helper'] = array('url', 'file');
*/
-
$autoload['helper'] = array('url');
-
/*
| -------------------------------------------------------------------
| Auto-load Config files
@@ -79,10 +103,8 @@ $autoload['helper'] = array('url');
| config files. Otherwise, leave it blank.
|
*/
-
$autoload['config'] = array();
-
/*
| -------------------------------------------------------------------
| Auto-load Language files
@@ -95,22 +117,19 @@ $autoload['config'] = array();
| "codeigniter_lang.php" would be referenced as array('codeigniter');
|
*/
-
$autoload['language'] = array();
-
/*
| -------------------------------------------------------------------
| Auto-load Models
| -------------------------------------------------------------------
| Prototype:
|
-| $autoload['model'] = array('model1', 'model2');
+| $autoload['model'] = array('first_model', 'second_model');
+|
+| You can also supply an alternative model name to be assigned
+| in the controller:
|
+| $autoload['model'] = array('first_model' => 'first');
*/
-
$autoload['model'] = array();
-
-
-/* End of file autoload.php */
-/* Location: ./application/config/autoload.php */ \ No newline at end of file
diff --git a/application/config/config.php b/application/config/config.php
index 5ec10e233..343528363 100644
--- a/application/config/config.php
+++ b/application/config/config.php
@@ -1,4 +1,5 @@
-<?php if ( ! defined('BASEPATH')) exit('No direct script access allowed');
+<?php
+defined('BASEPATH') OR exit('No direct script access allowed');
/*
|--------------------------------------------------------------------------
@@ -10,11 +11,19 @@
|
| http://example.com/
|
-| If this is not set then CodeIgniter will guess the protocol, domain and
-| path to your installation.
+| WARNING: You MUST set this value!
+|
+| If it is not set, then CodeIgniter will try to guess the protocol and
+| path to your installation, but due to security concerns the hostname will
+| be set to $_SERVER['SERVER_ADDR'] if available, or localhost otherwise.
+| The auto-detection mechanism exists only for convenience during
+| development and MUST NOT be used in production!
+|
+| If you need to allow multiple domains, remember that this file is still
+| a PHP script and you can easily do that on your own.
|
*/
-$config['base_url'] = '';
+$config['base_url'] = '';
/*
|--------------------------------------------------------------------------
@@ -34,17 +43,16 @@ $config['index_page'] = 'index.php';
|--------------------------------------------------------------------------
|
| This item determines which server global should be used to retrieve the
-| URI string. The default setting of 'AUTO' works for most servers.
+| URI string. The default setting of 'REQUEST_URI' works for most servers.
| If your links do not seem to work, try one of the other delicious flavors:
|
-| 'AUTO' Default - auto detects
-| 'PATH_INFO' Uses the PATH_INFO
-| 'QUERY_STRING' Uses the QUERY_STRING
-| 'REQUEST_URI' Uses the REQUEST_URI
-| 'ORIG_PATH_INFO' Uses the ORIG_PATH_INFO
+| 'REQUEST_URI' Uses $_SERVER['REQUEST_URI']
+| 'QUERY_STRING' Uses $_SERVER['QUERY_STRING']
+| 'PATH_INFO' Uses $_SERVER['PATH_INFO']
|
+| WARNING: If you set this to 'PATH_INFO', URIs will always be URL-decoded!
*/
-$config['uri_protocol'] = 'AUTO';
+$config['uri_protocol'] = 'REQUEST_URI';
/*
|--------------------------------------------------------------------------
@@ -54,9 +62,10 @@ $config['uri_protocol'] = 'AUTO';
| This option allows you to add a suffix to all URLs generated by CodeIgniter.
| For more information please see the user guide:
|
-| http://codeigniter.com/user_guide/general/urls.html
+| https://codeigniter.com/userguide3/general/urls.html
+|
+| Note: This option is ignored for CLI requests.
*/
-
$config['url_suffix'] = '';
/*
@@ -79,6 +88,8 @@ $config['language'] = 'english';
| This determines which character set is used by default in various methods
| that require a character set to be provided.
|
+| See https://secure.php.net/htmlspecialchars for a list of supported charsets.
+|
*/
$config['charset'] = 'UTF-8';
@@ -93,7 +104,6 @@ $config['charset'] = 'UTF-8';
*/
$config['enable_hooks'] = FALSE;
-
/*
|--------------------------------------------------------------------------
| Class Extension Prefix
@@ -102,29 +112,55 @@ $config['enable_hooks'] = FALSE;
| This item allows you to set the filename/classname prefix when extending
| native libraries. For more information please see the user guide:
|
-| http://codeigniter.com/user_guide/general/core_classes.html
-| http://codeigniter.com/user_guide/general/creating_libraries.html
+| https://codeigniter.com/userguide3/general/core_classes.html
+| https://codeigniter.com/userguide3/general/creating_libraries.html
|
*/
$config['subclass_prefix'] = 'MY_';
+/*
+|--------------------------------------------------------------------------
+| Composer auto-loading
+|--------------------------------------------------------------------------
+|
+| Enabling this setting will tell CodeIgniter to look for a Composer
+| package auto-loader script in application/vendor/autoload.php.
+|
+| $config['composer_autoload'] = TRUE;
+|
+| Or if you have your vendor/ directory located somewhere else, you
+| can opt to set a specific path as well:
+|
+| $config['composer_autoload'] = '/path/to/vendor/autoload.php';
+|
+| For more information about Composer, please visit https://getcomposer.org/
+|
+| Note: This will NOT disable or override the CodeIgniter-specific
+| autoloading (application/config/autoload.php)
+*/
+$config['composer_autoload'] = FALSE;
/*
|--------------------------------------------------------------------------
| Allowed URL Characters
|--------------------------------------------------------------------------
|
-| This lets you specify with a regular expression which characters are permitted
-| within your URLs. When someone tries to submit a URL with disallowed
-| characters they will get a warning message.
+| This lets you specify which characters are permitted within your URLs.
+| When someone tries to submit a URL with disallowed characters they will
+| get a warning message.
|
| As a security measure you are STRONGLY encouraged to restrict URLs to
| as few characters as possible. By default only these are allowed: a-z 0-9~%.:_-
|
| Leave blank to allow all characters -- but only if you are insane.
|
+| The configured value is actually a regular expression character group
+| and it will be executed as: ! preg_match('/^[<permitted_uri_chars>]+$/i
+|
| DO NOT CHANGE THIS UNLESS YOU FULLY UNDERSTAND THE REPERCUSSIONS!!
|
+| Note: This option is ignored for CLI requests.
+|
*/
if (php_sapi_name() == "cli") {
$config['permitted_uri_chars'] = '';
@@ -132,7 +168,6 @@ if (php_sapi_name() == "cli") {
$config['permitted_uri_chars'] = 'a-z 0-9~%.:_\-';
}
-
/*
|--------------------------------------------------------------------------
| Enable Query Strings
@@ -141,9 +176,6 @@ if (php_sapi_name() == "cli") {
| By default CodeIgniter uses search-engine friendly segment based URLs:
| example.com/who/what/where/
|
-| By default CodeIgniter enables access to the $_GET array. If for some
-| reason you would like to disable it, set 'allow_get_array' to FALSE.
-|
| You can optionally enable standard query string based URLs:
| example.com?who=me&what=something&where=here
|
@@ -158,19 +190,16 @@ if (php_sapi_name() == "cli") {
| use segment based URLs.
|
*/
-$config['allow_get_array'] = TRUE;
$config['enable_query_strings'] = FALSE;
-$config['controller_trigger'] = 'c';
-$config['function_trigger'] = 'm';
-$config['directory_trigger'] = 'd'; // experimental not currently in use
+$config['controller_trigger'] = 'c';
+$config['function_trigger'] = 'm';
+$config['directory_trigger'] = 'd';
/*
|--------------------------------------------------------------------------
| Error Logging Threshold
|--------------------------------------------------------------------------
|
-| If you have enabled error logging, you can set an error threshold to
-| determine what gets logged. Threshold options are:
| You can enable error logging by setting a threshold over zero. The
| threshold determines what gets logged. Threshold options are:
|
@@ -180,6 +209,10 @@ $config['directory_trigger'] = 'd'; // experimental not currently in use
| 3 = Informational Messages
| 4 = All Messages
|
+| You can also pass an array with threshold levels to show individual error types
+|
+| array(2) = Debug Messages, without Error Messages
+|
| For a live site you'll usually only enable Errors (1) to be logged otherwise
| your log files will fill up very fast.
|
@@ -192,13 +225,36 @@ $config['log_threshold'] = 0;
|--------------------------------------------------------------------------
|
| Leave this BLANK unless you would like to set something other than the default
-| application/logs/ folder. Use a full server path with trailing slash.
+| application/logs/ directory. Use a full server path.
|
*/
$config['log_path'] = '';
/*
|--------------------------------------------------------------------------
+| Error Logging FILENAME
+|--------------------------------------------------------------------------
+|
+| Leave this BLANK unless you would like to set something other than the default
+| 'log-'.date('Y-m-d').'.php'. No DIRECTORY_SEPARATOR(s), just the filename.
+|
+*/
+$config['log_filename'] = '';
+
+/*
+|--------------------------------------------------------------------------
+| Log File Permissions
+|--------------------------------------------------------------------------
+|
+| The file system permissions to be applied on newly created log files.
+|
+| IMPORTANT: This MUST be an integer (no quotes) and you MUST use octal
+| integer notation (i.e. 0700, 0644, etc.)
+*/
+$config['log_file_permissions'] = 0644;
+
+/*
+|--------------------------------------------------------------------------
| Date Format for Logs
|--------------------------------------------------------------------------
|
@@ -210,80 +266,143 @@ $config['log_date_format'] = 'Y-m-d H:i:s';
/*
|--------------------------------------------------------------------------
+| Error Views Directory Path
+|--------------------------------------------------------------------------
+|
+| Leave this BLANK unless you would like to set something other than the default
+| application/views/errors/ directory. Use a full server path.
+|
+*/
+$config['error_views_path'] = '';
+
+/*
+|--------------------------------------------------------------------------
| Cache Directory Path
|--------------------------------------------------------------------------
|
| Leave this BLANK unless you would like to set something other than the default
-| system/cache/ folder. Use a full server path with trailing slash.
+| application/cache/ directory. Use a full server path.
|
*/
$config['cache_path'] = '';
/*
|--------------------------------------------------------------------------
-| Encryption Key
+| Cache Include Query String
|--------------------------------------------------------------------------
|
-| If you use the Encryption class or the Session class you
-| MUST set an encryption key. See the user guide for info.
+| Whether to take the URL query string into consideration when generating
+| output cache files. Valid options are:
+|
+| FALSE = Disabled
+| TRUE = Enabled, take all query parameters into account.
+| Please be aware that this may result in numerous cache
+| files generated for the same page over and over again.
+| array('q') = Enabled, but only take into account the specified list
+| of query parameters.
|
*/
-$config['encryption_key'] = '';
+$config['cache_query_string'] = FALSE;
/*
|--------------------------------------------------------------------------
-| Session Variables
+| Encryption Key
|--------------------------------------------------------------------------
|
-| 'sess_cookie_name' = the name you want for the cookie
-| 'sess_expiration' = the number of SECONDS you want the session to last.
-| by default sessions last 7200 seconds (two hours). Set to zero for no expiration.
-| 'sess_expire_on_close' = Whether to cause the session to expire automatically
-| when the browser window is closed
-| 'sess_encrypt_cookie' = Whether to encrypt the cookie
-| 'sess_use_database' = Whether to save the session data to a database
-| 'sess_table_name' = The name of the session database table
-| 'sess_match_ip' = Whether to match the user's IP address when reading the session data
-| 'sess_match_useragent' = Whether to match the User Agent when reading the session data
-| 'sess_time_to_update' = how many seconds between CI refreshing Session Information
+| If you use the Encryption class, you must set an encryption key.
+| See the user guide for more info.
+|
+| https://codeigniter.com/userguide3/libraries/encryption.html
|
*/
-$config['sess_cookie_name'] = 'ci_session';
-$config['sess_expiration'] = 7200;
-$config['sess_expire_on_close'] = FALSE;
-$config['sess_encrypt_cookie'] = FALSE;
-$config['sess_use_database'] = true;
-$config['sess_table_name'] = 'ci_sessions';
-$config['sess_match_ip'] = FALSE;
-$config['sess_match_useragent'] = TRUE;
-$config['sess_time_to_update'] = 300;
+$config['encryption_key'] = '';
/*
|--------------------------------------------------------------------------
-| Cookie Related Variables
+| Session Variables
|--------------------------------------------------------------------------
|
-| 'cookie_prefix' = Set a prefix if you need to avoid collisions
-| 'cookie_domain' = Set to .your-domain.com for site-wide cookies
-| 'cookie_path' = Typically will be a forward slash
-| 'cookie_secure' = Cookies will only be set if a secure HTTPS connection exists.
+| 'sess_driver'
+|
+| The storage driver to use: files, database, redis, memcached
+|
+| 'sess_cookie_name'
+|
+| The session cookie name, must contain only [0-9a-z_-] characters
+|
+| 'sess_samesite'
+|
+| Session cookie SameSite attribute: Lax (default), Strict or None
+|
+| 'sess_expiration'
+|
+| The number of SECONDS you want the session to last.
+| Setting to 0 (zero) means expire when the browser is closed.
+|
+| 'sess_save_path'
+|
+| The location to save sessions to, driver dependent.
+|
+| For the 'files' driver, it's a path to a writable directory.
+| WARNING: Only absolute paths are supported!
+|
+| For the 'database' driver, it's a table name.
+| Please read up the manual for the format with other session drivers.
+|
+| IMPORTANT: You are REQUIRED to set a valid save path!
+|
+| 'sess_match_ip'
+|
+| Whether to match the user's IP address when reading the session data.
+|
+| WARNING: If you're using the database driver, don't forget to update
+| your session table's PRIMARY KEY when changing this setting.
+|
+| 'sess_time_to_update'
+|
+| How many seconds between CI regenerating the session ID.
+|
+| 'sess_regenerate_destroy'
+|
+| Whether to destroy session data associated with the old session ID
+| when auto-regenerating the session ID. When set to FALSE, the data
+| will be later deleted by the garbage collector.
+|
+| Other session cookie settings are shared with the rest of the application,
+| except for 'cookie_prefix' and 'cookie_httponly', which are ignored here.
|
*/
-$config['cookie_prefix'] = "";
-$config['cookie_domain'] = "";
-$config['cookie_path'] = "/";
-$config['cookie_secure'] = FALSE;
+$config['sess_driver'] = 'database';
+$config['sess_cookie_name'] = 'ci_session';
+$config['sess_samesite'] = 'Lax';
+$config['sess_expiration'] = 7200;
+$config['sess_save_path'] = "ci_sessions";
+$config['sess_match_ip'] = FALSE;
+$config['sess_time_to_update'] = 300;
+$config['sess_regenerate_destroy'] = FALSE;
/*
|--------------------------------------------------------------------------
-| Global XSS Filtering
+| Cookie Related Variables
|--------------------------------------------------------------------------
|
-| Determines whether the XSS filter is always active when GET, POST or
-| COOKIE data is encountered
+| 'cookie_prefix' = Set a cookie name prefix if you need to avoid collisions
+| 'cookie_domain' = Set to .your-domain.com for site-wide cookies
+| 'cookie_path' = Typically will be a forward slash
+| 'cookie_secure' = Cookie will only be set if a secure HTTPS connection exists.
+| 'cookie_httponly' = Cookie will only be accessible via HTTP(S) (no javascript)
+| 'cookie_samesite' = Cookie's samesite attribute (Lax, Strict or None)
+|
+| Note: These settings (with the exception of 'cookie_prefix' and
+| 'cookie_httponly') will also affect sessions.
|
*/
-$config['global_xss_filtering'] = FALSE;
+$config['cookie_prefix'] = '';
+$config['cookie_domain'] = '';
+$config['cookie_path'] = '/';
+$config['cookie_secure'] = FALSE;
+$config['cookie_httponly'] = FALSE;
+$config['cookie_samesite'] = 'Lax';
/*
|--------------------------------------------------------------------------
@@ -296,11 +415,15 @@ $config['global_xss_filtering'] = FALSE;
| 'csrf_token_name' = The token name
| 'csrf_cookie_name' = The cookie name
| 'csrf_expire' = The number in seconds the token should expire.
+| 'csrf_regenerate' = Regenerate token on every submission
+| 'csrf_exclude_uris' = Array of URIs which ignore CSRF checks
*/
$config['csrf_protection'] = FALSE; // our controller enables this later
$config['csrf_token_name'] = 'csrf_test_name';
$config['csrf_cookie_name'] = 'csrf_cookie_name';
$config['csrf_expire'] = 7200;
+$config['csrf_regenerate'] = TRUE;
+$config['csrf_exclude_uris'] = array();
/*
|--------------------------------------------------------------------------
@@ -312,6 +435,9 @@ $config['csrf_expire'] = 7200;
| Even if it does, however, not all browsers support compression
| so enable only if you are reasonably sure your visitors can handle it.
|
+| Only used if zlib.output_compression is turned off in your php.ini.
+| Please do not use it together with httpd-level output compression.
+|
| VERY IMPORTANT: If you are getting a blank page when compression is enabled it
| means you are prematurely outputting something to your browser. It could
| even be a line of whitespace at the end of one of your scripts. For
@@ -326,38 +452,29 @@ $config['compress_output'] = FALSE;
| Master Time Reference
|--------------------------------------------------------------------------
|
-| Options are 'local' or 'gmt'. This pref tells the system whether to use
-| your server's local time as the master 'now' reference, or convert it to
-| GMT. See the 'date helper' page of the user guide for information
-| regarding date handling.
+| Options are 'local' or any PHP supported timezone. This preference tells
+| the system whether to use your server's local time as the master 'now'
+| reference, or convert it to the configured one timezone. See the 'date
+| helper' page of the user guide for information regarding date handling.
|
*/
$config['time_reference'] = 'local';
-
-/*
-|--------------------------------------------------------------------------
-| Rewrite PHP Short Tags
-|--------------------------------------------------------------------------
-|
-| If your PHP installation does not have short tag support enabled CI
-| can rewrite the tags on-the-fly, enabling you to utilize that syntax
-| in your view files. Options are TRUE or FALSE (boolean)
-|
-*/
-$config['rewrite_short_tags'] = FALSE;
-
-
/*
|--------------------------------------------------------------------------
| Reverse Proxy IPs
|--------------------------------------------------------------------------
|
-| If your server is behind a reverse proxy, you must whitelist the proxy IP
-| addresses from which CodeIgniter should trust the HTTP_X_FORWARDED_FOR
-| header in order to properly identify the visitor's IP address.
-| Comma-delimited, e.g. '10.0.1.200,10.0.1.201'
+| If your server is behind a reverse proxy, you must whitelist the proxy
+| IP addresses from which CodeIgniter should trust headers such as
+| HTTP_X_FORWARDED_FOR and HTTP_CLIENT_IP in order to properly identify
+| the visitor's IP address.
+|
+| You can use both an array or a comma-separated list of proxy addresses,
+| as well as specifying whole subnets. Here are a few examples:
|
+| Comma-separated: '10.0.1.200,192.168.5.0/24'
+| Array: array('10.0.1.200', '192.168.5.0/24')
*/
$config['proxy_ips'] = '';
@@ -431,7 +548,13 @@ if (extension_loaded("ldap")) {
),
// Please note that php-ldap converts attributes to lowercase
"userid_field" => "uidnumber", // This has to be a unique integer
- "username_field" => "uid" // This is the value the user supplies on the login form
+ "username_field" => "uid", // This is the value the user supplies on the login form
+ // Optional parameters
+ // "bind_rdn" => "uid=search-user,cn=users,dc=example,dc=com", // This is the user used to authenticate for searches
+ // "bind_password" => "***", // This is the password for the search user
+ // You can optionally filter the LDAP users who are allowed to log in using any valid LDAP filter. %s will be replaced
+ // by the user name.
+ // "filter" => "(&(uid=%s)(memberOf=cn=FileBinUsers,cn=groups,dc=example,dc=com))",
);
}
@@ -478,5 +601,11 @@ if (file_exists(APPPATH.'config/config-local.php')) {
include APPPATH.'config/config-local.php';
}
-/* End of file config.php */
-/* Location: ./application/config/config.php */
+if (getenv("ENVIRONMENT") === "testsuite" && isset($_SERVER['SERVER_PORT'])) {
+ $config['base_url'] = 'http://127.0.0.1:'.$_SERVER['SERVER_PORT'].'/';
+}
+
+if (getenv("ENVIRONMENT") === "testsuite") {
+ $config['upload_path'] = FCPATH.'testsuite-tmp';
+ $config['auth_db']['hashing_options']['cost'] = 5;
+}
diff --git a/application/config/constants.php b/application/config/constants.php
index 1185dbca0..4da7b1af2 100644
--- a/application/config/constants.php
+++ b/application/config/constants.php
@@ -1,4 +1,17 @@
-<?php if ( ! defined('BASEPATH')) exit('No direct script access allowed');
+<?php
+defined('BASEPATH') OR exit('No direct script access allowed');
+
+/*
+|--------------------------------------------------------------------------
+| Display Debug backtrace
+|--------------------------------------------------------------------------
+|
+| If set to TRUE, a backtrace will be displayed along with php errors. If
+| error_reporting is disabled, the backtrace will not display, regardless
+| of this setting
+|
+*/
+defined('SHOW_DEBUG_BACKTRACE') OR define('SHOW_DEBUG_BACKTRACE', TRUE);
putenv('HOME='.FCPATH);
@@ -15,10 +28,10 @@ putenv('HOME='.FCPATH);
| always be used to set the mode correctly.
|
*/
-define('FILE_READ_MODE', 0644);
-define('FILE_WRITE_MODE', 0666);
-define('DIR_READ_MODE', 0755);
-define('DIR_WRITE_MODE', 0777);
+defined('FILE_READ_MODE') OR define('FILE_READ_MODE', 0644);
+defined('FILE_WRITE_MODE') OR define('FILE_WRITE_MODE', 0666);
+defined('DIR_READ_MODE') OR define('DIR_READ_MODE', 0755);
+defined('DIR_WRITE_MODE') OR define('DIR_WRITE_MODE', 0755);
/*
|--------------------------------------------------------------------------
@@ -28,16 +41,47 @@ define('DIR_WRITE_MODE', 0777);
| These modes are used when working with fopen()/popen()
|
*/
+defined('FOPEN_READ') OR define('FOPEN_READ', 'rb');
+defined('FOPEN_READ_WRITE') OR define('FOPEN_READ_WRITE', 'r+b');
+defined('FOPEN_WRITE_CREATE_DESTRUCTIVE') OR define('FOPEN_WRITE_CREATE_DESTRUCTIVE', 'wb'); // truncates existing file data, use with care
+defined('FOPEN_READ_WRITE_CREATE_DESTRUCTIVE') OR define('FOPEN_READ_WRITE_CREATE_DESTRUCTIVE', 'w+b'); // truncates existing file data, use with care
+defined('FOPEN_WRITE_CREATE') OR define('FOPEN_WRITE_CREATE', 'ab');
+defined('FOPEN_READ_WRITE_CREATE') OR define('FOPEN_READ_WRITE_CREATE', 'a+b');
+defined('FOPEN_WRITE_CREATE_STRICT') OR define('FOPEN_WRITE_CREATE_STRICT', 'xb');
+defined('FOPEN_READ_WRITE_CREATE_STRICT') OR define('FOPEN_READ_WRITE_CREATE_STRICT', 'x+b');
-define('FOPEN_READ', 'rb');
-define('FOPEN_READ_WRITE', 'r+b');
-define('FOPEN_WRITE_CREATE_DESTRUCTIVE', 'wb'); // truncates existing file data, use with care
-define('FOPEN_READ_WRITE_CREATE_DESTRUCTIVE', 'w+b'); // truncates existing file data, use with care
-define('FOPEN_WRITE_CREATE', 'ab');
-define('FOPEN_READ_WRITE_CREATE', 'a+b');
-define('FOPEN_WRITE_CREATE_STRICT', 'xb');
-define('FOPEN_READ_WRITE_CREATE_STRICT', 'x+b');
-
-
-/* End of file constants.php */
-/* Location: ./application/config/constants.php */ \ No newline at end of file
+/*
+|--------------------------------------------------------------------------
+| Exit Status Codes
+|--------------------------------------------------------------------------
+|
+| Used to indicate the conditions under which the script is exit()ing.
+| While there is no universal standard for error codes, there are some
+| broad conventions. Three such conventions are mentioned below, for
+| those who wish to make use of them. The CodeIgniter defaults were
+| chosen for the least overlap with these conventions, while still
+| leaving room for others to be defined in future versions and user
+| applications.
+|
+| The three main conventions used for determining exit status codes
+| are as follows:
+|
+| Standard C/C++ Library (stdlibc):
+| https://www.gnu.org/software/libc/manual/html_node/Exit-Status.html
+| (This link also contains other GNU-specific conventions)
+| BSD sysexits.h:
+| https://www.gsp.com/cgi-bin/man.cgi?section=3&topic=sysexits
+| Bash scripting:
+| http://tldp.org/LDP/abs/html/exitcodes.html
+|
+*/
+defined('EXIT_SUCCESS') OR define('EXIT_SUCCESS', 0); // no errors
+defined('EXIT_ERROR') OR define('EXIT_ERROR', 1); // generic error
+defined('EXIT_CONFIG') OR define('EXIT_CONFIG', 3); // configuration error
+defined('EXIT_UNKNOWN_FILE') OR define('EXIT_UNKNOWN_FILE', 4); // file not found
+defined('EXIT_UNKNOWN_CLASS') OR define('EXIT_UNKNOWN_CLASS', 5); // unknown class
+defined('EXIT_UNKNOWN_METHOD') OR define('EXIT_UNKNOWN_METHOD', 6); // unknown class member
+defined('EXIT_USER_INPUT') OR define('EXIT_USER_INPUT', 7); // invalid user input
+defined('EXIT_DATABASE') OR define('EXIT_DATABASE', 8); // database error
+defined('EXIT__AUTO_MIN') OR define('EXIT__AUTO_MIN', 9); // lowest automatically-assigned error code
+defined('EXIT__AUTO_MAX') OR define('EXIT__AUTO_MAX', 125); // highest automatically-assigned error code
diff --git a/application/config/doctypes.php b/application/config/doctypes.php
index f7e1d19a2..59a7991e3 100644
--- a/application/config/doctypes.php
+++ b/application/config/doctypes.php
@@ -1,15 +1,24 @@
-<?php if ( ! defined('BASEPATH')) exit('No direct script access allowed');
+<?php
+defined('BASEPATH') OR exit('No direct script access allowed');
$_doctypes = array(
- 'xhtml11' => '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">',
- 'xhtml1-strict' => '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">',
- 'xhtml1-trans' => '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">',
- 'xhtml1-frame' => '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Frameset//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-frameset.dtd">',
- 'html5' => '<!DOCTYPE html>',
- 'html4-strict' => '<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">',
- 'html4-trans' => '<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">',
- 'html4-frame' => '<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Frameset//EN" "http://www.w3.org/TR/html4/frameset.dtd">'
- );
-
-/* End of file doctypes.php */
-/* Location: ./application/config/doctypes.php */ \ No newline at end of file
+ 'xhtml11' => '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">',
+ 'xhtml1-strict' => '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">',
+ 'xhtml1-trans' => '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">',
+ 'xhtml1-frame' => '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Frameset//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-frameset.dtd">',
+ 'xhtml-basic11' => '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML Basic 1.1//EN" "http://www.w3.org/TR/xhtml-basic/xhtml-basic11.dtd">',
+ 'html5' => '<!DOCTYPE html>',
+ 'html4-strict' => '<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">',
+ 'html4-trans' => '<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">',
+ 'html4-frame' => '<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Frameset//EN" "http://www.w3.org/TR/html4/frameset.dtd">',
+ 'mathml1' => '<!DOCTYPE math SYSTEM "http://www.w3.org/Math/DTD/mathml1/mathml.dtd">',
+ 'mathml2' => '<!DOCTYPE math PUBLIC "-//W3C//DTD MathML 2.0//EN" "http://www.w3.org/Math/DTD/mathml2/mathml2.dtd">',
+ 'svg10' => '<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.0//EN" "http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd">',
+ 'svg11' => '<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">',
+ 'svg11-basic' => '<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1 Basic//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11-basic.dtd">',
+ 'svg11-tiny' => '<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1 Tiny//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11-tiny.dtd">',
+ 'xhtml-math-svg-xh' => '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1 plus MathML 2.0 plus SVG 1.1//EN" "http://www.w3.org/2002/04/xhtml-math-svg/xhtml-math-svg.dtd">',
+ 'xhtml-math-svg-sh' => '<!DOCTYPE svg:svg PUBLIC "-//W3C//DTD XHTML 1.1 plus MathML 2.0 plus SVG 1.1//EN" "http://www.w3.org/2002/04/xhtml-math-svg/xhtml-math-svg.dtd">',
+ 'xhtml-rdfa-1' => '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML+RDFa 1.0//EN" "http://www.w3.org/MarkUp/DTD/xhtml-rdfa-1.dtd">',
+ 'xhtml-rdfa-2' => '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML+RDFa 1.1//EN" "http://www.w3.org/MarkUp/DTD/xhtml-rdfa-2.dtd">'
+);
diff --git a/application/config/example/config-local.php b/application/config/example/config-local.php
index 941c8b119..172f0e1cc 100644
--- a/application/config/example/config-local.php
+++ b/application/config/example/config-local.php
@@ -6,6 +6,9 @@
* For descriptions of the options please refer to config.php.
*/
+# URL to the application
+$config['base_url'] = '';
+
// set this to a 32char random string
$config['encryption_key'] = '';
diff --git a/application/config/example/database.php b/application/config/example/database.php
index 097e276b3..3f5255766 100644
--- a/application/config/example/database.php
+++ b/application/config/example/database.php
@@ -1,4 +1,6 @@
-<?php if ( ! defined('BASEPATH')) exit('No direct script access allowed');
+<?php
+defined('BASEPATH') OR exit('No direct script access allowed');
+
/*
| -------------------------------------------------------------------
| DATABASE CONNECTIVITY SETTINGS
@@ -12,18 +14,19 @@
| EXPLANATION OF VARIABLES
| -------------------------------------------------------------------
|
+| ['dsn'] The full DSN string describe a connection to the database.
| ['hostname'] The hostname of your database server.
| ['username'] The username used to connect to the database
| ['password'] The password used to connect to the database
| ['database'] The name of the database you want to connect to
-| ['dbdriver'] The database type. ie: mysql. Currently supported:
- mysql, mysqli, postgre, odbc, mssql, sqlite, oci8
+| ['dbdriver'] The database driver. e.g.: mysqli.
+| Currently supported:
+| cubrid, ibase, mssql, mysql, mysqli, oci8,
+| odbc, pdo, postgre, sqlite3, sqlsrv
| ['dbprefix'] You can add an optional prefix, which will be added
-| to the table name when using the Active Record class
+| to the table name when using the Query Builder class
| ['pconnect'] TRUE/FALSE - Whether to use a persistent connection
| ['db_debug'] TRUE/FALSE - Whether database errors should be displayed.
-| ['cache_on'] TRUE/FALSE - Enables/disables query caching
-| ['cachedir'] The path to the folder where cache files should be stored
| ['char_set'] The character set used in communicating with the database
| ['dbcollat'] The character collation used in communicating with the database
| NOTE: For MySQL and MySQLi databases, this setting is only used
@@ -34,44 +37,57 @@
| multi-byte character set and are running versions lower than these.
| Sites using Latin-1 or UTF-8 database character set and collation are unaffected.
| ['swap_pre'] A default table prefix that should be swapped with the dbprefix
-| ['autoinit'] Whether or not to automatically initialize the database.
+| ['encrypt'] Whether or not to use an encrypted connection.
+|
+| 'mysql' (deprecated), 'sqlsrv' and 'pdo/sqlsrv' drivers accept TRUE/FALSE
+| 'mysqli' and 'pdo/mysql' drivers accept an array with the following options:
+|
+| 'ssl_key' - Path to the private key file
+| 'ssl_cert' - Path to the public key certificate file
+| 'ssl_ca' - Path to the certificate authority file
+| 'ssl_capath' - Path to a directory containing trusted CA certificates in PEM format
+| 'ssl_cipher' - List of *allowed* ciphers to be used for the encryption, separated by colons (':')
+| 'ssl_verify' - TRUE/FALSE; Whether verify the server certificate or not
+|
+| ['compress'] Whether or not to use client compression (MySQL only)
| ['stricton'] TRUE/FALSE - forces 'Strict Mode' connections
| - good for ensuring strict SQL while developing
+| ['ssl_options'] Used to set various SSL options that can be used when making SSL connections.
+| ['failover'] array - A array with 0 or more data for connections if the main should fail.
+| ['save_queries'] TRUE/FALSE - Whether to "save" all executed queries.
+| NOTE: Disabling this will also effectively disable both
+| $this->db->last_query() and profiling of DB queries.
+| When you run a query, with this setting set to TRUE (default),
+| CodeIgniter will store the SQL statement for debugging purposes.
+| However, this may cause high memory usage, especially if you run
+| a lot of SQL queries ... disable this to avoid that problem.
|
| The $active_group variable lets you choose which connection group to
| make active. By default there is only one group (the 'default' group).
-|
-| The $active_record variables lets you determine whether or not to load
-| the active record class
*/
-
$active_group = 'default';
-$active_record = TRUE;
-
-$db['default']['hostname'] = "localhost";
-#$db['default']['port'] = 3306;
-$db['default']['username'] = "";
-$db['default']['password'] = "";
-$db['default']['database'] = "";
-$db['default']['dbdriver'] = "mysqli";
-$db['default']['dbprefix'] = "";
-$db['default']['pconnect'] = TRUE;
-$db['default']['db_debug'] = TRUE;
-$db['default']['cache_on'] = FALSE;
-$db['default']['cachedir'] = "";
-$db['default']['char_set'] = "utf8";
-$db['default']['dbcollat'] = "utf8_bin";
-$db['default']['swap_pre'] = '';
-$db['default']['autoinit'] = TRUE;
-$db['default']['stricton'] = FALSE;
+$db['default'] = array(
+ 'dsn' => '',
+ 'hostname' => 'localhost',
+ 'port' => 3306,
+ 'username' => '',
+ 'password' => '',
+ 'database' => '',
+ 'dbdriver' => 'mysqli',
+ 'dbprefix' => '',
+ 'pconnect' => FALSE,
+ 'db_debug' => TRUE,
+ 'char_set' => 'utf8mb4', // if you use postgres, set this to utf8
+ 'dbcollat' => 'utf8mb4_bin', // if you use postgres, set this to utf8_bin
+ 'swap_pre' => '',
+ 'encrypt' => FALSE,
+ 'compress' => FALSE,
+ 'stricton' => TRUE,
+ 'failover' => array(),
+ 'save_queries' => TRUE
+);
if (getenv("ENVIRONMENT") === "testsuite") {
- // Change these to your likeing, just make sure they
- // don't overlap with the normal settings.
$db['default']['database'] = "filebin_testsuite";
$db['default']['dbprefix'] = "testsuite_prefix_";
}
-
-
-/* End of file database.php */
-/* Location: ./application/config/database.php */ \ No newline at end of file
diff --git a/application/config/foreign_chars.php b/application/config/foreign_chars.php
index 14b0d7373..0231f3592 100644
--- a/application/config/foreign_chars.php
+++ b/application/config/foreign_chars.php
@@ -1,4 +1,6 @@
-<?php if ( ! defined('BASEPATH')) exit('No direct script access allowed');
+<?php
+defined('BASEPATH') OR exit('No direct script access allowed');
+
/*
| -------------------------------------------------------------------
| Foreign Characters
@@ -14,51 +16,99 @@ $foreign_characters = array(
'/Ä/' => 'Ae',
'/Ü/' => 'Ue',
'/Ö/' => 'Oe',
- '/À|Á|Â|Ã|Ä|Å|Ǻ|Ā|Ă|Ą|Ǎ/' => 'A',
- '/à|á|â|ã|å|ǻ|ā|ă|ą|ǎ|ª/' => 'a',
+ '/À|Á|Â|Ã|Ä|Å|Ǻ|Ā|Ă|Ą|Ǎ|Α|Ά|Ả|Ạ|Ầ|Ẫ|Ẩ|Ậ|Ằ|Ắ|Ẵ|Ẳ|Ặ|А/' => 'A',
+ '/à|á|â|ã|å|ǻ|ā|ă|ą|ǎ|ª|α|ά|ả|ạ|ầ|ấ|ẫ|ẩ|ậ|ằ|ắ|ẵ|ẳ|ặ|а/' => 'a',
+ '/Б/' => 'B',
+ '/б/' => 'b',
'/Ç|Ć|Ĉ|Ċ|Č/' => 'C',
'/ç|ć|ĉ|ċ|č/' => 'c',
- '/Ð|Ď|Đ/' => 'D',
- '/ð|ď|đ/' => 'd',
- '/È|É|Ê|Ë|Ē|Ĕ|Ė|Ę|Ě/' => 'E',
- '/è|é|ê|ë|ē|ĕ|ė|ę|ě/' => 'e',
- '/Ĝ|Ğ|Ġ|Ģ/' => 'G',
- '/ĝ|ğ|ġ|ģ/' => 'g',
+ '/Д|Δ/' => 'D',
+ '/д|δ/' => 'd',
+ '/Ð|Ď|Đ/' => 'Dj',
+ '/ð|ď|đ/' => 'dj',
+ '/È|É|Ê|Ë|Ē|Ĕ|Ė|Ę|Ě|Ε|Έ|Ẽ|Ẻ|Ẹ|Ề|Ế|Ễ|Ể|Ệ|Е|Э/' => 'E',
+ '/è|é|ê|ë|ē|ĕ|ė|ę|ě|έ|ε|ẽ|ẻ|ẹ|ề|ế|ễ|ể|ệ|е|э/' => 'e',
+ '/Ф/' => 'F',
+ '/ф/' => 'f',
+ '/Ĝ|Ğ|Ġ|Ģ|Γ|Г|Ґ/' => 'G',
+ '/ĝ|ğ|ġ|ģ|γ|г|ґ/' => 'g',
'/Ĥ|Ħ/' => 'H',
'/ĥ|ħ/' => 'h',
- '/Ì|Í|Î|Ï|Ĩ|Ī|Ĭ|Ǐ|Į|İ/' => 'I',
- '/ì|í|î|ï|ĩ|ī|ĭ|ǐ|į|ı/' => 'i',
+ '/Ì|Í|Î|Ï|Ĩ|Ī|Ĭ|Ǐ|Į|İ|Η|Ή|Ί|Ι|Ϊ|Ỉ|Ị|И|Ы/' => 'I',
+ '/ì|í|î|ï|ĩ|ī|ĭ|ǐ|į|ı|η|ή|ί|ι|ϊ|ỉ|ị|и|ы|ї/' => 'i',
'/Ĵ/' => 'J',
'/ĵ/' => 'j',
- '/Ķ/' => 'K',
- '/ķ/' => 'k',
- '/Ĺ|Ļ|Ľ|Ŀ|Ł/' => 'L',
- '/ĺ|ļ|ľ|ŀ|ł/' => 'l',
- '/Ñ|Ń|Ņ|Ň/' => 'N',
- '/ñ|ń|ņ|ň|ʼn/' => 'n',
- '/Ò|Ó|Ô|Õ|Ō|Ŏ|Ǒ|Ő|Ơ|Ø|Ǿ/' => 'O',
- '/ò|ó|ô|õ|ō|ŏ|ǒ|ő|ơ|ø|ǿ|º/' => 'o',
- '/Ŕ|Ŗ|Ř/' => 'R',
- '/ŕ|ŗ|ř/' => 'r',
- '/Ś|Ŝ|Ş|Š/' => 'S',
- '/ś|ŝ|ş|š|ſ/' => 's',
- '/Ţ|Ť|Ŧ/' => 'T',
- '/ţ|ť|ŧ/' => 't',
- '/Ù|Ú|Û|Ũ|Ū|Ŭ|Ů|Ű|Ų|Ư|Ǔ|Ǖ|Ǘ|Ǚ|Ǜ/' => 'U',
- '/ù|ú|û|ũ|ū|ŭ|ů|ű|ų|ư|ǔ|ǖ|ǘ|ǚ|ǜ/' => 'u',
- '/Ý|Ÿ|Ŷ/' => 'Y',
- '/ý|ÿ|ŷ/' => 'y',
+ '/Θ/' => 'TH',
+ '/θ/' => 'th',
+ '/Ķ|Κ|К/' => 'K',
+ '/ķ|κ|к/' => 'k',
+ '/Ĺ|Ļ|Ľ|Ŀ|Ł|Λ|Л/' => 'L',
+ '/ĺ|ļ|ľ|ŀ|ł|λ|л/' => 'l',
+ '/М/' => 'M',
+ '/м/' => 'm',
+ '/Ñ|Ń|Ņ|Ň|Ν|Н/' => 'N',
+ '/ñ|ń|ņ|ň|ʼn|ν|н/' => 'n',
+ '/Ò|Ó|Ô|Õ|Ō|Ŏ|Ǒ|Ő|Ơ|Ø|Ǿ|Ο|Ό|Ω|Ώ|Ỏ|Ọ|Ồ|Ố|Ỗ|Ổ|Ộ|Ờ|Ớ|Ỡ|Ở|Ợ|О/' => 'O',
+ '/ò|ó|ô|õ|ō|ŏ|ǒ|ő|ơ|ø|ǿ|º|ο|ό|ω|ώ|ỏ|ọ|ồ|ố|ỗ|ổ|ộ|ờ|ớ|ỡ|ở|ợ|о/' => 'o',
+ '/П/' => 'P',
+ '/п/' => 'p',
+ '/Ŕ|Ŗ|Ř|Ρ|Р/' => 'R',
+ '/ŕ|ŗ|ř|ρ|р/' => 'r',
+ '/Ś|Ŝ|Ş|Ș|Š|Σ|С/' => 'S',
+ '/ś|ŝ|ş|ș|š|ſ|σ|ς|с/' => 's',
+ '/Ț|Ţ|Ť|Ŧ|Τ|Т/' => 'T',
+ '/ț|ţ|ť|ŧ|τ|т/' => 't',
+ '/Þ|þ/' => 'th',
+ '/Ù|Ú|Û|Ũ|Ū|Ŭ|Ů|Ű|Ų|Ư|Ǔ|Ǖ|Ǘ|Ǚ|Ǜ|Ũ|Ủ|Ụ|Ừ|Ứ|Ữ|Ử|Ự|У/' => 'U',
+ '/ù|ú|û|ũ|ū|ŭ|ů|ű|ų|ư|ǔ|ǖ|ǘ|ǚ|ǜ|υ|ύ|ϋ|ủ|ụ|ừ|ứ|ữ|ử|ự|у/' => 'u',
+ '/Ƴ|Ɏ|Ỵ|Ẏ|Ӳ|Ӯ|Ў|Ý|Ÿ|Ŷ|Υ|Ύ|Ϋ|Ỳ|Ỹ|Ỷ|Ỵ|Й/' => 'Y',
+ '/ẙ|ʏ|ƴ|ɏ|ỵ|ẏ|ӳ|ӯ|ў|ý|ÿ|ŷ|ỳ|ỹ|ỷ|ỵ|й/' => 'y',
+ '/В/' => 'V',
+ '/в/' => 'v',
'/Ŵ/' => 'W',
'/ŵ/' => 'w',
- '/Ź|Ż|Ž/' => 'Z',
- '/ź|ż|ž/' => 'z',
+ '/Φ/' => 'F',
+ '/φ/' => 'f',
+ '/Χ/' => 'CH',
+ '/χ/' => 'ch',
+ '/Ź|Ż|Ž|Ζ|З/' => 'Z',
+ '/ź|ż|ž|ζ|з/' => 'z',
'/Æ|Ǽ/' => 'AE',
- '/ß/'=> 'ss',
+ '/ß/' => 'ss',
'/IJ/' => 'IJ',
'/ij/' => 'ij',
'/Œ/' => 'OE',
- '/ƒ/' => 'f'
+ '/ƒ/' => 'f',
+ '/Ξ/' => 'KS',
+ '/ξ/' => 'ks',
+ '/Π/' => 'P',
+ '/π/' => 'p',
+ '/Β/' => 'V',
+ '/β/' => 'v',
+ '/Μ/' => 'M',
+ '/μ/' => 'm',
+ '/Ψ/' => 'PS',
+ '/ψ/' => 'ps',
+ '/Ё/' => 'Yo',
+ '/ё/' => 'yo',
+ '/Є/' => 'Ye',
+ '/є/' => 'ye',
+ '/Ї/' => 'Yi',
+ '/Ж/' => 'Zh',
+ '/ж/' => 'zh',
+ '/Х/' => 'Kh',
+ '/х/' => 'kh',
+ '/Ц/' => 'Ts',
+ '/ц/' => 'ts',
+ '/Ч/' => 'Ch',
+ '/ч/' => 'ch',
+ '/Ш/' => 'Sh',
+ '/ш/' => 'sh',
+ '/Щ/' => 'Shch',
+ '/щ/' => 'shch',
+ '/Ъ|ъ|Ь|ь/' => '',
+ '/Ю/' => 'Yu',
+ '/ю/' => 'yu',
+ '/Я/' => 'Ya',
+ '/я/' => 'ya'
);
-
-/* End of file foreign_chars.php */
-/* Location: ./application/config/foreign_chars.php */ \ No newline at end of file
diff --git a/application/config/hooks.php b/application/config/hooks.php
index a4ad2be6d..79c5c162f 100644
--- a/application/config/hooks.php
+++ b/application/config/hooks.php
@@ -1,4 +1,6 @@
-<?php if ( ! defined('BASEPATH')) exit('No direct script access allowed');
+<?php
+defined('BASEPATH') OR exit('No direct script access allowed');
+
/*
| -------------------------------------------------------------------------
| Hooks
@@ -6,11 +8,6 @@
| This file lets you define "hooks" to extend CI without hacking the core
| files. Please see the user guide for info:
|
-| http://codeigniter.com/user_guide/general/hooks.html
+| https://codeigniter.com/userguide3/general/hooks.html
|
*/
-
-
-
-/* End of file hooks.php */
-/* Location: ./application/config/hooks.php */ \ No newline at end of file
diff --git a/application/config/index.html b/application/config/index.html
index c942a79ce..bcb7cae34 100644
--- a/application/config/index.html
+++ b/application/config/index.html
@@ -1,4 +1,5 @@
-<html>
+<!DOCTYPE html>
+<html lang="en">
<head>
<title>403 Forbidden</title>
</head>
@@ -7,4 +8,4 @@
<p>Directory access is forbidden.</p>
</body>
-</html> \ No newline at end of file
+</html>
diff --git a/application/config/memcached.php b/application/config/memcached.php
new file mode 100644
index 000000000..65a149617
--- /dev/null
+++ b/application/config/memcached.php
@@ -0,0 +1,19 @@
+<?php
+defined('BASEPATH') OR exit('No direct script access allowed');
+
+/*
+| -------------------------------------------------------------------------
+| Memcached settings
+| -------------------------------------------------------------------------
+| Your Memcached servers can be specified below.
+|
+| See: https://codeigniter.com/userguide3/libraries/caching.html#memcached
+|
+*/
+$config = array(
+ 'default' => array(
+ 'hostname' => '127.0.0.1',
+ 'port' => '11211',
+ 'weight' => '1',
+ ),
+);
diff --git a/application/config/migration.php b/application/config/migration.php
index 659907cb8..ffddae2ac 100644
--- a/application/config/migration.php
+++ b/application/config/migration.php
@@ -1,15 +1,63 @@
-<?php defined('BASEPATH') OR exit('No direct script access allowed');
+<?php
+defined('BASEPATH') OR exit('No direct script access allowed');
+
/*
|--------------------------------------------------------------------------
| Enable/Disable Migrations
|--------------------------------------------------------------------------
|
-| Migrations are disabled by default but should be enabled
-| whenever you intend to do a schema migration.
+| Migrations are disabled by default for security reasons.
+| You should enable migrations whenever you intend to do a schema migration
+| and disable it back when you're done.
|
*/
$config['migration_enabled'] = true;
+/*
+|--------------------------------------------------------------------------
+| Migration Type
+|--------------------------------------------------------------------------
+|
+| Migration file names may be based on a sequential identifier or on
+| a timestamp. Options are:
+|
+| 'sequential' = Sequential migration naming (001_add_blog.php)
+| 'timestamp' = Timestamp migration naming (20121031104401_add_blog.php)
+| Use timestamp format YYYYMMDDHHIISS.
+|
+| Note: If this configuration value is missing the Migration library
+| defaults to 'sequential' for backward compatibility with CI2.
+|
+*/
+$config['migration_type'] = 'sequential';
+
+/*
+|--------------------------------------------------------------------------
+| Migrations table
+|--------------------------------------------------------------------------
+|
+| This is the name of the table that will store the current migrations state.
+| When migrations runs it will store in a database table which migration
+| level the system is at. It then compares the migration level in this
+| table to the $config['migration_version'] if they are not the same it
+| will migrate up. This must be set.
+|
+*/
+$config['migration_table'] = 'migrations';
+
+/*
+|--------------------------------------------------------------------------
+| Auto Migrate To Latest
+|--------------------------------------------------------------------------
+|
+| If this is set to TRUE when you load the migrations class and have
+| $config['migration_enabled'] set to TRUE the system will auto migrate
+| to your latest migration (whatever $config['migration_version'] is
+| set to). This way you do not have to call migrations anywhere else
+| in your code to have the latest migration.
+|
+*/
+$config['migration_auto_latest'] = FALSE;
/*
|--------------------------------------------------------------------------
@@ -17,12 +65,11 @@ $config['migration_enabled'] = true;
|--------------------------------------------------------------------------
|
| This is used to set migration version that the file system should be on.
-| If you run $this->migration->latest() this is the version that schema will
+| If you run $this->migration->current() this is the version that schema will
| be upgraded / downgraded to.
|
*/
-$config['migration_version'] = 18;
-
+$config['migration_version'] = 21;
/*
|--------------------------------------------------------------------------
@@ -34,8 +81,5 @@ $config['migration_version'] = 18;
| Also, writing permission is required within the migrations path.
|
*/
-$config['migration_path'] = APPPATH . 'migrations/';
-
+$config['migration_path'] = APPPATH.'migrations/';
-/* End of file migration.php */
-/* Location: ./application/config/migration.php */
diff --git a/application/config/mimes.php b/application/config/mimes.php
index 100f7d442..b2e989fea 100644
--- a/application/config/mimes.php
+++ b/application/config/mimes.php
@@ -1,106 +1,186 @@
-<?php if ( ! defined('BASEPATH')) exit('No direct script access allowed');
+<?php
+defined('BASEPATH') OR exit('No direct script access allowed');
+
/*
| -------------------------------------------------------------------
| MIME TYPES
| -------------------------------------------------------------------
-| This file contains an array of mime types. It is used by the
+| This file contains an array of mime types. It is used by the
| Upload class to help identify allowed file types.
|
*/
-
-$mimes = array( 'hqx' => 'application/mac-binhex40',
- 'cpt' => 'application/mac-compactpro',
- 'csv' => array('text/x-comma-separated-values', 'text/comma-separated-values', 'application/octet-stream', 'application/vnd.ms-excel', 'application/x-csv', 'text/x-csv', 'text/csv', 'application/csv', 'application/excel', 'application/vnd.msexcel'),
- 'bin' => 'application/macbinary',
- 'dms' => 'application/octet-stream',
- 'lha' => 'application/octet-stream',
- 'lzh' => 'application/octet-stream',
- 'exe' => array('application/octet-stream', 'application/x-msdownload'),
- 'class' => 'application/octet-stream',
- 'psd' => 'application/x-photoshop',
- 'so' => 'application/octet-stream',
- 'sea' => 'application/octet-stream',
- 'dll' => 'application/octet-stream',
- 'oda' => 'application/oda',
- 'pdf' => array('application/pdf', 'application/x-download'),
- 'ai' => 'application/postscript',
- 'eps' => 'application/postscript',
- 'ps' => 'application/postscript',
- 'smi' => 'application/smil',
- 'smil' => 'application/smil',
- 'mif' => 'application/vnd.mif',
- 'xls' => array('application/excel', 'application/vnd.ms-excel', 'application/msexcel'),
- 'ppt' => array('application/powerpoint', 'application/vnd.ms-powerpoint'),
- 'wbxml' => 'application/wbxml',
- 'wmlc' => 'application/wmlc',
- 'dcr' => 'application/x-director',
- 'dir' => 'application/x-director',
- 'dxr' => 'application/x-director',
- 'dvi' => 'application/x-dvi',
- 'gtar' => 'application/x-gtar',
- 'gz' => 'application/x-gzip',
- 'php' => 'application/x-httpd-php',
- 'php4' => 'application/x-httpd-php',
- 'php3' => 'application/x-httpd-php',
- 'phtml' => 'application/x-httpd-php',
- 'phps' => 'application/x-httpd-php-source',
- 'js' => 'application/x-javascript',
- 'swf' => 'application/x-shockwave-flash',
- 'sit' => 'application/x-stuffit',
- 'tar' => 'application/x-tar',
- 'tgz' => array('application/x-tar', 'application/x-gzip-compressed'),
- 'xhtml' => 'application/xhtml+xml',
- 'xht' => 'application/xhtml+xml',
- 'zip' => array('application/x-zip', 'application/zip', 'application/x-zip-compressed'),
- 'mid' => 'audio/midi',
- 'midi' => 'audio/midi',
- 'mpga' => 'audio/mpeg',
- 'mp2' => 'audio/mpeg',
- 'mp3' => array('audio/mpeg', 'audio/mpg', 'audio/mpeg3', 'audio/mp3'),
- 'aif' => 'audio/x-aiff',
- 'aiff' => 'audio/x-aiff',
- 'aifc' => 'audio/x-aiff',
- 'ram' => 'audio/x-pn-realaudio',
- 'rm' => 'audio/x-pn-realaudio',
- 'rpm' => 'audio/x-pn-realaudio-plugin',
- 'ra' => 'audio/x-realaudio',
- 'rv' => 'video/vnd.rn-realvideo',
- 'wav' => array('audio/x-wav', 'audio/wave', 'audio/wav'),
- 'bmp' => array('image/bmp', 'image/x-windows-bmp'),
- 'gif' => 'image/gif',
- 'jpeg' => array('image/jpeg', 'image/pjpeg'),
- 'jpg' => array('image/jpeg', 'image/pjpeg'),
- 'jpe' => array('image/jpeg', 'image/pjpeg'),
- 'png' => array('image/png', 'image/x-png'),
- 'tiff' => 'image/tiff',
- 'tif' => 'image/tiff',
- 'css' => 'text/css',
- 'html' => 'text/html',
- 'htm' => 'text/html',
- 'shtml' => 'text/html',
- 'txt' => 'text/plain',
- 'text' => 'text/plain',
- 'log' => array('text/plain', 'text/x-log'),
- 'rtx' => 'text/richtext',
- 'rtf' => 'text/rtf',
- 'xml' => 'text/xml',
- 'xsl' => 'text/xml',
- 'mpeg' => 'video/mpeg',
- 'mpg' => 'video/mpeg',
- 'mpe' => 'video/mpeg',
- 'qt' => 'video/quicktime',
- 'mov' => 'video/quicktime',
- 'avi' => 'video/x-msvideo',
- 'movie' => 'video/x-sgi-movie',
- 'doc' => 'application/msword',
- 'docx' => array('application/vnd.openxmlformats-officedocument.wordprocessingml.document', 'application/zip'),
- 'xlsx' => array('application/vnd.openxmlformats-officedocument.spreadsheetml.sheet', 'application/zip'),
- 'word' => array('application/msword', 'application/octet-stream'),
- 'xl' => 'application/excel',
- 'eml' => 'message/rfc822',
- 'json' => array('application/json', 'text/json')
- );
-
-
-/* End of file mimes.php */
-/* Location: ./application/config/mimes.php */
+return array(
+ 'hqx' => array('application/mac-binhex40', 'application/mac-binhex', 'application/x-binhex40', 'application/x-mac-binhex40'),
+ 'cpt' => 'application/mac-compactpro',
+ 'csv' => array('text/x-comma-separated-values', 'text/comma-separated-values', 'application/octet-stream', 'application/vnd.ms-excel', 'application/x-csv', 'text/x-csv', 'text/csv', 'application/csv', 'application/excel', 'application/vnd.msexcel', 'text/plain'),
+ 'bin' => array('application/macbinary', 'application/mac-binary', 'application/octet-stream', 'application/x-binary', 'application/x-macbinary'),
+ 'dms' => 'application/octet-stream',
+ 'lha' => 'application/octet-stream',
+ 'lzh' => 'application/octet-stream',
+ 'exe' => array('application/octet-stream', 'application/x-msdownload'),
+ 'class' => 'application/octet-stream',
+ 'psd' => array('application/x-photoshop', 'image/vnd.adobe.photoshop'),
+ 'so' => 'application/octet-stream',
+ 'sea' => 'application/octet-stream',
+ 'dll' => 'application/octet-stream',
+ 'oda' => 'application/oda',
+ 'pdf' => array('application/pdf', 'application/force-download', 'application/x-download', 'binary/octet-stream'),
+ 'ai' => array('application/pdf', 'application/postscript'),
+ 'eps' => 'application/postscript',
+ 'ps' => 'application/postscript',
+ 'smi' => 'application/smil',
+ 'smil' => 'application/smil',
+ 'mif' => 'application/vnd.mif',
+ 'xls' => array('application/vnd.ms-excel', 'application/msexcel', 'application/x-msexcel', 'application/x-ms-excel', 'application/x-excel', 'application/x-dos_ms_excel', 'application/xls', 'application/x-xls', 'application/excel', 'application/download', 'application/vnd.ms-office', 'application/msword'),
+ 'ppt' => array('application/powerpoint', 'application/vnd.ms-powerpoint', 'application/vnd.ms-office', 'application/msword'),
+ 'pptx' => array('application/vnd.openxmlformats-officedocument.presentationml.presentation', 'application/x-zip', 'application/zip'),
+ 'wbxml' => 'application/wbxml',
+ 'wmlc' => 'application/wmlc',
+ 'dcr' => 'application/x-director',
+ 'dir' => 'application/x-director',
+ 'dxr' => 'application/x-director',
+ 'dvi' => 'application/x-dvi',
+ 'gtar' => 'application/x-gtar',
+ 'gz' => 'application/x-gzip',
+ 'gzip' => 'application/x-gzip',
+ 'php' => array('application/x-httpd-php', 'application/php', 'application/x-php', 'text/php', 'text/x-php', 'application/x-httpd-php-source'),
+ 'php4' => 'application/x-httpd-php',
+ 'php3' => 'application/x-httpd-php',
+ 'phtml' => 'application/x-httpd-php',
+ 'phps' => 'application/x-httpd-php-source',
+ 'js' => array('application/x-javascript', 'text/plain'),
+ 'swf' => 'application/x-shockwave-flash',
+ 'sit' => 'application/x-stuffit',
+ 'tar' => 'application/x-tar',
+ 'tgz' => array('application/x-tar', 'application/x-gzip-compressed'),
+ 'z' => 'application/x-compress',
+ 'xhtml' => 'application/xhtml+xml',
+ 'xht' => 'application/xhtml+xml',
+ 'zip' => array('application/x-zip', 'application/zip', 'application/x-zip-compressed', 'application/s-compressed', 'multipart/x-zip'),
+ 'rar' => array('application/x-rar', 'application/rar', 'application/x-rar-compressed'),
+ 'mid' => 'audio/midi',
+ 'midi' => 'audio/midi',
+ 'mpga' => 'audio/mpeg',
+ 'mp2' => 'audio/mpeg',
+ 'mp3' => array('audio/mpeg', 'audio/mpg', 'audio/mpeg3', 'audio/mp3'),
+ 'aif' => array('audio/x-aiff', 'audio/aiff'),
+ 'aiff' => array('audio/x-aiff', 'audio/aiff'),
+ 'aifc' => 'audio/x-aiff',
+ 'ram' => 'audio/x-pn-realaudio',
+ 'rm' => 'audio/x-pn-realaudio',
+ 'rpm' => 'audio/x-pn-realaudio-plugin',
+ 'ra' => 'audio/x-realaudio',
+ 'rv' => 'video/vnd.rn-realvideo',
+ 'wav' => array('audio/x-wav', 'audio/wave', 'audio/wav'),
+ 'bmp' => array('image/bmp', 'image/x-bmp', 'image/x-bitmap', 'image/x-xbitmap', 'image/x-win-bitmap', 'image/x-windows-bmp', 'image/ms-bmp', 'image/x-ms-bmp', 'application/bmp', 'application/x-bmp', 'application/x-win-bitmap'),
+ 'gif' => 'image/gif',
+ 'jpeg' => array('image/jpeg', 'image/pjpeg'),
+ 'jpg' => array('image/jpeg', 'image/pjpeg'),
+ 'jpe' => array('image/jpeg', 'image/pjpeg'),
+ 'jp2' => array('image/jp2', 'video/mj2', 'image/jpx', 'image/jpm'),
+ 'j2k' => array('image/jp2', 'video/mj2', 'image/jpx', 'image/jpm'),
+ 'jpf' => array('image/jp2', 'video/mj2', 'image/jpx', 'image/jpm'),
+ 'jpg2' => array('image/jp2', 'video/mj2', 'image/jpx', 'image/jpm'),
+ 'jpx' => array('image/jp2', 'video/mj2', 'image/jpx', 'image/jpm'),
+ 'jpm' => array('image/jp2', 'video/mj2', 'image/jpx', 'image/jpm'),
+ 'mj2' => array('image/jp2', 'video/mj2', 'image/jpx', 'image/jpm'),
+ 'mjp2' => array('image/jp2', 'video/mj2', 'image/jpx', 'image/jpm'),
+ 'png' => array('image/png', 'image/x-png'),
+ 'tiff' => 'image/tiff',
+ 'tif' => 'image/tiff',
+ 'heic' => 'image/heic',
+ 'heif' => 'image/heif',
+ 'css' => array('text/css', 'text/plain'),
+ 'html' => array('text/html', 'text/plain'),
+ 'htm' => array('text/html', 'text/plain'),
+ 'shtml' => array('text/html', 'text/plain'),
+ 'txt' => 'text/plain',
+ 'text' => 'text/plain',
+ 'log' => array('text/plain', 'text/x-log'),
+ 'rtx' => 'text/richtext',
+ 'rtf' => 'text/rtf',
+ 'xml' => array('application/xml', 'text/xml', 'text/plain'),
+ 'xsl' => array('application/xml', 'text/xsl', 'text/xml'),
+ 'mpeg' => 'video/mpeg',
+ 'mpg' => 'video/mpeg',
+ 'mpe' => 'video/mpeg',
+ 'qt' => 'video/quicktime',
+ 'mov' => 'video/quicktime',
+ 'avi' => array('video/x-msvideo', 'video/msvideo', 'video/avi', 'application/x-troff-msvideo'),
+ 'movie' => 'video/x-sgi-movie',
+ 'doc' => array('application/msword', 'application/vnd.ms-office'),
+ 'docx' => array('application/vnd.openxmlformats-officedocument.wordprocessingml.document', 'application/zip', 'application/msword', 'application/x-zip'),
+ 'dot' => array('application/msword', 'application/vnd.ms-office'),
+ 'dotx' => array('application/vnd.openxmlformats-officedocument.wordprocessingml.document', 'application/zip', 'application/msword'),
+ 'xlsx' => array('application/vnd.openxmlformats-officedocument.spreadsheetml.sheet', 'application/zip', 'application/vnd.ms-excel', 'application/msword', 'application/x-zip'),
+ 'word' => array('application/msword', 'application/octet-stream'),
+ 'xl' => 'application/excel',
+ 'eml' => 'message/rfc822',
+ 'json' => array('application/json', 'text/json'),
+ 'pem' => array('application/x-x509-user-cert', 'application/x-pem-file', 'application/octet-stream'),
+ 'p10' => array('application/x-pkcs10', 'application/pkcs10'),
+ 'p12' => 'application/x-pkcs12',
+ 'p7a' => 'application/x-pkcs7-signature',
+ 'p7c' => array('application/pkcs7-mime', 'application/x-pkcs7-mime'),
+ 'p7m' => array('application/pkcs7-mime', 'application/x-pkcs7-mime'),
+ 'p7r' => 'application/x-pkcs7-certreqresp',
+ 'p7s' => 'application/pkcs7-signature',
+ 'crt' => array('application/x-x509-ca-cert', 'application/x-x509-user-cert', 'application/pkix-cert'),
+ 'crl' => array('application/pkix-crl', 'application/pkcs-crl'),
+ 'der' => 'application/x-x509-ca-cert',
+ 'kdb' => 'application/octet-stream',
+ 'pgp' => 'application/pgp',
+ 'gpg' => 'application/gpg-keys',
+ 'sst' => 'application/octet-stream',
+ 'csr' => 'application/octet-stream',
+ 'rsa' => 'application/x-pkcs7',
+ 'cer' => array('application/pkix-cert', 'application/x-x509-ca-cert'),
+ '3g2' => 'video/3gpp2',
+ '3gp' => array('video/3gp', 'video/3gpp'),
+ 'mp4' => 'video/mp4',
+ 'm4a' => 'audio/x-m4a',
+ 'f4v' => array('video/mp4', 'video/x-f4v'),
+ 'flv' => 'video/x-flv',
+ 'webm' => 'video/webm',
+ 'aac' => array('audio/x-aac', 'audio/aac'),
+ 'm4u' => 'application/vnd.mpegurl',
+ 'm3u' => 'text/plain',
+ 'xspf' => 'application/xspf+xml',
+ 'vlc' => 'application/videolan',
+ 'wmv' => array('video/x-ms-wmv', 'video/x-ms-asf'),
+ 'au' => 'audio/x-au',
+ 'ac3' => 'audio/ac3',
+ 'flac' => 'audio/x-flac',
+ 'ogg' => array('audio/ogg', 'video/ogg', 'application/ogg'),
+ 'kmz' => array('application/vnd.google-earth.kmz', 'application/zip', 'application/x-zip'),
+ 'kml' => array('application/vnd.google-earth.kml+xml', 'application/xml', 'text/xml'),
+ 'ics' => 'text/calendar',
+ 'ical' => 'text/calendar',
+ 'zsh' => 'text/x-scriptzsh',
+ '7z' => array('application/x-7z-compressed', 'application/x-compressed', 'application/x-zip-compressed', 'application/zip', 'multipart/x-zip'),
+ '7zip' => array('application/x-7z-compressed', 'application/x-compressed', 'application/x-zip-compressed', 'application/zip', 'multipart/x-zip'),
+ 'cdr' => array('application/cdr', 'application/coreldraw', 'application/x-cdr', 'application/x-coreldraw', 'image/cdr', 'image/x-cdr', 'zz-application/zz-winassoc-cdr'),
+ 'wma' => array('audio/x-ms-wma', 'video/x-ms-asf'),
+ 'jar' => array('application/java-archive', 'application/x-java-application', 'application/x-jar', 'application/x-compressed'),
+ 'svg' => array('image/svg+xml', 'image/svg', 'application/xml', 'text/xml'),
+ 'vcf' => 'text/x-vcard',
+ 'srt' => array('text/srt', 'text/plain'),
+ 'vtt' => array('text/vtt', 'text/plain'),
+ 'ico' => array('image/x-icon', 'image/x-ico', 'image/vnd.microsoft.icon'),
+ 'odc' => 'application/vnd.oasis.opendocument.chart',
+ 'otc' => 'application/vnd.oasis.opendocument.chart-template',
+ 'odf' => 'application/vnd.oasis.opendocument.formula',
+ 'otf' => 'application/vnd.oasis.opendocument.formula-template',
+ 'odg' => 'application/vnd.oasis.opendocument.graphics',
+ 'otg' => 'application/vnd.oasis.opendocument.graphics-template',
+ 'odi' => 'application/vnd.oasis.opendocument.image',
+ 'oti' => 'application/vnd.oasis.opendocument.image-template',
+ 'odp' => 'application/vnd.oasis.opendocument.presentation',
+ 'otp' => 'application/vnd.oasis.opendocument.presentation-template',
+ 'ods' => 'application/vnd.oasis.opendocument.spreadsheet',
+ 'ots' => 'application/vnd.oasis.opendocument.spreadsheet-template',
+ 'odt' => 'application/vnd.oasis.opendocument.text',
+ 'odm' => 'application/vnd.oasis.opendocument.text-master',
+ 'ott' => 'application/vnd.oasis.opendocument.text-template',
+ 'oth' => 'application/vnd.oasis.opendocument.text-web'
+);
diff --git a/application/config/profiler.php b/application/config/profiler.php
index f8a5b1a1e..3436e931e 100644
--- a/application/config/profiler.php
+++ b/application/config/profiler.php
@@ -1,4 +1,6 @@
-<?php if ( ! defined('BASEPATH')) exit('No direct script access allowed');
+<?php
+defined('BASEPATH') OR exit('No direct script access allowed');
+
/*
| -------------------------------------------------------------------------
| Profiler Sections
@@ -7,11 +9,6 @@
| data are displayed when the Profiler is enabled.
| Please see the user guide for info:
|
-| http://codeigniter.com/user_guide/general/profiling.html
+| https://codeigniter.com/userguide3/general/profiling.html
|
*/
-
-
-
-/* End of file profiler.php */
-/* Location: ./application/config/profiler.php */ \ No newline at end of file
diff --git a/application/config/routes.php b/application/config/routes.php
index f44f283f0..a5dae1efb 100644
--- a/application/config/routes.php
+++ b/application/config/routes.php
@@ -1,4 +1,6 @@
-<?php if ( ! defined('BASEPATH')) exit('No direct script access allowed');
+<?php
+defined('BASEPATH') OR exit('No direct script access allowed');
+
/*
| -------------------------------------------------------------------------
| URI ROUTING
@@ -17,13 +19,13 @@
|
| Please see the user guide for complete details:
|
-| http://codeigniter.com/user_guide/general/routing.html
+| https://codeigniter.com/userguide3/general/routing.html
|
| -------------------------------------------------------------------------
| RESERVED ROUTES
| -------------------------------------------------------------------------
|
-| There area two reserved routes:
+| There are three reserved routes:
|
| $route['default_controller'] = 'welcome';
|
@@ -33,20 +35,26 @@
|
| $route['404_override'] = 'errors/page_missing';
|
-| This route will tell the Router what URI segments to use if those provided
-| in the URL cannot be matched to a valid route.
+| This route will tell the Router which controller/method to use if those
+| provided in the URL cannot be matched to a valid route.
+|
+| $route['translate_uri_dashes'] = FALSE;
+|
+| This is not exactly a route, but allows you to automatically route
+| controller and method names that contain dashes. '-' isn't a valid
+| class or method name character, so it requires translation.
+| When you set this option to TRUE, it will replace ALL dashes with
+| underscores in the controller and method URI segments.
|
+| Examples: my-controller/index -> my_controller/index
+| my-controller/my-method -> my_controller/my_method
*/
-
-$route['default_controller'] = "file/file_default";
-$route['user/(:any)'] = "user/$1";
-$route['file/multipaste/(:any)'] = "file/multipaste/$1";
-$route['file/(:any)'] = "file/file_default/$1";
-$route['tools/(:any)'] = "tools/$1";
-$route['api/(:any)'] = "api/route/$1";
-$route['(:any)'] = "file/file_default/index/$1";
+$route['default_controller'] = "main";
+$route['user/(.+)'] = "user/$1";
+$route['file/multipaste/(.+)'] = "file/multipaste/$1";
+$route['file/(.+)'] = "main/$1";
+$route['tools/(.+)'] = "tools/$1";
+$route['api/(.+)'] = "api/route/$1";
+$route['(.+)'] = "main/index/$1";
$route['404_override'] = '';
-
-
-/* End of file routes.php */
-/* Location: ./application/config/routes.php */ \ No newline at end of file
+$route['translate_uri_dashes'] = FALSE;
diff --git a/application/config/smileys.php b/application/config/smileys.php
deleted file mode 100644
index 25d28b2c4..000000000
--- a/application/config/smileys.php
+++ /dev/null
@@ -1,66 +0,0 @@
-<?php if ( ! defined('BASEPATH')) exit('No direct script access allowed');
-/*
-| -------------------------------------------------------------------
-| SMILEYS
-| -------------------------------------------------------------------
-| This file contains an array of smileys for use with the emoticon helper.
-| Individual images can be used to replace multiple simileys. For example:
-| :-) and :) use the same image replacement.
-|
-| Please see user guide for more info:
-| http://codeigniter.com/user_guide/helpers/smiley_helper.html
-|
-*/
-
-$smileys = array(
-
-// smiley image name width height alt
-
- ':-)' => array('grin.gif', '19', '19', 'grin'),
- ':lol:' => array('lol.gif', '19', '19', 'LOL'),
- ':cheese:' => array('cheese.gif', '19', '19', 'cheese'),
- ':)' => array('smile.gif', '19', '19', 'smile'),
- ';-)' => array('wink.gif', '19', '19', 'wink'),
- ';)' => array('wink.gif', '19', '19', 'wink'),
- ':smirk:' => array('smirk.gif', '19', '19', 'smirk'),
- ':roll:' => array('rolleyes.gif', '19', '19', 'rolleyes'),
- ':-S' => array('confused.gif', '19', '19', 'confused'),
- ':wow:' => array('surprise.gif', '19', '19', 'surprised'),
- ':bug:' => array('bigsurprise.gif', '19', '19', 'big surprise'),
- ':-P' => array('tongue_laugh.gif', '19', '19', 'tongue laugh'),
- '%-P' => array('tongue_rolleye.gif', '19', '19', 'tongue rolleye'),
- ';-P' => array('tongue_wink.gif', '19', '19', 'tongue wink'),
- ':P' => array('raspberry.gif', '19', '19', 'raspberry'),
- ':blank:' => array('blank.gif', '19', '19', 'blank stare'),
- ':long:' => array('longface.gif', '19', '19', 'long face'),
- ':ohh:' => array('ohh.gif', '19', '19', 'ohh'),
- ':grrr:' => array('grrr.gif', '19', '19', 'grrr'),
- ':gulp:' => array('gulp.gif', '19', '19', 'gulp'),
- '8-/' => array('ohoh.gif', '19', '19', 'oh oh'),
- ':down:' => array('downer.gif', '19', '19', 'downer'),
- ':red:' => array('embarrassed.gif', '19', '19', 'red face'),
- ':sick:' => array('sick.gif', '19', '19', 'sick'),
- ':shut:' => array('shuteye.gif', '19', '19', 'shut eye'),
- ':-/' => array('hmm.gif', '19', '19', 'hmmm'),
- '>:(' => array('mad.gif', '19', '19', 'mad'),
- ':mad:' => array('mad.gif', '19', '19', 'mad'),
- '>:-(' => array('angry.gif', '19', '19', 'angry'),
- ':angry:' => array('angry.gif', '19', '19', 'angry'),
- ':zip:' => array('zip.gif', '19', '19', 'zipper'),
- ':kiss:' => array('kiss.gif', '19', '19', 'kiss'),
- ':ahhh:' => array('shock.gif', '19', '19', 'shock'),
- ':coolsmile:' => array('shade_smile.gif', '19', '19', 'cool smile'),
- ':coolsmirk:' => array('shade_smirk.gif', '19', '19', 'cool smirk'),
- ':coolgrin:' => array('shade_grin.gif', '19', '19', 'cool grin'),
- ':coolhmm:' => array('shade_hmm.gif', '19', '19', 'cool hmm'),
- ':coolmad:' => array('shade_mad.gif', '19', '19', 'cool mad'),
- ':coolcheese:' => array('shade_cheese.gif', '19', '19', 'cool cheese'),
- ':vampire:' => array('vampire.gif', '19', '19', 'vampire'),
- ':snake:' => array('snake.gif', '19', '19', 'snake'),
- ':exclaim:' => array('exclaim.gif', '19', '19', 'excaim'),
- ':question:' => array('question.gif', '19', '19', 'question') // no comma after last item
-
- );
-
-/* End of file smileys.php */
-/* Location: ./application/config/smileys.php */ \ No newline at end of file
diff --git a/application/config/user_agents.php b/application/config/user_agents.php
index e2d3c3af0..21251f46f 100644
--- a/application/config/user_agents.php
+++ b/application/config/user_agents.php
@@ -1,178 +1,223 @@
-<?php if ( ! defined('BASEPATH')) exit('No direct script access allowed');
+<?php
+defined('BASEPATH') OR exit('No direct script access allowed');
+
/*
| -------------------------------------------------------------------
| USER AGENT TYPES
| -------------------------------------------------------------------
-| This file contains four arrays of user agent data. It is used by the
+| This file contains four arrays of user agent data. It is used by the
| User Agent Class to help identify browser, platform, robot, and
-| mobile device data. The array keys are used to identify the device
+| mobile device data. The array keys are used to identify the device
| and the array values are used to set the actual name of the item.
-|
*/
-
-$platforms = array (
- 'windows nt 6.0' => 'Windows Longhorn',
- 'windows nt 5.2' => 'Windows 2003',
- 'windows nt 5.0' => 'Windows 2000',
- 'windows nt 5.1' => 'Windows XP',
- 'windows nt 4.0' => 'Windows NT 4.0',
- 'winnt4.0' => 'Windows NT 4.0',
- 'winnt 4.0' => 'Windows NT',
- 'winnt' => 'Windows NT',
- 'windows 98' => 'Windows 98',
- 'win98' => 'Windows 98',
- 'windows 95' => 'Windows 95',
- 'win95' => 'Windows 95',
- 'windows' => 'Unknown Windows OS',
- 'os x' => 'Mac OS X',
- 'ppc mac' => 'Power PC Mac',
- 'freebsd' => 'FreeBSD',
- 'ppc' => 'Macintosh',
- 'linux' => 'Linux',
- 'debian' => 'Debian',
- 'sunos' => 'Sun Solaris',
- 'beos' => 'BeOS',
- 'apachebench' => 'ApacheBench',
- 'aix' => 'AIX',
- 'irix' => 'Irix',
- 'osf' => 'DEC OSF',
- 'hp-ux' => 'HP-UX',
- 'netbsd' => 'NetBSD',
- 'bsdi' => 'BSDi',
- 'openbsd' => 'OpenBSD',
- 'gnu' => 'GNU/Linux',
- 'unix' => 'Unknown Unix OS'
- );
+$platforms = array(
+ 'windows nt 10.0' => 'Windows 10',
+ 'windows nt 6.3' => 'Windows 8.1',
+ 'windows nt 6.2' => 'Windows 8',
+ 'windows nt 6.1' => 'Windows 7',
+ 'windows nt 6.0' => 'Windows Vista',
+ 'windows nt 5.2' => 'Windows 2003',
+ 'windows nt 5.1' => 'Windows XP',
+ 'windows nt 5.0' => 'Windows 2000',
+ 'windows nt 4.0' => 'Windows NT 4.0',
+ 'winnt4.0' => 'Windows NT 4.0',
+ 'winnt 4.0' => 'Windows NT',
+ 'winnt' => 'Windows NT',
+ 'windows 98' => 'Windows 98',
+ 'win98' => 'Windows 98',
+ 'windows 95' => 'Windows 95',
+ 'win95' => 'Windows 95',
+ 'windows phone' => 'Windows Phone',
+ 'windows' => 'Unknown Windows OS',
+ 'android' => 'Android',
+ 'blackberry' => 'BlackBerry',
+ 'iphone' => 'iOS',
+ 'ipad' => 'iOS',
+ 'ipod' => 'iOS',
+ 'os x' => 'Mac OS X',
+ 'ppc mac' => 'Power PC Mac',
+ 'freebsd' => 'FreeBSD',
+ 'ppc' => 'Macintosh',
+ 'linux' => 'Linux',
+ 'debian' => 'Debian',
+ 'sunos' => 'Sun Solaris',
+ 'beos' => 'BeOS',
+ 'apachebench' => 'ApacheBench',
+ 'aix' => 'AIX',
+ 'irix' => 'Irix',
+ 'osf' => 'DEC OSF',
+ 'hp-ux' => 'HP-UX',
+ 'netbsd' => 'NetBSD',
+ 'bsdi' => 'BSDi',
+ 'openbsd' => 'OpenBSD',
+ 'gnu' => 'GNU/Linux',
+ 'unix' => 'Unknown Unix OS',
+ 'symbian' => 'Symbian OS'
+);
// The order of this array should NOT be changed. Many browsers return
// multiple browser types so we want to identify the sub-type first.
$browsers = array(
- 'Flock' => 'Flock',
- 'Chrome' => 'Chrome',
- 'Opera' => 'Opera',
- 'MSIE' => 'Internet Explorer',
- 'Internet Explorer' => 'Internet Explorer',
- 'Shiira' => 'Shiira',
- 'Firefox' => 'Firefox',
- 'Chimera' => 'Chimera',
- 'Phoenix' => 'Phoenix',
- 'Firebird' => 'Firebird',
- 'Camino' => 'Camino',
- 'Netscape' => 'Netscape',
- 'OmniWeb' => 'OmniWeb',
- 'Safari' => 'Safari',
- 'Mozilla' => 'Mozilla',
- 'Konqueror' => 'Konqueror',
- 'icab' => 'iCab',
- 'Lynx' => 'Lynx',
- 'Links' => 'Links',
- 'hotjava' => 'HotJava',
- 'amaya' => 'Amaya',
- 'IBrowse' => 'IBrowse'
- );
+ 'OPR' => 'Opera',
+ 'Flock' => 'Flock',
+ 'Edge' => 'Edge',
+ 'Chrome' => 'Chrome',
+ // Opera 10+ always reports Opera/9.80 and appends Version/<real version> to the user agent string
+ 'Opera.*?Version' => 'Opera',
+ 'Opera' => 'Opera',
+ 'MSIE' => 'Internet Explorer',
+ 'Internet Explorer' => 'Internet Explorer',
+ 'Trident.* rv' => 'Internet Explorer',
+ 'Shiira' => 'Shiira',
+ 'Firefox' => 'Firefox',
+ 'Chimera' => 'Chimera',
+ 'Phoenix' => 'Phoenix',
+ 'Firebird' => 'Firebird',
+ 'Camino' => 'Camino',
+ 'Netscape' => 'Netscape',
+ 'OmniWeb' => 'OmniWeb',
+ 'Safari' => 'Safari',
+ 'Mozilla' => 'Mozilla',
+ 'Konqueror' => 'Konqueror',
+ 'icab' => 'iCab',
+ 'Lynx' => 'Lynx',
+ 'Links' => 'Links',
+ 'hotjava' => 'HotJava',
+ 'amaya' => 'Amaya',
+ 'IBrowse' => 'IBrowse',
+ 'Maxthon' => 'Maxthon',
+ 'Ubuntu' => 'Ubuntu Web Browser',
+ 'Vivaldi' => 'Vivaldi'
+);
$mobiles = array(
- // legacy array, old values commented out
- 'mobileexplorer' => 'Mobile Explorer',
-// 'openwave' => 'Open Wave',
-// 'opera mini' => 'Opera Mini',
-// 'operamini' => 'Opera Mini',
-// 'elaine' => 'Palm',
- 'palmsource' => 'Palm',
-// 'digital paths' => 'Palm',
-// 'avantgo' => 'Avantgo',
-// 'xiino' => 'Xiino',
- 'palmscape' => 'Palmscape',
-// 'nokia' => 'Nokia',
-// 'ericsson' => 'Ericsson',
-// 'blackberry' => 'BlackBerry',
-// 'motorola' => 'Motorola'
+ // legacy array, old values commented out
+ 'mobileexplorer' => 'Mobile Explorer',
+// 'openwave' => 'Open Wave',
+// 'opera mini' => 'Opera Mini',
+// 'operamini' => 'Opera Mini',
+// 'elaine' => 'Palm',
+ 'palmsource' => 'Palm',
+// 'digital paths' => 'Palm',
+// 'avantgo' => 'Avantgo',
+// 'xiino' => 'Xiino',
+ 'palmscape' => 'Palmscape',
+// 'nokia' => 'Nokia',
+// 'ericsson' => 'Ericsson',
+// 'blackberry' => 'BlackBerry',
+// 'motorola' => 'Motorola'
- // Phones and Manufacturers
- 'motorola' => "Motorola",
- 'nokia' => "Nokia",
- 'palm' => "Palm",
- 'iphone' => "Apple iPhone",
- 'ipad' => "iPad",
- 'ipod' => "Apple iPod Touch",
- 'sony' => "Sony Ericsson",
- 'ericsson' => "Sony Ericsson",
- 'blackberry' => "BlackBerry",
- 'cocoon' => "O2 Cocoon",
- 'blazer' => "Treo",
- 'lg' => "LG",
- 'amoi' => "Amoi",
- 'xda' => "XDA",
- 'mda' => "MDA",
- 'vario' => "Vario",
- 'htc' => "HTC",
- 'samsung' => "Samsung",
- 'sharp' => "Sharp",
- 'sie-' => "Siemens",
- 'alcatel' => "Alcatel",
- 'benq' => "BenQ",
- 'ipaq' => "HP iPaq",
- 'mot-' => "Motorola",
- 'playstation portable' => "PlayStation Portable",
- 'hiptop' => "Danger Hiptop",
- 'nec-' => "NEC",
- 'panasonic' => "Panasonic",
- 'philips' => "Philips",
- 'sagem' => "Sagem",
- 'sanyo' => "Sanyo",
- 'spv' => "SPV",
- 'zte' => "ZTE",
- 'sendo' => "Sendo",
+ // Phones and Manufacturers
+ 'motorola' => 'Motorola',
+ 'nokia' => 'Nokia',
+ 'nexus' => 'Nexus',
+ 'palm' => 'Palm',
+ 'iphone' => 'Apple iPhone',
+ 'ipad' => 'iPad',
+ 'ipod' => 'Apple iPod Touch',
+ 'sony' => 'Sony Ericsson',
+ 'ericsson' => 'Sony Ericsson',
+ 'blackberry' => 'BlackBerry',
+ 'cocoon' => 'O2 Cocoon',
+ 'blazer' => 'Treo',
+ 'lg' => 'LG',
+ 'amoi' => 'Amoi',
+ 'xda' => 'XDA',
+ 'mda' => 'MDA',
+ 'vario' => 'Vario',
+ 'htc' => 'HTC',
+ 'samsung' => 'Samsung',
+ 'sharp' => 'Sharp',
+ 'sie-' => 'Siemens',
+ 'alcatel' => 'Alcatel',
+ 'benq' => 'BenQ',
+ 'ipaq' => 'HP iPaq',
+ 'mot-' => 'Motorola',
+ 'playstation portable' => 'PlayStation Portable',
+ 'playstation 3' => 'PlayStation 3',
+ 'playstation vita' => 'PlayStation Vita',
+ 'hiptop' => 'Danger Hiptop',
+ 'nec-' => 'NEC',
+ 'panasonic' => 'Panasonic',
+ 'philips' => 'Philips',
+ 'sagem' => 'Sagem',
+ 'sanyo' => 'Sanyo',
+ 'spv' => 'SPV',
+ 'zte' => 'ZTE',
+ 'sendo' => 'Sendo',
+ 'nintendo dsi' => 'Nintendo DSi',
+ 'nintendo ds' => 'Nintendo DS',
+ 'nintendo 3ds' => 'Nintendo 3DS',
+ 'wii' => 'Nintendo Wii',
+ 'open web' => 'Open Web',
+ 'openweb' => 'OpenWeb',
+ 'meizu' => 'Meizu',
+ 'huawei' => 'Huawei',
+ 'xiaomi' => 'Xiaomi',
+ 'oppo' => 'Oppo',
+ 'vivo' => 'Vivo',
+ 'infinix' => 'Infinix',
- // Operating Systems
- 'symbian' => "Symbian",
- 'SymbianOS' => "SymbianOS",
- 'elaine' => "Palm",
- 'palm' => "Palm",
- 'series60' => "Symbian S60",
- 'windows ce' => "Windows CE",
+ // Operating Systems
+ 'android' => 'Android',
+ 'symbian' => 'Symbian',
+ 'SymbianOS' => 'SymbianOS',
+ 'elaine' => 'Palm',
+ 'series60' => 'Symbian S60',
+ 'windows ce' => 'Windows CE',
- // Browsers
- 'obigo' => "Obigo",
- 'netfront' => "Netfront Browser",
- 'openwave' => "Openwave Browser",
- 'mobilexplorer' => "Mobile Explorer",
- 'operamini' => "Opera Mini",
- 'opera mini' => "Opera Mini",
+ // Browsers
+ 'obigo' => 'Obigo',
+ 'netfront' => 'Netfront Browser',
+ 'openwave' => 'Openwave Browser',
+ 'mobilexplorer' => 'Mobile Explorer',
+ 'operamini' => 'Opera Mini',
+ 'opera mini' => 'Opera Mini',
+ 'opera mobi' => 'Opera Mobile',
+ 'fennec' => 'Firefox Mobile',
- // Other
- 'digital paths' => "Digital Paths",
- 'avantgo' => "AvantGo",
- 'xiino' => "Xiino",
- 'novarra' => "Novarra Transcoder",
- 'vodafone' => "Vodafone",
- 'docomo' => "NTT DoCoMo",
- 'o2' => "O2",
+ // Other
+ 'digital paths' => 'Digital Paths',
+ 'avantgo' => 'AvantGo',
+ 'xiino' => 'Xiino',
+ 'novarra' => 'Novarra Transcoder',
+ 'vodafone' => 'Vodafone',
+ 'docomo' => 'NTT DoCoMo',
+ 'o2' => 'O2',
- // Fallback
- 'mobile' => "Generic Mobile",
- 'wireless' => "Generic Mobile",
- 'j2me' => "Generic Mobile",
- 'midp' => "Generic Mobile",
- 'cldc' => "Generic Mobile",
- 'up.link' => "Generic Mobile",
- 'up.browser' => "Generic Mobile",
- 'smartphone' => "Generic Mobile",
- 'cellphone' => "Generic Mobile"
- );
+ // Fallback
+ 'mobile' => 'Generic Mobile',
+ 'wireless' => 'Generic Mobile',
+ 'j2me' => 'Generic Mobile',
+ 'midp' => 'Generic Mobile',
+ 'cldc' => 'Generic Mobile',
+ 'up.link' => 'Generic Mobile',
+ 'up.browser' => 'Generic Mobile',
+ 'smartphone' => 'Generic Mobile',
+ 'cellphone' => 'Generic Mobile'
+);
// There are hundreds of bots but these are the most common.
$robots = array(
- 'googlebot' => 'Googlebot',
- 'msnbot' => 'MSNBot',
- 'slurp' => 'Inktomi Slurp',
- 'yahoo' => 'Yahoo',
- 'askjeeves' => 'AskJeeves',
- 'fastcrawler' => 'FastCrawler',
- 'infoseek' => 'InfoSeek Robot 1.0',
- 'lycos' => 'Lycos'
- );
-
-/* End of file user_agents.php */
-/* Location: ./application/config/user_agents.php */ \ No newline at end of file
+ 'googlebot' => 'Googlebot',
+ 'msnbot' => 'MSNBot',
+ 'baiduspider' => 'Baiduspider',
+ 'bingbot' => 'Bing',
+ 'slurp' => 'Inktomi Slurp',
+ 'yahoo' => 'Yahoo',
+ 'ask jeeves' => 'Ask Jeeves',
+ 'fastcrawler' => 'FastCrawler',
+ 'infoseek' => 'InfoSeek Robot 1.0',
+ 'lycos' => 'Lycos',
+ 'yandex' => 'YandexBot',
+ 'mediapartners-google' => 'MediaPartners Google',
+ 'CRAZYWEBCRAWLER' => 'Crazy Webcrawler',
+ 'adsbot-google' => 'AdsBot Google',
+ 'feedfetcher-google' => 'Feedfetcher Google',
+ 'curious george' => 'Curious George',
+ 'ia_archiver' => 'Alexa Crawler',
+ 'MJ12bot' => 'Majestic-12',
+ 'Uptimebot' => 'Uptimebot',
+ 'UptimeRobot' => 'UptimeRobot'
+);
diff --git a/application/controllers/api.php b/application/controllers/Api.php
index 9540f1ff7..b41f090dd 100644
--- a/application/controllers/api.php
+++ b/application/controllers/Api.php
@@ -24,7 +24,7 @@ class Api extends MY_Controller {
$function = $this->uri->segment(4);
if (!preg_match("/^v([0-9]+)(.[0-9]+){0,2}$/", $requested_version)) {
- throw new \exceptions\PublicApiException("api/invalid-version", "Invalid API version requested");
+ throw new \exceptions\UserInputException("api/invalid-version", "Invalid API version requested");
}
$requested_version = substr($requested_version, 1);
@@ -32,11 +32,11 @@ class Api extends MY_Controller {
$major = intval(explode(".", $requested_version)[0]);
if (!preg_match("/^[a-zA-Z-_]+$/", $controller)) {
- throw new \exceptions\PublicApiException("api/invalid-endpoint", "Invalid endpoint requested");
+ throw new \exceptions\UserInputException("api/invalid-endpoint", "Invalid endpoint requested");
}
if (!preg_match("/^[a-zA-Z-_]+$/", $function)) {
- throw new \exceptions\PublicApiException("api/invalid-endpoint", "Invalid endpoint requested");
+ throw new \exceptions\UserInputException("api/invalid-endpoint", "Invalid endpoint requested");
}
$namespace = "controllers\\api\\v".$major;
@@ -44,23 +44,50 @@ class Api extends MY_Controller {
$class_info = $namespace."\\api_info";
if (!class_exists($class_info) || version_compare($class_info::get_version(), $requested_version, "<")) {
- throw new \exceptions\PublicApiException("api/version-not-supported", "Requested API version is not supported");
+ throw new \exceptions\UserInputException("api/version-not-supported", "Requested API version is not supported");
}
if (!class_exists($class)) {
- throw new \exceptions\PublicApiException("api/unknown-endpoint", "Unknown endpoint requested");
+ throw new \exceptions\UserInputException("api/unknown-endpoint", "Unknown endpoint requested");
}
$c= new $class;
- if (!method_exists($c, $function)) {
- throw new \exceptions\PublicApiException("api/unknown-endpoint", "Unknown endpoint requested");
+ if (!method_exists($c, $function) || !is_callable([$c, $function])) {
+ throw new \exceptions\UserInputException("api/unknown-endpoint", "Unknown endpoint requested");
}
- return send_json_reply($c->$function());
+ return $this->send_json_reply($c->$function());
} catch (\exceptions\PublicApiException $e) {
- return send_json_error_reply($e->get_error_id(), $e->getMessage(), $e->get_data());
+ return $this->send_json_error_reply($e->get_error_id(), $e->getMessage(), $e->get_data());
} catch (\Exception $e) {
\libraries\ExceptionHandler::log_exception($e);
- return send_json_error_reply("internal-error", "An unhandled internal server error occured");
+ return $this->send_json_error_reply("internal-error", "An unhandled internal server error occured");
}
}
+
+ private function send_json_reply($array, $status = "success") {
+ $reply = array();
+ $reply["status"] = $status;
+ $reply["data"] = $array;
+
+ $CI =& get_instance();
+ $CI->output->set_content_type('application/json');
+ $CI->output->set_output(json_encode($reply));
+ }
+
+ private function send_json_error_reply($error_id, $message, $array = null, $status_code = 400) {
+ $reply = array();
+ $reply["status"] = "error";
+ $reply["error_id"] = $error_id;
+ $reply["message"] = $message;
+
+ if ($array !== null) {
+ $reply["data"] = $array;
+ }
+
+ $CI =& get_instance();
+ $CI->output->set_status_header($status_code);
+ $CI->output->set_content_type('application/json');
+ $CI->output->set_output(json_encode($reply));
+ }
+
}
diff --git a/application/controllers/file/file_default.php b/application/controllers/Main.php
index f4f106990..2cfaacb16 100644
--- a/application/controllers/file/file_default.php
+++ b/application/controllers/Main.php
@@ -7,7 +7,7 @@
*
*/
-class File_default extends MY_Controller {
+class Main extends MY_Controller {
function __construct()
{
@@ -19,14 +19,14 @@ class File_default extends MY_Controller {
function index()
{
- if ($this->input->is_cli_request()) {
- $this->load->library("../controllers/tools");
- return $this->tools->index();
+ if (is_cli()) {
+ output_cli_usage();
+ exit;
}
// Try to guess what the user would like to do.
$id = $this->uri->segment(1);
- if (strpos($id, "m-") === 0 && $this->mmultipaste->id_exists($id)) {
+ if (isset($id) && strpos($id, "m-") === 0 && $this->mmultipaste->id_exists($id)) {
$this->_download();
} elseif ($id != "file" && $this->mfile->id_exists($id)) {
$this->_download();
@@ -37,6 +37,28 @@ class File_default extends MY_Controller {
}
}
+ private function _handle_etag($etag)
+ {
+ $etag = strtolower($etag);
+ $modified = true;
+
+ if(isset($_SERVER['HTTP_IF_NONE_MATCH'])) {
+ $oldtag = trim(strtolower($_SERVER['HTTP_IF_NONE_MATCH']), '"');
+ if($oldtag == $etag) {
+ $modified = false;
+ } else {
+ $modified = true;
+ }
+ }
+
+ header('Etag: "'.$etag.'"');
+
+ if (!$modified) {
+ header("HTTP/1.1 304 Not Modified");
+ exit();
+ }
+ }
+
/**
* Generate a page title of the format "Multipaste - $filename, $filename, … (N more)".
* This mainly helps in IRC channels to quickly determine what is in a multipaste.
@@ -74,8 +96,9 @@ class File_default extends MY_Controller {
function _download()
{
+ session_write_close();
$id = $this->uri->segment(1);
- $lexer = urldecode($this->uri->segment(2));
+ $lexer = urldecode($this->uri->segment(2) ?? '');
$is_multipaste = false;
if ($this->mmultipaste->id_exists($id)) {
@@ -120,7 +143,7 @@ class File_default extends MY_Controller {
break;
case "qr":
- handle_etag($etag);
+ $this->_handle_etag($etag);
header("Content-disposition: inline; filename=\"".$id."_qr.png\"\n");
header("Content-Type: image/png\n");
$qr = new \Endroid\QrCode\QrCode();
@@ -155,7 +178,7 @@ class File_default extends MY_Controller {
// user wants the plain file
if ($lexer == 'plain') {
assert(count($files) == 1);
- handle_etag($etag);
+ $this->_handle_etag($etag);
$filedata = $files[0];
$filepath = $this->mfile->file($filedata["data_id"]);
@@ -171,7 +194,7 @@ class File_default extends MY_Controller {
// autodetect the lexer for highlighting if the URL contains a / after the ID (/ID/)
// /ID/lexer disables autodetection
- $autodetect_lexer = !$lexer && substr_count(ltrim($this->uri->uri_string(), "/"), '/') >= 1;
+ $autodetect_lexer = !$lexer && preg_match('/^[^?]*\/(\?.*)?$/', $_SERVER['REQUEST_URI']);
$autodetect_lexer = $is_multipaste ? true : $autodetect_lexer;
if ($autodetect_lexer) {
$lexer = $pygments->autodetect_lexer();
@@ -186,14 +209,19 @@ class File_default extends MY_Controller {
$filesize_too_big = filesize($file) > $this->config->item('upload_max_text_size');
+ if ($lexer == "asciinema") {
+ $output_cache->add(array("filedata" => $filedata), "file/fragments/asciinema-player");
+ continue;
+ }
+
if (!$can_highlight || $filesize_too_big || !$lexer) {
if (!$is_multipaste) {
// prevent javascript from being executed and forbid frames
// this should allow us to serve user submitted HTML content without huge security risks
foreach (array("X-WebKit-CSP", "X-Content-Security-Policy", "Content-Security-Policy") as $header_name) {
- header("$header_name: default-src 'none'; img-src *; media-src *; font-src *; style-src 'unsafe-inline' *; script-src 'none'; object-src *; frame-src 'none'; ");
+ header("$header_name: default-src 'none'; img-src data: *; media-src *; font-src data: *; style-src 'unsafe-inline' *; script-src 'none'; object-src *; frame-src 'none'; ");
}
- handle_etag($etag);
+ $this->_handle_etag($etag);
$this->ddownload->serveFile($file, $filedata["filename"], $filedata["mimetype"]);
exit();
} else {
@@ -221,14 +249,10 @@ class File_default extends MY_Controller {
}
}
- if ($lexer == "asciinema") {
- $output_cache->add(array("filedata" => $filedata), "file/fragments/asciinema-player");
- } else {
- $output_cache->add_function(function() use ($output_cache, $filedata, $lexer, $is_multipaste) {
- $renderer = new \service\renderer($output_cache, $this->mfile, $this->data);
- $renderer->highlight_file($filedata, $lexer, $is_multipaste);
- });
- }
+ $output_cache->add_function(function() use ($output_cache, $filedata, $lexer, $is_multipaste) {
+ $renderer = new \service\renderer($output_cache, $this->mfile, $this->data);
+ $renderer->highlight_file($filedata, $lexer, $is_multipaste);
+ });
}
// TODO: move lexers json to dedicated URL
@@ -376,7 +400,7 @@ class File_default extends MY_Controller {
$this->muser->require_session();
// keep the upload but require the user to login
$last_upload = $this->session->userdata("last_upload");
- if ($last_upload === false) {
+ if ($last_upload === NULL) {
$last_upload = array(
"ids" => [],
"lexer" => "",
@@ -433,7 +457,7 @@ class File_default extends MY_Controller {
$this->data['title'] .= ' - Upload';
$this->data['small_upload_size'] = $this->config->item('small_upload_size');
$this->data['max_upload_size'] = $this->config->item('upload_max_size');
- $this->data['upload_max_age'] = $this->config->item('upload_max_age')/60/60/24;
+ $this->data['upload_max_age'] = $this->config->item('upload_max_age');
$this->data['username'] = $this->muser->get_username();
@@ -441,10 +465,12 @@ class File_default extends MY_Controller {
if ($repaste_id) {
$filedata = $this->mfile->get_filedata($repaste_id);
-
- $pygments = new \libraries\Pygments($this->mfile->file($filedata["data_id"]), $filedata["mimetype"], $filedata["filename"]);
- if ($filedata !== false && $pygments->can_highlight()) {
- $this->data["textarea_content"] = file_get_contents($this->mfile->file($filedata["data_id"]));
+ if ($filedata !== false) {
+ $pygments = new \libraries\Pygments($this->mfile->file($filedata["data_id"]), $filedata["mimetype"], $filedata["filename"]);
+ if ($pygments->can_highlight()) {
+ $this->data["textarea_filename"] = $filedata["filename"];
+ $this->data["textarea_content"] = file_get_contents($this->mfile->file($filedata["data_id"]));
+ }
}
}
@@ -460,15 +486,9 @@ class File_default extends MY_Controller {
$this->load->view('footer', $this->data);
}
- // Allow CLI clients to query the server for the maxium filesize so they can
- // stop the upload before wasting time and bandwith
- function get_max_size()
- {
- echo $this->config->item('upload_max_size');
- }
-
function thumbnail()
{
+ session_write_close();
$id = $this->uri->segment(3);
if (!$this->mfile->valid_id($id)) {
@@ -476,7 +496,7 @@ class File_default extends MY_Controller {
}
$etag = "$id-thumb";
- handle_etag($etag);
+ $this->_handle_etag($etag);
$thumb_size = 150;
$cache_timeout = 60*60*24*30; # 1 month
@@ -517,7 +537,6 @@ class File_default extends MY_Controller {
(files.user = '.$this->db->escape($user).')
AND (
mimetype LIKE \'image%\'
- OR mimetype IN (\'application/pdf\')
)', null, false)
->order_by('date', 'desc')
->get()->result_array();
@@ -566,7 +585,7 @@ class File_default extends MY_Controller {
private function _append_multipaste_queue()
{
$ids = $this->input->post_array("ids");
- if ($ids === false) {
+ if ($ids === null) {
$ids = [];
}
@@ -592,9 +611,6 @@ class File_default extends MY_Controller {
);
$this->data['title'] .= ' - Upload history';
- foreach($fields as $length_key => $value) {
- $lengths[$length_key] = mb_strlen($value);
- }
foreach ($history["multipaste_items"] as $key => $item) {
$size = 0;
@@ -645,7 +661,6 @@ class File_default extends MY_Controller {
}
$this->data["items"] = $history["items"];
- $this->data["lengths"] = $lengths;
$this->data["fields"] = $fields;
$this->data["total_size"] = format_bytes($history["total_size"]);
@@ -778,7 +793,7 @@ class File_default extends MY_Controller {
$last_upload = $this->session->userdata("last_upload");
- if ($last_upload === false) {
+ if ($last_upload === NULL) {
throw new \exceptions\PublicApiException("file/claim_id/last_upload-failed", "Failed to get last upload data, unable to claim uploads");
}
@@ -829,23 +844,7 @@ class File_default extends MY_Controller {
{
$this->_require_cli_request();
- $tarball_dir = $this->config->item("upload_path")."/special/multipaste-tarballs";
- if (is_dir($tarball_dir)) {
- $tarball_cache_time = $this->config->item("tarball_cache_time");
- $it = new RecursiveIteratorIterator(
- new RecursiveDirectoryIterator($tarball_dir), RecursiveIteratorIterator::SELF_FIRST);
-
- foreach ($it as $file) {
- if ($file->isFile()) {
- if ($file->getMTime() < time() - $tarball_cache_time) {
- $lock = fopen($file, "r+");
- flock($lock, LOCK_EX);
- unlink($file);
- flock($lock, LOCK_UN);
- }
- }
- }
- }
+ \service\files::clean_multipaste_tarballs();
$oldest_time = (time() - $this->config->item('upload_max_age'));
$oldest_session_time = (time() - $this->config->item("sess_expiration"));
@@ -887,54 +886,9 @@ class File_default extends MY_Controller {
{
$this->_require_cli_request();
- $upload_path = $this->config->item("upload_path");
- $outer_dh = opendir($upload_path);
-
- while (($dir = readdir($outer_dh)) !== false) {
- if (!is_dir($upload_path."/".$dir) || $dir == ".." || $dir == "." || $dir == "special") {
- continue;
- }
-
- $dh = opendir($upload_path."/".$dir);
-
- $empty = true;
-
- while (($file = readdir($dh)) !== false) {
- if ($file == ".." || $file == ".") {
- continue;
- }
-
- try {
- list($hash, $storage_id) = explode("-", $file);
- } catch (\ErrorException $e) {
- unlink($upload_path."/".$dir."/".$file);
- continue;
- }
-
- $query = $this->db->select('hash, id')
- ->from('file_storage')
- ->where('hash', $hash)
- ->where('id', $storage_id)
- ->limit(1)
- ->get()->row_array();
-
- if (empty($query)) {
- $this->mfile->delete_data_id($file);
- } else {
- $empty = false;
- }
- }
-
- closedir($dh);
-
- if ($empty && file_exists($upload_path."/".$dir)) {
- rmdir($upload_path."/".$dir);
- }
- }
- closedir($outer_dh);
-
- // TODO: clean up special/multipaste-tarballs? cron() already expires
- // after a rather short time, do we really need this here then?
+ \service\files::remove_files_missing_in_db();
+ \service\files::remove_files_missing_on_disk();
+ \service\files::clean_multipaste_tarballs();
}
function nuke_id()
@@ -971,11 +925,14 @@ class File_default extends MY_Controller {
foreach ($query as $key => $item) {
$data_id = $item["hash"].'-'.$item['id'];
- $mimetype = mimetype($this->mfile->file($data_id));
+ $filepath = $this->mfile->file($data_id);
+ $mimetype = mimetype($filepath);
+ $filesize = filesize($filepath);
$this->db->where('id', $item['id'])
->set(array(
'mimetype' => $mimetype,
+ 'filesize' => $filesize,
))
->update('file_storage');
}
diff --git a/application/controllers/tools.php b/application/controllers/Tools.php
index c3209e8f7..040f0c711 100644
--- a/application/controllers/tools.php
+++ b/application/controllers/Tools.php
@@ -19,26 +19,24 @@ class Tools extends MY_Controller {
function index()
{
- echo "php index.php <controller> <function> [arguments]\n";
- echo "\n";
- echo "Functions:\n";
- echo " file cron Cronjob\n";
- echo " file nuke_id <ID> Nukes all IDs sharing the same hash\n";
- echo " user cron Cronjob\n";
- echo " tools update_database Update/Initialise the database\n";
- echo "\n";
- echo "Functions that shouldn't have to be run:\n";
- echo " file clean_stale_files Remove files without database entries\n";
- echo " file update_file_metadata Update filesize and mimetype in database\n";
+ output_cli_usage();
exit;
}
function update_database()
{
$this->load->library('migration');
- if ( ! $this->migration->current()) {
+ $upgraded = $this->migration->current();
+ if ( ! $upgraded) {
throw new \exceptions\ApiException("tools/update_database/migration-error", $this->migration->error_string());
}
+
+ if ($upgraded === true) {
+ echo "Already at latest database version. No upgrade performed\n";
+ return;
+ }
+
+ echo "Database upgraded sucessfully to version: $upgraded\n";
}
function drop_all_tables()
@@ -107,7 +105,10 @@ class Tools extends MY_Controller {
function generate_coverage_report()
{
include APPPATH."../vendor/autoload.php";
- $coverage = new \SebastianBergmann\CodeCoverage\CodeCoverage();
+ $filter = new \SebastianBergmann\CodeCoverage\Filter;
+ $coverage = new \SebastianBergmann\CodeCoverage\CodeCoverage(
+ (new \SebastianBergmann\CodeCoverage\Driver\Selector)->forLineCoverage($filter),
+ $filter);
foreach (glob(FCPATH."/test-coverage-data/*") as $file) {
$coverage->merge(unserialize(file_get_contents($file)));
}
diff --git a/application/controllers/user.php b/application/controllers/User.php
index d87b544c7..43b5040b3 100644
--- a/application/controllers/user.php
+++ b/application/controllers/User.php
@@ -16,7 +16,7 @@ class User extends MY_Controller {
function index()
{
- if ($this->input->is_cli_request()) {
+ if (is_cli()) {
$this->load->library("../controllers/tools");
return $this->tools->index();
}
@@ -45,7 +45,7 @@ class User extends MY_Controller {
$redirect_uri = $this->input->get("redirect_uri");
$this->muser->require_session();
- if (!preg_match('/^[0-9a-zA-Z\/_-]*$/', $redirect_uri)) {
+ if (!isset($redirect_uri) || !preg_match('/^[0-9a-zA-Z\/_-]*$/', $redirect_uri)) {
$redirect_uri = '/';
}
@@ -55,7 +55,7 @@ class User extends MY_Controller {
$this->data['redirect_uri'] = $redirect_uri;
- if ($this->input->post('process') !== false) {
+ if ($this->input->post('process') !== null) {
$username = $this->input->post('username');
$password = $this->input->post('password');
@@ -82,10 +82,10 @@ class User extends MY_Controller {
$userid = $this->muser->get_userid();
$comment = $this->input->post("comment");
- $comment = $comment === false ? "" : $comment;
+ $comment = $comment === null ? "" : $comment;
$access_level = $this->input->post("access_level");
- if ($access_level === false) {
+ if ($access_level === null) {
$access_level = "apikey";
}
@@ -128,26 +128,20 @@ class User extends MY_Controller {
$userid = $this->muser->get_userid();
- $invitations = $this->db->select('user')
- ->from('actions')
- ->where('user', $userid)
- ->where('action', 'invitation')
- ->count_all_results();
+ \service\user::create_invitation_key($userid);
- if ($invitations + 1 > $this->config->item('max_invitation_keys')) {
- throw new \exceptions\PublicApiException("user/invitation-limit", "You can't create more invitation keys at this time.");
- }
+ redirect("user/invite");
+ }
- $key = random_alphanum(12, 16);
+ function delete_invitation_key()
+ {
+ $this->duser->require_implemented("can_register_new_users");
+ $this->muser->require_access();
- $this->db->set(array(
- 'key' => $key,
- 'user' => $userid,
- 'date' => time(),
- 'action' => 'invitation'
- ))
- ->insert('actions');
+ $userid = $this->muser->get_userid();
+ $key = $this->input->post("key");
+ \service\user::delete_invitation_key($userid, $key);
redirect("user/invite");
}
@@ -188,7 +182,7 @@ class User extends MY_Controller {
$this->data['redirect_uri'] = "/";
- if ($process !== false) {
+ if ($process !== null) {
$username = $this->input->post("username");
$email = $this->input->post("email");
$password = $this->input->post("password");
@@ -271,10 +265,10 @@ class User extends MY_Controller {
$this->email->to($useremail);
$this->email->subject("FileBin account deleted");
$this->email->message(""
- ."Your FileBin account '${username}' at ".site_url()."\n"
+ ."Your FileBin account '{$username}' at ".site_url()."\n"
."has been permemently deleted.\n"
."\n"
- ."The request has been sent from the IP address '${_SERVER["REMOTE_ADDR"]}'\n"
+ ."The request has been sent from the IP address '{$_SERVER["REMOTE_ADDR"]}'\n"
."and was confirmed with your password.\n"
."\n"
."Thank you for using FileBin!\n"
@@ -302,15 +296,15 @@ class User extends MY_Controller {
$this->duser->require_implemented("can_reset_password");
$key = $this->uri->segment(3);
- if ($_SERVER["REQUEST_METHOD"] == "GET" && $key === false) {
+ if ($_SERVER["REQUEST_METHOD"] == "GET" && $key === null) {
return $this->_reset_password_username_form();
}
- if ($key === false) {
+ if ($key === null) {
return $this->_reset_password_send_mail();
}
- if ($key !== false) {
+ if ($key !== null) {
return $this->_reset_password_form();
}
}
@@ -332,7 +326,7 @@ class User extends MY_Controller {
$username = $this->input->post("username");
if (!$this->muser->username_exists($username)) {
- throw new \exceptions\PublicApiException("user/reset_password/invalid-username", "Invalid username");
+ throw new \exceptions\UserInputException("user/reset_password/invalid-username", "Invalid username");
}
$userinfo = $this->db->select('id, email, username')
@@ -354,8 +348,8 @@ class User extends MY_Controller {
$this->email->to($userinfo["email"]);
$this->email->subject("FileBin password reset");
$this->email->message(""
- ."Someone requested a password reset for the account '${userinfo["username"]}'\n"
- ."from the IP address '${_SERVER["REMOTE_ADDR"]}'.\n"
+ ."Someone requested a password reset for the account '{$userinfo["username"]}'\n"
+ ."from the IP address '{$_SERVER["REMOTE_ADDR"]}'.\n"
."\n"
."Please follow this link to reset your password:\n"
.site_url("user/reset_password/$key")
@@ -381,7 +375,7 @@ class User extends MY_Controller {
$userid = $query["user"];
- if ($process !== false) {
+ if ($process !== null) {
$password = $this->input->post("password");
$password_confirm = $this->input->post("password_confirm");
@@ -462,7 +456,7 @@ class User extends MY_Controller {
{
$this->muser->require_access();
- if ($this->input->post("process") !== false) {
+ if ($this->input->post("process") !== null) {
$this->_save_profile();
}
@@ -491,18 +485,18 @@ class User extends MY_Controller {
$values = explode("-", $value);
if (!is_array($values) || count($values) != 2) {
- throw new \exceptions\PublicApiException("user/profile/invalid-upload-id-limit", "Invalid upload id limit value");
+ throw new \exceptions\UserInputException("user/profile/invalid-upload-id-limit", "Invalid upload id limit value");
}
$lower = intval($values[0]);
$upper = intval($values[1]);
if ($lower > $upper) {
- throw new \exceptions\PublicApiException("user/profile/lower-bigger-than-upper", "lower limit > upper limit");
+ throw new \exceptions\UserInputException("user/profile/lower-bigger-than-upper", "lower limit > upper limit");
}
if ($lower < 3 || $upper > 64) {
- throw new \exceptions\PublicApiException("user/profile/limit-out-of-bounds", "upper or lower limit out of bounds (3-64)");
+ throw new \exceptions\UserInputException("user/profile/limit-out-of-bounds", "upper or lower limit out of bounds (3-64)");
}
return $lower."-".$upper;
@@ -518,7 +512,7 @@ class User extends MY_Controller {
}
if (!$this->muser->valid_email($value)) {
- throw new \exceptions\PublicApiException("user/profile/invalid-email", "Invalid email");
+ throw new \exceptions\UserInputException("user/profile/invalid-email", "Invalid email");
}
$this->load->library("email");
@@ -559,7 +553,7 @@ class User extends MY_Controller {
$this->email->to($email['email']);
$this->email->subject("FileBin email change confirmation");
$this->email->message(""
- ."A request has been sent to change the email address of account '${old["username"]}'\n"
+ ."A request has been sent to change the email address of account '{$old["username"]}'\n"
."from ".$old['email']." to $value.\n"
."\n"
."Please follow this link to CONFIRM the change:\n"
@@ -584,7 +578,7 @@ class User extends MY_Controller {
foreach (array_keys($value_processor) as $field) {
$value = $this->input->post($field);
- if ($value !== false) {
+ if ($value !== null) {
$new_value = $value_processor[$field]($value);
if ($new_value !== null) {
$data[$field] = $new_value;
@@ -619,7 +613,7 @@ class User extends MY_Controller {
$this->data["hash"] = false;
$this->data["password"] = $password;
- if ($process !== false) {
+ if ($process !== null) {
if (!$password || $password !== $password_confirm) {
$error[]= "No password or passwords don't match.";
} else {
@@ -702,4 +696,22 @@ class User extends MY_Controller {
echo "User added\n";
}
+
+ function delete_user()
+ {
+ $this->_require_cli_request();
+ $this->duser->require_implemented("can_delete_account");
+
+ echo "\nWARNING: Deleting a user will delete ALL their data permanently.\n\n";
+
+ $username = $this->_get_line_cli("Username", function($username) {
+ if (get_instance()->muser->username_exists($username)) {
+ return true;
+ }
+ return false;
+ });
+ $this->muser->delete_user_real($username);
+ echo "User removed\n";
+ }
+
}
diff --git a/application/controllers/api/api_controller.php b/application/controllers/api/api_controller.php
index 2b9054b17..ad3ac6e73 100644
--- a/application/controllers/api/api_controller.php
+++ b/application/controllers/api/api_controller.php
@@ -9,6 +9,11 @@
namespace controllers\api;
-abstract class api_controller extends \CI_Controller {
+abstract class api_controller {
+ public $CI;
+ public function __construct() {
+ $this->CI =& get_instance();
+ }
+
}
diff --git a/application/controllers/api/v2/api_info.php b/application/controllers/api/v2/api_info.php
index 8d2bdf6dc..bd1d63590 100644
--- a/application/controllers/api/v2/api_info.php
+++ b/application/controllers/api/v2/api_info.php
@@ -11,6 +11,6 @@ namespace controllers\api\v2;
class api_info extends \controllers\api\api_controller {
static public function get_version()
{
- return "2.1.1";
+ return "2.2.0";
}
}
diff --git a/application/controllers/api/v2/file.php b/application/controllers/api/v2/file.php
index 15a43fc45..2e792e577 100644
--- a/application/controllers/api/v2/file.php
+++ b/application/controllers/api/v2/file.php
@@ -13,28 +13,28 @@ class file extends \controllers\api\api_controller {
{
parent::__construct();
- $this->load->model('mfile');
- $this->load->model('mmultipaste');
+ $this->CI->load->model('mfile');
+ $this->CI->load->model('mmultipaste');
}
public function upload()
{
- $this->muser->require_access("basic");
+ $this->CI->muser->require_access("basic");
$files = getNormalizedFILES();
if (empty($files)) {
- throw new \exceptions\PublicApiException("file/no-file", "No file was uploaded or unknown error occurred.");
+ throw new \exceptions\UserInputException("file/no-file", "No file was uploaded or unknown error occurred.");
}
\service\files::verify_uploaded_files($files);
- $limits = $this->muser->get_upload_id_limits();
- $userid = $this->muser->get_userid();
+ $limits = $this->determine_id_limits();
+ $userid = $this->CI->muser->get_userid();
$urls = array();
foreach ($files as $file) {
- $id = $this->mfile->new_id($limits[0], $limits[1]);
+ $id = $this->CI->mfile->new_id($limits[0], $limits[1]);
\service\files::add_uploaded_file($userid, $id, $file["tmp_name"], $file["name"]);
$ids[] = $id;
$urls[] = site_url($id).'/';
@@ -49,7 +49,7 @@ class file extends \controllers\api\api_controller {
public function get_config()
{
return array(
- "upload_max_size" => $this->config->item("upload_max_size"),
+ "upload_max_size" => $this->CI->config->item("upload_max_size"),
"max_files_per_request" => intval(ini_get("max_file_uploads")),
"max_input_vars" => intval(ini_get("max_input_vars")),
"request_max_size" => return_bytes(ini_get("post_max_size")),
@@ -58,8 +58,8 @@ class file extends \controllers\api\api_controller {
public function history()
{
- $this->muser->require_access("apikey");
- $history = \service\files::history($this->muser->get_userid());
+ $this->CI->muser->require_access("apikey");
+ $history = \service\files::history($this->CI->muser->get_userid());
foreach ($history['multipaste_items'] as $key => $item) {
foreach ($item['items'] as $inner_key => $item) {
unset($history['multipaste_items'][$key]['items'][$inner_key]['sort_order']);
@@ -73,8 +73,8 @@ class file extends \controllers\api\api_controller {
public function delete()
{
- $this->muser->require_access("apikey");
- $ids = $this->input->post_array("ids");
+ $this->CI->muser->require_access("apikey");
+ $ids = $this->CI->input->post_array("ids");
$ret = \service\files::delete($ids);
$ret = ensure_json_keys_contain_objects($ret, array("errors", "deleted"));
@@ -84,13 +84,29 @@ class file extends \controllers\api\api_controller {
public function create_multipaste()
{
- $this->muser->require_access("basic");
- $ids = $this->input->post_array("ids");
- $userid = $this->muser->get_userid();
- $limits = $this->muser->get_upload_id_limits();
+ $this->CI->muser->require_access("basic");
+ $ids = $this->CI->input->post_array("ids");
+ $userid = $this->CI->muser->get_userid();
+ $limits = $this->determine_id_limits();
return \service\files::create_multipaste($ids, $userid, $limits);
}
+
+ private function determine_id_limits()
+ {
+ $posted_minlength = $this->CI->input->post('minimum-id-length');
+ if (is_null($posted_minlength)) {
+ $limits = $this->CI->muser->get_upload_id_limits();
+ } else {
+ if ((!preg_match("/^\d+$/", $posted_minlength)) || intval($posted_minlength) <= 1 ) {
+ throw new \exceptions\UserInputException("file/bad-minimum-id-length", "Passed parameter 'minimum-id-length' is not a valid integer or too small (min value: 2)");
+ }
+
+ $limits = [$posted_minlength, null];
+ }
+
+ return $limits;
+ }
}
# vim: set noet:
diff --git a/application/controllers/api/v2/user.php b/application/controllers/api/v2/user.php
index 655dc62f3..677a870c4 100644
--- a/application/controllers/api/v2/user.php
+++ b/application/controllers/api/v2/user.php
@@ -13,31 +13,31 @@ class user extends \controllers\api\api_controller {
{
parent::__construct();
- $this->load->model('muser');
+ $this->CI->load->model('muser');
}
public function apikeys()
{
- $this->muser->require_access("full");
- return \service\user::apikeys($this->muser->get_userid());
+ $this->CI->muser->require_access("full");
+ return \service\user::apikeys($this->CI->muser->get_userid());
}
public function create_apikey()
{
- $username = $this->input->post("username");
- $password = $this->input->post("password");
+ $username = $this->CI->input->post("username");
+ $password = $this->CI->input->post("password");
if ($username && $password) {
- if (!$this->muser->login($username, $password)) {
+ if (!$this->CI->muser->login($username, $password)) {
throw new \exceptions\NotAuthenticatedException("user/login-failed", "Login failed");
}
}
- $this->muser->require_access("full");
+ $this->CI->muser->require_access("full");
- $userid = $this->muser->get_userid();
- $comment = $this->input->post("comment");
- $comment = $comment === false ? "" : $comment;
- $access_level = $this->input->post("access_level");
+ $userid = $this->CI->muser->get_userid();
+ $comment = $this->CI->input->post("comment");
+ $comment = $comment === null ? "" : $comment;
+ $access_level = $this->CI->input->post("access_level");
$key = \service\user::create_apikey($userid, $comment, $access_level);
@@ -48,16 +48,16 @@ class user extends \controllers\api\api_controller {
public function delete_apikey()
{
- $this->muser->require_access("full");
+ $this->CI->muser->require_access("full");
- $userid = $this->muser->get_userid();
- $key = $this->input->post("delete_key");
+ $userid = $this->CI->muser->get_userid();
+ $key = $this->CI->input->post("delete_key");
- $this->db->where('user', $userid)
+ $this->CI->db->where('user', $userid)
->where('key', $key)
->delete('apikeys');
- $affected = $this->db->affected_rows();
+ $affected = $this->CI->db->affected_rows();
assert($affected >= 0 && $affected <= 1);
if ($affected == 1) {
diff --git a/application/controllers/file/multipaste.php b/application/controllers/file/Multipaste.php
index 50367697c..bc042e2f3 100644
--- a/application/controllers/file/multipaste.php
+++ b/application/controllers/file/Multipaste.php
@@ -20,7 +20,7 @@ class Multipaste extends MY_Controller {
$this->muser->require_access("basic");
$ids = $this->input->post_array("ids");
- if ($ids === false) {
+ if ($ids === null) {
$ids = [];
}
@@ -46,6 +46,7 @@ class Multipaste extends MY_Controller {
$this->data['ids'] = $ids;
$this->data['items'] = array_map(function($id) {return $this->_get_multipaste_item($id);}, $ids);
+ $this->data['items'] = array_filter($this->data['items'], function($item) {return $item !== false;});
$this->load->view('header', $this->data);
$this->load->view('file/multipaste/queue', $this->data);
@@ -58,7 +59,7 @@ class Multipaste extends MY_Controller {
$ids = $this->input->post_array('ids');
$process = $this->input->post('process');
- if ($ids === false) {
+ if ($ids === null) {
$ids = [];
}
@@ -89,7 +90,7 @@ class Multipaste extends MY_Controller {
$this->muser->require_access("basic");
$ids = $this->input->post_array('ids');
- if ($ids === false) {
+ if ($ids === null) {
$ids = [];
}
@@ -99,6 +100,10 @@ class Multipaste extends MY_Controller {
private function _get_multipaste_item($id) {
$filedata = $this->mfile->get_filedata($id);
+ if ($filedata === false) {
+ return false;
+ }
+
$item = [];
$item['id'] = $filedata['id'];
$item['tooltip'] = \service\files::tooltip($filedata);
diff --git a/application/controllers/index.html b/application/controllers/index.html
index c942a79ce..bcb7cae34 100644
--- a/application/controllers/index.html
+++ b/application/controllers/index.html
@@ -1,4 +1,5 @@
-<html>
+<!DOCTYPE html>
+<html lang="en">
<head>
<title>403 Forbidden</title>
</head>
@@ -7,4 +8,4 @@
<p>Directory access is forbidden.</p>
</body>
-</html> \ No newline at end of file
+</html>
diff --git a/application/core/MY_Controller.php b/application/core/MY_Controller.php
index 47dd6a899..ce8e0e948 100644
--- a/application/core/MY_Controller.php
+++ b/application/core/MY_Controller.php
@@ -20,14 +20,13 @@ class MY_Controller extends CI_Controller {
$this->load->library('customautoloader');
// check if DB is up to date
- if (!($this->input->is_cli_request() && $this->uri->segment(1) === "tools")) {
+ if (!(is_cli() && $this->uri->segment(1) === "tools")) {
$this->_ensure_database_schema_up_to_date();
}
$old_path = getenv("PATH");
putenv("PATH=$old_path:/usr/local/bin:/usr/bin:/bin:/usr/local/sbin:/usr/sbin:/sbin");
- mb_internal_encoding('UTF-8');
$this->load->helper(array('form', 'filebin'));
if ($this->uri->segment(1) == "api") {
@@ -54,8 +53,8 @@ class MY_Controller extends CI_Controller {
protected function _require_cli_request()
{
- if (!$this->input->is_cli_request()) {
- throw new \exceptions\PublicApiException("api/cli-only", "This function can only be accessed via the CLI interface");
+ if (!is_cli()) {
+ throw new \exceptions\InsufficientPermissionsException("api/cli-only", "This function can only be accessed via the CLI interface");
}
}
@@ -79,7 +78,7 @@ class MY_Controller extends CI_Controller {
private function _check_csrf_protection_required()
{
- if ($this->input->post("apikey") !== false || is_api_client()) {
+ if ($this->input->post("apikey") !== null || is_api_client()) {
/* This relies on the authentication code always verifying the supplied
* apikey. If the key is not verified/logged in an attacker could simply
* add an empty "apikey" field to the CSRF form to circumvent the
@@ -106,7 +105,7 @@ class MY_Controller extends CI_Controller {
return false;
}
- if ($this->input->is_cli_request()) {
+ if (is_cli()) {
return false;
}
@@ -118,7 +117,11 @@ class MY_Controller extends CI_Controller {
// 2 functions for accessing config options, really?
$this->config->set_item('csrf_protection', true);
config_item("csrf_protection", true);
- $this->security->__construct();
- $this->security->csrf_verify();
+
+ if ($this->uri->uri_string() == "file/multipaste/ajax_submit") {
+ $this->config->set_item('csrf_regenerate', false);
+ }
+
+ $this->security->__construct('UTF-8');
}
}
diff --git a/application/core/MY_Input.php b/application/core/MY_Input.php
index 4d43774c0..5a08ea4bb 100644
--- a/application/core/MY_Input.php
+++ b/application/core/MY_Input.php
@@ -26,8 +26,8 @@ class MY_Input extends CI_Input {
public function post_array($key) {
$ret = parent::post($key);
- if ($ret === false) {
- return false;
+ if ($ret === null) {
+ return null;
} elseif (!is_array($ret)) {
$data = [
"key" => $key,
diff --git a/application/core/index.html b/application/core/index.html
index c942a79ce..bcb7cae34 100644
--- a/application/core/index.html
+++ b/application/core/index.html
@@ -1,4 +1,5 @@
-<html>
+<!DOCTYPE html>
+<html lang="en">
<head>
<title>403 Forbidden</title>
</head>
@@ -7,4 +8,4 @@
<p>Directory access is forbidden.</p>
</body>
-</html> \ No newline at end of file
+</html>
diff --git a/application/errors/error_404.php b/application/errors/error_404.php
deleted file mode 100644
index 2e69d7e00..000000000
--- a/application/errors/error_404.php
+++ /dev/null
@@ -1,3 +0,0 @@
-<?php
-$title = "404 Page Not Found";
-include APPPATH."errors/error_general.php";
diff --git a/application/errors/error_db.php b/application/errors/error_db.php
deleted file mode 100644
index 827d7cc31..000000000
--- a/application/errors/error_db.php
+++ /dev/null
@@ -1,3 +0,0 @@
-<?php
-$title = "Database Error";
-include APPPATH."errors/error_general.php";
diff --git a/application/errors/error_general.php b/application/errors/error_general.php
deleted file mode 100644
index 14e5e7a2e..000000000
--- a/application/errors/error_general.php
+++ /dev/null
@@ -1,104 +0,0 @@
-<?php
-
-// fancy error page only works if we can load helpers
-if (class_exists("CI_Controller") && !isset($GLOBALS["is_error_page"]) && isset(get_instance()->load)) {
- if (!isset($title)) {
- $title = "Error";
- }
- $GLOBALS["is_error_page"] = true;
-
- $CI =& get_instance();
- $CI->load->helper("filebin");
- $CI->load->helper("url");
-
- if ($CI->input->is_cli_request()) {
- $message = str_replace("</p>", "</p>\n", $message);
- $message = strip_tags($message);
- echo "$heading: $message\n";
- exit();
- }
-
- include APPPATH.'views/header.php';
-
- ?>
- <div class="error">
- <h1><?php echo $heading; ?></h1>
- <?php echo $message; ?>
- </div>
-
- <?php
- include APPPATH.'views/footer.php';
-} elseif (php_sapi_name() === 'cli' OR defined('STDIN')) {
- echo "# $heading\n";
- $msg = strip_tags(str_replace("<br>", "\n", $message));
- foreach (explode("\n", $msg) as $line) {
- echo "# $line\n";
- }
- exit(255);
-} else {
- // default CI error page
-?>
-<!DOCTYPE html>
-<html lang="en">
-<head>
-<title>Error</title>
-<style type="text/css">
-
-::selection{ background-color: #E13300; color: white; }
-::moz-selection{ background-color: #E13300; color: white; }
-::webkit-selection{ background-color: #E13300; color: white; }
-
-body {
- background-color: #fff;
- margin: 40px;
- font: 13px/20px normal Helvetica, Arial, sans-serif;
- color: #4F5155;
-}
-
-a {
- color: #003399;
- background-color: transparent;
- font-weight: normal;
-}
-
-h1 {
- color: #444;
- background-color: transparent;
- border-bottom: 1px solid #D0D0D0;
- font-size: 19px;
- font-weight: normal;
- margin: 0 0 14px 0;
- padding: 14px 15px 10px 15px;
-}
-
-code {
- font-family: Consolas, Monaco, Courier New, Courier, monospace;
- font-size: 12px;
- background-color: #f9f9f9;
- border: 1px solid #D0D0D0;
- color: #002166;
- display: block;
- margin: 14px 0 14px 0;
- padding: 12px 10px 12px 10px;
-}
-
-#container {
- margin: 10px;
- border: 1px solid #D0D0D0;
- -webkit-box-shadow: 0 0 8px #D0D0D0;
-}
-
-p, div {
- margin: 12px 15px 12px 15px;
-}
-</style>
-</head>
-<body>
- <div id="container">
- <h1><?php echo $heading; ?></h1>
- <?php echo $message; ?>
- </div>
-</body>
-</html>
-<?php
-}
diff --git a/application/errors/error_php.php b/application/errors/error_php.php
deleted file mode 100644
index 5f91e07a0..000000000
--- a/application/errors/error_php.php
+++ /dev/null
@@ -1,11 +0,0 @@
-<div style="border:1px solid #990000;padding-left:20px;margin:0 0 10px 0;">
-
-<h4>A PHP Error was encountered</h4>
-
-<p>Severity: <?php echo $severity; ?></p>
-<p>Message: <?php echo $message; ?></p>
-<p>Filename: <?php echo $filepath; ?></p>
-<p>Line Number: <?php echo $line; ?></p>
-
-</div>
-<?php exit();
diff --git a/application/helpers/filebin_helper.php b/application/helpers/filebin_helper.php
index c1a6f4f96..0fa986225 100644
--- a/application/helpers/filebin_helper.php
+++ b/application/helpers/filebin_helper.php
@@ -1,5 +1,31 @@
<?php
+function expiration_duration($duration)
+{
+ $total = $duration;
+ $days = floor($total / 86400);
+ $total -= $days * 86400;
+ $hours = floor($total / 3600);
+ $total -= $hours * 3600;
+ $minutes = floor($total / 60);
+ $seconds = $total - $minutes * 60;
+ $times = array($days, $hours, $minutes, $seconds);
+ $suffixes = array(' day', ' hour', ' minute', ' second');
+ $expiration = array();
+
+ for ($i = 0; $i < count($suffixes); $i++) {
+ if ($times[$i] != 0) {
+ $duration = $times[$i].$suffixes[$i];
+ if ($times[$i] > 1) {
+ $duration .= "s";
+ }
+ array_push($expiration, $duration);
+ }
+ }
+
+ return join(", ", $expiration);
+}
+
function format_bytes($size)
{
$suffixes = array('B', 'KiB', 'MiB', 'GiB', 'TiB' , 'PiB' , 'EiB', 'ZiB', 'YiB');
@@ -20,70 +46,6 @@ function format_bytes($size)
}
}
-function even_odd($reset = false)
-{
- static $counter = 1;
-
- if ($reset) {
- $counter = 1;
- }
-
- if ($counter++%2 == 0) {
- return 'even';
- } else {
- return 'odd';
- }
-}
-
-// Source: http://hu.php.net/manual/en/function.str-pad.php#71558
-// This is a multibyte enabled str_pad
-function mb_str_pad($ps_input, $pn_pad_length, $ps_pad_string = " ", $pn_pad_type = STR_PAD_RIGHT, $ps_encoding = NULL)
-{
- $ret = "";
-
- if (is_null($ps_encoding))
- $ps_encoding = mb_internal_encoding();
-
- $hn_length_of_padding = $pn_pad_length - mb_strlen($ps_input, $ps_encoding);
- $hn_psLength = mb_strlen($ps_pad_string, $ps_encoding); // pad string length
-
- if ($hn_psLength <= 0 || $hn_length_of_padding <= 0) {
- // Padding string equal to 0:
- //
- $ret = $ps_input;
- }
- else {
- $hn_repeatCount = floor($hn_length_of_padding / $hn_psLength); // how many times repeat
-
- if ($pn_pad_type == STR_PAD_BOTH) {
- $hs_lastStrLeft = "";
- $hs_lastStrRight = "";
- $hn_repeatCountLeft = $hn_repeatCountRight = ($hn_repeatCount - $hn_repeatCount % 2) / 2;
-
- $hs_lastStrLength = $hn_length_of_padding - 2 * $hn_repeatCountLeft * $hn_psLength; // the rest length to pad
- $hs_lastStrLeftLength = $hs_lastStrRightLength = floor($hs_lastStrLength / 2); // the rest length divide to 2 parts
- $hs_lastStrRightLength += $hs_lastStrLength % 2; // the last char add to right side
-
- $hs_lastStrLeft = mb_substr($ps_pad_string, 0, $hs_lastStrLeftLength, $ps_encoding);
- $hs_lastStrRight = mb_substr($ps_pad_string, 0, $hs_lastStrRightLength, $ps_encoding);
-
- $ret = str_repeat($ps_pad_string, $hn_repeatCountLeft) . $hs_lastStrLeft;
- $ret .= $ps_input;
- $ret .= str_repeat($ps_pad_string, $hn_repeatCountRight) . $hs_lastStrRight;
- }
- else {
- $hs_lastStr = mb_substr($ps_pad_string, 0, $hn_length_of_padding % $hn_psLength, $ps_encoding); // last part of pad string
-
- if ($pn_pad_type == STR_PAD_LEFT)
- $ret = str_repeat($ps_pad_string, $hn_repeatCount) . $hs_lastStr . $ps_input;
- else
- $ret = $ps_input . str_repeat($ps_pad_string, $hn_repeatCount) . $hs_lastStr;
- }
- }
-
- return $ret;
-}
-
function is_api_client($override = null)
{
static $is_api = null;
@@ -152,28 +114,6 @@ function js_cache_buster()
return $ret;
}
-function handle_etag($etag)
-{
- $etag = strtolower($etag);
- $modified = true;
-
- if(isset($_SERVER['HTTP_IF_NONE_MATCH'])) {
- $oldtag = trim(strtolower($_SERVER['HTTP_IF_NONE_MATCH']), '"');
- if($oldtag == $etag) {
- $modified = false;
- } else {
- $modified = true;
- }
- }
-
- header('Etag: "'.$etag.'"');
-
- if (!$modified) {
- header("HTTP/1.1 304 Not Modified");
- exit();
- }
-}
-
// Reference: http://php.net/manual/en/features.file-upload.multiple.php#109437
// This is a little different because we don't care about the fieldname
function getNormalizedFILES()
@@ -217,34 +157,6 @@ function auth_driver_function_implemented($function)
return $result[$function];
}
-function send_json_reply($array, $status = "success")
-{
- $reply = array();
- $reply["status"] = $status;
- $reply["data"] = $array;
-
- $CI =& get_instance();
- $CI->output->set_content_type('application/json');
- $CI->output->set_output(json_encode($reply));
-}
-
-function send_json_error_reply($error_id, $message, $array = null, $status_code = 400)
-{
- $reply = array();
- $reply["status"] = "error";
- $reply["error_id"] = $error_id;
- $reply["message"] = $message;
-
- if ($array !== null) {
- $reply["data"] = $array;
- }
-
- $CI =& get_instance();
- $CI->output->set_status_header($status_code);
- $CI->output->set_content_type('application/json');
- $CI->output->set_output(json_encode($reply));
-}
-
function static_storage($key, $value = null)
{
static $storage = array();
@@ -392,4 +304,22 @@ function ensure_json_keys_contain_objects($data, $keys) {
return $data;
}
+function output_cli_usage() {
+ echo "php index.php <controller> <function> [arguments]\n";
+ echo "\n";
+ echo "Functions:\n";
+ echo " file cron Cronjob\n";
+ echo " file nuke_id <ID> Nukes all IDs sharing the same hash\n";
+ echo " user cron Cronjob\n";
+ echo " user add_user Add a user\n";
+ echo " user delete_user Delete a user including all their data\n";
+ echo " tools update_database Update/Initialise the database\n";
+ echo "\n";
+ echo "Functions that shouldn't have to be run:\n";
+ echo " file clean_stale_files Remove files without database entries,\n";
+ echo " database entries without files and multipaste\n";
+ echo " tarballs that are no longer needed\n";
+ echo " file update_file_metadata Update filesize and mimetype in database\n";
+}
+
# vim: set noet:
diff --git a/application/helpers/index.html b/application/helpers/index.html
index c942a79ce..bcb7cae34 100644
--- a/application/helpers/index.html
+++ b/application/helpers/index.html
@@ -1,4 +1,5 @@
-<html>
+<!DOCTYPE html>
+<html lang="en">
<head>
<title>403 Forbidden</title>
</head>
@@ -7,4 +8,4 @@
<p>Directory access is forbidden.</p>
</body>
-</html> \ No newline at end of file
+</html>
diff --git a/application/hooks/index.html b/application/hooks/index.html
index c942a79ce..bcb7cae34 100644
--- a/application/hooks/index.html
+++ b/application/hooks/index.html
@@ -1,4 +1,5 @@
-<html>
+<!DOCTYPE html>
+<html lang="en">
<head>
<title>403 Forbidden</title>
</head>
@@ -7,4 +8,4 @@
<p>Directory access is forbidden.</p>
</body>
-</html> \ No newline at end of file
+</html>
diff --git a/application/index.html b/application/index.html
index c942a79ce..bcb7cae34 100644
--- a/application/index.html
+++ b/application/index.html
@@ -1,4 +1,5 @@
-<html>
+<!DOCTYPE html>
+<html lang="en">
<head>
<title>403 Forbidden</title>
</head>
@@ -7,4 +8,4 @@
<p>Directory access is forbidden.</p>
</body>
-</html> \ No newline at end of file
+</html>
diff --git a/application/language/english/index.html b/application/language/english/index.html
index c942a79ce..bcb7cae34 100644
--- a/application/language/english/index.html
+++ b/application/language/english/index.html
@@ -1,4 +1,5 @@
-<html>
+<!DOCTYPE html>
+<html lang="en">
<head>
<title>403 Forbidden</title>
</head>
@@ -7,4 +8,4 @@
<p>Directory access is forbidden.</p>
</body>
-</html> \ No newline at end of file
+</html>
diff --git a/application/language/index.html b/application/language/index.html
index c942a79ce..bcb7cae34 100644
--- a/application/language/index.html
+++ b/application/language/index.html
@@ -1,4 +1,5 @@
-<html>
+<!DOCTYPE html>
+<html lang="en">
<head>
<title>403 Forbidden</title>
</head>
@@ -7,4 +8,4 @@
<p>Directory access is forbidden.</p>
</body>
-</html> \ No newline at end of file
+</html>
diff --git a/application/libraries/Customautoloader.php b/application/libraries/Customautoloader.php
index 426364ee3..eb14c5624 100644
--- a/application/libraries/Customautoloader.php
+++ b/application/libraries/Customautoloader.php
@@ -8,7 +8,7 @@
*/
// Original source: http://stackoverflow.com/a/9526005/953022
-class CustomAutoloader{
+class Customautoloader {
public function __construct()
{
spl_autoload_register(array($this, 'loader'));
diff --git a/application/libraries/Ddownload/Ddownload.php b/application/libraries/Ddownload/Ddownload.php
index 808dfe776..3a98d4154 100644
--- a/application/libraries/Ddownload/Ddownload.php
+++ b/application/libraries/Ddownload/Ddownload.php
@@ -17,7 +17,7 @@ class Ddownload extends CI_Driver_Library {
protected $_adapter = null;
protected $valid_drivers = array(
- 'ddownload_php', 'ddownload_nginx', 'ddownload_lighttpd'
+ 'php', 'nginx', 'lighttpd'
);
function __construct()
diff --git a/application/libraries/Ddownload/drivers/Ddownload_php.php b/application/libraries/Ddownload/drivers/Ddownload_php.php
index 344db53f0..90c002b58 100644
--- a/application/libraries/Ddownload/drivers/Ddownload_php.php
+++ b/application/libraries/Ddownload/drivers/Ddownload_php.php
@@ -12,7 +12,7 @@ class Ddownload_php extends Ddownload_Driver {
// Original source: http://www.phpfreaks.com/forums/index.php?topic=198274.msg895468#msg895468
public function serveFile($file, $filename, $type)
{
- $fp = @fopen($file, 'r');
+ $fp = fopen($file, 'r');
$size = filesize($file); // File size
$length = $size; // Content length
@@ -54,7 +54,7 @@ class Ddownload_php extends Ddownload_Driver {
// If the range starts with an '-' we start from the beginning
// If not, we forward the file pointer
// And make sure to get the end byte if spesified
- if ($range{0} == '-')
+ if ($range[0] == '-')
{
// The n-number of the last bytes is requested
$c_start = $size - substr($range, 1);
diff --git a/application/libraries/Duser/Duser.php b/application/libraries/Duser/Duser.php
index 8005a00bb..0007fabd8 100644
--- a/application/libraries/Duser/Duser.php
+++ b/application/libraries/Duser/Duser.php
@@ -50,7 +50,7 @@ class Duser extends CI_Driver_Library {
protected $_adapter = null;
protected $valid_drivers = array(
- 'duser_db', 'duser_ldap', 'duser_fluxbb'
+ 'db', 'ldap', 'fluxbb'
);
function __construct()
diff --git a/application/libraries/Duser/drivers/Duser_db.php b/application/libraries/Duser/drivers/Duser_db.php
index 062da9e54..e1df20f1f 100644
--- a/application/libraries/Duser/drivers/Duser_db.php
+++ b/application/libraries/Duser/drivers/Duser_db.php
@@ -24,6 +24,10 @@ class Duser_db extends Duser_Driver {
{
$CI =& get_instance();
+ if ($username === null) {
+ return false;
+ }
+
$query = $CI->db->select('username, id, password')
->from('users')
->where('username', $username)
@@ -48,6 +52,10 @@ class Duser_db extends Duser_Driver {
{
$CI =& get_instance();
+ if ($username === null) {
+ return false;
+ }
+
$query = $CI->db->select('id')
->from('users')
->where('username', $username)
@@ -64,6 +72,10 @@ class Duser_db extends Duser_Driver {
{
$CI =& get_instance();
+ if ($userid === null) {
+ throw new \exceptions\ApiException("libraries/duser/db/get_email-failed", "User does not exist");
+ }
+
$query = $CI->db->select('email')
->from('users')
->where('id', $userid)
diff --git a/application/libraries/Duser/drivers/Duser_ldap.php b/application/libraries/Duser/drivers/Duser_ldap.php
index b80385fe0..9481397d0 100644
--- a/application/libraries/Duser/drivers/Duser_ldap.php
+++ b/application/libraries/Duser/drivers/Duser_ldap.php
@@ -26,15 +26,26 @@ class Duser_ldap extends Duser_Driver {
return false;
}
+ if (isset($config['bind_rdn']) && isset($config['bind_password'])) {
+ ldap_bind($ds, $config['bind_rdn'], $config['bind_password']);
+ }
+
+ if (isset($config['filter'])) {
+ $filter = sprintf($config['filter'], $username);
+ } else {
+ $filter = $config["username_field"].'='.$username;
+ }
+
+
switch ($config["scope"]) {
case "base":
- $r = ldap_read($ds, $config['basedn'], $config["username_field"].'='.$username);
+ $r = ldap_read($ds, $config['basedn'], $filter);
break;
case "one":
- $r = ldap_list($ds, $config['basedn'], $config["username_field"].'='.$username);
+ $r = ldap_list($ds, $config['basedn'], $filter);
break;
case "subtree":
- $r = ldap_search($ds, $config['basedn'], $config["username_field"].'='.$username);
+ $r = ldap_search($ds, $config['basedn'], $filter);
break;
default:
throw new \exceptions\ApiException("libraries/duser/ldap/invalid-ldap-scope", "Invalid LDAP scope");
diff --git a/application/libraries/ExceptionHandler.php b/application/libraries/ExceptionHandler.php
index acfa97163..ed7f9b8c5 100644
--- a/application/libraries/ExceptionHandler.php
+++ b/application/libraries/ExceptionHandler.php
@@ -15,8 +15,6 @@ class ExceptionHandler {
set_error_handler(array("\libraries\ExceptionHandler", "error_handler"));
set_exception_handler(array("\libraries\ExceptionHandler", 'exception_handler'));
register_shutdown_function(array("\libraries\ExceptionHandler", "check_for_fatal"));
- assert_options(ASSERT_ACTIVE, true);
- assert_options(ASSERT_CALLBACK, array("\libraries\ExceptionHandler", '_assert_failure'));
}
static function error_handler($errno, $errstr, $errfile, $errline)
@@ -130,7 +128,7 @@ class ExceptionHandler {
}
$message = "$message";
- include APPPATH."/errors/error_general.php";
+ include VIEWPATH."/errors/html/error_general.php";
}
/**
@@ -139,16 +137,10 @@ class ExceptionHandler {
static public function check_for_fatal()
{
$error = error_get_last();
- if ($error["type"] == E_ERROR) {
+ if (isset($error) && $error["type"] == E_ERROR) {
self::exception_handler(new \ErrorException(
$error["message"], 0, $error["type"], $error["file"], $error["line"]));
}
}
- static public function assert_failure($file, $line, $expr, $message = "")
- {
- self::exception_handler(new Exception("assert($expr): Assertion failed in $file at line $line".($message != "" ? " with message: '$message'" : "")));
- exit(1);
- }
-
}
diff --git a/application/libraries/Image/Drivers/GD.php b/application/libraries/Image/Drivers/GD.php
index e2e0a99be..2aecd18ca 100644
--- a/application/libraries/Image/Drivers/GD.php
+++ b/application/libraries/Image/Drivers/GD.php
@@ -128,8 +128,8 @@ class GD implements \libraries\Image\ImageDriver {
$this->resize($temp_width, $temp_height);
- $x0 = ($temp_width - $target_width) / 2;
- $y0 = ($temp_height - $target_height) / 2;
+ $x0 = floor(($temp_width - $target_width) / 2);
+ $y0 = floor(($temp_height - $target_height) / 2);
$this->crop($x0, $y0, $target_width, $target_height);
$this->apply_exif_orientation();
diff --git a/application/libraries/Image/Drivers/imagemagick.php b/application/libraries/Image/Drivers/imagemagick.php
index 8295469eb..e65d05d3e 100644
--- a/application/libraries/Image/Drivers/imagemagick.php
+++ b/application/libraries/Image/Drivers/imagemagick.php
@@ -18,8 +18,7 @@ class imagemagick implements \libraries\Image\ImageDriver {
$mimetype = $mimetype;
$base = explode("/", $mimetype)[0];
- if ($base == "image"
- || in_array($mimetype, array("application/pdf"))) {
+ if ($base == "image") {
return 100;
}
@@ -81,14 +80,14 @@ class imagemagick implements \libraries\Image\ImageDriver {
public function resize($width, $height)
{
$this->arguments[] = "-resize";
- $this->arguments[] = "${width}x${height}";
+ $this->arguments[] = "{$width}x{$height}";
}
public function crop($x, $y, $width, $height)
{
$this->arguments[] = "+repage";
$this->arguments[] = "-crop";
- $this->arguments[] = "${width}x${height}+${x}+${y}";
+ $this->arguments[] = "{$width}x{$height}+{$x}+{$y}";
$this->arguments[] = "+repage";
}
@@ -101,11 +100,11 @@ class imagemagick implements \libraries\Image\ImageDriver {
$this->apply_exif_orientation();
$this->arguments[] = "-thumbnail";
- $this->arguments[] = "${target_width}x${target_height}^";
+ $this->arguments[] = "{$target_width}x{$target_height}^";
$this->arguments[] = "-gravity";
$this->arguments[] = "center";
$this->arguments[] = "-extent";
- $this->arguments[] = "${target_width}x${target_height}^";
+ $this->arguments[] = "{$target_width}x{$target_height}^";
}
public function apply_exif_orientation()
diff --git a/application/libraries/Pygments.php b/application/libraries/Pygments.php
index e96b84258..4a771c08f 100644
--- a/application/libraries/Pygments.php
+++ b/application/libraries/Pygments.php
@@ -38,6 +38,9 @@ class Pygments {
$last_desc = "";
foreach (self::get_pygments_info() as $lexer) {
+ if (empty($lexer['names'])) {
+ continue;
+ }
$desc = $lexer['fullname'];
$name = $lexer['names'][0];
if ($desc == $last_desc) {
@@ -189,16 +192,20 @@ class Pygments {
$extensionarray = array(
'awk' => 'awk',
+ 'cast' => 'asciinema',
'c' => 'c',
'coffee' => 'coffee-script',
'cpp' => 'cpp',
+ 'cr' => 'crystal',
'diff' => 'diff',
+ 'go' => 'go',
'haml' => 'haml',
'h' => 'c',
'hs' => 'haskell',
'html' => 'xml',
'java' => 'java',
'js' => 'js',
+ 'json' => 'json',
'lhs' => 'lhs',
'lua' => 'lua',
'mli' => 'ocaml',
@@ -211,10 +218,12 @@ class Pygments {
'php' => 'php',
'pl' => 'perl',
'plpgsql' => 'plpgsql',
+ 'pm' => 'perl',
'postgresql' => 'postgresql',
'pp' => 'puppet',
'py' => 'python',
'rb' => 'ruby',
+ 'rs' => 'rust',
's' => 'asm',
'sh' => 'bash',
'sql' => 'sql',
diff --git a/application/libraries/index.html b/application/libraries/index.html
index c942a79ce..bcb7cae34 100644
--- a/application/libraries/index.html
+++ b/application/libraries/index.html
@@ -1,4 +1,5 @@
-<html>
+<!DOCTYPE html>
+<html lang="en">
<head>
<title>403 Forbidden</title>
</head>
@@ -7,4 +8,4 @@
<p>Directory access is forbidden.</p>
</body>
-</html> \ No newline at end of file
+</html>
diff --git a/application/logs/index.html b/application/logs/index.html
index c942a79ce..bcb7cae34 100644
--- a/application/logs/index.html
+++ b/application/logs/index.html
@@ -1,4 +1,5 @@
-<html>
+<!DOCTYPE html>
+<html lang="en">
<head>
<title>403 Forbidden</title>
</head>
@@ -7,4 +8,4 @@
<p>Directory access is forbidden.</p>
</body>
-</html> \ No newline at end of file
+</html>
diff --git a/application/migrations/019_change_filesize_type.php b/application/migrations/019_change_filesize_type.php
new file mode 100644
index 000000000..33abf89ed
--- /dev/null
+++ b/application/migrations/019_change_filesize_type.php
@@ -0,0 +1,51 @@
+<?php
+defined('BASEPATH') OR exit('No direct script access allowed');
+
+class Migration_change_filesize_type extends CI_Migration {
+
+ public function up()
+ {
+ $prefix = $this->db->dbprefix;
+
+ if ($this->db->dbdriver == 'postgre') {
+ $this->db->query('
+ ALTER TABLE "'.$prefix.'file_storage"
+ ALTER "filesize" TYPE bigint;
+ ');
+ } else {
+ $this->db->query('
+ ALTER TABLE `'.$prefix.'file_storage`
+ MODIFY `filesize` bigint;
+ ');
+ }
+
+ $chunk = 500;
+
+ $this->db->where('filesize', 2147483647);
+ $total = $this->db->count_all_results("file_storage");
+
+ for ($limit = 0; $limit < $total; $limit += $chunk) {
+ $query = $this->db->select('hash, id')
+ ->from('file_storage')
+ ->where('filesize', 2147483647)
+ ->limit($chunk, $limit)
+ ->get()->result_array();
+
+ foreach ($query as $key => $item) {
+ $data_id = $item["hash"].'-'.$item['id'];
+ $filesize = filesize($this->mfile->file($data_id));
+
+ $this->db->where('id', $item['id'])
+ ->set(array(
+ 'filesize' => $filesize,
+ ))
+ ->update('file_storage');
+ }
+ }
+ }
+
+ public function down()
+ {
+ throw new \exceptions\ApiException("migration/downgrade-not-supported", "downgrade not supported");
+ }
+}
diff --git a/application/migrations/020_update_session_table.php b/application/migrations/020_update_session_table.php
new file mode 100644
index 000000000..94a240def
--- /dev/null
+++ b/application/migrations/020_update_session_table.php
@@ -0,0 +1,45 @@
+<?php
+defined('BASEPATH') OR exit('No direct script access allowed');
+
+class Migration_update_session_table extends CI_Migration {
+
+ public function up()
+ {
+ $prefix = $this->db->dbprefix;
+
+ if ($this->db->dbdriver == 'postgre') {
+ $this->db->query('
+ ALTER TABLE "'.$prefix.'ci_sessions"
+ DROP COLUMN "user_agent";
+ ');
+ $this->db->query('
+ ALTER TABLE "'.$prefix.'ci_sessions"
+ RENAME COLUMN "session_id" TO "id";
+ ');
+ $this->db->query('
+ ALTER TABLE "'.$prefix.'ci_sessions"
+ RENAME COLUMN "last_activity" TO "timestamp";
+ ');
+ $this->db->query('
+ ALTER TABLE "'.$prefix.'ci_sessions"
+ RENAME COLUMN "user_data" TO "data";
+ ');
+ $this->db->query('
+ ALTER TABLE "'.$prefix.'ci_sessions" ALTER COLUMN id SET DATA TYPE varchar(128);
+ ');
+ } else {
+ $this->db->query('
+ ALTER TABLE `'.$prefix.'ci_sessions`
+ DROP `user_agent`,
+ CHANGE `session_id` `id` VARCHAR(128) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL,
+ CHANGE `last_activity` `timestamp` INT(10) UNSIGNED NOT NULL DEFAULT 0,
+ CHANGE `user_data` `data` BLOB NOT NULL;
+ ');
+ }
+ }
+
+ public function down()
+ {
+ throw new \exceptions\ApiException("migration/downgrade-not-supported", "downgrade not supported");
+ }
+}
diff --git a/application/migrations/021_change_charset.php b/application/migrations/021_change_charset.php
new file mode 100644
index 000000000..475732ed5
--- /dev/null
+++ b/application/migrations/021_change_charset.php
@@ -0,0 +1,28 @@
+<?php
+defined('BASEPATH') OR exit('No direct script access allowed');
+
+class Migration_change_charset extends CI_Migration {
+
+ public function up()
+ {
+ $prefix = $this->db->dbprefix;
+
+ if ($this->db->dbdriver == 'postgre') {
+ # nothing to do
+ } else {
+ $this->db->query('SET FOREIGN_KEY_CHECKS = 0');
+ foreach ([
+ ['apikeys', 'comment', 'VARCHAR(255)'],
+ ['files', 'filename', 'VARCHAR(256)'],
+ ] as $col) {
+ $this->db->query('ALTER TABLE `'.$prefix.$col[0].'` CHANGE `'.$col[1].'` `'.$col[1].'` '.$col[2].' CHARACTER SET utf8mb4 COLLATE utf8mb4_bin;');
+ }
+ $this->db->query('SET FOREIGN_KEY_CHECKS = 1');
+ }
+ }
+
+ public function down()
+ {
+ throw new \exceptions\ApiException("migration/downgrade-not-supported", "downgrade not supported");
+ }
+}
diff --git a/application/models/mfile.php b/application/models/Mfile.php
index 977240c89..b9bb67d3c 100644
--- a/application/models/mfile.php
+++ b/application/models/Mfile.php
@@ -10,10 +10,10 @@
class Mfile extends CI_Model {
private $upload_path;
+ private $id_validation_config;
function __construct()
{
- parent::__construct();
$this->load->model("muser");
$this->upload_path = $this->config->item('upload_path');
@@ -258,7 +258,7 @@ class Mfile extends CI_Model {
rmdir($dir);
}
}
- delete_cache("${data_id}_thumb_150");
+ delete_cache("{$data_id}_thumb_150");
}
public function get_owner($id)
diff --git a/application/models/mmultipaste.php b/application/models/Mmultipaste.php
index 52ea4dfb4..014dc4d61 100644
--- a/application/models/mmultipaste.php
+++ b/application/models/Mmultipaste.php
@@ -11,7 +11,6 @@ class Mmultipaste extends CI_Model {
function __construct()
{
- parent::__construct();
$this->load->model("muser");
$this->load->model("mfile");
}
diff --git a/application/models/muser.php b/application/models/Muser.php
index 1ee6c259a..1d8e97d14 100644
--- a/application/models/muser.php
+++ b/application/models/Muser.php
@@ -19,8 +19,6 @@ class Muser extends CI_Model {
function __construct()
{
- parent::__construct();
-
$this->load->helper("filebin");
$this->load->driver("duser");
$this->hashalgo = $this->config->item('auth_db')['hashing_algorithm'];
@@ -156,22 +154,21 @@ class Muser extends CI_Model {
*/
public function valid_email($email)
{
- $this->load->helper("email");
- return valid_email($email);
+ return $email === filter_var($email, FILTER_VALIDATE_EMAIL);
}
public function add_user($username, $password, $email, $referrer)
{
if (!$this->valid_username($username)) {
- throw new \exceptions\PublicApiException("user/invalid-username", "Invalid username (only up to 32 chars of a-z0-9 are allowed)");
+ throw new \exceptions\UserInputException("user/invalid-username", "Invalid username (only up to 32 chars of a-z0-9 are allowed)");
} else {
if ($this->muser->username_exists($username)) {
- throw new \exceptions\PublicApiException("user/username-already-exists", "Username already exists");
+ throw new \exceptions\UserInputException("user/username-already-exists", "Username already exists");
}
}
if (!$this->valid_email($email)) {
- throw new \exceptions\PublicApiException("user/invalid-email", "Invalid email");
+ throw new \exceptions\UserInputException("user/invalid-email", "Invalid email");
}
$this->db->set(array(
@@ -195,35 +192,48 @@ class Muser extends CI_Model {
$this->duser->require_implemented("can_delete_account");
if ($this->duser->test_login_credentials($username, $password)) {
- $userid = $this->get_userid_by_name($username);
- assert($userid !== null);
-
- $this->db->delete('profiles', array('user' => $userid));
-
- $this->load->model("mfile");
- $this->load->model("mmultipaste");
- $this->mfile->delete_by_user($userid);
- $this->mmultipaste->delete_by_user($userid);
-
- # null out user data to keep referer information traceable
- # If referer information was relinked, one user could create many
- # accounts, delete the account that was used to invite them and
- # then cause trouble so that the account that invited him gets
- # banned because the admin thinks that account invited abusers
- $this->db->set(array(
- 'username' => null,
- 'password' => null,
- 'email' => null,
- ))
- ->where(array('username' => $username))
- ->update('users');
-
+ $this->delete_user_real($username);
return true;
}
return false;
}
+ /**
+ * Delete a user
+ *
+ * @param username
+ * @return void
+ */
+ public function delete_user_real($username)
+ {
+ $this->duser->require_implemented("can_delete_account");
+ $userid = $this->get_userid_by_name($username);
+ if ($userid === null) {
+ throw new \exceptions\ApiException("user/delete", "User cannot be found", ["username" => $username]);
+ }
+
+ $this->db->delete('profiles', array('user' => $userid));
+
+ $this->load->model("mfile");
+ $this->load->model("mmultipaste");
+ $this->mfile->delete_by_user($userid);
+ $this->mmultipaste->delete_by_user($userid);
+
+ # null out user data to keep referer information traceable
+ # If referer information was relinked, one user could create many
+ # accounts, delete the account that was used to invite them and
+ # then cause trouble so that the account that invited him gets
+ # banned because the admin thinks that account invited abusers
+ $this->db->set(array(
+ 'username' => null,
+ 'password' => null,
+ 'email' => null,
+ ))
+ ->where(array('username' => $username))
+ ->update('users');
+ }
+
function get_userid()
{
if (!$this->logged_in()) {
@@ -276,7 +286,7 @@ class Muser extends CI_Model {
function require_access($wanted_level = "full")
{
- if ($this->input->post("apikey") !== false) {
+ if ($this->input->post("apikey") !== null) {
$this->apilogin($this->input->post("apikey"));
}
@@ -324,6 +334,10 @@ class Muser extends CI_Model {
->where('user', $userid)
->get()->row_array();
+ if ($query === null) {
+ $query = [];
+ }
+
$extra_fields = array(
"username" => $this->get_username(),
"email" => $this->get_email($userid),
diff --git a/application/models/index.html b/application/models/index.html
index c942a79ce..bcb7cae34 100644
--- a/application/models/index.html
+++ b/application/models/index.html
@@ -1,4 +1,5 @@
-<html>
+<!DOCTYPE html>
+<html lang="en">
<head>
<title>403 Forbidden</title>
</head>
@@ -7,4 +8,4 @@
<p>Directory access is forbidden.</p>
</body>
-</html> \ No newline at end of file
+</html>
diff --git a/application/service/files.php b/application/service/files.php
index b45ac8e6f..576adb2ef 100644
--- a/application/service/files.php
+++ b/application/service/files.php
@@ -150,6 +150,7 @@ class files {
$hash = md5_file($new_file);
$storage_id = null;
+ $CI->db->trans_start();
$query = $CI->db->select('id, hash')
->from('file_storage')
->where('hash', $hash)
@@ -165,6 +166,7 @@ class files {
}
}
+ $new_storage_id_created = false;
if ($storage_id === null) {
$filesize = filesize($new_file);
$mimetype = mimetype($new_file);
@@ -176,21 +178,33 @@ class files {
"date" => time(),
));
$storage_id = $CI->db->insert_id();
+ $new_storage_id_created = true;
+ assert(!file_exists($CI->mfile->file($hash."-".$storage_id)));
}
$data_id = $hash."-".$storage_id;
- // TODO: all this doesn't have to run if the file exists. updating the mtime would be enough
- // that would also be better for COW filesystems
$dir = $CI->mfile->folder($data_id);
file_exists($dir) || mkdir ($dir);
$new_path = $CI->mfile->file($data_id);
- $dest = new \service\storage($new_path);
- $tmpfile = $dest->begin();
- rename($new_file, $tmpfile);
- $dest->commit();
+ // Update mtime for cronjob
+ touch($new_path);
+
+ // touch may create a new file if the cronjob cleaned up in between the db check and here.
+ // In that case the file will be empty so move in the data
+ if ($new_storage_id_created || filesize($new_path) === 0) {
+ $dest = new \service\storage($new_path);
+ $tmpfile = $dest->begin();
+
+ // $new_file may reside on a different file system so this call
+ // could perform a copy operation internally. $dest->commit() will
+ // ensure that it performs an atomic overwrite (rename).
+ rename($new_file, $tmpfile);
+ $dest->commit();
+ }
$CI->mfile->add_file($userid, $id, $filename, $storage_id);
+ $CI->db->trans_complete();
}
static public function verify_uploaded_files($files)
@@ -228,7 +242,7 @@ class files {
}
}
- $filesize = filesize($file['tmp_name']);
+ $filesize = isset($file['tmp_name']) ? filesize($file['tmp_name']) : 0;
if ($filesize > $CI->config->item('upload_max_size')) {
$error_message = "File too big";
}
@@ -425,17 +439,115 @@ class files {
// likely unsupported filetype
}
- $tooltip = "${filedata["id"]} - $filesize<br>";
+ $tooltip = "{$filedata["id"]} - $filesize<br>";
$tooltip .= "$upload_date<br>";
if ($height > 0 && $width > 0) {
- $tooltip .= "${width}x${height} - ${filedata["mimetype"]}<br>";
+ $tooltip .= "{$width}x{$height} - {$filedata["mimetype"]}<br>";
} else {
- $tooltip .= "${filedata["mimetype"]}<br>";
+ $tooltip .= "{$filedata["mimetype"]}<br>";
}
return $tooltip;
}
+ static public function clean_multipaste_tarballs()
+ {
+ $CI =& get_instance();
+
+ $tarball_dir = $CI->config->item("upload_path")."/special/multipaste-tarballs";
+ if (is_dir($tarball_dir)) {
+ $tarball_cache_time = $CI->config->item("tarball_cache_time");
+ $it = new \RecursiveIteratorIterator(
+ new \RecursiveDirectoryIterator($tarball_dir), \RecursiveIteratorIterator::SELF_FIRST);
+
+ foreach ($it as $file) {
+ if ($file->isFile()) {
+ if ($file->getMTime() < time() - $tarball_cache_time) {
+ $lock = fopen($file, "r+");
+ flock($lock, LOCK_EX);
+ unlink($file);
+ flock($lock, LOCK_UN);
+ }
+ }
+ }
+ }
+ }
+
+ static public function remove_files_missing_in_db()
+ {
+ $CI =& get_instance();
+
+ $upload_path = $CI->config->item("upload_path");
+ $outer_dh = opendir($upload_path);
+
+ while (($dir = readdir($outer_dh)) !== false) {
+ if (!is_dir($upload_path."/".$dir) || $dir == ".." || $dir == "." || $dir == "special") {
+ continue;
+ }
+
+ $dh = opendir($upload_path."/".$dir);
+
+ $empty = true;
+
+ while (($file = readdir($dh)) !== false) {
+ if ($file == ".." || $file == ".") {
+ continue;
+ }
+
+ try {
+ list($hash, $storage_id) = explode("-", $file);
+ } catch (\ErrorException $e) {
+ unlink($upload_path."/".$dir."/".$file);
+ continue;
+ }
+
+ $query = $CI->db->select('hash, id')
+ ->from('file_storage')
+ ->where('hash', $hash)
+ ->where('id', $storage_id)
+ ->limit(1)
+ ->get()->row_array();
+
+ if (empty($query)) {
+ $CI->mfile->delete_data_id($file);
+ } else {
+ $empty = false;
+ }
+ }
+
+ closedir($dh);
+
+ if ($empty && file_exists($upload_path."/".$dir)) {
+ rmdir($upload_path."/".$dir);
+ }
+ }
+ closedir($outer_dh);
+ }
+
+ static public function remove_files_missing_on_disk()
+ {
+ $CI =& get_instance();
+
+ $chunk = 500;
+ $total = $CI->db->count_all("file_storage");
+
+ for ($limit = 0; $limit < $total; $limit += $chunk) {
+ $query = $CI->db->select('hash, id')
+ ->from('file_storage')
+ ->limit($chunk, $limit)
+ ->get()->result_array();
+
+ foreach ($query as $key => $item) {
+ $data_id = $item["hash"].'-'.$item['id'];
+ $file = $CI->mfile->file($data_id);
+
+ if (!$CI->mfile->file_exists($file)) {
+ $CI->mfile->delete_data_id($data_id);
+ }
+ }
+ }
+ }
+
}
diff --git a/application/service/multipaste_queue.php b/application/service/multipaste_queue.php
index 453ea3429..ff202366c 100644
--- a/application/service/multipaste_queue.php
+++ b/application/service/multipaste_queue.php
@@ -11,6 +11,10 @@ namespace service;
class multipaste_queue {
+ private $session;
+ private $mfile;
+ private $mmultipaste;
+
public function __construct($session = null, $mfile = null, $mmultipaste = null) {
$CI =& get_instance();
@@ -73,7 +77,7 @@ class multipaste_queue {
*/
public function get() {
$ids = $this->session->userdata("multipaste_queue");
- if ($ids === false) {
+ if ($ids === NULL) {
$ids = [];
}
diff --git a/application/service/renderer.php b/application/service/renderer.php
index 6f57e7458..325b4f1f7 100644
--- a/application/service/renderer.php
+++ b/application/service/renderer.php
@@ -10,6 +10,9 @@
namespace service;
class renderer {
+ private $output_cache;
+ private $mfile;
+ private $data;
/**
* @param $output_cache output cache object
@@ -158,23 +161,29 @@ class renderer {
*/
private function reformat_json($lexer, $linecount, $content)
{
- if ($lexer === "json" && $linecount === 1) {
- $decoded_json = json_decode($content);
- if ($decoded_json !== null && $decoded_json !== false) {
- $pretty_json = json_encode($decoded_json, JSON_PRETTY_PRINT);
- if ($pretty_json !== false) {
- $content = $pretty_json;
- $this->output_cache->render_now(
- array(
- "error_type" => "alert-info",
- "error_message" => "<p>The file below has been reformated for readability. It may differ from the original.</p>"
- ),
- "file/fragments/alert-wide"
- );
- }
- }
+ if ($lexer !== "json" || $linecount !== 1) {
+ return $content;
+ }
+
+ $decoded_json = json_decode($content);
+ if ($decoded_json === null || $decoded_json === false) {
+ return $content;
}
- return $content;
+
+ $pretty_json = json_encode($decoded_json, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES);
+ if ($pretty_json === false) {
+ return $content;
+ }
+
+ $this->output_cache->render_now(
+ array(
+ "error_type" => "alert-info",
+ "error_message" => "<p>The file below has been reformated for readability. It may differ from the original.</p>"
+ ),
+ "file/fragments/alert-wide"
+ );
+
+ return $pretty_json;
}
diff --git a/application/service/user.php b/application/service/user.php
index 1d922a102..1d678106a 100644
--- a/application/service/user.php
+++ b/application/service/user.php
@@ -77,4 +77,52 @@ class user {
"apikeys" => $ret,
);
}
+
+ /**
+ * Create an invitation key for a user
+ * @param userid id of the user
+ * @return key the created invitation key
+ */
+ static public function create_invitation_key($userid) {
+ $CI =& get_instance();
+
+ $invitations = $CI->db->select('user')
+ ->from('actions')
+ ->where('user', $userid)
+ ->where('action', 'invitation')
+ ->count_all_results();
+
+ if ($invitations + 1 > $CI->config->item('max_invitation_keys')) {
+ throw new \exceptions\InsufficientPermissionsException("user/invitation-limit", "You can't create more invitation keys at this time.");
+ }
+
+ $key = random_alphanum(12, 16);
+
+ $CI->db->set(array(
+ 'key' => $key,
+ 'user' => $userid,
+ 'date' => time(),
+ 'action' => 'invitation'
+ ))
+ ->insert('actions');
+
+ return $key;
+ }
+
+ /**
+ * Remove an invitation key belonging to a user
+ * @param userid id of the user
+ * @param key key to remove
+ * @return number of removed keys
+ */
+ static public function delete_invitation_key($userid, $key) {
+ $CI =& get_instance();
+
+ $CI->db
+ ->where('key', $key)
+ ->where('user', $userid)
+ ->delete('actions');
+
+ return $CI->db->affected_rows();
+ }
}
diff --git a/application/test/tests/api_v2/test_api_permissions.php b/application/test/tests/api_v2/test_api_permissions.php
index 6df612911..281457c45 100644
--- a/application/test/tests/api_v2/test_api_permissions.php
+++ b/application/test/tests/api_v2/test_api_permissions.php
@@ -99,7 +99,7 @@ class test_api_permissions extends common {
$this->t->is_deeply(array(
'status' => "error",
'error_id' => "api/insufficient-permissions",
- 'message' => "Access denied: Access level too low. Required: ${test['wanted_level']}; Have: ${test['have_level']}",
+ 'message' => "Access denied: Access level too low. Required: {$test['wanted_level']}; Have: {$test['have_level']}",
), $ret, "expected permission error");
}
}
diff --git a/application/test/tests/api_v2/test_file_create_multipaste.php b/application/test/tests/api_v2/test_file_create_multipaste.php
index 8556616d1..2b6e9d8de 100644
--- a/application/test/tests/api_v2/test_file_create_multipaste.php
+++ b/application/test/tests/api_v2/test_file_create_multipaste.php
@@ -122,4 +122,27 @@ class test_file_create_multipaste extends common {
$this->t->is($ret["data"]["total_count"], 1, "total_count correct");
$this->t->is($ret["data"]["deleted_count"], 1, "deleted_count correct");
}
+
+ public function test_create_multipaste_minidlength()
+ {
+ $apikey = $this->createUserAndApikey("basic");
+ $ret = $this->uploadFile($apikey, "data/tests/small-file");
+ $id = $ret["data"]["ids"][0];
+
+ $ret = $this->uploadFile($apikey, "data/tests/small-file");
+ $id2 = $ret["data"]["ids"][0];
+
+ $ret = $this->CallEndpoint("POST", "file/create_multipaste", array(
+ "apikey" => $apikey,
+ "ids[1]" => $id,
+ "ids[2]" => $id2,
+ "minimum-id-length" => 42,
+ ));
+ $this->expectSuccess("create multipaste", $ret);
+
+ $this->t->isnt($ret["data"]["url_id"], "", "got a multipaste ID");
+ $this->t->isnt($ret["data"]["url"], "", "got a multipaste URL");
+
+ $this->t->ok(strlen($ret["data"]["url_id"]) >= 42, "minimum url length upheld");
+ }
}
diff --git a/application/test/tests/api_v2/test_file_upload.php b/application/test/tests/api_v2/test_file_upload.php
index cb2f81b74..07769774f 100644
--- a/application/test/tests/api_v2/test_file_upload.php
+++ b/application/test/tests/api_v2/test_file_upload.php
@@ -50,8 +50,8 @@ class test_file_upload extends common {
$data[] = $this->SendHTTPRequest("GET", $url, '');
}
$this->t->ok($data[0] !== $data[1], 'Returned file contents should differ');
- $this->t->ok($data[0] === file_get_contents("data/tests/message1.bin"), "Returned correct data for file 1");
- $this->t->ok($data[1] === file_get_contents("data/tests/message2.bin"), "Returned correct data for file 2");
+ $this->t->is($data[0], file_get_contents("data/tests/message1.bin"), "Returned correct data for file 1");
+ $this->t->is($data[1], file_get_contents("data/tests/message2.bin"), "Returned correct data for file 2");
}
public function test_upload_uploadNothing()
@@ -68,4 +68,45 @@ class test_file_upload extends common {
), $ret, "expected reply");
}
+ public function test_upload_minidlength()
+ {
+ $apikey = $this->createUserAndApikey();
+ $ret = $this->CallEndpoint("POST", "file/upload", array(
+ "apikey" => $apikey,
+ "file[1]" => curl_file_create("data/tests/small-file"),
+ "minimum-id-length" => 42,
+ ));
+ $this->expectSuccess("upload file", $ret);
+
+ foreach ($ret["data"]["urls"] as $url) {
+ $matches = array();
+ preg_match('/\/([^\/]+)\/$/', $url, $matches);
+ $this->t->ok(strlen($matches[1]) >= 42, "minimum url length upheld");
+ }
+ }
+
+ public function test_upload_bad_minidlength()
+ {
+ $apikey = $this->createUserAndApikey();
+
+ $combinations = [
+ "non-numberic minimum-id-length" => "nonumber",
+ "negative minimum-id-length (-42)" => -42,
+ "minimum-id-length=0" => 0,
+ "minimum-id-length=1" => 1,
+ ];
+ foreach ($combinations as $msg => $input) {
+ $ret = $this->CallEndpoint("POST", "file/upload", array(
+ "apikey" => $apikey,
+ "file[1]" => curl_file_create("data/tests/small-file"),
+ "minimum-id-length" => $input,
+ ));
+ $this->expectError("upload file with bad minimum-id-length. Test value: $msg", $ret);
+ $this->t->is_deeply(array(
+ 'status' => 'error',
+ 'error_id' => 'file/bad-minimum-id-length',
+ 'message' => "Passed parameter 'minimum-id-length' is not a valid integer or too small (min value: 2)",
+ ), $ret, "expected reply");
+ }
+ }
}
diff --git a/application/test/tests/test_database_schema.php b/application/test/tests/test_database_schema.php
new file mode 100644
index 000000000..02f188e1f
--- /dev/null
+++ b/application/test/tests/test_database_schema.php
@@ -0,0 +1,38 @@
+<?php
+/*
+ * Copyright 2017 Florian "Bluewind" Pritz <bluewind@server-speed.net>
+ *
+ * Licensed under AGPLv3
+ * (see COPYING for full license text)
+ *
+ */
+
+namespace test\tests;
+
+class test_database_schema extends \test\Test {
+
+ public function __construct()
+ {
+ parent::__construct();
+ }
+
+ public function test_file_storage_bigint() {
+ $filesize = pow(2, 35) + 1;
+
+ $CI =& get_instance();
+ $CI->db->insert("file_storage", array(
+ "filesize" => $filesize,
+ "mimetype" => "text/plain",
+ "hash" => md5("test"),
+ "date" => time(),
+ ));
+ $id = $CI->db->insert_id();
+ $db_value = $CI->db->select('filesize')
+ ->from('file_storage')
+ ->where('id', $id)
+ ->get()->result_array()[0]["filesize"];
+ $this->t->is(intval($db_value), $filesize, "Large filesize is stored correctly in db");
+ }
+
+
+}
diff --git a/application/test/tests/test_filebin_helper.php b/application/test/tests/test_filebin_helper.php
index 2069f2953..a46d4bc3c 100644
--- a/application/test/tests/test_filebin_helper.php
+++ b/application/test/tests/test_filebin_helper.php
@@ -24,6 +24,51 @@ class test_filebin_helper extends \test\Test {
{
}
+ public function test_expiration_duration()
+ {
+ $this->t->is(expiration_duration(60*60*24*2), "2 days", "2 days");
+ $this->t->is(expiration_duration(60*60*24), "1 day", "1 day");
+ $this->t->is(expiration_duration(60*60*2), "2 hours", "2 hours");
+ $this->t->is(expiration_duration(60*60), "1 hour", "1 hour");
+ $this->t->is(expiration_duration(60*2), "2 minutes", "2 minutes");
+ $this->t->is(expiration_duration(60), "1 minute", "1 minute");
+ $this->t->is(expiration_duration(59), "59 seconds", "59 seconds");
+ $this->t->is(expiration_duration(1), "1 second", "1 second");
+
+ $this->t->is(expiration_duration(60*60*24 + 60*60 + 60), "1 day, 1 hour, 1 minute", "1 day, 1 hour, 1 minute");
+ $this->t->is(expiration_duration(60*60*24 + 60*60 + 120), "1 day, 1 hour, 2 minutes", "1 day, 1 hour, 2 minutes");
+ $this->t->is(expiration_duration(60*60*24 + 60*60*2 + 60), "1 day, 2 hours, 1 minute", "1 day, 2 hours, 1 minute");
+ $this->t->is(expiration_duration(60*60*24 + 60*60*2 + 120), "1 day, 2 hours, 2 minutes", "1 day, 2 hours, 2 minutes");
+ $this->t->is(expiration_duration(60*60*24*2 + 60*60 + 60), "2 days, 1 hour, 1 minute", "2 days, 1 hour, 1 minute");
+ $this->t->is(expiration_duration(60*60*24*2 + 60*60 + 120), "2 days, 1 hour, 2 minutes", "2 days, 1 hour, 2 minutes");
+ $this->t->is(expiration_duration(60*60*24*2 + 60*60*2 + 60), "2 days, 2 hours, 1 minute", "2 days, 2 hours, 1 minute");
+ $this->t->is(expiration_duration(60*60*24*2 + 60*60*2 + 120), "2 days, 2 hours, 2 minutes", "2 days, 2 hours, 2 minutes");
+
+ $this->t->is(expiration_duration(60*60*24 + 60*60), "1 day, 1 hour", "1 day, 1 hour");
+ $this->t->is(expiration_duration(60*60*24 + 60*60*2), "1 day, 2 hours", "1 day, 2 hours");
+ $this->t->is(expiration_duration(60*60*24*2 + 60*60), "2 days, 1 hour", "2 days, 1 hour");
+ $this->t->is(expiration_duration(60*60*24*2 + 60*60*2), "2 days, 2 hours", "2 days, 2 hours");
+
+ $this->t->is(expiration_duration(60*60*24 + 60), "1 day, 1 minute", "1 day, 1 minute");
+ $this->t->is(expiration_duration(60*60*24 + 120), "1 day, 2 minutes", "1 day, 2 minutes");
+ $this->t->is(expiration_duration(60*60*24*2 + 60), "2 days, 1 minute", "2 days, 1 minute");
+ $this->t->is(expiration_duration(60*60*2*24 + 120), "2 days, 2 minutes", "2 days, 2 minutes");
+
+ $this->t->is(expiration_duration(60*60 + 60), "1 hour, 1 minute", "1 hour, 1 minute");
+ $this->t->is(expiration_duration(60*60 + 120), "1 hour, 2 minutes", "1 hour, 2 minutes");
+ $this->t->is(expiration_duration(60*60*2 + 60), "2 hours, 1 minute", "2 hours, 1 minute");
+ $this->t->is(expiration_duration(60*60*2 + 120), "2 hours, 2 minutes", "2 hours, 2 minutes");
+
+ $this->t->is(expiration_duration(61), "1 minute, 1 second", "1 minute, 1 second");
+ $this->t->is(expiration_duration(62), "1 minute, 2 seconds", "1 minute, 2 seconds");
+ $this->t->is(expiration_duration(121), "2 minutes, 1 second", "2 minutes, 1 second");
+ $this->t->is(expiration_duration(122), "2 minutes, 2 seconds", "2 minutes, 2 seconds");
+
+ $this->t->is(expiration_duration(60*60*24 + 60*60*23 + 60*59), "1 day, 23 hours, 59 minutes", "1 day, 23 hours, 59 minutes");
+ $this->t->is(expiration_duration(60*60*23 + 60*59), "23 hours, 59 minutes", "23 hours, 59 minutes");
+ $this->t->is(expiration_duration(60*60*2 + 60*59), "2 hours, 59 minutes", "2 hours, 59 minutes");
+ }
+
public function test_format_bytes()
{
$this->t->is(format_bytes(500), "500B", "500B");
@@ -35,20 +80,6 @@ class test_filebin_helper extends \test\Test {
$this->t->is(format_bytes(1500*1024*1024*1024*1024*1024), "1500.00PiB", "1500.00PiB");
}
- public function test_even_odd()
- {
- $this->t->is(even_odd(true), "odd", "odd after reset");
- $this->t->is(even_odd(), "even", "even");
- $this->t->is(even_odd(), "odd", "odd");
- $this->t->is(even_odd(true), "odd", "odd after reset");
- }
-
- public function test_mb_str_pad()
- {
- $this->t->is(mb_str_pad('test', 6), 'test ', 'Simple test with length=6');
- $this->t->is(mb_str_pad('絫ö', 6), '絫ö ', 'UTF8 test with length=6');
- }
-
public function test_files_are_equal()
{
$a1 = FCPATH.'/data/tests/message1.bin';
diff --git a/application/test/tests/test_libraries_image.php b/application/test/tests/test_libraries_image.php
index 99a963dea..d6afc64df 100644
--- a/application/test/tests/test_libraries_image.php
+++ b/application/test/tests/test_libraries_image.php
@@ -28,8 +28,8 @@ class test_libraries_image extends \test\Test {
{
$this->t->is(\libraries\Image::type_supported('image/png'), true, 'image/png should be supported');
$this->t->is(\libraries\Image::type_supported('image/jpeg'), true, 'image/jpeg should be supported');
- $this->t->is(\libraries\Image::type_supported('application/pdf'), true, 'application/pdf should be supported');
+ $this->t->is(\libraries\Image::type_supported('application/pdf'), false, 'application/pdf should not be supported');
$this->t->is(\libraries\Image::type_supported('application/octet-stream'), false, 'application/octet-stream should not be supported');
$this->t->is(\libraries\Image::type_supported('text/plain'), false, 'text/plain should not be supported');
}
@@ -45,11 +45,21 @@ class test_libraries_image extends \test\Test {
public function test_makeThumb_PDF()
{
- $img = new \libraries\Image(FCPATH."/data/tests/simple.pdf");
- $img->makeThumb(150, 150);
- $thumb = $img->get(IMAGETYPE_JPEG);
-
- $this->t->ok($thumb !== "", "Got thumbnail");
+ try {
+ $img = new \libraries\Image(FCPATH."/data/tests/simple.pdf");
+ $this->t->fail("PDF should not be supported");
+ $img->makeThumb(150, 150);
+ $thumb = $img->get(IMAGETYPE_JPEG);
+ $this->t->ok($thumb !== "", "Got thumbnail");
+ } catch (\exceptions\PublicApiException $e) {
+ $correct_error = $e->get_error_id() == "libraries/Image/unsupported-image-type";
+ $this->t->ok($correct_error, "Should get exception");
+ if (!$correct_error) {
+ // @codeCoverageIgnoreStart
+ throw $e;
+ // @codeCoverageIgnoreEnd
+ }
+ }
}
public function test_makeThumb_binaryFile()
diff --git a/application/test/tests/test_libraries_procrunner.php b/application/test/tests/test_libraries_procrunner.php
index 4e6adf8dd..daac8a2bc 100644
--- a/application/test/tests/test_libraries_procrunner.php
+++ b/application/test/tests/test_libraries_procrunner.php
@@ -49,7 +49,11 @@ class test_libraries_procrunner extends \test\Test {
$p = new \libraries\ProcRunner(['thisCommandDoesNotExist']);
$ret = $p->exec();
- $this->t->is($ret['stderr'], "sh: thisCommandDoesNotExist: command not found\n", 'stderr should be empty');
+ if (PHP_MAJOR_VERSION >= 8) {
+ $this->t->is($ret['stderr'], "sh: line 1: thisCommandDoesNotExist: command not found\n", 'stderr should be empty');
+ } else {
+ $this->t->is($ret['stderr'], "sh: thisCommandDoesNotExist: command not found\n", 'stderr should be empty');
+ }
$this->t->is($ret['stdout'], '', 'stdout should be empty');
$this->t->is($ret['return_code'], 127, 'return code should be 127');
}
@@ -95,7 +99,7 @@ class test_libraries_procrunner extends \test\Test {
public function test_forbid_stderr()
{
- $p = new \libraries\ProcRunner(['python', 'thisDoesNotExist']);
+ $p = new \libraries\ProcRunner(['bash', '-c', 'echo "This is a test error message" >&2; exit 2;']);
$p->forbid_stderr();
try {
@@ -104,12 +108,12 @@ class test_libraries_procrunner extends \test\Test {
} catch (\exceptions\ApiException $e) {
$this->t->is($e->get_error_id(), 'procrunner/stderr', "correct exception triggered");
$this->t->is_deeply($e->get_data(), [
- "'python' 'thisDoesNotExist'",
+ "'bash' '-c' 'echo \"This is a test error message\" >&2; exit 2;'",
null,
[
'return_code' => 2,
'stdout' => '',
- 'stderr' => "python: can't open file 'thisDoesNotExist': [Errno 2] No such file or directory\n",
+ 'stderr' => "This is a test error message\n",
],
], "correct exception data");
}
diff --git a/application/test/tests/test_service_multipaste_queue.php b/application/test/tests/test_service_multipaste_queue.php
index 0427425a0..6bf078d97 100644
--- a/application/test/tests/test_service_multipaste_queue.php
+++ b/application/test/tests/test_service_multipaste_queue.php
@@ -11,6 +11,11 @@ namespace test\tests;
class test_service_multipaste_queue extends \test\Test {
+ private $session;
+ private $mfile;
+ private $mmultipaste;
+ private $m;
+
public function __construct()
{
parent::__construct();
@@ -38,7 +43,7 @@ class test_service_multipaste_queue extends \test\Test {
public function test_get()
{
- $this->session->shouldReceive('userdata')->with("multipaste_queue")->once()->andReturn(false);
+ $this->session->shouldReceive('userdata')->with("multipaste_queue")->once()->andReturn(null);
$this->t->is_deeply($this->m->get(), [], "Fresh queue is empty");
}
@@ -54,7 +59,7 @@ class test_service_multipaste_queue extends \test\Test {
public function test_append()
{
- $this->session->shouldReceive('userdata')->with("multipaste_queue")->once()->andReturn(false);
+ $this->session->shouldReceive('userdata')->with("multipaste_queue")->once()->andReturn(null);
$this->mfile->shouldReceive('valid_id')->with('abc')->times(2)->andReturn(true);
$this->session->shouldReceive('set_userdata')->with("multipaste_queue", ['abc'])->once();
$this->t->is($this->m->append(['abc']), null, "append([abc]) should succeed");
diff --git a/application/test/tests/test_service_user.php b/application/test/tests/test_service_user.php
new file mode 100644
index 000000000..d7e34a71b
--- /dev/null
+++ b/application/test/tests/test_service_user.php
@@ -0,0 +1,65 @@
+<?php
+/*
+ * Copyright 2018 Florian "Bluewind" Pritz <bluewind@server-speed.net>
+ *
+ * Licensed under AGPLv3
+ * (see COPYING for full license text)
+ *
+ */
+
+namespace test\tests;
+
+class test_service_user extends \test\Test {
+
+ public function __construct() {
+ parent::__construct();
+ }
+
+ public function init() {
+ }
+
+ public function cleanup() {
+ }
+
+ public function test_invitation_key_delete() {
+ $CI =& get_instance();
+
+ $userid = 1;
+
+ $result = $CI->db->select('user, key, action')->from('actions')->get()->result_array();
+ $this->t->is_deeply([], $result, "database contains no actions");
+
+ $key = \service\user::create_invitation_key($userid);
+
+ $result = $CI->db->select('user, key, action')->from('actions')->get()->result_array();
+ $this->t->is_deeply([['user' => "".$userid, 'key' => $key, 'action' => 'invitation']], $result, "database contains new key");
+
+ $ret = \service\user::delete_invitation_key($userid+1, $key);
+ $this->t->is(0, $ret, "Should have removed no keys because incorrect user/key");
+ $result = $CI->db->select('user, key, action')->from('actions')->get()->result_array();
+ $this->t->is_deeply([['user' => "".$userid, 'key' => $key, 'action' => 'invitation']], $result, "database contains new key after incorrect deletion");
+
+ $ret = \service\user::delete_invitation_key($userid+1, "foobar-");
+ $this->t->is(0, $ret, "Should have removed no keys because incorrect user/key");
+ $result = $CI->db->select('user, key, action')->from('actions')->get()->result_array();
+ $this->t->is_deeply([['user' => "".$userid, 'key' => $key, 'action' => 'invitation']], $result, "database contains new key after incorrect deletion");
+
+ $ret = \service\user::delete_invitation_key($userid+1, "");
+ $this->t->is(0, $ret, "Should have removed no keys because incorrect user/key");
+ $result = $CI->db->select('user, key, action')->from('actions')->get()->result_array();
+ $this->t->is_deeply([['user' => "".$userid, 'key' => $key, 'action' => 'invitation']], $result, "database contains new key after incorrect deletion");
+
+ $ret = \service\user::delete_invitation_key($userid, "");
+ $this->t->is(0, $ret, "Should have removed no keys because incorrect user/key");
+ $result = $CI->db->select('user, key, action')->from('actions')->get()->result_array();
+ $this->t->is_deeply([['user' => "".$userid, 'key' => $key, 'action' => 'invitation']], $result, "database contains new key");
+
+ $ret = \service\user::delete_invitation_key($userid, $key);
+ $this->t->is(1, $ret, "One key should be removed");
+ $result = $CI->db->select('user, key, action')->from('actions')->get()->result_array();
+ $this->t->is_deeply([], $result, "key has been deleted");
+
+ }
+
+}
+
diff --git a/application/third_party/index.html b/application/third_party/index.html
index c942a79ce..bcb7cae34 100644
--- a/application/third_party/index.html
+++ b/application/third_party/index.html
@@ -1,4 +1,5 @@
-<html>
+<!DOCTYPE html>
+<html lang="en">
<head>
<title>403 Forbidden</title>
</head>
@@ -7,4 +8,4 @@
<p>Directory access is forbidden.</p>
</body>
-</html> \ No newline at end of file
+</html>
diff --git a/application/third_party/mockery b/application/third_party/mockery
-Subproject d141c5b1b302d4d746e38ccc95ffe215129a855
+Subproject 20cab678faed06fac225193be281ea0fddb43b9
diff --git a/application/third_party/test-more-php/Test-More-OO.php b/application/third_party/test-more-php/Test-More-OO.php
index 005c7dc01..6020288bd 100755
--- a/application/third_party/test-more-php/Test-More-OO.php
+++ b/application/third_party/test-more-php/Test-More-OO.php
@@ -87,6 +87,11 @@ class TestMore extends TestSimple {
$ret = ob_get_clean();
$ret = preg_replace("/^[^\n]*\n/", "", $ret);
$ret = preg_replace("/\n$/", "", $ret);
+ # replace unprintable characters with questionmarks
+ $old = ini_get("mbstring.substitute_character");
+ ini_set("mbstring.substitute_character", "?");
+ $ret = mb_convert_encoding($ret, 'UTF-8', 'UTF-8');
+ ini_set("mbstring.substitute_character", $old);
return $ret;
}
@@ -210,7 +215,7 @@ class TestMore extends TestSimple {
if ( is_int($this->NumberOfTests) ) {
$unrun = $this->NumberOfTests - (int)$this->TestsRun;
$plural = $unrun == 1 ? '' : 's';
- $unrunmsg = "# Looks like ${unrun} planned test${plural} never ran.\n";
+ $unrunmsg = "# Looks like {$unrun} planned test{$plural} never ran.\n";
}
$gasp = $this->LastFail . "\n"
@@ -263,7 +268,7 @@ class TestMore extends TestSimple {
$error = " Syntax check for '$module' failed";
}
} else {
- $error = " Cannot find ${type}d file '$module'";
+ $error = " Cannot find {$type}d file '$module'";
}
$pass = !$retval && $done;
diff --git a/application/third_party/test-more-php/Test-Simple-OO.php b/application/third_party/test-more-php/Test-Simple-OO.php
index 9bbe4aada..a8302d208 100755
--- a/application/third_party/test-more-php/Test-Simple-OO.php
+++ b/application/third_party/test-more-php/Test-Simple-OO.php
@@ -73,6 +73,7 @@ class TestSimple {
protected $NumberOfTests;
protected $CurrentTestNumber;
protected $Filter;
+ protected $LastFail;
protected $notes;
@@ -103,7 +104,7 @@ class TestSimple {
$skipinfo = '';
if ($this->NumberOfTests === 'skip_all') $skipinfo = ' # '.$this->SkipAllReason;
- echo "1..${NumberOfTests}${skipinfo}\n";
+ echo "1..{$NumberOfTests}{$skipinfo}\n";
$this->NumberOfTests = $NumberOfTests;
return;
diff --git a/application/views/errors/cli/error_404.php b/application/views/errors/cli/error_404.php
new file mode 100644
index 000000000..6984b61e9
--- /dev/null
+++ b/application/views/errors/cli/error_404.php
@@ -0,0 +1,8 @@
+<?php
+defined('BASEPATH') OR exit('No direct script access allowed');
+
+echo "\nERROR: ",
+ $heading,
+ "\n\n",
+ $message,
+ "\n\n"; \ No newline at end of file
diff --git a/application/views/errors/cli/error_db.php b/application/views/errors/cli/error_db.php
new file mode 100644
index 000000000..2ff43ffc7
--- /dev/null
+++ b/application/views/errors/cli/error_db.php
@@ -0,0 +1,8 @@
+<?php
+defined('BASEPATH') OR exit('No direct script access allowed');
+
+echo "\nDatabase error: ",
+ $heading,
+ "\n\n",
+ $message,
+ "\n\n"; \ No newline at end of file
diff --git a/application/views/errors/cli/error_exception.php b/application/views/errors/cli/error_exception.php
new file mode 100644
index 000000000..efa6a66d1
--- /dev/null
+++ b/application/views/errors/cli/error_exception.php
@@ -0,0 +1,21 @@
+<?php defined('BASEPATH') OR exit('No direct script access allowed'); ?>
+
+An uncaught Exception was encountered
+
+Type: <?php echo get_class($exception), "\n"; ?>
+Message: <?php echo $message, "\n"; ?>
+Filename: <?php echo $exception->getFile(), "\n"; ?>
+Line Number: <?php echo $exception->getLine(); ?>
+
+<?php if (defined('SHOW_DEBUG_BACKTRACE') && SHOW_DEBUG_BACKTRACE === TRUE): ?>
+
+Backtrace:
+<?php foreach ($exception->getTrace() as $error): ?>
+<?php if (isset($error['file']) && strpos($error['file'], realpath(BASEPATH)) !== 0): ?>
+ File: <?php echo $error['file'], "\n"; ?>
+ Line: <?php echo $error['line'], "\n"; ?>
+ Function: <?php echo $error['function'], "\n\n"; ?>
+<?php endif ?>
+<?php endforeach ?>
+
+<?php endif ?>
diff --git a/application/views/errors/cli/error_general.php b/application/views/errors/cli/error_general.php
new file mode 100644
index 000000000..6984b61e9
--- /dev/null
+++ b/application/views/errors/cli/error_general.php
@@ -0,0 +1,8 @@
+<?php
+defined('BASEPATH') OR exit('No direct script access allowed');
+
+echo "\nERROR: ",
+ $heading,
+ "\n\n",
+ $message,
+ "\n\n"; \ No newline at end of file
diff --git a/application/views/errors/cli/error_php.php b/application/views/errors/cli/error_php.php
new file mode 100644
index 000000000..8a24b6491
--- /dev/null
+++ b/application/views/errors/cli/error_php.php
@@ -0,0 +1,21 @@
+<?php defined('BASEPATH') OR exit('No direct script access allowed'); ?>
+
+A PHP Error was encountered
+
+Severity: <?php echo $severity, "\n"; ?>
+Message: <?php echo $message, "\n"; ?>
+Filename: <?php echo $filepath, "\n"; ?>
+Line Number: <?php echo $line; ?>
+
+<?php if (defined('SHOW_DEBUG_BACKTRACE') && SHOW_DEBUG_BACKTRACE === TRUE): ?>
+
+Backtrace:
+<?php foreach (debug_backtrace() as $error): ?>
+<?php if (isset($error['file']) && strpos($error['file'], realpath(BASEPATH)) !== 0): ?>
+ File: <?php echo $error['file'], "\n"; ?>
+ Line: <?php echo $error['line'], "\n"; ?>
+ Function: <?php echo $error['function'], "\n\n"; ?>
+<?php endif ?>
+<?php endforeach ?>
+
+<?php endif ?>
diff --git a/application/errors/index.html b/application/views/errors/cli/index.html
index c942a79ce..bcb7cae34 100644
--- a/application/errors/index.html
+++ b/application/views/errors/cli/index.html
@@ -1,4 +1,5 @@
-<html>
+<!DOCTYPE html>
+<html lang="en">
<head>
<title>403 Forbidden</title>
</head>
@@ -7,4 +8,4 @@
<p>Directory access is forbidden.</p>
</body>
-</html> \ No newline at end of file
+</html>
diff --git a/application/views/errors/html/error_404.php b/application/views/errors/html/error_404.php
new file mode 100644
index 000000000..b71da106d
--- /dev/null
+++ b/application/views/errors/html/error_404.php
@@ -0,0 +1,3 @@
+<?php
+$title = "404 Page Not Found";
+include VIEWPATH."errors/html/error_general.php";
diff --git a/application/views/errors/html/error_db.php b/application/views/errors/html/error_db.php
new file mode 100644
index 000000000..adff63559
--- /dev/null
+++ b/application/views/errors/html/error_db.php
@@ -0,0 +1,3 @@
+<?php
+$title = "Database Error";
+include VIEWPATH."errors/html/error_general.php";
diff --git a/application/views/errors/html/error_exception.php b/application/views/errors/html/error_exception.php
new file mode 100644
index 000000000..befa12955
--- /dev/null
+++ b/application/views/errors/html/error_exception.php
@@ -0,0 +1,32 @@
+<?php
+defined('BASEPATH') OR exit('No direct script access allowed');
+?>
+
+<div style="border:1px solid #dd4814;padding-left:20px;margin:10px 0;">
+
+ <h4>An uncaught Exception was encountered</h4>
+
+ <p>Type: <?php echo get_class($exception); ?></p>
+ <p>Message: <?php echo $message; ?></p>
+ <p>Filename: <?php echo $exception->getFile(); ?></p>
+ <p>Line Number: <?php echo $exception->getLine(); ?></p>
+
+ <?php if (defined('SHOW_DEBUG_BACKTRACE') && SHOW_DEBUG_BACKTRACE === TRUE): ?>
+
+ <p>Backtrace:</p>
+ <?php foreach ($exception->getTrace() as $error): ?>
+
+ <?php if (isset($error['file']) && strpos($error['file'], realpath(BASEPATH)) !== 0): ?>
+
+ <p style="margin-left:10px">
+ File: <?php echo $error['file']; ?><br />
+ Line: <?php echo $error['line']; ?><br />
+ Function: <?php echo $error['function']; ?>
+ </p>
+ <?php endif ?>
+
+ <?php endforeach ?>
+
+ <?php endif ?>
+
+</div>
diff --git a/application/views/errors/html/error_general.php b/application/views/errors/html/error_general.php
new file mode 100644
index 000000000..637a1fd35
--- /dev/null
+++ b/application/views/errors/html/error_general.php
@@ -0,0 +1,127 @@
+<?php
+defined('BASEPATH') OR exit('No direct script access allowed');
+
+// fancy error page only works if we can load helpers
+if (class_exists("CI_Controller") && !isset($GLOBALS["is_error_page"]) && isset(get_instance()->load)) {
+ if (!isset($title)) {
+ $title = "Error";
+ }
+ $GLOBALS["is_error_page"] = true;
+
+ $CI =& get_instance();
+ $CI->load->helper("filebin");
+ $CI->load->helper("url");
+
+ if (is_cli()) {
+ $message = str_replace("</p>", "</p>\n", $message);
+ $message = strip_tags($message);
+ echo "$heading: $message\n";
+ exit();
+ }
+
+ include APPPATH.'views/header.php';
+
+ ?>
+ <div class="error">
+ <h1><?php echo $heading; ?></h1>
+ <?php echo $message; ?>
+ </div>
+
+ <?php
+ include APPPATH.'views/footer.php';
+} elseif (php_sapi_name() === 'cli' OR defined('STDIN')) {
+ echo "# $heading\n";
+ $msg = strip_tags(str_replace("<br>", "\n", $message));
+ foreach (explode("\n", $msg) as $line) {
+ echo "# $line\n";
+ }
+ exit(255);
+} else {
+ // default CI error page
+?><!DOCTYPE html>
+<html lang="en">
+<head>
+ <meta charset="utf-8">
+ <title>Error</title>
+ <style type="text/css">
+
+ ::selection { background-color: #f07746; color: #fff; }
+ ::-moz-selection { background-color: #f07746; color: #fff; }
+
+ body {
+ background-color: #fff;
+ margin: 40px auto;
+ max-width: 1024px;
+ font: 16px/24px normal "Helvetica Neue", Helvetica, Arial, sans-serif;
+ color: #808080;
+ }
+
+ a {
+ color: #dd4814;
+ background-color: transparent;
+ font-weight: normal;
+ text-decoration: none;
+ }
+
+ a:hover {
+ color: #97310e;
+ }
+
+ h1 {
+ color: #fff;
+ background-color: #dd4814;
+ border-bottom: 1px solid #d0d0d0;
+ font-size: 22px;
+ font-weight: bold;
+ margin: 0 0 14px 0;
+ padding: 5px 15px;
+ line-height: 40px;
+ }
+
+ h2 {
+ color:#404040;
+ margin:0;
+ padding:0 0 10px 0;
+ }
+
+ code {
+ font-family: Consolas, Monaco, Courier New, Courier, monospace;
+ font-size: 13px;
+ background-color: #f5f5f5;
+ border: 1px solid #e3e3e3;
+ border-radius: 4px;
+ color: #002166;
+ display: block;
+ margin: 14px 0 14px 0;
+ padding: 12px 10px 12px 10px;
+ }
+
+ #container {
+ margin: 10px;
+ border: 1px solid #d0d0d0;
+ box-shadow: 0 0 8px #d0d0d0;
+ border-radius: 4px;
+ }
+
+ p {
+ margin: 0 0 10px;
+ padding:0;
+ }
+
+ #body {
+ margin: 0 15px 0 15px;
+ min-height: 96px;
+ }
+ </style>
+</head>
+<body>
+ <div id="container">
+ <h1><?php echo $heading; ?></h1>
+ <div id="body">
+ <?php echo $message; ?>
+ </div>
+ </div>
+</body>
+</html>
+<?php
+}
diff --git a/application/views/errors/html/error_php.php b/application/views/errors/html/error_php.php
new file mode 100644
index 000000000..8b445ef39
--- /dev/null
+++ b/application/views/errors/html/error_php.php
@@ -0,0 +1,33 @@
+<?php
+defined('BASEPATH') OR exit('No direct script access allowed');
+?>
+
+<div style="border:1px solid #dd4814;padding-left:20px;margin:10px 0;">
+
+ <h4>A PHP Error was encountered</h4>
+
+ <p>Severity: <?php echo $severity; ?></p>
+ <p>Message: <?php echo $message; ?></p>
+ <p>Filename: <?php echo $filepath; ?></p>
+ <p>Line Number: <?php echo $line; ?></p>
+
+ <?php if (defined('SHOW_DEBUG_BACKTRACE') && SHOW_DEBUG_BACKTRACE === TRUE): ?>
+
+ <p>Backtrace:</p>
+ <?php foreach (debug_backtrace() as $error): ?>
+
+ <?php if (isset($error['file']) && strpos($error['file'], realpath(BASEPATH)) !== 0): ?>
+
+ <p style="margin-left:10px">
+ File: <?php echo $error['file'] ?><br />
+ Line: <?php echo $error['line'] ?><br />
+ Function: <?php echo $error['function'] ?>
+ </p>
+
+ <?php endif ?>
+
+ <?php endforeach ?>
+
+ <?php endif ?>
+
+</div>
diff --git a/system/database/drivers/sqlite/index.html b/application/views/errors/html/index.html
index c942a79ce..bcb7cae34 100644
--- a/system/database/drivers/sqlite/index.html
+++ b/application/views/errors/html/index.html
@@ -1,4 +1,5 @@
-<html>
+<!DOCTYPE html>
+<html lang="en">
<head>
<title>403 Forbidden</title>
</head>
@@ -7,4 +8,4 @@
<p>Directory access is forbidden.</p>
</body>
-</html> \ No newline at end of file
+</html>
diff --git a/application/views/errors/index.html b/application/views/errors/index.html
new file mode 100644
index 000000000..bcb7cae34
--- /dev/null
+++ b/application/views/errors/index.html
@@ -0,0 +1,11 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+ <title>403 Forbidden</title>
+</head>
+<body>
+
+<p>Directory access is forbidden.</p>
+
+</body>
+</html>
diff --git a/application/views/file/deleted.php b/application/views/file/deleted.php
index 8a5818f2d..741824e58 100644
--- a/application/views/file/deleted.php
+++ b/application/views/file/deleted.php
@@ -2,7 +2,7 @@
<?php if (!empty($errors)) {
echo "<p>";
foreach ($errors as $error) {
- echo "${error["id"]}: ${error["reason"]}<br>\n";
+ echo "{$error["id"]}: {$error["reason"]}<br>\n";
}
echo "</p>";
} ?>
diff --git a/application/views/file/upload_form.php b/application/views/file/upload_form.php
index b7d6fbabd..a466e6640 100644
--- a/application/views/file/upload_form.php
+++ b/application/views/file/upload_form.php
@@ -14,7 +14,7 @@
<div class="tab-pane active" id="text-upload-tab-1">
<div class="panel panel-default">
<div class="panel-heading">
- <input type="text" name="filename[1]" class="form-control" placeholder="Filename/title (default: stdin)">
+ <input type="text" name="filename[1]" class="form-control" placeholder="Filename/title (default: stdin)" value="<?php if (isset($textarea_filename)) { echo htmlspecialchars($textarea_filename); } ?>">
</div>
<textarea name="content[1]" class="form-control text-upload" placeholder="Paste content"><?php
if (isset($textarea_content)) {
@@ -53,7 +53,7 @@
<p><button type="submit" id="upload_button" class="btn btn-primary">Upload/Paste it!</button></p>
<p>
Uploads/pastes are <?php if ($upload_max_age > 0) {
- echo "deleted after ".$upload_max_age." days";
+ echo "deleted after ".expiration_duration($upload_max_age);
if ($small_upload_size > 0) {
echo " unless they are smaller than ".format_bytes($small_upload_size);
}
@@ -119,7 +119,7 @@
<h3>Special filenames:</h3>
<dl class="dl-horizontal">
- <dt>*.asciinema.json</dt><dd>treat the file as an <a href="https://asciinema.org/">asciinema screencast</a> and display a videoplayer for it</dd>
+ <dt>*.asciinema.json<br>or *.cast</dt><dd>treat the file as an <a href="https://asciinema.org/">asciinema screencast</a> and display a videoplayer for it</dd>
</dl>
</div>
@@ -155,14 +155,20 @@
<p>
Arch Linux: <code>pacman -S fb-client</code><br />
- Gentoo: Add <a href="https://git.holgersson.xyz/holgersson-overlay/tree/README">this overlay</a> and run <code>emerge -a fb-client</code><br />
+ Gentoo: Add <a href="https://git.holgersson.xyz/foss/holgersson-overlay/src/branch/master/README.rst">this overlay</a> and run <code>emerge -a fb-client</code><br />
FreeBSD: <code>pkg install fb</code><br />
+ OpenSUSE: <a href="https://build.opensuse.org/package/show/home:mwilhelmy/fb-client">home:mwilhelmy / fb-client</a>
</p>
<h4>Android</h4>
<p>
+ Development: <a href="https://git.myservermanager.com/varakh/fbmobile">v4rakh/fbmobile @ git.myservermanager.com</a><br>
+ Google Play: <a href="https://play.google.com/store/apps/details?id=de.varakh.fbmobile">FileBin @ Google Play</a><br>
+ </p>
+
+ <p>
+ Unmaintained Legacy Client:<br>
Development: <a href="https://github.com/sebastianrakel/fb-client-android">sebastianrakel/fb-client-android @ Github</a><br>
- Google Playstore: <a href="https://play.google.com/store/apps/details?id=eu.devunit.fb_client">fb-client Android @ Google Play</a><br>
F-Droid Store: <a href="https://f-droid.org/repository/browse/?fdid=eu.devunit.fb_client">fb-client Android @ F-Droid</a><br>
</p>
</div>
diff --git a/application/views/index.html b/application/views/index.html
index c942a79ce..bcb7cae34 100644
--- a/application/views/index.html
+++ b/application/views/index.html
@@ -1,4 +1,5 @@
-<html>
+<!DOCTYPE html>
+<html lang="en">
<head>
<title>403 Forbidden</title>
</head>
@@ -7,4 +8,4 @@
<p>Directory access is forbidden.</p>
</body>
-</html> \ No newline at end of file
+</html>
diff --git a/application/views/user/invite.php b/application/views/user/invite.php
index d3e2fb7a6..042ba0b61 100644
--- a/application/views/user/invite.php
+++ b/application/views/user/invite.php
@@ -26,6 +26,12 @@
<td><?php echo $i++; ?></td>
<td><?php echo anchor("user/register/".$item["key"], $item["key"]) ?></td>
<td><?php echo date("Y/m/d H:i", $item["date"]) ?></td>
+ <td>
+ <?php echo form_open('user/delete_invitation_key'); ?>
+ <input class="btn btn-danger btn-xs" type="submit" value="Delete" name="delete" />
+ <input type="hidden" name="key" value="<?php echo $item["key"]; ?>" />
+ </form>
+ </td>
</tr>
<?php endforeach; ?>
</tbody>
diff --git a/application/views/user/register.php b/application/views/user/register.php
index af4558ff9..0f03a2f00 100644
--- a/application/views/user/register.php
+++ b/application/views/user/register.php
@@ -9,6 +9,7 @@
<label class="control-label col-lg-2 col-md-2" for="inputUsername">Username</label>
<div class="col-lg-5 col-md-5">
<input type="text" id="inputUsername" name="username" placeholder="Username" value="<?php echo $values["username"]; ?>" class="form-control">
+ <span class="help-block">The username may contain up to 32 chars of a-z0-9 (only lowercase characters).</span>
</div>
</div>
</div>
diff --git a/check_deps.php b/check_deps.php
index dd1e30c61..bb716a0a2 100755
--- a/check_deps.php
+++ b/check_deps.php
@@ -64,6 +64,7 @@ $mod_groups = array(
"thumbnail generation - EXIF" => array("exif"),
"database support" => array("mysql", "mysqli", "pgsql", "pdo_mysql", "pdo_pgsql"),
"multipaste tarball support" => array("phar"),
+ "multibyte functions" => array("mbstring"),
);
foreach ($mod_groups as $function => $mods) {
$found = 0;
diff --git a/composer.json b/composer.json
index 34fe9a5dc..4c950d5d4 100644
--- a/composer.json
+++ b/composer.json
@@ -1,5 +1,26 @@
{
- "require-dev": {
- "phpunit/php-code-coverage": "^4.0"
- }
+ "description": "FileBin",
+ "name": "bluewind/filebin",
+ "type": "project",
+ "homepage": "https://github.com/Bluewind/filebin",
+ "license": "AGPLv3",
+ "support": {
+ "forum": "https://github.com/Bluewind/filebin",
+ "source": "https://github.com/Bluewind/filebin"
+ },
+ "require": {
+ "php": ">=7.0",
+ "ext-gd": "*",
+ "ext-exif": "*",
+ "ext-fileinfo": "*",
+ "ext-phar": "*",
+ "ext-mbstring": "*"
+ },
+ "suggest": {
+ "paragonie/random_compat": "Provides better randomness in PHP 5.x"
+ },
+ "require-dev": {
+ "phpunit/php-code-coverage": "^9 || ^10",
+ "ext-xdebug": "*"
+ }
}
diff --git a/composer.lock b/composer.lock
index 63d0f0b2a..fb8e70d66 100644
--- a/composer.lock
+++ b/composer.lock
@@ -1,49 +1,108 @@
{
"_readme": [
"This file locks the dependencies of your project to a known state",
- "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file",
+ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
"This file is @generated automatically"
],
- "hash": "e68d6f6c082c79c76bc6de6e8d5e8e5d",
- "content-hash": "d83aef008ebb5adea7113241c0ff2f99",
+ "content-hash": "fb6340c619249421c28d00c097888547",
"packages": [],
"packages-dev": [
{
+ "name": "nikic/php-parser",
+ "version": "v4.13.2",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/nikic/PHP-Parser.git",
+ "reference": "210577fe3cf7badcc5814d99455df46564f3c077"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/210577fe3cf7badcc5814d99455df46564f3c077",
+ "reference": "210577fe3cf7badcc5814d99455df46564f3c077",
+ "shasum": ""
+ },
+ "require": {
+ "ext-tokenizer": "*",
+ "php": ">=7.0"
+ },
+ "require-dev": {
+ "ircmaxell/php-yacc": "^0.0.7",
+ "phpunit/phpunit": "^6.5 || ^7.0 || ^8.0 || ^9.0"
+ },
+ "bin": [
+ "bin/php-parse"
+ ],
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "4.9-dev"
+ }
+ },
+ "autoload": {
+ "psr-4": {
+ "PhpParser\\": "lib/PhpParser"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "BSD-3-Clause"
+ ],
+ "authors": [
+ {
+ "name": "Nikita Popov"
+ }
+ ],
+ "description": "A PHP parser written in PHP",
+ "keywords": [
+ "parser",
+ "php"
+ ],
+ "support": {
+ "issues": "https://github.com/nikic/PHP-Parser/issues",
+ "source": "https://github.com/nikic/PHP-Parser/tree/v4.13.2"
+ },
+ "time": "2021-11-30T19:35:32+00:00"
+ },
+ {
"name": "phpunit/php-code-coverage",
- "version": "4.0.1",
+ "version": "9.2.15",
"source": {
"type": "git",
"url": "https://github.com/sebastianbergmann/php-code-coverage.git",
- "reference": "5f3f7e736d6319d5f1fc402aff8b026da26709a3"
+ "reference": "2e9da11878c4202f97915c1cb4bb1ca318a63f5f"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/5f3f7e736d6319d5f1fc402aff8b026da26709a3",
- "reference": "5f3f7e736d6319d5f1fc402aff8b026da26709a3",
+ "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/2e9da11878c4202f97915c1cb4bb1ca318a63f5f",
+ "reference": "2e9da11878c4202f97915c1cb4bb1ca318a63f5f",
"shasum": ""
},
"require": {
- "php": "^5.6 || ^7.0",
- "phpunit/php-file-iterator": "~1.3",
- "phpunit/php-text-template": "~1.2",
- "phpunit/php-token-stream": "^1.4.2",
- "sebastian/code-unit-reverse-lookup": "~1.0",
- "sebastian/environment": "^1.3.2 || ^2.0",
- "sebastian/version": "~1.0|~2.0"
+ "ext-dom": "*",
+ "ext-libxml": "*",
+ "ext-xmlwriter": "*",
+ "nikic/php-parser": "^4.13.0",
+ "php": ">=7.3",
+ "phpunit/php-file-iterator": "^3.0.3",
+ "phpunit/php-text-template": "^2.0.2",
+ "sebastian/code-unit-reverse-lookup": "^2.0.2",
+ "sebastian/complexity": "^2.0",
+ "sebastian/environment": "^5.1.2",
+ "sebastian/lines-of-code": "^1.0.3",
+ "sebastian/version": "^3.0.1",
+ "theseer/tokenizer": "^1.2.0"
},
"require-dev": {
- "ext-xdebug": ">=2.1.4",
- "phpunit/phpunit": "^5.4"
+ "phpunit/phpunit": "^9.3"
},
"suggest": {
- "ext-dom": "*",
- "ext-xdebug": ">=2.4.0",
- "ext-xmlwriter": "*"
+ "ext-pcov": "*",
+ "ext-xdebug": "*"
},
"type": "library",
"extra": {
"branch-alias": {
- "dev-master": "4.0.x-dev"
+ "dev-master": "9.2-dev"
}
},
"autoload": {
@@ -58,7 +117,7 @@
"authors": [
{
"name": "Sebastian Bergmann",
- "email": "sb@sebastian-bergmann.de",
+ "email": "sebastian@phpunit.de",
"role": "lead"
}
],
@@ -69,29 +128,42 @@
"testing",
"xunit"
],
- "time": "2016-07-26 14:39:29"
+ "support": {
+ "issues": "https://github.com/sebastianbergmann/php-code-coverage/issues",
+ "source": "https://github.com/sebastianbergmann/php-code-coverage/tree/9.2.15"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/sebastianbergmann",
+ "type": "github"
+ }
+ ],
+ "time": "2022-03-07T09:28:20+00:00"
},
{
"name": "phpunit/php-file-iterator",
- "version": "1.4.1",
+ "version": "3.0.6",
"source": {
"type": "git",
"url": "https://github.com/sebastianbergmann/php-file-iterator.git",
- "reference": "6150bf2c35d3fc379e50c7602b75caceaa39dbf0"
+ "reference": "cf1c2e7c203ac650e352f4cc675a7021e7d1b3cf"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/sebastianbergmann/php-file-iterator/zipball/6150bf2c35d3fc379e50c7602b75caceaa39dbf0",
- "reference": "6150bf2c35d3fc379e50c7602b75caceaa39dbf0",
+ "url": "https://api.github.com/repos/sebastianbergmann/php-file-iterator/zipball/cf1c2e7c203ac650e352f4cc675a7021e7d1b3cf",
+ "reference": "cf1c2e7c203ac650e352f4cc675a7021e7d1b3cf",
"shasum": ""
},
"require": {
- "php": ">=5.3.3"
+ "php": ">=7.3"
+ },
+ "require-dev": {
+ "phpunit/phpunit": "^9.3"
},
"type": "library",
"extra": {
"branch-alias": {
- "dev-master": "1.4.x-dev"
+ "dev-master": "3.0-dev"
}
},
"autoload": {
@@ -106,7 +178,7 @@
"authors": [
{
"name": "Sebastian Bergmann",
- "email": "sb@sebastian-bergmann.de",
+ "email": "sebastian@phpunit.de",
"role": "lead"
}
],
@@ -116,26 +188,44 @@
"filesystem",
"iterator"
],
- "time": "2015-06-21 13:08:43"
+ "support": {
+ "issues": "https://github.com/sebastianbergmann/php-file-iterator/issues",
+ "source": "https://github.com/sebastianbergmann/php-file-iterator/tree/3.0.6"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/sebastianbergmann",
+ "type": "github"
+ }
+ ],
+ "time": "2021-12-02T12:48:52+00:00"
},
{
"name": "phpunit/php-text-template",
- "version": "1.2.1",
+ "version": "2.0.4",
"source": {
"type": "git",
"url": "https://github.com/sebastianbergmann/php-text-template.git",
- "reference": "31f8b717e51d9a2afca6c9f046f5d69fc27c8686"
+ "reference": "5da5f67fc95621df9ff4c4e5a84d6a8a2acf7c28"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/sebastianbergmann/php-text-template/zipball/31f8b717e51d9a2afca6c9f046f5d69fc27c8686",
- "reference": "31f8b717e51d9a2afca6c9f046f5d69fc27c8686",
+ "url": "https://api.github.com/repos/sebastianbergmann/php-text-template/zipball/5da5f67fc95621df9ff4c4e5a84d6a8a2acf7c28",
+ "reference": "5da5f67fc95621df9ff4c4e5a84d6a8a2acf7c28",
"shasum": ""
},
"require": {
- "php": ">=5.3.3"
+ "php": ">=7.3"
+ },
+ "require-dev": {
+ "phpunit/phpunit": "^9.3"
},
"type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "2.0-dev"
+ }
+ },
"autoload": {
"classmap": [
"src/"
@@ -157,33 +247,42 @@
"keywords": [
"template"
],
- "time": "2015-06-21 13:50:34"
+ "support": {
+ "issues": "https://github.com/sebastianbergmann/php-text-template/issues",
+ "source": "https://github.com/sebastianbergmann/php-text-template/tree/2.0.4"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/sebastianbergmann",
+ "type": "github"
+ }
+ ],
+ "time": "2020-10-26T05:33:50+00:00"
},
{
- "name": "phpunit/php-token-stream",
- "version": "1.4.8",
+ "name": "sebastian/code-unit-reverse-lookup",
+ "version": "2.0.3",
"source": {
"type": "git",
- "url": "https://github.com/sebastianbergmann/php-token-stream.git",
- "reference": "3144ae21711fb6cac0b1ab4cbe63b75ce3d4e8da"
+ "url": "https://github.com/sebastianbergmann/code-unit-reverse-lookup.git",
+ "reference": "ac91f01ccec49fb77bdc6fd1e548bc70f7faa3e5"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/sebastianbergmann/php-token-stream/zipball/3144ae21711fb6cac0b1ab4cbe63b75ce3d4e8da",
- "reference": "3144ae21711fb6cac0b1ab4cbe63b75ce3d4e8da",
+ "url": "https://api.github.com/repos/sebastianbergmann/code-unit-reverse-lookup/zipball/ac91f01ccec49fb77bdc6fd1e548bc70f7faa3e5",
+ "reference": "ac91f01ccec49fb77bdc6fd1e548bc70f7faa3e5",
"shasum": ""
},
"require": {
- "ext-tokenizer": "*",
- "php": ">=5.3.3"
+ "php": ">=7.3"
},
"require-dev": {
- "phpunit/phpunit": "~4.2"
+ "phpunit/phpunit": "^9.3"
},
"type": "library",
"extra": {
"branch-alias": {
- "dev-master": "1.4-dev"
+ "dev-master": "2.0-dev"
}
},
"autoload": {
@@ -201,37 +300,45 @@
"email": "sebastian@phpunit.de"
}
],
- "description": "Wrapper around PHP's tokenizer extension.",
- "homepage": "https://github.com/sebastianbergmann/php-token-stream/",
- "keywords": [
- "tokenizer"
+ "description": "Looks up which function or method a line of code belongs to",
+ "homepage": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/",
+ "support": {
+ "issues": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/issues",
+ "source": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/tree/2.0.3"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/sebastianbergmann",
+ "type": "github"
+ }
],
- "time": "2015-09-15 10:49:45"
+ "time": "2020-09-28T05:30:19+00:00"
},
{
- "name": "sebastian/code-unit-reverse-lookup",
- "version": "1.0.0",
+ "name": "sebastian/complexity",
+ "version": "2.0.2",
"source": {
"type": "git",
- "url": "https://github.com/sebastianbergmann/code-unit-reverse-lookup.git",
- "reference": "c36f5e7cfce482fde5bf8d10d41a53591e0198fe"
+ "url": "https://github.com/sebastianbergmann/complexity.git",
+ "reference": "739b35e53379900cc9ac327b2147867b8b6efd88"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/sebastianbergmann/code-unit-reverse-lookup/zipball/c36f5e7cfce482fde5bf8d10d41a53591e0198fe",
- "reference": "c36f5e7cfce482fde5bf8d10d41a53591e0198fe",
+ "url": "https://api.github.com/repos/sebastianbergmann/complexity/zipball/739b35e53379900cc9ac327b2147867b8b6efd88",
+ "reference": "739b35e53379900cc9ac327b2147867b8b6efd88",
"shasum": ""
},
"require": {
- "php": ">=5.6"
+ "nikic/php-parser": "^4.7",
+ "php": ">=7.3"
},
"require-dev": {
- "phpunit/phpunit": "~5"
+ "phpunit/phpunit": "^9.3"
},
"type": "library",
"extra": {
"branch-alias": {
- "dev-master": "1.0.x-dev"
+ "dev-master": "2.0-dev"
}
},
"autoload": {
@@ -246,37 +353,51 @@
"authors": [
{
"name": "Sebastian Bergmann",
- "email": "sebastian@phpunit.de"
+ "email": "sebastian@phpunit.de",
+ "role": "lead"
}
],
- "description": "Looks up which function or method a line of code belongs to",
- "homepage": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/",
- "time": "2016-02-13 06:45:14"
+ "description": "Library for calculating the complexity of PHP code units",
+ "homepage": "https://github.com/sebastianbergmann/complexity",
+ "support": {
+ "issues": "https://github.com/sebastianbergmann/complexity/issues",
+ "source": "https://github.com/sebastianbergmann/complexity/tree/2.0.2"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/sebastianbergmann",
+ "type": "github"
+ }
+ ],
+ "time": "2020-10-26T15:52:27+00:00"
},
{
"name": "sebastian/environment",
- "version": "1.3.7",
+ "version": "5.1.4",
"source": {
"type": "git",
"url": "https://github.com/sebastianbergmann/environment.git",
- "reference": "4e8f0da10ac5802913afc151413bc8c53b6c2716"
+ "reference": "1b5dff7bb151a4db11d49d90e5408e4e938270f7"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/sebastianbergmann/environment/zipball/4e8f0da10ac5802913afc151413bc8c53b6c2716",
- "reference": "4e8f0da10ac5802913afc151413bc8c53b6c2716",
+ "url": "https://api.github.com/repos/sebastianbergmann/environment/zipball/1b5dff7bb151a4db11d49d90e5408e4e938270f7",
+ "reference": "1b5dff7bb151a4db11d49d90e5408e4e938270f7",
"shasum": ""
},
"require": {
- "php": ">=5.3.3"
+ "php": ">=7.3"
},
"require-dev": {
- "phpunit/phpunit": "~4.4"
+ "phpunit/phpunit": "^9.3"
+ },
+ "suggest": {
+ "ext-posix": "*"
},
"type": "library",
"extra": {
"branch-alias": {
- "dev-master": "1.3.x-dev"
+ "dev-master": "5.1-dev"
}
},
"autoload": {
@@ -301,29 +422,96 @@
"environment",
"hhvm"
],
- "time": "2016-05-17 03:18:57"
+ "support": {
+ "issues": "https://github.com/sebastianbergmann/environment/issues",
+ "source": "https://github.com/sebastianbergmann/environment/tree/5.1.4"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/sebastianbergmann",
+ "type": "github"
+ }
+ ],
+ "time": "2022-04-03T09:37:03+00:00"
+ },
+ {
+ "name": "sebastian/lines-of-code",
+ "version": "1.0.3",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/sebastianbergmann/lines-of-code.git",
+ "reference": "c1c2e997aa3146983ed888ad08b15470a2e22ecc"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/sebastianbergmann/lines-of-code/zipball/c1c2e997aa3146983ed888ad08b15470a2e22ecc",
+ "reference": "c1c2e997aa3146983ed888ad08b15470a2e22ecc",
+ "shasum": ""
+ },
+ "require": {
+ "nikic/php-parser": "^4.6",
+ "php": ">=7.3"
+ },
+ "require-dev": {
+ "phpunit/phpunit": "^9.3"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "1.0-dev"
+ }
+ },
+ "autoload": {
+ "classmap": [
+ "src/"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "BSD-3-Clause"
+ ],
+ "authors": [
+ {
+ "name": "Sebastian Bergmann",
+ "email": "sebastian@phpunit.de",
+ "role": "lead"
+ }
+ ],
+ "description": "Library for counting the lines of code in PHP source code",
+ "homepage": "https://github.com/sebastianbergmann/lines-of-code",
+ "support": {
+ "issues": "https://github.com/sebastianbergmann/lines-of-code/issues",
+ "source": "https://github.com/sebastianbergmann/lines-of-code/tree/1.0.3"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/sebastianbergmann",
+ "type": "github"
+ }
+ ],
+ "time": "2020-11-28T06:42:11+00:00"
},
{
"name": "sebastian/version",
- "version": "2.0.0",
+ "version": "3.0.2",
"source": {
"type": "git",
"url": "https://github.com/sebastianbergmann/version.git",
- "reference": "c829badbd8fdf16a0bad8aa7fa7971c029f1b9c5"
+ "reference": "c6c1022351a901512170118436c764e473f6de8c"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/sebastianbergmann/version/zipball/c829badbd8fdf16a0bad8aa7fa7971c029f1b9c5",
- "reference": "c829badbd8fdf16a0bad8aa7fa7971c029f1b9c5",
+ "url": "https://api.github.com/repos/sebastianbergmann/version/zipball/c6c1022351a901512170118436c764e473f6de8c",
+ "reference": "c6c1022351a901512170118436c764e473f6de8c",
"shasum": ""
},
"require": {
- "php": ">=5.6"
+ "php": ">=7.3"
},
"type": "library",
"extra": {
"branch-alias": {
- "dev-master": "2.0.x-dev"
+ "dev-master": "3.0-dev"
}
},
"autoload": {
@@ -344,7 +532,67 @@
],
"description": "Library that helps with managing the version number of Git-hosted PHP projects",
"homepage": "https://github.com/sebastianbergmann/version",
- "time": "2016-02-04 12:56:52"
+ "support": {
+ "issues": "https://github.com/sebastianbergmann/version/issues",
+ "source": "https://github.com/sebastianbergmann/version/tree/3.0.2"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/sebastianbergmann",
+ "type": "github"
+ }
+ ],
+ "time": "2020-09-28T06:39:44+00:00"
+ },
+ {
+ "name": "theseer/tokenizer",
+ "version": "1.2.1",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/theseer/tokenizer.git",
+ "reference": "34a41e998c2183e22995f158c581e7b5e755ab9e"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/theseer/tokenizer/zipball/34a41e998c2183e22995f158c581e7b5e755ab9e",
+ "reference": "34a41e998c2183e22995f158c581e7b5e755ab9e",
+ "shasum": ""
+ },
+ "require": {
+ "ext-dom": "*",
+ "ext-tokenizer": "*",
+ "ext-xmlwriter": "*",
+ "php": "^7.2 || ^8.0"
+ },
+ "type": "library",
+ "autoload": {
+ "classmap": [
+ "src/"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "BSD-3-Clause"
+ ],
+ "authors": [
+ {
+ "name": "Arne Blankerts",
+ "email": "arne@blankerts.de",
+ "role": "Developer"
+ }
+ ],
+ "description": "A small library for converting tokenized PHP source code into XML and potentially other formats",
+ "support": {
+ "issues": "https://github.com/theseer/tokenizer/issues",
+ "source": "https://github.com/theseer/tokenizer/tree/1.2.1"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/theseer",
+ "type": "github"
+ }
+ ],
+ "time": "2021-07-28T10:34:58+00:00"
}
],
"aliases": [],
@@ -352,6 +600,16 @@
"stability-flags": [],
"prefer-stable": false,
"prefer-lowest": false,
- "platform": [],
- "platform-dev": []
+ "platform": {
+ "php": ">=5.5",
+ "ext-gd": "*",
+ "ext-exif": "*",
+ "ext-fileinfo": "*",
+ "ext-phar": "*",
+ "ext-mbstring": "*"
+ },
+ "platform-dev": {
+ "ext-xdebug": "*"
+ },
+ "plugin-api-version": "2.2.0"
}
diff --git a/contributing.md b/contributing.md
new file mode 100644
index 000000000..206784d1f
--- /dev/null
+++ b/contributing.md
@@ -0,0 +1,93 @@
+# Contributing to CodeIgniter
+
+CodeIgniter is a community driven project and accepts contributions of code and documentation from the community. These contributions are made in the form of Issues or [Pull Requests](http://help.github.com/send-pull-requests/) on the [CodeIgniter repository](https://github.com/bcit-ci/CodeIgniter) on GitHub.
+
+Issues are a quick way to point out a bug. If you find a bug or documentation error in CodeIgniter then please check a few things first:
+
+1. There is not already an open Issue
+2. The issue has already been fixed (check the develop branch, or look for closed Issues)
+3. Is it something really obvious that you can fix yourself?
+
+Reporting issues is helpful but an even better approach is to send a Pull Request, which is done by "Forking" the main repository and committing to your own copy. This will require you to use the version control system called Git.
+
+## Guidelines
+
+Before we look into how, here are the guidelines. If your Pull Requests fail
+to pass these guidelines it will be declined and you will need to re-submit
+when you’ve made the changes. This might sound a bit tough, but it is required
+for us to maintain quality of the code-base.
+
+### PHP Style
+
+All code must meet the [Style Guide](https://codeigniter.com/userguide3/general/styleguide.html), which is
+essentially the [Allman indent style](https://en.wikipedia.org/wiki/Indent_style#Allman_style), underscores and readable operators. This makes certain that all code is the same format as the existing code and means it will be as readable as possible.
+
+### Documentation
+
+If you change anything that requires a change to documentation then you will need to add it. New classes, methods, parameters, changing default values, etc are all things that will require a change to documentation. The change-log must also be updated for every change. Also PHPDoc blocks must be maintained.
+
+### Compatibility
+
+CodeIgniter recommends PHP 5.5 or newer to be used, but it should be
+compatible with PHP 5.2.4 so all code supplied must stick to this
+requirement. If PHP 5.3 (and above) functions or features are used then
+there must be a fallback for PHP 5.2.4.
+
+### Branching
+
+CodeIgniter uses the [Git-Flow](https://nvie.com/posts/a-successful-git-branching-model/) branching model which requires all pull requests to be sent to the "develop" branch. This is
+where the next planned version will be developed. The "master" branch will always contain the latest stable version and is kept clean so a "hotfix" (e.g: an emergency security patch) can be applied to master to create a new version, without worrying about other features holding it up. For this reason all commits need to be made to "develop" and any sent to "master" will be closed automatically. If you have multiple changes to submit, please place all changes into their own branch on your fork.
+
+One thing at a time: A pull request should only contain one change. That does not mean only one commit, but one change - however many commits it took. The reason for this is that if you change X and Y but send a pull request for both at the same time, we might really want X but disagree with Y, meaning we cannot merge the request. Using the Git-Flow branching model you can create new branches for both of these features and send two requests.
+
+### Signing
+
+You must sign your work, certifying that you either wrote the work or otherwise have the right to pass it on to an open source project. git makes this trivial as you merely have to use `--signoff` on your commits to your CodeIgniter fork.
+
+`git commit --signoff`
+
+or simply
+
+`git commit -s`
+
+This will sign your commits with the information setup in your git config, e.g.
+
+`Signed-off-by: John Q Public <john.public@example.com>`
+
+If you are using [Tower](https://www.git-tower.com/) there is a "Sign-Off" checkbox in the commit window. You could even alias git commit to use the `-s` flag so you don’t have to think about it.
+
+By signing your work in this manner, you certify to a "Developer's Certificate of Origin". The current version of this certificate is in the `DCO.txt` file in the root of this repository.
+
+## How-to Guide
+
+There are two ways to make changes, the easy way and the hard way. Either way you will need to [create a GitHub account](https://github.com/signup/free).
+
+Easy way GitHub allows in-line editing of files for making simple typo changes and quick-fixes. This is not the best way as you are unable to test the code works. If you do this you could be introducing syntax errors, etc, but for a Git-phobic user this is good for a quick-fix.
+
+Hard way The best way to contribute is to "clone" your fork of CodeIgniter to your development area. That sounds like some jargon, but "forking" on GitHub means "making a copy of that repo to your account" and "cloning" means "copying that code to your environment so you can work on it".
+
+1. [Set up Git](https://help.github.com/en/articles/set-up-git) (Windows, Mac & Linux)
+2. Go to the [CodeIgniter repo](https://github.com/bcit-ci/CodeIgniter)
+3. [Fork it](https://help.github.com/en/articles/fork-a-repo)
+4. [Clone](https://help.github.com/en/articles/fetching-a-remote#clone) your forked CodeIgniter repo: git@github.com:<your-name>/CodeIgniter.git.
+5. Checkout the "develop" branch. At this point you are ready to start making changes.
+6. Fix existing bugs on the Issue tracker after taking a look to see nobody else is working on them.
+7. [Commit](https://help.github.com/en/articles/adding-a-file-to-a-repository-using-the-command-line) the files
+8. [Push](https://help.github.com/en/articles/pushing-to-a-remote) your develop branch to your fork
+9. [Send a pull request](https://help.github.com/en/articles/creating-a-pull-request)
+
+The Reactor Engineers will now be alerted about the change and at least one of the team will respond. If your change fails to meet the guidelines it will be bounced, or feedback will be provided to help you improve it.
+
+Once the Reactor Engineer handling your pull request is happy with it they will merge it into develop and your patch will be part of the next release.
+
+### Keeping your fork up-to-date
+
+Unlike systems like Subversion, Git can have multiple remotes. A remote is the name for a URL of a Git repository. By default your fork will have a remote named "origin" which points to your fork, but you can add another remote named "codeigniter" which points to `git://github.com/bcit-ci/CodeIgniter.git`. This is a read-only remote but you can pull from this develop branch to update your own.
+
+If you are using command-line you can do the following:
+
+1. `git remote add codeigniter git://github.com/bcit-ci/CodeIgniter.git`
+2. `git pull codeigniter develop`
+3. `git push origin develop`
+
+Now your fork is up to date. This should be done regularly, or before you send a pull request at least.
diff --git a/doc/api.md b/doc/api.md
index 1fe475fa8..3484acd2b 100644
--- a/doc/api.md
+++ b/doc/api.md
@@ -147,6 +147,8 @@ These are the most common errors that can be returned by any API call.
| Version | Endpoint | Note |
| ------- | -------- | ---- |
+| 2.2.0 | file/create_multipaste | Add paramter ''minimum-id-length'' to control the length of generated content id |
+| 2.2.0 | file/upload | Add parameter ''minimum-id-length'' to control the length of generated content id |
| 2.1.1 | file/history | Empty objects (values of `items` and `multipaste_items`) are now always returned as {}. Before they were returned as [] |
| 2.1.1 | file/delete | Empty objects (values of `errors` and `deleted`) are now always returned as {}. Before they were returned as [] |
| 2.1.0 | file/history | Add ''item.thumbnail'' |
diff --git a/doc/api/file.md b/doc/api/file.md
index 7d95274a3..0dcf6a8ad 100644
--- a/doc/api/file.md
+++ b/doc/api/file.md
@@ -58,14 +58,16 @@ Required access level: `basic`
Upload a new file.
-| POST field | Type | Comment |
-| ---------- | ---- | ------- |
-| file[`<index>`] | File | Required. Arbitrary index. |
+| POST field | Type | Comment |
+| ---------- | ---- | ------- |
+| file[`<index>`] | File | Required. Arbitrary index. |
+| minimum-id-length | Int | Optional. Values >= 2 only |
-| error_id | Message | Note |
-| -------- | ------- | ---- |
-| file/no-file | No file was uploaded or unknown error occurred | |
-| file/upload-verify | Failed to verify uploaded file(s) | This error provides additional detail |
+| error_id | Message | Note |
+| -------- | ------- | ---- |
+| file/no-file | No file was uploaded or unknown error occurred | |
+| file/bad-minimum-id-length | Invalid value passsed to bad-minimum-id-length | |
+| file/upload-verify | Failed to verify uploaded file(s) | This error provides additional detail |
```javascript
// Success response
@@ -101,6 +103,10 @@ Example:
}
```
+| Version | Change |
+| ------- | ------ |
+| 2.2.0 | Add parameter ''minimum-id-length'' to control the length of generated content id |
+
## file/history
Return the currently available files/multipastes.
@@ -253,15 +259,17 @@ Required access level: `basic`
Create a new multipaste.
-| POST field | Type | Comment |
-| ---------- | ---- | ------- |
-| ids[`<index>`] | upload-id | Required. Arbitrary index. This only accepts IDs of files, not other multipastes. |
+| POST field | Type | Comment |
+| ---------- | ---- | ------- |
+| ids[`<index>`] | upload-id | Required. Arbitrary index. This only accepts IDs of files, not other multipastes. |
+| minimum-id-length | Int | Optional. Values >= 2 only |
-| error_id | Message | Note |
-| -------- | ------- | ---- |
-| file/create_multipaste/no-ids | No IDs specified | |
-| file/create_multipaste/duplicate-id | Duplicate IDs are not supported | |
-| file/create_multipaste/verify-failed | Failed to verify ID(s) | This error provides additional detail |
+| error_id | Message | Note |
+| -------- | ------- | ---- |
+| file/bad-minimum-id-length | Invalid value passsed to bad-minimum-id-length | |
+| file/create_multipaste/no-ids | No IDs specified | |
+| file/create_multipaste/duplicate-id | Duplicate IDs are not supported | |
+| file/create_multipaste/verify-failed | Failed to verify ID(s) | This error provides additional detail |
```javascript
// Success response
@@ -292,7 +300,8 @@ Example:
}
```
-| Version | Change |
-| ------- | ------ |
-| 1.1.0 | Add url key to response |
-| 1.3.0 | Change required access level from ''apikey'' to ''basic'' |
+| Version | Change |
+| ------- | ------ |
+| 1.1.0 | Add url key to response |
+| 1.3.0 | Change required access level from ''apikey'' to ''basic'' |
+| 2.2.0 | Add paramter ''minimum-id-length'' to control the length of generated content id |
diff --git a/docker/README.md b/docker/README.md
index 32a812cda..870591c9f 100644
--- a/docker/README.md
+++ b/docker/README.md
@@ -4,7 +4,7 @@
Filebin is a paste service developed by Florian Pritz [https://paste.xinu.at/](https://paste.xinu.at/)
## Dockerfile
-[Dockerfile](https://git.server-speed.net/users/flo/filebin/tree/docker/Dockerfile)
+[Dockerfile](https://github.com/Bluewind/filebin/blob/master/Dockerfile)
## Ports
The PHP webserver is listening on ```8080```
@@ -23,6 +23,11 @@ The PHP webserver is listening on ```8080```
- **FB_CONTACT_NAME:** Contact Name
- **FB_CONTACT_MAIL:** Contact E-Mail (will be used as email for the first user)
+- **FB_SMTP_HOST:** Address of the SMTP Server
+- **FB_SMTP_PORT:** Port for SMTP Server (default 587)
+- **FB_SMTP_USER:** Username for SMTP Server (will also be used as mail from)
+- **FB_SMTP_PASSWORD:** Password for the SMTP Server Useraccount
+
## First User
The first user is **admin** with the password **admin**
diff --git a/docker/add_user.sh b/docker/add_user.sh
index 4342528ef..88618746d 100755
--- a/docker/add_user.sh
+++ b/docker/add_user.sh
@@ -1,4 +1,5 @@
#!/bin/bash
cd ${FILEBIN_DIR}
+echo "Creating initial user. If it exists, this will show an error message instead"
printf "%s\n%s\n%s\n" admin ${FB_CONTACT_MAIL} admin | php index.php user add_user
diff --git a/docker/docker-compose.yml b/docker/docker-compose.yml
index 03bc37f59..e6975682f 100644
--- a/docker/docker-compose.yml
+++ b/docker/docker-compose.yml
@@ -2,6 +2,7 @@ version: '2'
services:
mysql:
image: mysql
+ command: --default-authentication-plugin=mysql_native_password
environment:
- MYSQL_DATABASE=filebin
- MYSQL_USER=filebin
@@ -22,4 +23,8 @@ services:
- FB_DB_DATABASE=filebin
- FB_CONTACT_NAME=John Doe
- FB_CONTACT_MAIL=root@example.local
+ - FB_SMTP_HOST=localhost
+ - FB_SMTP_PORT=587
+ - FB_SMTP_USER=webmaster@example.invalid
+ - FB_SMTP_PASSWORD=mysecretpassword
diff --git a/docker/filebin_starter.sh b/docker/filebin_starter.sh
index 4fa50d583..38088537e 100755
--- a/docker/filebin_starter.sh
+++ b/docker/filebin_starter.sh
@@ -1,15 +1,61 @@
#!/bin/bash
+#set -euo pipefail
+
+function set_mail_config() {
+cat <<EOF > ${FILEBIN_HOME_DIR}/msmtprc
+account filebinmail
+tls on
+tls_certcheck off
+auth on
+host ${FB_SMTP_HOST}
+port ${FB_SMTP_PORT}
+user ${FB_SMTP_USER}
+from ${FB_SMTP_USER}
+password ${FB_SMTP_PASSWORD}
+EOF
+
+chmod 600 ${FILEBIN_HOME_DIR}/msmtprc
+}
+
function set_config() {
FB_ENCRYPTION_KEY=`< /dev/urandom tr -dc _A-Z-a-z-0-9 | head -c32`
- sed -i "s/\$config\['encryption_key'\] = ''/\$config['encryption_key'] = '${FB_ENCRYPTION_KEY}'/" ${FILEBIN_DIR}/application/config/config-local.php
+cat <<EOF >${FILEBIN_DIR}/application/config/config-local.php
+<?php
+\$config['base_url'] = 'http://127.0.0.1:8080/';
+\$config['encryption_key'] = '${FB_ENCRYPTION_KEY}';
+\$config['email_from'] = '${FB_SMTP_USER}';
+EOF
}
function set_database_config() {
- sed -i "s/\$db\['default'\]\['hostname'\] = .*/\$db['default']['hostname'] = \"${FB_DB_HOSTNAME}\";/" ${FILEBIN_DIR}/application/config/database.php
- sed -i "s/\$db\['default'\]\['username'\] = .*/\$db['default']['username'] = \"${FB_DB_USERNAME}\";/" ${FILEBIN_DIR}/application/config/database.php
- sed -i "s/\$db\['default'\]\['password'\] = .*/\$db['default']['password'] = \"${FB_DB_PASSWORD}\";/" ${FILEBIN_DIR}/application/config/database.php
- sed -i "s/\$db\['default'\]\['database'\] = .*/\$db['default']['database'] = \"${FB_DB_DATABASE}\";/" ${FILEBIN_DIR}/application/config/database.php
+cat <<EOF >${FILEBIN_DIR}/application/config/database.php
+<?php
+defined('BASEPATH') OR exit('No direct script access allowed');
+\$active_group = 'default';
+\$query_builder = TRUE;
+
+\$db['default'] = array(
+ 'dsn' => 'mysql:host=${FB_DB_HOSTNAME};dbname=${FB_DB_DATABASE}',
+ 'hostname' => '',
+ 'port' => 3306,
+ 'username' => '${FB_DB_USERNAME}',
+ 'password' => '${FB_DB_PASSWORD}',
+ 'database' => '${FB_DB_DATABASE}',
+ 'dbdriver' => 'pdo',
+ 'dbprefix' => '',
+ 'pconnect' => FALSE,
+ 'db_debug' => TRUE,
+ 'char_set' => 'utf8mb4', // if you use postgres, set this to utf8
+ 'dbcollat' => 'utf8mb4_bin', // if you use postgres, set this to utf8_bin
+ 'swap_pre' => '',
+ 'encrypt' => FALSE,
+ 'compress' => FALSE,
+ 'stricton' => TRUE,
+ 'failover' => array(),
+ 'save_queries' => TRUE
+);
+EOF
}
# wait for DB to be ready
@@ -18,25 +64,20 @@ while ! nc "$FB_DB_HOSTNAME" 3306 </dev/null >/dev/null; do
sleep 0.5
done
+set_config
+set_database_config
+set_mail_config
-if [[ ! -e $FILEBIN_DIR/application/config/config-local.php ]]; then
- echo "no config found, new config will be generated"
- cp $FILEBIN_DIR/application/config/example/config-local.php ${FILEBIN_DIR}/application/config/config-local.php
-
- set_config
- set_database_config
-
- CONTACT_INFO_FILE=${FILEBIN_DIR}/data/local/contact-info.php
- cp $FILEBIN_DIR/data/local/examples/contact-info.php ${CONTACT_INFO_FILE}
+CONTACT_INFO_FILE=${FILEBIN_DIR}/data/local/contact-info.php
+cp $FILEBIN_DIR/data/local/examples/contact-info.php ${CONTACT_INFO_FILE}
- sed -i "s/John Doe/${FB_CONTACT_NAME}/" ${CONTACT_INFO_FILE}
- sed -i "s/john.doe@example.com/${FB_CONTACT_MAIL}/" ${CONTACT_INFO_FILE}
+sed -i "s/John Doe/${FB_CONTACT_NAME}/" ${CONTACT_INFO_FILE}
+sed -i "s/john.doe@example.com/${FB_CONTACT_MAIL}/" ${CONTACT_INFO_FILE}
- ${FILEBIN_DIR}/scripts/install-git-hooks.sh
- ${FILEBIN_DIR}/git-hooks/post-merge
+${FILEBIN_DIR}/scripts/install-git-hooks.sh
+${FILEBIN_DIR}/git-hooks/post-merge
- ${FILEBIN_HOME_DIR}/add_user.sh
-fi
+${FILEBIN_HOME_DIR}/add_user.sh
cd $FILEBIN_DIR/public_html
php -S 0.0.0.0:8080
diff --git a/license.txt b/license.txt
index 0973fd37b..82be622d9 100644
--- a/license.txt
+++ b/license.txt
@@ -1,51 +1,21 @@
-Copyright (c) 2008 - 2014, EllisLab, Inc.
-All rights reserved.
-
-This license is a legal agreement between you and EllisLab Inc. for the use
-of CodeIgniter Software (the "Software"). By obtaining the Software you
-agree to comply with the terms and conditions of this license.
-
-PERMITTED USE
-You are permitted to use, copy, modify, and distribute the Software and its
-documentation, with or without modification, for any purpose, provided that
-the following conditions are met:
-
-1. A copy of this license agreement must be included with the distribution.
-
-2. Redistributions of source code must retain the above copyright notice in
- all source code files.
-
-3. Redistributions in binary form must reproduce the above copyright notice
- in the documentation and/or other materials provided with the distribution.
-
-4. Any files that have been modified must carry notices stating the nature
- of the change and the names of those who changed them.
-
-5. Products derived from the Software must include an acknowledgment that
- they are derived from CodeIgniter in their documentation and/or other
- materials provided with the distribution.
-
-6. Products derived from the Software may not be called "CodeIgniter",
- nor may "CodeIgniter" appear in their name, without prior written
- permission from EllisLab, Inc.
-
-INDEMNITY
-You agree to indemnify and hold harmless the authors of the Software and
-any contributors for any direct, indirect, incidental, or consequential
-third-party claims, actions or suits, as well as any related expenses,
-liabilities, damages, settlements or fees arising from your use or misuse
-of the Software, or a violation of any terms of this license.
-
-DISCLAIMER OF WARRANTY
-THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESSED OR
-IMPLIED, INCLUDING, BUT NOT LIMITED TO, WARRANTIES OF QUALITY, PERFORMANCE,
-NON-INFRINGEMENT, MERCHANTABILITY, OR FITNESS FOR A PARTICULAR PURPOSE.
-
-LIMITATIONS OF LIABILITY
-YOU ASSUME ALL RISK ASSOCIATED WITH THE INSTALLATION AND USE OF THE SOFTWARE.
-IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS OF THE SOFTWARE BE LIABLE
-FOR CLAIMS, DAMAGES OR OTHER LIABILITY ARISING FROM, OUT OF, OR IN CONNECTION
-WITH THE SOFTWARE. LICENSE HOLDERS ARE SOLELY RESPONSIBLE FOR DETERMINING THE
-APPROPRIATENESS OF USE AND ASSUME ALL RISKS ASSOCIATED WITH ITS USE, INCLUDING
-BUT NOT LIMITED TO THE RISKS OF PROGRAM ERRORS, DAMAGE TO EQUIPMENT, LOSS OF
-DATA OR SOFTWARE PROGRAMS, OR UNAVAILABILITY OR INTERRUPTION OF OPERATIONS.
+The MIT License (MIT)
+
+Copyright (c) 2019 - 2022, CodeIgniter Foundation
+
+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.
diff --git a/public_html/data/css/asciinema-player.css b/public_html/data/css/asciinema-player.css
index 8df91b964..8d77df46e 100644
--- a/public_html/data/css/asciinema-player.css
+++ b/public_html/data/css/asciinema-player.css
@@ -176,23 +176,24 @@
.asciinema-terminal.font-big {
font-size: 24px;
}
-.asciinema-player:hover .control-bar {
- color: #ffffff;
-}
-.asciinema-player:hover .control-bar .progressbar .bar .gutter span {
- background-color: #ffffff;
-}
-.asciinema-player:hover .control-bar svg.icon path {
- fill: #ffffff;
-}
.asciinema-player .control-bar {
width: 100%;
height: 32px;
- background-color: #000;
+ background: rgba(0, 0, 0, 0.8);
+ /* no gradient fallback */
+ background: -moz-linear-gradient(top, rgba(0, 0, 0, 0.5) 0%, #000000 25%, #000000 100%);
+ /* FF3.6-15 */
+ background: -webkit-linear-gradient(top, rgba(0, 0, 0, 0.5) 0%, #000000 25%, #000000 100%);
+ /* Chrome10-25,Safari5.1-6 */
+ background: linear-gradient(to bottom, rgba(0, 0, 0, 0.5) 0%, #000000 25%, #000000 100%);
+ /* W3C, IE10+, FF16+, Chrome26+, Opera12+, Safari7+ */
color: #bbbbbb;
- border-top: 1px solid #222;
box-sizing: content-box;
line-height: 1;
+ position: absolute;
+ bottom: -35px;
+ left: 0;
+ transition: bottom 0.15s linear;
}
.asciinema-player .control-bar * {
box-sizing: inherit;
@@ -283,66 +284,33 @@
.asciinema-player .control-bar .fullscreen-button svg:last-child {
display: none;
}
+.asciinema-player-wrapper.hud .control-bar {
+ bottom: 0px;
+}
.asciinema-player-wrapper:fullscreen .fullscreen-button svg:first-child {
display: none;
}
.asciinema-player-wrapper:fullscreen .fullscreen-button svg:last-child {
display: inline;
}
-.asciinema-player-wrapper:fullscreen .control-bar {
- position: absolute;
- bottom: -35px;
- left: 0;
- transition: bottom 0.15s linear;
-}
-.asciinema-player-wrapper:fullscreen.hud .control-bar {
- bottom: 0px;
-}
.asciinema-player-wrapper:-webkit-full-screen .fullscreen-button svg:first-child {
display: none;
}
.asciinema-player-wrapper:-webkit-full-screen .fullscreen-button svg:last-child {
display: inline;
}
-.asciinema-player-wrapper:-webkit-full-screen .control-bar {
- position: absolute;
- bottom: -35px;
- left: 0;
- transition: bottom 0.15s linear;
-}
-.asciinema-player-wrapper:-webkit-full-screen.hud .control-bar {
- bottom: 0px;
-}
.asciinema-player-wrapper:-moz-full-screen .fullscreen-button svg:first-child {
display: none;
}
.asciinema-player-wrapper:-moz-full-screen .fullscreen-button svg:last-child {
display: inline;
}
-.asciinema-player-wrapper:-moz-full-screen .control-bar {
- position: absolute;
- bottom: -35px;
- left: 0;
- transition: bottom 0.15s linear;
-}
-.asciinema-player-wrapper:-moz-full-screen.hud .control-bar {
- bottom: 0px;
-}
.asciinema-player-wrapper:-ms-fullscreen .fullscreen-button svg:first-child {
display: none;
}
.asciinema-player-wrapper:-ms-fullscreen .fullscreen-button svg:last-child {
display: inline;
}
-.asciinema-player-wrapper:-ms-fullscreen .control-bar {
- position: absolute;
- bottom: -35px;
- left: 0;
- transition: bottom 0.15s linear;
-}
-.asciinema-player-wrapper:-ms-fullscreen.hud .control-bar {
- bottom: 0px;
-}
.asciinema-player .loading {
z-index: 10;
background-repeat: no-repeat;
@@ -2263,10 +2231,10 @@
background-color: #657b83;
}
.asciinema-theme-solarized-light .fg-0 {
- color: #eee8d5;
+ color: #073642;
}
.asciinema-theme-solarized-light .bg-0 {
- background-color: #eee8d5;
+ background-color: #073642;
}
.asciinema-theme-solarized-light .fg-1 {
color: #dc322f;
@@ -2305,16 +2273,16 @@
background-color: #2aa198;
}
.asciinema-theme-solarized-light .fg-7 {
- color: #073642;
+ color: #eee8d5;
}
.asciinema-theme-solarized-light .bg-7 {
- background-color: #073642;
+ background-color: #eee8d5;
}
.asciinema-theme-solarized-light .fg-8 {
- color: #fdf6e3;
+ color: #002b36;
}
.asciinema-theme-solarized-light .bg-8 {
- background-color: #fdf6e3;
+ background-color: #002b36;
}
.asciinema-theme-solarized-light .fg-9 {
color: #cb4b16;
@@ -2323,22 +2291,22 @@
background-color: #cb4b16;
}
.asciinema-theme-solarized-light .fg-10 {
- color: #93a1a1;
+ color: #586e75;
}
.asciinema-theme-solarized-light .bg-10 {
- background-color: #93a1a1;
+ background-color: #586e75;
}
.asciinema-theme-solarized-light .fg-11 {
- color: #839496;
+ color: #657c83;
}
.asciinema-theme-solarized-light .bg-11 {
- background-color: #839496;
+ background-color: #657c83;
}
.asciinema-theme-solarized-light .fg-12 {
- color: #657b83;
+ color: #839496;
}
.asciinema-theme-solarized-light .bg-12 {
- background-color: #657b83;
+ background-color: #839496;
}
.asciinema-theme-solarized-light .fg-13 {
color: #6c71c4;
@@ -2347,16 +2315,16 @@
background-color: #6c71c4;
}
.asciinema-theme-solarized-light .fg-14 {
- color: #586e75;
+ color: #93a1a1;
}
.asciinema-theme-solarized-light .bg-14 {
- background-color: #586e75;
+ background-color: #93a1a1;
}
.asciinema-theme-solarized-light .fg-15 {
- color: #002b36;
+ color: #fdf6e3;
}
.asciinema-theme-solarized-light .bg-15 {
- background-color: #002b36;
+ background-color: #fdf6e3;
}
.asciinema-theme-seti .asciinema-terminal {
color: #cacecd;
diff --git a/public_html/data/js/thumbnail-view.js b/public_html/data/js/thumbnail-view.js
index dc2f547ab..eb9ee33ce 100644
--- a/public_html/data/js/thumbnail-view.js
+++ b/public_html/data/js/thumbnail-view.js
@@ -20,7 +20,19 @@ define(['jquery', 'underscore', 'multipaste', 'jquery.colorbox'], function ($, _
$(window).resize(_.bind(this.onResize, this));
},
+ browserHandlesImageOrientation: function () {
+ var testImg = $('<img>');
+ $('body').append(testImg);
+ var style = window.getComputedStyle(testImg.get(0));
+ var result = style.getPropertyValue('image-orientation')
+ console.log('Browser default image-orientation: ', result)
+ testImg.remove();
+ return result == 'from-image';
+ },
+
setupColorbox: function () {
+ var browserHandlesImageOrientation = PrivateFunctions.browserHandlesImageOrientation();
+
$(ui.colorbox).colorbox({
transistion: "none",
speed: 0,
@@ -36,7 +48,11 @@ define(['jquery', 'underscore', 'multipaste', 'jquery.colorbox'], function ($, _
close: '<span class="glyphicon glyphicon-remove"></span>',
loop: false,
orientation: function() {
- return $(this).data('orientation');
+ if (browserHandlesImageOrientation) {
+ return 1;
+ } else {
+ return $(this).data('orientation');
+ }
},
});
},
diff --git a/public_html/data/js/vendor/asciinema-player.js b/public_html/data/js/vendor/asciinema-player.js
index 64320e418..5ad47e08b 100644
--- a/public_html/data/js/vendor/asciinema-player.js
+++ b/public_html/data/js/vendor/asciinema-player.js
@@ -1,11 +1,38 @@
/**
- * asciinema-player v2.2.0
+ * asciinema-player v2.6.1
*
- * Copyright 2011-2016, Marcin Kulik
- * All rights reserved.
+ * Copyright 2011-2018, Marcin Kulik
*
*/
+// CustomEvent polyfill from MDN (https://developer.mozilla.org/en-US/docs/Web/API/CustomEvent/CustomEvent)
+
+(function () {
+ if (typeof window.CustomEvent === "function") return false;
+
+ function CustomEvent ( event, params ) {
+ params = params || { bubbles: false, cancelable: false, detail: undefined };
+ var evt = document.createEvent( 'CustomEvent');
+ evt.initCustomEvent(event, params.bubbles, params.cancelable, params.detail);
+ return evt;
+ }
+
+ CustomEvent.prototype = window.Event.prototype;
+
+ window.CustomEvent = CustomEvent;
+})();
+
+/**
+ * @license
+ * Copyright (c) 2014 The Polymer Project Authors. All rights reserved.
+ * This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt
+ * The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt
+ * The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt
+ * Code distributed by Google as part of the polymer project is also
+ * subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt
+ */
+// @version 0.7.22
+"undefined"==typeof WeakMap&&!function(){var e=Object.defineProperty,t=Date.now()%1e9,n=function(){this.name="__st"+(1e9*Math.random()>>>0)+(t++ +"__")};n.prototype={set:function(t,n){var o=t[this.name];return o&&o[0]===t?o[1]=n:e(t,this.name,{value:[t,n],writable:!0}),this},get:function(e){var t;return(t=e[this.name])&&t[0]===e?t[1]:void 0},"delete":function(e){var t=e[this.name];return t&&t[0]===e?(t[0]=t[1]=void 0,!0):!1},has:function(e){var t=e[this.name];return t?t[0]===e:!1}},window.WeakMap=n}(),function(e){function t(e){E.push(e),b||(b=!0,w(o))}function n(e){return window.ShadowDOMPolyfill&&window.ShadowDOMPolyfill.wrapIfNeeded(e)||e}function o(){b=!1;var e=E;E=[],e.sort(function(e,t){return e.uid_-t.uid_});var t=!1;e.forEach(function(e){var n=e.takeRecords();r(e),n.length&&(e.callback_(n,e),t=!0)}),t&&o()}function r(e){e.nodes_.forEach(function(t){var n=v.get(t);n&&n.forEach(function(t){t.observer===e&&t.removeTransientObservers()})})}function i(e,t){for(var n=e;n;n=n.parentNode){var o=v.get(n);if(o)for(var r=0;r<o.length;r++){var i=o[r],a=i.options;if(n===e||a.subtree){var d=t(a);d&&i.enqueue(d)}}}}function a(e){this.callback_=e,this.nodes_=[],this.records_=[],this.uid_=++_}function d(e,t){this.type=e,this.target=t,this.addedNodes=[],this.removedNodes=[],this.previousSibling=null,this.nextSibling=null,this.attributeName=null,this.attributeNamespace=null,this.oldValue=null}function s(e){var t=new d(e.type,e.target);return t.addedNodes=e.addedNodes.slice(),t.removedNodes=e.removedNodes.slice(),t.previousSibling=e.previousSibling,t.nextSibling=e.nextSibling,t.attributeName=e.attributeName,t.attributeNamespace=e.attributeNamespace,t.oldValue=e.oldValue,t}function u(e,t){return y=new d(e,t)}function c(e){return N?N:(N=s(y),N.oldValue=e,N)}function l(){y=N=void 0}function f(e){return e===N||e===y}function p(e,t){return e===t?e:N&&f(e)?N:null}function m(e,t,n){this.observer=e,this.target=t,this.options=n,this.transientObservedNodes=[]}if(!e.JsMutationObserver){var w,v=new WeakMap;if(/Trident|Edge/.test(navigator.userAgent))w=setTimeout;else if(window.setImmediate)w=window.setImmediate;else{var h=[],g=String(Math.random());window.addEventListener("message",function(e){if(e.data===g){var t=h;h=[],t.forEach(function(e){e()})}}),w=function(e){h.push(e),window.postMessage(g,"*")}}var b=!1,E=[],_=0;a.prototype={observe:function(e,t){if(e=n(e),!t.childList&&!t.attributes&&!t.characterData||t.attributeOldValue&&!t.attributes||t.attributeFilter&&t.attributeFilter.length&&!t.attributes||t.characterDataOldValue&&!t.characterData)throw new SyntaxError;var o=v.get(e);o||v.set(e,o=[]);for(var r,i=0;i<o.length;i++)if(o[i].observer===this){r=o[i],r.removeListeners(),r.options=t;break}r||(r=new m(this,e,t),o.push(r),this.nodes_.push(e)),r.addListeners()},disconnect:function(){this.nodes_.forEach(function(e){for(var t=v.get(e),n=0;n<t.length;n++){var o=t[n];if(o.observer===this){o.removeListeners(),t.splice(n,1);break}}},this),this.records_=[]},takeRecords:function(){var e=this.records_;return this.records_=[],e}};var y,N;m.prototype={enqueue:function(e){var n=this.observer.records_,o=n.length;if(n.length>0){var r=n[o-1],i=p(r,e);if(i)return void(n[o-1]=i)}else t(this.observer);n[o]=e},addListeners:function(){this.addListeners_(this.target)},addListeners_:function(e){var t=this.options;t.attributes&&e.addEventListener("DOMAttrModified",this,!0),t.characterData&&e.addEventListener("DOMCharacterDataModified",this,!0),t.childList&&e.addEventListener("DOMNodeInserted",this,!0),(t.childList||t.subtree)&&e.addEventListener("DOMNodeRemoved",this,!0)},removeListeners:function(){this.removeListeners_(this.target)},removeListeners_:function(e){var t=this.options;t.attributes&&e.removeEventListener("DOMAttrModified",this,!0),t.characterData&&e.removeEventListener("DOMCharacterDataModified",this,!0),t.childList&&e.removeEventListener("DOMNodeInserted",this,!0),(t.childList||t.subtree)&&e.removeEventListener("DOMNodeRemoved",this,!0)},addTransientObserver:function(e){if(e!==this.target){this.addListeners_(e),this.transientObservedNodes.push(e);var t=v.get(e);t||v.set(e,t=[]),t.push(this)}},removeTransientObservers:function(){var e=this.transientObservedNodes;this.transientObservedNodes=[],e.forEach(function(e){this.removeListeners_(e);for(var t=v.get(e),n=0;n<t.length;n++)if(t[n]===this){t.splice(n,1);break}},this)},handleEvent:function(e){switch(e.stopImmediatePropagation(),e.type){case"DOMAttrModified":var t=e.attrName,n=e.relatedNode.namespaceURI,o=e.target,r=new u("attributes",o);r.attributeName=t,r.attributeNamespace=n;var a=e.attrChange===MutationEvent.ADDITION?null:e.prevValue;i(o,function(e){return!e.attributes||e.attributeFilter&&e.attributeFilter.length&&-1===e.attributeFilter.indexOf(t)&&-1===e.attributeFilter.indexOf(n)?void 0:e.attributeOldValue?c(a):r});break;case"DOMCharacterDataModified":var o=e.target,r=u("characterData",o),a=e.prevValue;i(o,function(e){return e.characterData?e.characterDataOldValue?c(a):r:void 0});break;case"DOMNodeRemoved":this.addTransientObserver(e.target);case"DOMNodeInserted":var d,s,f=e.target;"DOMNodeInserted"===e.type?(d=[f],s=[]):(d=[],s=[f]);var p=f.previousSibling,m=f.nextSibling,r=u("childList",e.target.parentNode);r.addedNodes=d,r.removedNodes=s,r.previousSibling=p,r.nextSibling=m,i(e.relatedNode,function(e){return e.childList?r:void 0})}l()}},e.JsMutationObserver=a,e.MutationObserver||(e.MutationObserver=a,a._isPolyfilled=!0)}}(self),function(e){"use strict";if(!window.performance){var t=Date.now();window.performance={now:function(){return Date.now()-t}}}window.requestAnimationFrame||(window.requestAnimationFrame=function(){var e=window.webkitRequestAnimationFrame||window.mozRequestAnimationFrame;return e?function(t){return e(function(){t(performance.now())})}:function(e){return window.setTimeout(e,1e3/60)}}()),window.cancelAnimationFrame||(window.cancelAnimationFrame=function(){return window.webkitCancelAnimationFrame||window.mozCancelAnimationFrame||function(e){clearTimeout(e)}}());var n=function(){var e=document.createEvent("Event");return e.initEvent("foo",!0,!0),e.preventDefault(),e.defaultPrevented}();if(!n){var o=Event.prototype.preventDefault;Event.prototype.preventDefault=function(){this.cancelable&&(o.call(this),Object.defineProperty(this,"defaultPrevented",{get:function(){return!0},configurable:!0}))}}var r=/Trident/.test(navigator.userAgent);if((!window.CustomEvent||r&&"function"!=typeof window.CustomEvent)&&(window.CustomEvent=function(e,t){t=t||{};var n=document.createEvent("CustomEvent");return n.initCustomEvent(e,Boolean(t.bubbles),Boolean(t.cancelable),t.detail),n},window.CustomEvent.prototype=window.Event.prototype),!window.Event||r&&"function"!=typeof window.Event){var i=window.Event;window.Event=function(e,t){t=t||{};var n=document.createEvent("Event");return n.initEvent(e,Boolean(t.bubbles),Boolean(t.cancelable)),n},window.Event.prototype=i.prototype}}(window.WebComponents),window.CustomElements=window.CustomElements||{flags:{}},function(e){var t=e.flags,n=[],o=function(e){n.push(e)},r=function(){n.forEach(function(t){t(e)})};e.addModule=o,e.initializeModules=r,e.hasNative=Boolean(document.registerElement),e.isIE=/Trident/.test(navigator.userAgent),e.useNative=!t.register&&e.hasNative&&!window.ShadowDOMPolyfill&&(!window.HTMLImports||window.HTMLImports.useNative)}(window.CustomElements),window.CustomElements.addModule(function(e){function t(e,t){n(e,function(e){return t(e)?!0:void o(e,t)}),o(e,t)}function n(e,t,o){var r=e.firstElementChild;if(!r)for(r=e.firstChild;r&&r.nodeType!==Node.ELEMENT_NODE;)r=r.nextSibling;for(;r;)t(r,o)!==!0&&n(r,t,o),r=r.nextElementSibling;return null}function o(e,n){for(var o=e.shadowRoot;o;)t(o,n),o=o.olderShadowRoot}function r(e,t){i(e,t,[])}function i(e,t,n){if(e=window.wrap(e),!(n.indexOf(e)>=0)){n.push(e);for(var o,r=e.querySelectorAll("link[rel="+a+"]"),d=0,s=r.length;s>d&&(o=r[d]);d++)o["import"]&&i(o["import"],t,n);t(e)}}var a=window.HTMLImports?window.HTMLImports.IMPORT_LINK_TYPE:"none";e.forDocumentTree=r,e.forSubtree=t}),window.CustomElements.addModule(function(e){function t(e,t){return n(e,t)||o(e,t)}function n(t,n){return e.upgrade(t,n)?!0:void(n&&a(t))}function o(e,t){b(e,function(e){return n(e,t)?!0:void 0})}function r(e){N.push(e),y||(y=!0,setTimeout(i))}function i(){y=!1;for(var e,t=N,n=0,o=t.length;o>n&&(e=t[n]);n++)e();N=[]}function a(e){_?r(function(){d(e)}):d(e)}function d(e){e.__upgraded__&&!e.__attached&&(e.__attached=!0,e.attachedCallback&&e.attachedCallback())}function s(e){u(e),b(e,function(e){u(e)})}function u(e){_?r(function(){c(e)}):c(e)}function c(e){e.__upgraded__&&e.__attached&&(e.__attached=!1,e.detachedCallback&&e.detachedCallback())}function l(e){for(var t=e,n=window.wrap(document);t;){if(t==n)return!0;t=t.parentNode||t.nodeType===Node.DOCUMENT_FRAGMENT_NODE&&t.host}}function f(e){if(e.shadowRoot&&!e.shadowRoot.__watched){g.dom&&console.log("watching shadow-root for: ",e.localName);for(var t=e.shadowRoot;t;)w(t),t=t.olderShadowRoot}}function p(e,n){if(g.dom){var o=n[0];if(o&&"childList"===o.type&&o.addedNodes&&o.addedNodes){for(var r=o.addedNodes[0];r&&r!==document&&!r.host;)r=r.parentNode;var i=r&&(r.URL||r._URL||r.host&&r.host.localName)||"";i=i.split("/?").shift().split("/").pop()}console.group("mutations (%d) [%s]",n.length,i||"")}var a=l(e);n.forEach(function(e){"childList"===e.type&&(M(e.addedNodes,function(e){e.localName&&t(e,a)}),M(e.removedNodes,function(e){e.localName&&s(e)}))}),g.dom&&console.groupEnd()}function m(e){for(e=window.wrap(e),e||(e=window.wrap(document));e.parentNode;)e=e.parentNode;var t=e.__observer;t&&(p(e,t.takeRecords()),i())}function w(e){if(!e.__observer){var t=new MutationObserver(p.bind(this,e));t.observe(e,{childList:!0,subtree:!0}),e.__observer=t}}function v(e){e=window.wrap(e),g.dom&&console.group("upgradeDocument: ",e.baseURI.split("/").pop());var n=e===window.wrap(document);t(e,n),w(e),g.dom&&console.groupEnd()}function h(e){E(e,v)}var g=e.flags,b=e.forSubtree,E=e.forDocumentTree,_=window.MutationObserver._isPolyfilled&&g["throttle-attached"];e.hasPolyfillMutations=_,e.hasThrottledAttached=_;var y=!1,N=[],M=Array.prototype.forEach.call.bind(Array.prototype.forEach),O=Element.prototype.createShadowRoot;O&&(Element.prototype.createShadowRoot=function(){var e=O.call(this);return window.CustomElements.watchShadow(this),e}),e.watchShadow=f,e.upgradeDocumentTree=h,e.upgradeDocument=v,e.upgradeSubtree=o,e.upgradeAll=t,e.attached=a,e.takeRecords=m}),window.CustomElements.addModule(function(e){function t(t,o){if("template"===t.localName&&window.HTMLTemplateElement&&HTMLTemplateElement.decorate&&HTMLTemplateElement.decorate(t),!t.__upgraded__&&t.nodeType===Node.ELEMENT_NODE){var r=t.getAttribute("is"),i=e.getRegisteredDefinition(t.localName)||e.getRegisteredDefinition(r);if(i&&(r&&i.tag==t.localName||!r&&!i["extends"]))return n(t,i,o)}}function n(t,n,r){return a.upgrade&&console.group("upgrade:",t.localName),n.is&&t.setAttribute("is",n.is),o(t,n),t.__upgraded__=!0,i(t),r&&e.attached(t),e.upgradeSubtree(t,r),a.upgrade&&console.groupEnd(),t}function o(e,t){Object.__proto__?e.__proto__=t.prototype:(r(e,t.prototype,t["native"]),e.__proto__=t.prototype)}function r(e,t,n){for(var o={},r=t;r!==n&&r!==HTMLElement.prototype;){for(var i,a=Object.getOwnPropertyNames(r),d=0;i=a[d];d++)o[i]||(Object.defineProperty(e,i,Object.getOwnPropertyDescriptor(r,i)),o[i]=1);r=Object.getPrototypeOf(r)}}function i(e){e.createdCallback&&e.createdCallback()}var a=e.flags;e.upgrade=t,e.upgradeWithDefinition=n,e.implementPrototype=o}),window.CustomElements.addModule(function(e){function t(t,o){var s=o||{};if(!t)throw new Error("document.registerElement: first argument `name` must not be empty");if(t.indexOf("-")<0)throw new Error("document.registerElement: first argument ('name') must contain a dash ('-'). Argument provided was '"+String(t)+"'.");if(r(t))throw new Error("Failed to execute 'registerElement' on 'Document': Registration failed for type '"+String(t)+"'. The type name is invalid.");if(u(t))throw new Error("DuplicateDefinitionError: a type with name '"+String(t)+"' is already registered");return s.prototype||(s.prototype=Object.create(HTMLElement.prototype)),s.__name=t.toLowerCase(),s["extends"]&&(s["extends"]=s["extends"].toLowerCase()),s.lifecycle=s.lifecycle||{},s.ancestry=i(s["extends"]),a(s),d(s),n(s.prototype),c(s.__name,s),s.ctor=l(s),s.ctor.prototype=s.prototype,s.prototype.constructor=s.ctor,e.ready&&v(document),s.ctor}function n(e){if(!e.setAttribute._polyfilled){var t=e.setAttribute;e.setAttribute=function(e,n){o.call(this,e,n,t)};var n=e.removeAttribute;e.removeAttribute=function(e){o.call(this,e,null,n)},e.setAttribute._polyfilled=!0}}function o(e,t,n){e=e.toLowerCase();var o=this.getAttribute(e);n.apply(this,arguments);var r=this.getAttribute(e);this.attributeChangedCallback&&r!==o&&this.attributeChangedCallback(e,o,r)}function r(e){for(var t=0;t<_.length;t++)if(e===_[t])return!0}function i(e){var t=u(e);return t?i(t["extends"]).concat([t]):[]}function a(e){for(var t,n=e["extends"],o=0;t=e.ancestry[o];o++)n=t.is&&t.tag;e.tag=n||e.__name,n&&(e.is=e.__name)}function d(e){if(!Object.__proto__){var t=HTMLElement.prototype;if(e.is){var n=document.createElement(e.tag);t=Object.getPrototypeOf(n)}for(var o,r=e.prototype,i=!1;r;)r==t&&(i=!0),o=Object.getPrototypeOf(r),o&&(r.__proto__=o),r=o;i||console.warn(e.tag+" prototype not found in prototype chain for "+e.is),e["native"]=t}}function s(e){return g(M(e.tag),e)}function u(e){return e?y[e.toLowerCase()]:void 0}function c(e,t){y[e]=t}function l(e){return function(){return s(e)}}function f(e,t,n){return e===N?p(t,n):O(e,t)}function p(e,t){e&&(e=e.toLowerCase()),t&&(t=t.toLowerCase());var n=u(t||e);if(n){if(e==n.tag&&t==n.is)return new n.ctor;if(!t&&!n.is)return new n.ctor}var o;return t?(o=p(e),o.setAttribute("is",t),o):(o=M(e),e.indexOf("-")>=0&&b(o,HTMLElement),o)}function m(e,t){var n=e[t];e[t]=function(){var e=n.apply(this,arguments);return h(e),e}}var w,v=(e.isIE,e.upgradeDocumentTree),h=e.upgradeAll,g=e.upgradeWithDefinition,b=e.implementPrototype,E=e.useNative,_=["annotation-xml","color-profile","font-face","font-face-src","font-face-uri","font-face-format","font-face-name","missing-glyph"],y={},N="http://www.w3.org/1999/xhtml",M=document.createElement.bind(document),O=document.createElementNS.bind(document);w=Object.__proto__||E?function(e,t){return e instanceof t}:function(e,t){if(e instanceof t)return!0;for(var n=e;n;){if(n===t.prototype)return!0;n=n.__proto__}return!1},m(Node.prototype,"cloneNode"),m(document,"importNode"),document.registerElement=t,document.createElement=p,document.createElementNS=f,e.registry=y,e["instanceof"]=w,e.reservedTagList=_,e.getRegisteredDefinition=u,document.register=document.registerElement}),function(e){function t(){i(window.wrap(document)),window.CustomElements.ready=!0;var e=window.requestAnimationFrame||function(e){setTimeout(e,16)};e(function(){setTimeout(function(){window.CustomElements.readyTime=Date.now(),window.HTMLImports&&(window.CustomElements.elapsed=window.CustomElements.readyTime-window.HTMLImports.readyTime),document.dispatchEvent(new CustomEvent("WebComponentsReady",{bubbles:!0}))})})}var n=e.useNative,o=e.initializeModules;e.isIE;if(n){var r=function(){};e.watchShadow=r,e.upgrade=r,e.upgradeAll=r,e.upgradeDocumentTree=r,e.upgradeSubtree=r,e.takeRecords=r,e["instanceof"]=function(e,t){return e instanceof t}}else o();var i=e.upgradeDocumentTree,a=e.upgradeDocument;if(window.wrap||(window.ShadowDOMPolyfill?(window.wrap=window.ShadowDOMPolyfill.wrapIfNeeded,window.unwrap=window.ShadowDOMPolyfill.unwrapIfNeeded):window.wrap=window.unwrap=function(e){return e}),window.HTMLImports&&(window.HTMLImports.__importsParsingHook=function(e){e["import"]&&a(wrap(e["import"]))}),"complete"===document.readyState||e.flags.eager)t();else if("interactive"!==document.readyState||window.attachEvent||window.HTMLImports&&!window.HTMLImports.ready){var d=window.HTMLImports&&!window.HTMLImports.ready?"HTMLImportsLoaded":"DOMContentLoaded";window.addEventListener(d,t)}else t()}(window.CustomElements);
if(typeof Math.imul == "undefined" || (Math.imul(0xffffffff,5) == 0)) {
Math.imul = function (a, b) {
var ah = (a >>> 16) & 0xffff;
@@ -19,9 +46,9 @@ if(typeof Math.imul == "undefined" || (Math.imul(0xffffffff,5) == 0)) {
}
/**
- * React v0.13.3
+ * React v15.5.4
*
- * Copyright 2013-2015, Facebook, Inc.
+ * Copyright 2013-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
@@ -29,881 +56,1158 @@ if(typeof Math.imul == "undefined" || (Math.imul(0xffffffff,5) == 0)) {
* of patent rights can be found in the PATENTS file in the same directory.
*
*/
-!function(e){if("object"==typeof exports&&"undefined"!=typeof module)module.exports=e();else if("function"==typeof define&&define.amd)define([],e);else{var t;t="undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof self?self:this,t.React=e()}}(function(){return function e(t,n,r){function o(a,u){if(!n[a]){if(!t[a]){var s="function"==typeof require&&require;if(!u&&s)return s(a,!0);if(i)return i(a,!0);var l=new Error("Cannot find module '"+a+"'");throw l.code="MODULE_NOT_FOUND",l}var c=n[a]={exports:{}};t[a][0].call(c.exports,function(e){var n=t[a][1][e];return o(n?n:e)},c,c.exports,e,t,n,r)}return n[a].exports}for(var i="function"==typeof require&&require,a=0;a<r.length;a++)o(r[a]);return o}({1:[function(e,t,n){"use strict";var r=e(19),o=e(32),i=e(34),a=e(33),u=e(38),s=e(39),l=e(55),c=(e(56),e(40)),p=e(51),d=e(54),f=e(64),h=e(68),m=e(73),v=e(76),g=e(79),y=e(82),C=e(27),E=e(115),b=e(142);d.inject();var _=l.createElement,x=l.createFactory,D=l.cloneElement,M=m.measure("React","render",h.render),N={Children:{map:o.map,forEach:o.forEach,count:o.count,only:b},Component:i,DOM:c,PropTypes:v,initializeTouchEvents:function(e){r.useTouchEvents=e},createClass:a.createClass,createElement:_,cloneElement:D,createFactory:x,createMixin:function(e){return e},constructAndRenderComponent:h.constructAndRenderComponent,constructAndRenderComponentByID:h.constructAndRenderComponentByID,findDOMNode:E,render:M,renderToString:y.renderToString,renderToStaticMarkup:y.renderToStaticMarkup,unmountComponentAtNode:h.unmountComponentAtNode,isValidElement:l.isValidElement,withContext:u.withContext,__spread:C};"undefined"!=typeof __REACT_DEVTOOLS_GLOBAL_HOOK__&&"function"==typeof __REACT_DEVTOOLS_GLOBAL_HOOK__.inject&&__REACT_DEVTOOLS_GLOBAL_HOOK__.inject({CurrentOwner:s,InstanceHandles:f,Mount:h,Reconciler:g,TextComponent:p});N.version="0.13.3",t.exports=N},{115:115,142:142,19:19,27:27,32:32,33:33,34:34,38:38,39:39,40:40,51:51,54:54,55:55,56:56,64:64,68:68,73:73,76:76,79:79,82:82}],2:[function(e,t,n){"use strict";var r=e(117),o={componentDidMount:function(){this.props.autoFocus&&r(this.getDOMNode())}};t.exports=o},{117:117}],3:[function(e,t,n){"use strict";function r(){var e=window.opera;return"object"==typeof e&&"function"==typeof e.version&&parseInt(e.version(),10)<=12}function o(e){return(e.ctrlKey||e.altKey||e.metaKey)&&!(e.ctrlKey&&e.altKey)}function i(e){switch(e){case T.topCompositionStart:return P.compositionStart;case T.topCompositionEnd:return P.compositionEnd;case T.topCompositionUpdate:return P.compositionUpdate}}function a(e,t){return e===T.topKeyDown&&t.keyCode===b}function u(e,t){switch(e){case T.topKeyUp:return-1!==E.indexOf(t.keyCode);case T.topKeyDown:return t.keyCode!==b;case T.topKeyPress:case T.topMouseDown:case T.topBlur:return!0;default:return!1}}function s(e){var t=e.detail;return"object"==typeof t&&"data"in t?t.data:null}function l(e,t,n,r){var o,l;if(_?o=i(e):w?u(e,r)&&(o=P.compositionEnd):a(e,r)&&(o=P.compositionStart),!o)return null;M&&(w||o!==P.compositionStart?o===P.compositionEnd&&w&&(l=w.getData()):w=v.getPooled(t));var c=g.getPooled(o,n,r);if(l)c.data=l;else{var p=s(r);null!==p&&(c.data=p)}return h.accumulateTwoPhaseDispatches(c),c}function c(e,t){switch(e){case T.topCompositionEnd:return s(t);case T.topKeyPress:var n=t.which;return n!==N?null:(R=!0,I);case T.topTextInput:var r=t.data;return r===I&&R?null:r;default:return null}}function p(e,t){if(w){if(e===T.topCompositionEnd||u(e,t)){var n=w.getData();return v.release(w),w=null,n}return null}switch(e){case T.topPaste:return null;case T.topKeyPress:return t.which&&!o(t)?String.fromCharCode(t.which):null;case T.topCompositionEnd:return M?null:t.data;default:return null}}function d(e,t,n,r){var o;if(o=D?c(e,r):p(e,r),!o)return null;var i=y.getPooled(P.beforeInput,n,r);return i.data=o,h.accumulateTwoPhaseDispatches(i),i}var f=e(15),h=e(20),m=e(21),v=e(22),g=e(91),y=e(95),C=e(139),E=[9,13,27,32],b=229,_=m.canUseDOM&&"CompositionEvent"in window,x=null;m.canUseDOM&&"documentMode"in document&&(x=document.documentMode);var D=m.canUseDOM&&"TextEvent"in window&&!x&&!r(),M=m.canUseDOM&&(!_||x&&x>8&&11>=x),N=32,I=String.fromCharCode(N),T=f.topLevelTypes,P={beforeInput:{phasedRegistrationNames:{bubbled:C({onBeforeInput:null}),captured:C({onBeforeInputCapture:null})},dependencies:[T.topCompositionEnd,T.topKeyPress,T.topTextInput,T.topPaste]},compositionEnd:{phasedRegistrationNames:{bubbled:C({onCompositionEnd:null}),captured:C({onCompositionEndCapture:null})},dependencies:[T.topBlur,T.topCompositionEnd,T.topKeyDown,T.topKeyPress,T.topKeyUp,T.topMouseDown]},compositionStart:{phasedRegistrationNames:{bubbled:C({onCompositionStart:null}),captured:C({onCompositionStartCapture:null})},dependencies:[T.topBlur,T.topCompositionStart,T.topKeyDown,T.topKeyPress,T.topKeyUp,T.topMouseDown]},compositionUpdate:{phasedRegistrationNames:{bubbled:C({onCompositionUpdate:null}),captured:C({onCompositionUpdateCapture:null})},dependencies:[T.topBlur,T.topCompositionUpdate,T.topKeyDown,T.topKeyPress,T.topKeyUp,T.topMouseDown]}},R=!1,w=null,O={eventTypes:P,extractEvents:function(e,t,n,r){return[l(e,t,n,r),d(e,t,n,r)]}};t.exports=O},{139:139,15:15,20:20,21:21,22:22,91:91,95:95}],4:[function(e,t,n){"use strict";function r(e,t){return e+t.charAt(0).toUpperCase()+t.substring(1)}var o={boxFlex:!0,boxFlexGroup:!0,columnCount:!0,flex:!0,flexGrow:!0,flexPositive:!0,flexShrink:!0,flexNegative:!0,fontWeight:!0,lineClamp:!0,lineHeight:!0,opacity:!0,order:!0,orphans:!0,widows:!0,zIndex:!0,zoom:!0,fillOpacity:!0,strokeDashoffset:!0,strokeOpacity:!0,strokeWidth:!0},i=["Webkit","ms","Moz","O"];Object.keys(o).forEach(function(e){i.forEach(function(t){o[r(t,e)]=o[e]})});var a={background:{backgroundImage:!0,backgroundPosition:!0,backgroundRepeat:!0,backgroundColor:!0},border:{borderWidth:!0,borderStyle:!0,borderColor:!0},borderBottom:{borderBottomWidth:!0,borderBottomStyle:!0,borderBottomColor:!0},borderLeft:{borderLeftWidth:!0,borderLeftStyle:!0,borderLeftColor:!0},borderRight:{borderRightWidth:!0,borderRightStyle:!0,borderRightColor:!0},borderTop:{borderTopWidth:!0,borderTopStyle:!0,borderTopColor:!0},font:{fontStyle:!0,fontVariant:!0,fontWeight:!0,fontSize:!0,lineHeight:!0,fontFamily:!0}},u={isUnitlessNumber:o,shorthandPropertyExpansions:a};t.exports=u},{}],5:[function(e,t,n){"use strict";var r=e(4),o=e(21),i=(e(106),e(111)),a=e(131),u=e(141),s=(e(150),u(function(e){return a(e)})),l="cssFloat";o.canUseDOM&&void 0===document.documentElement.style.cssFloat&&(l="styleFloat");var c={createMarkupForStyles:function(e){var t="";for(var n in e)if(e.hasOwnProperty(n)){var r=e[n];null!=r&&(t+=s(n)+":",t+=i(n,r)+";")}return t||null},setValueForStyles:function(e,t){var n=e.style;for(var o in t)if(t.hasOwnProperty(o)){var a=i(o,t[o]);if("float"===o&&(o=l),a)n[o]=a;else{var u=r.shorthandPropertyExpansions[o];if(u)for(var s in u)n[s]="";else n[o]=""}}}};t.exports=c},{106:106,111:111,131:131,141:141,150:150,21:21,4:4}],6:[function(e,t,n){"use strict";function r(){this._callbacks=null,this._contexts=null}var o=e(28),i=e(27),a=e(133);i(r.prototype,{enqueue:function(e,t){this._callbacks=this._callbacks||[],this._contexts=this._contexts||[],this._callbacks.push(e),this._contexts.push(t)},notifyAll:function(){var e=this._callbacks,t=this._contexts;if(e){a(e.length===t.length),this._callbacks=null,this._contexts=null;for(var n=0,r=e.length;r>n;n++)e[n].call(t[n]);e.length=0,t.length=0}},reset:function(){this._callbacks=null,this._contexts=null},destructor:function(){this.reset()}}),o.addPoolingTo(r),t.exports=r},{133:133,27:27,28:28}],7:[function(e,t,n){"use strict";function r(e){return"SELECT"===e.nodeName||"INPUT"===e.nodeName&&"file"===e.type}function o(e){var t=x.getPooled(T.change,R,e);E.accumulateTwoPhaseDispatches(t),_.batchedUpdates(i,t)}function i(e){C.enqueueEvents(e),C.processEventQueue()}function a(e,t){P=e,R=t,P.attachEvent("onchange",o)}function u(){P&&(P.detachEvent("onchange",o),P=null,R=null)}function s(e,t,n){return e===I.topChange?n:void 0}function l(e,t,n){e===I.topFocus?(u(),a(t,n)):e===I.topBlur&&u()}function c(e,t){P=e,R=t,w=e.value,O=Object.getOwnPropertyDescriptor(e.constructor.prototype,"value"),Object.defineProperty(P,"value",k),P.attachEvent("onpropertychange",d)}function p(){P&&(delete P.value,P.detachEvent("onpropertychange",d),P=null,R=null,w=null,O=null)}function d(e){if("value"===e.propertyName){var t=e.srcElement.value;t!==w&&(w=t,o(e))}}function f(e,t,n){return e===I.topInput?n:void 0}function h(e,t,n){e===I.topFocus?(p(),c(t,n)):e===I.topBlur&&p()}function m(e,t,n){return e!==I.topSelectionChange&&e!==I.topKeyUp&&e!==I.topKeyDown||!P||P.value===w?void 0:(w=P.value,R)}function v(e){return"INPUT"===e.nodeName&&("checkbox"===e.type||"radio"===e.type)}function g(e,t,n){return e===I.topClick?n:void 0}var y=e(15),C=e(17),E=e(20),b=e(21),_=e(85),x=e(93),D=e(134),M=e(136),N=e(139),I=y.topLevelTypes,T={change:{phasedRegistrationNames:{bubbled:N({onChange:null}),captured:N({onChangeCapture:null})},dependencies:[I.topBlur,I.topChange,I.topClick,I.topFocus,I.topInput,I.topKeyDown,I.topKeyUp,I.topSelectionChange]}},P=null,R=null,w=null,O=null,S=!1;b.canUseDOM&&(S=D("change")&&(!("documentMode"in document)||document.documentMode>8));var A=!1;b.canUseDOM&&(A=D("input")&&(!("documentMode"in document)||document.documentMode>9));var k={get:function(){return O.get.call(this)},set:function(e){w=""+e,O.set.call(this,e)}},L={eventTypes:T,extractEvents:function(e,t,n,o){var i,a;if(r(t)?S?i=s:a=l:M(t)?A?i=f:(i=m,a=h):v(t)&&(i=g),i){var u=i(e,t,n);if(u){var c=x.getPooled(T.change,u,o);return E.accumulateTwoPhaseDispatches(c),c}}a&&a(e,t,n)}};t.exports=L},{134:134,136:136,139:139,15:15,17:17,20:20,21:21,85:85,93:93}],8:[function(e,t,n){"use strict";var r=0,o={createReactRootIndex:function(){return r++}};t.exports=o},{}],9:[function(e,t,n){"use strict";function r(e,t,n){e.insertBefore(t,e.childNodes[n]||null)}var o=e(12),i=e(70),a=e(145),u=e(133),s={dangerouslyReplaceNodeWithMarkup:o.dangerouslyReplaceNodeWithMarkup,updateTextContent:a,processUpdates:function(e,t){for(var n,s=null,l=null,c=0;c<e.length;c++)if(n=e[c],n.type===i.MOVE_EXISTING||n.type===i.REMOVE_NODE){var p=n.fromIndex,d=n.parentNode.childNodes[p],f=n.parentID;u(d),s=s||{},s[f]=s[f]||[],s[f][p]=d,l=l||[],l.push(d)}var h=o.dangerouslyRenderMarkup(t);if(l)for(var m=0;m<l.length;m++)l[m].parentNode.removeChild(l[m]);for(var v=0;v<e.length;v++)switch(n=e[v],n.type){case i.INSERT_MARKUP:r(n.parentNode,h[n.markupIndex],n.toIndex);break;case i.MOVE_EXISTING:r(n.parentNode,s[n.parentID][n.fromIndex],n.toIndex);break;case i.TEXT_CONTENT:a(n.parentNode,n.textContent);break;case i.REMOVE_NODE:}}};t.exports=s},{12:12,133:133,145:145,70:70}],10:[function(e,t,n){"use strict";function r(e,t){return(e&t)===t}var o=e(133),i={MUST_USE_ATTRIBUTE:1,MUST_USE_PROPERTY:2,HAS_SIDE_EFFECTS:4,HAS_BOOLEAN_VALUE:8,HAS_NUMERIC_VALUE:16,HAS_POSITIVE_NUMERIC_VALUE:48,HAS_OVERLOADED_BOOLEAN_VALUE:64,injectDOMPropertyConfig:function(e){var t=e.Properties||{},n=e.DOMAttributeNames||{},a=e.DOMPropertyNames||{},s=e.DOMMutationMethods||{};e.isCustomAttribute&&u._isCustomAttributeFunctions.push(e.isCustomAttribute);for(var l in t){o(!u.isStandardName.hasOwnProperty(l)),u.isStandardName[l]=!0;var c=l.toLowerCase();if(u.getPossibleStandardName[c]=l,n.hasOwnProperty(l)){var p=n[l];u.getPossibleStandardName[p]=l,u.getAttributeName[l]=p}else u.getAttributeName[l]=c;u.getPropertyName[l]=a.hasOwnProperty(l)?a[l]:l,s.hasOwnProperty(l)?u.getMutationMethod[l]=s[l]:u.getMutationMethod[l]=null;var d=t[l];u.mustUseAttribute[l]=r(d,i.MUST_USE_ATTRIBUTE),u.mustUseProperty[l]=r(d,i.MUST_USE_PROPERTY),u.hasSideEffects[l]=r(d,i.HAS_SIDE_EFFECTS),u.hasBooleanValue[l]=r(d,i.HAS_BOOLEAN_VALUE),u.hasNumericValue[l]=r(d,i.HAS_NUMERIC_VALUE),u.hasPositiveNumericValue[l]=r(d,i.HAS_POSITIVE_NUMERIC_VALUE),u.hasOverloadedBooleanValue[l]=r(d,i.HAS_OVERLOADED_BOOLEAN_VALUE),o(!u.mustUseAttribute[l]||!u.mustUseProperty[l]),o(u.mustUseProperty[l]||!u.hasSideEffects[l]),o(!!u.hasBooleanValue[l]+!!u.hasNumericValue[l]+!!u.hasOverloadedBooleanValue[l]<=1)}}},a={},u={ID_ATTRIBUTE_NAME:"data-reactid",isStandardName:{},getPossibleStandardName:{},getAttributeName:{},getPropertyName:{},getMutationMethod:{},mustUseAttribute:{},mustUseProperty:{},hasSideEffects:{},hasBooleanValue:{},hasNumericValue:{},hasPositiveNumericValue:{},hasOverloadedBooleanValue:{},_isCustomAttributeFunctions:[],isCustomAttribute:function(e){for(var t=0;t<u._isCustomAttributeFunctions.length;t++){var n=u._isCustomAttributeFunctions[t];if(n(e))return!0}return!1},getDefaultValueForProperty:function(e,t){var n,r=a[e];return r||(a[e]=r={}),t in r||(n=document.createElement(e),r[t]=n[t]),r[t]},injection:i};t.exports=u},{133:133}],11:[function(e,t,n){"use strict";function r(e,t){return null==t||o.hasBooleanValue[e]&&!t||o.hasNumericValue[e]&&isNaN(t)||o.hasPositiveNumericValue[e]&&1>t||o.hasOverloadedBooleanValue[e]&&t===!1}var o=e(10),i=e(143),a=(e(150),{createMarkupForID:function(e){return o.ID_ATTRIBUTE_NAME+"="+i(e)},createMarkupForProperty:function(e,t){if(o.isStandardName.hasOwnProperty(e)&&o.isStandardName[e]){if(r(e,t))return"";var n=o.getAttributeName[e];return o.hasBooleanValue[e]||o.hasOverloadedBooleanValue[e]&&t===!0?n:n+"="+i(t)}return o.isCustomAttribute(e)?null==t?"":e+"="+i(t):null},setValueForProperty:function(e,t,n){if(o.isStandardName.hasOwnProperty(t)&&o.isStandardName[t]){var i=o.getMutationMethod[t];if(i)i(e,n);else if(r(t,n))this.deleteValueForProperty(e,t);else if(o.mustUseAttribute[t])e.setAttribute(o.getAttributeName[t],""+n);else{var a=o.getPropertyName[t];o.hasSideEffects[t]&&""+e[a]==""+n||(e[a]=n)}}else o.isCustomAttribute(t)&&(null==n?e.removeAttribute(t):e.setAttribute(t,""+n))},deleteValueForProperty:function(e,t){if(o.isStandardName.hasOwnProperty(t)&&o.isStandardName[t]){var n=o.getMutationMethod[t];if(n)n(e,void 0);else if(o.mustUseAttribute[t])e.removeAttribute(o.getAttributeName[t]);else{var r=o.getPropertyName[t],i=o.getDefaultValueForProperty(e.nodeName,r);o.hasSideEffects[t]&&""+e[r]===i||(e[r]=i)}}else o.isCustomAttribute(t)&&e.removeAttribute(t)}});t.exports=a},{10:10,143:143,150:150}],12:[function(e,t,n){"use strict";function r(e){return e.substring(1,e.indexOf(" "))}var o=e(21),i=e(110),a=e(112),u=e(125),s=e(133),l=/^(<[^ \/>]+)/,c="data-danger-index",p={dangerouslyRenderMarkup:function(e){s(o.canUseDOM);for(var t,n={},p=0;p<e.length;p++)s(e[p]),t=r(e[p]),t=u(t)?t:"*",n[t]=n[t]||[],n[t][p]=e[p];var d=[],f=0;for(t in n)if(n.hasOwnProperty(t)){var h,m=n[t];for(h in m)if(m.hasOwnProperty(h)){var v=m[h];m[h]=v.replace(l,"$1 "+c+'="'+h+'" ')}for(var g=i(m.join(""),a),y=0;y<g.length;++y){var C=g[y];C.hasAttribute&&C.hasAttribute(c)&&(h=+C.getAttribute(c),C.removeAttribute(c),s(!d.hasOwnProperty(h)),d[h]=C,f+=1)}}return s(f===d.length),s(d.length===e.length),d},dangerouslyReplaceNodeWithMarkup:function(e,t){s(o.canUseDOM),s(t),s("html"!==e.tagName.toLowerCase());var n=i(t,a)[0];e.parentNode.replaceChild(n,e)}};t.exports=p},{110:110,112:112,125:125,133:133,21:21}],13:[function(e,t,n){"use strict";var r=e(139),o=[r({ResponderEventPlugin:null}),r({SimpleEventPlugin:null}),r({TapEventPlugin:null}),r({EnterLeaveEventPlugin:null}),r({ChangeEventPlugin:null}),r({SelectEventPlugin:null}),r({BeforeInputEventPlugin:null}),r({AnalyticsEventPlugin:null}),r({MobileSafariClickEventPlugin:null})];t.exports=o},{139:139}],14:[function(e,t,n){"use strict";var r=e(15),o=e(20),i=e(97),a=e(68),u=e(139),s=r.topLevelTypes,l=a.getFirstReactDOM,c={mouseEnter:{registrationName:u({onMouseEnter:null}),dependencies:[s.topMouseOut,s.topMouseOver]},mouseLeave:{registrationName:u({onMouseLeave:null}),dependencies:[s.topMouseOut,s.topMouseOver]}},p=[null,null],d={eventTypes:c,extractEvents:function(e,t,n,r){if(e===s.topMouseOver&&(r.relatedTarget||r.fromElement))return null;if(e!==s.topMouseOut&&e!==s.topMouseOver)return null;var u;if(t.window===t)u=t;else{var d=t.ownerDocument;u=d?d.defaultView||d.parentWindow:window}var f,h;if(e===s.topMouseOut?(f=t,h=l(r.relatedTarget||r.toElement)||u):(f=u,h=t),f===h)return null;var m=f?a.getID(f):"",v=h?a.getID(h):"",g=i.getPooled(c.mouseLeave,m,r);g.type="mouseleave",g.target=f,g.relatedTarget=h;var y=i.getPooled(c.mouseEnter,v,r);return y.type="mouseenter",y.target=h,y.relatedTarget=f,o.accumulateEnterLeaveDispatches(g,y,m,v),p[0]=g,p[1]=y,p}};t.exports=d},{139:139,15:15,20:20,68:68,97:97}],15:[function(e,t,n){"use strict";var r=e(138),o=r({bubbled:null,captured:null}),i=r({topBlur:null,topChange:null,topClick:null,topCompositionEnd:null,topCompositionStart:null,topCompositionUpdate:null,topContextMenu:null,topCopy:null,topCut:null,topDoubleClick:null,topDrag:null,topDragEnd:null,topDragEnter:null,topDragExit:null,topDragLeave:null,topDragOver:null,topDragStart:null,topDrop:null,topError:null,topFocus:null,topInput:null,topKeyDown:null,topKeyPress:null,topKeyUp:null,topLoad:null,topMouseDown:null,topMouseMove:null,topMouseOut:null,topMouseOver:null,topMouseUp:null,topPaste:null,topReset:null,topScroll:null,topSelectionChange:null,topSubmit:null,topTextInput:null,topTouchCancel:null,topTouchEnd:null,topTouchMove:null,topTouchStart:null,topWheel:null}),a={topLevelTypes:i,PropagationPhases:o};t.exports=a},{138:138}],16:[function(e,t,n){var r=e(112),o={listen:function(e,t,n){return e.addEventListener?(e.addEventListener(t,n,!1),{remove:function(){e.removeEventListener(t,n,!1)}}):e.attachEvent?(e.attachEvent("on"+t,n),{remove:function(){e.detachEvent("on"+t,n)}}):void 0},capture:function(e,t,n){return e.addEventListener?(e.addEventListener(t,n,!0),{remove:function(){e.removeEventListener(t,n,!0)}}):{remove:r}},registerDefault:function(){}};t.exports=o},{112:112}],17:[function(e,t,n){"use strict";var r=e(18),o=e(19),i=e(103),a=e(118),u=e(133),s={},l=null,c=function(e){if(e){var t=o.executeDispatch,n=r.getPluginModuleForEvent(e);n&&n.executeDispatch&&(t=n.executeDispatch),o.executeDispatchesInOrder(e,t),e.isPersistent()||e.constructor.release(e)}},p=null,d={injection:{injectMount:o.injection.injectMount,injectInstanceHandle:function(e){p=e},getInstanceHandle:function(){return p},injectEventPluginOrder:r.injectEventPluginOrder,injectEventPluginsByName:r.injectEventPluginsByName},eventNameDispatchConfigs:r.eventNameDispatchConfigs,registrationNameModules:r.registrationNameModules,putListener:function(e,t,n){u(!n||"function"==typeof n);var r=s[t]||(s[t]={});r[e]=n},getListener:function(e,t){var n=s[t];return n&&n[e]},deleteListener:function(e,t){var n=s[t];n&&delete n[e]},deleteAllListeners:function(e){for(var t in s)delete s[t][e]},extractEvents:function(e,t,n,o){for(var a,u=r.plugins,s=0,l=u.length;l>s;s++){var c=u[s];if(c){var p=c.extractEvents(e,t,n,o);p&&(a=i(a,p))}}return a},enqueueEvents:function(e){e&&(l=i(l,e))},processEventQueue:function(){var e=l;l=null,a(e,c),u(!l)},__purge:function(){s={}},__getListenerBank:function(){return s}};t.exports=d},{103:103,118:118,133:133,18:18,19:19}],18:[function(e,t,n){"use strict";function r(){if(u)for(var e in s){var t=s[e],n=u.indexOf(e);if(a(n>-1),!l.plugins[n]){a(t.extractEvents),l.plugins[n]=t;var r=t.eventTypes;for(var i in r)a(o(r[i],t,i))}}}function o(e,t,n){a(!l.eventNameDispatchConfigs.hasOwnProperty(n)),l.eventNameDispatchConfigs[n]=e;var r=e.phasedRegistrationNames;if(r){for(var o in r)if(r.hasOwnProperty(o)){var u=r[o];i(u,t,n)}return!0}return e.registrationName?(i(e.registrationName,t,n),!0):!1}function i(e,t,n){a(!l.registrationNameModules[e]),l.registrationNameModules[e]=t,l.registrationNameDependencies[e]=t.eventTypes[n].dependencies}var a=e(133),u=null,s={},l={plugins:[],eventNameDispatchConfigs:{},registrationNameModules:{},registrationNameDependencies:{},injectEventPluginOrder:function(e){a(!u),u=Array.prototype.slice.call(e),r()},injectEventPluginsByName:function(e){var t=!1;for(var n in e)if(e.hasOwnProperty(n)){var o=e[n];s.hasOwnProperty(n)&&s[n]===o||(a(!s[n]),s[n]=o,t=!0)}t&&r()},getPluginModuleForEvent:function(e){var t=e.dispatchConfig;if(t.registrationName)return l.registrationNameModules[t.registrationName]||null;for(var n in t.phasedRegistrationNames)if(t.phasedRegistrationNames.hasOwnProperty(n)){var r=l.registrationNameModules[t.phasedRegistrationNames[n]];if(r)return r}return null},_resetEventPlugins:function(){u=null;for(var e in s)s.hasOwnProperty(e)&&delete s[e];l.plugins.length=0;var t=l.eventNameDispatchConfigs;for(var n in t)t.hasOwnProperty(n)&&delete t[n];var r=l.registrationNameModules;for(var o in r)r.hasOwnProperty(o)&&delete r[o]}};t.exports=l},{133:133}],19:[function(e,t,n){"use strict";function r(e){return e===v.topMouseUp||e===v.topTouchEnd||e===v.topTouchCancel}function o(e){return e===v.topMouseMove||e===v.topTouchMove}function i(e){return e===v.topMouseDown||e===v.topTouchStart}function a(e,t){var n=e._dispatchListeners,r=e._dispatchIDs;if(Array.isArray(n))for(var o=0;o<n.length&&!e.isPropagationStopped();o++)t(e,n[o],r[o]);else n&&t(e,n,r)}function u(e,t,n){e.currentTarget=m.Mount.getNode(n);var r=t(e,n);return e.currentTarget=null,r}function s(e,t){a(e,t),e._dispatchListeners=null,e._dispatchIDs=null}function l(e){var t=e._dispatchListeners,n=e._dispatchIDs;if(Array.isArray(t)){for(var r=0;r<t.length&&!e.isPropagationStopped();r++)if(t[r](e,n[r]))return n[r]}else if(t&&t(e,n))return n;return null}function c(e){var t=l(e);return e._dispatchIDs=null,e._dispatchListeners=null,t}function p(e){var t=e._dispatchListeners,n=e._dispatchIDs;h(!Array.isArray(t));var r=t?t(e,n):null;return e._dispatchListeners=null,e._dispatchIDs=null,r}function d(e){return!!e._dispatchListeners}var f=e(15),h=e(133),m={Mount:null,injectMount:function(e){m.Mount=e}},v=f.topLevelTypes,g={isEndish:r,isMoveish:o,isStartish:i,executeDirectDispatch:p,executeDispatch:u,executeDispatchesInOrder:s,executeDispatchesInOrderStopAtTrue:c,hasDispatches:d,injection:m,useTouchEvents:!1};t.exports=g},{133:133,15:15}],20:[function(e,t,n){"use strict";function r(e,t,n){var r=t.dispatchConfig.phasedRegistrationNames[n];return v(e,r)}function o(e,t,n){var o=t?m.bubbled:m.captured,i=r(e,n,o);i&&(n._dispatchListeners=f(n._dispatchListeners,i),n._dispatchIDs=f(n._dispatchIDs,e))}function i(e){e&&e.dispatchConfig.phasedRegistrationNames&&d.injection.getInstanceHandle().traverseTwoPhase(e.dispatchMarker,o,e)}function a(e,t,n){if(n&&n.dispatchConfig.registrationName){var r=n.dispatchConfig.registrationName,o=v(e,r);o&&(n._dispatchListeners=f(n._dispatchListeners,o),n._dispatchIDs=f(n._dispatchIDs,e))}}function u(e){e&&e.dispatchConfig.registrationName&&a(e.dispatchMarker,null,e)}function s(e){h(e,i)}function l(e,t,n,r){d.injection.getInstanceHandle().traverseEnterLeave(n,r,a,e,t)}function c(e){h(e,u)}var p=e(15),d=e(17),f=e(103),h=e(118),m=p.PropagationPhases,v=d.getListener,g={accumulateTwoPhaseDispatches:s,accumulateDirectDispatches:c,accumulateEnterLeaveDispatches:l};t.exports=g},{103:103,118:118,15:15,17:17}],21:[function(e,t,n){"use strict";var r=!("undefined"==typeof window||!window.document||!window.document.createElement),o={canUseDOM:r,canUseWorkers:"undefined"!=typeof Worker,canUseEventListeners:r&&!(!window.addEventListener&&!window.attachEvent),canUseViewport:r&&!!window.screen,isInWorker:!r};t.exports=o},{}],22:[function(e,t,n){"use strict";function r(e){this._root=e,this._startText=this.getText(),this._fallbackText=null}var o=e(28),i=e(27),a=e(128);i(r.prototype,{getText:function(){return"value"in this._root?this._root.value:this._root[a()]},getData:function(){if(this._fallbackText)return this._fallbackText;var e,t,n=this._startText,r=n.length,o=this.getText(),i=o.length;for(e=0;r>e&&n[e]===o[e];e++);var a=r-e;for(t=1;a>=t&&n[r-t]===o[i-t];t++);var u=t>1?1-t:void 0;return this._fallbackText=o.slice(e,u),this._fallbackText}}),o.addPoolingTo(r),t.exports=r},{128:128,27:27,28:28}],23:[function(e,t,n){"use strict";var r,o=e(10),i=e(21),a=o.injection.MUST_USE_ATTRIBUTE,u=o.injection.MUST_USE_PROPERTY,s=o.injection.HAS_BOOLEAN_VALUE,l=o.injection.HAS_SIDE_EFFECTS,c=o.injection.HAS_NUMERIC_VALUE,p=o.injection.HAS_POSITIVE_NUMERIC_VALUE,d=o.injection.HAS_OVERLOADED_BOOLEAN_VALUE;if(i.canUseDOM){var f=document.implementation;r=f&&f.hasFeature&&f.hasFeature("http://www.w3.org/TR/SVG11/feature#BasicStructure","1.1")}var h={isCustomAttribute:RegExp.prototype.test.bind(/^(data|aria)-[a-z_][a-z\d_.\-]*$/),Properties:{accept:null,acceptCharset:null,accessKey:null,action:null,allowFullScreen:a|s,allowTransparency:a,alt:null,async:s,autoComplete:null,autoPlay:s,cellPadding:null,cellSpacing:null,charSet:a,checked:u|s,classID:a,className:r?a:u,cols:a|p,colSpan:null,content:null,contentEditable:null,contextMenu:a,controls:u|s,coords:null,crossOrigin:null,data:null,dateTime:a,defer:s,dir:null,disabled:a|s,download:d,draggable:null,encType:null,form:a,formAction:a,formEncType:a,formMethod:a,formNoValidate:s,formTarget:a,frameBorder:a,headers:null,height:a,hidden:a|s,high:null,href:null,hrefLang:null,htmlFor:null,httpEquiv:null,icon:null,id:u,label:null,lang:null,list:a,loop:u|s,low:null,manifest:a,marginHeight:null,marginWidth:null,max:null,maxLength:a,media:a,mediaGroup:null,method:null,min:null,multiple:u|s,muted:u|s,name:null,noValidate:s,open:s,optimum:null,pattern:null,placeholder:null,poster:null,preload:null,radioGroup:null,readOnly:u|s,rel:null,required:s,role:a,rows:a|p,rowSpan:null,sandbox:null,scope:null,scoped:s,scrolling:null,seamless:a|s,selected:u|s,shape:null,size:a|p,sizes:a,span:p,spellCheck:null,src:null,srcDoc:u,srcSet:a,start:c,step:null,style:null,tabIndex:null,target:null,title:null,type:null,useMap:null,value:u|l,width:a,wmode:a,autoCapitalize:null,autoCorrect:null,itemProp:a,itemScope:a|s,itemType:a,itemID:a,itemRef:a,property:null,unselectable:a},DOMAttributeNames:{acceptCharset:"accept-charset",className:"class",htmlFor:"for",httpEquiv:"http-equiv"},DOMPropertyNames:{autoCapitalize:"autocapitalize",autoComplete:"autocomplete",autoCorrect:"autocorrect",autoFocus:"autofocus",autoPlay:"autoplay",encType:"encoding",hrefLang:"hreflang",radioGroup:"radiogroup",spellCheck:"spellcheck",srcDoc:"srcdoc",srcSet:"srcset"}};t.exports=h},{10:10,21:21}],24:[function(e,t,n){"use strict";function r(e){l(null==e.props.checkedLink||null==e.props.valueLink)}function o(e){r(e),l(null==e.props.value&&null==e.props.onChange)}function i(e){r(e),l(null==e.props.checked&&null==e.props.onChange)}function a(e){this.props.valueLink.requestChange(e.target.value)}function u(e){this.props.checkedLink.requestChange(e.target.checked)}var s=e(76),l=e(133),c={button:!0,checkbox:!0,image:!0,hidden:!0,radio:!0,reset:!0,submit:!0},p={Mixin:{propTypes:{value:function(e,t,n){return!e[t]||c[e.type]||e.onChange||e.readOnly||e.disabled?null:new Error("You provided a `value` prop to a form field without an `onChange` handler. This will render a read-only field. If the field should be mutable use `defaultValue`. Otherwise, set either `onChange` or `readOnly`.")},checked:function(e,t,n){return!e[t]||e.onChange||e.readOnly||e.disabled?null:new Error("You provided a `checked` prop to a form field without an `onChange` handler. This will render a read-only field. If the field should be mutable use `defaultChecked`. Otherwise, set either `onChange` or `readOnly`.")},onChange:s.func}},getValue:function(e){return e.props.valueLink?(o(e),e.props.valueLink.value):e.props.value},getChecked:function(e){return e.props.checkedLink?(i(e),e.props.checkedLink.value):e.props.checked},getOnChange:function(e){return e.props.valueLink?(o(e),a):e.props.checkedLink?(i(e),u):e.props.onChange}};t.exports=p},{133:133,76:76}],25:[function(e,t,n){"use strict";function r(e){e.remove()}var o=e(30),i=e(103),a=e(118),u=e(133),s={trapBubbledEvent:function(e,t){u(this.isMounted());var n=this.getDOMNode();u(n);var r=o.trapBubbledEvent(e,t,n);this._localEventListeners=i(this._localEventListeners,r)},componentWillUnmount:function(){this._localEventListeners&&a(this._localEventListeners,r)}};t.exports=s},{103:103,118:118,133:133,30:30}],26:[function(e,t,n){"use strict";var r=e(15),o=e(112),i=r.topLevelTypes,a={eventTypes:null,extractEvents:function(e,t,n,r){if(e===i.topTouchStart){var a=r.target;a&&!a.onclick&&(a.onclick=o)}}};t.exports=a},{112:112,15:15}],27:[function(e,t,n){"use strict";function r(e,t){if(null==e)throw new TypeError("Object.assign target cannot be null or undefined");for(var n=Object(e),r=Object.prototype.hasOwnProperty,o=1;o<arguments.length;o++){var i=arguments[o];if(null!=i){var a=Object(i);for(var u in a)r.call(a,u)&&(n[u]=a[u])}}return n}t.exports=r},{}],28:[function(e,t,n){"use strict";var r=e(133),o=function(e){var t=this;if(t.instancePool.length){var n=t.instancePool.pop();return t.call(n,e),n}return new t(e)},i=function(e,t){var n=this;if(n.instancePool.length){var r=n.instancePool.pop();return n.call(r,e,t),r}return new n(e,t)},a=function(e,t,n){var r=this;if(r.instancePool.length){var o=r.instancePool.pop();return r.call(o,e,t,n),o}return new r(e,t,n)},u=function(e,t,n,r,o){var i=this;if(i.instancePool.length){var a=i.instancePool.pop();return i.call(a,e,t,n,r,o),a}return new i(e,t,n,r,o)},s=function(e){var t=this;r(e instanceof t),e.destructor&&e.destructor(),t.instancePool.length<t.poolSize&&t.instancePool.push(e)},l=10,c=o,p=function(e,t){var n=e;return n.instancePool=[],n.getPooled=t||c,n.poolSize||(n.poolSize=l),n.release=s,n},d={addPoolingTo:p,oneArgumentPooler:o,twoArgumentPooler:i,threeArgumentPooler:a,fiveArgumentPooler:u};t.exports=d},{133:133}],29:[function(e,t,n){"use strict";var r=e(115),o={getDOMNode:function(){return r(this)}};t.exports=o},{115:115}],30:[function(e,t,n){"use strict";function r(e){return Object.prototype.hasOwnProperty.call(e,m)||(e[m]=f++,p[e[m]]={}),p[e[m]]}var o=e(15),i=e(17),a=e(18),u=e(59),s=e(102),l=e(27),c=e(134),p={},d=!1,f=0,h={topBlur:"blur",topChange:"change",topClick:"click",topCompositionEnd:"compositionend",topCompositionStart:"compositionstart",topCompositionUpdate:"compositionupdate",topContextMenu:"contextmenu",topCopy:"copy",topCut:"cut",topDoubleClick:"dblclick",topDrag:"drag",topDragEnd:"dragend",topDragEnter:"dragenter",topDragExit:"dragexit",topDragLeave:"dragleave",topDragOver:"dragover",topDragStart:"dragstart",topDrop:"drop",topFocus:"focus",topInput:"input",topKeyDown:"keydown",topKeyPress:"keypress",topKeyUp:"keyup",topMouseDown:"mousedown",topMouseMove:"mousemove",topMouseOut:"mouseout",topMouseOver:"mouseover",topMouseUp:"mouseup",topPaste:"paste",topScroll:"scroll",topSelectionChange:"selectionchange",topTextInput:"textInput",topTouchCancel:"touchcancel",topTouchEnd:"touchend",topTouchMove:"touchmove",topTouchStart:"touchstart",topWheel:"wheel"},m="_reactListenersID"+String(Math.random()).slice(2),v=l({},u,{ReactEventListener:null,injection:{injectReactEventListener:function(e){e.setHandleTopLevel(v.handleTopLevel),v.ReactEventListener=e}},setEnabled:function(e){v.ReactEventListener&&v.ReactEventListener.setEnabled(e)},isEnabled:function(){return!(!v.ReactEventListener||!v.ReactEventListener.isEnabled())},listenTo:function(e,t){for(var n=t,i=r(n),u=a.registrationNameDependencies[e],s=o.topLevelTypes,l=0,p=u.length;p>l;l++){var d=u[l];i.hasOwnProperty(d)&&i[d]||(d===s.topWheel?c("wheel")?v.ReactEventListener.trapBubbledEvent(s.topWheel,"wheel",n):c("mousewheel")?v.ReactEventListener.trapBubbledEvent(s.topWheel,"mousewheel",n):v.ReactEventListener.trapBubbledEvent(s.topWheel,"DOMMouseScroll",n):d===s.topScroll?c("scroll",!0)?v.ReactEventListener.trapCapturedEvent(s.topScroll,"scroll",n):v.ReactEventListener.trapBubbledEvent(s.topScroll,"scroll",v.ReactEventListener.WINDOW_HANDLE):d===s.topFocus||d===s.topBlur?(c("focus",!0)?(v.ReactEventListener.trapCapturedEvent(s.topFocus,"focus",n),v.ReactEventListener.trapCapturedEvent(s.topBlur,"blur",n)):c("focusin")&&(v.ReactEventListener.trapBubbledEvent(s.topFocus,"focusin",n),v.ReactEventListener.trapBubbledEvent(s.topBlur,"focusout",n)),i[s.topBlur]=!0,i[s.topFocus]=!0):h.hasOwnProperty(d)&&v.ReactEventListener.trapBubbledEvent(d,h[d],n),i[d]=!0)}},trapBubbledEvent:function(e,t,n){
-return v.ReactEventListener.trapBubbledEvent(e,t,n)},trapCapturedEvent:function(e,t,n){return v.ReactEventListener.trapCapturedEvent(e,t,n)},ensureScrollValueMonitoring:function(){if(!d){var e=s.refreshScrollValues;v.ReactEventListener.monitorScrollValue(e),d=!0}},eventNameDispatchConfigs:i.eventNameDispatchConfigs,registrationNameModules:i.registrationNameModules,putListener:i.putListener,getListener:i.getListener,deleteListener:i.deleteListener,deleteAllListeners:i.deleteAllListeners});t.exports=v},{102:102,134:134,15:15,17:17,18:18,27:27,59:59}],31:[function(e,t,n){"use strict";var r=e(79),o=e(116),i=e(132),a=e(147),u={instantiateChildren:function(e,t,n){var r=o(e);for(var a in r)if(r.hasOwnProperty(a)){var u=r[a],s=i(u,null);r[a]=s}return r},updateChildren:function(e,t,n,u){var s=o(t);if(!s&&!e)return null;var l;for(l in s)if(s.hasOwnProperty(l)){var c=e&&e[l],p=c&&c._currentElement,d=s[l];if(a(p,d))r.receiveComponent(c,d,n,u),s[l]=c;else{c&&r.unmountComponent(c,l);var f=i(d,null);s[l]=f}}for(l in e)!e.hasOwnProperty(l)||s&&s.hasOwnProperty(l)||r.unmountComponent(e[l]);return s},unmountChildren:function(e){for(var t in e){var n=e[t];r.unmountComponent(n)}}};t.exports=u},{116:116,132:132,147:147,79:79}],32:[function(e,t,n){"use strict";function r(e,t){this.forEachFunction=e,this.forEachContext=t}function o(e,t,n,r){var o=e;o.forEachFunction.call(o.forEachContext,t,r)}function i(e,t,n){if(null==e)return e;var i=r.getPooled(t,n);f(e,o,i),r.release(i)}function a(e,t,n){this.mapResult=e,this.mapFunction=t,this.mapContext=n}function u(e,t,n,r){var o=e,i=o.mapResult,a=!i.hasOwnProperty(n);if(a){var u=o.mapFunction.call(o.mapContext,t,r);i[n]=u}}function s(e,t,n){if(null==e)return e;var r={},o=a.getPooled(r,t,n);return f(e,u,o),a.release(o),d.create(r)}function l(e,t,n,r){return null}function c(e,t){return f(e,l,null)}var p=e(28),d=e(61),f=e(149),h=(e(150),p.twoArgumentPooler),m=p.threeArgumentPooler;p.addPoolingTo(r,h),p.addPoolingTo(a,m);var v={forEach:i,map:s,count:c};t.exports=v},{149:149,150:150,28:28,61:61}],33:[function(e,t,n){"use strict";function r(e,t){var n=D.hasOwnProperty(t)?D[t]:null;N.hasOwnProperty(t)&&y(n===_.OVERRIDE_BASE),e.hasOwnProperty(t)&&y(n===_.DEFINE_MANY||n===_.DEFINE_MANY_MERGED)}function o(e,t){if(t){y("function"!=typeof t),y(!d.isValidElement(t));var n=e.prototype;t.hasOwnProperty(b)&&M.mixins(e,t.mixins);for(var o in t)if(t.hasOwnProperty(o)&&o!==b){var i=t[o];if(r(n,o),M.hasOwnProperty(o))M[o](e,i);else{var a=D.hasOwnProperty(o),l=n.hasOwnProperty(o),c=i&&i.__reactDontBind,p="function"==typeof i,f=p&&!a&&!l&&!c;if(f)n.__reactAutoBindMap||(n.__reactAutoBindMap={}),n.__reactAutoBindMap[o]=i,n[o]=i;else if(l){var h=D[o];y(a&&(h===_.DEFINE_MANY_MERGED||h===_.DEFINE_MANY)),h===_.DEFINE_MANY_MERGED?n[o]=u(n[o],i):h===_.DEFINE_MANY&&(n[o]=s(n[o],i))}else n[o]=i}}}}function i(e,t){if(t)for(var n in t){var r=t[n];if(t.hasOwnProperty(n)){var o=n in M;y(!o);var i=n in e;y(!i),e[n]=r}}}function a(e,t){y(e&&t&&"object"==typeof e&&"object"==typeof t);for(var n in t)t.hasOwnProperty(n)&&(y(void 0===e[n]),e[n]=t[n]);return e}function u(e,t){return function(){var n=e.apply(this,arguments),r=t.apply(this,arguments);if(null==n)return r;if(null==r)return n;var o={};return a(o,n),a(o,r),o}}function s(e,t){return function(){e.apply(this,arguments),t.apply(this,arguments)}}function l(e,t){var n=t.bind(e);return n}function c(e){for(var t in e.__reactAutoBindMap)if(e.__reactAutoBindMap.hasOwnProperty(t)){var n=e.__reactAutoBindMap[t];e[t]=l(e,f.guard(n,e.constructor.displayName+"."+t))}}var p=e(34),d=(e(39),e(55)),f=e(58),h=e(65),m=e(66),v=(e(75),e(74),e(84)),g=e(27),y=e(133),C=e(138),E=e(139),b=(e(150),E({mixins:null})),_=C({DEFINE_ONCE:null,DEFINE_MANY:null,OVERRIDE_BASE:null,DEFINE_MANY_MERGED:null}),x=[],D={mixins:_.DEFINE_MANY,statics:_.DEFINE_MANY,propTypes:_.DEFINE_MANY,contextTypes:_.DEFINE_MANY,childContextTypes:_.DEFINE_MANY,getDefaultProps:_.DEFINE_MANY_MERGED,getInitialState:_.DEFINE_MANY_MERGED,getChildContext:_.DEFINE_MANY_MERGED,render:_.DEFINE_ONCE,componentWillMount:_.DEFINE_MANY,componentDidMount:_.DEFINE_MANY,componentWillReceiveProps:_.DEFINE_MANY,shouldComponentUpdate:_.DEFINE_ONCE,componentWillUpdate:_.DEFINE_MANY,componentDidUpdate:_.DEFINE_MANY,componentWillUnmount:_.DEFINE_MANY,updateComponent:_.OVERRIDE_BASE},M={displayName:function(e,t){e.displayName=t},mixins:function(e,t){if(t)for(var n=0;n<t.length;n++)o(e,t[n])},childContextTypes:function(e,t){e.childContextTypes=g({},e.childContextTypes,t)},contextTypes:function(e,t){e.contextTypes=g({},e.contextTypes,t)},getDefaultProps:function(e,t){e.getDefaultProps?e.getDefaultProps=u(e.getDefaultProps,t):e.getDefaultProps=t},propTypes:function(e,t){e.propTypes=g({},e.propTypes,t)},statics:function(e,t){i(e,t)}},N={replaceState:function(e,t){v.enqueueReplaceState(this,e),t&&v.enqueueCallback(this,t)},isMounted:function(){var e=h.get(this);return e&&e!==m.currentlyMountingInstance},setProps:function(e,t){v.enqueueSetProps(this,e),t&&v.enqueueCallback(this,t)},replaceProps:function(e,t){v.enqueueReplaceProps(this,e),t&&v.enqueueCallback(this,t)}},I=function(){};g(I.prototype,p.prototype,N);var T={createClass:function(e){var t=function(e,t){this.__reactAutoBindMap&&c(this),this.props=e,this.context=t,this.state=null;var n=this.getInitialState?this.getInitialState():null;y("object"==typeof n&&!Array.isArray(n)),this.state=n};t.prototype=new I,t.prototype.constructor=t,x.forEach(o.bind(null,t)),o(t,e),t.getDefaultProps&&(t.defaultProps=t.getDefaultProps()),y(t.prototype.render);for(var n in D)t.prototype[n]||(t.prototype[n]=null);return t.type=t,t},injection:{injectMixin:function(e){x.push(e)}}};t.exports=T},{133:133,138:138,139:139,150:150,27:27,34:34,39:39,55:55,58:58,65:65,66:66,74:74,75:75,84:84}],34:[function(e,t,n){"use strict";function r(e,t){this.props=e,this.context=t}{var o=e(84),i=e(133);e(150)}r.prototype.setState=function(e,t){i("object"==typeof e||"function"==typeof e||null==e),o.enqueueSetState(this,e),t&&o.enqueueCallback(this,t)},r.prototype.forceUpdate=function(e){o.enqueueForceUpdate(this),e&&o.enqueueCallback(this,e)};t.exports=r},{133:133,150:150,84:84}],35:[function(e,t,n){"use strict";var r=e(44),o=e(68),i={processChildrenUpdates:r.dangerouslyProcessChildrenUpdates,replaceNodeWithMarkupByID:r.dangerouslyReplaceNodeWithMarkupByID,unmountIDFromEnvironment:function(e){o.purgeID(e)}};t.exports=i},{44:44,68:68}],36:[function(e,t,n){"use strict";var r=e(133),o=!1,i={unmountIDFromEnvironment:null,replaceNodeWithMarkupByID:null,processChildrenUpdates:null,injection:{injectEnvironment:function(e){r(!o),i.unmountIDFromEnvironment=e.unmountIDFromEnvironment,i.replaceNodeWithMarkupByID=e.replaceNodeWithMarkupByID,i.processChildrenUpdates=e.processChildrenUpdates,o=!0}}};t.exports=i},{133:133}],37:[function(e,t,n){"use strict";function r(e){var t=e._currentElement._owner||null;if(t){var n=t.getName();if(n)return" Check the render method of `"+n+"`."}return""}var o=e(36),i=e(38),a=e(39),u=e(55),s=(e(56),e(65)),l=e(66),c=e(71),p=e(73),d=e(75),f=(e(74),e(79)),h=e(85),m=e(27),v=e(113),g=e(133),y=e(147),C=(e(150),1),E={construct:function(e){this._currentElement=e,this._rootNodeID=null,this._instance=null,this._pendingElement=null,this._pendingStateQueue=null,this._pendingReplaceState=!1,this._pendingForceUpdate=!1,this._renderedComponent=null,this._context=null,this._mountOrder=0,this._isTopLevel=!1,this._pendingCallbacks=null},mountComponent:function(e,t,n){this._context=n,this._mountOrder=C++,this._rootNodeID=e;var r=this._processProps(this._currentElement.props),o=this._processContext(this._currentElement._context),i=c.getComponentClassForElement(this._currentElement),a=new i(r,o);a.props=r,a.context=o,a.refs=v,this._instance=a,s.set(a,this);var u=a.state;void 0===u&&(a.state=u=null),g("object"==typeof u&&!Array.isArray(u)),this._pendingStateQueue=null,this._pendingReplaceState=!1,this._pendingForceUpdate=!1;var p,d,h=l.currentlyMountingInstance;l.currentlyMountingInstance=this;try{a.componentWillMount&&(a.componentWillMount(),this._pendingStateQueue&&(a.state=this._processPendingState(a.props,a.context))),p=this._getValidatedChildContext(n),d=this._renderValidatedComponent(p)}finally{l.currentlyMountingInstance=h}this._renderedComponent=this._instantiateReactComponent(d,this._currentElement.type);var m=f.mountComponent(this._renderedComponent,e,t,this._mergeChildContext(n,p));return a.componentDidMount&&t.getReactMountReady().enqueue(a.componentDidMount,a),m},unmountComponent:function(){var e=this._instance;if(e.componentWillUnmount){var t=l.currentlyUnmountingInstance;l.currentlyUnmountingInstance=this;try{e.componentWillUnmount()}finally{l.currentlyUnmountingInstance=t}}f.unmountComponent(this._renderedComponent),this._renderedComponent=null,this._pendingStateQueue=null,this._pendingReplaceState=!1,this._pendingForceUpdate=!1,this._pendingCallbacks=null,this._pendingElement=null,this._context=null,this._rootNodeID=null,s.remove(e)},_setPropsInternal:function(e,t){var n=this._pendingElement||this._currentElement;this._pendingElement=u.cloneAndReplaceProps(n,m({},n.props,e)),h.enqueueUpdate(this,t)},_maskContext:function(e){var t=null;if("string"==typeof this._currentElement.type)return v;var n=this._currentElement.type.contextTypes;if(!n)return v;t={};for(var r in n)t[r]=e[r];return t},_processContext:function(e){var t=this._maskContext(e);return t},_getValidatedChildContext:function(e){var t=this._instance,n=t.getChildContext&&t.getChildContext();if(n){g("object"==typeof t.constructor.childContextTypes);for(var r in n)g(r in t.constructor.childContextTypes);return n}return null},_mergeChildContext:function(e,t){return t?m({},e,t):e},_processProps:function(e){return e},_checkPropTypes:function(e,t,n){var o=this.getName();for(var i in e)if(e.hasOwnProperty(i)){var a;try{g("function"==typeof e[i]),a=e[i](t,i,o,n)}catch(u){a=u}a instanceof Error&&(r(this),n===d.prop)}},receiveComponent:function(e,t,n){var r=this._currentElement,o=this._context;this._pendingElement=null,this.updateComponent(t,r,e,o,n)},performUpdateIfNecessary:function(e){null!=this._pendingElement&&f.receiveComponent(this,this._pendingElement||this._currentElement,e,this._context),(null!==this._pendingStateQueue||this._pendingForceUpdate)&&this.updateComponent(e,this._currentElement,this._currentElement,this._context,this._context)},_warnIfContextsDiffer:function(e,t){e=this._maskContext(e),t=this._maskContext(t);for(var n=Object.keys(t).sort(),r=(this.getName()||"ReactCompositeComponent",0);r<n.length;r++)n[r]},updateComponent:function(e,t,n,r,o){var i=this._instance,a=i.context,u=i.props;t!==n&&(a=this._processContext(n._context),u=this._processProps(n.props),i.componentWillReceiveProps&&i.componentWillReceiveProps(u,a));var s=this._processPendingState(u,a),l=this._pendingForceUpdate||!i.shouldComponentUpdate||i.shouldComponentUpdate(u,s,a);l?(this._pendingForceUpdate=!1,this._performComponentUpdate(n,u,s,a,e,o)):(this._currentElement=n,this._context=o,i.props=u,i.state=s,i.context=a)},_processPendingState:function(e,t){var n=this._instance,r=this._pendingStateQueue,o=this._pendingReplaceState;if(this._pendingReplaceState=!1,this._pendingStateQueue=null,!r)return n.state;if(o&&1===r.length)return r[0];for(var i=m({},o?r[0]:n.state),a=o?1:0;a<r.length;a++){var u=r[a];m(i,"function"==typeof u?u.call(n,i,e,t):u)}return i},_performComponentUpdate:function(e,t,n,r,o,i){var a=this._instance,u=a.props,s=a.state,l=a.context;a.componentWillUpdate&&a.componentWillUpdate(t,n,r),this._currentElement=e,this._context=i,a.props=t,a.state=n,a.context=r,this._updateRenderedComponent(o,i),a.componentDidUpdate&&o.getReactMountReady().enqueue(a.componentDidUpdate.bind(a,u,s,l),a)},_updateRenderedComponent:function(e,t){var n=this._renderedComponent,r=n._currentElement,o=this._getValidatedChildContext(),i=this._renderValidatedComponent(o);if(y(r,i))f.receiveComponent(n,i,e,this._mergeChildContext(t,o));else{var a=this._rootNodeID,u=n._rootNodeID;f.unmountComponent(n),this._renderedComponent=this._instantiateReactComponent(i,this._currentElement.type);var s=f.mountComponent(this._renderedComponent,a,e,this._mergeChildContext(t,o));this._replaceNodeWithMarkupByID(u,s)}},_replaceNodeWithMarkupByID:function(e,t){o.replaceNodeWithMarkupByID(e,t)},_renderValidatedComponentWithoutOwnerOrContext:function(){var e=this._instance,t=e.render();return t},_renderValidatedComponent:function(e){var t,n=i.current;i.current=this._mergeChildContext(this._currentElement._context,e),a.current=this;try{t=this._renderValidatedComponentWithoutOwnerOrContext()}finally{i.current=n,a.current=null}return g(null===t||t===!1||u.isValidElement(t)),t},attachRef:function(e,t){var n=this.getPublicInstance(),r=n.refs===v?n.refs={}:n.refs;r[e]=t.getPublicInstance()},detachRef:function(e){var t=this.getPublicInstance().refs;delete t[e]},getName:function(){var e=this._currentElement.type,t=this._instance&&this._instance.constructor;return e.displayName||t&&t.displayName||e.name||t&&t.name||null},getPublicInstance:function(){return this._instance},_instantiateReactComponent:null};p.measureMethods(E,"ReactCompositeComponent",{mountComponent:"mountComponent",updateComponent:"updateComponent",_renderValidatedComponent:"_renderValidatedComponent"});var b={Mixin:E};t.exports=b},{113:113,133:133,147:147,150:150,27:27,36:36,38:38,39:39,55:55,56:56,65:65,66:66,71:71,73:73,74:74,75:75,79:79,85:85}],38:[function(e,t,n){"use strict";var r=e(27),o=e(113),i=(e(150),{current:o,withContext:function(e,t){var n,o=i.current;i.current=r({},o,e);try{n=t()}finally{i.current=o}return n}});t.exports=i},{113:113,150:150,27:27}],39:[function(e,t,n){"use strict";var r={current:null};t.exports=r},{}],40:[function(e,t,n){"use strict";function r(e){return o.createFactory(e)}var o=e(55),i=(e(56),e(140)),a=i({a:"a",abbr:"abbr",address:"address",area:"area",article:"article",aside:"aside",audio:"audio",b:"b",base:"base",bdi:"bdi",bdo:"bdo",big:"big",blockquote:"blockquote",body:"body",br:"br",button:"button",canvas:"canvas",caption:"caption",cite:"cite",code:"code",col:"col",colgroup:"colgroup",data:"data",datalist:"datalist",dd:"dd",del:"del",details:"details",dfn:"dfn",dialog:"dialog",div:"div",dl:"dl",dt:"dt",em:"em",embed:"embed",fieldset:"fieldset",figcaption:"figcaption",figure:"figure",footer:"footer",form:"form",h1:"h1",h2:"h2",h3:"h3",h4:"h4",h5:"h5",h6:"h6",head:"head",header:"header",hr:"hr",html:"html",i:"i",iframe:"iframe",img:"img",input:"input",ins:"ins",kbd:"kbd",keygen:"keygen",label:"label",legend:"legend",li:"li",link:"link",main:"main",map:"map",mark:"mark",menu:"menu",menuitem:"menuitem",meta:"meta",meter:"meter",nav:"nav",noscript:"noscript",object:"object",ol:"ol",optgroup:"optgroup",option:"option",output:"output",p:"p",param:"param",picture:"picture",pre:"pre",progress:"progress",q:"q",rp:"rp",rt:"rt",ruby:"ruby",s:"s",samp:"samp",script:"script",section:"section",select:"select",small:"small",source:"source",span:"span",strong:"strong",style:"style",sub:"sub",summary:"summary",sup:"sup",table:"table",tbody:"tbody",td:"td",textarea:"textarea",tfoot:"tfoot",th:"th",thead:"thead",time:"time",title:"title",tr:"tr",track:"track",u:"u",ul:"ul","var":"var",video:"video",wbr:"wbr",circle:"circle",clipPath:"clipPath",defs:"defs",ellipse:"ellipse",g:"g",line:"line",linearGradient:"linearGradient",mask:"mask",path:"path",pattern:"pattern",polygon:"polygon",polyline:"polyline",radialGradient:"radialGradient",rect:"rect",stop:"stop",svg:"svg",text:"text",tspan:"tspan"},r);t.exports=a},{140:140,55:55,56:56}],41:[function(e,t,n){"use strict";var r=e(2),o=e(29),i=e(33),a=e(55),u=e(138),s=a.createFactory("button"),l=u({onClick:!0,onDoubleClick:!0,onMouseDown:!0,onMouseMove:!0,onMouseUp:!0,onClickCapture:!0,onDoubleClickCapture:!0,onMouseDownCapture:!0,onMouseMoveCapture:!0,onMouseUpCapture:!0}),c=i.createClass({displayName:"ReactDOMButton",tagName:"BUTTON",mixins:[r,o],render:function(){var e={};for(var t in this.props)!this.props.hasOwnProperty(t)||this.props.disabled&&l[t]||(e[t]=this.props[t]);return s(e,this.props.children)}});t.exports=c},{138:138,2:2,29:29,33:33,55:55}],42:[function(e,t,n){"use strict";function r(e){e&&(null!=e.dangerouslySetInnerHTML&&(g(null==e.children),g("object"==typeof e.dangerouslySetInnerHTML&&"__html"in e.dangerouslySetInnerHTML)),g(null==e.style||"object"==typeof e.style))}function o(e,t,n,r){var o=d.findReactContainerForID(e);if(o){var i=o.nodeType===D?o.ownerDocument:o;E(t,i)}r.getPutListenerQueue().enqueuePutListener(e,t,n)}function i(e){P.call(T,e)||(g(I.test(e)),T[e]=!0)}function a(e){i(e),this._tag=e,this._renderedChildren=null,this._previousStyleCopy=null,this._rootNodeID=null}var u=e(5),s=e(10),l=e(11),c=e(30),p=e(35),d=e(68),f=e(69),h=e(73),m=e(27),v=e(114),g=e(133),y=(e(134),e(139)),C=(e(150),c.deleteListener),E=c.listenTo,b=c.registrationNameModules,_={string:!0,number:!0},x=y({style:null}),D=1,M=null,N={area:!0,base:!0,br:!0,col:!0,embed:!0,hr:!0,img:!0,input:!0,keygen:!0,link:!0,meta:!0,param:!0,source:!0,track:!0,wbr:!0},I=/^[a-zA-Z][a-zA-Z:_\.\-\d]*$/,T={},P={}.hasOwnProperty;a.displayName="ReactDOMComponent",a.Mixin={construct:function(e){this._currentElement=e},mountComponent:function(e,t,n){this._rootNodeID=e,r(this._currentElement.props);var o=N[this._tag]?"":"</"+this._tag+">";return this._createOpenTagMarkupAndPutListeners(t)+this._createContentMarkup(t,n)+o},_createOpenTagMarkupAndPutListeners:function(e){var t=this._currentElement.props,n="<"+this._tag;for(var r in t)if(t.hasOwnProperty(r)){var i=t[r];if(null!=i)if(b.hasOwnProperty(r))o(this._rootNodeID,r,i,e);else{r===x&&(i&&(i=this._previousStyleCopy=m({},t.style)),i=u.createMarkupForStyles(i));var a=l.createMarkupForProperty(r,i);a&&(n+=" "+a)}}if(e.renderToStaticMarkup)return n+">";var s=l.createMarkupForID(this._rootNodeID);return n+" "+s+">"},_createContentMarkup:function(e,t){var n="";("listing"===this._tag||"pre"===this._tag||"textarea"===this._tag)&&(n="\n");var r=this._currentElement.props,o=r.dangerouslySetInnerHTML;if(null!=o){if(null!=o.__html)return n+o.__html}else{var i=_[typeof r.children]?r.children:null,a=null!=i?null:r.children;if(null!=i)return n+v(i);if(null!=a){var u=this.mountChildren(a,e,t);return n+u.join("")}}return n},receiveComponent:function(e,t,n){var r=this._currentElement;this._currentElement=e,this.updateComponent(t,r,e,n)},updateComponent:function(e,t,n,o){r(this._currentElement.props),this._updateDOMProperties(t.props,e),this._updateDOMChildren(t.props,e,o)},_updateDOMProperties:function(e,t){var n,r,i,a=this._currentElement.props;for(n in e)if(!a.hasOwnProperty(n)&&e.hasOwnProperty(n))if(n===x){var u=this._previousStyleCopy;for(r in u)u.hasOwnProperty(r)&&(i=i||{},i[r]="");this._previousStyleCopy=null}else b.hasOwnProperty(n)?C(this._rootNodeID,n):(s.isStandardName[n]||s.isCustomAttribute(n))&&M.deletePropertyByID(this._rootNodeID,n);for(n in a){var l=a[n],c=n===x?this._previousStyleCopy:e[n];if(a.hasOwnProperty(n)&&l!==c)if(n===x)if(l?l=this._previousStyleCopy=m({},l):this._previousStyleCopy=null,c){for(r in c)!c.hasOwnProperty(r)||l&&l.hasOwnProperty(r)||(i=i||{},i[r]="");for(r in l)l.hasOwnProperty(r)&&c[r]!==l[r]&&(i=i||{},i[r]=l[r])}else i=l;else b.hasOwnProperty(n)?o(this._rootNodeID,n,l,t):(s.isStandardName[n]||s.isCustomAttribute(n))&&M.updatePropertyByID(this._rootNodeID,n,l)}i&&M.updateStylesByID(this._rootNodeID,i)},_updateDOMChildren:function(e,t,n){var r=this._currentElement.props,o=_[typeof e.children]?e.children:null,i=_[typeof r.children]?r.children:null,a=e.dangerouslySetInnerHTML&&e.dangerouslySetInnerHTML.__html,u=r.dangerouslySetInnerHTML&&r.dangerouslySetInnerHTML.__html,s=null!=o?null:e.children,l=null!=i?null:r.children,c=null!=o||null!=a,p=null!=i||null!=u;null!=s&&null==l?this.updateChildren(null,t,n):c&&!p&&this.updateTextContent(""),null!=i?o!==i&&this.updateTextContent(""+i):null!=u?a!==u&&M.updateInnerHTMLByID(this._rootNodeID,u):null!=l&&this.updateChildren(l,t,n)},unmountComponent:function(){this.unmountChildren(),c.deleteAllListeners(this._rootNodeID),p.unmountIDFromEnvironment(this._rootNodeID),this._rootNodeID=null}},h.measureMethods(a,"ReactDOMComponent",{mountComponent:"mountComponent",updateComponent:"updateComponent"}),m(a.prototype,a.Mixin,f.Mixin),a.injection={injectIDOperations:function(e){a.BackendIDOperations=M=e}},t.exports=a},{10:10,11:11,114:114,133:133,134:134,139:139,150:150,27:27,30:30,35:35,5:5,68:68,69:69,73:73}],43:[function(e,t,n){"use strict";var r=e(15),o=e(25),i=e(29),a=e(33),u=e(55),s=u.createFactory("form"),l=a.createClass({displayName:"ReactDOMForm",tagName:"FORM",mixins:[i,o],render:function(){return s(this.props)},componentDidMount:function(){this.trapBubbledEvent(r.topLevelTypes.topReset,"reset"),this.trapBubbledEvent(r.topLevelTypes.topSubmit,"submit")}});t.exports=l},{15:15,25:25,29:29,33:33,55:55}],44:[function(e,t,n){"use strict";var r=e(5),o=e(9),i=e(11),a=e(68),u=e(73),s=e(133),l=e(144),c={dangerouslySetInnerHTML:"`dangerouslySetInnerHTML` must be set using `updateInnerHTMLByID()`.",style:"`style` must be set using `updateStylesByID()`."},p={updatePropertyByID:function(e,t,n){var r=a.getNode(e);s(!c.hasOwnProperty(t)),null!=n?i.setValueForProperty(r,t,n):i.deleteValueForProperty(r,t)},deletePropertyByID:function(e,t,n){var r=a.getNode(e);s(!c.hasOwnProperty(t)),i.deleteValueForProperty(r,t,n)},updateStylesByID:function(e,t){var n=a.getNode(e);r.setValueForStyles(n,t)},updateInnerHTMLByID:function(e,t){var n=a.getNode(e);l(n,t)},updateTextContentByID:function(e,t){var n=a.getNode(e);o.updateTextContent(n,t)},dangerouslyReplaceNodeWithMarkupByID:function(e,t){var n=a.getNode(e);o.dangerouslyReplaceNodeWithMarkup(n,t)},dangerouslyProcessChildrenUpdates:function(e,t){for(var n=0;n<e.length;n++)e[n].parentNode=a.getNode(e[n].parentID);o.processUpdates(e,t)}};u.measureMethods(p,"ReactDOMIDOperations",{updatePropertyByID:"updatePropertyByID",deletePropertyByID:"deletePropertyByID",updateStylesByID:"updateStylesByID",updateInnerHTMLByID:"updateInnerHTMLByID",updateTextContentByID:"updateTextContentByID",dangerouslyReplaceNodeWithMarkupByID:"dangerouslyReplaceNodeWithMarkupByID",dangerouslyProcessChildrenUpdates:"dangerouslyProcessChildrenUpdates"}),t.exports=p},{11:11,133:133,144:144,5:5,68:68,73:73,9:9}],45:[function(e,t,n){"use strict";var r=e(15),o=e(25),i=e(29),a=e(33),u=e(55),s=u.createFactory("iframe"),l=a.createClass({displayName:"ReactDOMIframe",tagName:"IFRAME",mixins:[i,o],render:function(){return s(this.props)},componentDidMount:function(){this.trapBubbledEvent(r.topLevelTypes.topLoad,"load")}});t.exports=l},{15:15,25:25,29:29,33:33,55:55}],46:[function(e,t,n){"use strict";var r=e(15),o=e(25),i=e(29),a=e(33),u=e(55),s=u.createFactory("img"),l=a.createClass({displayName:"ReactDOMImg",tagName:"IMG",mixins:[i,o],render:function(){return s(this.props)},componentDidMount:function(){this.trapBubbledEvent(r.topLevelTypes.topLoad,"load"),this.trapBubbledEvent(r.topLevelTypes.topError,"error")}});t.exports=l},{15:15,25:25,29:29,33:33,55:55}],47:[function(e,t,n){"use strict";function r(){this.isMounted()&&this.forceUpdate()}var o=e(2),i=e(11),a=e(24),u=e(29),s=e(33),l=e(55),c=e(68),p=e(85),d=e(27),f=e(133),h=l.createFactory("input"),m={},v=s.createClass({displayName:"ReactDOMInput",tagName:"INPUT",mixins:[o,a.Mixin,u],getInitialState:function(){var e=this.props.defaultValue;return{initialChecked:this.props.defaultChecked||!1,initialValue:null!=e?e:null}},render:function(){var e=d({},this.props);e.defaultChecked=null,e.defaultValue=null;var t=a.getValue(this);e.value=null!=t?t:this.state.initialValue;var n=a.getChecked(this);return e.checked=null!=n?n:this.state.initialChecked,e.onChange=this._handleChange,h(e,this.props.children)},componentDidMount:function(){var e=c.getID(this.getDOMNode());m[e]=this},componentWillUnmount:function(){var e=this.getDOMNode(),t=c.getID(e);delete m[t]},componentDidUpdate:function(e,t,n){var r=this.getDOMNode();null!=this.props.checked&&i.setValueForProperty(r,"checked",this.props.checked||!1);var o=a.getValue(this);null!=o&&i.setValueForProperty(r,"value",""+o)},_handleChange:function(e){var t,n=a.getOnChange(this);n&&(t=n.call(this,e)),p.asap(r,this);var o=this.props.name;if("radio"===this.props.type&&null!=o){for(var i=this.getDOMNode(),u=i;u.parentNode;)u=u.parentNode;for(var s=u.querySelectorAll("input[name="+JSON.stringify(""+o)+'][type="radio"]'),l=0,d=s.length;d>l;l++){var h=s[l];if(h!==i&&h.form===i.form){var v=c.getID(h);f(v);var g=m[v];f(g),p.asap(r,g)}}}return t}});t.exports=v},{11:11,133:133,2:2,24:24,27:27,29:29,33:33,55:55,68:68,85:85}],48:[function(e,t,n){"use strict";var r=e(29),o=e(33),i=e(55),a=(e(150),i.createFactory("option")),u=o.createClass({displayName:"ReactDOMOption",tagName:"OPTION",mixins:[r],componentWillMount:function(){},render:function(){return a(this.props,this.props.children)}});t.exports=u},{150:150,29:29,33:33,55:55}],49:[function(e,t,n){"use strict";function r(){if(this._pendingUpdate){this._pendingUpdate=!1;var e=u.getValue(this);null!=e&&this.isMounted()&&i(this,e)}}function o(e,t,n){if(null==e[t])return null;if(e.multiple){if(!Array.isArray(e[t]))return new Error("The `"+t+"` prop supplied to <select> must be an array if `multiple` is true.")}else if(Array.isArray(e[t]))return new Error("The `"+t+"` prop supplied to <select> must be a scalar value if `multiple` is false.")}function i(e,t){var n,r,o,i=e.getDOMNode().options;if(e.props.multiple){for(n={},r=0,o=t.length;o>r;r++)n[""+t[r]]=!0;for(r=0,o=i.length;o>r;r++){var a=n.hasOwnProperty(i[r].value);i[r].selected!==a&&(i[r].selected=a)}}else{for(n=""+t,r=0,o=i.length;o>r;r++)if(i[r].value===n)return void(i[r].selected=!0);i.length&&(i[0].selected=!0)}}var a=e(2),u=e(24),s=e(29),l=e(33),c=e(55),p=e(85),d=e(27),f=c.createFactory("select"),h=l.createClass({displayName:"ReactDOMSelect",tagName:"SELECT",mixins:[a,u.Mixin,s],propTypes:{defaultValue:o,value:o},render:function(){var e=d({},this.props);return e.onChange=this._handleChange,e.value=null,f(e,this.props.children)},componentWillMount:function(){this._pendingUpdate=!1},componentDidMount:function(){var e=u.getValue(this);null!=e?i(this,e):null!=this.props.defaultValue&&i(this,this.props.defaultValue)},componentDidUpdate:function(e){var t=u.getValue(this);null!=t?(this._pendingUpdate=!1,i(this,t)):!e.multiple!=!this.props.multiple&&(null!=this.props.defaultValue?i(this,this.props.defaultValue):i(this,this.props.multiple?[]:""))},_handleChange:function(e){var t,n=u.getOnChange(this);return n&&(t=n.call(this,e)),this._pendingUpdate=!0,p.asap(r,this),t}});t.exports=h},{2:2,24:24,27:27,29:29,33:33,55:55,85:85}],50:[function(e,t,n){"use strict";function r(e,t,n,r){return e===n&&t===r}function o(e){var t=document.selection,n=t.createRange(),r=n.text.length,o=n.duplicate();o.moveToElementText(e),o.setEndPoint("EndToStart",n);var i=o.text.length,a=i+r;return{start:i,end:a}}function i(e){var t=window.getSelection&&window.getSelection();if(!t||0===t.rangeCount)return null;var n=t.anchorNode,o=t.anchorOffset,i=t.focusNode,a=t.focusOffset,u=t.getRangeAt(0),s=r(t.anchorNode,t.anchorOffset,t.focusNode,t.focusOffset),l=s?0:u.toString().length,c=u.cloneRange();c.selectNodeContents(e),c.setEnd(u.startContainer,u.startOffset);var p=r(c.startContainer,c.startOffset,c.endContainer,c.endOffset),d=p?0:c.toString().length,f=d+l,h=document.createRange();h.setStart(n,o),h.setEnd(i,a);var m=h.collapsed;return{start:m?f:d,end:m?d:f}}function a(e,t){var n,r,o=document.selection.createRange().duplicate();"undefined"==typeof t.end?(n=t.start,r=n):t.start>t.end?(n=t.end,r=t.start):(n=t.start,r=t.end),o.moveToElementText(e),o.moveStart("character",n),o.setEndPoint("EndToStart",o),o.moveEnd("character",r-n),o.select()}function u(e,t){if(window.getSelection){var n=window.getSelection(),r=e[c()].length,o=Math.min(t.start,r),i="undefined"==typeof t.end?o:Math.min(t.end,r);if(!n.extend&&o>i){var a=i;i=o,o=a}var u=l(e,o),s=l(e,i);if(u&&s){var p=document.createRange();p.setStart(u.node,u.offset),n.removeAllRanges(),o>i?(n.addRange(p),n.extend(s.node,s.offset)):(p.setEnd(s.node,s.offset),n.addRange(p))}}}var s=e(21),l=e(126),c=e(128),p=s.canUseDOM&&"selection"in document&&!("getSelection"in window),d={getOffsets:p?o:i,setOffsets:p?a:u};t.exports=d},{126:126,128:128,21:21}],51:[function(e,t,n){"use strict";var r=e(11),o=e(35),i=e(42),a=e(27),u=e(114),s=function(e){};a(s.prototype,{construct:function(e){this._currentElement=e,this._stringText=""+e,this._rootNodeID=null,this._mountIndex=0},mountComponent:function(e,t,n){this._rootNodeID=e;var o=u(this._stringText);return t.renderToStaticMarkup?o:"<span "+r.createMarkupForID(e)+">"+o+"</span>"},receiveComponent:function(e,t){if(e!==this._currentElement){this._currentElement=e;var n=""+e;n!==this._stringText&&(this._stringText=n,i.BackendIDOperations.updateTextContentByID(this._rootNodeID,n))}},unmountComponent:function(){o.unmountIDFromEnvironment(this._rootNodeID)}}),t.exports=s},{11:11,114:114,27:27,35:35,42:42}],52:[function(e,t,n){"use strict";function r(){this.isMounted()&&this.forceUpdate()}var o=e(2),i=e(11),a=e(24),u=e(29),s=e(33),l=e(55),c=e(85),p=e(27),d=e(133),f=(e(150),l.createFactory("textarea")),h=s.createClass({displayName:"ReactDOMTextarea",tagName:"TEXTAREA",mixins:[o,a.Mixin,u],getInitialState:function(){var e=this.props.defaultValue,t=this.props.children;null!=t&&(d(null==e),Array.isArray(t)&&(d(t.length<=1),t=t[0]),e=""+t),null==e&&(e="");var n=a.getValue(this);return{initialValue:""+(null!=n?n:e)}},render:function(){var e=p({},this.props);return d(null==e.dangerouslySetInnerHTML),e.defaultValue=null,e.value=null,e.onChange=this._handleChange,f(e,this.state.initialValue)},componentDidUpdate:function(e,t,n){var r=a.getValue(this);if(null!=r){var o=this.getDOMNode();i.setValueForProperty(o,"value",""+r)}},_handleChange:function(e){var t,n=a.getOnChange(this);return n&&(t=n.call(this,e)),c.asap(r,this),t}});t.exports=h},{11:11,133:133,150:150,2:2,24:24,27:27,29:29,33:33,55:55,85:85}],53:[function(e,t,n){"use strict";function r(){this.reinitializeTransaction()}var o=e(85),i=e(101),a=e(27),u=e(112),s={initialize:u,close:function(){d.isBatchingUpdates=!1}},l={initialize:u,close:o.flushBatchedUpdates.bind(o)},c=[l,s];a(r.prototype,i.Mixin,{getTransactionWrappers:function(){return c}});var p=new r,d={isBatchingUpdates:!1,batchedUpdates:function(e,t,n,r,o){var i=d.isBatchingUpdates;d.isBatchingUpdates=!0,i?e(t,n,r,o):p.perform(e,null,t,n,r,o)}};t.exports=d},{101:101,112:112,27:27,85:85}],54:[function(e,t,n){"use strict";function r(e){return h.createClass({tagName:e.toUpperCase(),render:function(){return new T(e,null,null,null,null,this.props)}})}function o(){R.EventEmitter.injectReactEventListener(P),R.EventPluginHub.injectEventPluginOrder(s),R.EventPluginHub.injectInstanceHandle(w),R.EventPluginHub.injectMount(O),R.EventPluginHub.injectEventPluginsByName({SimpleEventPlugin:L,EnterLeaveEventPlugin:l,ChangeEventPlugin:a,MobileSafariClickEventPlugin:d,SelectEventPlugin:A,BeforeInputEventPlugin:i}),R.NativeComponent.injectGenericComponentClass(g),R.NativeComponent.injectTextComponentClass(I),R.NativeComponent.injectAutoWrapper(r),R.Class.injectMixin(f),R.NativeComponent.injectComponentClasses({button:y,form:C,iframe:_,img:E,input:x,option:D,select:M,textarea:N,html:F("html"),head:F("head"),body:F("body")}),R.DOMProperty.injectDOMPropertyConfig(p),R.DOMProperty.injectDOMPropertyConfig(U),R.EmptyComponent.injectEmptyComponent("noscript"),R.Updates.injectReconcileTransaction(S),R.Updates.injectBatchingStrategy(v),R.RootIndex.injectCreateReactRootIndex(c.canUseDOM?u.createReactRootIndex:k.createReactRootIndex),R.Component.injectEnvironment(m),R.DOMComponent.injectIDOperations(b)}var i=e(3),a=e(7),u=e(8),s=e(13),l=e(14),c=e(21),p=e(23),d=e(26),f=e(29),h=e(33),m=e(35),v=e(53),g=e(42),y=e(41),C=e(43),E=e(46),b=e(44),_=e(45),x=e(47),D=e(48),M=e(49),N=e(52),I=e(51),T=e(55),P=e(60),R=e(62),w=e(64),O=e(68),S=e(78),A=e(87),k=e(88),L=e(89),U=e(86),F=e(109);
+!function(t){if("object"==typeof exports&&"undefined"!=typeof module)module.exports=t();else if("function"==typeof define&&define.amd)define([],t);else{var e;e="undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof self?self:this,e.React=t()}}(function(){return function t(e,n,r){function o(u,a){if(!n[u]){if(!e[u]){var s="function"==typeof require&&require;if(!a&&s)return s(u,!0);if(i)return i(u,!0);var c=new Error("Cannot find module '"+u+"'");throw c.code="MODULE_NOT_FOUND",c}var l=n[u]={exports:{}};e[u][0].call(l.exports,function(t){var n=e[u][1][t];return o(n||t)},l,l.exports,t,e,n,r)}return n[u].exports}for(var i="function"==typeof require&&require,u=0;u<r.length;u++)o(r[u]);return o}({1:[function(t,e,n){"use strict";function r(t){var e={"=":"=0",":":"=2"};return"$"+(""+t).replace(/[=:]/g,function(t){return e[t]})}function o(t){var e={"=0":"=","=2":":"};return(""+("."===t[0]&&"$"===t[1]?t.substring(2):t.substring(1))).replace(/(=0|=2)/g,function(t){return e[t]})}var i={escape:r,unescape:o};e.exports=i},{}],2:[function(t,e,n){"use strict";var r=t(20),o=(t(24),function(t){var e=this;if(e.instancePool.length){var n=e.instancePool.pop();return e.call(n,t),n}return new e(t)}),i=function(t,e){var n=this;if(n.instancePool.length){var r=n.instancePool.pop();return n.call(r,t,e),r}return new n(t,e)},u=function(t,e,n){var r=this;if(r.instancePool.length){var o=r.instancePool.pop();return r.call(o,t,e,n),o}return new r(t,e,n)},a=function(t,e,n,r){var o=this;if(o.instancePool.length){var i=o.instancePool.pop();return o.call(i,t,e,n,r),i}return new o(t,e,n,r)},s=function(t){var e=this;t instanceof e||r("25"),t.destructor(),e.instancePool.length<e.poolSize&&e.instancePool.push(t)},c=o,l=function(t,e){var n=t;return n.instancePool=[],n.getPooled=e||c,n.poolSize||(n.poolSize=10),n.release=s,n},f={addPoolingTo:l,oneArgumentPooler:o,twoArgumentPooler:i,threeArgumentPooler:u,fourArgumentPooler:a};e.exports=f},{20:20,24:24}],3:[function(t,e,n){"use strict";var r=t(26),o=t(4),i=t(6),u=t(14),a=t(5),s=t(8),c=t(9),l=t(13),f=t(16),p=t(19),d=(t(25),c.createElement),y=c.createFactory,h=c.cloneElement,v=r,m={Children:{map:o.map,forEach:o.forEach,count:o.count,toArray:o.toArray,only:p},Component:i,PureComponent:u,createElement:d,cloneElement:h,isValidElement:c.isValidElement,PropTypes:l,createClass:a.createClass,createFactory:y,createMixin:function(t){return t},DOM:s,version:f,__spread:v};e.exports=m},{13:13,14:14,16:16,19:19,25:25,26:26,4:4,5:5,6:6,8:8,9:9}],4:[function(t,e,n){"use strict";function r(t){return(""+t).replace(E,"$&/")}function o(t,e){this.func=t,this.context=e,this.count=0}function i(t,e,n){var r=t.func,o=t.context;r.call(o,e,t.count++)}function u(t,e,n){if(null==t)return t;var r=o.getPooled(e,n);m(t,i,r),o.release(r)}function a(t,e,n,r){this.result=t,this.keyPrefix=e,this.func=n,this.context=r,this.count=0}function s(t,e,n){var o=t.result,i=t.keyPrefix,u=t.func,a=t.context,s=u.call(a,e,t.count++);Array.isArray(s)?c(s,o,n,v.thatReturnsArgument):null!=s&&(h.isValidElement(s)&&(s=h.cloneAndReplaceKey(s,i+(!s.key||e&&e.key===s.key?"":r(s.key)+"/")+n)),o.push(s))}function c(t,e,n,o,i){var u="";null!=n&&(u=r(n)+"/");var c=a.getPooled(e,u,o,i);m(t,s,c),a.release(c)}function l(t,e,n){if(null==t)return t;var r=[];return c(t,r,null,e,n),r}function f(t,e,n){return null}function p(t,e){return m(t,f,null)}function d(t){var e=[];return c(t,e,null,v.thatReturnsArgument),e}var y=t(2),h=t(9),v=t(22),m=t(21),b=y.twoArgumentPooler,g=y.fourArgumentPooler,E=/\/+/g;o.prototype.destructor=function(){this.func=null,this.context=null,this.count=0},y.addPoolingTo(o,b),a.prototype.destructor=function(){this.result=null,this.keyPrefix=null,this.func=null,this.context=null,this.count=0},y.addPoolingTo(a,g);var x={forEach:u,map:l,mapIntoWithKeyPrefixInternal:c,count:p,toArray:d};e.exports=x},{2:2,21:21,22:22,9:9}],5:[function(t,e,n){"use strict";function r(t){return t}function o(t,e){var n=E.hasOwnProperty(e)?E[e]:null;_.hasOwnProperty(e)&&"OVERRIDE_BASE"!==n&&p("73",e),t&&"DEFINE_MANY"!==n&&"DEFINE_MANY_MERGED"!==n&&p("74",e)}function i(t,e){if(e){"function"==typeof e&&p("75"),h.isValidElement(e)&&p("76");var n=t.prototype,r=n.__reactAutoBindPairs;e.hasOwnProperty(b)&&x.mixins(t,e.mixins);for(var i in e)if(e.hasOwnProperty(i)&&i!==b){var u=e[i],a=n.hasOwnProperty(i);if(o(a,i),x.hasOwnProperty(i))x[i](t,u);else{var l=E.hasOwnProperty(i),f="function"==typeof u,d=f&&!l&&!a&&!1!==e.autobind;if(d)r.push(i,u),n[i]=u;else if(a){var y=E[i];(!l||"DEFINE_MANY_MERGED"!==y&&"DEFINE_MANY"!==y)&&p("77",y,i),"DEFINE_MANY_MERGED"===y?n[i]=s(n[i],u):"DEFINE_MANY"===y&&(n[i]=c(n[i],u))}else n[i]=u}}}}function u(t,e){if(e)for(var n in e){var r=e[n];if(e.hasOwnProperty(n)){var o=n in x;o&&p("78",n);var i=n in t;i&&p("79",n),t[n]=r}}}function a(t,e){t&&e&&"object"==typeof t&&"object"==typeof e||p("80");for(var n in e)e.hasOwnProperty(n)&&(void 0!==t[n]&&p("81",n),t[n]=e[n]);return t}function s(t,e){return function(){var n=t.apply(this,arguments),r=e.apply(this,arguments);if(null==n)return r;if(null==r)return n;var o={};return a(o,n),a(o,r),o}}function c(t,e){return function(){t.apply(this,arguments),e.apply(this,arguments)}}function l(t,e){return e.bind(t)}function f(t){for(var e=t.__reactAutoBindPairs,n=0;n<e.length;n+=2){var r=e[n],o=e[n+1];t[r]=l(t,o)}}var p=t(20),d=t(26),y=t(6),h=t(9),v=(t(12),t(11)),m=t(23),b=(t(24),t(25),"mixins"),g=[],E={mixins:"DEFINE_MANY",statics:"DEFINE_MANY",propTypes:"DEFINE_MANY",contextTypes:"DEFINE_MANY",childContextTypes:"DEFINE_MANY",getDefaultProps:"DEFINE_MANY_MERGED",getInitialState:"DEFINE_MANY_MERGED",getChildContext:"DEFINE_MANY_MERGED",render:"DEFINE_ONCE",componentWillMount:"DEFINE_MANY",componentDidMount:"DEFINE_MANY",componentWillReceiveProps:"DEFINE_MANY",shouldComponentUpdate:"DEFINE_ONCE",componentWillUpdate:"DEFINE_MANY",componentDidUpdate:"DEFINE_MANY",componentWillUnmount:"DEFINE_MANY",updateComponent:"OVERRIDE_BASE"},x={displayName:function(t,e){t.displayName=e},mixins:function(t,e){if(e)for(var n=0;n<e.length;n++)i(t,e[n])},childContextTypes:function(t,e){t.childContextTypes=d({},t.childContextTypes,e)},contextTypes:function(t,e){t.contextTypes=d({},t.contextTypes,e)},getDefaultProps:function(t,e){t.getDefaultProps?t.getDefaultProps=s(t.getDefaultProps,e):t.getDefaultProps=e},propTypes:function(t,e){t.propTypes=d({},t.propTypes,e)},statics:function(t,e){u(t,e)},autobind:function(){}},_={replaceState:function(t,e){this.updater.enqueueReplaceState(this,t),e&&this.updater.enqueueCallback(this,e,"replaceState")},isMounted:function(){return this.updater.isMounted(this)}},P=function(){};d(P.prototype,y.prototype,_);var w={createClass:function(t){var e=r(function(t,n,r){this.__reactAutoBindPairs.length&&f(this),this.props=t,this.context=n,this.refs=m,this.updater=r||v,this.state=null;var o=this.getInitialState?this.getInitialState():null;("object"!=typeof o||Array.isArray(o))&&p("82",e.displayName||"ReactCompositeComponent"),this.state=o});e.prototype=new P,e.prototype.constructor=e,e.prototype.__reactAutoBindPairs=[],g.forEach(i.bind(null,e)),i(e,t),e.getDefaultProps&&(e.defaultProps=e.getDefaultProps()),e.prototype.render||p("83");for(var n in E)e.prototype[n]||(e.prototype[n]=null);return e},injection:{injectMixin:function(t){g.push(t)}}};e.exports=w},{11:11,12:12,20:20,23:23,24:24,25:25,26:26,6:6,9:9}],6:[function(t,e,n){"use strict";function r(t,e,n){this.props=t,this.context=e,this.refs=u,this.updater=n||i}var o=t(20),i=t(11),u=(t(17),t(23));t(24),t(25);r.prototype.isReactComponent={},r.prototype.setState=function(t,e){"object"!=typeof t&&"function"!=typeof t&&null!=t&&o("85"),this.updater.enqueueSetState(this,t),e&&this.updater.enqueueCallback(this,e,"setState")},r.prototype.forceUpdate=function(t){this.updater.enqueueForceUpdate(this),t&&this.updater.enqueueCallback(this,t,"forceUpdate")};e.exports=r},{11:11,17:17,20:20,23:23,24:24,25:25}],7:[function(t,e,n){"use strict";var r={current:null};e.exports=r},{}],8:[function(t,e,n){"use strict";var r=t(9),o=r.createFactory,i={a:o("a"),abbr:o("abbr"),address:o("address"),area:o("area"),article:o("article"),aside:o("aside"),audio:o("audio"),b:o("b"),base:o("base"),bdi:o("bdi"),bdo:o("bdo"),big:o("big"),blockquote:o("blockquote"),body:o("body"),br:o("br"),button:o("button"),canvas:o("canvas"),caption:o("caption"),cite:o("cite"),code:o("code"),col:o("col"),colgroup:o("colgroup"),data:o("data"),datalist:o("datalist"),dd:o("dd"),del:o("del"),details:o("details"),dfn:o("dfn"),dialog:o("dialog"),div:o("div"),dl:o("dl"),dt:o("dt"),em:o("em"),embed:o("embed"),fieldset:o("fieldset"),figcaption:o("figcaption"),figure:o("figure"),footer:o("footer"),form:o("form"),h1:o("h1"),h2:o("h2"),h3:o("h3"),h4:o("h4"),h5:o("h5"),h6:o("h6"),head:o("head"),header:o("header"),hgroup:o("hgroup"),hr:o("hr"),html:o("html"),i:o("i"),iframe:o("iframe"),img:o("img"),input:o("input"),ins:o("ins"),kbd:o("kbd"),keygen:o("keygen"),label:o("label"),legend:o("legend"),li:o("li"),link:o("link"),main:o("main"),map:o("map"),mark:o("mark"),menu:o("menu"),menuitem:o("menuitem"),meta:o("meta"),meter:o("meter"),nav:o("nav"),noscript:o("noscript"),object:o("object"),ol:o("ol"),optgroup:o("optgroup"),option:o("option"),output:o("output"),p:o("p"),param:o("param"),picture:o("picture"),pre:o("pre"),progress:o("progress"),q:o("q"),rp:o("rp"),rt:o("rt"),ruby:o("ruby"),s:o("s"),samp:o("samp"),script:o("script"),section:o("section"),select:o("select"),small:o("small"),source:o("source"),span:o("span"),strong:o("strong"),style:o("style"),sub:o("sub"),summary:o("summary"),sup:o("sup"),table:o("table"),tbody:o("tbody"),td:o("td"),textarea:o("textarea"),tfoot:o("tfoot"),th:o("th"),thead:o("thead"),time:o("time"),title:o("title"),tr:o("tr"),track:o("track"),u:o("u"),ul:o("ul"),var:o("var"),video:o("video"),wbr:o("wbr"),circle:o("circle"),clipPath:o("clipPath"),defs:o("defs"),ellipse:o("ellipse"),g:o("g"),image:o("image"),line:o("line"),linearGradient:o("linearGradient"),mask:o("mask"),path:o("path"),pattern:o("pattern"),polygon:o("polygon"),polyline:o("polyline"),radialGradient:o("radialGradient"),rect:o("rect"),stop:o("stop"),svg:o("svg"),text:o("text"),tspan:o("tspan")};e.exports=i},{9:9}],9:[function(t,e,n){"use strict";function r(t){return void 0!==t.ref}function o(t){return void 0!==t.key}var i=t(26),u=t(7),a=(t(25),t(17),Object.prototype.hasOwnProperty),s=t(10),c={key:!0,ref:!0,__self:!0,__source:!0},l=function(t,e,n,r,o,i,u){return{$$typeof:s,type:t,key:e,ref:n,props:u,_owner:i}};l.createElement=function(t,e,n){var i,s={},f=null,p=null;if(null!=e){r(e)&&(p=e.ref),o(e)&&(f=""+e.key),void 0===e.__self?null:e.__self,void 0===e.__source?null:e.__source;for(i in e)a.call(e,i)&&!c.hasOwnProperty(i)&&(s[i]=e[i])}var d=arguments.length-2;if(1===d)s.children=n;else if(d>1){for(var y=Array(d),h=0;h<d;h++)y[h]=arguments[h+2];s.children=y}if(t&&t.defaultProps){var v=t.defaultProps;for(i in v)void 0===s[i]&&(s[i]=v[i])}return l(t,f,p,0,0,u.current,s)},l.createFactory=function(t){var e=l.createElement.bind(null,t);return e.type=t,e},l.cloneAndReplaceKey=function(t,e){return l(t.type,e,t.ref,t._self,t._source,t._owner,t.props)},l.cloneElement=function(t,e,n){var s,f=i({},t.props),p=t.key,d=t.ref,y=(t._self,t._source,t._owner);if(null!=e){r(e)&&(d=e.ref,y=u.current),o(e)&&(p=""+e.key);var h;t.type&&t.type.defaultProps&&(h=t.type.defaultProps);for(s in e)a.call(e,s)&&!c.hasOwnProperty(s)&&(void 0===e[s]&&void 0!==h?f[s]=h[s]:f[s]=e[s])}var v=arguments.length-2;if(1===v)f.children=n;else if(v>1){for(var m=Array(v),b=0;b<v;b++)m[b]=arguments[b+2];f.children=m}return l(t.type,p,d,0,0,y,f)},l.isValidElement=function(t){return"object"==typeof t&&null!==t&&t.$$typeof===s},e.exports=l},{10:10,17:17,25:25,26:26,7:7}],10:[function(t,e,n){"use strict";var r="function"==typeof Symbol&&Symbol.for&&Symbol.for("react.element")||60103;e.exports=r},{}],11:[function(t,e,n){"use strict";var r=(t(25),{isMounted:function(t){return!1},enqueueCallback:function(t,e){},enqueueForceUpdate:function(t){},enqueueReplaceState:function(t,e){},enqueueSetState:function(t,e){}});e.exports=r},{25:25}],12:[function(t,e,n){"use strict";var r={};e.exports=r},{}],13:[function(t,e,n){"use strict";var r=t(9),o=r.isValidElement,i=t(28);e.exports=i(o)},{28:28,9:9}],14:[function(t,e,n){"use strict";function r(t,e,n){this.props=t,this.context=e,this.refs=s,this.updater=n||a}function o(){}var i=t(26),u=t(6),a=t(11),s=t(23);o.prototype=u.prototype,r.prototype=new o,r.prototype.constructor=r,i(r.prototype,u.prototype),r.prototype.isPureReactComponent=!0,e.exports=r},{11:11,23:23,26:26,6:6}],15:[function(t,e,n){"use strict";var r=t(26),o=t(3),i=r(o,{__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED:{ReactCurrentOwner:t(7)}});e.exports=i},{26:26,3:3,7:7}],16:[function(t,e,n){"use strict";e.exports="15.5.4"},{}],17:[function(t,e,n){"use strict";e.exports=!1},{}],18:[function(t,e,n){"use strict";function r(t){var e=t&&(o&&t[o]||t[i]);if("function"==typeof e)return e}var o="function"==typeof Symbol&&Symbol.iterator,i="@@iterator";e.exports=r},{}],19:[function(t,e,n){"use strict";function r(t){return i.isValidElement(t)||o("143"),t}var o=t(20),i=t(9);t(24);e.exports=r},{20:20,24:24,9:9}],20:[function(t,e,n){"use strict";function r(t){for(var e=arguments.length-1,n="Minified React error #"+t+"; visit http://facebook.github.io/react/docs/error-decoder.html?invariant="+t,r=0;r<e;r++)n+="&args[]="+encodeURIComponent(arguments[r+1]);n+=" for the full message or use the non-minified dev environment for full errors and additional helpful warnings.";var o=new Error(n);throw o.name="Invariant Violation",o.framesToPop=1,o}e.exports=r},{}],21:[function(t,e,n){"use strict";function r(t,e){return t&&"object"==typeof t&&null!=t.key?c.escape(t.key):e.toString(36)}function o(t,e,n,i){var p=typeof t;if("undefined"!==p&&"boolean"!==p||(t=null),null===t||"string"===p||"number"===p||"object"===p&&t.$$typeof===a)return n(i,t,""===e?l+r(t,0):e),1;var d,y,h=0,v=""===e?l:e+f;if(Array.isArray(t))for(var m=0;m<t.length;m++)d=t[m],y=v+r(d,m),h+=o(d,y,n,i);else{var b=s(t);if(b){var g,E=b.call(t);if(b!==t.entries)for(var x=0;!(g=E.next()).done;)d=g.value,y=v+r(d,x++),h+=o(d,y,n,i);else for(;!(g=E.next()).done;){var _=g.value;_&&(d=_[1],y=v+c.escape(_[0])+f+r(d,0),h+=o(d,y,n,i))}}else if("object"===p){var P=String(t);u("31","[object Object]"===P?"object with keys {"+Object.keys(t).join(", ")+"}":P,"")}}return h}function i(t,e,n){return null==t?0:o(t,"",e,n)}var u=t(20),a=(t(7),t(10)),s=t(18),c=(t(24),t(1)),l=(t(25),"."),f=":";e.exports=i},{1:1,10:10,18:18,20:20,24:24,25:25,7:7}],22:[function(t,e,n){"use strict";function r(t){return function(){return t}}var o=function(){};o.thatReturns=r,o.thatReturnsFalse=r(!1),o.thatReturnsTrue=r(!0),o.thatReturnsNull=r(null),o.thatReturnsThis=function(){return this},o.thatReturnsArgument=function(t){return t},e.exports=o},{}],23:[function(t,e,n){"use strict";var r={};e.exports=r},{}],24:[function(t,e,n){"use strict";function r(t,e,n,r,i,u,a,s){if(o(e),!t){var c;if(void 0===e)c=new Error("Minified exception occurred; use the non-minified dev environment for the full error message and additional helpful warnings.");else{var l=[n,r,i,u,a,s],f=0;c=new Error(e.replace(/%s/g,function(){return l[f++]})),c.name="Invariant Violation"}throw c.framesToPop=1,c}}var o=function(t){};e.exports=r},{}],25:[function(t,e,n){"use strict";var r=t(22),o=r;e.exports=o},{22:22}],26:[function(t,e,n){"use strict";function r(t){if(null===t||void 0===t)throw new TypeError("Object.assign cannot be called with null or undefined");return Object(t)}var o=Object.getOwnPropertySymbols,i=Object.prototype.hasOwnProperty,u=Object.prototype.propertyIsEnumerable;e.exports=function(){try{if(!Object.assign)return!1;var t=new String("abc");if(t[5]="de","5"===Object.getOwnPropertyNames(t)[0])return!1;for(var e={},n=0;n<10;n++)e["_"+String.fromCharCode(n)]=n;if("0123456789"!==Object.getOwnPropertyNames(e).map(function(t){return e[t]}).join(""))return!1;var r={};return"abcdefghijklmnopqrst".split("").forEach(function(t){r[t]=t}),"abcdefghijklmnopqrst"===Object.keys(Object.assign({},r)).join("")}catch(t){return!1}}()?Object.assign:function(t,e){for(var n,a,s=r(t),c=1;c<arguments.length;c++){n=Object(arguments[c]);for(var l in n)i.call(n,l)&&(s[l]=n[l]);if(o){a=o(n);for(var f=0;f<a.length;f++)u.call(n,a[f])&&(s[a[f]]=n[a[f]])}}return s}},{}],27:[function(t,e,n){"use strict";function r(t,e,n,r,o){}e.exports=r},{24:24,25:25,30:30}],28:[function(t,e,n){"use strict";var r=t(29);e.exports=function(t){return r(t,!1)}},{29:29}],29:[function(t,e,n){"use strict";var r=t(22),o=t(24),i=(t(25),t(30)),u=t(27);e.exports=function(t,e){function n(t){var e=t&&(_&&t[_]||t[P]);if("function"==typeof e)return e}function a(t,e){return t===e?0!==t||1/t==1/e:t!==t&&e!==e}function s(t){this.message=t,this.stack=""}function c(t){function n(n,r,u,a,c,l,f){if(a=a||w,l=l||u,f!==i)if(e)o(!1,"Calling PropTypes validators directly is not supported by the `prop-types` package. Use `PropTypes.checkPropTypes()` to call them. Read more at http://fb.me/use-check-prop-types");else;return null==r[u]?n?new s(null===r[u]?"The "+c+" `"+l+"` is marked as required in `"+a+"`, but its value is `null`.":"The "+c+" `"+l+"` is marked as required in `"+a+"`, but its value is `undefined`."):null:t(r,u,a,c,l)}var r=n.bind(null,!1);return r.isRequired=n.bind(null,!0),r}function l(t){function e(e,n,r,o,i,u){var a=e[n];if(g(a)!==t)return new s("Invalid "+o+" `"+i+"` of type `"+E(a)+"` supplied to `"+r+"`, expected `"+t+"`.");return null}return c(e)}function f(t){function e(e,n,r,o,u){if("function"!=typeof t)return new s("Property `"+u+"` of component `"+r+"` has invalid PropType notation inside arrayOf.");var a=e[n];if(!Array.isArray(a)){return new s("Invalid "+o+" `"+u+"` of type `"+g(a)+"` supplied to `"+r+"`, expected an array.")}for(var c=0;c<a.length;c++){var l=t(a,c,r,o,u+"["+c+"]",i);if(l instanceof Error)return l}return null}return c(e)}function p(t){function e(e,n,r,o,i){if(!(e[n]instanceof t)){var u=t.name||w;return new s("Invalid "+o+" `"+i+"` of type `"+x(e[n])+"` supplied to `"+r+"`, expected instance of `"+u+"`.")}return null}return c(e)}function d(t){function e(e,n,r,o,i){for(var u=e[n],c=0;c<t.length;c++)if(a(u,t[c]))return null;return new s("Invalid "+o+" `"+i+"` of value `"+u+"` supplied to `"+r+"`, expected one of "+JSON.stringify(t)+".")}return Array.isArray(t)?c(e):r.thatReturnsNull}function y(t){function e(e,n,r,o,u){if("function"!=typeof t)return new s("Property `"+u+"` of component `"+r+"` has invalid PropType notation inside objectOf.");var a=e[n],c=g(a);if("object"!==c)return new s("Invalid "+o+" `"+u+"` of type `"+c+"` supplied to `"+r+"`, expected an object.");for(var l in a)if(a.hasOwnProperty(l)){var f=t(a,l,r,o,u+"."+l,i);if(f instanceof Error)return f}return null}return c(e)}function h(t){function e(e,n,r,o,u){for(var a=0;a<t.length;a++){if(null==(0,t[a])(e,n,r,o,u,i))return null}return new s("Invalid "+o+" `"+u+"` supplied to `"+r+"`.")}return Array.isArray(t)?c(e):r.thatReturnsNull}function v(t){function e(e,n,r,o,u){var a=e[n],c=g(a);if("object"!==c)return new s("Invalid "+o+" `"+u+"` of type `"+c+"` supplied to `"+r+"`, expected `object`.");for(var l in t){var f=t[l];if(f){var p=f(a,l,r,o,u+"."+l,i);if(p)return p}}return null}return c(e)}function m(e){switch(typeof e){case"number":case"string":case"undefined":return!0;case"boolean":return!e;case"object":if(Array.isArray(e))return e.every(m);if(null===e||t(e))return!0;var r=n(e);if(!r)return!1;var o,i=r.call(e);if(r!==e.entries){for(;!(o=i.next()).done;)if(!m(o.value))return!1}else for(;!(o=i.next()).done;){var u=o.value;if(u&&!m(u[1]))return!1}return!0;default:return!1}}function b(t,e){return"symbol"===t||("Symbol"===e["@@toStringTag"]||"function"==typeof Symbol&&e instanceof Symbol)}function g(t){var e=typeof t;return Array.isArray(t)?"array":t instanceof RegExp?"object":b(e,t)?"symbol":e}function E(t){var e=g(t);if("object"===e){if(t instanceof Date)return"date";if(t instanceof RegExp)return"regexp"}return e}function x(t){return t.constructor&&t.constructor.name?t.constructor.name:w}var _="function"==typeof Symbol&&Symbol.iterator,P="@@iterator",w="<<anonymous>>",N={array:l("array"),bool:l("boolean"),func:l("function"),number:l("number"),object:l("object"),string:l("string"),symbol:l("symbol"),any:function(){return c(r.thatReturnsNull)}(),arrayOf:f,element:function(){function e(e,n,r,o,i){var u=e[n];if(!t(u)){return new s("Invalid "+o+" `"+i+"` of type `"+g(u)+"` supplied to `"+r+"`, expected a single ReactElement.")}return null}return c(e)}(),instanceOf:p,node:function(){function t(t,e,n,r,o){return m(t[e])?null:new s("Invalid "+r+" `"+o+"` supplied to `"+n+"`, expected a ReactNode.")}return c(t)}(),objectOf:y,oneOf:d,oneOfType:h,shape:v};return s.prototype=Error.prototype,N.checkPropTypes=u,N.PropTypes=N,N}},{22:22,24:24,25:25,27:27,30:30}],30:[function(t,e,n){"use strict";e.exports="SECRET_DO_NOT_PASS_THIS_OR_YOU_WILL_BE_FIRED"},{}]},{},[15])(15)});
+!function(f){if("object"==typeof exports&&"undefined"!=typeof module)module.exports=f();else if("function"==typeof define&&define.amd)define([],f);else{var g;if(g="undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof self?self:this,void 0===g.React)throw Error("React module should be required before createClass");g.createReactClass=f()}}(function(){return function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a="function"==typeof require&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n||e)},l,l.exports,e,t,n,r)}return n[o].exports}for(var i="function"==typeof require&&require,o=0;o<r.length;o++)s(r[o]);return s}({1:[function(require,module,exports){"use strict";function identity(fn){return fn}function factory(ReactComponent,isValidElement,ReactNoopUpdateQueue){function validateMethodOverride(isAlreadyDefined,name){var specPolicy=ReactClassInterface.hasOwnProperty(name)?ReactClassInterface[name]:null;ReactClassMixin.hasOwnProperty(name)&&_invariant("OVERRIDE_BASE"===specPolicy,"ReactClassInterface: You are attempting to override `%s` from your class specification. Ensure that your method names do not overlap with React methods.",name),isAlreadyDefined&&_invariant("DEFINE_MANY"===specPolicy||"DEFINE_MANY_MERGED"===specPolicy,"ReactClassInterface: You are attempting to define `%s` on your component more than once. This conflict may be due to a mixin.",name)}function mixSpecIntoComponent(Constructor,spec){if(spec){_invariant("function"!=typeof spec,"ReactClass: You're attempting to use a component class or function as a mixin. Instead, just use a regular object."),_invariant(!isValidElement(spec),"ReactClass: You're attempting to use a component as a mixin. Instead, just use a regular object.");var proto=Constructor.prototype,autoBindPairs=proto.__reactAutoBindPairs;spec.hasOwnProperty(MIXINS_KEY)&&RESERVED_SPEC_KEYS.mixins(Constructor,spec.mixins);for(var name in spec)if(spec.hasOwnProperty(name)&&name!==MIXINS_KEY){var property=spec[name],isAlreadyDefined=proto.hasOwnProperty(name);if(validateMethodOverride(isAlreadyDefined,name),RESERVED_SPEC_KEYS.hasOwnProperty(name))RESERVED_SPEC_KEYS[name](Constructor,property);else{var isReactClassMethod=ReactClassInterface.hasOwnProperty(name),isFunction="function"==typeof property,shouldAutoBind=isFunction&&!isReactClassMethod&&!isAlreadyDefined&&!1!==spec.autobind;if(shouldAutoBind)autoBindPairs.push(name,property),proto[name]=property;else if(isAlreadyDefined){var specPolicy=ReactClassInterface[name];_invariant(isReactClassMethod&&("DEFINE_MANY_MERGED"===specPolicy||"DEFINE_MANY"===specPolicy),"ReactClass: Unexpected spec policy %s for key %s when mixing in component specs.",specPolicy,name),"DEFINE_MANY_MERGED"===specPolicy?proto[name]=createMergedResultFunction(proto[name],property):"DEFINE_MANY"===specPolicy&&(proto[name]=createChainedFunction(proto[name],property))}else proto[name]=property}}}else;}function mixStaticSpecIntoComponent(Constructor,statics){if(statics)for(var name in statics){var property=statics[name];if(statics.hasOwnProperty(name)){var isReserved=name in RESERVED_SPEC_KEYS;_invariant(!isReserved,'ReactClass: You are attempting to define a reserved property, `%s`, that shouldn\'t be on the "statics" key. Define it as an instance property instead; it will still be accessible on the constructor.',name);var isInherited=name in Constructor;_invariant(!isInherited,"ReactClass: You are attempting to define `%s` on your component more than once. This conflict may be due to a mixin.",name),Constructor[name]=property}}}function mergeIntoWithNoDuplicateKeys(one,two){_invariant(one&&two&&"object"==typeof one&&"object"==typeof two,"mergeIntoWithNoDuplicateKeys(): Cannot merge non-objects.");for(var key in two)two.hasOwnProperty(key)&&(_invariant(void 0===one[key],"mergeIntoWithNoDuplicateKeys(): Tried to merge two objects with the same key: `%s`. This conflict may be due to a mixin; in particular, this may be caused by two getInitialState() or getDefaultProps() methods returning objects with clashing keys.",key),one[key]=two[key]);return one}function createMergedResultFunction(one,two){return function(){var a=one.apply(this,arguments),b=two.apply(this,arguments);if(null==a)return b;if(null==b)return a;var c={};return mergeIntoWithNoDuplicateKeys(c,a),mergeIntoWithNoDuplicateKeys(c,b),c}}function createChainedFunction(one,two){return function(){one.apply(this,arguments),two.apply(this,arguments)}}function bindAutoBindMethod(component,method){var boundMethod=method.bind(component);return boundMethod}function bindAutoBindMethods(component){for(var pairs=component.__reactAutoBindPairs,i=0;i<pairs.length;i+=2){var autoBindKey=pairs[i],method=pairs[i+1];component[autoBindKey]=bindAutoBindMethod(component,method)}}function createClass(spec){var Constructor=identity(function(props,context,updater){this.__reactAutoBindPairs.length&&bindAutoBindMethods(this),this.props=props,this.context=context,this.refs=emptyObject,this.updater=updater||ReactNoopUpdateQueue,this.state=null;var initialState=this.getInitialState?this.getInitialState():null;_invariant("object"==typeof initialState&&!Array.isArray(initialState),"%s.getInitialState(): must return an object or null",Constructor.displayName||"ReactCompositeComponent"),this.state=initialState});Constructor.prototype=new ReactClassComponent,Constructor.prototype.constructor=Constructor,Constructor.prototype.__reactAutoBindPairs=[],injectedMixins.forEach(mixSpecIntoComponent.bind(null,Constructor)),mixSpecIntoComponent(Constructor,IsMountedMixin),mixSpecIntoComponent(Constructor,spec),Constructor.getDefaultProps&&(Constructor.defaultProps=Constructor.getDefaultProps()),_invariant(Constructor.prototype.render,"createClass(...): Class specification must implement a `render` method.");for(var methodName in ReactClassInterface)Constructor.prototype[methodName]||(Constructor.prototype[methodName]=null);return Constructor}var injectedMixins=[],ReactClassInterface={mixins:"DEFINE_MANY",statics:"DEFINE_MANY",propTypes:"DEFINE_MANY",contextTypes:"DEFINE_MANY",childContextTypes:"DEFINE_MANY",getDefaultProps:"DEFINE_MANY_MERGED",getInitialState:"DEFINE_MANY_MERGED",getChildContext:"DEFINE_MANY_MERGED",render:"DEFINE_ONCE",componentWillMount:"DEFINE_MANY",componentDidMount:"DEFINE_MANY",componentWillReceiveProps:"DEFINE_MANY",shouldComponentUpdate:"DEFINE_ONCE",componentWillUpdate:"DEFINE_MANY",componentDidUpdate:"DEFINE_MANY",componentWillUnmount:"DEFINE_MANY",updateComponent:"OVERRIDE_BASE"},RESERVED_SPEC_KEYS={displayName:function(Constructor,displayName){Constructor.displayName=displayName},mixins:function(Constructor,mixins){if(mixins)for(var i=0;i<mixins.length;i++)mixSpecIntoComponent(Constructor,mixins[i])},childContextTypes:function(Constructor,childContextTypes){Constructor.childContextTypes=_assign({},Constructor.childContextTypes,childContextTypes)},contextTypes:function(Constructor,contextTypes){Constructor.contextTypes=_assign({},Constructor.contextTypes,contextTypes)},getDefaultProps:function(Constructor,getDefaultProps){Constructor.getDefaultProps?Constructor.getDefaultProps=createMergedResultFunction(Constructor.getDefaultProps,getDefaultProps):Constructor.getDefaultProps=getDefaultProps},propTypes:function(Constructor,propTypes){Constructor.propTypes=_assign({},Constructor.propTypes,propTypes)},statics:function(Constructor,statics){mixStaticSpecIntoComponent(Constructor,statics)},autobind:function(){}},IsMountedMixin={componentDidMount:function(){this.__isMounted=!0},componentWillUnmount:function(){this.__isMounted=!1}},ReactClassMixin={replaceState:function(newState,callback){this.updater.enqueueReplaceState(this,newState,callback)},isMounted:function(){return!!this.__isMounted}},ReactClassComponent=function(){};return _assign(ReactClassComponent.prototype,ReactComponent.prototype,ReactClassMixin),createClass}var _assign=require(7),emptyObject=require(4),_invariant=require(5),MIXINS_KEY="mixins";module.exports=factory},{4:4,5:5,6:6,7:7}],2:[function(require,module,exports){"use strict";var factory=require(1),ReactNoopUpdateQueue=(new React.Component).updater;module.exports=factory(React.Component,React.isValidElement,ReactNoopUpdateQueue)},{1:1}],3:[function(require,module,exports){"use strict";function makeEmptyFunction(arg){return function(){return arg}}var emptyFunction=function(){};emptyFunction.thatReturns=makeEmptyFunction,emptyFunction.thatReturnsFalse=makeEmptyFunction(!1),emptyFunction.thatReturnsTrue=makeEmptyFunction(!0),emptyFunction.thatReturnsNull=makeEmptyFunction(null),emptyFunction.thatReturnsThis=function(){return this},emptyFunction.thatReturnsArgument=function(arg){return arg},module.exports=emptyFunction},{}],4:[function(require,module,exports){"use strict";var emptyObject={};module.exports=emptyObject},{}],5:[function(require,module,exports){"use strict";function invariant(condition,format,a,b,c,d,e,f){if(validateFormat(format),!condition){var error;if(void 0===format)error=new Error("Minified exception occurred; use the non-minified dev environment for the full error message and additional helpful warnings.");else{var args=[a,b,c,d,e,f],argIndex=0;error=new Error(format.replace(/%s/g,function(){return args[argIndex++]})),error.name="Invariant Violation"}throw error.framesToPop=1,error}}var validateFormat=function(format){};module.exports=invariant},{}],6:[function(require,module,exports){"use strict";var emptyFunction=require(3),warning=emptyFunction;module.exports=warning},{3:3}],7:[function(require,module,exports){"use strict";function toObject(val){if(null===val||void 0===val)throw new TypeError("Object.assign cannot be called with null or undefined");return Object(val)}var getOwnPropertySymbols=Object.getOwnPropertySymbols,hasOwnProperty=Object.prototype.hasOwnProperty,propIsEnumerable=Object.prototype.propertyIsEnumerable;module.exports=function(){try{if(!Object.assign)return!1;var test1=new String("abc");if(test1[5]="de","5"===Object.getOwnPropertyNames(test1)[0])return!1;for(var test2={},i=0;i<10;i++)test2["_"+String.fromCharCode(i)]=i;if("0123456789"!==Object.getOwnPropertyNames(test2).map(function(n){return test2[n]}).join(""))return!1;var test3={};return"abcdefghijklmnopqrst".split("").forEach(function(letter){test3[letter]=letter}),"abcdefghijklmnopqrst"===Object.keys(Object.assign({},test3)).join("")}catch(err){return!1}}()?Object.assign:function(target,source){for(var from,symbols,to=toObject(target),s=1;s<arguments.length;s++){from=Object(arguments[s]);for(var key in from)hasOwnProperty.call(from,key)&&(to[key]=from[key]);if(getOwnPropertySymbols){symbols=getOwnPropertySymbols(from);for(var i=0;i<symbols.length;i++)propIsEnumerable.call(from,symbols[i])&&(to[symbols[i]]=from[symbols[i]])}}return to}},{}]},{},[2])(2)});
+
+/**
+ * ReactDOM v15.5.4
+ *
+ * Copyright 2013-present, Facebook, Inc.
+ * All rights reserved.
+ *
+ * This source code is licensed under the BSD-style license found in the
+ * LICENSE file in the root directory of this source tree. An additional grant
+ * of patent rights can be found in the PATENTS file in the same directory.
+ *
+ */
+!function(e){if("object"==typeof exports&&"undefined"!=typeof module)module.exports=e(require("react"));else if("function"==typeof define&&define.amd)define(["react"],e);else{var t;t="undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof self?self:this,t.ReactDOM=e(t.React)}}(function(e){return function(t){return function(){return function e(t,n,r){function o(a,s){if(!n[a]){if(!t[a]){var u="function"==typeof require&&require;if(!s&&u)return u(a,!0);if(i)return i(a,!0);var l=new Error("Cannot find module '"+a+"'");throw l.code="MODULE_NOT_FOUND",l}var c=n[a]={exports:{}};t[a][0].call(c.exports,function(e){var n=t[a][1][e];return o(n||e)},c,c.exports,e,t,n,r)}return n[a].exports}for(var i="function"==typeof require&&require,a=0;a<r.length;a++)o(r[a]);return o}({1:[function(e,t,n){"use strict";var r={Properties:{"aria-current":0,"aria-details":0,"aria-disabled":0,"aria-hidden":0,"aria-invalid":0,"aria-keyshortcuts":0,"aria-label":0,"aria-roledescription":0,"aria-autocomplete":0,"aria-checked":0,"aria-expanded":0,"aria-haspopup":0,"aria-level":0,"aria-modal":0,"aria-multiline":0,"aria-multiselectable":0,"aria-orientation":0,"aria-placeholder":0,"aria-pressed":0,"aria-readonly":0,"aria-required":0,"aria-selected":0,"aria-sort":0,"aria-valuemax":0,"aria-valuemin":0,"aria-valuenow":0,"aria-valuetext":0,"aria-atomic":0,"aria-busy":0,"aria-live":0,"aria-relevant":0,"aria-dropeffect":0,"aria-grabbed":0,"aria-activedescendant":0,"aria-colcount":0,"aria-colindex":0,"aria-colspan":0,"aria-controls":0,"aria-describedby":0,"aria-errormessage":0,"aria-flowto":0,"aria-labelledby":0,"aria-owns":0,"aria-posinset":0,"aria-rowcount":0,"aria-rowindex":0,"aria-rowspan":0,"aria-setsize":0},DOMAttributeNames:{},DOMPropertyNames:{}};t.exports=r},{}],2:[function(e,t,n){"use strict";var r=e(33),o=e(131),i={focusDOMComponent:function(){o(r.getNodeFromInstance(this))}};t.exports=i},{131:131,33:33}],3:[function(e,t,n){"use strict";function r(e){return(e.ctrlKey||e.altKey||e.metaKey)&&!(e.ctrlKey&&e.altKey)}function o(e){switch(e){case"topCompositionStart":return T.compositionStart;case"topCompositionEnd":return T.compositionEnd;case"topCompositionUpdate":return T.compositionUpdate}}function i(e,t){return"topKeyDown"===e&&t.keyCode===y}function a(e,t){switch(e){case"topKeyUp":return-1!==g.indexOf(t.keyCode);case"topKeyDown":return t.keyCode!==y;case"topKeyPress":case"topMouseDown":case"topBlur":return!0;default:return!1}}function s(e){var t=e.detail;return"object"==typeof t&&"data"in t?t.data:null}function u(e,t,n,r){var u,l;if(_?u=o(e):P?a(e,n)&&(u=T.compositionEnd):i(e,n)&&(u=T.compositionStart),!u)return null;E&&(P||u!==T.compositionStart?u===T.compositionEnd&&P&&(l=P.getData()):P=h.getPooled(r));var c=m.getPooled(u,t,n,r);if(l)c.data=l;else{var p=s(n);null!==p&&(c.data=p)}return d.accumulateTwoPhaseDispatches(c),c}function l(e,t){switch(e){case"topCompositionEnd":return s(t);case"topKeyPress":return t.which!==x?null:(k=!0,w);case"topTextInput":var n=t.data;return n===w&&k?null:n;default:return null}}function c(e,t){if(P){if("topCompositionEnd"===e||!_&&a(e,t)){var n=P.getData();return h.release(P),P=null,n}return null}switch(e){case"topPaste":return null;case"topKeyPress":return t.which&&!r(t)?String.fromCharCode(t.which):null;case"topCompositionEnd":return E?null:t.data;default:return null}}function p(e,t,n,r){var o;if(!(o=b?l(e,n):c(e,n)))return null;var i=v.getPooled(T.beforeInput,t,n,r);return i.data=o,d.accumulateTwoPhaseDispatches(i),i}var d=e(19),f=e(123),h=e(20),m=e(78),v=e(82),g=[9,13,27,32],y=229,_=f.canUseDOM&&"CompositionEvent"in window,C=null;f.canUseDOM&&"documentMode"in document&&(C=document.documentMode);var b=f.canUseDOM&&"TextEvent"in window&&!C&&!function(){var e=window.opera;return"object"==typeof e&&"function"==typeof e.version&&parseInt(e.version(),10)<=12}(),E=f.canUseDOM&&(!_||C&&C>8&&C<=11),x=32,w=String.fromCharCode(x),T={beforeInput:{phasedRegistrationNames:{bubbled:"onBeforeInput",captured:"onBeforeInputCapture"},dependencies:["topCompositionEnd","topKeyPress","topTextInput","topPaste"]},compositionEnd:{phasedRegistrationNames:{bubbled:"onCompositionEnd",captured:"onCompositionEndCapture"},dependencies:["topBlur","topCompositionEnd","topKeyDown","topKeyPress","topKeyUp","topMouseDown"]},compositionStart:{phasedRegistrationNames:{bubbled:"onCompositionStart",captured:"onCompositionStartCapture"},dependencies:["topBlur","topCompositionStart","topKeyDown","topKeyPress","topKeyUp","topMouseDown"]},compositionUpdate:{phasedRegistrationNames:{bubbled:"onCompositionUpdate",captured:"onCompositionUpdateCapture"},dependencies:["topBlur","topCompositionUpdate","topKeyDown","topKeyPress","topKeyUp","topMouseDown"]}},k=!1,P=null,S={eventTypes:T,extractEvents:function(e,t,n,r){return[u(e,t,n,r),p(e,t,n,r)]}};t.exports=S},{123:123,19:19,20:20,78:78,82:82}],4:[function(e,t,n){"use strict";function r(e,t){return e+t.charAt(0).toUpperCase()+t.substring(1)}var o={animationIterationCount:!0,borderImageOutset:!0,borderImageSlice:!0,borderImageWidth:!0,boxFlex:!0,boxFlexGroup:!0,boxOrdinalGroup:!0,columnCount:!0,flex:!0,flexGrow:!0,flexPositive:!0,flexShrink:!0,flexNegative:!0,flexOrder:!0,gridRow:!0,gridColumn:!0,fontWeight:!0,lineClamp:!0,lineHeight:!0,opacity:!0,order:!0,orphans:!0,tabSize:!0,widows:!0,zIndex:!0,zoom:!0,fillOpacity:!0,floodOpacity:!0,stopOpacity:!0,strokeDasharray:!0,strokeDashoffset:!0,strokeMiterlimit:!0,strokeOpacity:!0,strokeWidth:!0},i=["Webkit","ms","Moz","O"];Object.keys(o).forEach(function(e){i.forEach(function(t){o[r(t,e)]=o[e]})});var a={background:{backgroundAttachment:!0,backgroundColor:!0,backgroundImage:!0,backgroundPositionX:!0,backgroundPositionY:!0,backgroundRepeat:!0},backgroundPosition:{backgroundPositionX:!0,backgroundPositionY:!0},border:{borderWidth:!0,borderStyle:!0,borderColor:!0},borderBottom:{borderBottomWidth:!0,borderBottomStyle:!0,borderBottomColor:!0},borderLeft:{borderLeftWidth:!0,borderLeftStyle:!0,borderLeftColor:!0},borderRight:{borderRightWidth:!0,borderRightStyle:!0,borderRightColor:!0},borderTop:{borderTopWidth:!0,borderTopStyle:!0,borderTopColor:!0},font:{fontStyle:!0,fontVariant:!0,fontWeight:!0,fontSize:!0,lineHeight:!0,fontFamily:!0},outline:{outlineWidth:!0,outlineStyle:!0,outlineColor:!0}},s={isUnitlessNumber:o,shorthandPropertyExpansions:a};t.exports=s},{}],5:[function(e,t,n){"use strict";var r=e(4),o=e(123),i=(e(58),e(125),e(94)),a=e(136),s=e(140),u=(e(142),s(function(e){return a(e)})),l=!1,c="cssFloat";if(o.canUseDOM){var p=document.createElement("div").style;try{p.font=""}catch(e){l=!0}void 0===document.documentElement.style.cssFloat&&(c="styleFloat")}var d={createMarkupForStyles:function(e,t){var n="";for(var r in e)if(e.hasOwnProperty(r)){var o=e[r];null!=o&&(n+=u(r)+":",n+=i(r,o,t)+";")}return n||null},setValueForStyles:function(e,t,n){var o=e.style;for(var a in t)if(t.hasOwnProperty(a)){var s=i(a,t[a],n);if("float"!==a&&"cssFloat"!==a||(a=c),s)o[a]=s;else{var u=l&&r.shorthandPropertyExpansions[a];if(u)for(var p in u)o[p]="";else o[a]=""}}}};t.exports=d},{123:123,125:125,136:136,140:140,142:142,4:4,58:58,94:94}],6:[function(e,t,n){"use strict";function r(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")}var o=e(112),i=e(24),a=(e(137),function(){function e(t){r(this,e),this._callbacks=null,this._contexts=null,this._arg=t}return e.prototype.enqueue=function(e,t){this._callbacks=this._callbacks||[],this._callbacks.push(e),this._contexts=this._contexts||[],this._contexts.push(t)},e.prototype.notifyAll=function(){var e=this._callbacks,t=this._contexts,n=this._arg;if(e&&t){e.length!==t.length&&o("24"),this._callbacks=null,this._contexts=null;for(var r=0;r<e.length;r++)e[r].call(t[r],n);e.length=0,t.length=0}},e.prototype.checkpoint=function(){return this._callbacks?this._callbacks.length:0},e.prototype.rollback=function(e){this._callbacks&&this._contexts&&(this._callbacks.length=e,this._contexts.length=e)},e.prototype.reset=function(){this._callbacks=null,this._contexts=null},e.prototype.destructor=function(){this.reset()},e}());t.exports=i.addPoolingTo(a)},{112:112,137:137,24:24}],7:[function(e,t,n){"use strict";function r(e){var t=e.nodeName&&e.nodeName.toLowerCase();return"select"===t||"input"===t&&"file"===e.type}function o(e){var t=w.getPooled(S.change,M,e,T(e));C.accumulateTwoPhaseDispatches(t),x.batchedUpdates(i,t)}function i(e){_.enqueueEvents(e),_.processEventQueue(!1)}function a(e,t){N=e,M=t,N.attachEvent("onchange",o)}function s(){N&&(N.detachEvent("onchange",o),N=null,M=null)}function u(e,t){if("topChange"===e)return t}function l(e,t,n){"topFocus"===e?(s(),a(t,n)):"topBlur"===e&&s()}function c(e,t){N=e,M=t,I=e.value,O=Object.getOwnPropertyDescriptor(e.constructor.prototype,"value"),Object.defineProperty(N,"value",D),N.attachEvent?N.attachEvent("onpropertychange",d):N.addEventListener("propertychange",d,!1)}function p(){N&&(delete N.value,N.detachEvent?N.detachEvent("onpropertychange",d):N.removeEventListener("propertychange",d,!1),N=null,M=null,I=null,O=null)}function d(e){if("value"===e.propertyName){var t=e.srcElement.value;t!==I&&(I=t,o(e))}}function f(e,t){if("topInput"===e)return t}function h(e,t,n){"topFocus"===e?(p(),c(t,n)):"topBlur"===e&&p()}function m(e,t){if(("topSelectionChange"===e||"topKeyUp"===e||"topKeyDown"===e)&&N&&N.value!==I)return I=N.value,M}function v(e){return e.nodeName&&"input"===e.nodeName.toLowerCase()&&("checkbox"===e.type||"radio"===e.type)}function g(e,t){if("topClick"===e)return t}function y(e,t){if(null!=e){var n=e._wrapperState||t._wrapperState;if(n&&n.controlled&&"number"===t.type){var r=""+t.value;t.getAttribute("value")!==r&&t.setAttribute("value",r)}}}var _=e(16),C=e(19),b=e(123),E=e(33),x=e(71),w=e(80),T=e(102),k=e(109),P=e(110),S={change:{phasedRegistrationNames:{bubbled:"onChange",captured:"onChangeCapture"},dependencies:["topBlur","topChange","topClick","topFocus","topInput","topKeyDown","topKeyUp","topSelectionChange"]}},N=null,M=null,I=null,O=null,R=!1;b.canUseDOM&&(R=k("change")&&(!document.documentMode||document.documentMode>8));var A=!1;b.canUseDOM&&(A=k("input")&&(!document.documentMode||document.documentMode>11));var D={get:function(){return O.get.call(this)},set:function(e){I=""+e,O.set.call(this,e)}},L={eventTypes:S,extractEvents:function(e,t,n,o){var i,a,s=t?E.getNodeFromInstance(t):window;if(r(s)?R?i=u:a=l:P(s)?A?i=f:(i=m,a=h):v(s)&&(i=g),i){var c=i(e,t);if(c){var p=w.getPooled(S.change,c,n,o);return p.type="change",C.accumulateTwoPhaseDispatches(p),p}}a&&a(e,s,t),"topBlur"===e&&y(t,s)}};t.exports=L},{102:102,109:109,110:110,123:123,16:16,19:19,33:33,71:71,80:80}],8:[function(e,t,n){"use strict";function r(e,t){return Array.isArray(t)&&(t=t[1]),t?t.nextSibling:e.firstChild}function o(e,t,n){c.insertTreeBefore(e,t,n)}function i(e,t,n){Array.isArray(t)?s(e,t[0],t[1],n):m(e,t,n)}function a(e,t){if(Array.isArray(t)){var n=t[1];t=t[0],u(e,t,n),e.removeChild(n)}e.removeChild(t)}function s(e,t,n,r){for(var o=t;;){var i=o.nextSibling;if(m(e,o,r),o===n)break;o=i}}function u(e,t,n){for(;;){var r=t.nextSibling;if(r===n)break;e.removeChild(r)}}function l(e,t,n){var r=e.parentNode,o=e.nextSibling;o===t?n&&m(r,document.createTextNode(n),o):n?(h(o,n),u(r,o,t)):u(r,e,t)}var c=e(9),p=e(13),d=(e(33),e(58),e(93)),f=e(114),h=e(115),m=d(function(e,t,n){e.insertBefore(t,n)}),v=p.dangerouslyReplaceNodeWithMarkup,g={dangerouslyReplaceNodeWithMarkup:v,replaceDelimitedText:l,processUpdates:function(e,t){for(var n=0;n<t.length;n++){var s=t[n];switch(s.type){case"INSERT_MARKUP":o(e,s.content,r(e,s.afterNode));break;case"MOVE_EXISTING":i(e,s.fromNode,r(e,s.afterNode));break;case"SET_MARKUP":f(e,s.content);break;case"TEXT_CONTENT":h(e,s.content);break;case"REMOVE_NODE":a(e,s.fromNode)}}}};t.exports=g},{114:114,115:115,13:13,33:33,58:58,9:9,93:93}],9:[function(e,t,n){"use strict";function r(e){if(h){var t=e.node,n=e.children;if(n.length)for(var r=0;r<n.length;r++)m(t,n[r],null);else null!=e.html?p(t,e.html):null!=e.text&&f(t,e.text)}}function o(e,t){e.parentNode.replaceChild(t.node,e),r(t)}function i(e,t){h?e.children.push(t):e.node.appendChild(t.node)}function a(e,t){h?e.html=t:p(e.node,t)}function s(e,t){h?e.text=t:f(e.node,t)}function u(){return this.node.nodeName}function l(e){return{node:e,children:[],html:null,text:null,toString:u}}var c=e(10),p=e(114),d=e(93),f=e(115),h="undefined"!=typeof document&&"number"==typeof document.documentMode||"undefined"!=typeof navigator&&"string"==typeof navigator.userAgent&&/\bEdge\/\d/.test(navigator.userAgent),m=d(function(e,t,n){11===t.node.nodeType||1===t.node.nodeType&&"object"===t.node.nodeName.toLowerCase()&&(null==t.node.namespaceURI||t.node.namespaceURI===c.html)?(r(t),e.insertBefore(t.node,n)):(e.insertBefore(t.node,n),r(t))});l.insertTreeBefore=m,l.replaceChildWithTree=o,l.queueChild=i,l.queueHTML=a,l.queueText=s,t.exports=l},{10:10,114:114,115:115,93:93}],10:[function(e,t,n){"use strict";var r={html:"http://www.w3.org/1999/xhtml",mathml:"http://www.w3.org/1998/Math/MathML",svg:"http://www.w3.org/2000/svg"};t.exports=r},{}],11:[function(e,t,n){"use strict";function r(e,t){return(e&t)===t}var o=e(112),i=(e(137),{MUST_USE_PROPERTY:1,HAS_BOOLEAN_VALUE:4,HAS_NUMERIC_VALUE:8,HAS_POSITIVE_NUMERIC_VALUE:24,HAS_OVERLOADED_BOOLEAN_VALUE:32,injectDOMPropertyConfig:function(e){var t=i,n=e.Properties||{},a=e.DOMAttributeNamespaces||{},u=e.DOMAttributeNames||{},l=e.DOMPropertyNames||{},c=e.DOMMutationMethods||{};e.isCustomAttribute&&s._isCustomAttributeFunctions.push(e.isCustomAttribute);for(var p in n){s.properties.hasOwnProperty(p)&&o("48",p);var d=p.toLowerCase(),f=n[p],h={attributeName:d,attributeNamespace:null,propertyName:p,mutationMethod:null,mustUseProperty:r(f,t.MUST_USE_PROPERTY),hasBooleanValue:r(f,t.HAS_BOOLEAN_VALUE),hasNumericValue:r(f,t.HAS_NUMERIC_VALUE),hasPositiveNumericValue:r(f,t.HAS_POSITIVE_NUMERIC_VALUE),hasOverloadedBooleanValue:r(f,t.HAS_OVERLOADED_BOOLEAN_VALUE)};if(h.hasBooleanValue+h.hasNumericValue+h.hasOverloadedBooleanValue<=1||o("50",p),u.hasOwnProperty(p)){var m=u[p];h.attributeName=m}a.hasOwnProperty(p)&&(h.attributeNamespace=a[p]),l.hasOwnProperty(p)&&(h.propertyName=l[p]),c.hasOwnProperty(p)&&(h.mutationMethod=c[p]),s.properties[p]=h}}}),a=":A-Z_a-z\\u00C0-\\u00D6\\u00D8-\\u00F6\\u00F8-\\u02FF\\u0370-\\u037D\\u037F-\\u1FFF\\u200C-\\u200D\\u2070-\\u218F\\u2C00-\\u2FEF\\u3001-\\uD7FF\\uF900-\\uFDCF\\uFDF0-\\uFFFD",s={ID_ATTRIBUTE_NAME:"data-reactid",ROOT_ATTRIBUTE_NAME:"data-reactroot",ATTRIBUTE_NAME_START_CHAR:a,ATTRIBUTE_NAME_CHAR:a+"\\-.0-9\\u00B7\\u0300-\\u036F\\u203F-\\u2040",properties:{},getPossibleStandardName:null,_isCustomAttributeFunctions:[],isCustomAttribute:function(e){for(var t=0;t<s._isCustomAttributeFunctions.length;t++)if((0,s._isCustomAttributeFunctions[t])(e))return!0;return!1},injection:i};t.exports=s},{112:112,137:137}],12:[function(e,t,n){"use strict";function r(e){return!!l.hasOwnProperty(e)||!u.hasOwnProperty(e)&&(s.test(e)?(l[e]=!0,!0):(u[e]=!0,!1))}function o(e,t){return null==t||e.hasBooleanValue&&!t||e.hasNumericValue&&isNaN(t)||e.hasPositiveNumericValue&&t<1||e.hasOverloadedBooleanValue&&!1===t}var i=e(11),a=(e(33),e(58),e(111)),s=(e(142),new RegExp("^["+i.ATTRIBUTE_NAME_START_CHAR+"]["+i.ATTRIBUTE_NAME_CHAR+"]*$")),u={},l={},c={createMarkupForID:function(e){return i.ID_ATTRIBUTE_NAME+"="+a(e)},setAttributeForID:function(e,t){e.setAttribute(i.ID_ATTRIBUTE_NAME,t)},createMarkupForRoot:function(){return i.ROOT_ATTRIBUTE_NAME+'=""'},setAttributeForRoot:function(e){e.setAttribute(i.ROOT_ATTRIBUTE_NAME,"")},createMarkupForProperty:function(e,t){var n=i.properties.hasOwnProperty(e)?i.properties[e]:null;if(n){if(o(n,t))return"";var r=n.attributeName;return n.hasBooleanValue||n.hasOverloadedBooleanValue&&!0===t?r+'=""':r+"="+a(t)}return i.isCustomAttribute(e)?null==t?"":e+"="+a(t):null},createMarkupForCustomAttribute:function(e,t){return r(e)&&null!=t?e+"="+a(t):""},setValueForProperty:function(e,t,n){var r=i.properties.hasOwnProperty(t)?i.properties[t]:null;if(r){var a=r.mutationMethod;if(a)a(e,n);else{if(o(r,n))return void this.deleteValueForProperty(e,t);if(r.mustUseProperty)e[r.propertyName]=n;else{var s=r.attributeName,u=r.attributeNamespace;u?e.setAttributeNS(u,s,""+n):r.hasBooleanValue||r.hasOverloadedBooleanValue&&!0===n?e.setAttribute(s,""):e.setAttribute(s,""+n)}}}else if(i.isCustomAttribute(t))return void c.setValueForAttribute(e,t,n)},setValueForAttribute:function(e,t,n){r(t)&&(null==n?e.removeAttribute(t):e.setAttribute(t,""+n))},deleteValueForAttribute:function(e,t){e.removeAttribute(t)},deleteValueForProperty:function(e,t){var n=i.properties.hasOwnProperty(t)?i.properties[t]:null;if(n){var r=n.mutationMethod;if(r)r(e,void 0);else if(n.mustUseProperty){var o=n.propertyName;n.hasBooleanValue?e[o]=!1:e[o]=""}else e.removeAttribute(n.attributeName)}else i.isCustomAttribute(t)&&e.removeAttribute(t)}};t.exports=c},{11:11,111:111,142:142,33:33,58:58}],13:[function(e,t,n){"use strict";var r=e(112),o=e(9),i=e(123),a=e(128),s=e(129),u=(e(137),{dangerouslyReplaceNodeWithMarkup:function(e,t){if(i.canUseDOM||r("56"),t||r("57"),"HTML"===e.nodeName&&r("58"),"string"==typeof t){var n=a(t,s)[0];e.parentNode.replaceChild(n,e)}else o.replaceChildWithTree(e,t)}});t.exports=u},{112:112,123:123,128:128,129:129,137:137,9:9}],14:[function(e,t,n){"use strict";var r=["ResponderEventPlugin","SimpleEventPlugin","TapEventPlugin","EnterLeaveEventPlugin","ChangeEventPlugin","SelectEventPlugin","BeforeInputEventPlugin"];t.exports=r},{}],15:[function(e,t,n){"use strict";var r=e(19),o=e(33),i=e(84),a={mouseEnter:{registrationName:"onMouseEnter",dependencies:["topMouseOut","topMouseOver"]},mouseLeave:{registrationName:"onMouseLeave",dependencies:["topMouseOut","topMouseOver"]}},s={eventTypes:a,extractEvents:function(e,t,n,s){if("topMouseOver"===e&&(n.relatedTarget||n.fromElement))return null;if("topMouseOut"!==e&&"topMouseOver"!==e)return null;var u;if(s.window===s)u=s;else{var l=s.ownerDocument;u=l?l.defaultView||l.parentWindow:window}var c,p;if("topMouseOut"===e){c=t;var d=n.relatedTarget||n.toElement;p=d?o.getClosestInstanceFromNode(d):null}else c=null,p=t;if(c===p)return null;var f=null==c?u:o.getNodeFromInstance(c),h=null==p?u:o.getNodeFromInstance(p),m=i.getPooled(a.mouseLeave,c,n,s);m.type="mouseleave",m.target=f,m.relatedTarget=h;var v=i.getPooled(a.mouseEnter,p,n,s);return v.type="mouseenter",v.target=h,v.relatedTarget=f,r.accumulateEnterLeaveDispatches(m,v,c,p),[m,v]}};t.exports=s},{19:19,33:33,84:84}],16:[function(e,t,n){"use strict";function r(e){return"button"===e||"input"===e||"select"===e||"textarea"===e}function o(e,t,n){switch(e){case"onClick":case"onClickCapture":case"onDoubleClick":case"onDoubleClickCapture":case"onMouseDown":case"onMouseDownCapture":case"onMouseMove":case"onMouseMoveCapture":case"onMouseUp":case"onMouseUpCapture":return!(!n.disabled||!r(t));default:return!1}}var i=e(112),a=e(17),s=e(18),u=e(50),l=e(91),c=e(98),p=(e(137),{}),d=null,f=function(e,t){e&&(s.executeDispatchesInOrder(e,t),e.isPersistent()||e.constructor.release(e))},h=function(e){return f(e,!0)},m=function(e){return f(e,!1)},v=function(e){return"."+e._rootNodeID},g={injection:{injectEventPluginOrder:a.injectEventPluginOrder,injectEventPluginsByName:a.injectEventPluginsByName},putListener:function(e,t,n){"function"!=typeof n&&i("94",t,typeof n);var r=v(e);(p[t]||(p[t]={}))[r]=n;var o=a.registrationNameModules[t];o&&o.didPutListener&&o.didPutListener(e,t,n)},getListener:function(e,t){var n=p[t];if(o(t,e._currentElement.type,e._currentElement.props))return null;var r=v(e);return n&&n[r]},deleteListener:function(e,t){var n=a.registrationNameModules[t];n&&n.willDeleteListener&&n.willDeleteListener(e,t);var r=p[t];r&&delete r[v(e)]},deleteAllListeners:function(e){var t=v(e);for(var n in p)if(p.hasOwnProperty(n)&&p[n][t]){var r=a.registrationNameModules[n];r&&r.willDeleteListener&&r.willDeleteListener(e,n),delete p[n][t]}},extractEvents:function(e,t,n,r){for(var o,i=a.plugins,s=0;s<i.length;s++){var u=i[s];if(u){var c=u.extractEvents(e,t,n,r);c&&(o=l(o,c))}}return o},enqueueEvents:function(e){e&&(d=l(d,e))},processEventQueue:function(e){var t=d;d=null,e?c(t,h):c(t,m),d&&i("95"),u.rethrowCaughtError()},__purge:function(){p={}},__getListenerBank:function(){return p}};t.exports=g},{112:112,137:137,17:17,18:18,50:50,91:91,98:98}],17:[function(e,t,n){"use strict";function r(){if(s)for(var e in u){var t=u[e],n=s.indexOf(e);if(n>-1||a("96",e),!l.plugins[n]){t.extractEvents||a("97",e),l.plugins[n]=t;var r=t.eventTypes;for(var i in r)o(r[i],t,i)||a("98",i,e)}}}function o(e,t,n){l.eventNameDispatchConfigs.hasOwnProperty(n)&&a("99",n),l.eventNameDispatchConfigs[n]=e;var r=e.phasedRegistrationNames;if(r){for(var o in r)if(r.hasOwnProperty(o)){var s=r[o];i(s,t,n)}return!0}return!!e.registrationName&&(i(e.registrationName,t,n),!0)}function i(e,t,n){l.registrationNameModules[e]&&a("100",e),l.registrationNameModules[e]=t,l.registrationNameDependencies[e]=t.eventTypes[n].dependencies}var a=e(112),s=(e(137),null),u={},l={plugins:[],eventNameDispatchConfigs:{},registrationNameModules:{},registrationNameDependencies:{},possibleRegistrationNames:null,injectEventPluginOrder:function(e){s&&a("101"),s=Array.prototype.slice.call(e),r()},injectEventPluginsByName:function(e){var t=!1;for(var n in e)if(e.hasOwnProperty(n)){var o=e[n];u.hasOwnProperty(n)&&u[n]===o||(u[n]&&a("102",n),u[n]=o,t=!0)}t&&r()},getPluginModuleForEvent:function(e){var t=e.dispatchConfig;if(t.registrationName)return l.registrationNameModules[t.registrationName]||null;if(void 0!==t.phasedRegistrationNames){var n=t.phasedRegistrationNames;for(var r in n)if(n.hasOwnProperty(r)){var o=l.registrationNameModules[n[r]];if(o)return o}}return null},_resetEventPlugins:function(){s=null;for(var e in u)u.hasOwnProperty(e)&&delete u[e];l.plugins.length=0;var t=l.eventNameDispatchConfigs;for(var n in t)t.hasOwnProperty(n)&&delete t[n];var r=l.registrationNameModules;for(var o in r)r.hasOwnProperty(o)&&delete r[o]}};t.exports=l},{112:112,137:137}],18:[function(e,t,n){"use strict";function r(e){return"topMouseUp"===e||"topTouchEnd"===e||"topTouchCancel"===e}function o(e){return"topMouseMove"===e||"topTouchMove"===e}function i(e){return"topMouseDown"===e||"topTouchStart"===e}function a(e,t,n,r){var o=e.type||"unknown-event";e.currentTarget=g.getNodeFromInstance(r),t?m.invokeGuardedCallbackWithCatch(o,n,e):m.invokeGuardedCallback(o,n,e),e.currentTarget=null}function s(e,t){var n=e._dispatchListeners,r=e._dispatchInstances;if(Array.isArray(n))for(var o=0;o<n.length&&!e.isPropagationStopped();o++)a(e,t,n[o],r[o]);else n&&a(e,t,n,r);e._dispatchListeners=null,e._dispatchInstances=null}function u(e){var t=e._dispatchListeners,n=e._dispatchInstances;if(Array.isArray(t)){for(var r=0;r<t.length&&!e.isPropagationStopped();r++)if(t[r](e,n[r]))return n[r]}else if(t&&t(e,n))return n;return null}function l(e){var t=u(e);return e._dispatchInstances=null,e._dispatchListeners=null,t}function c(e){var t=e._dispatchListeners,n=e._dispatchInstances;Array.isArray(t)&&h("103"),e.currentTarget=t?g.getNodeFromInstance(n):null;var r=t?t(e):null;return e.currentTarget=null,e._dispatchListeners=null,e._dispatchInstances=null,r}function p(e){return!!e._dispatchListeners}var d,f,h=e(112),m=e(50),v=(e(137),e(142),{injectComponentTree:function(e){d=e},injectTreeTraversal:function(e){f=e}}),g={isEndish:r,isMoveish:o,isStartish:i,executeDirectDispatch:c,executeDispatchesInOrder:s,executeDispatchesInOrderStopAtTrue:l,hasDispatches:p,getInstanceFromNode:function(e){return d.getInstanceFromNode(e)},getNodeFromInstance:function(e){return d.getNodeFromInstance(e)},isAncestor:function(e,t){return f.isAncestor(e,t)},getLowestCommonAncestor:function(e,t){return f.getLowestCommonAncestor(e,t)},getParentInstance:function(e){return f.getParentInstance(e)},traverseTwoPhase:function(e,t,n){return f.traverseTwoPhase(e,t,n)},traverseEnterLeave:function(e,t,n,r,o){return f.traverseEnterLeave(e,t,n,r,o)},injection:v};t.exports=g},{112:112,137:137,142:142,50:50}],19:[function(e,t,n){"use strict";function r(e,t,n){var r=t.dispatchConfig.phasedRegistrationNames[n];return g(e,r)}function o(e,t,n){var o=r(e,n,t);o&&(n._dispatchListeners=m(n._dispatchListeners,o),n._dispatchInstances=m(n._dispatchInstances,e))}function i(e){e&&e.dispatchConfig.phasedRegistrationNames&&h.traverseTwoPhase(e._targetInst,o,e)}function a(e){if(e&&e.dispatchConfig.phasedRegistrationNames){var t=e._targetInst,n=t?h.getParentInstance(t):null;h.traverseTwoPhase(n,o,e)}}function s(e,t,n){if(n&&n.dispatchConfig.registrationName){var r=n.dispatchConfig.registrationName,o=g(e,r);o&&(n._dispatchListeners=m(n._dispatchListeners,o),n._dispatchInstances=m(n._dispatchInstances,e))}}function u(e){e&&e.dispatchConfig.registrationName&&s(e._targetInst,null,e)}function l(e){v(e,i)}function c(e){v(e,a)}function p(e,t,n,r){h.traverseEnterLeave(n,r,s,e,t)}function d(e){v(e,u)}var f=e(16),h=e(18),m=e(91),v=e(98),g=(e(142),f.getListener),y={accumulateTwoPhaseDispatches:l,accumulateTwoPhaseDispatchesSkipTarget:c,accumulateDirectDispatches:d,accumulateEnterLeaveDispatches:p};t.exports=y},{142:142,16:16,18:18,91:91,98:98}],20:[function(e,t,n){"use strict";function r(e){this._root=e,this._startText=this.getText(),this._fallbackText=null}var o=e(143),i=e(24),a=e(106);o(r.prototype,{destructor:function(){this._root=null,this._startText=null,this._fallbackText=null},getText:function(){return"value"in this._root?this._root.value:this._root[a()]},getData:function(){if(this._fallbackText)return this._fallbackText;var e,t,n=this._startText,r=n.length,o=this.getText(),i=o.length;for(e=0;e<r&&n[e]===o[e];e++);var a=r-e;for(t=1;t<=a&&n[r-t]===o[i-t];t++);var s=t>1?1-t:void 0;return this._fallbackText=o.slice(e,s),this._fallbackText}}),i.addPoolingTo(r),t.exports=r},{106:106,143:143,24:24}],21:[function(e,t,n){"use strict";var r=e(11),o=r.injection.MUST_USE_PROPERTY,i=r.injection.HAS_BOOLEAN_VALUE,a=r.injection.HAS_NUMERIC_VALUE,s=r.injection.HAS_POSITIVE_NUMERIC_VALUE,u=r.injection.HAS_OVERLOADED_BOOLEAN_VALUE,l={isCustomAttribute:RegExp.prototype.test.bind(new RegExp("^(data|aria)-["+r.ATTRIBUTE_NAME_CHAR+"]*$")),Properties:{accept:0,acceptCharset:0,accessKey:0,action:0,allowFullScreen:i,allowTransparency:0,alt:0,as:0,async:i,autoComplete:0,autoPlay:i,capture:i,cellPadding:0,cellSpacing:0,charSet:0,challenge:0,checked:o|i,cite:0,classID:0,className:0,cols:s,colSpan:0,content:0,contentEditable:0,contextMenu:0,controls:i,coords:0,crossOrigin:0,data:0,dateTime:0,default:i,defer:i,dir:0,disabled:i,download:u,draggable:0,encType:0,form:0,formAction:0,formEncType:0,formMethod:0,formNoValidate:i,formTarget:0,frameBorder:0,headers:0,height:0,hidden:i,high:0,href:0,hrefLang:0,htmlFor:0,httpEquiv:0,icon:0,id:0,inputMode:0,integrity:0,is:0,keyParams:0,keyType:0,kind:0,label:0,lang:0,list:0,loop:i,low:0,manifest:0,marginHeight:0,marginWidth:0,max:0,maxLength:0,media:0,mediaGroup:0,method:0,min:0,minLength:0,multiple:o|i,muted:o|i,name:0,nonce:0,noValidate:i,open:i,optimum:0,pattern:0,placeholder:0,playsInline:i,poster:0,preload:0,profile:0,radioGroup:0,readOnly:i,referrerPolicy:0,rel:0,required:i,reversed:i,role:0,rows:s,rowSpan:a,sandbox:0,scope:0,scoped:i,scrolling:0,seamless:i,selected:o|i,shape:0,size:s,sizes:0,span:s,spellCheck:0,src:0,srcDoc:0,srcLang:0,srcSet:0,start:a,step:0,style:0,summary:0,tabIndex:0,target:0,title:0,type:0,useMap:0,value:0,width:0,wmode:0,wrap:0,about:0,datatype:0,inlist:0,prefix:0,property:0,resource:0,typeof:0,vocab:0,autoCapitalize:0,autoCorrect:0,autoSave:0,color:0,itemProp:0,itemScope:i,itemType:0,itemID:0,itemRef:0,results:0,security:0,unselectable:0},DOMAttributeNames:{acceptCharset:"accept-charset",className:"class",htmlFor:"for",httpEquiv:"http-equiv"},DOMPropertyNames:{},DOMMutationMethods:{value:function(e,t){if(null==t)return e.removeAttribute("value");"number"!==e.type||!1===e.hasAttribute("value")?e.setAttribute("value",""+t):e.validity&&!e.validity.badInput&&e.ownerDocument.activeElement!==e&&e.setAttribute("value",""+t)}}};t.exports=l},{11:11}],22:[function(e,t,n){"use strict";function r(e){var t={"=":"=0",":":"=2"};return"$"+(""+e).replace(/[=:]/g,function(e){return t[e]})}function o(e){var t={"=0":"=","=2":":"};return(""+("."===e[0]&&"$"===e[1]?e.substring(2):e.substring(1))).replace(/(=0|=2)/g,function(e){return t[e]})}var i={escape:r,unescape:o};t.exports=i},{}],23:[function(e,t,n){"use strict";function r(e){null!=e.checkedLink&&null!=e.valueLink&&s("87")}function o(e){r(e),(null!=e.value||null!=e.onChange)&&s("88")}function i(e){r(e),(null!=e.checked||null!=e.onChange)&&s("89")}function a(e){if(e){var t=e.getName();if(t)return" Check the render method of `"+t+"`."}return""}var s=e(112),u=e(64),l=e(145),c=e(120),p=l(c.isValidElement),d=(e(137),e(142),{button:!0,checkbox:!0,image:!0,hidden:!0,radio:!0,reset:!0,submit:!0}),f={value:function(e,t,n){return!e[t]||d[e.type]||e.onChange||e.readOnly||e.disabled?null:new Error("You provided a `value` prop to a form field without an `onChange` handler. This will render a read-only field. If the field should be mutable use `defaultValue`. Otherwise, set either `onChange` or `readOnly`.")},checked:function(e,t,n){return!e[t]||e.onChange||e.readOnly||e.disabled?null:new Error("You provided a `checked` prop to a form field without an `onChange` handler. This will render a read-only field. If the field should be mutable use `defaultChecked`. Otherwise, set either `onChange` or `readOnly`.")},onChange:p.func},h={},m={checkPropTypes:function(e,t,n){for(var r in f){if(f.hasOwnProperty(r))var o=f[r](t,r,e,"prop",null,u);o instanceof Error&&!(o.message in h)&&(h[o.message]=!0,a(n))}},getValue:function(e){return e.valueLink?(o(e),e.valueLink.value):e.value},getChecked:function(e){return e.checkedLink?(i(e),e.checkedLink.value):e.checked},executeOnChange:function(e,t){return e.valueLink?(o(e),e.valueLink.requestChange(t.target.value)):e.checkedLink?(i(e),e.checkedLink.requestChange(t.target.checked)):e.onChange?e.onChange.call(void 0,t):void 0}};t.exports=m},{112:112,120:120,137:137,142:142,145:145,64:64}],24:[function(e,t,n){"use strict";var r=e(112),o=(e(137),function(e){var t=this;if(t.instancePool.length){var n=t.instancePool.pop();return t.call(n,e),n}return new t(e)}),i=function(e,t){var n=this;if(n.instancePool.length){var r=n.instancePool.pop();return n.call(r,e,t),r}return new n(e,t)},a=function(e,t,n){var r=this;if(r.instancePool.length){var o=r.instancePool.pop();return r.call(o,e,t,n),o}return new r(e,t,n)},s=function(e,t,n,r){var o=this;if(o.instancePool.length){var i=o.instancePool.pop();return o.call(i,e,t,n,r),i}return new o(e,t,n,r)},u=function(e){var t=this;e instanceof t||r("25"),e.destructor(),t.instancePool.length<t.poolSize&&t.instancePool.push(e)},l=o,c=function(e,t){var n=e;return n.instancePool=[],n.getPooled=t||l,n.poolSize||(n.poolSize=10),n.release=u,n},p={addPoolingTo:c,oneArgumentPooler:o,twoArgumentPooler:i,threeArgumentPooler:a,fourArgumentPooler:s};t.exports=p},{112:112,137:137}],25:[function(e,t,n){"use strict";function r(e){return Object.prototype.hasOwnProperty.call(e,m)||(e[m]=f++,p[e[m]]={}),p[e[m]]}var o,i=e(143),a=e(17),s=e(51),u=e(90),l=e(107),c=e(109),p={},d=!1,f=0,h={topAbort:"abort",topAnimationEnd:l("animationend")||"animationend",topAnimationIteration:l("animationiteration")||"animationiteration",topAnimationStart:l("animationstart")||"animationstart",topBlur:"blur",topCanPlay:"canplay",topCanPlayThrough:"canplaythrough",topChange:"change",topClick:"click",topCompositionEnd:"compositionend",topCompositionStart:"compositionstart",topCompositionUpdate:"compositionupdate",topContextMenu:"contextmenu",topCopy:"copy",topCut:"cut",topDoubleClick:"dblclick",topDrag:"drag",topDragEnd:"dragend",topDragEnter:"dragenter",topDragExit:"dragexit",topDragLeave:"dragleave",topDragOver:"dragover",topDragStart:"dragstart",topDrop:"drop",topDurationChange:"durationchange",topEmptied:"emptied",topEncrypted:"encrypted",topEnded:"ended",topError:"error",
+topFocus:"focus",topInput:"input",topKeyDown:"keydown",topKeyPress:"keypress",topKeyUp:"keyup",topLoadedData:"loadeddata",topLoadedMetadata:"loadedmetadata",topLoadStart:"loadstart",topMouseDown:"mousedown",topMouseMove:"mousemove",topMouseOut:"mouseout",topMouseOver:"mouseover",topMouseUp:"mouseup",topPaste:"paste",topPause:"pause",topPlay:"play",topPlaying:"playing",topProgress:"progress",topRateChange:"ratechange",topScroll:"scroll",topSeeked:"seeked",topSeeking:"seeking",topSelectionChange:"selectionchange",topStalled:"stalled",topSuspend:"suspend",topTextInput:"textInput",topTimeUpdate:"timeupdate",topTouchCancel:"touchcancel",topTouchEnd:"touchend",topTouchMove:"touchmove",topTouchStart:"touchstart",topTransitionEnd:l("transitionend")||"transitionend",topVolumeChange:"volumechange",topWaiting:"waiting",topWheel:"wheel"},m="_reactListenersID"+String(Math.random()).slice(2),v=i({},s,{ReactEventListener:null,injection:{injectReactEventListener:function(e){e.setHandleTopLevel(v.handleTopLevel),v.ReactEventListener=e}},setEnabled:function(e){v.ReactEventListener&&v.ReactEventListener.setEnabled(e)},isEnabled:function(){return!(!v.ReactEventListener||!v.ReactEventListener.isEnabled())},listenTo:function(e,t){for(var n=t,o=r(n),i=a.registrationNameDependencies[e],s=0;s<i.length;s++){var u=i[s];o.hasOwnProperty(u)&&o[u]||("topWheel"===u?c("wheel")?v.ReactEventListener.trapBubbledEvent("topWheel","wheel",n):c("mousewheel")?v.ReactEventListener.trapBubbledEvent("topWheel","mousewheel",n):v.ReactEventListener.trapBubbledEvent("topWheel","DOMMouseScroll",n):"topScroll"===u?c("scroll",!0)?v.ReactEventListener.trapCapturedEvent("topScroll","scroll",n):v.ReactEventListener.trapBubbledEvent("topScroll","scroll",v.ReactEventListener.WINDOW_HANDLE):"topFocus"===u||"topBlur"===u?(c("focus",!0)?(v.ReactEventListener.trapCapturedEvent("topFocus","focus",n),v.ReactEventListener.trapCapturedEvent("topBlur","blur",n)):c("focusin")&&(v.ReactEventListener.trapBubbledEvent("topFocus","focusin",n),v.ReactEventListener.trapBubbledEvent("topBlur","focusout",n)),o.topBlur=!0,o.topFocus=!0):h.hasOwnProperty(u)&&v.ReactEventListener.trapBubbledEvent(u,h[u],n),o[u]=!0)}},trapBubbledEvent:function(e,t,n){return v.ReactEventListener.trapBubbledEvent(e,t,n)},trapCapturedEvent:function(e,t,n){return v.ReactEventListener.trapCapturedEvent(e,t,n)},supportsEventPageXY:function(){if(!document.createEvent)return!1;var e=document.createEvent("MouseEvent");return null!=e&&"pageX"in e},ensureScrollValueMonitoring:function(){if(void 0===o&&(o=v.supportsEventPageXY()),!o&&!d){var e=u.refreshScrollValues;v.ReactEventListener.monitorScrollValue(e),d=!0}}});t.exports=v},{107:107,109:109,143:143,17:17,51:51,90:90}],26:[function(e,t,n){(function(n){"use strict";function r(e,t,n,r){var o=void 0===e[n];null!=t&&o&&(e[n]=i(t,!0))}var o=e(66),i=e(108),a=(e(22),e(116)),s=e(117);e(142);void 0!==n&&n.env;var u={instantiateChildren:function(e,t,n,o){if(null==e)return null;var i={};return s(e,r,i),i},updateChildren:function(e,t,n,r,s,u,l,c,p){if(t||e){var d,f;for(d in t)if(t.hasOwnProperty(d)){f=e&&e[d];var h=f&&f._currentElement,m=t[d];if(null!=f&&a(h,m))o.receiveComponent(f,m,s,c),t[d]=f;else{f&&(r[d]=o.getHostNode(f),o.unmountComponent(f,!1));var v=i(m,!0);t[d]=v;var g=o.mountComponent(v,s,u,l,c,p);n.push(g)}}for(d in e)!e.hasOwnProperty(d)||t&&t.hasOwnProperty(d)||(f=e[d],r[d]=o.getHostNode(f),o.unmountComponent(f,!1))}},unmountChildren:function(e,t){for(var n in e)if(e.hasOwnProperty(n)){var r=e[n];o.unmountComponent(r,t)}}};t.exports=u}).call(this,void 0)},{108:108,116:116,117:117,142:142,22:22,66:66}],27:[function(e,t,n){"use strict";var r=e(8),o=e(37),i={processChildrenUpdates:o.dangerouslyProcessChildrenUpdates,replaceNodeWithMarkup:r.dangerouslyReplaceNodeWithMarkup};t.exports=i},{37:37,8:8}],28:[function(e,t,n){"use strict";var r=e(112),o=(e(137),!1),i={replaceNodeWithMarkup:null,processChildrenUpdates:null,injection:{injectEnvironment:function(e){o&&r("104"),i.replaceNodeWithMarkup=e.replaceNodeWithMarkup,i.processChildrenUpdates=e.processChildrenUpdates,o=!0}}};t.exports=i},{112:112,137:137}],29:[function(e,t,n){"use strict";function r(e){}function o(e){return!(!e.prototype||!e.prototype.isReactComponent)}function i(e){return!(!e.prototype||!e.prototype.isPureReactComponent)}var a=e(112),s=e(143),u=e(120),l=e(28),c=e(119),p=e(50),d=e(57),f=(e(58),e(62)),h=e(66),m=e(130),v=(e(137),e(141)),g=e(116),y=(e(142),{ImpureClass:0,PureClass:1,StatelessFunctional:2});r.prototype.render=function(){var e=d.get(this)._currentElement.type,t=e(this.props,this.context,this.updater);return t};var _=1,C={construct:function(e){this._currentElement=e,this._rootNodeID=0,this._compositeType=null,this._instance=null,this._hostParent=null,this._hostContainerInfo=null,this._updateBatchNumber=null,this._pendingElement=null,this._pendingStateQueue=null,this._pendingReplaceState=!1,this._pendingForceUpdate=!1,this._renderedNodeType=null,this._renderedComponent=null,this._context=null,this._mountOrder=0,this._topLevelWrapper=null,this._pendingCallbacks=null,this._calledComponentWillUnmount=!1},mountComponent:function(e,t,n,s){this._context=s,this._mountOrder=_++,this._hostParent=t,this._hostContainerInfo=n;var l,c=this._currentElement.props,p=this._processContext(s),f=this._currentElement.type,h=e.getUpdateQueue(),v=o(f),g=this._constructComponent(v,c,p,h);v||null!=g&&null!=g.render?i(f)?this._compositeType=y.PureClass:this._compositeType=y.ImpureClass:(l=g,null===g||!1===g||u.isValidElement(g)||a("105",f.displayName||f.name||"Component"),g=new r(f),this._compositeType=y.StatelessFunctional),g.props=c,g.context=p,g.refs=m,g.updater=h,this._instance=g,d.set(g,this);var C=g.state;void 0===C&&(g.state=C=null),("object"!=typeof C||Array.isArray(C))&&a("106",this.getName()||"ReactCompositeComponent"),this._pendingStateQueue=null,this._pendingReplaceState=!1,this._pendingForceUpdate=!1;var b;return b=g.unstable_handleError?this.performInitialMountWithErrorHandling(l,t,n,e,s):this.performInitialMount(l,t,n,e,s),g.componentDidMount&&e.getReactMountReady().enqueue(g.componentDidMount,g),b},_constructComponent:function(e,t,n,r){return this._constructComponentWithoutOwner(e,t,n,r)},_constructComponentWithoutOwner:function(e,t,n,r){var o=this._currentElement.type;return e?new o(t,n,r):o(t,n,r)},performInitialMountWithErrorHandling:function(e,t,n,r,o){var i,a=r.checkpoint();try{i=this.performInitialMount(e,t,n,r,o)}catch(s){r.rollback(a),this._instance.unstable_handleError(s),this._pendingStateQueue&&(this._instance.state=this._processPendingState(this._instance.props,this._instance.context)),a=r.checkpoint(),this._renderedComponent.unmountComponent(!0),r.rollback(a),i=this.performInitialMount(e,t,n,r,o)}return i},performInitialMount:function(e,t,n,r,o){var i=this._instance;i.componentWillMount&&(i.componentWillMount(),this._pendingStateQueue&&(i.state=this._processPendingState(i.props,i.context))),void 0===e&&(e=this._renderValidatedComponent());var a=f.getType(e);this._renderedNodeType=a;var s=this._instantiateReactComponent(e,a!==f.EMPTY);return this._renderedComponent=s,h.mountComponent(s,r,t,n,this._processChildContext(o),0)},getHostNode:function(){return h.getHostNode(this._renderedComponent)},unmountComponent:function(e){if(this._renderedComponent){var t=this._instance;if(t.componentWillUnmount&&!t._calledComponentWillUnmount)if(t._calledComponentWillUnmount=!0,e){var n=this.getName()+".componentWillUnmount()";p.invokeGuardedCallback(n,t.componentWillUnmount.bind(t))}else t.componentWillUnmount();this._renderedComponent&&(h.unmountComponent(this._renderedComponent,e),this._renderedNodeType=null,this._renderedComponent=null,this._instance=null),this._pendingStateQueue=null,this._pendingReplaceState=!1,this._pendingForceUpdate=!1,this._pendingCallbacks=null,this._pendingElement=null,this._context=null,this._rootNodeID=0,this._topLevelWrapper=null,d.remove(t)}},_maskContext:function(e){var t=this._currentElement.type,n=t.contextTypes;if(!n)return m;var r={};for(var o in n)r[o]=e[o];return r},_processContext:function(e){return this._maskContext(e)},_processChildContext:function(e){var t,n=this._currentElement.type,r=this._instance;if(r.getChildContext&&(t=r.getChildContext()),t){"object"!=typeof n.childContextTypes&&a("107",this.getName()||"ReactCompositeComponent");for(var o in t)o in n.childContextTypes||a("108",this.getName()||"ReactCompositeComponent",o);return s({},e,t)}return e},_checkContextTypes:function(e,t,n){},receiveComponent:function(e,t,n){var r=this._currentElement,o=this._context;this._pendingElement=null,this.updateComponent(t,r,e,o,n)},performUpdateIfNecessary:function(e){null!=this._pendingElement?h.receiveComponent(this,this._pendingElement,e,this._context):null!==this._pendingStateQueue||this._pendingForceUpdate?this.updateComponent(e,this._currentElement,this._currentElement,this._context,this._context):this._updateBatchNumber=null},updateComponent:function(e,t,n,r,o){var i=this._instance;null==i&&a("136",this.getName()||"ReactCompositeComponent");var s,u=!1;this._context===o?s=i.context:(s=this._processContext(o),u=!0);var l=t.props,c=n.props;t!==n&&(u=!0),u&&i.componentWillReceiveProps&&i.componentWillReceiveProps(c,s);var p=this._processPendingState(c,s),d=!0;this._pendingForceUpdate||(i.shouldComponentUpdate?d=i.shouldComponentUpdate(c,p,s):this._compositeType===y.PureClass&&(d=!v(l,c)||!v(i.state,p))),this._updateBatchNumber=null,d?(this._pendingForceUpdate=!1,this._performComponentUpdate(n,c,p,s,e,o)):(this._currentElement=n,this._context=o,i.props=c,i.state=p,i.context=s)},_processPendingState:function(e,t){var n=this._instance,r=this._pendingStateQueue,o=this._pendingReplaceState;if(this._pendingReplaceState=!1,this._pendingStateQueue=null,!r)return n.state;if(o&&1===r.length)return r[0];for(var i=s({},o?r[0]:n.state),a=o?1:0;a<r.length;a++){var u=r[a];s(i,"function"==typeof u?u.call(n,i,e,t):u)}return i},_performComponentUpdate:function(e,t,n,r,o,i){var a,s,u,l=this._instance,c=Boolean(l.componentDidUpdate);c&&(a=l.props,s=l.state,u=l.context),l.componentWillUpdate&&l.componentWillUpdate(t,n,r),this._currentElement=e,this._context=i,l.props=t,l.state=n,l.context=r,this._updateRenderedComponent(o,i),c&&o.getReactMountReady().enqueue(l.componentDidUpdate.bind(l,a,s,u),l)},_updateRenderedComponent:function(e,t){var n=this._renderedComponent,r=n._currentElement,o=this._renderValidatedComponent();if(g(r,o))h.receiveComponent(n,o,e,this._processChildContext(t));else{var i=h.getHostNode(n);h.unmountComponent(n,!1);var a=f.getType(o);this._renderedNodeType=a;var s=this._instantiateReactComponent(o,a!==f.EMPTY);this._renderedComponent=s;var u=h.mountComponent(s,e,this._hostParent,this._hostContainerInfo,this._processChildContext(t),0);this._replaceNodeWithMarkup(i,u,n)}},_replaceNodeWithMarkup:function(e,t,n){l.replaceNodeWithMarkup(e,t,n)},_renderValidatedComponentWithoutOwnerOrContext:function(){return this._instance.render()},_renderValidatedComponent:function(){var e;if(this._compositeType!==y.StatelessFunctional){c.current=this;try{e=this._renderValidatedComponentWithoutOwnerOrContext()}finally{c.current=null}}else e=this._renderValidatedComponentWithoutOwnerOrContext();return null===e||!1===e||u.isValidElement(e)||a("109",this.getName()||"ReactCompositeComponent"),e},attachRef:function(e,t){var n=this.getPublicInstance();null==n&&a("110");var r=t.getPublicInstance();(n.refs===m?n.refs={}:n.refs)[e]=r},detachRef:function(e){delete this.getPublicInstance().refs[e]},getName:function(){var e=this._currentElement.type,t=this._instance&&this._instance.constructor;return e.displayName||t&&t.displayName||e.name||t&&t.name||null},getPublicInstance:function(){var e=this._instance;return this._compositeType===y.StatelessFunctional?null:e},_instantiateReactComponent:null};t.exports=C},{112:112,116:116,119:119,120:120,130:130,137:137,141:141,142:142,143:143,28:28,50:50,57:57,58:58,62:62,66:66}],30:[function(e,t,n){"use strict";var r=e(33),o=e(47),i=e(60),a=e(66),s=e(71),u=e(72),l=e(96),c=e(103),p=e(113);e(142);o.inject();var d={findDOMNode:l,render:i.render,unmountComponentAtNode:i.unmountComponentAtNode,version:u,unstable_batchedUpdates:s.batchedUpdates,unstable_renderSubtreeIntoContainer:p};"undefined"!=typeof __REACT_DEVTOOLS_GLOBAL_HOOK__&&"function"==typeof __REACT_DEVTOOLS_GLOBAL_HOOK__.inject&&__REACT_DEVTOOLS_GLOBAL_HOOK__.inject({ComponentTree:{getClosestInstanceFromNode:r.getClosestInstanceFromNode,getNodeFromInstance:function(e){return e._renderedComponent&&(e=c(e)),e?r.getNodeFromInstance(e):null}},Mount:i,Reconciler:a});t.exports=d},{103:103,113:113,142:142,33:33,47:47,60:60,66:66,71:71,72:72,96:96}],31:[function(e,t,n){"use strict";function r(e){if(e){var t=e._currentElement._owner||null;if(t){var n=t.getName();if(n)return" This DOM node was rendered by `"+n+"`."}}return""}function o(e,t){t&&(Y[e._tag]&&(null!=t.children||null!=t.dangerouslySetInnerHTML)&&m("137",e._tag,e._currentElement._owner?" Check the render method of "+e._currentElement._owner.getName()+".":""),null!=t.dangerouslySetInnerHTML&&(null!=t.children&&m("60"),"object"==typeof t.dangerouslySetInnerHTML&&B in t.dangerouslySetInnerHTML||m("61")),null!=t.style&&"object"!=typeof t.style&&m("62",r(e)))}function i(e,t,n,r){if(!(r instanceof R)){var o=e._hostContainerInfo,i=o._node&&o._node.nodeType===H,s=i?o._node:o._ownerDocument;F(t,s),r.getReactMountReady().enqueue(a,{inst:e,registrationName:t,listener:n})}}function a(){var e=this;x.putListener(e.inst,e.registrationName,e.listener)}function s(){var e=this;S.postMountWrapper(e)}function u(){var e=this;I.postMountWrapper(e)}function l(){var e=this;N.postMountWrapper(e)}function c(){var e=this;e._rootNodeID||m("63");var t=U(e);switch(t||m("64"),e._tag){case"iframe":case"object":e._wrapperState.listeners=[T.trapBubbledEvent("topLoad","load",t)];break;case"video":case"audio":e._wrapperState.listeners=[];for(var n in q)q.hasOwnProperty(n)&&e._wrapperState.listeners.push(T.trapBubbledEvent(n,q[n],t));break;case"source":e._wrapperState.listeners=[T.trapBubbledEvent("topError","error",t)];break;case"img":e._wrapperState.listeners=[T.trapBubbledEvent("topError","error",t),T.trapBubbledEvent("topLoad","load",t)];break;case"form":e._wrapperState.listeners=[T.trapBubbledEvent("topReset","reset",t),T.trapBubbledEvent("topSubmit","submit",t)];break;case"input":case"select":case"textarea":e._wrapperState.listeners=[T.trapBubbledEvent("topInvalid","invalid",t)]}}function p(){M.postUpdateWrapper(this)}function d(e){G.call(Q,e)||(X.test(e)||m("65",e),Q[e]=!0)}function f(e,t){return e.indexOf("-")>=0||null!=t.is}function h(e){var t=e.type;d(t),this._currentElement=e,this._tag=t.toLowerCase(),this._namespaceURI=null,this._renderedChildren=null,this._previousStyle=null,this._previousStyleCopy=null,this._hostNode=null,this._hostParent=null,this._rootNodeID=0,this._domID=0,this._hostContainerInfo=null,this._wrapperState=null,this._topLevelWrapper=null,this._flags=0}var m=e(112),v=e(143),g=e(2),y=e(5),_=e(9),C=e(10),b=e(11),E=e(12),x=e(16),w=e(17),T=e(25),k=e(32),P=e(33),S=e(38),N=e(39),M=e(40),I=e(43),O=(e(58),e(61)),R=e(68),A=(e(129),e(95)),D=(e(137),e(109),e(141),e(118),e(142),k),L=x.deleteListener,U=P.getNodeFromInstance,F=T.listenTo,j=w.registrationNameModules,V={string:!0,number:!0},B="__html",W={children:null,dangerouslySetInnerHTML:null,suppressContentEditableWarning:null},H=11,q={topAbort:"abort",topCanPlay:"canplay",topCanPlayThrough:"canplaythrough",topDurationChange:"durationchange",topEmptied:"emptied",topEncrypted:"encrypted",topEnded:"ended",topError:"error",topLoadedData:"loadeddata",topLoadedMetadata:"loadedmetadata",topLoadStart:"loadstart",topPause:"pause",topPlay:"play",topPlaying:"playing",topProgress:"progress",topRateChange:"ratechange",topSeeked:"seeked",topSeeking:"seeking",topStalled:"stalled",topSuspend:"suspend",topTimeUpdate:"timeupdate",topVolumeChange:"volumechange",topWaiting:"waiting"},K={area:!0,base:!0,br:!0,col:!0,embed:!0,hr:!0,img:!0,input:!0,keygen:!0,link:!0,meta:!0,param:!0,source:!0,track:!0,wbr:!0},z={listing:!0,pre:!0,textarea:!0},Y=v({menuitem:!0},K),X=/^[a-zA-Z][a-zA-Z:_\.\-\d]*$/,Q={},G={}.hasOwnProperty,$=1;h.displayName="ReactDOMComponent",h.Mixin={mountComponent:function(e,t,n,r){this._rootNodeID=$++,this._domID=n._idCounter++,this._hostParent=t,this._hostContainerInfo=n;var i=this._currentElement.props;switch(this._tag){case"audio":case"form":case"iframe":case"img":case"link":case"object":case"source":case"video":this._wrapperState={listeners:null},e.getReactMountReady().enqueue(c,this);break;case"input":S.mountWrapper(this,i,t),i=S.getHostProps(this,i),e.getReactMountReady().enqueue(c,this);break;case"option":N.mountWrapper(this,i,t),i=N.getHostProps(this,i);break;case"select":M.mountWrapper(this,i,t),i=M.getHostProps(this,i),e.getReactMountReady().enqueue(c,this);break;case"textarea":I.mountWrapper(this,i,t),i=I.getHostProps(this,i),e.getReactMountReady().enqueue(c,this)}o(this,i);var a,p;null!=t?(a=t._namespaceURI,p=t._tag):n._tag&&(a=n._namespaceURI,p=n._tag),(null==a||a===C.svg&&"foreignobject"===p)&&(a=C.html),a===C.html&&("svg"===this._tag?a=C.svg:"math"===this._tag&&(a=C.mathml)),this._namespaceURI=a;var d;if(e.useCreateElement){var f,h=n._ownerDocument;if(a===C.html)if("script"===this._tag){var m=h.createElement("div"),v=this._currentElement.type;m.innerHTML="<"+v+"></"+v+">",f=m.removeChild(m.firstChild)}else f=i.is?h.createElement(this._currentElement.type,i.is):h.createElement(this._currentElement.type);else f=h.createElementNS(a,this._currentElement.type);P.precacheNode(this,f),this._flags|=D.hasCachedChildNodes,this._hostParent||E.setAttributeForRoot(f),this._updateDOMProperties(null,i,e);var y=_(f);this._createInitialChildren(e,i,r,y),d=y}else{var b=this._createOpenTagMarkupAndPutListeners(e,i),x=this._createContentMarkup(e,i,r);d=!x&&K[this._tag]?b+"/>":b+">"+x+"</"+this._currentElement.type+">"}switch(this._tag){case"input":e.getReactMountReady().enqueue(s,this),i.autoFocus&&e.getReactMountReady().enqueue(g.focusDOMComponent,this);break;case"textarea":e.getReactMountReady().enqueue(u,this),i.autoFocus&&e.getReactMountReady().enqueue(g.focusDOMComponent,this);break;case"select":case"button":i.autoFocus&&e.getReactMountReady().enqueue(g.focusDOMComponent,this);break;case"option":e.getReactMountReady().enqueue(l,this)}return d},_createOpenTagMarkupAndPutListeners:function(e,t){var n="<"+this._currentElement.type;for(var r in t)if(t.hasOwnProperty(r)){var o=t[r];if(null!=o)if(j.hasOwnProperty(r))o&&i(this,r,o,e);else{"style"===r&&(o&&(o=this._previousStyleCopy=v({},t.style)),o=y.createMarkupForStyles(o,this));var a=null;null!=this._tag&&f(this._tag,t)?W.hasOwnProperty(r)||(a=E.createMarkupForCustomAttribute(r,o)):a=E.createMarkupForProperty(r,o),a&&(n+=" "+a)}}return e.renderToStaticMarkup?n:(this._hostParent||(n+=" "+E.createMarkupForRoot()),n+=" "+E.createMarkupForID(this._domID))},_createContentMarkup:function(e,t,n){var r="",o=t.dangerouslySetInnerHTML;if(null!=o)null!=o.__html&&(r=o.__html);else{var i=V[typeof t.children]?t.children:null,a=null!=i?null:t.children;if(null!=i)r=A(i);else if(null!=a){var s=this.mountChildren(a,e,n);r=s.join("")}}return z[this._tag]&&"\n"===r.charAt(0)?"\n"+r:r},_createInitialChildren:function(e,t,n,r){var o=t.dangerouslySetInnerHTML;if(null!=o)null!=o.__html&&_.queueHTML(r,o.__html);else{var i=V[typeof t.children]?t.children:null,a=null!=i?null:t.children;if(null!=i)""!==i&&_.queueText(r,i);else if(null!=a)for(var s=this.mountChildren(a,e,n),u=0;u<s.length;u++)_.queueChild(r,s[u])}},receiveComponent:function(e,t,n){var r=this._currentElement;this._currentElement=e,this.updateComponent(t,r,e,n)},updateComponent:function(e,t,n,r){var i=t.props,a=this._currentElement.props;switch(this._tag){case"input":i=S.getHostProps(this,i),a=S.getHostProps(this,a);break;case"option":i=N.getHostProps(this,i),a=N.getHostProps(this,a);break;case"select":i=M.getHostProps(this,i),a=M.getHostProps(this,a);break;case"textarea":i=I.getHostProps(this,i),a=I.getHostProps(this,a)}switch(o(this,a),this._updateDOMProperties(i,a,e),this._updateDOMChildren(i,a,e,r),this._tag){case"input":S.updateWrapper(this);break;case"textarea":I.updateWrapper(this);break;case"select":e.getReactMountReady().enqueue(p,this)}},_updateDOMProperties:function(e,t,n){var r,o,a;for(r in e)if(!t.hasOwnProperty(r)&&e.hasOwnProperty(r)&&null!=e[r])if("style"===r){var s=this._previousStyleCopy;for(o in s)s.hasOwnProperty(o)&&(a=a||{},a[o]="");this._previousStyleCopy=null}else j.hasOwnProperty(r)?e[r]&&L(this,r):f(this._tag,e)?W.hasOwnProperty(r)||E.deleteValueForAttribute(U(this),r):(b.properties[r]||b.isCustomAttribute(r))&&E.deleteValueForProperty(U(this),r);for(r in t){var u=t[r],l="style"===r?this._previousStyleCopy:null!=e?e[r]:void 0;if(t.hasOwnProperty(r)&&u!==l&&(null!=u||null!=l))if("style"===r)if(u?u=this._previousStyleCopy=v({},u):this._previousStyleCopy=null,l){for(o in l)!l.hasOwnProperty(o)||u&&u.hasOwnProperty(o)||(a=a||{},a[o]="");for(o in u)u.hasOwnProperty(o)&&l[o]!==u[o]&&(a=a||{},a[o]=u[o])}else a=u;else if(j.hasOwnProperty(r))u?i(this,r,u,n):l&&L(this,r);else if(f(this._tag,t))W.hasOwnProperty(r)||E.setValueForAttribute(U(this),r,u);else if(b.properties[r]||b.isCustomAttribute(r)){var c=U(this);null!=u?E.setValueForProperty(c,r,u):E.deleteValueForProperty(c,r)}}a&&y.setValueForStyles(U(this),a,this)},_updateDOMChildren:function(e,t,n,r){var o=V[typeof e.children]?e.children:null,i=V[typeof t.children]?t.children:null,a=e.dangerouslySetInnerHTML&&e.dangerouslySetInnerHTML.__html,s=t.dangerouslySetInnerHTML&&t.dangerouslySetInnerHTML.__html,u=null!=o?null:e.children,l=null!=i?null:t.children,c=null!=o||null!=a,p=null!=i||null!=s;null!=u&&null==l?this.updateChildren(null,n,r):c&&!p&&this.updateTextContent(""),null!=i?o!==i&&this.updateTextContent(""+i):null!=s?a!==s&&this.updateMarkup(""+s):null!=l&&this.updateChildren(l,n,r)},getHostNode:function(){return U(this)},unmountComponent:function(e){switch(this._tag){case"audio":case"form":case"iframe":case"img":case"link":case"object":case"source":case"video":var t=this._wrapperState.listeners;if(t)for(var n=0;n<t.length;n++)t[n].remove();break;case"html":case"head":case"body":m("66",this._tag)}this.unmountChildren(e),P.uncacheNode(this),x.deleteAllListeners(this),this._rootNodeID=0,this._domID=0,this._wrapperState=null},getPublicInstance:function(){return U(this)}},v(h.prototype,h.Mixin,O.Mixin),t.exports=h},{10:10,109:109,11:11,112:112,118:118,12:12,129:129,137:137,141:141,142:142,143:143,16:16,17:17,2:2,25:25,32:32,33:33,38:38,39:39,40:40,43:43,5:5,58:58,61:61,68:68,9:9,95:95}],32:[function(e,t,n){"use strict";var r={hasCachedChildNodes:1};t.exports=r},{}],33:[function(e,t,n){"use strict";function r(e,t){return 1===e.nodeType&&e.getAttribute(h)===String(t)||8===e.nodeType&&e.nodeValue===" react-text: "+t+" "||8===e.nodeType&&e.nodeValue===" react-empty: "+t+" "}function o(e){for(var t;t=e._renderedComponent;)e=t;return e}function i(e,t){var n=o(e);n._hostNode=t,t[v]=n}function a(e){var t=e._hostNode;t&&(delete t[v],e._hostNode=null)}function s(e,t){if(!(e._flags&m.hasCachedChildNodes)){var n=e._renderedChildren,a=t.firstChild;e:for(var s in n)if(n.hasOwnProperty(s)){var u=n[s],l=o(u)._domID;if(0!==l){for(;null!==a;a=a.nextSibling)if(r(a,l)){i(u,a);continue e}p("32",l)}}e._flags|=m.hasCachedChildNodes}}function u(e){if(e[v])return e[v];for(var t=[];!e[v];){if(t.push(e),!e.parentNode)return null;e=e.parentNode}for(var n,r;e&&(r=e[v]);e=t.pop())n=r,t.length&&s(r,e);return n}function l(e){var t=u(e);return null!=t&&t._hostNode===e?t:null}function c(e){if(void 0===e._hostNode&&p("33"),e._hostNode)return e._hostNode;for(var t=[];!e._hostNode;)t.push(e),e._hostParent||p("34"),e=e._hostParent;for(;t.length;e=t.pop())s(e,e._hostNode);return e._hostNode}var p=e(112),d=e(11),f=e(32),h=(e(137),d.ID_ATTRIBUTE_NAME),m=f,v="__reactInternalInstance$"+Math.random().toString(36).slice(2),g={getClosestInstanceFromNode:u,getInstanceFromNode:l,getNodeFromInstance:c,precacheChildNodes:s,precacheNode:i,uncacheNode:a};t.exports=g},{11:11,112:112,137:137,32:32}],34:[function(e,t,n){"use strict";function r(e,t){return{_topLevelWrapper:e,_idCounter:1,_ownerDocument:t?t.nodeType===o?t:t.ownerDocument:null,_node:t,_tag:t?t.nodeName.toLowerCase():null,_namespaceURI:t?t.namespaceURI:null}}var o=(e(118),9);t.exports=r},{118:118}],35:[function(e,t,n){"use strict";var r=e(143),o=e(9),i=e(33),a=function(e){this._currentElement=null,this._hostNode=null,this._hostParent=null,this._hostContainerInfo=null,this._domID=0};r(a.prototype,{mountComponent:function(e,t,n,r){var a=n._idCounter++;this._domID=a,this._hostParent=t,this._hostContainerInfo=n;var s=" react-empty: "+this._domID+" ";if(e.useCreateElement){var u=n._ownerDocument,l=u.createComment(s);return i.precacheNode(this,l),o(l)}return e.renderToStaticMarkup?"":"<!--"+s+"-->"},receiveComponent:function(){},getHostNode:function(){return i.getNodeFromInstance(this)},unmountComponent:function(){i.uncacheNode(this)}}),t.exports=a},{143:143,33:33,9:9}],36:[function(e,t,n){"use strict";var r={useCreateElement:!0,useFiber:!1};t.exports=r},{}],37:[function(e,t,n){"use strict";var r=e(8),o=e(33),i={dangerouslyProcessChildrenUpdates:function(e,t){var n=o.getNodeFromInstance(e);r.processUpdates(n,t)}};t.exports=i},{33:33,8:8}],38:[function(e,t,n){"use strict";function r(){this._rootNodeID&&d.updateWrapper(this)}function o(e){return"checkbox"===e.type||"radio"===e.type?null!=e.checked:null!=e.value}function i(e){var t=this._currentElement.props,n=l.executeOnChange(t,e);p.asap(r,this);var o=t.name;if("radio"===t.type&&null!=o){for(var i=c.getNodeFromInstance(this),s=i;s.parentNode;)s=s.parentNode;for(var u=s.querySelectorAll("input[name="+JSON.stringify(""+o)+'][type="radio"]'),d=0;d<u.length;d++){var f=u[d];if(f!==i&&f.form===i.form){var h=c.getInstanceFromNode(f);h||a("90"),p.asap(r,h)}}}return n}var a=e(112),s=e(143),u=e(12),l=e(23),c=e(33),p=e(71),d=(e(137),e(142),{getHostProps:function(e,t){var n=l.getValue(t),r=l.getChecked(t);return s({type:void 0,step:void 0,min:void 0,max:void 0},t,{defaultChecked:void 0,defaultValue:void 0,value:null!=n?n:e._wrapperState.initialValue,checked:null!=r?r:e._wrapperState.initialChecked,onChange:e._wrapperState.onChange})},mountWrapper:function(e,t){var n=t.defaultValue;e._wrapperState={initialChecked:null!=t.checked?t.checked:t.defaultChecked,initialValue:null!=t.value?t.value:n,listeners:null,onChange:i.bind(e),controlled:o(t)}},updateWrapper:function(e){var t=e._currentElement.props,n=t.checked;null!=n&&u.setValueForProperty(c.getNodeFromInstance(e),"checked",n||!1);var r=c.getNodeFromInstance(e),o=l.getValue(t);if(null!=o)if(0===o&&""===r.value)r.value="0";else if("number"===t.type){var i=parseFloat(r.value,10)||0;o!=i&&(r.value=""+o)}else o!=r.value&&(r.value=""+o);else null==t.value&&null!=t.defaultValue&&r.defaultValue!==""+t.defaultValue&&(r.defaultValue=""+t.defaultValue),null==t.checked&&null!=t.defaultChecked&&(r.defaultChecked=!!t.defaultChecked)},postMountWrapper:function(e){var t=e._currentElement.props,n=c.getNodeFromInstance(e);switch(t.type){case"submit":case"reset":break;case"color":case"date":case"datetime":case"datetime-local":case"month":case"time":case"week":n.value="",n.value=n.defaultValue;break;default:n.value=n.value}var r=n.name;""!==r&&(n.name=""),n.defaultChecked=!n.defaultChecked,n.defaultChecked=!n.defaultChecked,""!==r&&(n.name=r)}});t.exports=d},{112:112,12:12,137:137,142:142,143:143,23:23,33:33,71:71}],39:[function(e,t,n){"use strict";function r(e){var t="";return i.Children.forEach(e,function(e){null!=e&&("string"==typeof e||"number"==typeof e?t+=e:u||(u=!0))}),t}var o=e(143),i=e(120),a=e(33),s=e(40),u=(e(142),!1),l={mountWrapper:function(e,t,n){var o=null;if(null!=n){var i=n;"optgroup"===i._tag&&(i=i._hostParent),null!=i&&"select"===i._tag&&(o=s.getSelectValueContext(i))}var a=null;if(null!=o){var u;if(u=null!=t.value?t.value+"":r(t.children),a=!1,Array.isArray(o)){for(var l=0;l<o.length;l++)if(""+o[l]===u){a=!0;break}}else a=""+o===u}e._wrapperState={selected:a}},postMountWrapper:function(e){var t=e._currentElement.props;null!=t.value&&a.getNodeFromInstance(e).setAttribute("value",t.value)},getHostProps:function(e,t){var n=o({selected:void 0,children:void 0},t);null!=e._wrapperState.selected&&(n.selected=e._wrapperState.selected);var i=r(t.children);return i&&(n.children=i),n}};t.exports=l},{120:120,142:142,143:143,33:33,40:40}],40:[function(e,t,n){"use strict";function r(){if(this._rootNodeID&&this._wrapperState.pendingUpdate){this._wrapperState.pendingUpdate=!1;var e=this._currentElement.props,t=s.getValue(e);null!=t&&o(this,Boolean(e.multiple),t)}}function o(e,t,n){var r,o,i=u.getNodeFromInstance(e).options;if(t){for(r={},o=0;o<n.length;o++)r[""+n[o]]=!0;for(o=0;o<i.length;o++){var a=r.hasOwnProperty(i[o].value);i[o].selected!==a&&(i[o].selected=a)}}else{for(r=""+n,o=0;o<i.length;o++)if(i[o].value===r)return void(i[o].selected=!0);i.length&&(i[0].selected=!0)}}function i(e){var t=this._currentElement.props,n=s.executeOnChange(t,e);return this._rootNodeID&&(this._wrapperState.pendingUpdate=!0),l.asap(r,this),n}var a=e(143),s=e(23),u=e(33),l=e(71),c=(e(142),!1),p={getHostProps:function(e,t){return a({},t,{onChange:e._wrapperState.onChange,value:void 0})},mountWrapper:function(e,t){var n=s.getValue(t);e._wrapperState={pendingUpdate:!1,initialValue:null!=n?n:t.defaultValue,listeners:null,onChange:i.bind(e),wasMultiple:Boolean(t.multiple)},void 0===t.value||void 0===t.defaultValue||c||(c=!0)},getSelectValueContext:function(e){return e._wrapperState.initialValue},postUpdateWrapper:function(e){var t=e._currentElement.props;e._wrapperState.initialValue=void 0;var n=e._wrapperState.wasMultiple;e._wrapperState.wasMultiple=Boolean(t.multiple);var r=s.getValue(t);null!=r?(e._wrapperState.pendingUpdate=!1,o(e,Boolean(t.multiple),r)):n!==Boolean(t.multiple)&&(null!=t.defaultValue?o(e,Boolean(t.multiple),t.defaultValue):o(e,Boolean(t.multiple),t.multiple?[]:""))}};t.exports=p},{142:142,143:143,23:23,33:33,71:71}],41:[function(e,t,n){"use strict";function r(e,t,n,r){return e===n&&t===r}function o(e){var t=document.selection,n=t.createRange(),r=n.text.length,o=n.duplicate();o.moveToElementText(e),o.setEndPoint("EndToStart",n);var i=o.text.length;return{start:i,end:i+r}}function i(e){var t=window.getSelection&&window.getSelection();if(!t||0===t.rangeCount)return null;var n=t.anchorNode,o=t.anchorOffset,i=t.focusNode,a=t.focusOffset,s=t.getRangeAt(0);try{s.startContainer.nodeType,s.endContainer.nodeType}catch(e){return null}var u=r(t.anchorNode,t.anchorOffset,t.focusNode,t.focusOffset),l=u?0:s.toString().length,c=s.cloneRange();c.selectNodeContents(e),c.setEnd(s.startContainer,s.startOffset);var p=r(c.startContainer,c.startOffset,c.endContainer,c.endOffset),d=p?0:c.toString().length,f=d+l,h=document.createRange();h.setStart(n,o),h.setEnd(i,a);var m=h.collapsed;return{start:m?f:d,end:m?d:f}}function a(e,t){var n,r,o=document.selection.createRange().duplicate();void 0===t.end?(n=t.start,r=n):t.start>t.end?(n=t.end,r=t.start):(n=t.start,r=t.end),o.moveToElementText(e),o.moveStart("character",n),o.setEndPoint("EndToStart",o),o.moveEnd("character",r-n),o.select()}function s(e,t){if(window.getSelection){var n=window.getSelection(),r=e[c()].length,o=Math.min(t.start,r),i=void 0===t.end?o:Math.min(t.end,r);if(!n.extend&&o>i){var a=i;i=o,o=a}var s=l(e,o),u=l(e,i);if(s&&u){var p=document.createRange();p.setStart(s.node,s.offset),n.removeAllRanges(),o>i?(n.addRange(p),n.extend(u.node,u.offset)):(p.setEnd(u.node,u.offset),n.addRange(p))}}}var u=e(123),l=e(105),c=e(106),p=u.canUseDOM&&"selection"in document&&!("getSelection"in window),d={getOffsets:p?o:i,setOffsets:p?a:s};t.exports=d},{105:105,106:106,123:123}],42:[function(e,t,n){"use strict";var r=e(112),o=e(143),i=e(8),a=e(9),s=e(33),u=e(95),l=(e(137),e(118),function(e){this._currentElement=e,this._stringText=""+e,
+this._hostNode=null,this._hostParent=null,this._domID=0,this._mountIndex=0,this._closingComment=null,this._commentNodes=null});o(l.prototype,{mountComponent:function(e,t,n,r){var o=n._idCounter++,i=" react-text: "+o+" ";if(this._domID=o,this._hostParent=t,e.useCreateElement){var l=n._ownerDocument,c=l.createComment(i),p=l.createComment(" /react-text "),d=a(l.createDocumentFragment());return a.queueChild(d,a(c)),this._stringText&&a.queueChild(d,a(l.createTextNode(this._stringText))),a.queueChild(d,a(p)),s.precacheNode(this,c),this._closingComment=p,d}var f=u(this._stringText);return e.renderToStaticMarkup?f:"<!--"+i+"-->"+f+"<!-- /react-text -->"},receiveComponent:function(e,t){if(e!==this._currentElement){this._currentElement=e;var n=""+e;if(n!==this._stringText){this._stringText=n;var r=this.getHostNode();i.replaceDelimitedText(r[0],r[1],n)}}},getHostNode:function(){var e=this._commentNodes;if(e)return e;if(!this._closingComment)for(var t=s.getNodeFromInstance(this),n=t.nextSibling;;){if(null==n&&r("67",this._domID),8===n.nodeType&&" /react-text "===n.nodeValue){this._closingComment=n;break}n=n.nextSibling}return e=[this._hostNode,this._closingComment],this._commentNodes=e,e},unmountComponent:function(){this._closingComment=null,this._commentNodes=null,s.uncacheNode(this)}}),t.exports=l},{112:112,118:118,137:137,143:143,33:33,8:8,9:9,95:95}],43:[function(e,t,n){"use strict";function r(){this._rootNodeID&&c.updateWrapper(this)}function o(e){var t=this._currentElement.props,n=s.executeOnChange(t,e);return l.asap(r,this),n}var i=e(112),a=e(143),s=e(23),u=e(33),l=e(71),c=(e(137),e(142),{getHostProps:function(e,t){return null!=t.dangerouslySetInnerHTML&&i("91"),a({},t,{value:void 0,defaultValue:void 0,children:""+e._wrapperState.initialValue,onChange:e._wrapperState.onChange})},mountWrapper:function(e,t){var n=s.getValue(t),r=n;if(null==n){var a=t.defaultValue,u=t.children;null!=u&&(null!=a&&i("92"),Array.isArray(u)&&(u.length<=1||i("93"),u=u[0]),a=""+u),null==a&&(a=""),r=a}e._wrapperState={initialValue:""+r,listeners:null,onChange:o.bind(e)}},updateWrapper:function(e){var t=e._currentElement.props,n=u.getNodeFromInstance(e),r=s.getValue(t);if(null!=r){var o=""+r;o!==n.value&&(n.value=o),null==t.defaultValue&&(n.defaultValue=o)}null!=t.defaultValue&&(n.defaultValue=t.defaultValue)},postMountWrapper:function(e){var t=u.getNodeFromInstance(e),n=t.textContent;n===e._wrapperState.initialValue&&(t.value=n)}});t.exports=c},{112:112,137:137,142:142,143:143,23:23,33:33,71:71}],44:[function(e,t,n){"use strict";function r(e,t){"_hostNode"in e||u("33"),"_hostNode"in t||u("33");for(var n=0,r=e;r;r=r._hostParent)n++;for(var o=0,i=t;i;i=i._hostParent)o++;for(;n-o>0;)e=e._hostParent,n--;for(;o-n>0;)t=t._hostParent,o--;for(var a=n;a--;){if(e===t)return e;e=e._hostParent,t=t._hostParent}return null}function o(e,t){"_hostNode"in e||u("35"),"_hostNode"in t||u("35");for(;t;){if(t===e)return!0;t=t._hostParent}return!1}function i(e){return"_hostNode"in e||u("36"),e._hostParent}function a(e,t,n){for(var r=[];e;)r.push(e),e=e._hostParent;var o;for(o=r.length;o-- >0;)t(r[o],"captured",n);for(o=0;o<r.length;o++)t(r[o],"bubbled",n)}function s(e,t,n,o,i){for(var a=e&&t?r(e,t):null,s=[];e&&e!==a;)s.push(e),e=e._hostParent;for(var u=[];t&&t!==a;)u.push(t),t=t._hostParent;var l;for(l=0;l<s.length;l++)n(s[l],"bubbled",o);for(l=u.length;l-- >0;)n(u[l],"captured",i)}var u=e(112);e(137);t.exports={isAncestor:o,getLowestCommonAncestor:r,getParentInstance:i,traverseTwoPhase:a,traverseEnterLeave:s}},{112:112,137:137}],45:[function(e,t,n){"use strict";var r=e(120),o=e(30),i=o;r.addons&&(r.__SECRET_INJECTED_REACT_DOM_DO_NOT_USE_OR_YOU_WILL_BE_FIRED=i),t.exports=i},{120:120,30:30}],46:[function(e,t,n){"use strict";function r(){this.reinitializeTransaction()}var o=e(143),i=e(71),a=e(89),s=e(129),u={initialize:s,close:function(){d.isBatchingUpdates=!1}},l={initialize:s,close:i.flushBatchedUpdates.bind(i)},c=[l,u];o(r.prototype,a,{getTransactionWrappers:function(){return c}});var p=new r,d={isBatchingUpdates:!1,batchedUpdates:function(e,t,n,r,o,i){var a=d.isBatchingUpdates;return d.isBatchingUpdates=!0,a?e(t,n,r,o,i):p.perform(e,null,t,n,r,o,i)}};t.exports=d},{129:129,143:143,71:71,89:89}],47:[function(e,t,n){"use strict";function r(){x||(x=!0,y.EventEmitter.injectReactEventListener(g),y.EventPluginHub.injectEventPluginOrder(s),y.EventPluginUtils.injectComponentTree(d),y.EventPluginUtils.injectTreeTraversal(h),y.EventPluginHub.injectEventPluginsByName({SimpleEventPlugin:E,EnterLeaveEventPlugin:u,ChangeEventPlugin:a,SelectEventPlugin:b,BeforeInputEventPlugin:i}),y.HostComponent.injectGenericComponentClass(p),y.HostComponent.injectTextComponentClass(m),y.DOMProperty.injectDOMPropertyConfig(o),y.DOMProperty.injectDOMPropertyConfig(l),y.DOMProperty.injectDOMPropertyConfig(C),y.EmptyComponent.injectEmptyComponentFactory(function(e){return new f(e)}),y.Updates.injectReconcileTransaction(_),y.Updates.injectBatchingStrategy(v),y.Component.injectEnvironment(c))}var o=e(1),i=e(3),a=e(7),s=e(14),u=e(15),l=e(21),c=e(27),p=e(31),d=e(33),f=e(35),h=e(44),m=e(42),v=e(46),g=e(52),y=e(55),_=e(65),C=e(73),b=e(74),E=e(75),x=!1;t.exports={inject:r}},{1:1,14:14,15:15,21:21,27:27,3:3,31:31,33:33,35:35,42:42,44:44,46:46,52:52,55:55,65:65,7:7,73:73,74:74,75:75}],48:[function(e,t,n){"use strict";var r="function"==typeof Symbol&&Symbol.for&&Symbol.for("react.element")||60103;t.exports=r},{}],49:[function(e,t,n){"use strict";var r,o={injectEmptyComponentFactory:function(e){r=e}},i={create:function(e){return r(e)}};i.injection=o,t.exports=i},{}],50:[function(e,t,n){"use strict";function r(e,t,n){try{t(n)}catch(e){null===o&&(o=e)}}var o=null,i={invokeGuardedCallback:r,invokeGuardedCallbackWithCatch:r,rethrowCaughtError:function(){if(o){var e=o;throw o=null,e}}};t.exports=i},{}],51:[function(e,t,n){"use strict";function r(e){o.enqueueEvents(e),o.processEventQueue(!1)}var o=e(16),i={handleTopLevel:function(e,t,n,i){r(o.extractEvents(e,t,n,i))}};t.exports=i},{16:16}],52:[function(e,t,n){"use strict";function r(e){for(;e._hostParent;)e=e._hostParent;var t=p.getNodeFromInstance(e),n=t.parentNode;return p.getClosestInstanceFromNode(n)}function o(e,t){this.topLevelType=e,this.nativeEvent=t,this.ancestors=[]}function i(e){var t=f(e.nativeEvent),n=p.getClosestInstanceFromNode(t),o=n;do{e.ancestors.push(o),o=o&&r(o)}while(o);for(var i=0;i<e.ancestors.length;i++)n=e.ancestors[i],m._handleTopLevel(e.topLevelType,n,e.nativeEvent,f(e.nativeEvent))}function a(e){e(h(window))}var s=e(143),u=e(122),l=e(123),c=e(24),p=e(33),d=e(71),f=e(102),h=e(134);s(o.prototype,{destructor:function(){this.topLevelType=null,this.nativeEvent=null,this.ancestors.length=0}}),c.addPoolingTo(o,c.twoArgumentPooler);var m={_enabled:!0,_handleTopLevel:null,WINDOW_HANDLE:l.canUseDOM?window:null,setHandleTopLevel:function(e){m._handleTopLevel=e},setEnabled:function(e){m._enabled=!!e},isEnabled:function(){return m._enabled},trapBubbledEvent:function(e,t,n){return n?u.listen(n,t,m.dispatchEvent.bind(null,e)):null},trapCapturedEvent:function(e,t,n){return n?u.capture(n,t,m.dispatchEvent.bind(null,e)):null},monitorScrollValue:function(e){var t=a.bind(null,e);u.listen(window,"scroll",t)},dispatchEvent:function(e,t){if(m._enabled){var n=o.getPooled(e,t);try{d.batchedUpdates(i,n)}finally{o.release(n)}}}};t.exports=m},{102:102,122:122,123:123,134:134,143:143,24:24,33:33,71:71}],53:[function(e,t,n){"use strict";var r={logTopLevelRenders:!1};t.exports=r},{}],54:[function(e,t,n){"use strict";function r(e){return s||a("111",e.type),new s(e)}function o(e){return new u(e)}function i(e){return e instanceof u}var a=e(112),s=(e(137),null),u=null,l={injectGenericComponentClass:function(e){s=e},injectTextComponentClass:function(e){u=e}},c={createInternalComponent:r,createInstanceForText:o,isTextComponent:i,injection:l};t.exports=c},{112:112,137:137}],55:[function(e,t,n){"use strict";var r=e(11),o=e(16),i=e(18),a=e(28),s=e(49),u=e(25),l=e(54),c=e(71),p={Component:a.injection,DOMProperty:r.injection,EmptyComponent:s.injection,EventPluginHub:o.injection,EventPluginUtils:i.injection,EventEmitter:u.injection,HostComponent:l.injection,Updates:c.injection};t.exports=p},{11:11,16:16,18:18,25:25,28:28,49:49,54:54,71:71}],56:[function(e,t,n){"use strict";function r(e){return i(document.documentElement,e)}var o=e(41),i=e(126),a=e(131),s=e(132),u={hasSelectionCapabilities:function(e){var t=e&&e.nodeName&&e.nodeName.toLowerCase();return t&&("input"===t&&"text"===e.type||"textarea"===t||"true"===e.contentEditable)},getSelectionInformation:function(){var e=s();return{focusedElem:e,selectionRange:u.hasSelectionCapabilities(e)?u.getSelection(e):null}},restoreSelection:function(e){var t=s(),n=e.focusedElem,o=e.selectionRange;t!==n&&r(n)&&(u.hasSelectionCapabilities(n)&&u.setSelection(n,o),a(n))},getSelection:function(e){var t;if("selectionStart"in e)t={start:e.selectionStart,end:e.selectionEnd};else if(document.selection&&e.nodeName&&"input"===e.nodeName.toLowerCase()){var n=document.selection.createRange();n.parentElement()===e&&(t={start:-n.moveStart("character",-e.value.length),end:-n.moveEnd("character",-e.value.length)})}else t=o.getOffsets(e);return t||{start:0,end:0}},setSelection:function(e,t){var n=t.start,r=t.end;if(void 0===r&&(r=n),"selectionStart"in e)e.selectionStart=n,e.selectionEnd=Math.min(r,e.value.length);else if(document.selection&&e.nodeName&&"input"===e.nodeName.toLowerCase()){var i=e.createTextRange();i.collapse(!0),i.moveStart("character",n),i.moveEnd("character",r-n),i.select()}else o.setOffsets(e,t)}};t.exports=u},{126:126,131:131,132:132,41:41}],57:[function(e,t,n){"use strict";var r={remove:function(e){e._reactInternalInstance=void 0},get:function(e){return e._reactInternalInstance},has:function(e){return void 0!==e._reactInternalInstance},set:function(e,t){e._reactInternalInstance=t}};t.exports=r},{}],58:[function(e,t,n){"use strict";t.exports={debugTool:null}},{}],59:[function(e,t,n){"use strict";var r=e(92),o=/^<\!\-\-/,i={CHECKSUM_ATTR_NAME:"data-react-checksum",addChecksumToMarkup:function(e){var t=r(e);return o.test(e)?e:e.replace(/\/?>/," "+i.CHECKSUM_ATTR_NAME+'="'+t+'"$&')},canReuseMarkup:function(e,t){var n=t.getAttribute(i.CHECKSUM_ATTR_NAME);return n=n&&parseInt(n,10),r(e)===n}};t.exports=i},{92:92}],60:[function(e,t,n){"use strict";function r(e,t){for(var n=Math.min(e.length,t.length),r=0;r<n;r++)if(e.charAt(r)!==t.charAt(r))return r;return e.length===t.length?-1:n}function o(e){return e?e.nodeType===A?e.documentElement:e.firstChild:null}function i(e){return e.getAttribute&&e.getAttribute(I)||""}function a(e,t,n,r,o){var i;if(b.logTopLevelRenders){var a=e._currentElement.props.child,s=a.type;i="React mount: "+("string"==typeof s?s:s.displayName||s.name),console.time(i)}var u=w.mountComponent(e,n,null,_(e,t),o,0);i&&console.timeEnd(i),e._renderedComponent._topLevelWrapper=e,j._mountImageIntoNode(u,t,e,r,n)}function s(e,t,n,r){var o=k.ReactReconcileTransaction.getPooled(!n&&C.useCreateElement);o.perform(a,null,e,t,o,n,r),k.ReactReconcileTransaction.release(o)}function u(e,t,n){for(w.unmountComponent(e,n),t.nodeType===A&&(t=t.documentElement);t.lastChild;)t.removeChild(t.lastChild)}function l(e){var t=o(e);if(t){var n=y.getInstanceFromNode(t);return!(!n||!n._hostParent)}}function c(e){return!(!e||e.nodeType!==R&&e.nodeType!==A&&e.nodeType!==D)}function p(e){var t=o(e),n=t&&y.getInstanceFromNode(t);return n&&!n._hostParent?n:null}function d(e){var t=p(e);return t?t._hostContainerInfo._topLevelWrapper:null}var f=e(112),h=e(9),m=e(11),v=e(120),g=e(25),y=(e(119),e(33)),_=e(34),C=e(36),b=e(53),E=e(57),x=(e(58),e(59)),w=e(66),T=e(70),k=e(71),P=e(130),S=e(108),N=(e(137),e(114)),M=e(116),I=(e(142),m.ID_ATTRIBUTE_NAME),O=m.ROOT_ATTRIBUTE_NAME,R=1,A=9,D=11,L={},U=1,F=function(){this.rootID=U++};F.prototype.isReactComponent={},F.prototype.render=function(){return this.props.child},F.isReactTopLevelWrapper=!0;var j={TopLevelWrapper:F,_instancesByReactRootID:L,scrollMonitor:function(e,t){t()},_updateRootComponent:function(e,t,n,r,o){return j.scrollMonitor(r,function(){T.enqueueElementInternal(e,t,n),o&&T.enqueueCallbackInternal(e,o)}),e},_renderNewRootComponent:function(e,t,n,r){c(t)||f("37"),g.ensureScrollValueMonitoring();var o=S(e,!1);k.batchedUpdates(s,o,t,n,r);var i=o._instance.rootID;return L[i]=o,o},renderSubtreeIntoContainer:function(e,t,n,r){return null!=e&&E.has(e)||f("38"),j._renderSubtreeIntoContainer(e,t,n,r)},_renderSubtreeIntoContainer:function(e,t,n,r){T.validateCallback(r,"ReactDOM.render"),v.isValidElement(t)||f("39","string"==typeof t?" Instead of passing a string like 'div', pass React.createElement('div') or <div />.":"function"==typeof t?" Instead of passing a class like Foo, pass React.createElement(Foo) or <Foo />.":null!=t&&void 0!==t.props?" This may be caused by unintentionally loading two independent copies of React.":"");var a,s=v.createElement(F,{child:t});if(e){var u=E.get(e);a=u._processChildContext(u._context)}else a=P;var c=d(n);if(c){var p=c._currentElement,h=p.props.child;if(M(h,t)){var m=c._renderedComponent.getPublicInstance(),g=r&&function(){r.call(m)};return j._updateRootComponent(c,s,a,n,g),m}j.unmountComponentAtNode(n)}var y=o(n),_=y&&!!i(y),C=l(n),b=_&&!c&&!C,x=j._renderNewRootComponent(s,n,b,a)._renderedComponent.getPublicInstance();return r&&r.call(x),x},render:function(e,t,n){return j._renderSubtreeIntoContainer(null,e,t,n)},unmountComponentAtNode:function(e){c(e)||f("40");var t=d(e);return t?(delete L[t._instance.rootID],k.batchedUpdates(u,t,e,!1),!0):(l(e),1===e.nodeType&&e.hasAttribute(O),!1)},_mountImageIntoNode:function(e,t,n,i,a){if(c(t)||f("41"),i){var s=o(t);if(x.canReuseMarkup(e,s))return void y.precacheNode(n,s);var u=s.getAttribute(x.CHECKSUM_ATTR_NAME);s.removeAttribute(x.CHECKSUM_ATTR_NAME);var l=s.outerHTML;s.setAttribute(x.CHECKSUM_ATTR_NAME,u);var p=e,d=r(p,l),m=" (client) "+p.substring(d-20,d+20)+"\n (server) "+l.substring(d-20,d+20);t.nodeType===A&&f("42",m)}if(t.nodeType===A&&f("43"),a.useCreateElement){for(;t.lastChild;)t.removeChild(t.lastChild);h.insertTreeBefore(t,e,null)}else N(t,e),y.precacheNode(n,t.firstChild)}};t.exports=j},{108:108,11:11,112:112,114:114,116:116,119:119,120:120,130:130,137:137,142:142,25:25,33:33,34:34,36:36,53:53,57:57,58:58,59:59,66:66,70:70,71:71,9:9}],61:[function(e,t,n){"use strict";function r(e,t,n){return{type:"INSERT_MARKUP",content:e,fromIndex:null,fromNode:null,toIndex:n,afterNode:t}}function o(e,t,n){return{type:"MOVE_EXISTING",content:null,fromIndex:e._mountIndex,fromNode:d.getHostNode(e),toIndex:n,afterNode:t}}function i(e,t){return{type:"REMOVE_NODE",content:null,fromIndex:e._mountIndex,fromNode:t,toIndex:null,afterNode:null}}function a(e){return{type:"SET_MARKUP",content:e,fromIndex:null,fromNode:null,toIndex:null,afterNode:null}}function s(e){return{type:"TEXT_CONTENT",content:e,fromIndex:null,fromNode:null,toIndex:null,afterNode:null}}function u(e,t){return t&&(e=e||[],e.push(t)),e}function l(e,t){p.processChildrenUpdates(e,t)}var c=e(112),p=e(28),d=(e(57),e(58),e(119),e(66)),f=e(26),h=(e(129),e(97)),m=(e(137),{Mixin:{_reconcilerInstantiateChildren:function(e,t,n){return f.instantiateChildren(e,t,n)},_reconcilerUpdateChildren:function(e,t,n,r,o,i){var a;return a=h(t,0),f.updateChildren(e,a,n,r,o,this,this._hostContainerInfo,i,0),a},mountChildren:function(e,t,n){var r=this._reconcilerInstantiateChildren(e,t,n);this._renderedChildren=r;var o=[],i=0;for(var a in r)if(r.hasOwnProperty(a)){var s=r[a],u=d.mountComponent(s,t,this,this._hostContainerInfo,n,0);s._mountIndex=i++,o.push(u)}return o},updateTextContent:function(e){var t=this._renderedChildren;f.unmountChildren(t,!1);for(var n in t)t.hasOwnProperty(n)&&c("118");l(this,[s(e)])},updateMarkup:function(e){var t=this._renderedChildren;f.unmountChildren(t,!1);for(var n in t)t.hasOwnProperty(n)&&c("118");l(this,[a(e)])},updateChildren:function(e,t,n){this._updateChildren(e,t,n)},_updateChildren:function(e,t,n){var r=this._renderedChildren,o={},i=[],a=this._reconcilerUpdateChildren(r,e,i,o,t,n);if(a||r){var s,c=null,p=0,f=0,h=0,m=null;for(s in a)if(a.hasOwnProperty(s)){var v=r&&r[s],g=a[s];v===g?(c=u(c,this.moveChild(v,m,p,f)),f=Math.max(v._mountIndex,f),v._mountIndex=p):(v&&(f=Math.max(v._mountIndex,f)),c=u(c,this._mountChildAtIndex(g,i[h],m,p,t,n)),h++),p++,m=d.getHostNode(g)}for(s in o)o.hasOwnProperty(s)&&(c=u(c,this._unmountChild(r[s],o[s])));c&&l(this,c),this._renderedChildren=a}},unmountChildren:function(e){var t=this._renderedChildren;f.unmountChildren(t,e),this._renderedChildren=null},moveChild:function(e,t,n,r){if(e._mountIndex<r)return o(e,t,n)},createChild:function(e,t,n){return r(n,t,e._mountIndex)},removeChild:function(e,t){return i(e,t)},_mountChildAtIndex:function(e,t,n,r,o,i){return e._mountIndex=r,this.createChild(e,n,t)},_unmountChild:function(e,t){var n=this.removeChild(e,t);return e._mountIndex=null,n}}});t.exports=m},{112:112,119:119,129:129,137:137,26:26,28:28,57:57,58:58,66:66,97:97}],62:[function(e,t,n){"use strict";var r=e(112),o=e(120),i=(e(137),{HOST:0,COMPOSITE:1,EMPTY:2,getType:function(e){return null===e||!1===e?i.EMPTY:o.isValidElement(e)?"function"==typeof e.type?i.COMPOSITE:i.HOST:void r("26",e)}});t.exports=i},{112:112,120:120,137:137}],63:[function(e,t,n){"use strict";function r(e){return!(!e||"function"!=typeof e.attachRef||"function"!=typeof e.detachRef)}var o=e(112),i=(e(137),{addComponentAsRefTo:function(e,t,n){r(n)||o("119"),n.attachRef(t,e)},removeComponentAsRefFrom:function(e,t,n){r(n)||o("120");var i=n.getPublicInstance();i&&i.refs[t]===e.getPublicInstance()&&n.detachRef(t)}});t.exports=i},{112:112,137:137}],64:[function(e,t,n){"use strict";t.exports="SECRET_DO_NOT_PASS_THIS_OR_YOU_WILL_BE_FIRED"},{}],65:[function(e,t,n){"use strict";function r(e){this.reinitializeTransaction(),this.renderToStaticMarkup=!1,this.reactMountReady=i.getPooled(null),this.useCreateElement=e}var o=e(143),i=e(6),a=e(24),s=e(25),u=e(56),l=(e(58),e(89)),c=e(70),p={initialize:u.getSelectionInformation,close:u.restoreSelection},d={initialize:function(){var e=s.isEnabled();return s.setEnabled(!1),e},close:function(e){s.setEnabled(e)}},f={initialize:function(){this.reactMountReady.reset()},close:function(){this.reactMountReady.notifyAll()}},h=[p,d,f],m={getTransactionWrappers:function(){return h},getReactMountReady:function(){return this.reactMountReady},getUpdateQueue:function(){return c},checkpoint:function(){return this.reactMountReady.checkpoint()},rollback:function(e){this.reactMountReady.rollback(e)},destructor:function(){i.release(this.reactMountReady),this.reactMountReady=null}};o(r.prototype,l,m),a.addPoolingTo(r),t.exports=r},{143:143,24:24,25:25,56:56,58:58,6:6,70:70,89:89}],66:[function(e,t,n){"use strict";function r(){o.attachRefs(this,this._currentElement)}var o=e(67),i=(e(58),e(142),{mountComponent:function(e,t,n,o,i,a){var s=e.mountComponent(t,n,o,i,a);return e._currentElement&&null!=e._currentElement.ref&&t.getReactMountReady().enqueue(r,e),s},getHostNode:function(e){return e.getHostNode()},unmountComponent:function(e,t){o.detachRefs(e,e._currentElement),e.unmountComponent(t)},receiveComponent:function(e,t,n,i){var a=e._currentElement;if(t!==a||i!==e._context){var s=o.shouldUpdateRefs(a,t);s&&o.detachRefs(e,a),e.receiveComponent(t,n,i),s&&e._currentElement&&null!=e._currentElement.ref&&n.getReactMountReady().enqueue(r,e)}},performUpdateIfNecessary:function(e,t,n){e._updateBatchNumber===n&&e.performUpdateIfNecessary(t)}});t.exports=i},{142:142,58:58,67:67}],67:[function(e,t,n){"use strict";function r(e,t,n){"function"==typeof e?e(t.getPublicInstance()):i.addComponentAsRefTo(t,e,n)}function o(e,t,n){"function"==typeof e?e(null):i.removeComponentAsRefFrom(t,e,n)}var i=e(63),a={};a.attachRefs=function(e,t){if(null!==t&&"object"==typeof t){var n=t.ref;null!=n&&r(n,e,t._owner)}},a.shouldUpdateRefs=function(e,t){var n=null,r=null;null!==e&&"object"==typeof e&&(n=e.ref,r=e._owner);var o=null,i=null;return null!==t&&"object"==typeof t&&(o=t.ref,i=t._owner),n!==o||"string"==typeof o&&i!==r},a.detachRefs=function(e,t){if(null!==t&&"object"==typeof t){var n=t.ref;null!=n&&o(n,e,t._owner)}},t.exports=a},{63:63}],68:[function(e,t,n){"use strict";function r(e){this.reinitializeTransaction(),this.renderToStaticMarkup=e,this.useCreateElement=!1,this.updateQueue=new s(this)}var o=e(143),i=e(24),a=e(89),s=(e(58),e(69)),u=[],l={enqueue:function(){}},c={getTransactionWrappers:function(){return u},getReactMountReady:function(){return l},getUpdateQueue:function(){return this.updateQueue},destructor:function(){},checkpoint:function(){},rollback:function(){}};o(r.prototype,a,c),i.addPoolingTo(r),t.exports=r},{143:143,24:24,58:58,69:69,89:89}],69:[function(e,t,n){"use strict";function r(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")}var o=e(70),i=(e(142),function(){function e(t){r(this,e),this.transaction=t}return e.prototype.isMounted=function(e){return!1},e.prototype.enqueueCallback=function(e,t,n){this.transaction.isInTransaction()&&o.enqueueCallback(e,t,n)},e.prototype.enqueueForceUpdate=function(e){this.transaction.isInTransaction()&&o.enqueueForceUpdate(e)},e.prototype.enqueueReplaceState=function(e,t){this.transaction.isInTransaction()&&o.enqueueReplaceState(e,t)},e.prototype.enqueueSetState=function(e,t){this.transaction.isInTransaction()&&o.enqueueSetState(e,t)},e}());t.exports=i},{142:142,70:70}],70:[function(e,t,n){"use strict";function r(e){u.enqueueUpdate(e)}function o(e){var t=typeof e;if("object"!==t)return t;var n=e.constructor&&e.constructor.name||t,r=Object.keys(e);return r.length>0&&r.length<20?n+" (keys: "+r.join(", ")+")":n}function i(e,t){var n=s.get(e);return n||null}var a=e(112),s=(e(119),e(57)),u=(e(58),e(71)),l=(e(137),e(142),{isMounted:function(e){var t=s.get(e);return!!t&&!!t._renderedComponent},enqueueCallback:function(e,t,n){l.validateCallback(t,n);var o=i(e);if(!o)return null;o._pendingCallbacks?o._pendingCallbacks.push(t):o._pendingCallbacks=[t],r(o)},enqueueCallbackInternal:function(e,t){e._pendingCallbacks?e._pendingCallbacks.push(t):e._pendingCallbacks=[t],r(e)},enqueueForceUpdate:function(e){var t=i(e,"forceUpdate");t&&(t._pendingForceUpdate=!0,r(t))},enqueueReplaceState:function(e,t,n){var o=i(e,"replaceState");o&&(o._pendingStateQueue=[t],o._pendingReplaceState=!0,void 0!==n&&null!==n&&(l.validateCallback(n,"replaceState"),o._pendingCallbacks?o._pendingCallbacks.push(n):o._pendingCallbacks=[n]),r(o))},enqueueSetState:function(e,t){var n=i(e,"setState");n&&((n._pendingStateQueue||(n._pendingStateQueue=[])).push(t),r(n))},enqueueElementInternal:function(e,t,n){e._pendingElement=t,e._context=n,r(e)},validateCallback:function(e,t){e&&"function"!=typeof e&&a("122",t,o(e))}});t.exports=l},{112:112,119:119,137:137,142:142,57:57,58:58,71:71}],71:[function(e,t,n){"use strict";function r(){P.ReactReconcileTransaction&&b||c("123")}function o(){this.reinitializeTransaction(),this.dirtyComponentsLength=null,this.callbackQueue=d.getPooled(),this.reconcileTransaction=P.ReactReconcileTransaction.getPooled(!0)}function i(e,t,n,o,i,a){return r(),b.batchedUpdates(e,t,n,o,i,a)}function a(e,t){return e._mountOrder-t._mountOrder}function s(e){var t=e.dirtyComponentsLength;t!==g.length&&c("124",t,g.length),g.sort(a),y++;for(var n=0;n<t;n++){var r=g[n],o=r._pendingCallbacks;r._pendingCallbacks=null;var i;if(h.logTopLevelRenders){var s=r;r._currentElement.type.isReactTopLevelWrapper&&(s=r._renderedComponent),i="React update: "+s.getName(),console.time(i)}if(m.performUpdateIfNecessary(r,e.reconcileTransaction,y),i&&console.timeEnd(i),o)for(var u=0;u<o.length;u++)e.callbackQueue.enqueue(o[u],r.getPublicInstance())}}function u(e){if(r(),!b.isBatchingUpdates)return void b.batchedUpdates(u,e);g.push(e),null==e._updateBatchNumber&&(e._updateBatchNumber=y+1)}function l(e,t){b.isBatchingUpdates||c("125"),_.enqueue(e,t),C=!0}var c=e(112),p=e(143),d=e(6),f=e(24),h=e(53),m=e(66),v=e(89),g=(e(137),[]),y=0,_=d.getPooled(),C=!1,b=null,E={initialize:function(){this.dirtyComponentsLength=g.length},close:function(){this.dirtyComponentsLength!==g.length?(g.splice(0,this.dirtyComponentsLength),T()):g.length=0}},x={initialize:function(){this.callbackQueue.reset()},close:function(){this.callbackQueue.notifyAll()}},w=[E,x];p(o.prototype,v,{getTransactionWrappers:function(){return w},destructor:function(){this.dirtyComponentsLength=null,d.release(this.callbackQueue),this.callbackQueue=null,P.ReactReconcileTransaction.release(this.reconcileTransaction),this.reconcileTransaction=null},perform:function(e,t,n){return v.perform.call(this,this.reconcileTransaction.perform,this.reconcileTransaction,e,t,n)}}),f.addPoolingTo(o);var T=function(){for(;g.length||C;){if(g.length){var e=o.getPooled();e.perform(s,null,e),o.release(e)}if(C){C=!1;var t=_;_=d.getPooled(),t.notifyAll(),d.release(t)}}},k={injectReconcileTransaction:function(e){e||c("126"),P.ReactReconcileTransaction=e},injectBatchingStrategy:function(e){e||c("127"),"function"!=typeof e.batchedUpdates&&c("128"),"boolean"!=typeof e.isBatchingUpdates&&c("129"),b=e}},P={ReactReconcileTransaction:null,batchedUpdates:i,enqueueUpdate:u,flushBatchedUpdates:T,injection:k,asap:l};t.exports=P},{112:112,137:137,143:143,24:24,53:53,6:6,66:66,89:89}],72:[function(e,t,n){"use strict";t.exports="15.5.4"},{}],73:[function(e,t,n){"use strict";var r={xlink:"http://www.w3.org/1999/xlink",xml:"http://www.w3.org/XML/1998/namespace"},o={accentHeight:"accent-height",accumulate:0,additive:0,alignmentBaseline:"alignment-baseline",allowReorder:"allowReorder",alphabetic:0,amplitude:0,arabicForm:"arabic-form",ascent:0,attributeName:"attributeName",attributeType:"attributeType",autoReverse:"autoReverse",azimuth:0,baseFrequency:"baseFrequency",baseProfile:"baseProfile",baselineShift:"baseline-shift",bbox:0,begin:0,bias:0,by:0,calcMode:"calcMode",capHeight:"cap-height",clip:0,clipPath:"clip-path",clipRule:"clip-rule",clipPathUnits:"clipPathUnits",colorInterpolation:"color-interpolation",colorInterpolationFilters:"color-interpolation-filters",colorProfile:"color-profile",colorRendering:"color-rendering",contentScriptType:"contentScriptType",contentStyleType:"contentStyleType",cursor:0,cx:0,cy:0,d:0,decelerate:0,descent:0,diffuseConstant:"diffuseConstant",direction:0,display:0,divisor:0,dominantBaseline:"dominant-baseline",dur:0,dx:0,dy:0,edgeMode:"edgeMode",elevation:0,enableBackground:"enable-background",end:0,exponent:0,externalResourcesRequired:"externalResourcesRequired",fill:0,fillOpacity:"fill-opacity",fillRule:"fill-rule",filter:0,filterRes:"filterRes",filterUnits:"filterUnits",floodColor:"flood-color",floodOpacity:"flood-opacity",focusable:0,fontFamily:"font-family",fontSize:"font-size",fontSizeAdjust:"font-size-adjust",fontStretch:"font-stretch",fontStyle:"font-style",fontVariant:"font-variant",fontWeight:"font-weight",format:0,from:0,fx:0,fy:0,g1:0,g2:0,glyphName:"glyph-name",glyphOrientationHorizontal:"glyph-orientation-horizontal",glyphOrientationVertical:"glyph-orientation-vertical",glyphRef:"glyphRef",gradientTransform:"gradientTransform",gradientUnits:"gradientUnits",hanging:0,horizAdvX:"horiz-adv-x",horizOriginX:"horiz-origin-x",ideographic:0,imageRendering:"image-rendering",in:0,in2:0,intercept:0,k:0,k1:0,k2:0,k3:0,k4:0,kernelMatrix:"kernelMatrix",kernelUnitLength:"kernelUnitLength",kerning:0,keyPoints:"keyPoints",keySplines:"keySplines",keyTimes:"keyTimes",lengthAdjust:"lengthAdjust",letterSpacing:"letter-spacing",lightingColor:"lighting-color",limitingConeAngle:"limitingConeAngle",local:0,markerEnd:"marker-end",markerMid:"marker-mid",markerStart:"marker-start",markerHeight:"markerHeight",markerUnits:"markerUnits",markerWidth:"markerWidth",mask:0,maskContentUnits:"maskContentUnits",maskUnits:"maskUnits",mathematical:0,mode:0,numOctaves:"numOctaves",offset:0,opacity:0,operator:0,order:0,orient:0,orientation:0,origin:0,overflow:0,overlinePosition:"overline-position",overlineThickness:"overline-thickness",paintOrder:"paint-order",panose1:"panose-1",pathLength:"pathLength",patternContentUnits:"patternContentUnits",patternTransform:"patternTransform",patternUnits:"patternUnits",pointerEvents:"pointer-events",points:0,pointsAtX:"pointsAtX",pointsAtY:"pointsAtY",pointsAtZ:"pointsAtZ",preserveAlpha:"preserveAlpha",preserveAspectRatio:"preserveAspectRatio",primitiveUnits:"primitiveUnits",r:0,radius:0,refX:"refX",refY:"refY",renderingIntent:"rendering-intent",repeatCount:"repeatCount",repeatDur:"repeatDur",requiredExtensions:"requiredExtensions",requiredFeatures:"requiredFeatures",restart:0,result:0,rotate:0,rx:0,ry:0,scale:0,seed:0,shapeRendering:"shape-rendering",slope:0,spacing:0,specularConstant:"specularConstant",specularExponent:"specularExponent",speed:0,spreadMethod:"spreadMethod",startOffset:"startOffset",stdDeviation:"stdDeviation",stemh:0,stemv:0,stitchTiles:"stitchTiles",stopColor:"stop-color",stopOpacity:"stop-opacity",strikethroughPosition:"strikethrough-position",strikethroughThickness:"strikethrough-thickness",string:0,stroke:0,strokeDasharray:"stroke-dasharray",strokeDashoffset:"stroke-dashoffset",strokeLinecap:"stroke-linecap",strokeLinejoin:"stroke-linejoin",strokeMiterlimit:"stroke-miterlimit",strokeOpacity:"stroke-opacity",strokeWidth:"stroke-width",surfaceScale:"surfaceScale",systemLanguage:"systemLanguage",tableValues:"tableValues",targetX:"targetX",targetY:"targetY",textAnchor:"text-anchor",textDecoration:"text-decoration",textRendering:"text-rendering",textLength:"textLength",to:0,transform:0,u1:0,u2:0,underlinePosition:"underline-position",underlineThickness:"underline-thickness",unicode:0,unicodeBidi:"unicode-bidi",unicodeRange:"unicode-range",unitsPerEm:"units-per-em",vAlphabetic:"v-alphabetic",vHanging:"v-hanging",vIdeographic:"v-ideographic",vMathematical:"v-mathematical",values:0,vectorEffect:"vector-effect",version:0,vertAdvY:"vert-adv-y",vertOriginX:"vert-origin-x",vertOriginY:"vert-origin-y",viewBox:"viewBox",viewTarget:"viewTarget",visibility:0,widths:0,wordSpacing:"word-spacing",writingMode:"writing-mode",x:0,xHeight:"x-height",x1:0,x2:0,xChannelSelector:"xChannelSelector",xlinkActuate:"xlink:actuate",xlinkArcrole:"xlink:arcrole",xlinkHref:"xlink:href",xlinkRole:"xlink:role",xlinkShow:"xlink:show",xlinkTitle:"xlink:title",xlinkType:"xlink:type",xmlBase:"xml:base",xmlns:0,xmlnsXlink:"xmlns:xlink",xmlLang:"xml:lang",xmlSpace:"xml:space",y:0,y1:0,y2:0,yChannelSelector:"yChannelSelector",z:0,zoomAndPan:"zoomAndPan"},i={Properties:{},DOMAttributeNamespaces:{xlinkActuate:r.xlink,xlinkArcrole:r.xlink,xlinkHref:r.xlink,xlinkRole:r.xlink,xlinkShow:r.xlink,xlinkTitle:r.xlink,xlinkType:r.xlink,xmlBase:r.xml,xmlLang:r.xml,xmlSpace:r.xml},DOMAttributeNames:{}};Object.keys(o).forEach(function(e){i.Properties[e]=0,o[e]&&(i.DOMAttributeNames[e]=o[e])}),t.exports=i},{}],74:[function(e,t,n){"use strict";function r(e){if("selectionStart"in e&&u.hasSelectionCapabilities(e))return{start:e.selectionStart,end:e.selectionEnd};if(window.getSelection){var t=window.getSelection();return{anchorNode:t.anchorNode,anchorOffset:t.anchorOffset,focusNode:t.focusNode,focusOffset:t.focusOffset}}if(document.selection){var n=document.selection.createRange();return{parentElement:n.parentElement(),text:n.text,top:n.boundingTop,left:n.boundingLeft}}}function o(e,t){if(y||null==m||m!==c())return null;var n=r(m);if(!g||!d(g,n)){g=n;var o=l.getPooled(h.select,v,e,t);return o.type="select",o.target=m,i.accumulateTwoPhaseDispatches(o),o}return null}var i=e(19),a=e(123),s=e(33),u=e(56),l=e(80),c=e(132),p=e(110),d=e(141),f=a.canUseDOM&&"documentMode"in document&&document.documentMode<=11,h={select:{phasedRegistrationNames:{bubbled:"onSelect",captured:"onSelectCapture"},dependencies:["topBlur","topContextMenu","topFocus","topKeyDown","topKeyUp","topMouseDown","topMouseUp","topSelectionChange"]}},m=null,v=null,g=null,y=!1,_=!1,C={eventTypes:h,extractEvents:function(e,t,n,r){if(!_)return null;var i=t?s.getNodeFromInstance(t):window;switch(e){case"topFocus":(p(i)||"true"===i.contentEditable)&&(m=i,v=t,g=null);break
+;case"topBlur":m=null,v=null,g=null;break;case"topMouseDown":y=!0;break;case"topContextMenu":case"topMouseUp":return y=!1,o(n,r);case"topSelectionChange":if(f)break;case"topKeyDown":case"topKeyUp":return o(n,r)}return null},didPutListener:function(e,t,n){"onSelect"===t&&(_=!0)}};t.exports=C},{110:110,123:123,132:132,141:141,19:19,33:33,56:56,80:80}],75:[function(e,t,n){"use strict";function r(e){return"."+e._rootNodeID}function o(e){return"button"===e||"input"===e||"select"===e||"textarea"===e}var i=e(112),a=e(122),s=e(19),u=e(33),l=e(76),c=e(77),p=e(80),d=e(81),f=e(83),h=e(84),m=e(79),v=e(85),g=e(86),y=e(87),_=e(88),C=e(129),b=e(99),E=(e(137),{}),x={};["abort","animationEnd","animationIteration","animationStart","blur","canPlay","canPlayThrough","click","contextMenu","copy","cut","doubleClick","drag","dragEnd","dragEnter","dragExit","dragLeave","dragOver","dragStart","drop","durationChange","emptied","encrypted","ended","error","focus","input","invalid","keyDown","keyPress","keyUp","load","loadedData","loadedMetadata","loadStart","mouseDown","mouseMove","mouseOut","mouseOver","mouseUp","paste","pause","play","playing","progress","rateChange","reset","scroll","seeked","seeking","stalled","submit","suspend","timeUpdate","touchCancel","touchEnd","touchMove","touchStart","transitionEnd","volumeChange","waiting","wheel"].forEach(function(e){var t=e[0].toUpperCase()+e.slice(1),n="on"+t,r="top"+t,o={phasedRegistrationNames:{bubbled:n,captured:n+"Capture"},dependencies:[r]};E[e]=o,x[r]=o});var w={},T={eventTypes:E,extractEvents:function(e,t,n,r){var o=x[e];if(!o)return null;var a;switch(e){case"topAbort":case"topCanPlay":case"topCanPlayThrough":case"topDurationChange":case"topEmptied":case"topEncrypted":case"topEnded":case"topError":case"topInput":case"topInvalid":case"topLoad":case"topLoadedData":case"topLoadedMetadata":case"topLoadStart":case"topPause":case"topPlay":case"topPlaying":case"topProgress":case"topRateChange":case"topReset":case"topSeeked":case"topSeeking":case"topStalled":case"topSubmit":case"topSuspend":case"topTimeUpdate":case"topVolumeChange":case"topWaiting":a=p;break;case"topKeyPress":if(0===b(n))return null;case"topKeyDown":case"topKeyUp":a=f;break;case"topBlur":case"topFocus":a=d;break;case"topClick":if(2===n.button)return null;case"topDoubleClick":case"topMouseDown":case"topMouseMove":case"topMouseUp":case"topMouseOut":case"topMouseOver":case"topContextMenu":a=h;break;case"topDrag":case"topDragEnd":case"topDragEnter":case"topDragExit":case"topDragLeave":case"topDragOver":case"topDragStart":case"topDrop":a=m;break;case"topTouchCancel":case"topTouchEnd":case"topTouchMove":case"topTouchStart":a=v;break;case"topAnimationEnd":case"topAnimationIteration":case"topAnimationStart":a=l;break;case"topTransitionEnd":a=g;break;case"topScroll":a=y;break;case"topWheel":a=_;break;case"topCopy":case"topCut":case"topPaste":a=c}a||i("86",e);var u=a.getPooled(o,t,n,r);return s.accumulateTwoPhaseDispatches(u),u},didPutListener:function(e,t,n){if("onClick"===t&&!o(e._tag)){var i=r(e),s=u.getNodeFromInstance(e);w[i]||(w[i]=a.listen(s,"click",C))}},willDeleteListener:function(e,t){if("onClick"===t&&!o(e._tag)){var n=r(e);w[n].remove(),delete w[n]}}};t.exports=T},{112:112,122:122,129:129,137:137,19:19,33:33,76:76,77:77,79:79,80:80,81:81,83:83,84:84,85:85,86:86,87:87,88:88,99:99}],76:[function(e,t,n){"use strict";function r(e,t,n,r){return o.call(this,e,t,n,r)}var o=e(80),i={animationName:null,elapsedTime:null,pseudoElement:null};o.augmentClass(r,i),t.exports=r},{80:80}],77:[function(e,t,n){"use strict";function r(e,t,n,r){return o.call(this,e,t,n,r)}var o=e(80),i={clipboardData:function(e){return"clipboardData"in e?e.clipboardData:window.clipboardData}};o.augmentClass(r,i),t.exports=r},{80:80}],78:[function(e,t,n){"use strict";function r(e,t,n,r){return o.call(this,e,t,n,r)}var o=e(80),i={data:null};o.augmentClass(r,i),t.exports=r},{80:80}],79:[function(e,t,n){"use strict";function r(e,t,n,r){return o.call(this,e,t,n,r)}var o=e(84),i={dataTransfer:null};o.augmentClass(r,i),t.exports=r},{84:84}],80:[function(e,t,n){"use strict";function r(e,t,n,r){this.dispatchConfig=e,this._targetInst=t,this.nativeEvent=n;var o=this.constructor.Interface;for(var i in o)if(o.hasOwnProperty(i)){var s=o[i];s?this[i]=s(n):"target"===i?this.target=r:this[i]=n[i]}var u=null!=n.defaultPrevented?n.defaultPrevented:!1===n.returnValue;return this.isDefaultPrevented=u?a.thatReturnsTrue:a.thatReturnsFalse,this.isPropagationStopped=a.thatReturnsFalse,this}var o=e(143),i=e(24),a=e(129),s=(e(142),["dispatchConfig","_targetInst","nativeEvent","isDefaultPrevented","isPropagationStopped","_dispatchListeners","_dispatchInstances"]),u={type:null,target:null,currentTarget:a.thatReturnsNull,eventPhase:null,bubbles:null,cancelable:null,timeStamp:function(e){return e.timeStamp||Date.now()},defaultPrevented:null,isTrusted:null};o(r.prototype,{preventDefault:function(){this.defaultPrevented=!0;var e=this.nativeEvent;e&&(e.preventDefault?e.preventDefault():"unknown"!=typeof e.returnValue&&(e.returnValue=!1),this.isDefaultPrevented=a.thatReturnsTrue)},stopPropagation:function(){var e=this.nativeEvent;e&&(e.stopPropagation?e.stopPropagation():"unknown"!=typeof e.cancelBubble&&(e.cancelBubble=!0),this.isPropagationStopped=a.thatReturnsTrue)},persist:function(){this.isPersistent=a.thatReturnsTrue},isPersistent:a.thatReturnsFalse,destructor:function(){var e=this.constructor.Interface;for(var t in e)this[t]=null;for(var n=0;n<s.length;n++)this[s[n]]=null}}),r.Interface=u,r.augmentClass=function(e,t){var n=this,r=function(){};r.prototype=n.prototype;var a=new r;o(a,e.prototype),e.prototype=a,e.prototype.constructor=e,e.Interface=o({},n.Interface,t),e.augmentClass=n.augmentClass,i.addPoolingTo(e,i.fourArgumentPooler)},i.addPoolingTo(r,i.fourArgumentPooler),t.exports=r},{129:129,142:142,143:143,24:24}],81:[function(e,t,n){"use strict";function r(e,t,n,r){return o.call(this,e,t,n,r)}var o=e(87),i={relatedTarget:null};o.augmentClass(r,i),t.exports=r},{87:87}],82:[function(e,t,n){"use strict";function r(e,t,n,r){return o.call(this,e,t,n,r)}var o=e(80),i={data:null};o.augmentClass(r,i),t.exports=r},{80:80}],83:[function(e,t,n){"use strict";function r(e,t,n,r){return o.call(this,e,t,n,r)}var o=e(87),i=e(99),a=e(100),s=e(101),u={key:a,location:null,ctrlKey:null,shiftKey:null,altKey:null,metaKey:null,repeat:null,locale:null,getModifierState:s,charCode:function(e){return"keypress"===e.type?i(e):0},keyCode:function(e){return"keydown"===e.type||"keyup"===e.type?e.keyCode:0},which:function(e){return"keypress"===e.type?i(e):"keydown"===e.type||"keyup"===e.type?e.keyCode:0}};o.augmentClass(r,u),t.exports=r},{100:100,101:101,87:87,99:99}],84:[function(e,t,n){"use strict";function r(e,t,n,r){return o.call(this,e,t,n,r)}var o=e(87),i=e(90),a=e(101),s={screenX:null,screenY:null,clientX:null,clientY:null,ctrlKey:null,shiftKey:null,altKey:null,metaKey:null,getModifierState:a,button:function(e){var t=e.button;return"which"in e?t:2===t?2:4===t?1:0},buttons:null,relatedTarget:function(e){return e.relatedTarget||(e.fromElement===e.srcElement?e.toElement:e.fromElement)},pageX:function(e){return"pageX"in e?e.pageX:e.clientX+i.currentScrollLeft},pageY:function(e){return"pageY"in e?e.pageY:e.clientY+i.currentScrollTop}};o.augmentClass(r,s),t.exports=r},{101:101,87:87,90:90}],85:[function(e,t,n){"use strict";function r(e,t,n,r){return o.call(this,e,t,n,r)}var o=e(87),i=e(101),a={touches:null,targetTouches:null,changedTouches:null,altKey:null,metaKey:null,ctrlKey:null,shiftKey:null,getModifierState:i};o.augmentClass(r,a),t.exports=r},{101:101,87:87}],86:[function(e,t,n){"use strict";function r(e,t,n,r){return o.call(this,e,t,n,r)}var o=e(80),i={propertyName:null,elapsedTime:null,pseudoElement:null};o.augmentClass(r,i),t.exports=r},{80:80}],87:[function(e,t,n){"use strict";function r(e,t,n,r){return o.call(this,e,t,n,r)}var o=e(80),i=e(102),a={view:function(e){if(e.view)return e.view;var t=i(e);if(t.window===t)return t;var n=t.ownerDocument;return n?n.defaultView||n.parentWindow:window},detail:function(e){return e.detail||0}};o.augmentClass(r,a),t.exports=r},{102:102,80:80}],88:[function(e,t,n){"use strict";function r(e,t,n,r){return o.call(this,e,t,n,r)}var o=e(84),i={deltaX:function(e){return"deltaX"in e?e.deltaX:"wheelDeltaX"in e?-e.wheelDeltaX:0},deltaY:function(e){return"deltaY"in e?e.deltaY:"wheelDeltaY"in e?-e.wheelDeltaY:"wheelDelta"in e?-e.wheelDelta:0},deltaZ:null,deltaMode:null};o.augmentClass(r,i),t.exports=r},{84:84}],89:[function(e,t,n){"use strict";var r=e(112),o=(e(137),{}),i={reinitializeTransaction:function(){this.transactionWrappers=this.getTransactionWrappers(),this.wrapperInitData?this.wrapperInitData.length=0:this.wrapperInitData=[],this._isInTransaction=!1},_isInTransaction:!1,getTransactionWrappers:null,isInTransaction:function(){return!!this._isInTransaction},perform:function(e,t,n,o,i,a,s,u){this.isInTransaction()&&r("27");var l,c;try{this._isInTransaction=!0,l=!0,this.initializeAll(0),c=e.call(t,n,o,i,a,s,u),l=!1}finally{try{if(l)try{this.closeAll(0)}catch(e){}else this.closeAll(0)}finally{this._isInTransaction=!1}}return c},initializeAll:function(e){for(var t=this.transactionWrappers,n=e;n<t.length;n++){var r=t[n];try{this.wrapperInitData[n]=o,this.wrapperInitData[n]=r.initialize?r.initialize.call(this):null}finally{if(this.wrapperInitData[n]===o)try{this.initializeAll(n+1)}catch(e){}}}},closeAll:function(e){this.isInTransaction()||r("28");for(var t=this.transactionWrappers,n=e;n<t.length;n++){var i,a=t[n],s=this.wrapperInitData[n];try{i=!0,s!==o&&a.close&&a.close.call(this,s),i=!1}finally{if(i)try{this.closeAll(n+1)}catch(e){}}}this.wrapperInitData.length=0}};t.exports=i},{112:112,137:137}],90:[function(e,t,n){"use strict";var r={currentScrollLeft:0,currentScrollTop:0,refreshScrollValues:function(e){r.currentScrollLeft=e.x,r.currentScrollTop=e.y}};t.exports=r},{}],91:[function(e,t,n){"use strict";function r(e,t){return null==t&&o("30"),null==e?t:Array.isArray(e)?Array.isArray(t)?(e.push.apply(e,t),e):(e.push(t),e):Array.isArray(t)?[e].concat(t):[e,t]}var o=e(112);e(137);t.exports=r},{112:112,137:137}],92:[function(e,t,n){"use strict";function r(e){for(var t=1,n=0,r=0,i=e.length,a=-4&i;r<a;){for(var s=Math.min(r+4096,a);r<s;r+=4)n+=(t+=e.charCodeAt(r))+(t+=e.charCodeAt(r+1))+(t+=e.charCodeAt(r+2))+(t+=e.charCodeAt(r+3));t%=o,n%=o}for(;r<i;r++)n+=t+=e.charCodeAt(r);return t%=o,n%=o,t|n<<16}var o=65521;t.exports=r},{}],93:[function(e,t,n){"use strict";var r=function(e){return"undefined"!=typeof MSApp&&MSApp.execUnsafeLocalFunction?function(t,n,r,o){MSApp.execUnsafeLocalFunction(function(){return e(t,n,r,o)})}:e};t.exports=r},{}],94:[function(e,t,n){"use strict";function r(e,t,n){return null==t||"boolean"==typeof t||""===t?"":isNaN(t)||0===t||i.hasOwnProperty(e)&&i[e]?""+t:("string"==typeof t&&(t=t.trim()),t+"px")}var o=e(4),i=(e(142),o.isUnitlessNumber);t.exports=r},{142:142,4:4}],95:[function(e,t,n){"use strict";function r(e){var t=""+e,n=i.exec(t);if(!n)return t;var r,o="",a=0,s=0;for(a=n.index;a<t.length;a++){switch(t.charCodeAt(a)){case 34:r="&quot;";break;case 38:r="&amp;";break;case 39:r="&#x27;";break;case 60:r="&lt;";break;case 62:r="&gt;";break;default:continue}s!==a&&(o+=t.substring(s,a)),s=a+1,o+=r}return s!==a?o+t.substring(s,a):o}function o(e){return"boolean"==typeof e||"number"==typeof e?""+e:r(e)}var i=/["'&<>]/;t.exports=o},{}],96:[function(e,t,n){"use strict";function r(e){if(null==e)return null;if(1===e.nodeType)return e;var t=a.get(e);if(t)return t=s(t),t?i.getNodeFromInstance(t):null;"function"==typeof e.render?o("44"):o("45",Object.keys(e))}var o=e(112),i=(e(119),e(33)),a=e(57),s=e(103);e(137),e(142);t.exports=r},{103:103,112:112,119:119,137:137,142:142,33:33,57:57}],97:[function(e,t,n){(function(n){"use strict";function r(e,t,n,r){if(e&&"object"==typeof e){var o=e;void 0===o[n]&&null!=t&&(o[n]=t)}}function o(e,t){if(null==e)return e;var n={};return i(e,r,n),n}var i=(e(22),e(117));e(142);void 0!==n&&n.env,t.exports=o}).call(this,void 0)},{117:117,142:142,22:22}],98:[function(e,t,n){"use strict";function r(e,t,n){Array.isArray(e)?e.forEach(t,n):e&&t.call(n,e)}t.exports=r},{}],99:[function(e,t,n){"use strict";function r(e){var t,n=e.keyCode;return"charCode"in e?0===(t=e.charCode)&&13===n&&(t=13):t=n,t>=32||13===t?t:0}t.exports=r},{}],100:[function(e,t,n){"use strict";function r(e){if(e.key){var t=i[e.key]||e.key;if("Unidentified"!==t)return t}if("keypress"===e.type){var n=o(e);return 13===n?"Enter":String.fromCharCode(n)}return"keydown"===e.type||"keyup"===e.type?a[e.keyCode]||"Unidentified":""}var o=e(99),i={Esc:"Escape",Spacebar:" ",Left:"ArrowLeft",Up:"ArrowUp",Right:"ArrowRight",Down:"ArrowDown",Del:"Delete",Win:"OS",Menu:"ContextMenu",Apps:"ContextMenu",Scroll:"ScrollLock",MozPrintableKey:"Unidentified"},a={8:"Backspace",9:"Tab",12:"Clear",13:"Enter",16:"Shift",17:"Control",18:"Alt",19:"Pause",20:"CapsLock",27:"Escape",32:" ",33:"PageUp",34:"PageDown",35:"End",36:"Home",37:"ArrowLeft",38:"ArrowUp",39:"ArrowRight",40:"ArrowDown",45:"Insert",46:"Delete",112:"F1",113:"F2",114:"F3",115:"F4",116:"F5",117:"F6",118:"F7",119:"F8",120:"F9",121:"F10",122:"F11",123:"F12",144:"NumLock",145:"ScrollLock",224:"Meta"};t.exports=r},{99:99}],101:[function(e,t,n){"use strict";function r(e){var t=this,n=t.nativeEvent;if(n.getModifierState)return n.getModifierState(e);var r=i[e];return!!r&&!!n[r]}function o(e){return r}var i={Alt:"altKey",Control:"ctrlKey",Meta:"metaKey",Shift:"shiftKey"};t.exports=o},{}],102:[function(e,t,n){"use strict";function r(e){var t=e.target||e.srcElement||window;return t.correspondingUseElement&&(t=t.correspondingUseElement),3===t.nodeType?t.parentNode:t}t.exports=r},{}],103:[function(e,t,n){"use strict";function r(e){for(var t;(t=e._renderedNodeType)===o.COMPOSITE;)e=e._renderedComponent;return t===o.HOST?e._renderedComponent:t===o.EMPTY?null:void 0}var o=e(62);t.exports=r},{62:62}],104:[function(e,t,n){"use strict";function r(e){var t=e&&(o&&e[o]||e[i]);if("function"==typeof t)return t}var o="function"==typeof Symbol&&Symbol.iterator,i="@@iterator";t.exports=r},{}],105:[function(e,t,n){"use strict";function r(e){for(;e&&e.firstChild;)e=e.firstChild;return e}function o(e){for(;e;){if(e.nextSibling)return e.nextSibling;e=e.parentNode}}function i(e,t){for(var n=r(e),i=0,a=0;n;){if(3===n.nodeType){if(a=i+n.textContent.length,i<=t&&a>=t)return{node:n,offset:t-i};i=a}n=r(o(n))}}t.exports=i},{}],106:[function(e,t,n){"use strict";function r(){return!i&&o.canUseDOM&&(i="textContent"in document.documentElement?"textContent":"innerText"),i}var o=e(123),i=null;t.exports=r},{123:123}],107:[function(e,t,n){"use strict";function r(e,t){var n={};return n[e.toLowerCase()]=t.toLowerCase(),n["Webkit"+e]="webkit"+t,n["Moz"+e]="moz"+t,n["ms"+e]="MS"+t,n["O"+e]="o"+t.toLowerCase(),n}function o(e){if(s[e])return s[e];if(!a[e])return e;var t=a[e];for(var n in t)if(t.hasOwnProperty(n)&&n in u)return s[e]=t[n];return""}var i=e(123),a={animationend:r("Animation","AnimationEnd"),animationiteration:r("Animation","AnimationIteration"),animationstart:r("Animation","AnimationStart"),transitionend:r("Transition","TransitionEnd")},s={},u={};i.canUseDOM&&(u=document.createElement("div").style,"AnimationEvent"in window||(delete a.animationend.animation,delete a.animationiteration.animation,delete a.animationstart.animation),"TransitionEvent"in window||delete a.transitionend.transition),t.exports=o},{123:123}],108:[function(e,t,n){"use strict";function r(e){if(e){var t=e.getName();if(t)return" Check the render method of `"+t+"`."}return""}function o(e){return"function"==typeof e&&void 0!==e.prototype&&"function"==typeof e.prototype.mountComponent&&"function"==typeof e.prototype.receiveComponent}function i(e,t){var n;if(null===e||!1===e)n=l.create(i);else if("object"==typeof e){var s=e,u=s.type;if("function"!=typeof u&&"string"!=typeof u){var d="";d+=r(s._owner),a("130",null==u?u:typeof u,d)}"string"==typeof s.type?n=c.createInternalComponent(s):o(s.type)?(n=new s.type(s),n.getHostNode||(n.getHostNode=n.getNativeNode)):n=new p(s)}else"string"==typeof e||"number"==typeof e?n=c.createInstanceForText(e):a("131",typeof e);return n._mountIndex=0,n._mountImage=null,n}var a=e(112),s=e(143),u=e(29),l=e(49),c=e(54),p=(e(121),e(137),e(142),function(e){this.construct(e)});s(p.prototype,u,{_instantiateReactComponent:i}),t.exports=i},{112:112,121:121,137:137,142:142,143:143,29:29,49:49,54:54}],109:[function(e,t,n){"use strict";function r(e,t){if(!i.canUseDOM||t&&!("addEventListener"in document))return!1;var n="on"+e,r=n in document;if(!r){var a=document.createElement("div");a.setAttribute(n,"return;"),r="function"==typeof a[n]}return!r&&o&&"wheel"===e&&(r=document.implementation.hasFeature("Events.wheel","3.0")),r}var o,i=e(123);i.canUseDOM&&(o=document.implementation&&document.implementation.hasFeature&&!0!==document.implementation.hasFeature("","")),t.exports=r},{123:123}],110:[function(e,t,n){"use strict";function r(e){var t=e&&e.nodeName&&e.nodeName.toLowerCase();return"input"===t?!!o[e.type]:"textarea"===t}var o={color:!0,date:!0,datetime:!0,"datetime-local":!0,email:!0,month:!0,number:!0,password:!0,range:!0,search:!0,tel:!0,text:!0,time:!0,url:!0,week:!0};t.exports=r},{}],111:[function(e,t,n){"use strict";function r(e){return'"'+o(e)+'"'}var o=e(95);t.exports=r},{95:95}],112:[function(e,t,n){"use strict";function r(e){for(var t=arguments.length-1,n="Minified React error #"+e+"; visit http://facebook.github.io/react/docs/error-decoder.html?invariant="+e,r=0;r<t;r++)n+="&args[]="+encodeURIComponent(arguments[r+1]);n+=" for the full message or use the non-minified dev environment for full errors and additional helpful warnings.";var o=new Error(n);throw o.name="Invariant Violation",o.framesToPop=1,o}t.exports=r},{}],113:[function(e,t,n){"use strict";var r=e(60);t.exports=r.renderSubtreeIntoContainer},{60:60}],114:[function(e,t,n){"use strict";var r,o=e(123),i=e(10),a=/^[ \r\n\t\f]/,s=/<(!--|link|noscript|meta|script|style)[ \r\n\t\f\/>]/,u=e(93),l=u(function(e,t){if(e.namespaceURI!==i.svg||"innerHTML"in e)e.innerHTML=t;else{r=r||document.createElement("div"),r.innerHTML="<svg>"+t+"</svg>";for(var n=r.firstChild;n.firstChild;)e.appendChild(n.firstChild)}});if(o.canUseDOM){var c=document.createElement("div");c.innerHTML=" ",""===c.innerHTML&&(l=function(e,t){if(e.parentNode&&e.parentNode.replaceChild(e,e),a.test(t)||"<"===t[0]&&s.test(t)){e.innerHTML=String.fromCharCode(65279)+t;var n=e.firstChild;1===n.data.length?e.removeChild(n):n.deleteData(0,1)}else e.innerHTML=t}),c=null}t.exports=l},{10:10,123:123,93:93}],115:[function(e,t,n){"use strict";var r=e(123),o=e(95),i=e(114),a=function(e,t){if(t){var n=e.firstChild;if(n&&n===e.lastChild&&3===n.nodeType)return void(n.nodeValue=t)}e.textContent=t};r.canUseDOM&&("textContent"in document.documentElement||(a=function(e,t){if(3===e.nodeType)return void(e.nodeValue=t);i(e,o(t))})),t.exports=a},{114:114,123:123,95:95}],116:[function(e,t,n){"use strict";function r(e,t){var n=null===e||!1===e,r=null===t||!1===t;if(n||r)return n===r;var o=typeof e,i=typeof t;return"string"===o||"number"===o?"string"===i||"number"===i:"object"===i&&e.type===t.type&&e.key===t.key}t.exports=r},{}],117:[function(e,t,n){"use strict";function r(e,t){return e&&"object"==typeof e&&null!=e.key?l.escape(e.key):t.toString(36)}function o(e,t,n,i){var d=typeof e;if("undefined"!==d&&"boolean"!==d||(e=null),null===e||"string"===d||"number"===d||"object"===d&&e.$$typeof===s)return n(i,e,""===t?c+r(e,0):t),1;var f,h,m=0,v=""===t?c:t+p;if(Array.isArray(e))for(var g=0;g<e.length;g++)f=e[g],h=v+r(f,g),m+=o(f,h,n,i);else{var y=u(e);if(y){var _,C=y.call(e);if(y!==e.entries)for(var b=0;!(_=C.next()).done;)f=_.value,h=v+r(f,b++),m+=o(f,h,n,i);else for(;!(_=C.next()).done;){var E=_.value;E&&(f=E[1],h=v+l.escape(E[0])+p+r(f,0),m+=o(f,h,n,i))}}else if("object"===d){var x=String(e);a("31","[object Object]"===x?"object with keys {"+Object.keys(e).join(", ")+"}":x,"")}}return m}function i(e,t,n){return null==e?0:o(e,"",t,n)}var a=e(112),s=(e(119),e(48)),u=e(104),l=(e(137),e(22)),c=(e(142),"."),p=":";t.exports=i},{104:104,112:112,119:119,137:137,142:142,22:22,48:48}],118:[function(e,t,n){"use strict";var r=(e(143),e(129)),o=(e(142),r);t.exports=o},{129:129,142:142,143:143}],119:[function(t,n,r){"use strict";var o=e.__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED;n.exports=o.ReactCurrentOwner},{}],120:[function(t,n,r){"use strict";n.exports=e},{}],121:[function(t,n,r){"use strict";var o=e.__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED;n.exports=o.getNextDebugID},{}],122:[function(e,t,n){"use strict";var r=e(129),o={listen:function(e,t,n){return e.addEventListener?(e.addEventListener(t,n,!1),{remove:function(){e.removeEventListener(t,n,!1)}}):e.attachEvent?(e.attachEvent("on"+t,n),{remove:function(){e.detachEvent("on"+t,n)}}):void 0},capture:function(e,t,n){return e.addEventListener?(e.addEventListener(t,n,!0),{remove:function(){e.removeEventListener(t,n,!0)}}):{remove:r}},registerDefault:function(){}};t.exports=o},{129:129}],123:[function(e,t,n){"use strict";var r=!("undefined"==typeof window||!window.document||!window.document.createElement),o={canUseDOM:r,canUseWorkers:"undefined"!=typeof Worker,canUseEventListeners:r&&!(!window.addEventListener&&!window.attachEvent),canUseViewport:r&&!!window.screen,isInWorker:!r};t.exports=o},{}],124:[function(e,t,n){"use strict";function r(e){return e.replace(o,function(e,t){return t.toUpperCase()})}var o=/-(.)/g;t.exports=r},{}],125:[function(e,t,n){"use strict";function r(e){return o(e.replace(i,"ms-"))}var o=e(124),i=/^-ms-/;t.exports=r},{124:124}],126:[function(e,t,n){"use strict";function r(e,t){return!(!e||!t)&&(e===t||!o(e)&&(o(t)?r(e,t.parentNode):"contains"in e?e.contains(t):!!e.compareDocumentPosition&&!!(16&e.compareDocumentPosition(t))))}var o=e(139);t.exports=r},{139:139}],127:[function(e,t,n){"use strict";function r(e){var t=e.length;if((Array.isArray(e)||"object"!=typeof e&&"function"!=typeof e)&&a(!1),"number"!=typeof t&&a(!1),0===t||t-1 in e||a(!1),"function"==typeof e.callee&&a(!1),e.hasOwnProperty)try{return Array.prototype.slice.call(e)}catch(e){}for(var n=Array(t),r=0;r<t;r++)n[r]=e[r];return n}function o(e){return!!e&&("object"==typeof e||"function"==typeof e)&&"length"in e&&!("setInterval"in e)&&"number"!=typeof e.nodeType&&(Array.isArray(e)||"callee"in e||"item"in e)}function i(e){return o(e)?Array.isArray(e)?e.slice():r(e):[e]}var a=e(137);t.exports=i},{137:137}],128:[function(e,t,n){"use strict";function r(e){var t=e.match(c);return t&&t[1].toLowerCase()}function o(e,t){var n=l;l||u(!1);var o=r(e),i=o&&s(o);if(i){n.innerHTML=i[1]+e+i[2];for(var c=i[0];c--;)n=n.lastChild}else n.innerHTML=e;var p=n.getElementsByTagName("script");p.length&&(t||u(!1),a(p).forEach(t));for(var d=Array.from(n.childNodes);n.lastChild;)n.removeChild(n.lastChild);return d}var i=e(123),a=e(127),s=e(133),u=e(137),l=i.canUseDOM?document.createElement("div"):null,c=/^\s*<(\w+)/;t.exports=o},{123:123,127:127,133:133,137:137}],129:[function(e,t,n){"use strict";function r(e){return function(){return e}}var o=function(){};o.thatReturns=r,o.thatReturnsFalse=r(!1),o.thatReturnsTrue=r(!0),o.thatReturnsNull=r(null),o.thatReturnsThis=function(){return this},o.thatReturnsArgument=function(e){return e},t.exports=o},{}],130:[function(e,t,n){"use strict";var r={};t.exports=r},{}],131:[function(e,t,n){"use strict";function r(e){try{e.focus()}catch(e){}}t.exports=r},{}],132:[function(e,t,n){"use strict";function r(e){if(void 0===(e=e||("undefined"!=typeof document?document:void 0)))return null;try{return e.activeElement||e.body}catch(t){return e.body}}t.exports=r},{}],133:[function(e,t,n){"use strict";function r(e){return a||i(!1),d.hasOwnProperty(e)||(e="*"),s.hasOwnProperty(e)||(a.innerHTML="*"===e?"<link />":"<"+e+"></"+e+">",s[e]=!a.firstChild),s[e]?d[e]:null}var o=e(123),i=e(137),a=o.canUseDOM?document.createElement("div"):null,s={},u=[1,'<select multiple="true">',"</select>"],l=[1,"<table>","</table>"],c=[3,"<table><tbody><tr>","</tr></tbody></table>"],p=[1,'<svg xmlns="http://www.w3.org/2000/svg">',"</svg>"],d={"*":[1,"?<div>","</div>"],area:[1,"<map>","</map>"],col:[2,"<table><tbody></tbody><colgroup>","</colgroup></table>"],legend:[1,"<fieldset>","</fieldset>"],param:[1,"<object>","</object>"],tr:[2,"<table><tbody>","</tbody></table>"],optgroup:u,option:u,caption:l,colgroup:l,tbody:l,tfoot:l,thead:l,td:c,th:c};["circle","clipPath","defs","ellipse","g","image","line","linearGradient","mask","path","pattern","polygon","polyline","radialGradient","rect","stop","text","tspan"].forEach(function(e){d[e]=p,s[e]=!0}),t.exports=r},{123:123,137:137}],134:[function(e,t,n){"use strict";function r(e){return e.Window&&e instanceof e.Window?{x:e.pageXOffset||e.document.documentElement.scrollLeft,y:e.pageYOffset||e.document.documentElement.scrollTop}:{x:e.scrollLeft,y:e.scrollTop}}t.exports=r},{}],135:[function(e,t,n){"use strict";function r(e){return e.replace(o,"-$1").toLowerCase()}var o=/([A-Z])/g;t.exports=r},{}],136:[function(e,t,n){"use strict";function r(e){return o(e).replace(i,"-ms-")}var o=e(135),i=/^ms-/;t.exports=r},{135:135}],137:[function(e,t,n){"use strict";function r(e,t,n,r,i,a,s,u){if(o(t),!e){var l;if(void 0===t)l=new Error("Minified exception occurred; use the non-minified dev environment for the full error message and additional helpful warnings.");else{var c=[n,r,i,a,s,u],p=0;l=new Error(t.replace(/%s/g,function(){return c[p++]})),l.name="Invariant Violation"}throw l.framesToPop=1,l}}var o=function(e){};t.exports=r},{}],138:[function(e,t,n){"use strict";function r(e){var t=e?e.ownerDocument||e:document,n=t.defaultView||window;return!(!e||!("function"==typeof n.Node?e instanceof n.Node:"object"==typeof e&&"number"==typeof e.nodeType&&"string"==typeof e.nodeName))}t.exports=r},{}],139:[function(e,t,n){"use strict";function r(e){return o(e)&&3==e.nodeType}var o=e(138);t.exports=r},{138:138}],140:[function(e,t,n){"use strict";function r(e){var t={};return function(n){return t.hasOwnProperty(n)||(t[n]=e.call(this,n)),t[n]}}t.exports=r},{}],141:[function(e,t,n){"use strict";function r(e,t){return e===t?0!==e||0!==t||1/e==1/t:e!==e&&t!==t}function o(e,t){if(r(e,t))return!0;if("object"!=typeof e||null===e||"object"!=typeof t||null===t)return!1;var n=Object.keys(e),o=Object.keys(t);if(n.length!==o.length)return!1;for(var a=0;a<n.length;a++)if(!i.call(t,n[a])||!r(e[n[a]],t[n[a]]))return!1;return!0}var i=Object.prototype.hasOwnProperty;t.exports=o},{}],142:[function(e,t,n){"use strict";var r=e(129),o=r;t.exports=o},{129:129}],143:[function(e,t,n){"use strict";function r(e){if(null===e||void 0===e)throw new TypeError("Object.assign cannot be called with null or undefined");return Object(e)}var o=Object.getOwnPropertySymbols,i=Object.prototype.hasOwnProperty,a=Object.prototype.propertyIsEnumerable;t.exports=function(){try{if(!Object.assign)return!1;var e=new String("abc");if(e[5]="de","5"===Object.getOwnPropertyNames(e)[0])return!1;for(var t={},n=0;n<10;n++)t["_"+String.fromCharCode(n)]=n;if("0123456789"!==Object.getOwnPropertyNames(t).map(function(e){return t[e]}).join(""))return!1;var r={};return"abcdefghijklmnopqrst".split("").forEach(function(e){r[e]=e}),"abcdefghijklmnopqrst"===Object.keys(Object.assign({},r)).join("")}catch(e){return!1}}()?Object.assign:function(e,t){for(var n,s,u=r(e),l=1;l<arguments.length;l++){n=Object(arguments[l]);for(var c in n)i.call(n,c)&&(u[c]=n[c]);if(o){s=o(n);for(var p=0;p<s.length;p++)a.call(n,s[p])&&(u[s[p]]=n[s[p]])}}return u}},{}],144:[function(e,t,n){"use strict";function r(e,t,n,r,o){}t.exports=r},{137:137,142:142,147:147}],145:[function(e,t,n){"use strict";var r=e(146);t.exports=function(e){return r(e,!1)}},{146:146}],146:[function(e,t,n){"use strict";var r=e(129),o=e(137),i=(e(142),e(147)),a=e(144);t.exports=function(e,t){function n(e){var t=e&&(E&&e[E]||e[x]);if("function"==typeof t)return t}function s(e,t){return e===t?0!==e||1/e==1/t:e!==e&&t!==t}function u(e){this.message=e,this.stack=""}function l(e){function n(n,r,a,s,l,c,p){if(s=s||w,c=c||a,p!==i)if(t)o(!1,"Calling PropTypes validators directly is not supported by the `prop-types` package. Use `PropTypes.checkPropTypes()` to call them. Read more at http://fb.me/use-check-prop-types");else;return null==r[a]?n?new u(null===r[a]?"The "+l+" `"+c+"` is marked as required in `"+s+"`, but its value is `null`.":"The "+l+" `"+c+"` is marked as required in `"+s+"`, but its value is `undefined`."):null:e(r,a,s,l,c)}var r=n.bind(null,!1);return r.isRequired=n.bind(null,!0),r}function c(e){function t(t,n,r,o,i,a){var s=t[n];if(_(s)!==e)return new u("Invalid "+o+" `"+i+"` of type `"+C(s)+"` supplied to `"+r+"`, expected `"+e+"`.");return null}return l(t)}function p(e){function t(t,n,r,o,a){if("function"!=typeof e)return new u("Property `"+a+"` of component `"+r+"` has invalid PropType notation inside arrayOf.");var s=t[n];if(!Array.isArray(s)){return new u("Invalid "+o+" `"+a+"` of type `"+_(s)+"` supplied to `"+r+"`, expected an array.")}for(var l=0;l<s.length;l++){var c=e(s,l,r,o,a+"["+l+"]",i);if(c instanceof Error)return c}return null}return l(t)}function d(e){function t(t,n,r,o,i){if(!(t[n]instanceof e)){var a=e.name||w;return new u("Invalid "+o+" `"+i+"` of type `"+b(t[n])+"` supplied to `"+r+"`, expected instance of `"+a+"`.")}return null}return l(t)}function f(e){function t(t,n,r,o,i){for(var a=t[n],l=0;l<e.length;l++)if(s(a,e[l]))return null;return new u("Invalid "+o+" `"+i+"` of value `"+a+"` supplied to `"+r+"`, expected one of "+JSON.stringify(e)+".")}return Array.isArray(e)?l(t):r.thatReturnsNull}function h(e){function t(t,n,r,o,a){if("function"!=typeof e)return new u("Property `"+a+"` of component `"+r+"` has invalid PropType notation inside objectOf.");var s=t[n],l=_(s);if("object"!==l)return new u("Invalid "+o+" `"+a+"` of type `"+l+"` supplied to `"+r+"`, expected an object.");for(var c in s)if(s.hasOwnProperty(c)){var p=e(s,c,r,o,a+"."+c,i);if(p instanceof Error)return p}return null}return l(t)}function m(e){function t(t,n,r,o,a){for(var s=0;s<e.length;s++){if(null==(0,e[s])(t,n,r,o,a,i))return null}return new u("Invalid "+o+" `"+a+"` supplied to `"+r+"`.")}return Array.isArray(e)?l(t):r.thatReturnsNull}function v(e){function t(t,n,r,o,a){var s=t[n],l=_(s);if("object"!==l)return new u("Invalid "+o+" `"+a+"` of type `"+l+"` supplied to `"+r+"`, expected `object`.");for(var c in e){var p=e[c];if(p){var d=p(s,c,r,o,a+"."+c,i);if(d)return d}}return null}return l(t)}function g(t){switch(typeof t){case"number":case"string":case"undefined":return!0;case"boolean":return!t;case"object":if(Array.isArray(t))return t.every(g);if(null===t||e(t))return!0;var r=n(t);if(!r)return!1;var o,i=r.call(t);if(r!==t.entries){for(;!(o=i.next()).done;)if(!g(o.value))return!1}else for(;!(o=i.next()).done;){var a=o.value;if(a&&!g(a[1]))return!1}return!0;default:return!1}}function y(e,t){return"symbol"===e||("Symbol"===t["@@toStringTag"]||"function"==typeof Symbol&&t instanceof Symbol)}function _(e){var t=typeof e;return Array.isArray(e)?"array":e instanceof RegExp?"object":y(t,e)?"symbol":t}function C(e){var t=_(e);if("object"===t){if(e instanceof Date)return"date";if(e instanceof RegExp)return"regexp"}return t}function b(e){return e.constructor&&e.constructor.name?e.constructor.name:w}var E="function"==typeof Symbol&&Symbol.iterator,x="@@iterator",w="<<anonymous>>",T={array:c("array"),bool:c("boolean"),func:c("function"),number:c("number"),object:c("object"),string:c("string"),symbol:c("symbol"),any:function(){return l(r.thatReturnsNull)}(),arrayOf:p,element:function(){function t(t,n,r,o,i){var a=t[n];if(!e(a)){return new u("Invalid "+o+" `"+i+"` of type `"+_(a)+"` supplied to `"+r+"`, expected a single ReactElement.")}return null}return l(t)}(),instanceOf:d,node:function(){function e(e,t,n,r,o){return g(e[t])?null:new u("Invalid "+r+" `"+o+"` supplied to `"+n+"`, expected a ReactNode.")}return l(e)}(),objectOf:h,oneOf:f,oneOfType:m,shape:v}
+;return u.prototype=Error.prototype,T.checkPropTypes=a,T.PropTypes=T,T}},{129:129,137:137,142:142,144:144,147:147}],147:[function(e,t,n){"use strict";t.exports="SECRET_DO_NOT_PASS_THIS_OR_YOU_WILL_BE_FIRED"},{}]},{},[45])(45)}()}()});
+/*! http://mths.be/fromcodepoint v0.1.0 by @mathias */
+if (!String.fromCodePoint) {
+ (function() {
+ var defineProperty = (function() {
+ // IE 8 only supports `Object.defineProperty` on DOM elements
+ try {
+ var object = {};
+ var $defineProperty = Object.defineProperty;
+ var result = $defineProperty(object, object, object) && $defineProperty;
+ } catch(error) {}
+ return result;
+ }());
+ var stringFromCharCode = String.fromCharCode;
+ var floor = Math.floor;
+ var fromCodePoint = function() {
+ var MAX_SIZE = 0x4000;
+ var codeUnits = [];
+ var highSurrogate;
+ var lowSurrogate;
+ var index = -1;
+ var length = arguments.length;
+ if (!length) {
+ return '';
+ }
+ var result = '';
+ while (++index < length) {
+ var codePoint = Number(arguments[index]);
+ if (
+ !isFinite(codePoint) || // `NaN`, `+Infinity`, or `-Infinity`
+ codePoint < 0 || // not a valid Unicode code point
+ codePoint > 0x10FFFF || // not a valid Unicode code point
+ floor(codePoint) != codePoint // not an integer
+ ) {
+ throw RangeError('Invalid code point: ' + codePoint);
+ }
+ if (codePoint <= 0xFFFF) { // BMP code point
+ codeUnits.push(codePoint);
+ } else { // Astral code point; split in surrogate halves
+ // http://mathiasbynens.be/notes/javascript-encoding#surrogate-formulae
+ codePoint -= 0x10000;
+ highSurrogate = (codePoint >> 10) + 0xD800;
+ lowSurrogate = (codePoint % 0x400) + 0xDC00;
+ codeUnits.push(highSurrogate, lowSurrogate);
+ }
+ if (index + 1 == length || codeUnits.length > MAX_SIZE) {
+ result += stringFromCharCode.apply(null, codeUnits);
+ codeUnits.length = 0;
+ }
+ }
+ return result;
+ };
+ if (defineProperty) {
+ defineProperty(String, 'fromCodePoint', {
+ 'value': fromCodePoint,
+ 'configurable': true,
+ 'writable': true
+ });
+ } else {
+ String.fromCodePoint = fromCodePoint;
+ }
+ }());
+}
+
+/*! http://mths.be/codepointat v0.1.0 by @mathias */
+if (!String.prototype.codePointAt) {
+ (function() {
+ 'use strict'; // needed to support `apply`/`call` with `undefined`/`null`
+ var codePointAt = function(position) {
+ if (this == null) {
+ throw TypeError();
+ }
+ var string = String(this);
+ var size = string.length;
+ // `ToInteger`
+ var index = position ? Number(position) : 0;
+ if (index != index) { // better `isNaN`
+ index = 0;
+ }
+ // Account for out-of-bounds indices:
+ if (index < 0 || index >= size) {
+ return undefined;
+ }
+ // Get the first code unit
+ var first = string.charCodeAt(index);
+ var second;
+ if ( // check if it’s the start of a surrogate pair
+ first >= 0xD800 && first <= 0xDBFF && // high surrogate
+ size > index + 1 // there is a next code unit
+ ) {
+ second = string.charCodeAt(index + 1);
+ if (second >= 0xDC00 && second <= 0xDFFF) { // low surrogate
+ // http://mathiasbynens.be/notes/javascript-encoding#surrogate-formulae
+ return (first - 0xD800) * 0x400 + second - 0xDC00 + 0x10000;
+ }
+ }
+ return first;
+ };
+ if (Object.defineProperty) {
+ Object.defineProperty(String.prototype, 'codePointAt', {
+ 'value': codePointAt,
+ 'configurable': true,
+ 'writable': true
+ });
+ } else {
+ String.prototype.codePointAt = codePointAt;
+ }
+ }());
+}
+
+function registerAsciinemaPlayerElement() {
+ var AsciinemaPlayerProto = Object.create(HTMLElement.prototype);
+
+ function merge() {
+ var merged = {};
+ for (var i=0; i<arguments.length; i++) {
+ var obj = arguments[i];
+ for (var attrname in obj) {
+ merged[attrname] = obj[attrname];
+ }
+ }
+ return merged;
+ }
+
+ function attribute(element, attrName, optName, defaultValue, coerceFn) {
+ var obj = {};
+ var value = element.getAttribute(attrName);
+ if (value !== null) {
+ if (value === '' && defaultValue !== undefined) {
+ value = defaultValue;
+ } else if (coerceFn) {
+ value = coerceFn(value);
+ }
+ obj[optName] = value;
+ }
+ return obj;
+ };
+
+ function fixEscapeCodes(text) {
+ if (text) {
+ var f = function(match, p1, offset, string) {
+ return String.fromCodePoint(parseInt(p1, 16));
+ };
+
+ return text.
+ replace(/\\u([a-z0-9]{4})/gi, f).
+ replace(/\\x([a-z0-9]{2})/gi, f).
+ replace(/\\e/g, "\x1b");
+ } else {
+ return text;
+ }
+ }
+
+ AsciinemaPlayerProto.createdCallback = function() {
+ var self = this;
+
+ var opts = merge(
+ attribute(this, 'cols', 'width', 0, parseInt),
+ attribute(this, 'rows', 'height', 0, parseInt),
+ attribute(this, 'autoplay', 'autoPlay', true, Boolean),
+ attribute(this, 'preload', 'preload', true, Boolean),
+ attribute(this, 'loop', 'loop', true, Boolean),
+ attribute(this, 'start-at', 'startAt', 0, parseInt),
+ attribute(this, 'speed', 'speed', 1, parseFloat),
+ attribute(this, 'idle-time-limit', 'idleTimeLimit', null, parseFloat),
+ attribute(this, 'poster', 'poster', null, fixEscapeCodes),
+ attribute(this, 'font-size', 'fontSize'),
+ attribute(this, 'theme', 'theme'),
+ attribute(this, 'title', 'title'),
+ attribute(this, 'author', 'author'),
+ attribute(this, 'author-url', 'authorURL'),
+ attribute(this, 'author-img-url', 'authorImgURL'),
+ {
+ onCanPlay: function() {
+ self.dispatchEvent(new CustomEvent("loadedmetadata"));
+ self.dispatchEvent(new CustomEvent("loadeddata"));
+ self.dispatchEvent(new CustomEvent("canplay"));
+ self.dispatchEvent(new CustomEvent("canplaythrough"));
+ },
+
+ onPlay: function() {
+ self.dispatchEvent(new CustomEvent("play"));
+ },
+
+ onPause: function() {
+ self.dispatchEvent(new CustomEvent("pause"));
+ }
+ }
+ );
+
+ this.player = asciinema.player.js.CreatePlayer(this, this.getAttribute('src'), opts);
+ };
+
+ AsciinemaPlayerProto.attachedCallback = function() {
+ var self = this;
+ setTimeout(function() {
+ self.dispatchEvent(new CustomEvent("attached"));
+ }, 0);
+ };
+
+ AsciinemaPlayerProto.detachedCallback = function() {
+ asciinema.player.js.UnmountPlayer(this);
+ this.player = undefined;
+ };
+
+ AsciinemaPlayerProto.play = function() {
+ this.player.play();
+ };
+
+ AsciinemaPlayerProto.pause = function() {
+ this.player.pause();
+ };
+
+ Object.defineProperty(AsciinemaPlayerProto, "duration", {
+ get: function() {
+ return this.player.getDuration() || 0;
+ },
+
+ set: function(value) {}
+ });
+
+ Object.defineProperty(AsciinemaPlayerProto, "currentTime", {
+ get: function() {
+ return this.player.getCurrentTime();
+ },
+
+ set: function(value) {
+ this.player.setCurrentTime(value);
+ }
+ });
+
+ document.registerElement('asciinema-player', { prototype: AsciinemaPlayerProto });
+};
-t.exports={inject:o}},{109:109,13:13,14:14,21:21,23:23,26:26,29:29,3:3,33:33,35:35,41:41,42:42,43:43,44:44,45:45,46:46,47:47,48:48,49:49,51:51,52:52,53:53,55:55,60:60,62:62,64:64,68:68,7:7,78:78,8:8,86:86,87:87,88:88,89:89}],55:[function(e,t,n){"use strict";var r=e(38),o=e(39),i=e(27),a=(e(150),{key:!0,ref:!0}),u=function(e,t,n,r,o,i){this.type=e,this.key=t,this.ref=n,this._owner=r,this._context=o,this.props=i};u.prototype={_isReactElement:!0},u.createElement=function(e,t,n){var i,s={},l=null,c=null;if(null!=t){c=void 0===t.ref?null:t.ref,l=void 0===t.key?null:""+t.key;for(i in t)t.hasOwnProperty(i)&&!a.hasOwnProperty(i)&&(s[i]=t[i])}var p=arguments.length-2;if(1===p)s.children=n;else if(p>1){for(var d=Array(p),f=0;p>f;f++)d[f]=arguments[f+2];s.children=d}if(e&&e.defaultProps){var h=e.defaultProps;for(i in h)"undefined"==typeof s[i]&&(s[i]=h[i])}return new u(e,l,c,o.current,r.current,s)},u.createFactory=function(e){var t=u.createElement.bind(null,e);return t.type=e,t},u.cloneAndReplaceProps=function(e,t){var n=new u(e.type,e.key,e.ref,e._owner,e._context,t);return n},u.cloneElement=function(e,t,n){var r,s=i({},e.props),l=e.key,c=e.ref,p=e._owner;if(null!=t){void 0!==t.ref&&(c=t.ref,p=o.current),void 0!==t.key&&(l=""+t.key);for(r in t)t.hasOwnProperty(r)&&!a.hasOwnProperty(r)&&(s[r]=t[r])}var d=arguments.length-2;if(1===d)s.children=n;else if(d>1){for(var f=Array(d),h=0;d>h;h++)f[h]=arguments[h+2];s.children=f}return new u(e.type,l,c,p,e._context,s)},u.isValidElement=function(e){var t=!(!e||!e._isReactElement);return t},t.exports=u},{150:150,27:27,38:38,39:39}],56:[function(e,t,n){"use strict";function r(){if(y.current){var e=y.current.getName();if(e)return" Check the render method of `"+e+"`."}return""}function o(e){var t=e&&e.getPublicInstance();if(!t)return void 0;var n=t.constructor;return n?n.displayName||n.name||void 0:void 0}function i(){var e=y.current;return e&&o(e)||void 0}function a(e,t){e._store.validated||null!=e.key||(e._store.validated=!0,s('Each child in an array or iterator should have a unique "key" prop.',e,t))}function u(e,t,n){D.test(e)&&s("Child objects should have non-numeric keys so ordering is preserved.",t,n)}function s(e,t,n){var r=i(),a="string"==typeof n?n:n.displayName||n.name,u=r||a,s=_[e]||(_[e]={});if(!s.hasOwnProperty(u)){s[u]=!0;var l="";if(t&&t._owner&&t._owner!==y.current){var c=o(t._owner);l=" It was passed a child from "+c+"."}}}function l(e,t){if(Array.isArray(e))for(var n=0;n<e.length;n++){var r=e[n];m.isValidElement(r)&&a(r,t)}else if(m.isValidElement(e))e._store.validated=!0;else if(e){var o=E(e);if(o){if(o!==e.entries)for(var i,s=o.call(e);!(i=s.next()).done;)m.isValidElement(i.value)&&a(i.value,t)}else if("object"==typeof e){var l=v.extractIfFragment(e);for(var c in l)l.hasOwnProperty(c)&&u(c,l[c],t)}}}function c(e,t,n,o){for(var i in t)if(t.hasOwnProperty(i)){var a;try{b("function"==typeof t[i]),a=t[i](n,i,e,o)}catch(u){a=u}a instanceof Error&&!(a.message in x)&&(x[a.message]=!0,r(this))}}function p(e,t){var n=t.type,r="string"==typeof n?n:n.displayName,o=t._owner?t._owner.getPublicInstance().constructor.displayName:null,i=e+"|"+r+"|"+o;if(!M.hasOwnProperty(i)){M[i]=!0;var a="";r&&(a=" <"+r+" />");var u="";o&&(u=" The element was created by "+o+".")}}function d(e,t){return e!==e?t!==t:0===e&&0===t?1/e===1/t:e===t}function f(e){if(e._store){var t=e._store.originalProps,n=e.props;for(var r in n)n.hasOwnProperty(r)&&(t.hasOwnProperty(r)&&d(t[r],n[r])||(p(r,e),t[r]=n[r]))}}function h(e){if(null!=e.type){var t=C.getComponentClassForElement(e),n=t.displayName||t.name;t.propTypes&&c(n,t.propTypes,e.props,g.prop),"function"==typeof t.getDefaultProps}}var m=e(55),v=e(61),g=e(75),y=(e(74),e(39)),C=e(71),E=e(124),b=e(133),_=(e(150),{}),x={},D=/^\d+$/,M={},N={checkAndWarnForMutatedProps:f,createElement:function(e,t,n){var r=m.createElement.apply(this,arguments);if(null==r)return r;for(var o=2;o<arguments.length;o++)l(arguments[o],e);return h(r),r},createFactory:function(e){var t=N.createElement.bind(null,e);return t.type=e,t},cloneElement:function(e,t,n){for(var r=m.cloneElement.apply(this,arguments),o=2;o<arguments.length;o++)l(arguments[o],r.type);return h(r),r}};t.exports=N},{124:124,133:133,150:150,39:39,55:55,61:61,71:71,74:74,75:75}],57:[function(e,t,n){"use strict";function r(e){c[e]=!0}function o(e){delete c[e]}function i(e){return!!c[e]}var a,u=e(55),s=e(65),l=e(133),c={},p={injectEmptyComponent:function(e){a=u.createFactory(e)}},d=function(){};d.prototype.componentDidMount=function(){var e=s.get(this);e&&r(e._rootNodeID)},d.prototype.componentWillUnmount=function(){var e=s.get(this);e&&o(e._rootNodeID)},d.prototype.render=function(){return l(a),a()};var f=u.createElement(d),h={emptyElement:f,injection:p,isNullComponentID:i};t.exports=h},{133:133,55:55,65:65}],58:[function(e,t,n){"use strict";var r={guard:function(e,t){return e}};t.exports=r},{}],59:[function(e,t,n){"use strict";function r(e){o.enqueueEvents(e),o.processEventQueue()}var o=e(17),i={handleTopLevel:function(e,t,n,i){var a=o.extractEvents(e,t,n,i);r(a)}};t.exports=i},{17:17}],60:[function(e,t,n){"use strict";function r(e){var t=p.getID(e),n=c.getReactRootIDFromNodeID(t),r=p.findReactContainerForID(n),o=p.getFirstReactDOM(r);return o}function o(e,t){this.topLevelType=e,this.nativeEvent=t,this.ancestors=[]}function i(e){for(var t=p.getFirstReactDOM(h(e.nativeEvent))||window,n=t;n;)e.ancestors.push(n),n=r(n);for(var o=0,i=e.ancestors.length;i>o;o++){t=e.ancestors[o];var a=p.getID(t)||"";v._handleTopLevel(e.topLevelType,t,a,e.nativeEvent)}}function a(e){var t=m(window);e(t)}var u=e(16),s=e(21),l=e(28),c=e(64),p=e(68),d=e(85),f=e(27),h=e(123),m=e(129);f(o.prototype,{destructor:function(){this.topLevelType=null,this.nativeEvent=null,this.ancestors.length=0}}),l.addPoolingTo(o,l.twoArgumentPooler);var v={_enabled:!0,_handleTopLevel:null,WINDOW_HANDLE:s.canUseDOM?window:null,setHandleTopLevel:function(e){v._handleTopLevel=e},setEnabled:function(e){v._enabled=!!e},isEnabled:function(){return v._enabled},trapBubbledEvent:function(e,t,n){var r=n;return r?u.listen(r,t,v.dispatchEvent.bind(null,e)):null},trapCapturedEvent:function(e,t,n){var r=n;return r?u.capture(r,t,v.dispatchEvent.bind(null,e)):null},monitorScrollValue:function(e){var t=a.bind(null,e);u.listen(window,"scroll",t)},dispatchEvent:function(e,t){if(v._enabled){var n=o.getPooled(e,t);try{d.batchedUpdates(i,n)}finally{o.release(n)}}}};t.exports=v},{123:123,129:129,16:16,21:21,27:27,28:28,64:64,68:68,85:85}],61:[function(e,t,n){"use strict";var r=(e(55),e(150),{create:function(e){return e},extract:function(e){return e},extractIfFragment:function(e){return e}});t.exports=r},{150:150,55:55}],62:[function(e,t,n){"use strict";var r=e(10),o=e(17),i=e(36),a=e(33),u=e(57),s=e(30),l=e(71),c=e(42),p=e(73),d=e(81),f=e(85),h={Component:i.injection,Class:a.injection,DOMComponent:c.injection,DOMProperty:r.injection,EmptyComponent:u.injection,EventPluginHub:o.injection,EventEmitter:s.injection,NativeComponent:l.injection,Perf:p.injection,RootIndex:d.injection,Updates:f.injection};t.exports=h},{10:10,17:17,30:30,33:33,36:36,42:42,57:57,71:71,73:73,81:81,85:85}],63:[function(e,t,n){"use strict";function r(e){return i(document.documentElement,e)}var o=e(50),i=e(107),a=e(117),u=e(119),s={hasSelectionCapabilities:function(e){return e&&("INPUT"===e.nodeName&&"text"===e.type||"TEXTAREA"===e.nodeName||"true"===e.contentEditable)},getSelectionInformation:function(){var e=u();return{focusedElem:e,selectionRange:s.hasSelectionCapabilities(e)?s.getSelection(e):null}},restoreSelection:function(e){var t=u(),n=e.focusedElem,o=e.selectionRange;t!==n&&r(n)&&(s.hasSelectionCapabilities(n)&&s.setSelection(n,o),a(n))},getSelection:function(e){var t;if("selectionStart"in e)t={start:e.selectionStart,end:e.selectionEnd};else if(document.selection&&"INPUT"===e.nodeName){var n=document.selection.createRange();n.parentElement()===e&&(t={start:-n.moveStart("character",-e.value.length),end:-n.moveEnd("character",-e.value.length)})}else t=o.getOffsets(e);return t||{start:0,end:0}},setSelection:function(e,t){var n=t.start,r=t.end;if("undefined"==typeof r&&(r=n),"selectionStart"in e)e.selectionStart=n,e.selectionEnd=Math.min(r,e.value.length);else if(document.selection&&"INPUT"===e.nodeName){var i=e.createTextRange();i.collapse(!0),i.moveStart("character",n),i.moveEnd("character",r-n),i.select()}else o.setOffsets(e,t)}};t.exports=s},{107:107,117:117,119:119,50:50}],64:[function(e,t,n){"use strict";function r(e){return f+e.toString(36)}function o(e,t){return e.charAt(t)===f||t===e.length}function i(e){return""===e||e.charAt(0)===f&&e.charAt(e.length-1)!==f}function a(e,t){return 0===t.indexOf(e)&&o(t,e.length)}function u(e){return e?e.substr(0,e.lastIndexOf(f)):""}function s(e,t){if(d(i(e)&&i(t)),d(a(e,t)),e===t)return e;var n,r=e.length+h;for(n=r;n<t.length&&!o(t,n);n++);return t.substr(0,n)}function l(e,t){var n=Math.min(e.length,t.length);if(0===n)return"";for(var r=0,a=0;n>=a;a++)if(o(e,a)&&o(t,a))r=a;else if(e.charAt(a)!==t.charAt(a))break;var u=e.substr(0,r);return d(i(u)),u}function c(e,t,n,r,o,i){e=e||"",t=t||"",d(e!==t);var l=a(t,e);d(l||a(e,t));for(var c=0,p=l?u:s,f=e;;f=p(f,t)){var h;if(o&&f===e||i&&f===t||(h=n(f,l,r)),h===!1||f===t)break;d(c++<m)}}var p=e(81),d=e(133),f=".",h=f.length,m=100,v={createReactRootID:function(){return r(p.createReactRootIndex())},createReactID:function(e,t){return e+t},getReactRootIDFromNodeID:function(e){if(e&&e.charAt(0)===f&&e.length>1){var t=e.indexOf(f,1);return t>-1?e.substr(0,t):e}return null},traverseEnterLeave:function(e,t,n,r,o){var i=l(e,t);i!==e&&c(e,i,n,r,!1,!0),i!==t&&c(i,t,n,o,!0,!1)},traverseTwoPhase:function(e,t,n){e&&(c("",e,t,n,!0,!1),c(e,"",t,n,!1,!0))},traverseAncestors:function(e,t,n){c("",e,t,n,!0,!1)},_getFirstCommonAncestorID:l,_getNextDescendantID:s,isAncestorIDOf:a,SEPARATOR:f};t.exports=v},{133:133,81:81}],65:[function(e,t,n){"use strict";var r={remove:function(e){e._reactInternalInstance=void 0},get:function(e){return e._reactInternalInstance},has:function(e){return void 0!==e._reactInternalInstance},set:function(e,t){e._reactInternalInstance=t}};t.exports=r},{}],66:[function(e,t,n){"use strict";var r={currentlyMountingInstance:null,currentlyUnmountingInstance:null};t.exports=r},{}],67:[function(e,t,n){"use strict";var r=e(104),o={CHECKSUM_ATTR_NAME:"data-react-checksum",addChecksumToMarkup:function(e){var t=r(e);return e.replace(">"," "+o.CHECKSUM_ATTR_NAME+'="'+t+'">')},canReuseMarkup:function(e,t){var n=t.getAttribute(o.CHECKSUM_ATTR_NAME);n=n&&parseInt(n,10);var i=r(e);return i===n}};t.exports=o},{104:104}],68:[function(e,t,n){"use strict";function r(e,t){for(var n=Math.min(e.length,t.length),r=0;n>r;r++)if(e.charAt(r)!==t.charAt(r))return r;return e.length===t.length?-1:n}function o(e){var t=P(e);return t&&K.getID(t)}function i(e){var t=a(e);if(t)if(L.hasOwnProperty(t)){var n=L[t];n!==e&&(w(!c(n,t)),L[t]=e)}else L[t]=e;return t}function a(e){return e&&e.getAttribute&&e.getAttribute(k)||""}function u(e,t){var n=a(e);n!==t&&delete L[n],e.setAttribute(k,t),L[t]=e}function s(e){return L.hasOwnProperty(e)&&c(L[e],e)||(L[e]=K.findReactNodeByID(e)),L[e]}function l(e){var t=b.get(e)._rootNodeID;return C.isNullComponentID(t)?null:(L.hasOwnProperty(t)&&c(L[t],t)||(L[t]=K.findReactNodeByID(t)),L[t])}function c(e,t){if(e){w(a(e)===t);var n=K.findReactContainerForID(t);if(n&&T(n,e))return!0}return!1}function p(e){delete L[e]}function d(e){var t=L[e];return t&&c(t,e)?void(W=t):!1}function f(e){W=null,E.traverseAncestors(e,d);var t=W;return W=null,t}function h(e,t,n,r,o){var i=D.mountComponent(e,t,r,I);e._isTopLevel=!0,K._mountImageIntoNode(i,n,o)}function m(e,t,n,r){var o=N.ReactReconcileTransaction.getPooled();o.perform(h,null,e,t,n,o,r),N.ReactReconcileTransaction.release(o)}var v=e(10),g=e(30),y=(e(39),e(55)),C=(e(56),e(57)),E=e(64),b=e(65),_=e(67),x=e(73),D=e(79),M=e(84),N=e(85),I=e(113),T=e(107),P=e(127),R=e(132),w=e(133),O=e(144),S=e(147),A=(e(150),E.SEPARATOR),k=v.ID_ATTRIBUTE_NAME,L={},U=1,F=9,B={},V={},j=[],W=null,K={_instancesByReactRootID:B,scrollMonitor:function(e,t){t()},_updateRootComponent:function(e,t,n,r){return K.scrollMonitor(n,function(){M.enqueueElementInternal(e,t),r&&M.enqueueCallbackInternal(e,r)}),e},_registerComponent:function(e,t){w(t&&(t.nodeType===U||t.nodeType===F)),g.ensureScrollValueMonitoring();var n=K.registerContainer(t);return B[n]=e,n},_renderNewRootComponent:function(e,t,n){var r=R(e,null),o=K._registerComponent(r,t);return N.batchedUpdates(m,r,o,t,n),r},render:function(e,t,n){w(y.isValidElement(e));var r=B[o(t)];if(r){var i=r._currentElement;if(S(i,e))return K._updateRootComponent(r,e,t,n).getPublicInstance();K.unmountComponentAtNode(t)}var a=P(t),u=a&&K.isRenderedByReact(a),s=u&&!r,l=K._renderNewRootComponent(e,t,s).getPublicInstance();return n&&n.call(l),l},constructAndRenderComponent:function(e,t,n){var r=y.createElement(e,t);return K.render(r,n)},constructAndRenderComponentByID:function(e,t,n){var r=document.getElementById(n);return w(r),K.constructAndRenderComponent(e,t,r)},registerContainer:function(e){var t=o(e);return t&&(t=E.getReactRootIDFromNodeID(t)),t||(t=E.createReactRootID()),V[t]=e,t},unmountComponentAtNode:function(e){w(e&&(e.nodeType===U||e.nodeType===F));var t=o(e),n=B[t];return n?(K.unmountComponentFromNode(n,e),delete B[t],delete V[t],!0):!1},unmountComponentFromNode:function(e,t){for(D.unmountComponent(e),t.nodeType===F&&(t=t.documentElement);t.lastChild;)t.removeChild(t.lastChild)},findReactContainerForID:function(e){var t=E.getReactRootIDFromNodeID(e),n=V[t];return n},findReactNodeByID:function(e){var t=K.findReactContainerForID(e);return K.findComponentRoot(t,e)},isRenderedByReact:function(e){if(1!==e.nodeType)return!1;var t=K.getID(e);return t?t.charAt(0)===A:!1},getFirstReactDOM:function(e){for(var t=e;t&&t.parentNode!==t;){if(K.isRenderedByReact(t))return t;t=t.parentNode}return null},findComponentRoot:function(e,t){var n=j,r=0,o=f(t)||e;for(n[0]=o.firstChild,n.length=1;r<n.length;){for(var i,a=n[r++];a;){var u=K.getID(a);u?t===u?i=a:E.isAncestorIDOf(u,t)&&(n.length=r=0,n.push(a.firstChild)):n.push(a.firstChild),a=a.nextSibling}if(i)return n.length=0,i}n.length=0,w(!1)},_mountImageIntoNode:function(e,t,n){if(w(t&&(t.nodeType===U||t.nodeType===F)),n){var o=P(t);if(_.canReuseMarkup(e,o))return;var i=o.getAttribute(_.CHECKSUM_ATTR_NAME);o.removeAttribute(_.CHECKSUM_ATTR_NAME);var a=o.outerHTML;o.setAttribute(_.CHECKSUM_ATTR_NAME,i);var u=r(e,a);" (client) "+e.substring(u-20,u+20)+"\n (server) "+a.substring(u-20,u+20),w(t.nodeType!==F)}w(t.nodeType!==F),O(t,e)},getReactRootID:o,getID:i,setID:u,getNode:s,getNodeFromInstance:l,purgeID:p};x.measureMethods(K,"ReactMount",{_renderNewRootComponent:"_renderNewRootComponent",_mountImageIntoNode:"_mountImageIntoNode"}),t.exports=K},{10:10,107:107,113:113,127:127,132:132,133:133,144:144,147:147,150:150,30:30,39:39,55:55,56:56,57:57,64:64,65:65,67:67,73:73,79:79,84:84,85:85}],69:[function(e,t,n){"use strict";function r(e,t,n){h.push({parentID:e,parentNode:null,type:c.INSERT_MARKUP,markupIndex:m.push(t)-1,textContent:null,fromIndex:null,toIndex:n})}function o(e,t,n){h.push({parentID:e,parentNode:null,type:c.MOVE_EXISTING,markupIndex:null,textContent:null,fromIndex:t,toIndex:n})}function i(e,t){h.push({parentID:e,parentNode:null,type:c.REMOVE_NODE,markupIndex:null,textContent:null,fromIndex:t,toIndex:null})}function a(e,t){h.push({parentID:e,parentNode:null,type:c.TEXT_CONTENT,markupIndex:null,textContent:t,fromIndex:null,toIndex:null})}function u(){h.length&&(l.processChildrenUpdates(h,m),s())}function s(){h.length=0,m.length=0}var l=e(36),c=e(70),p=e(79),d=e(31),f=0,h=[],m=[],v={Mixin:{mountChildren:function(e,t,n){var r=d.instantiateChildren(e,t,n);this._renderedChildren=r;var o=[],i=0;for(var a in r)if(r.hasOwnProperty(a)){var u=r[a],s=this._rootNodeID+a,l=p.mountComponent(u,s,t,n);u._mountIndex=i,o.push(l),i++}return o},updateTextContent:function(e){f++;var t=!0;try{var n=this._renderedChildren;d.unmountChildren(n);for(var r in n)n.hasOwnProperty(r)&&this._unmountChildByName(n[r],r);this.setTextContent(e),t=!1}finally{f--,f||(t?s():u())}},updateChildren:function(e,t,n){f++;var r=!0;try{this._updateChildren(e,t,n),r=!1}finally{f--,f||(r?s():u())}},_updateChildren:function(e,t,n){var r=this._renderedChildren,o=d.updateChildren(r,e,t,n);if(this._renderedChildren=o,o||r){var i,a=0,u=0;for(i in o)if(o.hasOwnProperty(i)){var s=r&&r[i],l=o[i];s===l?(this.moveChild(s,u,a),a=Math.max(s._mountIndex,a),s._mountIndex=u):(s&&(a=Math.max(s._mountIndex,a),this._unmountChildByName(s,i)),this._mountChildByNameAtIndex(l,i,u,t,n)),u++}for(i in r)!r.hasOwnProperty(i)||o&&o.hasOwnProperty(i)||this._unmountChildByName(r[i],i)}},unmountChildren:function(){var e=this._renderedChildren;d.unmountChildren(e),this._renderedChildren=null},moveChild:function(e,t,n){e._mountIndex<n&&o(this._rootNodeID,e._mountIndex,t)},createChild:function(e,t){r(this._rootNodeID,t,e._mountIndex)},removeChild:function(e){i(this._rootNodeID,e._mountIndex)},setTextContent:function(e){a(this._rootNodeID,e)},_mountChildByNameAtIndex:function(e,t,n,r,o){var i=this._rootNodeID+t,a=p.mountComponent(e,i,r,o);e._mountIndex=n,this.createChild(e,a)},_unmountChildByName:function(e,t){this.removeChild(e),e._mountIndex=null}}};t.exports=v},{31:31,36:36,70:70,79:79}],70:[function(e,t,n){"use strict";var r=e(138),o=r({INSERT_MARKUP:null,MOVE_EXISTING:null,REMOVE_NODE:null,TEXT_CONTENT:null});t.exports=o},{138:138}],71:[function(e,t,n){"use strict";function r(e){if("function"==typeof e.type)return e.type;var t=e.type,n=p[t];return null==n&&(p[t]=n=l(t)),n}function o(e){return s(c),new c(e.type,e.props)}function i(e){return new d(e)}function a(e){return e instanceof d}var u=e(27),s=e(133),l=null,c=null,p={},d=null,f={injectGenericComponentClass:function(e){c=e},injectTextComponentClass:function(e){d=e},injectComponentClasses:function(e){u(p,e)},injectAutoWrapper:function(e){l=e}},h={getComponentClassForElement:r,createInternalComponent:o,createInstanceForText:i,isTextComponent:a,injection:f};t.exports=h},{133:133,27:27}],72:[function(e,t,n){"use strict";var r=e(133),o={isValidOwner:function(e){return!(!e||"function"!=typeof e.attachRef||"function"!=typeof e.detachRef)},addComponentAsRefTo:function(e,t,n){r(o.isValidOwner(n)),n.attachRef(t,e)},removeComponentAsRefFrom:function(e,t,n){r(o.isValidOwner(n)),n.getPublicInstance().refs[t]===e.getPublicInstance()&&n.detachRef(t)}};t.exports=o},{133:133}],73:[function(e,t,n){"use strict";function r(e,t,n){return n}var o={enableMeasure:!1,storedMeasure:r,measureMethods:function(e,t,n){},measure:function(e,t,n){return n},injection:{injectMeasure:function(e){o.storedMeasure=e}}};t.exports=o},{}],74:[function(e,t,n){"use strict";var r={};t.exports=r},{}],75:[function(e,t,n){"use strict";var r=e(138),o=r({prop:null,context:null,childContext:null});t.exports=o},{138:138}],76:[function(e,t,n){"use strict";function r(e){function t(t,n,r,o,i){if(o=o||b,null==n[r]){var a=C[i];return t?new Error("Required "+a+" `"+r+"` was not specified in "+("`"+o+"`.")):null}return e(n,r,o,i)}var n=t.bind(null,!1);return n.isRequired=t.bind(null,!0),n}function o(e){function t(t,n,r,o){var i=t[n],a=m(i);if(a!==e){var u=C[o],s=v(i);return new Error("Invalid "+u+" `"+n+"` of type `"+s+"` "+("supplied to `"+r+"`, expected `"+e+"`."))}return null}return r(t)}function i(){return r(E.thatReturns(null))}function a(e){function t(t,n,r,o){var i=t[n];if(!Array.isArray(i)){var a=C[o],u=m(i);return new Error("Invalid "+a+" `"+n+"` of type "+("`"+u+"` supplied to `"+r+"`, expected an array."))}for(var s=0;s<i.length;s++){var l=e(i,s,r,o);if(l instanceof Error)return l}return null}return r(t)}function u(){function e(e,t,n,r){if(!g.isValidElement(e[t])){var o=C[r];return new Error("Invalid "+o+" `"+t+"` supplied to "+("`"+n+"`, expected a ReactElement."))}return null}return r(e)}function s(e){function t(t,n,r,o){if(!(t[n]instanceof e)){var i=C[o],a=e.name||b;return new Error("Invalid "+i+" `"+n+"` supplied to "+("`"+r+"`, expected instance of `"+a+"`."))}return null}return r(t)}function l(e){function t(t,n,r,o){for(var i=t[n],a=0;a<e.length;a++)if(i===e[a])return null;var u=C[o],s=JSON.stringify(e);return new Error("Invalid "+u+" `"+n+"` of value `"+i+"` "+("supplied to `"+r+"`, expected one of "+s+"."))}return r(t)}function c(e){function t(t,n,r,o){var i=t[n],a=m(i);if("object"!==a){var u=C[o];return new Error("Invalid "+u+" `"+n+"` of type "+("`"+a+"` supplied to `"+r+"`, expected an object."))}for(var s in i)if(i.hasOwnProperty(s)){var l=e(i,s,r,o);if(l instanceof Error)return l}return null}return r(t)}function p(e){function t(t,n,r,o){for(var i=0;i<e.length;i++){var a=e[i];if(null==a(t,n,r,o))return null}var u=C[o];return new Error("Invalid "+u+" `"+n+"` supplied to "+("`"+r+"`."))}return r(t)}function d(){function e(e,t,n,r){if(!h(e[t])){var o=C[r];return new Error("Invalid "+o+" `"+t+"` supplied to "+("`"+n+"`, expected a ReactNode."))}return null}return r(e)}function f(e){function t(t,n,r,o){var i=t[n],a=m(i);if("object"!==a){var u=C[o];return new Error("Invalid "+u+" `"+n+"` of type `"+a+"` "+("supplied to `"+r+"`, expected `object`."))}for(var s in e){var l=e[s];if(l){var c=l(i,s,r,o);if(c)return c}}return null}return r(t)}function h(e){switch(typeof e){case"number":case"string":case"undefined":return!0;case"boolean":return!e;case"object":if(Array.isArray(e))return e.every(h);if(null===e||g.isValidElement(e))return!0;e=y.extractIfFragment(e);for(var t in e)if(!h(e[t]))return!1;return!0;default:return!1}}function m(e){var t=typeof e;return Array.isArray(e)?"array":e instanceof RegExp?"object":t}function v(e){var t=m(e);if("object"===t){if(e instanceof Date)return"date";if(e instanceof RegExp)return"regexp"}return t}var g=e(55),y=e(61),C=e(74),E=e(112),b="<<anonymous>>",_=u(),x=d(),D={array:o("array"),bool:o("boolean"),func:o("function"),number:o("number"),object:o("object"),string:o("string"),any:i(),arrayOf:a,element:_,instanceOf:s,node:x,objectOf:c,oneOf:l,oneOfType:p,shape:f};t.exports=D},{112:112,55:55,61:61,74:74}],77:[function(e,t,n){"use strict";function r(){this.listenersToPut=[]}var o=e(28),i=e(30),a=e(27);a(r.prototype,{enqueuePutListener:function(e,t,n){this.listenersToPut.push({rootNodeID:e,propKey:t,propValue:n})},putListeners:function(){for(var e=0;e<this.listenersToPut.length;e++){var t=this.listenersToPut[e];i.putListener(t.rootNodeID,t.propKey,t.propValue)}},reset:function(){this.listenersToPut.length=0},destructor:function(){this.reset()}}),o.addPoolingTo(r),t.exports=r},{27:27,28:28,30:30}],78:[function(e,t,n){"use strict";function r(){this.reinitializeTransaction(),this.renderToStaticMarkup=!1,this.reactMountReady=o.getPooled(null),this.putListenerQueue=s.getPooled()}var o=e(6),i=e(28),a=e(30),u=e(63),s=e(77),l=e(101),c=e(27),p={initialize:u.getSelectionInformation,close:u.restoreSelection},d={initialize:function(){var e=a.isEnabled();return a.setEnabled(!1),e},close:function(e){a.setEnabled(e)}},f={initialize:function(){this.reactMountReady.reset()},close:function(){this.reactMountReady.notifyAll()}},h={initialize:function(){this.putListenerQueue.reset()},close:function(){this.putListenerQueue.putListeners()}},m=[h,p,d,f],v={getTransactionWrappers:function(){return m},getReactMountReady:function(){return this.reactMountReady},getPutListenerQueue:function(){return this.putListenerQueue},destructor:function(){o.release(this.reactMountReady),this.reactMountReady=null,s.release(this.putListenerQueue),this.putListenerQueue=null}};c(r.prototype,l.Mixin,v),i.addPoolingTo(r),t.exports=r},{101:101,27:27,28:28,30:30,6:6,63:63,77:77}],79:[function(e,t,n){"use strict";function r(){o.attachRefs(this,this._currentElement)}var o=e(80),i=(e(56),{mountComponent:function(e,t,n,o){var i=e.mountComponent(t,n,o);return n.getReactMountReady().enqueue(r,e),i},unmountComponent:function(e){o.detachRefs(e,e._currentElement),e.unmountComponent()},receiveComponent:function(e,t,n,i){var a=e._currentElement;if(t!==a||null==t._owner){var u=o.shouldUpdateRefs(a,t);u&&o.detachRefs(e,a),e.receiveComponent(t,n,i),u&&n.getReactMountReady().enqueue(r,e)}},performUpdateIfNecessary:function(e,t){e.performUpdateIfNecessary(t)}});t.exports=i},{56:56,80:80}],80:[function(e,t,n){"use strict";function r(e,t,n){"function"==typeof e?e(t.getPublicInstance()):i.addComponentAsRefTo(t,e,n)}function o(e,t,n){"function"==typeof e?e(null):i.removeComponentAsRefFrom(t,e,n)}var i=e(72),a={};a.attachRefs=function(e,t){var n=t.ref;null!=n&&r(n,e,t._owner)},a.shouldUpdateRefs=function(e,t){return t._owner!==e._owner||t.ref!==e.ref},a.detachRefs=function(e,t){var n=t.ref;null!=n&&o(n,e,t._owner)},t.exports=a},{72:72}],81:[function(e,t,n){"use strict";var r={injectCreateReactRootIndex:function(e){o.createReactRootIndex=e}},o={createReactRootIndex:null,injection:r};t.exports=o},{}],82:[function(e,t,n){"use strict";function r(e){p(i.isValidElement(e));var t;try{var n=a.createReactRootID();return t=s.getPooled(!1),t.perform(function(){var r=c(e,null),o=r.mountComponent(n,t,l);return u.addChecksumToMarkup(o)},null)}finally{s.release(t)}}function o(e){p(i.isValidElement(e));var t;try{var n=a.createReactRootID();return t=s.getPooled(!0),t.perform(function(){var r=c(e,null);return r.mountComponent(n,t,l)},null)}finally{s.release(t)}}var i=e(55),a=e(64),u=e(67),s=e(83),l=e(113),c=e(132),p=e(133);t.exports={renderToString:r,renderToStaticMarkup:o}},{113:113,132:132,133:133,55:55,64:64,67:67,83:83}],83:[function(e,t,n){"use strict";function r(e){this.reinitializeTransaction(),this.renderToStaticMarkup=e,this.reactMountReady=i.getPooled(null),this.putListenerQueue=a.getPooled()}var o=e(28),i=e(6),a=e(77),u=e(101),s=e(27),l=e(112),c={initialize:function(){this.reactMountReady.reset()},close:l},p={initialize:function(){this.putListenerQueue.reset()},close:l},d=[p,c],f={getTransactionWrappers:function(){return d},getReactMountReady:function(){return this.reactMountReady},getPutListenerQueue:function(){return this.putListenerQueue},destructor:function(){i.release(this.reactMountReady),this.reactMountReady=null,a.release(this.putListenerQueue),this.putListenerQueue=null}};s(r.prototype,u.Mixin,f),o.addPoolingTo(r),t.exports=r},{101:101,112:112,27:27,28:28,6:6,77:77}],84:[function(e,t,n){"use strict";function r(e){e!==i.currentlyMountingInstance&&l.enqueueUpdate(e)}function o(e,t){p(null==a.current);var n=s.get(e);return n?n===i.currentlyUnmountingInstance?null:n:null}var i=e(66),a=e(39),u=e(55),s=e(65),l=e(85),c=e(27),p=e(133),d=(e(150),{enqueueCallback:function(e,t){p("function"==typeof t);var n=o(e);return n&&n!==i.currentlyMountingInstance?(n._pendingCallbacks?n._pendingCallbacks.push(t):n._pendingCallbacks=[t],void r(n)):null},enqueueCallbackInternal:function(e,t){p("function"==typeof t),e._pendingCallbacks?e._pendingCallbacks.push(t):e._pendingCallbacks=[t],r(e)},enqueueForceUpdate:function(e){var t=o(e,"forceUpdate");t&&(t._pendingForceUpdate=!0,r(t))},enqueueReplaceState:function(e,t){var n=o(e,"replaceState");n&&(n._pendingStateQueue=[t],n._pendingReplaceState=!0,r(n))},enqueueSetState:function(e,t){var n=o(e,"setState");if(n){var i=n._pendingStateQueue||(n._pendingStateQueue=[]);i.push(t),r(n)}},enqueueSetProps:function(e,t){var n=o(e,"setProps");if(n){p(n._isTopLevel);var i=n._pendingElement||n._currentElement,a=c({},i.props,t);n._pendingElement=u.cloneAndReplaceProps(i,a),r(n)}},enqueueReplaceProps:function(e,t){var n=o(e,"replaceProps");if(n){p(n._isTopLevel);var i=n._pendingElement||n._currentElement;n._pendingElement=u.cloneAndReplaceProps(i,t),r(n)}},enqueueElementInternal:function(e,t){e._pendingElement=t,r(e)}});t.exports=d},{133:133,150:150,27:27,39:39,55:55,65:65,66:66,85:85}],85:[function(e,t,n){"use strict";function r(){v(N.ReactReconcileTransaction&&E)}function o(){this.reinitializeTransaction(),this.dirtyComponentsLength=null,this.callbackQueue=c.getPooled(),this.reconcileTransaction=N.ReactReconcileTransaction.getPooled()}function i(e,t,n,o,i){r(),E.batchedUpdates(e,t,n,o,i)}function a(e,t){return e._mountOrder-t._mountOrder}function u(e){var t=e.dirtyComponentsLength;v(t===g.length),g.sort(a);for(var n=0;t>n;n++){var r=g[n],o=r._pendingCallbacks;if(r._pendingCallbacks=null,f.performUpdateIfNecessary(r,e.reconcileTransaction),o)for(var i=0;i<o.length;i++)e.callbackQueue.enqueue(o[i],r.getPublicInstance())}}function s(e){return r(),E.isBatchingUpdates?void g.push(e):void E.batchedUpdates(s,e)}function l(e,t){v(E.isBatchingUpdates),y.enqueue(e,t),C=!0}var c=e(6),p=e(28),d=(e(39),e(73)),f=e(79),h=e(101),m=e(27),v=e(133),g=(e(150),[]),y=c.getPooled(),C=!1,E=null,b={initialize:function(){this.dirtyComponentsLength=g.length},close:function(){this.dirtyComponentsLength!==g.length?(g.splice(0,this.dirtyComponentsLength),D()):g.length=0}},_={initialize:function(){this.callbackQueue.reset()},close:function(){this.callbackQueue.notifyAll()}},x=[b,_];m(o.prototype,h.Mixin,{getTransactionWrappers:function(){return x},destructor:function(){this.dirtyComponentsLength=null,c.release(this.callbackQueue),this.callbackQueue=null,N.ReactReconcileTransaction.release(this.reconcileTransaction),this.reconcileTransaction=null},perform:function(e,t,n){return h.Mixin.perform.call(this,this.reconcileTransaction.perform,this.reconcileTransaction,e,t,n)}}),p.addPoolingTo(o);var D=function(){for(;g.length||C;){if(g.length){var e=o.getPooled();e.perform(u,null,e),o.release(e)}if(C){C=!1;var t=y;y=c.getPooled(),t.notifyAll(),c.release(t)}}};D=d.measure("ReactUpdates","flushBatchedUpdates",D);var M={injectReconcileTransaction:function(e){v(e),N.ReactReconcileTransaction=e},injectBatchingStrategy:function(e){v(e),v("function"==typeof e.batchedUpdates),v("boolean"==typeof e.isBatchingUpdates),E=e}},N={ReactReconcileTransaction:null,batchedUpdates:i,enqueueUpdate:s,flushBatchedUpdates:D,injection:M,asap:l};t.exports=N},{101:101,133:133,150:150,27:27,28:28,39:39,6:6,73:73,79:79}],86:[function(e,t,n){"use strict";var r=e(10),o=r.injection.MUST_USE_ATTRIBUTE,i={Properties:{clipPath:o,cx:o,cy:o,d:o,dx:o,dy:o,fill:o,fillOpacity:o,fontFamily:o,fontSize:o,fx:o,fy:o,gradientTransform:o,gradientUnits:o,markerEnd:o,markerMid:o,markerStart:o,offset:o,opacity:o,patternContentUnits:o,patternUnits:o,points:o,preserveAspectRatio:o,r:o,rx:o,ry:o,spreadMethod:o,stopColor:o,stopOpacity:o,stroke:o,strokeDasharray:o,strokeLinecap:o,strokeOpacity:o,strokeWidth:o,textAnchor:o,transform:o,version:o,viewBox:o,x1:o,x2:o,x:o,y1:o,y2:o,y:o},DOMAttributeNames:{clipPath:"clip-path",fillOpacity:"fill-opacity",fontFamily:"font-family",fontSize:"font-size",gradientTransform:"gradientTransform",gradientUnits:"gradientUnits",markerEnd:"marker-end",markerMid:"marker-mid",markerStart:"marker-start",patternContentUnits:"patternContentUnits",patternUnits:"patternUnits",preserveAspectRatio:"preserveAspectRatio",spreadMethod:"spreadMethod",stopColor:"stop-color",stopOpacity:"stop-opacity",strokeDasharray:"stroke-dasharray",strokeLinecap:"stroke-linecap",strokeOpacity:"stroke-opacity",strokeWidth:"stroke-width",textAnchor:"text-anchor",viewBox:"viewBox"}};t.exports=i},{10:10}],87:[function(e,t,n){"use strict";function r(e){if("selectionStart"in e&&u.hasSelectionCapabilities(e))return{start:e.selectionStart,end:e.selectionEnd};if(window.getSelection){var t=window.getSelection();return{anchorNode:t.anchorNode,anchorOffset:t.anchorOffset,focusNode:t.focusNode,focusOffset:t.focusOffset}}if(document.selection){var n=document.selection.createRange();return{parentElement:n.parentElement(),text:n.text,top:n.boundingTop,left:n.boundingLeft}}}function o(e){if(y||null==m||m!==l())return null;var t=r(m);if(!g||!d(g,t)){g=t;var n=s.getPooled(h.select,v,e);return n.type="select",n.target=m,a.accumulateTwoPhaseDispatches(n),n}}var i=e(15),a=e(20),u=e(63),s=e(93),l=e(119),c=e(136),p=e(139),d=e(146),f=i.topLevelTypes,h={select:{phasedRegistrationNames:{bubbled:p({onSelect:null}),captured:p({onSelectCapture:null})},dependencies:[f.topBlur,f.topContextMenu,f.topFocus,f.topKeyDown,f.topMouseDown,f.topMouseUp,f.topSelectionChange]
-}},m=null,v=null,g=null,y=!1,C={eventTypes:h,extractEvents:function(e,t,n,r){switch(e){case f.topFocus:(c(t)||"true"===t.contentEditable)&&(m=t,v=n,g=null);break;case f.topBlur:m=null,v=null,g=null;break;case f.topMouseDown:y=!0;break;case f.topContextMenu:case f.topMouseUp:return y=!1,o(r);case f.topSelectionChange:case f.topKeyDown:case f.topKeyUp:return o(r)}}};t.exports=C},{119:119,136:136,139:139,146:146,15:15,20:20,63:63,93:93}],88:[function(e,t,n){"use strict";var r=Math.pow(2,53),o={createReactRootIndex:function(){return Math.ceil(Math.random()*r)}};t.exports=o},{}],89:[function(e,t,n){"use strict";var r=e(15),o=e(19),i=e(20),a=e(90),u=e(93),s=e(94),l=e(96),c=e(97),p=e(92),d=e(98),f=e(99),h=e(100),m=e(120),v=e(133),g=e(139),y=(e(150),r.topLevelTypes),C={blur:{phasedRegistrationNames:{bubbled:g({onBlur:!0}),captured:g({onBlurCapture:!0})}},click:{phasedRegistrationNames:{bubbled:g({onClick:!0}),captured:g({onClickCapture:!0})}},contextMenu:{phasedRegistrationNames:{bubbled:g({onContextMenu:!0}),captured:g({onContextMenuCapture:!0})}},copy:{phasedRegistrationNames:{bubbled:g({onCopy:!0}),captured:g({onCopyCapture:!0})}},cut:{phasedRegistrationNames:{bubbled:g({onCut:!0}),captured:g({onCutCapture:!0})}},doubleClick:{phasedRegistrationNames:{bubbled:g({onDoubleClick:!0}),captured:g({onDoubleClickCapture:!0})}},drag:{phasedRegistrationNames:{bubbled:g({onDrag:!0}),captured:g({onDragCapture:!0})}},dragEnd:{phasedRegistrationNames:{bubbled:g({onDragEnd:!0}),captured:g({onDragEndCapture:!0})}},dragEnter:{phasedRegistrationNames:{bubbled:g({onDragEnter:!0}),captured:g({onDragEnterCapture:!0})}},dragExit:{phasedRegistrationNames:{bubbled:g({onDragExit:!0}),captured:g({onDragExitCapture:!0})}},dragLeave:{phasedRegistrationNames:{bubbled:g({onDragLeave:!0}),captured:g({onDragLeaveCapture:!0})}},dragOver:{phasedRegistrationNames:{bubbled:g({onDragOver:!0}),captured:g({onDragOverCapture:!0})}},dragStart:{phasedRegistrationNames:{bubbled:g({onDragStart:!0}),captured:g({onDragStartCapture:!0})}},drop:{phasedRegistrationNames:{bubbled:g({onDrop:!0}),captured:g({onDropCapture:!0})}},focus:{phasedRegistrationNames:{bubbled:g({onFocus:!0}),captured:g({onFocusCapture:!0})}},input:{phasedRegistrationNames:{bubbled:g({onInput:!0}),captured:g({onInputCapture:!0})}},keyDown:{phasedRegistrationNames:{bubbled:g({onKeyDown:!0}),captured:g({onKeyDownCapture:!0})}},keyPress:{phasedRegistrationNames:{bubbled:g({onKeyPress:!0}),captured:g({onKeyPressCapture:!0})}},keyUp:{phasedRegistrationNames:{bubbled:g({onKeyUp:!0}),captured:g({onKeyUpCapture:!0})}},load:{phasedRegistrationNames:{bubbled:g({onLoad:!0}),captured:g({onLoadCapture:!0})}},error:{phasedRegistrationNames:{bubbled:g({onError:!0}),captured:g({onErrorCapture:!0})}},mouseDown:{phasedRegistrationNames:{bubbled:g({onMouseDown:!0}),captured:g({onMouseDownCapture:!0})}},mouseMove:{phasedRegistrationNames:{bubbled:g({onMouseMove:!0}),captured:g({onMouseMoveCapture:!0})}},mouseOut:{phasedRegistrationNames:{bubbled:g({onMouseOut:!0}),captured:g({onMouseOutCapture:!0})}},mouseOver:{phasedRegistrationNames:{bubbled:g({onMouseOver:!0}),captured:g({onMouseOverCapture:!0})}},mouseUp:{phasedRegistrationNames:{bubbled:g({onMouseUp:!0}),captured:g({onMouseUpCapture:!0})}},paste:{phasedRegistrationNames:{bubbled:g({onPaste:!0}),captured:g({onPasteCapture:!0})}},reset:{phasedRegistrationNames:{bubbled:g({onReset:!0}),captured:g({onResetCapture:!0})}},scroll:{phasedRegistrationNames:{bubbled:g({onScroll:!0}),captured:g({onScrollCapture:!0})}},submit:{phasedRegistrationNames:{bubbled:g({onSubmit:!0}),captured:g({onSubmitCapture:!0})}},touchCancel:{phasedRegistrationNames:{bubbled:g({onTouchCancel:!0}),captured:g({onTouchCancelCapture:!0})}},touchEnd:{phasedRegistrationNames:{bubbled:g({onTouchEnd:!0}),captured:g({onTouchEndCapture:!0})}},touchMove:{phasedRegistrationNames:{bubbled:g({onTouchMove:!0}),captured:g({onTouchMoveCapture:!0})}},touchStart:{phasedRegistrationNames:{bubbled:g({onTouchStart:!0}),captured:g({onTouchStartCapture:!0})}},wheel:{phasedRegistrationNames:{bubbled:g({onWheel:!0}),captured:g({onWheelCapture:!0})}}},E={topBlur:C.blur,topClick:C.click,topContextMenu:C.contextMenu,topCopy:C.copy,topCut:C.cut,topDoubleClick:C.doubleClick,topDrag:C.drag,topDragEnd:C.dragEnd,topDragEnter:C.dragEnter,topDragExit:C.dragExit,topDragLeave:C.dragLeave,topDragOver:C.dragOver,topDragStart:C.dragStart,topDrop:C.drop,topError:C.error,topFocus:C.focus,topInput:C.input,topKeyDown:C.keyDown,topKeyPress:C.keyPress,topKeyUp:C.keyUp,topLoad:C.load,topMouseDown:C.mouseDown,topMouseMove:C.mouseMove,topMouseOut:C.mouseOut,topMouseOver:C.mouseOver,topMouseUp:C.mouseUp,topPaste:C.paste,topReset:C.reset,topScroll:C.scroll,topSubmit:C.submit,topTouchCancel:C.touchCancel,topTouchEnd:C.touchEnd,topTouchMove:C.touchMove,topTouchStart:C.touchStart,topWheel:C.wheel};for(var b in E)E[b].dependencies=[b];var _={eventTypes:C,executeDispatch:function(e,t,n){var r=o.executeDispatch(e,t,n);r===!1&&(e.stopPropagation(),e.preventDefault())},extractEvents:function(e,t,n,r){var o=E[e];if(!o)return null;var g;switch(e){case y.topInput:case y.topLoad:case y.topError:case y.topReset:case y.topSubmit:g=u;break;case y.topKeyPress:if(0===m(r))return null;case y.topKeyDown:case y.topKeyUp:g=l;break;case y.topBlur:case y.topFocus:g=s;break;case y.topClick:if(2===r.button)return null;case y.topContextMenu:case y.topDoubleClick:case y.topMouseDown:case y.topMouseMove:case y.topMouseOut:case y.topMouseOver:case y.topMouseUp:g=c;break;case y.topDrag:case y.topDragEnd:case y.topDragEnter:case y.topDragExit:case y.topDragLeave:case y.topDragOver:case y.topDragStart:case y.topDrop:g=p;break;case y.topTouchCancel:case y.topTouchEnd:case y.topTouchMove:case y.topTouchStart:g=d;break;case y.topScroll:g=f;break;case y.topWheel:g=h;break;case y.topCopy:case y.topCut:case y.topPaste:g=a}v(g);var C=g.getPooled(o,n,r);return i.accumulateTwoPhaseDispatches(C),C}};t.exports=_},{100:100,120:120,133:133,139:139,15:15,150:150,19:19,20:20,90:90,92:92,93:93,94:94,96:96,97:97,98:98,99:99}],90:[function(e,t,n){"use strict";function r(e,t,n){o.call(this,e,t,n)}var o=e(93),i={clipboardData:function(e){return"clipboardData"in e?e.clipboardData:window.clipboardData}};o.augmentClass(r,i),t.exports=r},{93:93}],91:[function(e,t,n){"use strict";function r(e,t,n){o.call(this,e,t,n)}var o=e(93),i={data:null};o.augmentClass(r,i),t.exports=r},{93:93}],92:[function(e,t,n){"use strict";function r(e,t,n){o.call(this,e,t,n)}var o=e(97),i={dataTransfer:null};o.augmentClass(r,i),t.exports=r},{97:97}],93:[function(e,t,n){"use strict";function r(e,t,n){this.dispatchConfig=e,this.dispatchMarker=t,this.nativeEvent=n;var r=this.constructor.Interface;for(var o in r)if(r.hasOwnProperty(o)){var i=r[o];i?this[o]=i(n):this[o]=n[o]}var u=null!=n.defaultPrevented?n.defaultPrevented:n.returnValue===!1;u?this.isDefaultPrevented=a.thatReturnsTrue:this.isDefaultPrevented=a.thatReturnsFalse,this.isPropagationStopped=a.thatReturnsFalse}var o=e(28),i=e(27),a=e(112),u=e(123),s={type:null,target:u,currentTarget:a.thatReturnsNull,eventPhase:null,bubbles:null,cancelable:null,timeStamp:function(e){return e.timeStamp||Date.now()},defaultPrevented:null,isTrusted:null};i(r.prototype,{preventDefault:function(){this.defaultPrevented=!0;var e=this.nativeEvent;e.preventDefault?e.preventDefault():e.returnValue=!1,this.isDefaultPrevented=a.thatReturnsTrue},stopPropagation:function(){var e=this.nativeEvent;e.stopPropagation?e.stopPropagation():e.cancelBubble=!0,this.isPropagationStopped=a.thatReturnsTrue},persist:function(){this.isPersistent=a.thatReturnsTrue},isPersistent:a.thatReturnsFalse,destructor:function(){var e=this.constructor.Interface;for(var t in e)this[t]=null;this.dispatchConfig=null,this.dispatchMarker=null,this.nativeEvent=null}}),r.Interface=s,r.augmentClass=function(e,t){var n=this,r=Object.create(n.prototype);i(r,e.prototype),e.prototype=r,e.prototype.constructor=e,e.Interface=i({},n.Interface,t),e.augmentClass=n.augmentClass,o.addPoolingTo(e,o.threeArgumentPooler)},o.addPoolingTo(r,o.threeArgumentPooler),t.exports=r},{112:112,123:123,27:27,28:28}],94:[function(e,t,n){"use strict";function r(e,t,n){o.call(this,e,t,n)}var o=e(99),i={relatedTarget:null};o.augmentClass(r,i),t.exports=r},{99:99}],95:[function(e,t,n){"use strict";function r(e,t,n){o.call(this,e,t,n)}var o=e(93),i={data:null};o.augmentClass(r,i),t.exports=r},{93:93}],96:[function(e,t,n){"use strict";function r(e,t,n){o.call(this,e,t,n)}var o=e(99),i=e(120),a=e(121),u=e(122),s={key:a,location:null,ctrlKey:null,shiftKey:null,altKey:null,metaKey:null,repeat:null,locale:null,getModifierState:u,charCode:function(e){return"keypress"===e.type?i(e):0},keyCode:function(e){return"keydown"===e.type||"keyup"===e.type?e.keyCode:0},which:function(e){return"keypress"===e.type?i(e):"keydown"===e.type||"keyup"===e.type?e.keyCode:0}};o.augmentClass(r,s),t.exports=r},{120:120,121:121,122:122,99:99}],97:[function(e,t,n){"use strict";function r(e,t,n){o.call(this,e,t,n)}var o=e(99),i=e(102),a=e(122),u={screenX:null,screenY:null,clientX:null,clientY:null,ctrlKey:null,shiftKey:null,altKey:null,metaKey:null,getModifierState:a,button:function(e){var t=e.button;return"which"in e?t:2===t?2:4===t?1:0},buttons:null,relatedTarget:function(e){return e.relatedTarget||(e.fromElement===e.srcElement?e.toElement:e.fromElement)},pageX:function(e){return"pageX"in e?e.pageX:e.clientX+i.currentScrollLeft},pageY:function(e){return"pageY"in e?e.pageY:e.clientY+i.currentScrollTop}};o.augmentClass(r,u),t.exports=r},{102:102,122:122,99:99}],98:[function(e,t,n){"use strict";function r(e,t,n){o.call(this,e,t,n)}var o=e(99),i=e(122),a={touches:null,targetTouches:null,changedTouches:null,altKey:null,metaKey:null,ctrlKey:null,shiftKey:null,getModifierState:i};o.augmentClass(r,a),t.exports=r},{122:122,99:99}],99:[function(e,t,n){"use strict";function r(e,t,n){o.call(this,e,t,n)}var o=e(93),i=e(123),a={view:function(e){if(e.view)return e.view;var t=i(e);if(null!=t&&t.window===t)return t;var n=t.ownerDocument;return n?n.defaultView||n.parentWindow:window},detail:function(e){return e.detail||0}};o.augmentClass(r,a),t.exports=r},{123:123,93:93}],100:[function(e,t,n){"use strict";function r(e,t,n){o.call(this,e,t,n)}var o=e(97),i={deltaX:function(e){return"deltaX"in e?e.deltaX:"wheelDeltaX"in e?-e.wheelDeltaX:0},deltaY:function(e){return"deltaY"in e?e.deltaY:"wheelDeltaY"in e?-e.wheelDeltaY:"wheelDelta"in e?-e.wheelDelta:0},deltaZ:null,deltaMode:null};o.augmentClass(r,i),t.exports=r},{97:97}],101:[function(e,t,n){"use strict";var r=e(133),o={reinitializeTransaction:function(){this.transactionWrappers=this.getTransactionWrappers(),this.wrapperInitData?this.wrapperInitData.length=0:this.wrapperInitData=[],this._isInTransaction=!1},_isInTransaction:!1,getTransactionWrappers:null,isInTransaction:function(){return!!this._isInTransaction},perform:function(e,t,n,o,i,a,u,s){r(!this.isInTransaction());var l,c;try{this._isInTransaction=!0,l=!0,this.initializeAll(0),c=e.call(t,n,o,i,a,u,s),l=!1}finally{try{if(l)try{this.closeAll(0)}catch(p){}else this.closeAll(0)}finally{this._isInTransaction=!1}}return c},initializeAll:function(e){for(var t=this.transactionWrappers,n=e;n<t.length;n++){var r=t[n];try{this.wrapperInitData[n]=i.OBSERVED_ERROR,this.wrapperInitData[n]=r.initialize?r.initialize.call(this):null}finally{if(this.wrapperInitData[n]===i.OBSERVED_ERROR)try{this.initializeAll(n+1)}catch(o){}}}},closeAll:function(e){r(this.isInTransaction());for(var t=this.transactionWrappers,n=e;n<t.length;n++){var o,a=t[n],u=this.wrapperInitData[n];try{o=!0,u!==i.OBSERVED_ERROR&&a.close&&a.close.call(this,u),o=!1}finally{if(o)try{this.closeAll(n+1)}catch(s){}}}this.wrapperInitData.length=0}},i={Mixin:o,OBSERVED_ERROR:{}};t.exports=i},{133:133}],102:[function(e,t,n){"use strict";var r={currentScrollLeft:0,currentScrollTop:0,refreshScrollValues:function(e){r.currentScrollLeft=e.x,r.currentScrollTop=e.y}};t.exports=r},{}],103:[function(e,t,n){"use strict";function r(e,t){if(o(null!=t),null==e)return t;var n=Array.isArray(e),r=Array.isArray(t);return n&&r?(e.push.apply(e,t),e):n?(e.push(t),e):r?[e].concat(t):[e,t]}var o=e(133);t.exports=r},{133:133}],104:[function(e,t,n){"use strict";function r(e){for(var t=1,n=0,r=0;r<e.length;r++)t=(t+e.charCodeAt(r))%o,n=(n+t)%o;return t|n<<16}var o=65521;t.exports=r},{}],105:[function(e,t,n){function r(e){return e.replace(o,function(e,t){return t.toUpperCase()})}var o=/-(.)/g;t.exports=r},{}],106:[function(e,t,n){"use strict";function r(e){return o(e.replace(i,"ms-"))}var o=e(105),i=/^-ms-/;t.exports=r},{105:105}],107:[function(e,t,n){function r(e,t){return e&&t?e===t?!0:o(e)?!1:o(t)?r(e,t.parentNode):e.contains?e.contains(t):e.compareDocumentPosition?!!(16&e.compareDocumentPosition(t)):!1:!1}var o=e(137);t.exports=r},{137:137}],108:[function(e,t,n){function r(e){return!!e&&("object"==typeof e||"function"==typeof e)&&"length"in e&&!("setInterval"in e)&&"number"!=typeof e.nodeType&&(Array.isArray(e)||"callee"in e||"item"in e)}function o(e){return r(e)?Array.isArray(e)?e.slice():i(e):[e]}var i=e(148);t.exports=o},{148:148}],109:[function(e,t,n){"use strict";function r(e){var t=i.createFactory(e),n=o.createClass({tagName:e.toUpperCase(),displayName:"ReactFullPageComponent"+e,componentWillUnmount:function(){a(!1)},render:function(){return t(this.props)}});return n}var o=e(33),i=e(55),a=e(133);t.exports=r},{133:133,33:33,55:55}],110:[function(e,t,n){function r(e){var t=e.match(c);return t&&t[1].toLowerCase()}function o(e,t){var n=l;s(!!l);var o=r(e),i=o&&u(o);if(i){n.innerHTML=i[1]+e+i[2];for(var c=i[0];c--;)n=n.lastChild}else n.innerHTML=e;var p=n.getElementsByTagName("script");p.length&&(s(t),a(p).forEach(t));for(var d=a(n.childNodes);n.lastChild;)n.removeChild(n.lastChild);return d}var i=e(21),a=e(108),u=e(125),s=e(133),l=i.canUseDOM?document.createElement("div"):null,c=/^\s*<(\w+)/;t.exports=o},{108:108,125:125,133:133,21:21}],111:[function(e,t,n){"use strict";function r(e,t){var n=null==t||"boolean"==typeof t||""===t;if(n)return"";var r=isNaN(t);return r||0===t||i.hasOwnProperty(e)&&i[e]?""+t:("string"==typeof t&&(t=t.trim()),t+"px")}var o=e(4),i=o.isUnitlessNumber;t.exports=r},{4:4}],112:[function(e,t,n){function r(e){return function(){return e}}function o(){}o.thatReturns=r,o.thatReturnsFalse=r(!1),o.thatReturnsTrue=r(!0),o.thatReturnsNull=r(null),o.thatReturnsThis=function(){return this},o.thatReturnsArgument=function(e){return e},t.exports=o},{}],113:[function(e,t,n){"use strict";var r={};t.exports=r},{}],114:[function(e,t,n){"use strict";function r(e){return i[e]}function o(e){return(""+e).replace(a,r)}var i={"&":"&amp;",">":"&gt;","<":"&lt;",'"':"&quot;","'":"&#x27;"},a=/[&><"']/g;t.exports=o},{}],115:[function(e,t,n){"use strict";function r(e){return null==e?null:u(e)?e:o.has(e)?i.getNodeFromInstance(e):(a(null==e.render||"function"!=typeof e.render),void a(!1))}{var o=(e(39),e(65)),i=e(68),a=e(133),u=e(135);e(150)}t.exports=r},{133:133,135:135,150:150,39:39,65:65,68:68}],116:[function(e,t,n){"use strict";function r(e,t,n){var r=e,o=!r.hasOwnProperty(n);o&&null!=t&&(r[n]=t)}function o(e){if(null==e)return e;var t={};return i(e,r,t),t}{var i=e(149);e(150)}t.exports=o},{149:149,150:150}],117:[function(e,t,n){"use strict";function r(e){try{e.focus()}catch(t){}}t.exports=r},{}],118:[function(e,t,n){"use strict";var r=function(e,t,n){Array.isArray(e)?e.forEach(t,n):e&&t.call(n,e)};t.exports=r},{}],119:[function(e,t,n){function r(){try{return document.activeElement||document.body}catch(e){return document.body}}t.exports=r},{}],120:[function(e,t,n){"use strict";function r(e){var t,n=e.keyCode;return"charCode"in e?(t=e.charCode,0===t&&13===n&&(t=13)):t=n,t>=32||13===t?t:0}t.exports=r},{}],121:[function(e,t,n){"use strict";function r(e){if(e.key){var t=i[e.key]||e.key;if("Unidentified"!==t)return t}if("keypress"===e.type){var n=o(e);return 13===n?"Enter":String.fromCharCode(n)}return"keydown"===e.type||"keyup"===e.type?a[e.keyCode]||"Unidentified":""}var o=e(120),i={Esc:"Escape",Spacebar:" ",Left:"ArrowLeft",Up:"ArrowUp",Right:"ArrowRight",Down:"ArrowDown",Del:"Delete",Win:"OS",Menu:"ContextMenu",Apps:"ContextMenu",Scroll:"ScrollLock",MozPrintableKey:"Unidentified"},a={8:"Backspace",9:"Tab",12:"Clear",13:"Enter",16:"Shift",17:"Control",18:"Alt",19:"Pause",20:"CapsLock",27:"Escape",32:" ",33:"PageUp",34:"PageDown",35:"End",36:"Home",37:"ArrowLeft",38:"ArrowUp",39:"ArrowRight",40:"ArrowDown",45:"Insert",46:"Delete",112:"F1",113:"F2",114:"F3",115:"F4",116:"F5",117:"F6",118:"F7",119:"F8",120:"F9",121:"F10",122:"F11",123:"F12",144:"NumLock",145:"ScrollLock",224:"Meta"};t.exports=r},{120:120}],122:[function(e,t,n){"use strict";function r(e){var t=this,n=t.nativeEvent;if(n.getModifierState)return n.getModifierState(e);var r=i[e];return r?!!n[r]:!1}function o(e){return r}var i={Alt:"altKey",Control:"ctrlKey",Meta:"metaKey",Shift:"shiftKey"};t.exports=o},{}],123:[function(e,t,n){"use strict";function r(e){var t=e.target||e.srcElement||window;return 3===t.nodeType?t.parentNode:t}t.exports=r},{}],124:[function(e,t,n){"use strict";function r(e){var t=e&&(o&&e[o]||e[i]);return"function"==typeof t?t:void 0}var o="function"==typeof Symbol&&Symbol.iterator,i="@@iterator";t.exports=r},{}],125:[function(e,t,n){function r(e){return i(!!a),d.hasOwnProperty(e)||(e="*"),u.hasOwnProperty(e)||("*"===e?a.innerHTML="<link />":a.innerHTML="<"+e+"></"+e+">",u[e]=!a.firstChild),u[e]?d[e]:null}var o=e(21),i=e(133),a=o.canUseDOM?document.createElement("div"):null,u={circle:!0,clipPath:!0,defs:!0,ellipse:!0,g:!0,line:!0,linearGradient:!0,path:!0,polygon:!0,polyline:!0,radialGradient:!0,rect:!0,stop:!0,text:!0},s=[1,'<select multiple="true">',"</select>"],l=[1,"<table>","</table>"],c=[3,"<table><tbody><tr>","</tr></tbody></table>"],p=[1,"<svg>","</svg>"],d={"*":[1,"?<div>","</div>"],area:[1,"<map>","</map>"],col:[2,"<table><tbody></tbody><colgroup>","</colgroup></table>"],legend:[1,"<fieldset>","</fieldset>"],param:[1,"<object>","</object>"],tr:[2,"<table><tbody>","</tbody></table>"],optgroup:s,option:s,caption:l,colgroup:l,tbody:l,tfoot:l,thead:l,td:c,th:c,circle:p,clipPath:p,defs:p,ellipse:p,g:p,line:p,linearGradient:p,path:p,polygon:p,polyline:p,radialGradient:p,rect:p,stop:p,text:p};t.exports=r},{133:133,21:21}],126:[function(e,t,n){"use strict";function r(e){for(;e&&e.firstChild;)e=e.firstChild;return e}function o(e){for(;e;){if(e.nextSibling)return e.nextSibling;e=e.parentNode}}function i(e,t){for(var n=r(e),i=0,a=0;n;){if(3===n.nodeType){if(a=i+n.textContent.length,t>=i&&a>=t)return{node:n,offset:t-i};i=a}n=r(o(n))}}t.exports=i},{}],127:[function(e,t,n){"use strict";function r(e){return e?e.nodeType===o?e.documentElement:e.firstChild:null}var o=9;t.exports=r},{}],128:[function(e,t,n){"use strict";function r(){return!i&&o.canUseDOM&&(i="textContent"in document.documentElement?"textContent":"innerText"),i}var o=e(21),i=null;t.exports=r},{21:21}],129:[function(e,t,n){"use strict";function r(e){return e===window?{x:window.pageXOffset||document.documentElement.scrollLeft,y:window.pageYOffset||document.documentElement.scrollTop}:{x:e.scrollLeft,y:e.scrollTop}}t.exports=r},{}],130:[function(e,t,n){function r(e){return e.replace(o,"-$1").toLowerCase()}var o=/([A-Z])/g;t.exports=r},{}],131:[function(e,t,n){"use strict";function r(e){return o(e).replace(i,"-ms-")}var o=e(130),i=/^ms-/;t.exports=r},{130:130}],132:[function(e,t,n){"use strict";function r(e){return"function"==typeof e&&"undefined"!=typeof e.prototype&&"function"==typeof e.prototype.mountComponent&&"function"==typeof e.prototype.receiveComponent}function o(e,t){var n;if((null===e||e===!1)&&(e=a.emptyElement),"object"==typeof e){var o=e;n=t===o.type&&"string"==typeof o.type?u.createInternalComponent(o):r(o.type)?new o.type(o):new c}else"string"==typeof e||"number"==typeof e?n=u.createInstanceForText(e):l(!1);return n.construct(e),n._mountIndex=0,n._mountImage=null,n}var i=e(37),a=e(57),u=e(71),s=e(27),l=e(133),c=(e(150),function(){});s(c.prototype,i.Mixin,{_instantiateReactComponent:o}),t.exports=o},{133:133,150:150,27:27,37:37,57:57,71:71}],133:[function(e,t,n){"use strict";var r=function(e,t,n,r,o,i,a,u){if(!e){var s;if(void 0===t)s=new Error("Minified exception occurred; use the non-minified dev environment for the full error message and additional helpful warnings.");else{var l=[n,r,o,i,a,u],c=0;s=new Error("Invariant Violation: "+t.replace(/%s/g,function(){return l[c++]}))}throw s.framesToPop=1,s}};t.exports=r},{}],134:[function(e,t,n){"use strict";function r(e,t){if(!i.canUseDOM||t&&!("addEventListener"in document))return!1;var n="on"+e,r=n in document;if(!r){var a=document.createElement("div");a.setAttribute(n,"return;"),r="function"==typeof a[n]}return!r&&o&&"wheel"===e&&(r=document.implementation.hasFeature("Events.wheel","3.0")),r}var o,i=e(21);i.canUseDOM&&(o=document.implementation&&document.implementation.hasFeature&&document.implementation.hasFeature("","")!==!0),t.exports=r},{21:21}],135:[function(e,t,n){function r(e){return!(!e||!("function"==typeof Node?e instanceof Node:"object"==typeof e&&"number"==typeof e.nodeType&&"string"==typeof e.nodeName))}t.exports=r},{}],136:[function(e,t,n){"use strict";function r(e){return e&&("INPUT"===e.nodeName&&o[e.type]||"TEXTAREA"===e.nodeName)}var o={color:!0,date:!0,datetime:!0,"datetime-local":!0,email:!0,month:!0,number:!0,password:!0,range:!0,search:!0,tel:!0,text:!0,time:!0,url:!0,week:!0};t.exports=r},{}],137:[function(e,t,n){function r(e){return o(e)&&3==e.nodeType}var o=e(135);t.exports=r},{135:135}],138:[function(e,t,n){"use strict";var r=e(133),o=function(e){var t,n={};r(e instanceof Object&&!Array.isArray(e));for(t in e)e.hasOwnProperty(t)&&(n[t]=t);return n};t.exports=o},{133:133}],139:[function(e,t,n){var r=function(e){var t;for(t in e)if(e.hasOwnProperty(t))return t;return null};t.exports=r},{}],140:[function(e,t,n){"use strict";function r(e,t,n){if(!e)return null;var r={};for(var i in e)o.call(e,i)&&(r[i]=t.call(n,e[i],i,e));return r}var o=Object.prototype.hasOwnProperty;t.exports=r},{}],141:[function(e,t,n){"use strict";function r(e){var t={};return function(n){return t.hasOwnProperty(n)||(t[n]=e.call(this,n)),t[n]}}t.exports=r},{}],142:[function(e,t,n){"use strict";function r(e){return i(o.isValidElement(e)),e}var o=e(55),i=e(133);t.exports=r},{133:133,55:55}],143:[function(e,t,n){"use strict";function r(e){return'"'+o(e)+'"'}var o=e(114);t.exports=r},{114:114}],144:[function(e,t,n){"use strict";var r=e(21),o=/^[ \r\n\t\f]/,i=/<(!--|link|noscript|meta|script|style)[ \r\n\t\f\/>]/,a=function(e,t){e.innerHTML=t};if("undefined"!=typeof MSApp&&MSApp.execUnsafeLocalFunction&&(a=function(e,t){MSApp.execUnsafeLocalFunction(function(){e.innerHTML=t})}),r.canUseDOM){var u=document.createElement("div");u.innerHTML=" ",""===u.innerHTML&&(a=function(e,t){if(e.parentNode&&e.parentNode.replaceChild(e,e),o.test(t)||"<"===t[0]&&i.test(t)){e.innerHTML="\ufeff"+t;var n=e.firstChild;1===n.data.length?e.removeChild(n):n.deleteData(0,1)}else e.innerHTML=t})}t.exports=a},{21:21}],145:[function(e,t,n){"use strict";var r=e(21),o=e(114),i=e(144),a=function(e,t){e.textContent=t};r.canUseDOM&&("textContent"in document.documentElement||(a=function(e,t){i(e,o(t))})),t.exports=a},{114:114,144:144,21:21}],146:[function(e,t,n){"use strict";function r(e,t){if(e===t)return!0;var n;for(n in e)if(e.hasOwnProperty(n)&&(!t.hasOwnProperty(n)||e[n]!==t[n]))return!1;for(n in t)if(t.hasOwnProperty(n)&&!e.hasOwnProperty(n))return!1;return!0}t.exports=r},{}],147:[function(e,t,n){"use strict";function r(e,t){if(null!=e&&null!=t){var n=typeof e,r=typeof t;if("string"===n||"number"===n)return"string"===r||"number"===r;if("object"===r&&e.type===t.type&&e.key===t.key){var o=e._owner===t._owner;return o}}return!1}e(150);t.exports=r},{150:150}],148:[function(e,t,n){function r(e){var t=e.length;if(o(!Array.isArray(e)&&("object"==typeof e||"function"==typeof e)),o("number"==typeof t),o(0===t||t-1 in e),e.hasOwnProperty)try{return Array.prototype.slice.call(e)}catch(n){}for(var r=Array(t),i=0;t>i;i++)r[i]=e[i];return r}var o=e(133);t.exports=r},{133:133}],149:[function(e,t,n){"use strict";function r(e){return v[e]}function o(e,t){return e&&null!=e.key?a(e.key):t.toString(36)}function i(e){return(""+e).replace(g,r)}function a(e){return"$"+i(e)}function u(e,t,n,r,i){var s=typeof e;if(("undefined"===s||"boolean"===s)&&(e=null),null===e||"string"===s||"number"===s||l.isValidElement(e))return r(i,e,""===t?h+o(e,0):t,n),1;var p,v,g,y=0;if(Array.isArray(e))for(var C=0;C<e.length;C++)p=e[C],v=(""!==t?t+m:h)+o(p,C),g=n+y,y+=u(p,v,g,r,i);else{var E=d(e);if(E){var b,_=E.call(e);if(E!==e.entries)for(var x=0;!(b=_.next()).done;)p=b.value,v=(""!==t?t+m:h)+o(p,x++),g=n+y,y+=u(p,v,g,r,i);else for(;!(b=_.next()).done;){var D=b.value;D&&(p=D[1],v=(""!==t?t+m:h)+a(D[0])+m+o(p,0),g=n+y,y+=u(p,v,g,r,i))}}else if("object"===s){f(1!==e.nodeType);var M=c.extract(e);for(var N in M)M.hasOwnProperty(N)&&(p=M[N],v=(""!==t?t+m:h)+a(N)+m+o(p,0),g=n+y,y+=u(p,v,g,r,i))}}return y}function s(e,t,n){return null==e?0:u(e,"",0,t,n)}var l=e(55),c=e(61),p=e(64),d=e(124),f=e(133),h=(e(150),p.SEPARATOR),m=":",v={"=":"=0",".":"=1",":":"=2"},g=/[=.:]/g;t.exports=s},{124:124,133:133,150:150,55:55,61:61,64:64}],150:[function(e,t,n){"use strict";var r=e(112),o=r;t.exports=o},{112:112}]},{},[1])(1)});
;(function(){
-var h,aa=aa||{},ca=this;function da(a,b){var c=a.split("."),d=ca;c[0]in d||!d.execScript||d.execScript("var "+c[0]);for(var e;c.length&&(e=c.shift());)c.length||void 0===b?d=d[e]?d[e]:d[e]={}:d[e]=b}function ea(){}
-function p(a){var b=typeof a;if("object"==b)if(a){if(a instanceof Array)return"array";if(a instanceof Object)return b;var c=Object.prototype.toString.call(a);if("[object Window]"==c)return"object";if("[object Array]"==c||"number"==typeof a.length&&"undefined"!=typeof a.splice&&"undefined"!=typeof a.propertyIsEnumerable&&!a.propertyIsEnumerable("splice"))return"array";if("[object Function]"==c||"undefined"!=typeof a.call&&"undefined"!=typeof a.propertyIsEnumerable&&!a.propertyIsEnumerable("call"))return"function"}else return"null";
-else if("function"==b&&"undefined"==typeof a.call)return"object";return b}function fa(a){return"array"==p(a)}function ga(a){var b=p(a);return"array"==b||"object"==b&&"number"==typeof a.length}function ha(a){return"string"==typeof a}function ia(a){return"function"==p(a)}function ja(a){return a[ka]||(a[ka]=++la)}var ka="closure_uid_"+(1E9*Math.random()>>>0),la=0;function ma(a,b,c){return a.call.apply(a.bind,arguments)}
-function na(a,b,c){if(!a)throw Error();if(2<arguments.length){var d=Array.prototype.slice.call(arguments,2);return function(){var c=Array.prototype.slice.call(arguments);Array.prototype.unshift.apply(c,d);return a.apply(b,c)}}return function(){return a.apply(b,arguments)}}function pa(a,b,c){pa=Function.prototype.bind&&-1!=Function.prototype.bind.toString().indexOf("native code")?ma:na;return pa.apply(null,arguments)}var qa=Date.now||function(){return+new Date};
-function ra(a,b){function c(){}c.prototype=b.prototype;a.Bf=b.prototype;a.prototype=new c;a.prototype.constructor=a;a.base=function(a,c,f){for(var g=Array(arguments.length-2),k=2;k<arguments.length;k++)g[k-2]=arguments[k];return b.prototype[c].apply(a,g)}};function sa(a,b){for(var c=a.split("%s"),d="",e=Array.prototype.slice.call(arguments,1);e.length&&1<c.length;)d+=c.shift()+e.shift();return d+c.join("%s")}var ta=String.prototype.trim?function(a){return a.trim()}:function(a){return a.replace(/^[\s\xa0]+|[\s\xa0]+$/g,"")};function ua(a,b){return a<b?-1:a>b?1:0};function va(a,b){for(var c in a)b.call(void 0,a[c],c,a)}function za(a,b){for(var c in a)if(b.call(void 0,a[c],c,a))return!0;return!1}function Aa(a){var b=[],c=0,d;for(d in a)b[c++]=a[d];return b}function Ba(a){var b=[],c=0,d;for(d in a)b[c++]=d;return b}function Da(a){return null!==a&&"withCredentials"in a}var Ea="constructor hasOwnProperty isPrototypeOf propertyIsEnumerable toLocaleString toString valueOf".split(" ");
-function Fa(a,b){for(var c,d,e=1;e<arguments.length;e++){d=arguments[e];for(c in d)a[c]=d[c];for(var f=0;f<Ea.length;f++)c=Ea[f],Object.prototype.hasOwnProperty.call(d,c)&&(a[c]=d[c])}};function Ga(a,b){null!=a&&this.append.apply(this,arguments)}h=Ga.prototype;h.Zb="";h.set=function(a){this.Zb=""+a};h.append=function(a,b,c){this.Zb+=a;if(null!=b)for(var d=1;d<arguments.length;d++)this.Zb+=arguments[d];return this};h.clear=function(){this.Zb=""};h.toString=function(){return this.Zb};function Ha(a){if(Error.captureStackTrace)Error.captureStackTrace(this,Ha);else{var b=Error().stack;b&&(this.stack=b)}a&&(this.message=String(a))}ra(Ha,Error);Ha.prototype.name="CustomError";function Ia(a,b){b.unshift(a);Ha.call(this,sa.apply(null,b));b.shift()}ra(Ia,Ha);Ia.prototype.name="AssertionError";function Ja(a,b){throw new Ia("Failure"+(a?": "+a:""),Array.prototype.slice.call(arguments,1));};var Ka=Array.prototype,La=Ka.indexOf?function(a,b,c){return Ka.indexOf.call(a,b,c)}:function(a,b,c){c=null==c?0:0>c?Math.max(0,a.length+c):c;if(ha(a))return ha(b)&&1==b.length?a.indexOf(b,c):-1;for(;c<a.length;c++)if(c in a&&a[c]===b)return c;return-1},Ma=Ka.forEach?function(a,b,c){Ka.forEach.call(a,b,c)}:function(a,b,c){for(var d=a.length,e=ha(a)?a.split(""):a,f=0;f<d;f++)f in e&&b.call(c,e[f],f,a)};
-function Qa(a){var b;a:{b=Sa;for(var c=a.length,d=ha(a)?a.split(""):a,e=0;e<c;e++)if(e in d&&b.call(void 0,d[e],e,a)){b=e;break a}b=-1}return 0>b?null:ha(a)?a.charAt(b):a[b]}function Ua(a){return Ka.concat.apply(Ka,arguments)}function Va(a){var b=a.length;if(0<b){for(var c=Array(b),d=0;d<b;d++)c[d]=a[d];return c}return[]}function Xa(a,b){return a>b?1:a<b?-1:0};var Ya;if("undefined"===typeof Za)var Za=function(){throw Error("No *print-fn* fn set for evaluation environment");};if("undefined"===typeof ab)var ab=function(){throw Error("No *print-err-fn* fn set for evaluation environment");};var bb=!0,cb=null;if("undefined"===typeof db)var db=null;function gb(){return new r(null,5,[hb,!0,ib,!0,jb,!1,lb,!1,mb,null],null)}nb;function u(a){return null!=a&&!1!==a}pb;v;function qb(a){return null==a}function rb(a){return a instanceof Array}
-function tb(a){return null==a?!0:!1===a?!0:!1}function ub(a,b){return a[p(null==b?null:b)]?!0:a._?!0:!1}function vb(a){return null==a?null:a.constructor}function x(a,b){var c=vb(b),c=u(u(c)?c.mc:c)?c.Tb:p(b);return Error(["No protocol method ",a," defined for type ",c,": ",b].join(""))}function wb(a){var b=a.Tb;return u(b)?b:""+y(a)}var xb="undefined"!==typeof Symbol&&"function"===p(Symbol)?Symbol.iterator:"@@iterator";
-function yb(a){for(var b=a.length,c=Array(b),d=0;;)if(d<b)c[d]=a[d],d+=1;else break;return c}A;var zb=function zb(b){for(var c=[],d=arguments.length,e=0;;)if(e<d)c.push(arguments[e]),e+=1;else break;switch(c.length){case 2:return zb.h(arguments[0],arguments[1]);default:return zb.w(arguments[0],arguments[1],new B(c.slice(2),0))}};zb.h=function(a,b){return a[b]};zb.w=function(a,b,c){a=a[b];return A.l?A.l(zb,a,c):A.call(null,zb,a,c)};zb.K=function(a){var b=C(a),c=D(a);a=C(c);c=D(c);return zb.w(b,a,c)};
-zb.J=2;Ab;var nb=function nb(b){for(var c=[],d=arguments.length,e=0;;)if(e<d)c.push(arguments[e]),e+=1;else break;switch(c.length){case 1:return nb.j(arguments[0]);case 2:return nb.h(arguments[0],arguments[1]);default:throw Error([y("Invalid arity: "),y(c.length)].join(""));}};nb.j=function(a){return nb.h(null,a)};nb.h=function(a,b){function c(a,b){a.push(b);return a}var d=[];return Ab.l?Ab.l(c,d,b):Ab.call(null,c,d,b)};nb.J=2;function Bb(){}function Cb(){}
-var Db=function Db(b){if(null!=b&&null!=b.Za)return b.Za(b);var c=Db[p(null==b?null:b)];if(null!=c)return c.j?c.j(b):c.call(null,b);c=Db._;if(null!=c)return c.j?c.j(b):c.call(null,b);throw x("ICloneable.-clone",b);};function Eb(){}
-var Gb=function Gb(b){if(null!=b&&null!=b.ia)return b.ia(b);var c=Gb[p(null==b?null:b)];if(null!=c)return c.j?c.j(b):c.call(null,b);c=Gb._;if(null!=c)return c.j?c.j(b):c.call(null,b);throw x("ICounted.-count",b);},Hb=function Hb(b){if(null!=b&&null!=b.qa)return b.qa(b);var c=Hb[p(null==b?null:b)];if(null!=c)return c.j?c.j(b):c.call(null,b);c=Hb._;if(null!=c)return c.j?c.j(b):c.call(null,b);throw x("IEmptyableCollection.-empty",b);};function Ib(){}
-var Jb=function Jb(b,c){if(null!=b&&null!=b.ha)return b.ha(b,c);var d=Jb[p(null==b?null:b)];if(null!=d)return d.h?d.h(b,c):d.call(null,b,c);d=Jb._;if(null!=d)return d.h?d.h(b,c):d.call(null,b,c);throw x("ICollection.-conj",b);};function Kb(){}
-var Lb=function Lb(b){for(var c=[],d=arguments.length,e=0;;)if(e<d)c.push(arguments[e]),e+=1;else break;switch(c.length){case 2:return Lb.h(arguments[0],arguments[1]);case 3:return Lb.l(arguments[0],arguments[1],arguments[2]);default:throw Error([y("Invalid arity: "),y(c.length)].join(""));}};
-Lb.h=function(a,b){if(null!=a&&null!=a.aa)return a.aa(a,b);var c=Lb[p(null==a?null:a)];if(null!=c)return c.h?c.h(a,b):c.call(null,a,b);c=Lb._;if(null!=c)return c.h?c.h(a,b):c.call(null,a,b);throw x("IIndexed.-nth",a);};Lb.l=function(a,b,c){if(null!=a&&null!=a.kb)return a.kb(a,b,c);var d=Lb[p(null==a?null:a)];if(null!=d)return d.l?d.l(a,b,c):d.call(null,a,b,c);d=Lb._;if(null!=d)return d.l?d.l(a,b,c):d.call(null,a,b,c);throw x("IIndexed.-nth",a);};Lb.J=3;function Mb(){}
-var Nb=function Nb(b){if(null!=b&&null!=b.wa)return b.wa(b);var c=Nb[p(null==b?null:b)];if(null!=c)return c.j?c.j(b):c.call(null,b);c=Nb._;if(null!=c)return c.j?c.j(b):c.call(null,b);throw x("ISeq.-first",b);},Pb=function Pb(b){if(null!=b&&null!=b.Da)return b.Da(b);var c=Pb[p(null==b?null:b)];if(null!=c)return c.j?c.j(b):c.call(null,b);c=Pb._;if(null!=c)return c.j?c.j(b):c.call(null,b);throw x("ISeq.-rest",b);};function Sb(){}function Tb(){}
-var Ub=function Ub(b){for(var c=[],d=arguments.length,e=0;;)if(e<d)c.push(arguments[e]),e+=1;else break;switch(c.length){case 2:return Ub.h(arguments[0],arguments[1]);case 3:return Ub.l(arguments[0],arguments[1],arguments[2]);default:throw Error([y("Invalid arity: "),y(c.length)].join(""));}};
-Ub.h=function(a,b){if(null!=a&&null!=a.X)return a.X(a,b);var c=Ub[p(null==a?null:a)];if(null!=c)return c.h?c.h(a,b):c.call(null,a,b);c=Ub._;if(null!=c)return c.h?c.h(a,b):c.call(null,a,b);throw x("ILookup.-lookup",a);};Ub.l=function(a,b,c){if(null!=a&&null!=a.P)return a.P(a,b,c);var d=Ub[p(null==a?null:a)];if(null!=d)return d.l?d.l(a,b,c):d.call(null,a,b,c);d=Ub._;if(null!=d)return d.l?d.l(a,b,c):d.call(null,a,b,c);throw x("ILookup.-lookup",a);};Ub.J=3;
-var Vb=function Vb(b,c){if(null!=b&&null!=b.Hd)return b.Hd(b,c);var d=Vb[p(null==b?null:b)];if(null!=d)return d.h?d.h(b,c):d.call(null,b,c);d=Vb._;if(null!=d)return d.h?d.h(b,c):d.call(null,b,c);throw x("IAssociative.-contains-key?",b);},Wb=function Wb(b,c,d){if(null!=b&&null!=b.Gb)return b.Gb(b,c,d);var e=Wb[p(null==b?null:b)];if(null!=e)return e.l?e.l(b,c,d):e.call(null,b,c,d);e=Wb._;if(null!=e)return e.l?e.l(b,c,d):e.call(null,b,c,d);throw x("IAssociative.-assoc",b);};function Yb(){}
-var Zb=function Zb(b,c){if(null!=b&&null!=b.jc)return b.jc(b,c);var d=Zb[p(null==b?null:b)];if(null!=d)return d.h?d.h(b,c):d.call(null,b,c);d=Zb._;if(null!=d)return d.h?d.h(b,c):d.call(null,b,c);throw x("IMap.-dissoc",b);};function $b(){}
-var ac=function ac(b){if(null!=b&&null!=b.Yc)return b.Yc(b);var c=ac[p(null==b?null:b)];if(null!=c)return c.j?c.j(b):c.call(null,b);c=ac._;if(null!=c)return c.j?c.j(b):c.call(null,b);throw x("IMapEntry.-key",b);},bc=function bc(b){if(null!=b&&null!=b.Zc)return b.Zc(b);var c=bc[p(null==b?null:b)];if(null!=c)return c.j?c.j(b):c.call(null,b);c=bc._;if(null!=c)return c.j?c.j(b):c.call(null,b);throw x("IMapEntry.-val",b);};function cc(){}
-var dc=function dc(b,c){if(null!=b&&null!=b.xe)return b.xe(b,c);var d=dc[p(null==b?null:b)];if(null!=d)return d.h?d.h(b,c):d.call(null,b,c);d=dc._;if(null!=d)return d.h?d.h(b,c):d.call(null,b,c);throw x("ISet.-disjoin",b);},ec=function ec(b){if(null!=b&&null!=b.ac)return b.ac(b);var c=ec[p(null==b?null:b)];if(null!=c)return c.j?c.j(b):c.call(null,b);c=ec._;if(null!=c)return c.j?c.j(b):c.call(null,b);throw x("IStack.-peek",b);},fc=function fc(b){if(null!=b&&null!=b.bc)return b.bc(b);var c=fc[p(null==
-b?null:b)];if(null!=c)return c.j?c.j(b):c.call(null,b);c=fc._;if(null!=c)return c.j?c.j(b):c.call(null,b);throw x("IStack.-pop",b);};function gc(){}
-var hc=function hc(b,c,d){if(null!=b&&null!=b.lc)return b.lc(b,c,d);var e=hc[p(null==b?null:b)];if(null!=e)return e.l?e.l(b,c,d):e.call(null,b,c,d);e=hc._;if(null!=e)return e.l?e.l(b,c,d):e.call(null,b,c,d);throw x("IVector.-assoc-n",b);},ic=function ic(b){if(null!=b&&null!=b.$b)return b.$b(b);var c=ic[p(null==b?null:b)];if(null!=c)return c.j?c.j(b):c.call(null,b);c=ic._;if(null!=c)return c.j?c.j(b):c.call(null,b);throw x("IDeref.-deref",b);};function jc(){}
-var kc=function kc(b){if(null!=b&&null!=b.Z)return b.Z(b);var c=kc[p(null==b?null:b)];if(null!=c)return c.j?c.j(b):c.call(null,b);c=kc._;if(null!=c)return c.j?c.j(b):c.call(null,b);throw x("IMeta.-meta",b);};function lc(){}var mc=function mc(b,c){if(null!=b&&null!=b.ba)return b.ba(b,c);var d=mc[p(null==b?null:b)];if(null!=d)return d.h?d.h(b,c):d.call(null,b,c);d=mc._;if(null!=d)return d.h?d.h(b,c):d.call(null,b,c);throw x("IWithMeta.-with-meta",b);};function nc(){}
-var oc=function oc(b){for(var c=[],d=arguments.length,e=0;;)if(e<d)c.push(arguments[e]),e+=1;else break;switch(c.length){case 2:return oc.h(arguments[0],arguments[1]);case 3:return oc.l(arguments[0],arguments[1],arguments[2]);default:throw Error([y("Invalid arity: "),y(c.length)].join(""));}};
-oc.h=function(a,b){if(null!=a&&null!=a.za)return a.za(a,b);var c=oc[p(null==a?null:a)];if(null!=c)return c.h?c.h(a,b):c.call(null,a,b);c=oc._;if(null!=c)return c.h?c.h(a,b):c.call(null,a,b);throw x("IReduce.-reduce",a);};oc.l=function(a,b,c){if(null!=a&&null!=a.Aa)return a.Aa(a,b,c);var d=oc[p(null==a?null:a)];if(null!=d)return d.l?d.l(a,b,c):d.call(null,a,b,c);d=oc._;if(null!=d)return d.l?d.l(a,b,c):d.call(null,a,b,c);throw x("IReduce.-reduce",a);};oc.J=3;
-var pc=function pc(b,c,d){if(null!=b&&null!=b.Cc)return b.Cc(b,c,d);var e=pc[p(null==b?null:b)];if(null!=e)return e.l?e.l(b,c,d):e.call(null,b,c,d);e=pc._;if(null!=e)return e.l?e.l(b,c,d):e.call(null,b,c,d);throw x("IKVReduce.-kv-reduce",b);},rc=function rc(b,c){if(null!=b&&null!=b.L)return b.L(b,c);var d=rc[p(null==b?null:b)];if(null!=d)return d.h?d.h(b,c):d.call(null,b,c);d=rc._;if(null!=d)return d.h?d.h(b,c):d.call(null,b,c);throw x("IEquiv.-equiv",b);},sc=function sc(b){if(null!=b&&null!=b.W)return b.W(b);
-var c=sc[p(null==b?null:b)];if(null!=c)return c.j?c.j(b):c.call(null,b);c=sc._;if(null!=c)return c.j?c.j(b):c.call(null,b);throw x("IHash.-hash",b);};function tc(){}var uc=function uc(b){if(null!=b&&null!=b.fa)return b.fa(b);var c=uc[p(null==b?null:b)];if(null!=c)return c.j?c.j(b):c.call(null,b);c=uc._;if(null!=c)return c.j?c.j(b):c.call(null,b);throw x("ISeqable.-seq",b);};function vc(){}function xc(){}function yc(){}
-var zc=function zc(b){if(null!=b&&null!=b.Dc)return b.Dc(b);var c=zc[p(null==b?null:b)];if(null!=c)return c.j?c.j(b):c.call(null,b);c=zc._;if(null!=c)return c.j?c.j(b):c.call(null,b);throw x("IReversible.-rseq",b);},Ac=function Ac(b,c){if(null!=b&&null!=b.df)return b.df(0,c);var d=Ac[p(null==b?null:b)];if(null!=d)return d.h?d.h(b,c):d.call(null,b,c);d=Ac._;if(null!=d)return d.h?d.h(b,c):d.call(null,b,c);throw x("IWriter.-write",b);},Bc=function Bc(b,c,d){if(null!=b&&null!=b.T)return b.T(b,c,d);var e=
-Bc[p(null==b?null:b)];if(null!=e)return e.l?e.l(b,c,d):e.call(null,b,c,d);e=Bc._;if(null!=e)return e.l?e.l(b,c,d):e.call(null,b,c,d);throw x("IPrintWithWriter.-pr-writer",b);},Cc=function Cc(b,c,d){if(null!=b&&null!=b.Kd)return b.Kd(b,c,d);var e=Cc[p(null==b?null:b)];if(null!=e)return e.l?e.l(b,c,d):e.call(null,b,c,d);e=Cc._;if(null!=e)return e.l?e.l(b,c,d):e.call(null,b,c,d);throw x("IWatchable.-notify-watches",b);},Dc=function Dc(b,c,d){if(null!=b&&null!=b.Jd)return b.Jd(b,c,d);var e=Dc[p(null==
-b?null:b)];if(null!=e)return e.l?e.l(b,c,d):e.call(null,b,c,d);e=Dc._;if(null!=e)return e.l?e.l(b,c,d):e.call(null,b,c,d);throw x("IWatchable.-add-watch",b);},Ec=function Ec(b,c){if(null!=b&&null!=b.Ld)return b.Ld(b,c);var d=Ec[p(null==b?null:b)];if(null!=d)return d.h?d.h(b,c):d.call(null,b,c);d=Ec._;if(null!=d)return d.h?d.h(b,c):d.call(null,b,c);throw x("IWatchable.-remove-watch",b);},Fc=function Fc(b){if(null!=b&&null!=b.Bc)return b.Bc(b);var c=Fc[p(null==b?null:b)];if(null!=c)return c.j?c.j(b):
-c.call(null,b);c=Fc._;if(null!=c)return c.j?c.j(b):c.call(null,b);throw x("IEditableCollection.-as-transient",b);},Gc=function Gc(b,c){if(null!=b&&null!=b.kc)return b.kc(b,c);var d=Gc[p(null==b?null:b)];if(null!=d)return d.h?d.h(b,c):d.call(null,b,c);d=Gc._;if(null!=d)return d.h?d.h(b,c):d.call(null,b,c);throw x("ITransientCollection.-conj!",b);},Hc=function Hc(b){if(null!=b&&null!=b.Ec)return b.Ec(b);var c=Hc[p(null==b?null:b)];if(null!=c)return c.j?c.j(b):c.call(null,b);c=Hc._;if(null!=c)return c.j?
-c.j(b):c.call(null,b);throw x("ITransientCollection.-persistent!",b);},Ic=function Ic(b,c,d){if(null!=b&&null!=b.bd)return b.bd(b,c,d);var e=Ic[p(null==b?null:b)];if(null!=e)return e.l?e.l(b,c,d):e.call(null,b,c,d);e=Ic._;if(null!=e)return e.l?e.l(b,c,d):e.call(null,b,c,d);throw x("ITransientAssociative.-assoc!",b);},Jc=function Jc(b,c,d){if(null!=b&&null!=b.bf)return b.bf(0,c,d);var e=Jc[p(null==b?null:b)];if(null!=e)return e.l?e.l(b,c,d):e.call(null,b,c,d);e=Jc._;if(null!=e)return e.l?e.l(b,c,d):
-e.call(null,b,c,d);throw x("ITransientVector.-assoc-n!",b);};function Kc(){}
-var Lc=function Lc(b,c){if(null!=b&&null!=b.Sb)return b.Sb(b,c);var d=Lc[p(null==b?null:b)];if(null!=d)return d.h?d.h(b,c):d.call(null,b,c);d=Lc._;if(null!=d)return d.h?d.h(b,c):d.call(null,b,c);throw x("IComparable.-compare",b);},Mc=function Mc(b){if(null!=b&&null!=b.Ye)return b.Ye();var c=Mc[p(null==b?null:b)];if(null!=c)return c.j?c.j(b):c.call(null,b);c=Mc._;if(null!=c)return c.j?c.j(b):c.call(null,b);throw x("IChunk.-drop-first",b);},Nc=function Nc(b){if(null!=b&&null!=b.se)return b.se(b);var c=
-Nc[p(null==b?null:b)];if(null!=c)return c.j?c.j(b):c.call(null,b);c=Nc._;if(null!=c)return c.j?c.j(b):c.call(null,b);throw x("IChunkedSeq.-chunked-first",b);},Oc=function Oc(b){if(null!=b&&null!=b.te)return b.te(b);var c=Oc[p(null==b?null:b)];if(null!=c)return c.j?c.j(b):c.call(null,b);c=Oc._;if(null!=c)return c.j?c.j(b):c.call(null,b);throw x("IChunkedSeq.-chunked-rest",b);},Pc=function Pc(b){if(null!=b&&null!=b.re)return b.re(b);var c=Pc[p(null==b?null:b)];if(null!=c)return c.j?c.j(b):c.call(null,
-b);c=Pc._;if(null!=c)return c.j?c.j(b):c.call(null,b);throw x("IChunkedNext.-chunked-next",b);},Qc=function Qc(b){if(null!=b&&null!=b.$c)return b.$c(b);var c=Qc[p(null==b?null:b)];if(null!=c)return c.j?c.j(b):c.call(null,b);c=Qc._;if(null!=c)return c.j?c.j(b):c.call(null,b);throw x("INamed.-name",b);},Rc=function Rc(b){if(null!=b&&null!=b.ad)return b.ad(b);var c=Rc[p(null==b?null:b)];if(null!=c)return c.j?c.j(b):c.call(null,b);c=Rc._;if(null!=c)return c.j?c.j(b):c.call(null,b);throw x("INamed.-namespace",
-b);},Sc=function Sc(b,c){if(null!=b&&null!=b.we)return b.we(b,c);var d=Sc[p(null==b?null:b)];if(null!=d)return d.h?d.h(b,c):d.call(null,b,c);d=Sc._;if(null!=d)return d.h?d.h(b,c):d.call(null,b,c);throw x("IReset.-reset!",b);},Tc=function Tc(b){for(var c=[],d=arguments.length,e=0;;)if(e<d)c.push(arguments[e]),e+=1;else break;switch(c.length){case 2:return Tc.h(arguments[0],arguments[1]);case 3:return Tc.l(arguments[0],arguments[1],arguments[2]);case 4:return Tc.G(arguments[0],arguments[1],arguments[2],
-arguments[3]);case 5:return Tc.N(arguments[0],arguments[1],arguments[2],arguments[3],arguments[4]);default:throw Error([y("Invalid arity: "),y(c.length)].join(""));}};Tc.h=function(a,b){if(null!=a&&null!=a.ye)return a.ye(a,b);var c=Tc[p(null==a?null:a)];if(null!=c)return c.h?c.h(a,b):c.call(null,a,b);c=Tc._;if(null!=c)return c.h?c.h(a,b):c.call(null,a,b);throw x("ISwap.-swap!",a);};
-Tc.l=function(a,b,c){if(null!=a&&null!=a.ze)return a.ze(a,b,c);var d=Tc[p(null==a?null:a)];if(null!=d)return d.l?d.l(a,b,c):d.call(null,a,b,c);d=Tc._;if(null!=d)return d.l?d.l(a,b,c):d.call(null,a,b,c);throw x("ISwap.-swap!",a);};Tc.G=function(a,b,c,d){if(null!=a&&null!=a.Ae)return a.Ae(a,b,c,d);var e=Tc[p(null==a?null:a)];if(null!=e)return e.G?e.G(a,b,c,d):e.call(null,a,b,c,d);e=Tc._;if(null!=e)return e.G?e.G(a,b,c,d):e.call(null,a,b,c,d);throw x("ISwap.-swap!",a);};
-Tc.N=function(a,b,c,d,e){if(null!=a&&null!=a.Be)return a.Be(a,b,c,d,e);var f=Tc[p(null==a?null:a)];if(null!=f)return f.N?f.N(a,b,c,d,e):f.call(null,a,b,c,d,e);f=Tc._;if(null!=f)return f.N?f.N(a,b,c,d,e):f.call(null,a,b,c,d,e);throw x("ISwap.-swap!",a);};Tc.J=5;
-var Uc=function Uc(b,c){if(null!=b&&null!=b.cf)return b.cf(0,c);var d=Uc[p(null==b?null:b)];if(null!=d)return d.h?d.h(b,c):d.call(null,b,c);d=Uc._;if(null!=d)return d.h?d.h(b,c):d.call(null,b,c);throw x("IVolatile.-vreset!",b);},Vc=function Vc(b){if(null!=b&&null!=b.qb)return b.qb(b);var c=Vc[p(null==b?null:b)];if(null!=c)return c.j?c.j(b):c.call(null,b);c=Vc._;if(null!=c)return c.j?c.j(b):c.call(null,b);throw x("IIterable.-iterator",b);};function Wc(a){this.ng=a;this.o=1073741824;this.M=0}
-Wc.prototype.df=function(a,b){return this.ng.append(b)};function Xc(a){var b=new Ga;a.T(null,new Wc(b),gb());return""+y(b)}var Yc="undefined"!==typeof Math.imul&&0!==Math.imul(4294967295,5)?function(a,b){return Math.imul(a,b)}:function(a,b){var c=a&65535,d=b&65535;return c*d+((a>>>16&65535)*d+c*(b>>>16&65535)<<16>>>0)|0};function Zc(a){a=Yc(a|0,-862048943);return Yc(a<<15|a>>>-15,461845907)}function $c(a,b){var c=(a|0)^(b|0);return Yc(c<<13|c>>>-13,5)+-430675100|0}
-function ad(a,b){var c=(a|0)^b,c=Yc(c^c>>>16,-2048144789),c=Yc(c^c>>>13,-1028477387);return c^c>>>16}function cd(a){var b;a:{b=1;for(var c=0;;)if(b<a.length){var d=b+2,c=$c(c,Zc(a.charCodeAt(b-1)|a.charCodeAt(b)<<16));b=d}else{b=c;break a}}b=1===(a.length&1)?b^Zc(a.charCodeAt(a.length-1)):b;return ad(b,Yc(2,a.length))}G;dd;H;ed;var fd={},gd=0;
-function hd(a){255<gd&&(fd={},gd=0);var b=fd[a];if("number"!==typeof b){a:if(null!=a)if(b=a.length,0<b)for(var c=0,d=0;;)if(c<b)var e=c+1,d=Yc(31,d)+a.charCodeAt(c),c=e;else{b=d;break a}else b=0;else b=0;fd[a]=b;gd+=1}return a=b}function id(a){null!=a&&(a.o&4194304||a.ve)?a=a.W(null):"number"===typeof a?a=Math.floor(a)%2147483647:!0===a?a=1:!1===a?a=0:"string"===typeof a?(a=hd(a),0!==a&&(a=Zc(a),a=$c(0,a),a=ad(a,4))):a=a instanceof Date?a.valueOf():null==a?0:sc(a);return a}
-function jd(a,b){return a^b+2654435769+(a<<6)+(a>>2)}function pb(a,b){return b instanceof a}function kd(a,b){if(a.ib===b.ib)return 0;var c=tb(a.bb);if(u(c?b.bb:c))return-1;if(u(a.bb)){if(tb(b.bb))return 1;c=Xa(a.bb,b.bb);return 0===c?Xa(a.name,b.name):c}return Xa(a.name,b.name)}I;function dd(a,b,c,d,e){this.bb=a;this.name=b;this.ib=c;this.xc=d;this.jb=e;this.o=2154168321;this.M=4096}h=dd.prototype;h.toString=function(){return this.ib};h.equiv=function(a){return this.L(null,a)};
-h.L=function(a,b){return b instanceof dd?this.ib===b.ib:!1};h.call=function(){function a(a,b,c){return I.l?I.l(b,this,c):I.call(null,b,this,c)}function b(a,b){return I.h?I.h(b,this):I.call(null,b,this)}var c=null,c=function(c,e,f){switch(arguments.length){case 2:return b.call(this,0,e);case 3:return a.call(this,0,e,f)}throw Error("Invalid arity: "+arguments.length);};c.h=b;c.l=a;return c}();h.apply=function(a,b){return this.call.apply(this,[this].concat(yb(b)))};
-h.j=function(a){return I.h?I.h(a,this):I.call(null,a,this)};h.h=function(a,b){return I.l?I.l(a,this,b):I.call(null,a,this,b)};h.Z=function(){return this.jb};h.ba=function(a,b){return new dd(this.bb,this.name,this.ib,this.xc,b)};h.W=function(){var a=this.xc;return null!=a?a:this.xc=a=jd(cd(this.name),hd(this.bb))};h.$c=function(){return this.name};h.ad=function(){return this.bb};h.T=function(a,b){return Ac(b,this.ib)};
-var ld=function ld(b){for(var c=[],d=arguments.length,e=0;;)if(e<d)c.push(arguments[e]),e+=1;else break;switch(c.length){case 1:return ld.j(arguments[0]);case 2:return ld.h(arguments[0],arguments[1]);default:throw Error([y("Invalid arity: "),y(c.length)].join(""));}};ld.j=function(a){if(a instanceof dd)return a;var b=a.indexOf("/");return-1===b?ld.h(null,a):ld.h(a.substring(0,b),a.substring(b+1,a.length))};ld.h=function(a,b){var c=null!=a?[y(a),y("/"),y(b)].join(""):b;return new dd(a,b,c,null,null)};
-ld.J=2;J;md;B;function K(a){if(null==a)return null;if(null!=a&&(a.o&8388608||a.Uf))return a.fa(null);if(rb(a)||"string"===typeof a)return 0===a.length?null:new B(a,0);if(ub(tc,a))return uc(a);throw Error([y(a),y(" is not ISeqable")].join(""));}function C(a){if(null==a)return null;if(null!=a&&(a.o&64||a.D))return a.wa(null);a=K(a);return null==a?null:Nb(a)}function N(a){return null!=a?null!=a&&(a.o&64||a.D)?a.Da(null):(a=K(a))?Pb(a):nd:nd}
-function D(a){return null==a?null:null!=a&&(a.o&128||a.Id)?a.$a(null):K(N(a))}var H=function H(b){for(var c=[],d=arguments.length,e=0;;)if(e<d)c.push(arguments[e]),e+=1;else break;switch(c.length){case 1:return H.j(arguments[0]);case 2:return H.h(arguments[0],arguments[1]);default:return H.w(arguments[0],arguments[1],new B(c.slice(2),0))}};H.j=function(){return!0};H.h=function(a,b){return null==a?null==b:a===b||rc(a,b)};
-H.w=function(a,b,c){for(;;)if(H.h(a,b))if(D(c))a=b,b=C(c),c=D(c);else return H.h(b,C(c));else return!1};H.K=function(a){var b=C(a),c=D(a);a=C(c);c=D(c);return H.w(b,a,c)};H.J=2;function od(a){this.s=a}od.prototype.next=function(){if(null!=this.s){var a=C(this.s);this.s=D(this.s);return{value:a,done:!1}}return{value:null,done:!0}};function sd(a){return new od(K(a))}td;function ud(a,b,c){this.value=a;this.Kc=b;this.he=c;this.o=8388672;this.M=0}ud.prototype.fa=function(){return this};
-ud.prototype.wa=function(){return this.value};ud.prototype.Da=function(){null==this.he&&(this.he=td.j?td.j(this.Kc):td.call(null,this.Kc));return this.he};function td(a){var b=a.next();return u(b.done)?nd:new ud(b.value,a,null)}function vd(a,b){var c=Zc(a),c=$c(0,c);return ad(c,b)}function wd(a){var b=0,c=1;for(a=K(a);;)if(null!=a)b+=1,c=Yc(31,c)+id(C(a))|0,a=D(a);else return vd(c,b)}var xd=vd(1,0);function yd(a){var b=0,c=0;for(a=K(a);;)if(null!=a)b+=1,c=c+id(C(a))|0,a=D(a);else return vd(c,b)}
-var zd=vd(0,0);P;G;Ad;Eb["null"]=!0;Gb["null"]=function(){return 0};Date.prototype.L=function(a,b){return b instanceof Date&&this.valueOf()===b.valueOf()};Date.prototype.ic=!0;Date.prototype.Sb=function(a,b){if(b instanceof Date)return Xa(this.valueOf(),b.valueOf());throw Error([y("Cannot compare "),y(this),y(" to "),y(b)].join(""));};rc.number=function(a,b){return a===b};Bd;Bb["function"]=!0;jc["function"]=!0;kc["function"]=function(){return null};sc._=function(a){return ja(a)};
-function Cd(a){return a+1}Q;function Dd(a){this.I=a;this.o=32768;this.M=0}Dd.prototype.$b=function(){return this.I};function Ed(a){return a instanceof Dd}function Q(a){return ic(a)}function Fd(a,b){var c=Gb(a);if(0===c)return b.A?b.A():b.call(null);for(var d=Lb.h(a,0),e=1;;)if(e<c){var f=Lb.h(a,e),d=b.h?b.h(d,f):b.call(null,d,f);if(Ed(d))return ic(d);e+=1}else return d}
-function Gd(a,b,c){var d=Gb(a),e=c;for(c=0;;)if(c<d){var f=Lb.h(a,c),e=b.h?b.h(e,f):b.call(null,e,f);if(Ed(e))return ic(e);c+=1}else return e}function Hd(a,b){var c=a.length;if(0===a.length)return b.A?b.A():b.call(null);for(var d=a[0],e=1;;)if(e<c){var f=a[e],d=b.h?b.h(d,f):b.call(null,d,f);if(Ed(d))return ic(d);e+=1}else return d}function Id(a,b,c){var d=a.length,e=c;for(c=0;;)if(c<d){var f=a[c],e=b.h?b.h(e,f):b.call(null,e,f);if(Ed(e))return ic(e);c+=1}else return e}
-function Jd(a,b,c,d){for(var e=a.length;;)if(d<e){var f=a[d];c=b.h?b.h(c,f):b.call(null,c,f);if(Ed(c))return ic(c);d+=1}else return c}Kd;Md;Nd;Od;function Pd(a){return null!=a?a.o&2||a.Kf?!0:a.o?!1:ub(Eb,a):ub(Eb,a)}function Qd(a){return null!=a?a.o&16||a.Ze?!0:a.o?!1:ub(Kb,a):ub(Kb,a)}function Rd(a,b){this.v=a;this.i=b}Rd.prototype.Fa=function(){return this.i<this.v.length};Rd.prototype.next=function(){var a=this.v[this.i];this.i+=1;return a};
-function B(a,b){this.v=a;this.i=b;this.o=166199550;this.M=8192}h=B.prototype;h.toString=function(){return Xc(this)};h.equiv=function(a){return this.L(null,a)};h.aa=function(a,b){var c=b+this.i;return c<this.v.length?this.v[c]:null};h.kb=function(a,b,c){a=b+this.i;return a<this.v.length?this.v[a]:c};h.qb=function(){return new Rd(this.v,this.i)};h.Za=function(){return new B(this.v,this.i)};h.$a=function(){return this.i+1<this.v.length?new B(this.v,this.i+1):null};
-h.ia=function(){var a=this.v.length-this.i;return 0>a?0:a};h.Dc=function(){var a=Gb(this);return 0<a?new Nd(this,a-1,null):null};h.W=function(){return wd(this)};h.L=function(a,b){return Ad.h?Ad.h(this,b):Ad.call(null,this,b)};h.qa=function(){return nd};h.za=function(a,b){return Jd(this.v,b,this.v[this.i],this.i+1)};h.Aa=function(a,b,c){return Jd(this.v,b,c,this.i)};h.wa=function(){return this.v[this.i]};h.Da=function(){return this.i+1<this.v.length?new B(this.v,this.i+1):nd};
-h.fa=function(){return this.i<this.v.length?this:null};h.ha=function(a,b){return Md.h?Md.h(b,this):Md.call(null,b,this)};B.prototype[xb]=function(){return sd(this)};var md=function md(b){for(var c=[],d=arguments.length,e=0;;)if(e<d)c.push(arguments[e]),e+=1;else break;switch(c.length){case 1:return md.j(arguments[0]);case 2:return md.h(arguments[0],arguments[1]);default:throw Error([y("Invalid arity: "),y(c.length)].join(""));}};md.j=function(a){return md.h(a,0)};
-md.h=function(a,b){return b<a.length?new B(a,b):null};md.J=2;var J=function J(b){for(var c=[],d=arguments.length,e=0;;)if(e<d)c.push(arguments[e]),e+=1;else break;switch(c.length){case 1:return J.j(arguments[0]);case 2:return J.h(arguments[0],arguments[1]);default:throw Error([y("Invalid arity: "),y(c.length)].join(""));}};J.j=function(a){return md.h(a,0)};J.h=function(a,b){return md.h(a,b)};J.J=2;Bd;Sd;function Nd(a,b,c){this.Wc=a;this.i=b;this.meta=c;this.o=32374990;this.M=8192}h=Nd.prototype;
-h.toString=function(){return Xc(this)};h.equiv=function(a){return this.L(null,a)};h.Z=function(){return this.meta};h.Za=function(){return new Nd(this.Wc,this.i,this.meta)};h.$a=function(){return 0<this.i?new Nd(this.Wc,this.i-1,null):null};h.ia=function(){return this.i+1};h.W=function(){return wd(this)};h.L=function(a,b){return Ad.h?Ad.h(this,b):Ad.call(null,this,b)};h.qa=function(){var a=nd,b=this.meta;return Bd.h?Bd.h(a,b):Bd.call(null,a,b)};
-h.za=function(a,b){return Sd.h?Sd.h(b,this):Sd.call(null,b,this)};h.Aa=function(a,b,c){return Sd.l?Sd.l(b,c,this):Sd.call(null,b,c,this)};h.wa=function(){return Lb.h(this.Wc,this.i)};h.Da=function(){return 0<this.i?new Nd(this.Wc,this.i-1,null):nd};h.fa=function(){return this};h.ba=function(a,b){return new Nd(this.Wc,this.i,b)};h.ha=function(a,b){return Md.h?Md.h(b,this):Md.call(null,b,this)};Nd.prototype[xb]=function(){return sd(this)};function Td(a){return C(D(a))}
-function Ud(a){for(;;){var b=D(a);if(null!=b)a=b;else return C(a)}}rc._=function(a,b){return a===b};var Vd=function Vd(b){for(var c=[],d=arguments.length,e=0;;)if(e<d)c.push(arguments[e]),e+=1;else break;switch(c.length){case 0:return Vd.A();case 1:return Vd.j(arguments[0]);case 2:return Vd.h(arguments[0],arguments[1]);default:return Vd.w(arguments[0],arguments[1],new B(c.slice(2),0))}};Vd.A=function(){return Wd};Vd.j=function(a){return a};Vd.h=function(a,b){return null!=a?Jb(a,b):Jb(nd,b)};
-Vd.w=function(a,b,c){for(;;)if(u(c))a=Vd.h(a,b),b=C(c),c=D(c);else return Vd.h(a,b)};Vd.K=function(a){var b=C(a),c=D(a);a=C(c);c=D(c);return Vd.w(b,a,c)};Vd.J=2;function Xd(a){return null==a?null:Hb(a)}function R(a){if(null!=a)if(null!=a&&(a.o&2||a.Kf))a=a.ia(null);else if(rb(a))a=a.length;else if("string"===typeof a)a=a.length;else if(null!=a&&(a.o&8388608||a.Uf))a:{a=K(a);for(var b=0;;){if(Pd(a)){a=b+Gb(a);break a}a=D(a);b+=1}}else a=Gb(a);else a=0;return a}
-function Yd(a,b,c){for(;;){if(null==a)return c;if(0===b)return K(a)?C(a):c;if(Qd(a))return Lb.l(a,b,c);if(K(a))a=D(a),--b;else return c}}
-function Zd(a,b){if("number"!==typeof b)throw Error("index argument to nth must be a number");if(null==a)return a;if(null!=a&&(a.o&16||a.Ze))return a.aa(null,b);if(rb(a))return b<a.length?a[b]:null;if("string"===typeof a)return b<a.length?a.charAt(b):null;if(null!=a&&(a.o&64||a.D)){var c;a:{c=a;for(var d=b;;){if(null==c)throw Error("Index out of bounds");if(0===d){if(K(c)){c=C(c);break a}throw Error("Index out of bounds");}if(Qd(c)){c=Lb.h(c,d);break a}if(K(c))c=D(c),--d;else throw Error("Index out of bounds");
-}}return c}if(ub(Kb,a))return Lb.h(a,b);throw Error([y("nth not supported on this type "),y(wb(vb(a)))].join(""));}
-function S(a,b,c){if("number"!==typeof b)throw Error("index argument to nth must be a number.");if(null==a)return c;if(null!=a&&(a.o&16||a.Ze))return a.kb(null,b,c);if(rb(a))return b<a.length?a[b]:c;if("string"===typeof a)return b<a.length?a.charAt(b):c;if(null!=a&&(a.o&64||a.D))return Yd(a,b,c);if(ub(Kb,a))return Lb.h(a,b);throw Error([y("nth not supported on this type "),y(wb(vb(a)))].join(""));}
-var I=function I(b){for(var c=[],d=arguments.length,e=0;;)if(e<d)c.push(arguments[e]),e+=1;else break;switch(c.length){case 2:return I.h(arguments[0],arguments[1]);case 3:return I.l(arguments[0],arguments[1],arguments[2]);default:throw Error([y("Invalid arity: "),y(c.length)].join(""));}};I.h=function(a,b){return null==a?null:null!=a&&(a.o&256||a.$e)?a.X(null,b):rb(a)?b<a.length?a[b|0]:null:"string"===typeof a?b<a.length?a[b|0]:null:ub(Tb,a)?Ub.h(a,b):null};
-I.l=function(a,b,c){return null!=a?null!=a&&(a.o&256||a.$e)?a.P(null,b,c):rb(a)?b<a.length?a[b]:c:"string"===typeof a?b<a.length?a[b]:c:ub(Tb,a)?Ub.l(a,b,c):c:c};I.J=3;$d;var T=function T(b){for(var c=[],d=arguments.length,e=0;;)if(e<d)c.push(arguments[e]),e+=1;else break;switch(c.length){case 3:return T.l(arguments[0],arguments[1],arguments[2]);default:return T.w(arguments[0],arguments[1],arguments[2],new B(c.slice(3),0))}};T.l=function(a,b,c){return null!=a?Wb(a,b,c):ae([b],[c])};
-T.w=function(a,b,c,d){for(;;)if(a=T.l(a,b,c),u(d))b=C(d),c=Td(d),d=D(D(d));else return a};T.K=function(a){var b=C(a),c=D(a);a=C(c);var d=D(c),c=C(d),d=D(d);return T.w(b,a,c,d)};T.J=3;var be=function be(b){for(var c=[],d=arguments.length,e=0;;)if(e<d)c.push(arguments[e]),e+=1;else break;switch(c.length){case 1:return be.j(arguments[0]);case 2:return be.h(arguments[0],arguments[1]);default:return be.w(arguments[0],arguments[1],new B(c.slice(2),0))}};be.j=function(a){return a};
-be.h=function(a,b){return null==a?null:Zb(a,b)};be.w=function(a,b,c){for(;;){if(null==a)return null;a=be.h(a,b);if(u(c))b=C(c),c=D(c);else return a}};be.K=function(a){var b=C(a),c=D(a);a=C(c);c=D(c);return be.w(b,a,c)};be.J=2;function ce(a){var b=ia(a);return b?b:null!=a?a.Jf?!0:a.ed?!1:ub(Bb,a):ub(Bb,a)}function de(a,b){this.B=a;this.meta=b;this.o=393217;this.M=0}h=de.prototype;h.Z=function(){return this.meta};h.ba=function(a,b){return new de(this.B,b)};h.Jf=!0;
-h.call=function(){function a(a,b,c,d,e,f,g,k,l,m,n,t,q,z,w,E,F,M,O,L,Z,ba){a=this;return A.Xc?A.Xc(a.B,b,c,d,e,f,g,k,l,m,n,t,q,z,w,E,F,M,O,L,Z,ba):A.call(null,a.B,b,c,d,e,f,g,k,l,m,n,t,q,z,w,E,F,M,O,L,Z,ba)}function b(a,b,c,d,e,f,g,k,l,m,n,t,q,z,w,E,F,M,O,L,Z){a=this;return a.B.Ta?a.B.Ta(b,c,d,e,f,g,k,l,m,n,t,q,z,w,E,F,M,O,L,Z):a.B.call(null,b,c,d,e,f,g,k,l,m,n,t,q,z,w,E,F,M,O,L,Z)}function c(a,b,c,d,e,f,g,k,l,m,n,t,q,z,w,E,F,M,O,L){a=this;return a.B.Sa?a.B.Sa(b,c,d,e,f,g,k,l,m,n,t,q,z,w,E,F,M,O,
-L):a.B.call(null,b,c,d,e,f,g,k,l,m,n,t,q,z,w,E,F,M,O,L)}function d(a,b,c,d,e,f,g,k,l,m,n,t,q,z,w,E,F,M,O){a=this;return a.B.Ra?a.B.Ra(b,c,d,e,f,g,k,l,m,n,t,q,z,w,E,F,M,O):a.B.call(null,b,c,d,e,f,g,k,l,m,n,t,q,z,w,E,F,M,O)}function e(a,b,c,d,e,f,g,k,l,m,n,t,q,z,w,E,F,M){a=this;return a.B.Qa?a.B.Qa(b,c,d,e,f,g,k,l,m,n,t,q,z,w,E,F,M):a.B.call(null,b,c,d,e,f,g,k,l,m,n,t,q,z,w,E,F,M)}function f(a,b,c,d,e,f,g,k,l,m,n,t,q,z,w,E,F){a=this;return a.B.Pa?a.B.Pa(b,c,d,e,f,g,k,l,m,n,t,q,z,w,E,F):a.B.call(null,
-b,c,d,e,f,g,k,l,m,n,t,q,z,w,E,F)}function g(a,b,c,d,e,f,g,k,l,m,n,t,q,z,w,E){a=this;return a.B.Oa?a.B.Oa(b,c,d,e,f,g,k,l,m,n,t,q,z,w,E):a.B.call(null,b,c,d,e,f,g,k,l,m,n,t,q,z,w,E)}function k(a,b,c,d,e,f,g,k,l,m,n,t,q,z,w){a=this;return a.B.Na?a.B.Na(b,c,d,e,f,g,k,l,m,n,t,q,z,w):a.B.call(null,b,c,d,e,f,g,k,l,m,n,t,q,z,w)}function l(a,b,c,d,e,f,g,k,l,m,n,t,q,z){a=this;return a.B.Ma?a.B.Ma(b,c,d,e,f,g,k,l,m,n,t,q,z):a.B.call(null,b,c,d,e,f,g,k,l,m,n,t,q,z)}function n(a,b,c,d,e,f,g,k,l,m,n,t,q){a=this;
-return a.B.La?a.B.La(b,c,d,e,f,g,k,l,m,n,t,q):a.B.call(null,b,c,d,e,f,g,k,l,m,n,t,q)}function m(a,b,c,d,e,f,g,k,l,m,n,t){a=this;return a.B.Ka?a.B.Ka(b,c,d,e,f,g,k,l,m,n,t):a.B.call(null,b,c,d,e,f,g,k,l,m,n,t)}function t(a,b,c,d,e,f,g,k,l,m,n){a=this;return a.B.Ca?a.B.Ca(b,c,d,e,f,g,k,l,m,n):a.B.call(null,b,c,d,e,f,g,k,l,m,n)}function q(a,b,c,d,e,f,g,k,l,m){a=this;return a.B.Va?a.B.Va(b,c,d,e,f,g,k,l,m):a.B.call(null,b,c,d,e,f,g,k,l,m)}function z(a,b,c,d,e,f,g,k,l){a=this;return a.B.Ua?a.B.Ua(b,c,
-d,e,f,g,k,l):a.B.call(null,b,c,d,e,f,g,k,l)}function w(a,b,c,d,e,f,g,k){a=this;return a.B.ta?a.B.ta(b,c,d,e,f,g,k):a.B.call(null,b,c,d,e,f,g,k)}function E(a,b,c,d,e,f,g){a=this;return a.B.ra?a.B.ra(b,c,d,e,f,g):a.B.call(null,b,c,d,e,f,g)}function F(a,b,c,d,e,f){a=this;return a.B.N?a.B.N(b,c,d,e,f):a.B.call(null,b,c,d,e,f)}function M(a,b,c,d,e){a=this;return a.B.G?a.B.G(b,c,d,e):a.B.call(null,b,c,d,e)}function O(a,b,c,d){a=this;return a.B.l?a.B.l(b,c,d):a.B.call(null,b,c,d)}function Z(a,b,c){a=this;
-return a.B.h?a.B.h(b,c):a.B.call(null,b,c)}function ba(a,b){a=this;return a.B.j?a.B.j(b):a.B.call(null,b)}function Ca(a){a=this;return a.B.A?a.B.A():a.B.call(null)}var L=null,L=function(fb,oa,wa,xa,ya,L,Na,Ta,Wa,eb,$a,kb,sb,Fb,Ob,qc,Oa,bd,Ld,xe,Ff,vh){switch(arguments.length){case 1:return Ca.call(this,fb);case 2:return ba.call(this,fb,oa);case 3:return Z.call(this,fb,oa,wa);case 4:return O.call(this,fb,oa,wa,xa);case 5:return M.call(this,fb,oa,wa,xa,ya);case 6:return F.call(this,fb,oa,wa,xa,ya,L);
-case 7:return E.call(this,fb,oa,wa,xa,ya,L,Na);case 8:return w.call(this,fb,oa,wa,xa,ya,L,Na,Ta);case 9:return z.call(this,fb,oa,wa,xa,ya,L,Na,Ta,Wa);case 10:return q.call(this,fb,oa,wa,xa,ya,L,Na,Ta,Wa,eb);case 11:return t.call(this,fb,oa,wa,xa,ya,L,Na,Ta,Wa,eb,$a);case 12:return m.call(this,fb,oa,wa,xa,ya,L,Na,Ta,Wa,eb,$a,kb);case 13:return n.call(this,fb,oa,wa,xa,ya,L,Na,Ta,Wa,eb,$a,kb,sb);case 14:return l.call(this,fb,oa,wa,xa,ya,L,Na,Ta,Wa,eb,$a,kb,sb,Fb);case 15:return k.call(this,fb,oa,wa,
-xa,ya,L,Na,Ta,Wa,eb,$a,kb,sb,Fb,Ob);case 16:return g.call(this,fb,oa,wa,xa,ya,L,Na,Ta,Wa,eb,$a,kb,sb,Fb,Ob,qc);case 17:return f.call(this,fb,oa,wa,xa,ya,L,Na,Ta,Wa,eb,$a,kb,sb,Fb,Ob,qc,Oa);case 18:return e.call(this,fb,oa,wa,xa,ya,L,Na,Ta,Wa,eb,$a,kb,sb,Fb,Ob,qc,Oa,bd);case 19:return d.call(this,fb,oa,wa,xa,ya,L,Na,Ta,Wa,eb,$a,kb,sb,Fb,Ob,qc,Oa,bd,Ld);case 20:return c.call(this,fb,oa,wa,xa,ya,L,Na,Ta,Wa,eb,$a,kb,sb,Fb,Ob,qc,Oa,bd,Ld,xe);case 21:return b.call(this,fb,oa,wa,xa,ya,L,Na,Ta,Wa,eb,$a,kb,
-sb,Fb,Ob,qc,Oa,bd,Ld,xe,Ff);case 22:return a.call(this,fb,oa,wa,xa,ya,L,Na,Ta,Wa,eb,$a,kb,sb,Fb,Ob,qc,Oa,bd,Ld,xe,Ff,vh)}throw Error("Invalid arity: "+arguments.length);};L.j=Ca;L.h=ba;L.l=Z;L.G=O;L.N=M;L.ra=F;L.ta=E;L.Ua=w;L.Va=z;L.Ca=q;L.Ka=t;L.La=m;L.Ma=n;L.Na=l;L.Oa=k;L.Pa=g;L.Qa=f;L.Ra=e;L.Sa=d;L.Ta=c;L.ue=b;L.Xc=a;return L}();h.apply=function(a,b){return this.call.apply(this,[this].concat(yb(b)))};h.A=function(){return this.B.A?this.B.A():this.B.call(null)};
-h.j=function(a){return this.B.j?this.B.j(a):this.B.call(null,a)};h.h=function(a,b){return this.B.h?this.B.h(a,b):this.B.call(null,a,b)};h.l=function(a,b,c){return this.B.l?this.B.l(a,b,c):this.B.call(null,a,b,c)};h.G=function(a,b,c,d){return this.B.G?this.B.G(a,b,c,d):this.B.call(null,a,b,c,d)};h.N=function(a,b,c,d,e){return this.B.N?this.B.N(a,b,c,d,e):this.B.call(null,a,b,c,d,e)};h.ra=function(a,b,c,d,e,f){return this.B.ra?this.B.ra(a,b,c,d,e,f):this.B.call(null,a,b,c,d,e,f)};
-h.ta=function(a,b,c,d,e,f,g){return this.B.ta?this.B.ta(a,b,c,d,e,f,g):this.B.call(null,a,b,c,d,e,f,g)};h.Ua=function(a,b,c,d,e,f,g,k){return this.B.Ua?this.B.Ua(a,b,c,d,e,f,g,k):this.B.call(null,a,b,c,d,e,f,g,k)};h.Va=function(a,b,c,d,e,f,g,k,l){return this.B.Va?this.B.Va(a,b,c,d,e,f,g,k,l):this.B.call(null,a,b,c,d,e,f,g,k,l)};h.Ca=function(a,b,c,d,e,f,g,k,l,n){return this.B.Ca?this.B.Ca(a,b,c,d,e,f,g,k,l,n):this.B.call(null,a,b,c,d,e,f,g,k,l,n)};
-h.Ka=function(a,b,c,d,e,f,g,k,l,n,m){return this.B.Ka?this.B.Ka(a,b,c,d,e,f,g,k,l,n,m):this.B.call(null,a,b,c,d,e,f,g,k,l,n,m)};h.La=function(a,b,c,d,e,f,g,k,l,n,m,t){return this.B.La?this.B.La(a,b,c,d,e,f,g,k,l,n,m,t):this.B.call(null,a,b,c,d,e,f,g,k,l,n,m,t)};h.Ma=function(a,b,c,d,e,f,g,k,l,n,m,t,q){return this.B.Ma?this.B.Ma(a,b,c,d,e,f,g,k,l,n,m,t,q):this.B.call(null,a,b,c,d,e,f,g,k,l,n,m,t,q)};
-h.Na=function(a,b,c,d,e,f,g,k,l,n,m,t,q,z){return this.B.Na?this.B.Na(a,b,c,d,e,f,g,k,l,n,m,t,q,z):this.B.call(null,a,b,c,d,e,f,g,k,l,n,m,t,q,z)};h.Oa=function(a,b,c,d,e,f,g,k,l,n,m,t,q,z,w){return this.B.Oa?this.B.Oa(a,b,c,d,e,f,g,k,l,n,m,t,q,z,w):this.B.call(null,a,b,c,d,e,f,g,k,l,n,m,t,q,z,w)};h.Pa=function(a,b,c,d,e,f,g,k,l,n,m,t,q,z,w,E){return this.B.Pa?this.B.Pa(a,b,c,d,e,f,g,k,l,n,m,t,q,z,w,E):this.B.call(null,a,b,c,d,e,f,g,k,l,n,m,t,q,z,w,E)};
-h.Qa=function(a,b,c,d,e,f,g,k,l,n,m,t,q,z,w,E,F){return this.B.Qa?this.B.Qa(a,b,c,d,e,f,g,k,l,n,m,t,q,z,w,E,F):this.B.call(null,a,b,c,d,e,f,g,k,l,n,m,t,q,z,w,E,F)};h.Ra=function(a,b,c,d,e,f,g,k,l,n,m,t,q,z,w,E,F,M){return this.B.Ra?this.B.Ra(a,b,c,d,e,f,g,k,l,n,m,t,q,z,w,E,F,M):this.B.call(null,a,b,c,d,e,f,g,k,l,n,m,t,q,z,w,E,F,M)};
-h.Sa=function(a,b,c,d,e,f,g,k,l,n,m,t,q,z,w,E,F,M,O){return this.B.Sa?this.B.Sa(a,b,c,d,e,f,g,k,l,n,m,t,q,z,w,E,F,M,O):this.B.call(null,a,b,c,d,e,f,g,k,l,n,m,t,q,z,w,E,F,M,O)};h.Ta=function(a,b,c,d,e,f,g,k,l,n,m,t,q,z,w,E,F,M,O,Z){return this.B.Ta?this.B.Ta(a,b,c,d,e,f,g,k,l,n,m,t,q,z,w,E,F,M,O,Z):this.B.call(null,a,b,c,d,e,f,g,k,l,n,m,t,q,z,w,E,F,M,O,Z)};
-h.ue=function(a,b,c,d,e,f,g,k,l,n,m,t,q,z,w,E,F,M,O,Z,ba){return A.Xc?A.Xc(this.B,a,b,c,d,e,f,g,k,l,n,m,t,q,z,w,E,F,M,O,Z,ba):A.call(null,this.B,a,b,c,d,e,f,g,k,l,n,m,t,q,z,w,E,F,M,O,Z,ba)};function Bd(a,b){return ia(a)?new de(a,b):null==a?null:mc(a,b)}function ee(a){var b=null!=a;return(b?null!=a?a.o&131072||a.Sf||(a.o?0:ub(jc,a)):ub(jc,a):b)?kc(a):null}
-var fe=function fe(b){for(var c=[],d=arguments.length,e=0;;)if(e<d)c.push(arguments[e]),e+=1;else break;switch(c.length){case 1:return fe.j(arguments[0]);case 2:return fe.h(arguments[0],arguments[1]);default:return fe.w(arguments[0],arguments[1],new B(c.slice(2),0))}};fe.j=function(a){return a};fe.h=function(a,b){return null==a?null:dc(a,b)};fe.w=function(a,b,c){for(;;){if(null==a)return null;a=fe.h(a,b);if(u(c))b=C(c),c=D(c);else return a}};
-fe.K=function(a){var b=C(a),c=D(a);a=C(c);c=D(c);return fe.w(b,a,c)};fe.J=2;function ge(a){return null==a||tb(K(a))}function he(a){return null==a?!1:null!=a?a.o&8||a.rg?!0:a.o?!1:ub(Ib,a):ub(Ib,a)}function ie(a){return null==a?!1:null!=a?a.o&4096||a.wg?!0:a.o?!1:ub(cc,a):ub(cc,a)}function je(a){return null!=a?a.o&16777216||a.vg?!0:a.o?!1:ub(vc,a):ub(vc,a)}function ke(a){return null==a?!1:null!=a?a.o&1024||a.Qf?!0:a.o?!1:ub(Yb,a):ub(Yb,a)}
-function le(a){return null!=a?a.o&16384||a.xg?!0:a.o?!1:ub(gc,a):ub(gc,a)}me;ne;function oe(a){return null!=a?a.M&512||a.qg?!0:!1:!1}function pe(a){var b=[];va(a,function(a,b){return function(a,c){return b.push(c)}}(a,b));return b}function qe(a,b,c,d,e){for(;0!==e;)c[d]=a[b],d+=1,--e,b+=1}var re={};function se(a){return null==a?!1:null!=a?a.o&64||a.D?!0:a.o?!1:ub(Mb,a):ub(Mb,a)}function te(a){return null==a?!1:!1===a?!1:!0}
-function ue(a){var b=ce(a);return b?b:null!=a?a.o&1||a.sg?!0:a.o?!1:ub(Cb,a):ub(Cb,a)}function ve(a,b){return I.l(a,b,re)===re?!1:!0}
-function ed(a,b){if(a===b)return 0;if(null==a)return-1;if(null==b)return 1;if("number"===typeof a){if("number"===typeof b)return Xa(a,b);throw Error([y("Cannot compare "),y(a),y(" to "),y(b)].join(""));}if(null!=a?a.M&2048||a.ic||(a.M?0:ub(Kc,a)):ub(Kc,a))return Lc(a,b);if("string"!==typeof a&&!rb(a)&&!0!==a&&!1!==a||vb(a)!==vb(b))throw Error([y("Cannot compare "),y(a),y(" to "),y(b)].join(""));return Xa(a,b)}
-function we(a,b){var c=R(a),d=R(b);if(c<d)c=-1;else if(c>d)c=1;else if(0===c)c=0;else a:for(d=0;;){var e=ed(Zd(a,d),Zd(b,d));if(0===e&&d+1<c)d+=1;else{c=e;break a}}return c}ye;var Sd=function Sd(b){for(var c=[],d=arguments.length,e=0;;)if(e<d)c.push(arguments[e]),e+=1;else break;switch(c.length){case 2:return Sd.h(arguments[0],arguments[1]);case 3:return Sd.l(arguments[0],arguments[1],arguments[2]);default:throw Error([y("Invalid arity: "),y(c.length)].join(""));}};
-Sd.h=function(a,b){var c=K(b);if(c){var d=C(c),c=D(c);return Ab.l?Ab.l(a,d,c):Ab.call(null,a,d,c)}return a.A?a.A():a.call(null)};Sd.l=function(a,b,c){for(c=K(c);;)if(c){var d=C(c);b=a.h?a.h(b,d):a.call(null,b,d);if(Ed(b))return ic(b);c=D(c)}else return b};Sd.J=3;ze;
-var Ab=function Ab(b){for(var c=[],d=arguments.length,e=0;;)if(e<d)c.push(arguments[e]),e+=1;else break;switch(c.length){case 2:return Ab.h(arguments[0],arguments[1]);case 3:return Ab.l(arguments[0],arguments[1],arguments[2]);default:throw Error([y("Invalid arity: "),y(c.length)].join(""));}};Ab.h=function(a,b){return null!=b&&(b.o&524288||b.Tf)?b.za(null,a):rb(b)?Hd(b,a):"string"===typeof b?Hd(b,a):ub(nc,b)?oc.h(b,a):Sd.h(a,b)};
-Ab.l=function(a,b,c){return null!=c&&(c.o&524288||c.Tf)?c.Aa(null,a,b):rb(c)?Id(c,a,b):"string"===typeof c?Id(c,a,b):ub(nc,c)?oc.l(c,a,b):Sd.l(a,b,c)};Ab.J=3;function Ae(a,b,c){return null!=c?pc(c,a,b):b}function Be(a){return a}function Ce(a,b,c,d){a=a.j?a.j(b):a.call(null,b);c=Ab.l(a,c,d);return a.j?a.j(c):a.call(null,c)}
-var De=function De(b){for(var c=[],d=arguments.length,e=0;;)if(e<d)c.push(arguments[e]),e+=1;else break;switch(c.length){case 0:return De.A();case 1:return De.j(arguments[0]);case 2:return De.h(arguments[0],arguments[1]);default:return De.w(arguments[0],arguments[1],new B(c.slice(2),0))}};De.A=function(){return 0};De.j=function(a){return a};De.h=function(a,b){return a+b};De.w=function(a,b,c){return Ab.l(De,a+b,c)};De.K=function(a){var b=C(a),c=D(a);a=C(c);c=D(c);return De.w(b,a,c)};De.J=2;
-var Ee=function Ee(b){for(var c=[],d=arguments.length,e=0;;)if(e<d)c.push(arguments[e]),e+=1;else break;switch(c.length){case 0:return Ee.A();case 1:return Ee.j(arguments[0]);case 2:return Ee.h(arguments[0],arguments[1]);default:return Ee.w(arguments[0],arguments[1],new B(c.slice(2),0))}};Ee.A=function(){return 1};Ee.j=function(a){return a};Ee.h=function(a,b){return a*b};Ee.w=function(a,b,c){return Ab.l(Ee,a*b,c)};Ee.K=function(a){var b=C(a),c=D(a);a=C(c);c=D(c);return Ee.w(b,a,c)};Ee.J=2;({}).zg;
-var Fe=function Fe(b){for(var c=[],d=arguments.length,e=0;;)if(e<d)c.push(arguments[e]),e+=1;else break;switch(c.length){case 1:return Fe.j(arguments[0]);case 2:return Fe.h(arguments[0],arguments[1]);default:return Fe.w(arguments[0],arguments[1],new B(c.slice(2),0))}};Fe.j=function(){return!0};Fe.h=function(a,b){return a>b};Fe.w=function(a,b,c){for(;;)if(a>b)if(D(c))a=b,b=C(c),c=D(c);else return b>C(c);else return!1};Fe.K=function(a){var b=C(a),c=D(a);a=C(c);c=D(c);return Fe.w(b,a,c)};Fe.J=2;
-var Ge=function Ge(b){for(var c=[],d=arguments.length,e=0;;)if(e<d)c.push(arguments[e]),e+=1;else break;switch(c.length){case 1:return Ge.j(arguments[0]);case 2:return Ge.h(arguments[0],arguments[1]);default:return Ge.w(arguments[0],arguments[1],new B(c.slice(2),0))}};Ge.j=function(){return!0};Ge.h=function(a,b){return a>=b};Ge.w=function(a,b,c){for(;;)if(a>=b)if(D(c))a=b,b=C(c),c=D(c);else return b>=C(c);else return!1};Ge.K=function(a){var b=C(a),c=D(a);a=C(c);c=D(c);return Ge.w(b,a,c)};Ge.J=2;
-function He(a){return a-1}Ie;function Ie(a,b){return(a%b+b)%b}function Je(a){a=(a-a%2)/2;return 0<=a?Math.floor(a):Math.ceil(a)}function Ke(a){a-=a>>1&1431655765;a=(a&858993459)+(a>>2&858993459);return 16843009*(a+(a>>4)&252645135)>>24}function Le(a,b){for(var c=b,d=K(a);;)if(d&&0<c)--c,d=D(d);else return d}
-var y=function y(b){for(var c=[],d=arguments.length,e=0;;)if(e<d)c.push(arguments[e]),e+=1;else break;switch(c.length){case 0:return y.A();case 1:return y.j(arguments[0]);default:return y.w(arguments[0],new B(c.slice(1),0))}};y.A=function(){return""};y.j=function(a){return null==a?"":""+a};y.w=function(a,b){for(var c=new Ga(""+y(a)),d=b;;)if(u(d))c=c.append(""+y(C(d))),d=D(d);else return c.toString()};y.K=function(a){var b=C(a);a=D(a);return y.w(b,a)};y.J=1;Me;Ne;
-function Ad(a,b){var c;if(je(b))if(Pd(a)&&Pd(b)&&R(a)!==R(b))c=!1;else a:{c=K(a);for(var d=K(b);;){if(null==c){c=null==d;break a}if(null!=d&&H.h(C(c),C(d)))c=D(c),d=D(d);else{c=!1;break a}}}else c=null;return te(c)}function Kd(a){if(K(a)){var b=id(C(a));for(a=D(a);;){if(null==a)return b;b=jd(b,id(C(a)));a=D(a)}}else return 0}Oe;Pe;function Qe(a){var b=0;for(a=K(a);;)if(a){var c=C(a),b=(b+(id(Oe.j?Oe.j(c):Oe.call(null,c))^id(Pe.j?Pe.j(c):Pe.call(null,c))))%4503599627370496;a=D(a)}else return b}Ne;
-Re;Se;function Od(a,b,c,d,e){this.meta=a;this.first=b;this.Ya=c;this.count=d;this.H=e;this.o=65937646;this.M=8192}h=Od.prototype;h.toString=function(){return Xc(this)};h.equiv=function(a){return this.L(null,a)};h.Z=function(){return this.meta};h.Za=function(){return new Od(this.meta,this.first,this.Ya,this.count,this.H)};h.$a=function(){return 1===this.count?null:this.Ya};h.ia=function(){return this.count};h.ac=function(){return this.first};h.bc=function(){return Pb(this)};
-h.W=function(){var a=this.H;return null!=a?a:this.H=a=wd(this)};h.L=function(a,b){return Ad(this,b)};h.qa=function(){return mc(nd,this.meta)};h.za=function(a,b){return Sd.h(b,this)};h.Aa=function(a,b,c){return Sd.l(b,c,this)};h.wa=function(){return this.first};h.Da=function(){return 1===this.count?nd:this.Ya};h.fa=function(){return this};h.ba=function(a,b){return new Od(b,this.first,this.Ya,this.count,this.H)};h.ha=function(a,b){return new Od(this.meta,b,this,this.count+1,null)};
-Od.prototype[xb]=function(){return sd(this)};function Te(a){this.meta=a;this.o=65937614;this.M=8192}h=Te.prototype;h.toString=function(){return Xc(this)};h.equiv=function(a){return this.L(null,a)};h.Z=function(){return this.meta};h.Za=function(){return new Te(this.meta)};h.$a=function(){return null};h.ia=function(){return 0};h.ac=function(){return null};h.bc=function(){throw Error("Can't pop empty list");};h.W=function(){return xd};
-h.L=function(a,b){return(null!=b?b.o&33554432||b.tg||(b.o?0:ub(xc,b)):ub(xc,b))||je(b)?null==K(b):!1};h.qa=function(){return this};h.za=function(a,b){return Sd.h(b,this)};h.Aa=function(a,b,c){return Sd.l(b,c,this)};h.wa=function(){return null};h.Da=function(){return nd};h.fa=function(){return null};h.ba=function(a,b){return new Te(b)};h.ha=function(a,b){return new Od(this.meta,b,null,1,null)};var nd=new Te(null);Te.prototype[xb]=function(){return sd(this)};
-function Ue(a){return(null!=a?a.o&134217728||a.ug||(a.o?0:ub(yc,a)):ub(yc,a))?zc(a):Ab.l(Vd,nd,a)}var G=function G(b){for(var c=[],d=arguments.length,e=0;;)if(e<d)c.push(arguments[e]),e+=1;else break;return G.w(0<c.length?new B(c.slice(0),0):null)};G.w=function(a){var b;if(a instanceof B&&0===a.i)b=a.v;else a:for(b=[];;)if(null!=a)b.push(a.wa(null)),a=a.$a(null);else break a;a=b.length;for(var c=nd;;)if(0<a){var d=a-1,c=c.ha(null,b[a-1]);a=d}else return c};G.J=0;G.K=function(a){return G.w(K(a))};
-function Ve(a,b,c,d){this.meta=a;this.first=b;this.Ya=c;this.H=d;this.o=65929452;this.M=8192}h=Ve.prototype;h.toString=function(){return Xc(this)};h.equiv=function(a){return this.L(null,a)};h.Z=function(){return this.meta};h.Za=function(){return new Ve(this.meta,this.first,this.Ya,this.H)};h.$a=function(){return null==this.Ya?null:K(this.Ya)};h.W=function(){var a=this.H;return null!=a?a:this.H=a=wd(this)};h.L=function(a,b){return Ad(this,b)};h.qa=function(){return Bd(nd,this.meta)};
-h.za=function(a,b){return Sd.h(b,this)};h.Aa=function(a,b,c){return Sd.l(b,c,this)};h.wa=function(){return this.first};h.Da=function(){return null==this.Ya?nd:this.Ya};h.fa=function(){return this};h.ba=function(a,b){return new Ve(b,this.first,this.Ya,this.H)};h.ha=function(a,b){return new Ve(null,b,this,this.H)};Ve.prototype[xb]=function(){return sd(this)};function Md(a,b){var c=null==b;return(c?c:null!=b&&(b.o&64||b.D))?new Ve(null,a,b,null):new Ve(null,a,K(b),null)}
-function We(a,b){if(a.ab===b.ab)return 0;var c=tb(a.bb);if(u(c?b.bb:c))return-1;if(u(a.bb)){if(tb(b.bb))return 1;c=Xa(a.bb,b.bb);return 0===c?Xa(a.name,b.name):c}return Xa(a.name,b.name)}function v(a,b,c,d){this.bb=a;this.name=b;this.ab=c;this.xc=d;this.o=2153775105;this.M=4096}h=v.prototype;h.toString=function(){return[y(":"),y(this.ab)].join("")};h.equiv=function(a){return this.L(null,a)};h.L=function(a,b){return b instanceof v?this.ab===b.ab:!1};
-h.call=function(){var a=null,a=function(a,c,d){switch(arguments.length){case 2:return I.h(c,this);case 3:return I.l(c,this,d)}throw Error("Invalid arity: "+arguments.length);};a.h=function(a,c){return I.h(c,this)};a.l=function(a,c,d){return I.l(c,this,d)};return a}();h.apply=function(a,b){return this.call.apply(this,[this].concat(yb(b)))};h.j=function(a){return I.h(a,this)};h.h=function(a,b){return I.l(a,this,b)};
-h.W=function(){var a=this.xc;return null!=a?a:this.xc=a=jd(cd(this.name),hd(this.bb))+2654435769|0};h.$c=function(){return this.name};h.ad=function(){return this.bb};h.T=function(a,b){return Ac(b,[y(":"),y(this.ab)].join(""))};function U(a,b){return a===b?!0:a instanceof v&&b instanceof v?a.ab===b.ab:!1}
-var Ye=function Ye(b){for(var c=[],d=arguments.length,e=0;;)if(e<d)c.push(arguments[e]),e+=1;else break;switch(c.length){case 1:return Ye.j(arguments[0]);case 2:return Ye.h(arguments[0],arguments[1]);default:throw Error([y("Invalid arity: "),y(c.length)].join(""));}};
-Ye.j=function(a){if(a instanceof v)return a;if(a instanceof dd){var b;if(null!=a&&(a.M&4096||a.af))b=a.ad(null);else throw Error([y("Doesn't support namespace: "),y(a)].join(""));return new v(b,Ne.j?Ne.j(a):Ne.call(null,a),a.ib,null)}return"string"===typeof a?(b=a.split("/"),2===b.length?new v(b[0],b[1],a,null):new v(null,b[0],a,null)):null};Ye.h=function(a,b){return new v(a,b,[y(u(a)?[y(a),y("/")].join(""):null),y(b)].join(""),null)};Ye.J=2;
-function Ze(a,b,c,d){this.meta=a;this.Hc=b;this.s=c;this.H=d;this.o=32374988;this.M=0}h=Ze.prototype;h.toString=function(){return Xc(this)};h.equiv=function(a){return this.L(null,a)};function $e(a){null!=a.Hc&&(a.s=a.Hc.A?a.Hc.A():a.Hc.call(null),a.Hc=null);return a.s}h.Z=function(){return this.meta};h.$a=function(){uc(this);return null==this.s?null:D(this.s)};h.W=function(){var a=this.H;return null!=a?a:this.H=a=wd(this)};h.L=function(a,b){return Ad(this,b)};h.qa=function(){return Bd(nd,this.meta)};
-h.za=function(a,b){return Sd.h(b,this)};h.Aa=function(a,b,c){return Sd.l(b,c,this)};h.wa=function(){uc(this);return null==this.s?null:C(this.s)};h.Da=function(){uc(this);return null!=this.s?N(this.s):nd};h.fa=function(){$e(this);if(null==this.s)return null;for(var a=this.s;;)if(a instanceof Ze)a=$e(a);else return this.s=a,K(this.s)};h.ba=function(a,b){return new Ze(b,this.Hc,this.s,this.H)};h.ha=function(a,b){return Md(b,this)};Ze.prototype[xb]=function(){return sd(this)};af;
-function bf(a,b){this.U=a;this.end=b;this.o=2;this.M=0}bf.prototype.add=function(a){this.U[this.end]=a;return this.end+=1};bf.prototype.vb=function(){var a=new af(this.U,0,this.end);this.U=null;return a};bf.prototype.ia=function(){return this.end};function af(a,b,c){this.v=a;this.Ia=b;this.end=c;this.o=524306;this.M=0}h=af.prototype;h.ia=function(){return this.end-this.Ia};h.aa=function(a,b){return this.v[this.Ia+b]};h.kb=function(a,b,c){return 0<=b&&b<this.end-this.Ia?this.v[this.Ia+b]:c};
-h.Ye=function(){if(this.Ia===this.end)throw Error("-drop-first of empty chunk");return new af(this.v,this.Ia+1,this.end)};h.za=function(a,b){return Jd(this.v,b,this.v[this.Ia],this.Ia+1)};h.Aa=function(a,b,c){return Jd(this.v,b,c,this.Ia)};function me(a,b,c,d){this.vb=a;this.Ob=b;this.meta=c;this.H=d;this.o=31850732;this.M=1536}h=me.prototype;h.toString=function(){return Xc(this)};h.equiv=function(a){return this.L(null,a)};h.Z=function(){return this.meta};
-h.$a=function(){if(1<Gb(this.vb))return new me(Mc(this.vb),this.Ob,this.meta,null);var a=uc(this.Ob);return null==a?null:a};h.W=function(){var a=this.H;return null!=a?a:this.H=a=wd(this)};h.L=function(a,b){return Ad(this,b)};h.qa=function(){return Bd(nd,this.meta)};h.wa=function(){return Lb.h(this.vb,0)};h.Da=function(){return 1<Gb(this.vb)?new me(Mc(this.vb),this.Ob,this.meta,null):null==this.Ob?nd:this.Ob};h.fa=function(){return this};h.se=function(){return this.vb};
-h.te=function(){return null==this.Ob?nd:this.Ob};h.ba=function(a,b){return new me(this.vb,this.Ob,b,this.H)};h.ha=function(a,b){return Md(b,this)};h.re=function(){return null==this.Ob?null:this.Ob};me.prototype[xb]=function(){return sd(this)};function cf(a,b){return 0===Gb(a)?b:new me(a,b,null,null)}function df(a,b){a.add(b)}function Re(a){return Nc(a)}function Se(a){return Oc(a)}function ye(a){for(var b=[];;)if(K(a))b.push(C(a)),a=D(a);else return b}
-function ef(a,b){if(Pd(a))return R(a);for(var c=a,d=b,e=0;;)if(0<d&&K(c))c=D(c),--d,e+=1;else return e}var ff=function ff(b){return null==b?null:null==D(b)?K(C(b)):Md(C(b),ff(D(b)))},gf=function gf(b){for(var c=[],d=arguments.length,e=0;;)if(e<d)c.push(arguments[e]),e+=1;else break;switch(c.length){case 0:return gf.A();case 1:return gf.j(arguments[0]);case 2:return gf.h(arguments[0],arguments[1]);default:return gf.w(arguments[0],arguments[1],new B(c.slice(2),0))}};
-gf.A=function(){return new Ze(null,function(){return null},null,null)};gf.j=function(a){return new Ze(null,function(){return a},null,null)};gf.h=function(a,b){return new Ze(null,function(){var c=K(a);return c?oe(c)?cf(Nc(c),gf.h(Oc(c),b)):Md(C(c),gf.h(N(c),b)):b},null,null)};gf.w=function(a,b,c){return function e(a,b){return new Ze(null,function(){var c=K(a);return c?oe(c)?cf(Nc(c),e(Oc(c),b)):Md(C(c),e(N(c),b)):u(b)?e(C(b),D(b)):null},null,null)}(gf.h(a,b),c)};
-gf.K=function(a){var b=C(a),c=D(a);a=C(c);c=D(c);return gf.w(b,a,c)};gf.J=2;function hf(a){return Hc(a)}var jf=function jf(b){for(var c=[],d=arguments.length,e=0;;)if(e<d)c.push(arguments[e]),e+=1;else break;switch(c.length){case 0:return jf.A();case 1:return jf.j(arguments[0]);case 2:return jf.h(arguments[0],arguments[1]);default:return jf.w(arguments[0],arguments[1],new B(c.slice(2),0))}};jf.A=function(){return Fc(Wd)};jf.j=function(a){return a};jf.h=function(a,b){return Gc(a,b)};
-jf.w=function(a,b,c){for(;;)if(a=Gc(a,b),u(c))b=C(c),c=D(c);else return a};jf.K=function(a){var b=C(a),c=D(a);a=C(c);c=D(c);return jf.w(b,a,c)};jf.J=2;
-function kf(a,b,c){var d=K(c);if(0===b)return a.A?a.A():a.call(null);c=Nb(d);var e=Pb(d);if(1===b)return a.j?a.j(c):a.j?a.j(c):a.call(null,c);var d=Nb(e),f=Pb(e);if(2===b)return a.h?a.h(c,d):a.h?a.h(c,d):a.call(null,c,d);var e=Nb(f),g=Pb(f);if(3===b)return a.l?a.l(c,d,e):a.l?a.l(c,d,e):a.call(null,c,d,e);var f=Nb(g),k=Pb(g);if(4===b)return a.G?a.G(c,d,e,f):a.G?a.G(c,d,e,f):a.call(null,c,d,e,f);var g=Nb(k),l=Pb(k);if(5===b)return a.N?a.N(c,d,e,f,g):a.N?a.N(c,d,e,f,g):a.call(null,c,d,e,f,g);var k=Nb(l),
-n=Pb(l);if(6===b)return a.ra?a.ra(c,d,e,f,g,k):a.ra?a.ra(c,d,e,f,g,k):a.call(null,c,d,e,f,g,k);var l=Nb(n),m=Pb(n);if(7===b)return a.ta?a.ta(c,d,e,f,g,k,l):a.ta?a.ta(c,d,e,f,g,k,l):a.call(null,c,d,e,f,g,k,l);var n=Nb(m),t=Pb(m);if(8===b)return a.Ua?a.Ua(c,d,e,f,g,k,l,n):a.Ua?a.Ua(c,d,e,f,g,k,l,n):a.call(null,c,d,e,f,g,k,l,n);var m=Nb(t),q=Pb(t);if(9===b)return a.Va?a.Va(c,d,e,f,g,k,l,n,m):a.Va?a.Va(c,d,e,f,g,k,l,n,m):a.call(null,c,d,e,f,g,k,l,n,m);var t=Nb(q),z=Pb(q);if(10===b)return a.Ca?a.Ca(c,
-d,e,f,g,k,l,n,m,t):a.Ca?a.Ca(c,d,e,f,g,k,l,n,m,t):a.call(null,c,d,e,f,g,k,l,n,m,t);var q=Nb(z),w=Pb(z);if(11===b)return a.Ka?a.Ka(c,d,e,f,g,k,l,n,m,t,q):a.Ka?a.Ka(c,d,e,f,g,k,l,n,m,t,q):a.call(null,c,d,e,f,g,k,l,n,m,t,q);var z=Nb(w),E=Pb(w);if(12===b)return a.La?a.La(c,d,e,f,g,k,l,n,m,t,q,z):a.La?a.La(c,d,e,f,g,k,l,n,m,t,q,z):a.call(null,c,d,e,f,g,k,l,n,m,t,q,z);var w=Nb(E),F=Pb(E);if(13===b)return a.Ma?a.Ma(c,d,e,f,g,k,l,n,m,t,q,z,w):a.Ma?a.Ma(c,d,e,f,g,k,l,n,m,t,q,z,w):a.call(null,c,d,e,f,g,k,l,
-n,m,t,q,z,w);var E=Nb(F),M=Pb(F);if(14===b)return a.Na?a.Na(c,d,e,f,g,k,l,n,m,t,q,z,w,E):a.Na?a.Na(c,d,e,f,g,k,l,n,m,t,q,z,w,E):a.call(null,c,d,e,f,g,k,l,n,m,t,q,z,w,E);var F=Nb(M),O=Pb(M);if(15===b)return a.Oa?a.Oa(c,d,e,f,g,k,l,n,m,t,q,z,w,E,F):a.Oa?a.Oa(c,d,e,f,g,k,l,n,m,t,q,z,w,E,F):a.call(null,c,d,e,f,g,k,l,n,m,t,q,z,w,E,F);var M=Nb(O),Z=Pb(O);if(16===b)return a.Pa?a.Pa(c,d,e,f,g,k,l,n,m,t,q,z,w,E,F,M):a.Pa?a.Pa(c,d,e,f,g,k,l,n,m,t,q,z,w,E,F,M):a.call(null,c,d,e,f,g,k,l,n,m,t,q,z,w,E,F,M);var O=
-Nb(Z),ba=Pb(Z);if(17===b)return a.Qa?a.Qa(c,d,e,f,g,k,l,n,m,t,q,z,w,E,F,M,O):a.Qa?a.Qa(c,d,e,f,g,k,l,n,m,t,q,z,w,E,F,M,O):a.call(null,c,d,e,f,g,k,l,n,m,t,q,z,w,E,F,M,O);var Z=Nb(ba),Ca=Pb(ba);if(18===b)return a.Ra?a.Ra(c,d,e,f,g,k,l,n,m,t,q,z,w,E,F,M,O,Z):a.Ra?a.Ra(c,d,e,f,g,k,l,n,m,t,q,z,w,E,F,M,O,Z):a.call(null,c,d,e,f,g,k,l,n,m,t,q,z,w,E,F,M,O,Z);ba=Nb(Ca);Ca=Pb(Ca);if(19===b)return a.Sa?a.Sa(c,d,e,f,g,k,l,n,m,t,q,z,w,E,F,M,O,Z,ba):a.Sa?a.Sa(c,d,e,f,g,k,l,n,m,t,q,z,w,E,F,M,O,Z,ba):a.call(null,
-c,d,e,f,g,k,l,n,m,t,q,z,w,E,F,M,O,Z,ba);var L=Nb(Ca);Pb(Ca);if(20===b)return a.Ta?a.Ta(c,d,e,f,g,k,l,n,m,t,q,z,w,E,F,M,O,Z,ba,L):a.Ta?a.Ta(c,d,e,f,g,k,l,n,m,t,q,z,w,E,F,M,O,Z,ba,L):a.call(null,c,d,e,f,g,k,l,n,m,t,q,z,w,E,F,M,O,Z,ba,L);throw Error("Only up to 20 arguments supported on functions");}
-var A=function A(b){for(var c=[],d=arguments.length,e=0;;)if(e<d)c.push(arguments[e]),e+=1;else break;switch(c.length){case 2:return A.h(arguments[0],arguments[1]);case 3:return A.l(arguments[0],arguments[1],arguments[2]);case 4:return A.G(arguments[0],arguments[1],arguments[2],arguments[3]);case 5:return A.N(arguments[0],arguments[1],arguments[2],arguments[3],arguments[4]);default:return A.w(arguments[0],arguments[1],arguments[2],arguments[3],arguments[4],new B(c.slice(5),0))}};
-A.h=function(a,b){var c=a.J;if(a.K){var d=ef(b,c+1);return d<=c?kf(a,d,b):a.K(b)}return a.apply(a,ye(b))};A.l=function(a,b,c){b=Md(b,c);c=a.J;if(a.K){var d=ef(b,c+1);return d<=c?kf(a,d,b):a.K(b)}return a.apply(a,ye(b))};A.G=function(a,b,c,d){b=Md(b,Md(c,d));c=a.J;return a.K?(d=ef(b,c+1),d<=c?kf(a,d,b):a.K(b)):a.apply(a,ye(b))};A.N=function(a,b,c,d,e){b=Md(b,Md(c,Md(d,e)));c=a.J;return a.K?(d=ef(b,c+1),d<=c?kf(a,d,b):a.K(b)):a.apply(a,ye(b))};
-A.w=function(a,b,c,d,e,f){b=Md(b,Md(c,Md(d,Md(e,ff(f)))));c=a.J;return a.K?(d=ef(b,c+1),d<=c?kf(a,d,b):a.K(b)):a.apply(a,ye(b))};A.K=function(a){var b=C(a),c=D(a);a=C(c);var d=D(c),c=C(d),e=D(d),d=C(e),f=D(e),e=C(f),f=D(f);return A.w(b,a,c,d,e,f)};A.J=5;function lf(a){return K(a)?a:null}
-var mf=function mf(){"undefined"===typeof Ya&&(Ya=function(b,c){this.hg=b;this.ag=c;this.o=393216;this.M=0},Ya.prototype.ba=function(b,c){return new Ya(this.hg,c)},Ya.prototype.Z=function(){return this.ag},Ya.prototype.Fa=function(){return!1},Ya.prototype.next=function(){return Error("No such element")},Ya.prototype.remove=function(){return Error("Unsupported operation")},Ya.kd=function(){return new V(null,2,5,W,[Bd(nf,new r(null,1,[of,G(pf,G(Wd))],null)),qf],null)},Ya.mc=!0,Ya.Tb="cljs.core/t_cljs$core20844",
-Ya.Fc=function(b,c){return Ac(c,"cljs.core/t_cljs$core20844")});return new Ya(mf,rf)};sf;function sf(a,b,c,d){this.Oc=a;this.first=b;this.Ya=c;this.meta=d;this.o=31719628;this.M=0}h=sf.prototype;h.ba=function(a,b){return new sf(this.Oc,this.first,this.Ya,b)};h.ha=function(a,b){return Md(b,uc(this))};h.qa=function(){return nd};h.L=function(a,b){return null!=uc(this)?Ad(this,b):je(b)&&null==K(b)};h.W=function(){return wd(this)};
-h.fa=function(){null!=this.Oc&&this.Oc.step(this);return null==this.Ya?null:this};h.wa=function(){null!=this.Oc&&uc(this);return null==this.Ya?null:this.first};h.Da=function(){null!=this.Oc&&uc(this);return null==this.Ya?nd:this.Ya};h.$a=function(){null!=this.Oc&&uc(this);return null==this.Ya?null:uc(this.Ya)};sf.prototype[xb]=function(){return sd(this)};function tf(a,b){for(;;){if(null==K(b))return!0;var c;c=C(b);c=a.j?a.j(c):a.call(null,c);if(u(c)){c=a;var d=D(b);a=c;b=d}else return!1}}
-function uf(a,b){for(;;)if(K(b)){var c;c=C(b);c=a.j?a.j(c):a.call(null,c);if(u(c))return c;c=a;var d=D(b);a=c;b=d}else return null}
-function vf(){return function(){function a(a,b){return tb(qb.h?qb.h(a,b):qb.call(null,a))}function b(a){return tb(qb.j?qb.j(a):qb.call(null,a))}function c(){return tb(qb.A?qb.A():qb.call(null))}var d=null,e=function(){function a(c,d,e){var f=null;if(2<arguments.length){for(var f=0,t=Array(arguments.length-2);f<t.length;)t[f]=arguments[f+2],++f;f=new B(t,0)}return b.call(this,c,d,f)}function b(a,c,d){return tb(A.G(qb,a,c,d))}a.J=2;a.K=function(a){var c=C(a);a=D(a);var d=C(a);a=N(a);return b(c,d,a)};
-a.w=b;return a}(),d=function(d,g,k){switch(arguments.length){case 0:return c.call(this);case 1:return b.call(this,d);case 2:return a.call(this,d,g);default:var l=null;if(2<arguments.length){for(var l=0,n=Array(arguments.length-2);l<n.length;)n[l]=arguments[l+2],++l;l=new B(n,0)}return e.w(d,g,l)}throw Error("Invalid arity: "+arguments.length);};d.J=2;d.K=e.K;d.A=c;d.j=b;d.h=a;d.w=e.w;return d}()}
-function wf(){return function(){function a(a){if(0<arguments.length)for(var c=0,d=Array(arguments.length-0);c<d.length;)d[c]=arguments[c+0],++c;return!1}a.J=0;a.K=function(a){K(a);return!1};a.w=function(){return!1};return a}()}
-var xf=function xf(b){for(var c=[],d=arguments.length,e=0;;)if(e<d)c.push(arguments[e]),e+=1;else break;switch(c.length){case 0:return xf.A();case 1:return xf.j(arguments[0]);case 2:return xf.h(arguments[0],arguments[1]);case 3:return xf.l(arguments[0],arguments[1],arguments[2]);default:return xf.w(arguments[0],arguments[1],arguments[2],new B(c.slice(3),0))}};xf.A=function(){return Be};xf.j=function(a){return a};
-xf.h=function(a,b){return function(){function c(c,d,e){c=b.l?b.l(c,d,e):b.call(null,c,d,e);return a.j?a.j(c):a.call(null,c)}function d(c,d){var e=b.h?b.h(c,d):b.call(null,c,d);return a.j?a.j(e):a.call(null,e)}function e(c){c=b.j?b.j(c):b.call(null,c);return a.j?a.j(c):a.call(null,c)}function f(){var c=b.A?b.A():b.call(null);return a.j?a.j(c):a.call(null,c)}var g=null,k=function(){function c(a,b,e,f){var g=null;if(3<arguments.length){for(var g=0,k=Array(arguments.length-3);g<k.length;)k[g]=arguments[g+
-3],++g;g=new B(k,0)}return d.call(this,a,b,e,g)}function d(c,e,f,g){c=A.N(b,c,e,f,g);return a.j?a.j(c):a.call(null,c)}c.J=3;c.K=function(a){var b=C(a);a=D(a);var c=C(a);a=D(a);var e=C(a);a=N(a);return d(b,c,e,a)};c.w=d;return c}(),g=function(a,b,g,t){switch(arguments.length){case 0:return f.call(this);case 1:return e.call(this,a);case 2:return d.call(this,a,b);case 3:return c.call(this,a,b,g);default:var q=null;if(3<arguments.length){for(var q=0,z=Array(arguments.length-3);q<z.length;)z[q]=arguments[q+
-3],++q;q=new B(z,0)}return k.w(a,b,g,q)}throw Error("Invalid arity: "+arguments.length);};g.J=3;g.K=k.K;g.A=f;g.j=e;g.h=d;g.l=c;g.w=k.w;return g}()};
-xf.l=function(a,b,c){return function(){function d(d,e,f){d=c.l?c.l(d,e,f):c.call(null,d,e,f);d=b.j?b.j(d):b.call(null,d);return a.j?a.j(d):a.call(null,d)}function e(d,e){var f;f=c.h?c.h(d,e):c.call(null,d,e);f=b.j?b.j(f):b.call(null,f);return a.j?a.j(f):a.call(null,f)}function f(d){d=c.j?c.j(d):c.call(null,d);d=b.j?b.j(d):b.call(null,d);return a.j?a.j(d):a.call(null,d)}function g(){var d;d=c.A?c.A():c.call(null);d=b.j?b.j(d):b.call(null,d);return a.j?a.j(d):a.call(null,d)}var k=null,l=function(){function d(a,
-b,c,f){var g=null;if(3<arguments.length){for(var g=0,k=Array(arguments.length-3);g<k.length;)k[g]=arguments[g+3],++g;g=new B(k,0)}return e.call(this,a,b,c,g)}function e(d,f,g,k){d=A.N(c,d,f,g,k);d=b.j?b.j(d):b.call(null,d);return a.j?a.j(d):a.call(null,d)}d.J=3;d.K=function(a){var b=C(a);a=D(a);var c=C(a);a=D(a);var d=C(a);a=N(a);return e(b,c,d,a)};d.w=e;return d}(),k=function(a,b,c,k){switch(arguments.length){case 0:return g.call(this);case 1:return f.call(this,a);case 2:return e.call(this,a,b);
-case 3:return d.call(this,a,b,c);default:var z=null;if(3<arguments.length){for(var z=0,w=Array(arguments.length-3);z<w.length;)w[z]=arguments[z+3],++z;z=new B(w,0)}return l.w(a,b,c,z)}throw Error("Invalid arity: "+arguments.length);};k.J=3;k.K=l.K;k.A=g;k.j=f;k.h=e;k.l=d;k.w=l.w;return k}()};
-xf.w=function(a,b,c,d){return function(a){return function(){function b(a){var d=null;if(0<arguments.length){for(var d=0,e=Array(arguments.length-0);d<e.length;)e[d]=arguments[d+0],++d;d=new B(e,0)}return c.call(this,d)}function c(b){b=A.h(C(a),b);for(var d=D(a);;)if(d)b=C(d).call(null,b),d=D(d);else return b}b.J=0;b.K=function(a){a=K(a);return c(a)};b.w=c;return b}()}(Ue(Md(a,Md(b,Md(c,d)))))};xf.K=function(a){var b=C(a),c=D(a);a=C(c);var d=D(c),c=C(d),d=D(d);return xf.w(b,a,c,d)};xf.J=3;
-var yf=function yf(b){for(var c=[],d=arguments.length,e=0;;)if(e<d)c.push(arguments[e]),e+=1;else break;switch(c.length){case 1:return yf.j(arguments[0]);case 2:return yf.h(arguments[0],arguments[1]);case 3:return yf.l(arguments[0],arguments[1],arguments[2]);case 4:return yf.G(arguments[0],arguments[1],arguments[2],arguments[3]);default:return yf.w(arguments[0],arguments[1],arguments[2],arguments[3],new B(c.slice(4),0))}};yf.j=function(a){return a};
-yf.h=function(a,b){return function(){function c(c,d,e){return a.G?a.G(b,c,d,e):a.call(null,b,c,d,e)}function d(c,d){return a.l?a.l(b,c,d):a.call(null,b,c,d)}function e(c){return a.h?a.h(b,c):a.call(null,b,c)}function f(){return a.j?a.j(b):a.call(null,b)}var g=null,k=function(){function c(a,b,e,f){var g=null;if(3<arguments.length){for(var g=0,k=Array(arguments.length-3);g<k.length;)k[g]=arguments[g+3],++g;g=new B(k,0)}return d.call(this,a,b,e,g)}function d(c,e,f,g){return A.w(a,b,c,e,f,J([g],0))}c.J=
-3;c.K=function(a){var b=C(a);a=D(a);var c=C(a);a=D(a);var e=C(a);a=N(a);return d(b,c,e,a)};c.w=d;return c}(),g=function(a,b,g,t){switch(arguments.length){case 0:return f.call(this);case 1:return e.call(this,a);case 2:return d.call(this,a,b);case 3:return c.call(this,a,b,g);default:var q=null;if(3<arguments.length){for(var q=0,z=Array(arguments.length-3);q<z.length;)z[q]=arguments[q+3],++q;q=new B(z,0)}return k.w(a,b,g,q)}throw Error("Invalid arity: "+arguments.length);};g.J=3;g.K=k.K;g.A=f;g.j=e;
-g.h=d;g.l=c;g.w=k.w;return g}()};
-yf.l=function(a,b,c){return function(){function d(d,e,f){return a.N?a.N(b,c,d,e,f):a.call(null,b,c,d,e,f)}function e(d,e){return a.G?a.G(b,c,d,e):a.call(null,b,c,d,e)}function f(d){return a.l?a.l(b,c,d):a.call(null,b,c,d)}function g(){return a.h?a.h(b,c):a.call(null,b,c)}var k=null,l=function(){function d(a,b,c,f){var g=null;if(3<arguments.length){for(var g=0,k=Array(arguments.length-3);g<k.length;)k[g]=arguments[g+3],++g;g=new B(k,0)}return e.call(this,a,b,c,g)}function e(d,f,g,k){return A.w(a,b,
-c,d,f,J([g,k],0))}d.J=3;d.K=function(a){var b=C(a);a=D(a);var c=C(a);a=D(a);var d=C(a);a=N(a);return e(b,c,d,a)};d.w=e;return d}(),k=function(a,b,c,k){switch(arguments.length){case 0:return g.call(this);case 1:return f.call(this,a);case 2:return e.call(this,a,b);case 3:return d.call(this,a,b,c);default:var z=null;if(3<arguments.length){for(var z=0,w=Array(arguments.length-3);z<w.length;)w[z]=arguments[z+3],++z;z=new B(w,0)}return l.w(a,b,c,z)}throw Error("Invalid arity: "+arguments.length);};k.J=
-3;k.K=l.K;k.A=g;k.j=f;k.h=e;k.l=d;k.w=l.w;return k}()};
-yf.G=function(a,b,c,d){return function(){function e(e,f,g){return a.ra?a.ra(b,c,d,e,f,g):a.call(null,b,c,d,e,f,g)}function f(e,f){return a.N?a.N(b,c,d,e,f):a.call(null,b,c,d,e,f)}function g(e){return a.G?a.G(b,c,d,e):a.call(null,b,c,d,e)}function k(){return a.l?a.l(b,c,d):a.call(null,b,c,d)}var l=null,n=function(){function e(a,b,c,d){var g=null;if(3<arguments.length){for(var g=0,k=Array(arguments.length-3);g<k.length;)k[g]=arguments[g+3],++g;g=new B(k,0)}return f.call(this,a,b,c,g)}function f(e,g,
-k,l){return A.w(a,b,c,d,e,J([g,k,l],0))}e.J=3;e.K=function(a){var b=C(a);a=D(a);var c=C(a);a=D(a);var d=C(a);a=N(a);return f(b,c,d,a)};e.w=f;return e}(),l=function(a,b,c,d){switch(arguments.length){case 0:return k.call(this);case 1:return g.call(this,a);case 2:return f.call(this,a,b);case 3:return e.call(this,a,b,c);default:var l=null;if(3<arguments.length){for(var l=0,E=Array(arguments.length-3);l<E.length;)E[l]=arguments[l+3],++l;l=new B(E,0)}return n.w(a,b,c,l)}throw Error("Invalid arity: "+arguments.length);
-};l.J=3;l.K=n.K;l.A=k;l.j=g;l.h=f;l.l=e;l.w=n.w;return l}()};yf.w=function(a,b,c,d,e){return function(){function f(a){var b=null;if(0<arguments.length){for(var b=0,c=Array(arguments.length-0);b<c.length;)c[b]=arguments[b+0],++b;b=new B(c,0)}return g.call(this,b)}function g(f){return A.N(a,b,c,d,gf.h(e,f))}f.J=0;f.K=function(a){a=K(a);return g(a)};f.w=g;return f}()};yf.K=function(a){var b=C(a),c=D(a);a=C(c);var d=D(c),c=C(d),e=D(d),d=C(e),e=D(e);return yf.w(b,a,c,d,e)};yf.J=4;zf;
-function Af(a,b){return function d(b,f){return new Ze(null,function(){var g=K(f);if(g){if(oe(g)){for(var k=Nc(g),l=R(k),n=new bf(Array(l),0),m=0;;)if(m<l)df(n,function(){var d=b+m,f=Lb.h(k,m);return a.h?a.h(d,f):a.call(null,d,f)}()),m+=1;else break;return cf(n.vb(),d(b+l,Oc(g)))}return Md(function(){var d=C(g);return a.h?a.h(b,d):a.call(null,b,d)}(),d(b+1,N(g)))}return null},null,null)}(0,b)}function Bf(a,b,c,d){this.state=a;this.meta=b;this.Qc=c;this.Ja=d;this.M=16386;this.o=6455296}h=Bf.prototype;
-h.equiv=function(a){return this.L(null,a)};h.L=function(a,b){return this===b};h.$b=function(){return this.state};h.Z=function(){return this.meta};h.Kd=function(a,b,c){a=K(this.Ja);for(var d=null,e=0,f=0;;)if(f<e){var g=d.aa(null,f),k=S(g,0,null),g=S(g,1,null);g.G?g.G(k,this,b,c):g.call(null,k,this,b,c);f+=1}else if(a=K(a))oe(a)?(d=Nc(a),a=Oc(a),k=d,e=R(d),d=k):(d=C(a),k=S(d,0,null),g=S(d,1,null),g.G?g.G(k,this,b,c):g.call(null,k,this,b,c),a=D(a),d=null,e=0),f=0;else return null};
-h.Jd=function(a,b,c){this.Ja=T.l(this.Ja,b,c);return this};h.Ld=function(a,b){return this.Ja=be.h(this.Ja,b)};h.W=function(){return ja(this)};var X=function X(b){for(var c=[],d=arguments.length,e=0;;)if(e<d)c.push(arguments[e]),e+=1;else break;switch(c.length){case 1:return X.j(arguments[0]);default:return X.w(arguments[0],new B(c.slice(1),0))}};X.j=function(a){return new Bf(a,null,null,null)};X.w=function(a,b){var c=null!=b&&(b.o&64||b.D)?A.h(P,b):b,d=I.h(c,jb),c=I.h(c,Cf);return new Bf(a,d,c,null)};
-X.K=function(a){var b=C(a);a=D(a);return X.w(b,a)};X.J=1;Df;function Ef(a,b){if(a instanceof Bf){var c=a.Qc;if(null!=c&&!u(c.j?c.j(b):c.call(null,b)))throw Error([y("Assert failed: "),y("Validator rejected reference state"),y("\n"),y(function(){var a=G(Gf,Hf);return Df.j?Df.j(a):Df.call(null,a)}())].join(""));c=a.state;a.state=b;null!=a.Ja&&Cc(a,c,b);return b}return Sc(a,b)}
-var If=function If(b){for(var c=[],d=arguments.length,e=0;;)if(e<d)c.push(arguments[e]),e+=1;else break;switch(c.length){case 2:return If.h(arguments[0],arguments[1]);case 3:return If.l(arguments[0],arguments[1],arguments[2]);case 4:return If.G(arguments[0],arguments[1],arguments[2],arguments[3]);default:return If.w(arguments[0],arguments[1],arguments[2],arguments[3],new B(c.slice(4),0))}};If.h=function(a,b){var c;a instanceof Bf?(c=a.state,c=b.j?b.j(c):b.call(null,c),c=Ef(a,c)):c=Tc.h(a,b);return c};
-If.l=function(a,b,c){if(a instanceof Bf){var d=a.state;b=b.h?b.h(d,c):b.call(null,d,c);a=Ef(a,b)}else a=Tc.l(a,b,c);return a};If.G=function(a,b,c,d){if(a instanceof Bf){var e=a.state;b=b.l?b.l(e,c,d):b.call(null,e,c,d);a=Ef(a,b)}else a=Tc.G(a,b,c,d);return a};If.w=function(a,b,c,d,e){return a instanceof Bf?Ef(a,A.N(b,a.state,c,d,e)):Tc.N(a,b,c,d,e)};If.K=function(a){var b=C(a),c=D(a);a=C(c);var d=D(c),c=C(d),e=D(d),d=C(e),e=D(e);return If.w(b,a,c,d,e)};If.J=4;
-function Jf(a){this.state=a;this.o=32768;this.M=0}Jf.prototype.cf=function(a,b){return this.state=b};Jf.prototype.$b=function(){return this.state};function zf(a){return new Jf(a)}function Kf(a,b){return Uc(a,b)}
-var Me=function Me(b){for(var c=[],d=arguments.length,e=0;;)if(e<d)c.push(arguments[e]),e+=1;else break;switch(c.length){case 1:return Me.j(arguments[0]);case 2:return Me.h(arguments[0],arguments[1]);case 3:return Me.l(arguments[0],arguments[1],arguments[2]);case 4:return Me.G(arguments[0],arguments[1],arguments[2],arguments[3]);default:return Me.w(arguments[0],arguments[1],arguments[2],arguments[3],new B(c.slice(4),0))}};
-Me.j=function(a){return function(b){return function(){function c(c,d){var e=a.j?a.j(d):a.call(null,d);return b.h?b.h(c,e):b.call(null,c,e)}function d(a){return b.j?b.j(a):b.call(null,a)}function e(){return b.A?b.A():b.call(null)}var f=null,g=function(){function c(a,b,e){var f=null;if(2<arguments.length){for(var f=0,g=Array(arguments.length-2);f<g.length;)g[f]=arguments[f+2],++f;f=new B(g,0)}return d.call(this,a,b,f)}function d(c,e,f){e=A.l(a,e,f);return b.h?b.h(c,e):b.call(null,c,e)}c.J=2;c.K=function(a){var b=
-C(a);a=D(a);var c=C(a);a=N(a);return d(b,c,a)};c.w=d;return c}(),f=function(a,b,f){switch(arguments.length){case 0:return e.call(this);case 1:return d.call(this,a);case 2:return c.call(this,a,b);default:var m=null;if(2<arguments.length){for(var m=0,t=Array(arguments.length-2);m<t.length;)t[m]=arguments[m+2],++m;m=new B(t,0)}return g.w(a,b,m)}throw Error("Invalid arity: "+arguments.length);};f.J=2;f.K=g.K;f.A=e;f.j=d;f.h=c;f.w=g.w;return f}()}};
-Me.h=function(a,b){return new Ze(null,function(){var c=K(b);if(c){if(oe(c)){for(var d=Nc(c),e=R(d),f=new bf(Array(e),0),g=0;;)if(g<e)df(f,function(){var b=Lb.h(d,g);return a.j?a.j(b):a.call(null,b)}()),g+=1;else break;return cf(f.vb(),Me.h(a,Oc(c)))}return Md(function(){var b=C(c);return a.j?a.j(b):a.call(null,b)}(),Me.h(a,N(c)))}return null},null,null)};
-Me.l=function(a,b,c){return new Ze(null,function(){var d=K(b),e=K(c);if(d&&e){var f=Md,g;g=C(d);var k=C(e);g=a.h?a.h(g,k):a.call(null,g,k);d=f(g,Me.l(a,N(d),N(e)))}else d=null;return d},null,null)};Me.G=function(a,b,c,d){return new Ze(null,function(){var e=K(b),f=K(c),g=K(d);if(e&&f&&g){var k=Md,l;l=C(e);var n=C(f),m=C(g);l=a.l?a.l(l,n,m):a.call(null,l,n,m);e=k(l,Me.G(a,N(e),N(f),N(g)))}else e=null;return e},null,null)};
-Me.w=function(a,b,c,d,e){var f=function k(a){return new Ze(null,function(){var b=Me.h(K,a);return tf(Be,b)?Md(Me.h(C,b),k(Me.h(N,b))):null},null,null)};return Me.h(function(){return function(b){return A.h(a,b)}}(f),f(Vd.w(e,d,J([c,b],0))))};Me.K=function(a){var b=C(a),c=D(a);a=C(c);var d=D(c),c=C(d),e=D(d),d=C(e),e=D(e);return Me.w(b,a,c,d,e)};Me.J=4;
-function Lf(a,b){if("number"!==typeof a)throw Error([y("Assert failed: "),y(function(){var a=G(Mf,Nf);return Df.j?Df.j(a):Df.call(null,a)}())].join(""));return new Ze(null,function(){if(0<a){var c=K(b);return c?Md(C(c),Lf(a-1,N(c))):null}return null},null,null)}
-function Of(a,b){if("number"!==typeof a)throw Error([y("Assert failed: "),y(function(){var a=G(Mf,Nf);return Df.j?Df.j(a):Df.call(null,a)}())].join(""));return new Ze(null,function(c){return function(){return c(a,b)}}(function(a,b){for(;;){var e=K(b);if(0<a&&e){var f=a-1,e=N(e);a=f;b=e}else return e}}),null,null)}function Pf(a){return Me.l(function(a){return a},a,Of(2,a))}
-function Qf(a,b){return new Ze(null,function(c){return function(){return c(a,b)}}(function(a,b){for(;;){var e=K(b),f;if(f=e)f=C(e),f=a.j?a.j(f):a.call(null,f);if(u(f))f=a,e=N(e),a=f,b=e;else return e}}),null,null)}function Rf(a){return new Ze(null,function(){return Md(a,Rf(a))},null,null)}function Sf(a,b){return Lf(a,Rf(b))}function Tf(a){return new Ze(null,function(){return Md(a.A?a.A():a.call(null),Tf(a))},null,null)}
-var Uf=function Uf(b,c){return Md(c,new Ze(null,function(){return Uf(b,b.j?b.j(c):b.call(null,c))},null,null))};Vf;function Wf(a,b){return new Ze(null,function(){var c=K(b);if(c){if(oe(c)){for(var d=Nc(c),e=R(d),f=new bf(Array(e),0),g=0;;)if(g<e){var k;k=Lb.h(d,g);k=a.j?a.j(k):a.call(null,k);u(k)&&(k=Lb.h(d,g),f.add(k));g+=1}else break;return cf(f.vb(),Wf(a,Oc(c)))}d=C(c);c=N(c);return u(a.j?a.j(d):a.call(null,d))?Md(d,Wf(a,c)):Wf(a,c)}return null},null,null)}function Xf(a){return Wf(vf(),a)}
-var Yf=function Yf(b){for(var c=[],d=arguments.length,e=0;;)if(e<d)c.push(arguments[e]),e+=1;else break;switch(c.length){case 2:return Yf.h(arguments[0],arguments[1]);case 3:return Yf.l(arguments[0],arguments[1],arguments[2]);default:throw Error([y("Invalid arity: "),y(c.length)].join(""));}};Yf.h=function(a,b){return null!=a?null!=a&&(a.M&4||a.Lf)?Bd(hf(Ab.l(Gc,Fc(a),b)),ee(a)):Ab.l(Jb,a,b):Ab.l(Vd,nd,b)};
-Yf.l=function(a,b,c){return null!=a&&(a.M&4||a.Lf)?Bd(hf(Ce(b,jf,Fc(a),c)),ee(a)):Ce(b,Vd,a,c)};Yf.J=3;function Zf(a,b){return hf(Ab.l(function(b,d){return jf.h(b,a.j?a.j(d):a.call(null,d))},Fc(Wd),b))}function $f(a,b,c){return new Ze(null,function(){var d=K(c);if(d){var e=Lf(a,d);return a===R(e)?Md(e,$f(a,b,Of(b,d))):null}return null},null,null)}
-function ag(a,b){var c;a:{c=re;for(var d=a,e=K(b);;)if(e)if(null!=d?d.o&256||d.$e||(d.o?0:ub(Tb,d)):ub(Tb,d)){d=I.l(d,C(e),c);if(c===d){c=null;break a}e=D(e)}else{c=null;break a}else{c=d;break a}}return c}
-var bg=function bg(b,c,d){var e=S(c,0,null);c=Le(c,1);return u(c)?T.l(b,e,bg(I.h(b,e),c,d)):T.l(b,e,d)},cg=function cg(b){for(var c=[],d=arguments.length,e=0;;)if(e<d)c.push(arguments[e]),e+=1;else break;switch(c.length){case 3:return cg.l(arguments[0],arguments[1],arguments[2]);case 4:return cg.G(arguments[0],arguments[1],arguments[2],arguments[3]);case 5:return cg.N(arguments[0],arguments[1],arguments[2],arguments[3],arguments[4]);case 6:return cg.ra(arguments[0],arguments[1],arguments[2],arguments[3],
-arguments[4],arguments[5]);default:return cg.w(arguments[0],arguments[1],arguments[2],arguments[3],arguments[4],arguments[5],new B(c.slice(6),0))}};cg.l=function(a,b,c){var d=S(b,0,null);b=Le(b,1);return u(b)?T.l(a,d,cg.l(I.h(a,d),b,c)):T.l(a,d,function(){var b=I.h(a,d);return c.j?c.j(b):c.call(null,b)}())};cg.G=function(a,b,c,d){var e=S(b,0,null);b=Le(b,1);return u(b)?T.l(a,e,cg.G(I.h(a,e),b,c,d)):T.l(a,e,function(){var b=I.h(a,e);return c.h?c.h(b,d):c.call(null,b,d)}())};
-cg.N=function(a,b,c,d,e){var f=S(b,0,null);b=Le(b,1);return u(b)?T.l(a,f,cg.N(I.h(a,f),b,c,d,e)):T.l(a,f,function(){var b=I.h(a,f);return c.l?c.l(b,d,e):c.call(null,b,d,e)}())};cg.ra=function(a,b,c,d,e,f){var g=S(b,0,null);b=Le(b,1);return u(b)?T.l(a,g,cg.ra(I.h(a,g),b,c,d,e,f)):T.l(a,g,function(){var b=I.h(a,g);return c.G?c.G(b,d,e,f):c.call(null,b,d,e,f)}())};
-cg.w=function(a,b,c,d,e,f,g){var k=S(b,0,null);b=Le(b,1);return u(b)?T.l(a,k,A.w(cg,I.h(a,k),b,c,d,J([e,f,g],0))):T.l(a,k,A.w(c,I.h(a,k),d,e,f,J([g],0)))};cg.K=function(a){var b=C(a),c=D(a);a=C(c);var d=D(c),c=C(d),e=D(d),d=C(e),f=D(e),e=C(f),g=D(f),f=C(g),g=D(g);return cg.w(b,a,c,d,e,f,g)};cg.J=6;function dg(a,b){this.oa=a;this.v=b}
-function eg(a){return new dg(a,[null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null])}function fg(a){return new dg(a.oa,yb(a.v))}function gg(a){a=a.F;return 32>a?0:a-1>>>5<<5}function hg(a,b,c){for(;;){if(0===b)return c;var d=eg(a);d.v[0]=c;c=d;b-=5}}var ig=function ig(b,c,d,e){var f=fg(d),g=b.F-1>>>c&31;5===c?f.v[g]=e:(d=d.v[g],b=null!=d?ig(b,c-5,d,e):hg(null,c-5,e),f.v[g]=b);return f};
-function jg(a,b){throw Error([y("No item "),y(a),y(" in vector of length "),y(b)].join(""));}function kg(a,b){if(b>=gg(a))return a.ga;for(var c=a.root,d=a.shift;;)if(0<d)var e=d-5,c=c.v[b>>>d&31],d=e;else return c.v}function lg(a,b){return 0<=b&&b<a.F?kg(a,b):jg(b,a.F)}
-var mg=function mg(b,c,d,e,f){var g=fg(d);if(0===c)g.v[e&31]=f;else{var k=e>>>c&31;b=mg(b,c-5,d.v[k],e,f);g.v[k]=b}return g},ng=function ng(b,c,d){var e=b.F-2>>>c&31;if(5<c){b=ng(b,c-5,d.v[e]);if(null==b&&0===e)return null;d=fg(d);d.v[e]=b;return d}if(0===e)return null;d=fg(d);d.v[e]=null;return d};function og(a,b,c,d,e,f){this.i=a;this.base=b;this.v=c;this.tb=d;this.start=e;this.end=f}og.prototype.Fa=function(){return this.i<this.end};
-og.prototype.next=function(){32===this.i-this.base&&(this.v=kg(this.tb,this.i),this.base+=32);var a=this.v[this.i&31];this.i+=1;return a};pg;sg;tg;Q;ug;vg;wg;function V(a,b,c,d,e,f){this.meta=a;this.F=b;this.shift=c;this.root=d;this.ga=e;this.H=f;this.o=167668511;this.M=8196}h=V.prototype;h.toString=function(){return Xc(this)};h.equiv=function(a){return this.L(null,a)};h.X=function(a,b){return Ub.l(this,b,null)};h.P=function(a,b,c){return"number"===typeof b?Lb.l(this,b,c):c};
-h.Cc=function(a,b,c){a=0;for(var d=c;;)if(a<this.F){var e=kg(this,a);c=e.length;a:for(var f=0;;)if(f<c){var g=f+a,k=e[f],d=b.l?b.l(d,g,k):b.call(null,d,g,k);if(Ed(d)){e=d;break a}f+=1}else{e=d;break a}if(Ed(e))return Q.j?Q.j(e):Q.call(null,e);a+=c;d=e}else return d};h.aa=function(a,b){return lg(this,b)[b&31]};h.kb=function(a,b,c){return 0<=b&&b<this.F?kg(this,b)[b&31]:c};
-h.lc=function(a,b,c){if(0<=b&&b<this.F)return gg(this)<=b?(a=yb(this.ga),a[b&31]=c,new V(this.meta,this.F,this.shift,this.root,a,null)):new V(this.meta,this.F,this.shift,mg(this,this.shift,this.root,b,c),this.ga,null);if(b===this.F)return Jb(this,c);throw Error([y("Index "),y(b),y(" out of bounds [0,"),y(this.F),y("]")].join(""));};h.qb=function(){var a=this.F;return new og(0,0,0<R(this)?kg(this,0):null,this,0,a)};h.Z=function(){return this.meta};
-h.Za=function(){return new V(this.meta,this.F,this.shift,this.root,this.ga,this.H)};h.ia=function(){return this.F};h.Yc=function(){return Lb.h(this,0)};h.Zc=function(){return Lb.h(this,1)};h.ac=function(){return 0<this.F?Lb.h(this,this.F-1):null};
-h.bc=function(){if(0===this.F)throw Error("Can't pop empty vector");if(1===this.F)return mc(Wd,this.meta);if(1<this.F-gg(this))return new V(this.meta,this.F-1,this.shift,this.root,this.ga.slice(0,-1),null);var a=kg(this,this.F-2),b=ng(this,this.shift,this.root),b=null==b?W:b,c=this.F-1;return 5<this.shift&&null==b.v[1]?new V(this.meta,c,this.shift-5,b.v[0],a,null):new V(this.meta,c,this.shift,b,a,null)};h.Dc=function(){return 0<this.F?new Nd(this,this.F-1,null):null};
-h.W=function(){var a=this.H;return null!=a?a:this.H=a=wd(this)};h.L=function(a,b){if(b instanceof V)if(this.F===R(b))for(var c=Vc(this),d=Vc(b);;)if(u(c.Fa())){var e=c.next(),f=d.next();if(!H.h(e,f))return!1}else return!0;else return!1;else return Ad(this,b)};h.Bc=function(){return new tg(this.F,this.shift,pg.j?pg.j(this.root):pg.call(null,this.root),sg.j?sg.j(this.ga):sg.call(null,this.ga))};h.qa=function(){return Bd(Wd,this.meta)};h.za=function(a,b){return Fd(this,b)};
-h.Aa=function(a,b,c){a=0;for(var d=c;;)if(a<this.F){var e=kg(this,a);c=e.length;a:for(var f=0;;)if(f<c){var g=e[f],d=b.h?b.h(d,g):b.call(null,d,g);if(Ed(d)){e=d;break a}f+=1}else{e=d;break a}if(Ed(e))return Q.j?Q.j(e):Q.call(null,e);a+=c;d=e}else return d};h.Gb=function(a,b,c){if("number"===typeof b)return hc(this,b,c);throw Error("Vector's key for assoc must be a number.");};
-h.fa=function(){if(0===this.F)return null;if(32>=this.F)return new B(this.ga,0);var a;a:{a=this.root;for(var b=this.shift;;)if(0<b)b-=5,a=a.v[0];else{a=a.v;break a}}return wg.G?wg.G(this,a,0,0):wg.call(null,this,a,0,0)};h.ba=function(a,b){return new V(b,this.F,this.shift,this.root,this.ga,this.H)};
-h.ha=function(a,b){if(32>this.F-gg(this)){for(var c=this.ga.length,d=Array(c+1),e=0;;)if(e<c)d[e]=this.ga[e],e+=1;else break;d[c]=b;return new V(this.meta,this.F+1,this.shift,this.root,d,null)}c=(d=this.F>>>5>1<<this.shift)?this.shift+5:this.shift;d?(d=eg(null),d.v[0]=this.root,e=hg(null,this.shift,new dg(null,this.ga)),d.v[1]=e):d=ig(this,this.shift,this.root,new dg(null,this.ga));return new V(this.meta,this.F+1,c,d,[b],null)};
-h.call=function(){var a=null,a=function(a,c,d){switch(arguments.length){case 2:return this.aa(null,c);case 3:return this.kb(null,c,d)}throw Error("Invalid arity: "+arguments.length);};a.h=function(a,c){return this.aa(null,c)};a.l=function(a,c,d){return this.kb(null,c,d)};return a}();h.apply=function(a,b){return this.call.apply(this,[this].concat(yb(b)))};h.j=function(a){return this.aa(null,a)};h.h=function(a,b){return this.kb(null,a,b)};
-var W=new dg(null,[null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null]),Wd=new V(null,0,5,W,[],xd);function xg(a,b){var c=a.length,d=b?a:yb(a);if(32>c)return new V(null,c,5,W,d,null);for(var e=32,f=(new V(null,32,5,W,d.slice(0,32),null)).Bc(null);;)if(e<c)var g=e+1,f=jf.h(f,d[e]),e=g;else return Hc(f)}V.prototype[xb]=function(){return sd(this)};
-function ze(a){return rb(a)?xg(a,!0):Hc(Ab.l(Gc,Fc(Wd),a))}var yg=function yg(b){for(var c=[],d=arguments.length,e=0;;)if(e<d)c.push(arguments[e]),e+=1;else break;return yg.w(0<c.length?new B(c.slice(0),0):null)};yg.w=function(a){return a instanceof B&&0===a.i?xg(a.v,!0):ze(a)};yg.J=0;yg.K=function(a){return yg.w(K(a))};zg;function ne(a,b,c,d,e,f){this.ub=a;this.node=b;this.i=c;this.Ia=d;this.meta=e;this.H=f;this.o=32375020;this.M=1536}h=ne.prototype;h.toString=function(){return Xc(this)};
-h.equiv=function(a){return this.L(null,a)};h.Z=function(){return this.meta};h.$a=function(){if(this.Ia+1<this.node.length){var a;a=this.ub;var b=this.node,c=this.i,d=this.Ia+1;a=wg.G?wg.G(a,b,c,d):wg.call(null,a,b,c,d);return null==a?null:a}return Pc(this)};h.W=function(){var a=this.H;return null!=a?a:this.H=a=wd(this)};h.L=function(a,b){return Ad(this,b)};h.qa=function(){return Bd(Wd,this.meta)};
-h.za=function(a,b){var c;c=this.ub;var d=this.i+this.Ia,e=R(this.ub);c=zg.l?zg.l(c,d,e):zg.call(null,c,d,e);return Fd(c,b)};h.Aa=function(a,b,c){a=this.ub;var d=this.i+this.Ia,e=R(this.ub);a=zg.l?zg.l(a,d,e):zg.call(null,a,d,e);return Gd(a,b,c)};h.wa=function(){return this.node[this.Ia]};h.Da=function(){if(this.Ia+1<this.node.length){var a;a=this.ub;var b=this.node,c=this.i,d=this.Ia+1;a=wg.G?wg.G(a,b,c,d):wg.call(null,a,b,c,d);return null==a?nd:a}return Oc(this)};h.fa=function(){return this};
-h.se=function(){var a=this.node;return new af(a,this.Ia,a.length)};h.te=function(){var a=this.i+this.node.length;if(a<Gb(this.ub)){var b=this.ub,c=kg(this.ub,a);return wg.G?wg.G(b,c,a,0):wg.call(null,b,c,a,0)}return nd};h.ba=function(a,b){return wg.N?wg.N(this.ub,this.node,this.i,this.Ia,b):wg.call(null,this.ub,this.node,this.i,this.Ia,b)};h.ha=function(a,b){return Md(b,this)};
-h.re=function(){var a=this.i+this.node.length;if(a<Gb(this.ub)){var b=this.ub,c=kg(this.ub,a);return wg.G?wg.G(b,c,a,0):wg.call(null,b,c,a,0)}return null};ne.prototype[xb]=function(){return sd(this)};
-var wg=function wg(b){for(var c=[],d=arguments.length,e=0;;)if(e<d)c.push(arguments[e]),e+=1;else break;switch(c.length){case 3:return wg.l(arguments[0],arguments[1],arguments[2]);case 4:return wg.G(arguments[0],arguments[1],arguments[2],arguments[3]);case 5:return wg.N(arguments[0],arguments[1],arguments[2],arguments[3],arguments[4]);default:throw Error([y("Invalid arity: "),y(c.length)].join(""));}};wg.l=function(a,b,c){return new ne(a,lg(a,b),b,c,null,null)};
-wg.G=function(a,b,c,d){return new ne(a,b,c,d,null,null)};wg.N=function(a,b,c,d,e){return new ne(a,b,c,d,e,null)};wg.J=5;Ag;function Bg(a,b,c,d,e){this.meta=a;this.tb=b;this.start=c;this.end=d;this.H=e;this.o=167666463;this.M=8192}h=Bg.prototype;h.toString=function(){return Xc(this)};h.equiv=function(a){return this.L(null,a)};h.X=function(a,b){return Ub.l(this,b,null)};h.P=function(a,b,c){return"number"===typeof b?Lb.l(this,b,c):c};
-h.Cc=function(a,b,c){a=this.start;for(var d=0;;)if(a<this.end){var e=d,f=Lb.h(this.tb,a);c=b.l?b.l(c,e,f):b.call(null,c,e,f);if(Ed(c))return Q.j?Q.j(c):Q.call(null,c);d+=1;a+=1}else return c};h.aa=function(a,b){return 0>b||this.end<=this.start+b?jg(b,this.end-this.start):Lb.h(this.tb,this.start+b)};h.kb=function(a,b,c){return 0>b||this.end<=this.start+b?c:Lb.l(this.tb,this.start+b,c)};
-h.lc=function(a,b,c){var d=this.start+b;a=this.meta;c=T.l(this.tb,d,c);b=this.start;var e=this.end,d=d+1,d=e>d?e:d;return Ag.N?Ag.N(a,c,b,d,null):Ag.call(null,a,c,b,d,null)};h.Z=function(){return this.meta};h.Za=function(){return new Bg(this.meta,this.tb,this.start,this.end,this.H)};h.ia=function(){return this.end-this.start};h.ac=function(){return Lb.h(this.tb,this.end-1)};
-h.bc=function(){if(this.start===this.end)throw Error("Can't pop empty vector");var a=this.meta,b=this.tb,c=this.start,d=this.end-1;return Ag.N?Ag.N(a,b,c,d,null):Ag.call(null,a,b,c,d,null)};h.Dc=function(){return this.start!==this.end?new Nd(this,this.end-this.start-1,null):null};h.W=function(){var a=this.H;return null!=a?a:this.H=a=wd(this)};h.L=function(a,b){return Ad(this,b)};h.qa=function(){return Bd(Wd,this.meta)};h.za=function(a,b){return Fd(this,b)};h.Aa=function(a,b,c){return Gd(this,b,c)};
-h.Gb=function(a,b,c){if("number"===typeof b)return hc(this,b,c);throw Error("Subvec's key for assoc must be a number.");};h.fa=function(){var a=this;return function(b){return function d(e){return e===a.end?null:Md(Lb.h(a.tb,e),new Ze(null,function(){return function(){return d(e+1)}}(b),null,null))}}(this)(a.start)};h.ba=function(a,b){return Ag.N?Ag.N(b,this.tb,this.start,this.end,this.H):Ag.call(null,b,this.tb,this.start,this.end,this.H)};
-h.ha=function(a,b){var c=this.meta,d=hc(this.tb,this.end,b),e=this.start,f=this.end+1;return Ag.N?Ag.N(c,d,e,f,null):Ag.call(null,c,d,e,f,null)};h.call=function(){var a=null,a=function(a,c,d){switch(arguments.length){case 2:return this.aa(null,c);case 3:return this.kb(null,c,d)}throw Error("Invalid arity: "+arguments.length);};a.h=function(a,c){return this.aa(null,c)};a.l=function(a,c,d){return this.kb(null,c,d)};return a}();h.apply=function(a,b){return this.call.apply(this,[this].concat(yb(b)))};
-h.j=function(a){return this.aa(null,a)};h.h=function(a,b){return this.kb(null,a,b)};Bg.prototype[xb]=function(){return sd(this)};function Ag(a,b,c,d,e){for(;;)if(b instanceof Bg)c=b.start+c,d=b.start+d,b=b.tb;else{var f=R(b);if(0>c||0>d||c>f||d>f)throw Error("Index out of bounds");return new Bg(a,b,c,d,e)}}
-var zg=function zg(b){for(var c=[],d=arguments.length,e=0;;)if(e<d)c.push(arguments[e]),e+=1;else break;switch(c.length){case 2:return zg.h(arguments[0],arguments[1]);case 3:return zg.l(arguments[0],arguments[1],arguments[2]);default:throw Error([y("Invalid arity: "),y(c.length)].join(""));}};zg.h=function(a,b){return zg.l(a,b,R(a))};zg.l=function(a,b,c){return Ag(null,a,b,c,null)};zg.J=3;function Cg(a,b){return a===b.oa?b:new dg(a,yb(b.v))}function pg(a){return new dg({},yb(a.v))}
-function sg(a){var b=[null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null];qe(a,0,b,0,a.length);return b}var Dg=function Dg(b,c,d,e){d=Cg(b.root.oa,d);var f=b.F-1>>>c&31;if(5===c)b=e;else{var g=d.v[f];b=null!=g?Dg(b,c-5,g,e):hg(b.root.oa,c-5,e)}d.v[f]=b;return d};function tg(a,b,c,d){this.F=a;this.shift=b;this.root=c;this.ga=d;this.M=88;this.o=275}h=tg.prototype;
-h.kc=function(a,b){if(this.root.oa){if(32>this.F-gg(this))this.ga[this.F&31]=b;else{var c=new dg(this.root.oa,this.ga),d=[null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null];d[0]=b;this.ga=d;if(this.F>>>5>1<<this.shift){var d=[null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null],e=this.shift+
-5;d[0]=this.root;d[1]=hg(this.root.oa,this.shift,c);this.root=new dg(this.root.oa,d);this.shift=e}else this.root=Dg(this,this.shift,this.root,c)}this.F+=1;return this}throw Error("conj! after persistent!");};h.Ec=function(){if(this.root.oa){this.root.oa=null;var a=this.F-gg(this),b=Array(a);qe(this.ga,0,b,0,a);return new V(null,this.F,this.shift,this.root,b,null)}throw Error("persistent! called twice");};
-h.bd=function(a,b,c){if("number"===typeof b)return Jc(this,b,c);throw Error("TransientVector's key for assoc! must be a number.");};
-h.bf=function(a,b,c){var d=this;if(d.root.oa){if(0<=b&&b<d.F)return gg(this)<=b?d.ga[b&31]=c:(a=function(){return function f(a,k){var l=Cg(d.root.oa,k);if(0===a)l.v[b&31]=c;else{var n=b>>>a&31,m=f(a-5,l.v[n]);l.v[n]=m}return l}}(this).call(null,d.shift,d.root),d.root=a),this;if(b===d.F)return Gc(this,c);throw Error([y("Index "),y(b),y(" out of bounds for TransientVector of length"),y(d.F)].join(""));}throw Error("assoc! after persistent!");};
-h.ia=function(){if(this.root.oa)return this.F;throw Error("count after persistent!");};h.aa=function(a,b){if(this.root.oa)return lg(this,b)[b&31];throw Error("nth after persistent!");};h.kb=function(a,b,c){return 0<=b&&b<this.F?Lb.h(this,b):c};h.X=function(a,b){return Ub.l(this,b,null)};h.P=function(a,b,c){return"number"===typeof b?Lb.l(this,b,c):c};
-h.call=function(){var a=null,a=function(a,c,d){switch(arguments.length){case 2:return this.X(null,c);case 3:return this.P(null,c,d)}throw Error("Invalid arity: "+arguments.length);};a.h=function(a,c){return this.X(null,c)};a.l=function(a,c,d){return this.P(null,c,d)};return a}();h.apply=function(a,b){return this.call.apply(this,[this].concat(yb(b)))};h.j=function(a){return this.X(null,a)};h.h=function(a,b){return this.P(null,a,b)};function Eg(a,b){this.Ic=a;this.wd=b}
-Eg.prototype.Fa=function(){var a=null!=this.Ic&&K(this.Ic);return a?a:(a=null!=this.wd)?this.wd.Fa():a};Eg.prototype.next=function(){if(null!=this.Ic){var a=C(this.Ic);this.Ic=D(this.Ic);return a}if(null!=this.wd&&this.wd.Fa())return this.wd.next();throw Error("No such element");};Eg.prototype.remove=function(){return Error("Unsupported operation")};function Fg(a,b,c,d){this.meta=a;this.mb=b;this.Fb=c;this.H=d;this.o=31850572;this.M=0}h=Fg.prototype;h.toString=function(){return Xc(this)};
-h.equiv=function(a){return this.L(null,a)};h.Z=function(){return this.meta};h.W=function(){var a=this.H;return null!=a?a:this.H=a=wd(this)};h.L=function(a,b){return Ad(this,b)};h.qa=function(){return Bd(nd,this.meta)};h.wa=function(){return C(this.mb)};h.Da=function(){var a=D(this.mb);return a?new Fg(this.meta,a,this.Fb,null):null==this.Fb?Hb(this):new Fg(this.meta,this.Fb,null,null)};h.fa=function(){return this};h.ba=function(a,b){return new Fg(b,this.mb,this.Fb,this.H)};
-h.ha=function(a,b){return Md(b,this)};Fg.prototype[xb]=function(){return sd(this)};function Gg(a,b,c,d,e){this.meta=a;this.count=b;this.mb=c;this.Fb=d;this.H=e;this.o=31858766;this.M=8192}h=Gg.prototype;h.toString=function(){return Xc(this)};h.equiv=function(a){return this.L(null,a)};h.qb=function(){return new Eg(this.mb,Vc(this.Fb))};h.Z=function(){return this.meta};h.Za=function(){return new Gg(this.meta,this.count,this.mb,this.Fb,this.H)};h.ia=function(){return this.count};h.ac=function(){return C(this.mb)};
-h.bc=function(){if(u(this.mb)){var a=D(this.mb);return a?new Gg(this.meta,this.count-1,a,this.Fb,null):new Gg(this.meta,this.count-1,K(this.Fb),Wd,null)}return this};h.W=function(){var a=this.H;return null!=a?a:this.H=a=wd(this)};h.L=function(a,b){return Ad(this,b)};h.qa=function(){return Bd(Hg,this.meta)};h.wa=function(){return C(this.mb)};h.Da=function(){return N(K(this))};h.fa=function(){var a=K(this.Fb),b=this.mb;return u(u(b)?b:a)?new Fg(null,this.mb,K(a),null):null};
-h.ba=function(a,b){return new Gg(b,this.count,this.mb,this.Fb,this.H)};h.ha=function(a,b){var c;u(this.mb)?(c=this.Fb,c=new Gg(this.meta,this.count+1,this.mb,Vd.h(u(c)?c:Wd,b),null)):c=new Gg(this.meta,this.count+1,Vd.h(this.mb,b),Wd,null);return c};var Hg=new Gg(null,0,null,Wd,xd);Gg.prototype[xb]=function(){return sd(this)};function Ig(){this.o=2097152;this.M=0}Ig.prototype.equiv=function(a){return this.L(null,a)};Ig.prototype.L=function(){return!1};var Jg=new Ig;
-function Kg(a,b){return te(ke(b)?R(a)===R(b)?tf(Be,Me.h(function(a){return H.h(I.l(b,C(a),Jg),Td(a))},a)):null:null)}function Lg(a,b,c,d,e){this.i=a;this.mg=b;this.Ve=c;this.Xf=d;this.jf=e}Lg.prototype.Fa=function(){var a=this.i<this.Ve;return a?a:this.jf.Fa()};Lg.prototype.next=function(){if(this.i<this.Ve){var a=Zd(this.Xf,this.i);this.i+=1;return new V(null,2,5,W,[a,Ub.h(this.mg,a)],null)}return this.jf.next()};Lg.prototype.remove=function(){return Error("Unsupported operation")};
-function Mg(a){this.s=a}Mg.prototype.next=function(){if(null!=this.s){var a=C(this.s),b=S(a,0,null),a=S(a,1,null);this.s=D(this.s);return{value:[b,a],done:!1}}return{value:null,done:!0}};function Ng(a){return new Mg(K(a))}function Og(a){this.s=a}Og.prototype.next=function(){if(null!=this.s){var a=C(this.s);this.s=D(this.s);return{value:[a,a],done:!1}}return{value:null,done:!0}};function Pg(a){return new Og(K(a))}
-function Qg(a,b){var c;if(b instanceof v)a:{c=a.length;for(var d=b.ab,e=0;;){if(c<=e){c=-1;break a}if(a[e]instanceof v&&d===a[e].ab){c=e;break a}e+=2}}else if(ha(b)||"number"===typeof b)a:for(c=a.length,d=0;;){if(c<=d){c=-1;break a}if(b===a[d]){c=d;break a}d+=2}else if(b instanceof dd)a:for(c=a.length,d=b.ib,e=0;;){if(c<=e){c=-1;break a}if(a[e]instanceof dd&&d===a[e].ib){c=e;break a}e+=2}else if(null==b)a:for(c=a.length,d=0;;){if(c<=d){c=-1;break a}if(null==a[d]){c=d;break a}d+=2}else a:for(c=a.length,
-d=0;;){if(c<=d){c=-1;break a}if(H.h(b,a[d])){c=d;break a}d+=2}return c}Rg;function Sg(a,b,c){this.v=a;this.i=b;this.jb=c;this.o=32374990;this.M=0}h=Sg.prototype;h.toString=function(){return Xc(this)};h.equiv=function(a){return this.L(null,a)};h.Z=function(){return this.jb};h.$a=function(){return this.i<this.v.length-2?new Sg(this.v,this.i+2,this.jb):null};h.ia=function(){return(this.v.length-this.i)/2};h.W=function(){return wd(this)};h.L=function(a,b){return Ad(this,b)};
-h.qa=function(){return Bd(nd,this.jb)};h.za=function(a,b){return Sd.h(b,this)};h.Aa=function(a,b,c){return Sd.l(b,c,this)};h.wa=function(){return new V(null,2,5,W,[this.v[this.i],this.v[this.i+1]],null)};h.Da=function(){return this.i<this.v.length-2?new Sg(this.v,this.i+2,this.jb):nd};h.fa=function(){return this};h.ba=function(a,b){return new Sg(this.v,this.i,b)};h.ha=function(a,b){return Md(b,this)};Sg.prototype[xb]=function(){return sd(this)};Tg;Ug;
-function Vg(a,b,c){this.v=a;this.i=b;this.F=c}Vg.prototype.Fa=function(){return this.i<this.F};Vg.prototype.next=function(){var a=new V(null,2,5,W,[this.v[this.i],this.v[this.i+1]],null);this.i+=2;return a};function r(a,b,c,d){this.meta=a;this.F=b;this.v=c;this.H=d;this.o=16647951;this.M=8196}h=r.prototype;h.toString=function(){return Xc(this)};h.equiv=function(a){return this.L(null,a)};h.keys=function(){return sd(Tg.j?Tg.j(this):Tg.call(null,this))};h.entries=function(){return Ng(K(this))};
-h.values=function(){return sd(Ug.j?Ug.j(this):Ug.call(null,this))};h.has=function(a){return ve(this,a)};h.get=function(a,b){return this.P(null,a,b)};h.forEach=function(a){for(var b=K(this),c=null,d=0,e=0;;)if(e<d){var f=c.aa(null,e),g=S(f,0,null),f=S(f,1,null);a.h?a.h(f,g):a.call(null,f,g);e+=1}else if(b=K(b))oe(b)?(c=Nc(b),b=Oc(b),g=c,d=R(c),c=g):(c=C(b),g=S(c,0,null),f=S(c,1,null),a.h?a.h(f,g):a.call(null,f,g),b=D(b),c=null,d=0),e=0;else return null};h.X=function(a,b){return Ub.l(this,b,null)};
-h.P=function(a,b,c){a=Qg(this.v,b);return-1===a?c:this.v[a+1]};h.Cc=function(a,b,c){a=this.v.length;for(var d=0;;)if(d<a){var e=this.v[d],f=this.v[d+1];c=b.l?b.l(c,e,f):b.call(null,c,e,f);if(Ed(c))return Q.j?Q.j(c):Q.call(null,c);d+=2}else return c};h.qb=function(){return new Vg(this.v,0,2*this.F)};h.Z=function(){return this.meta};h.Za=function(){return new r(this.meta,this.F,this.v,this.H)};h.ia=function(){return this.F};h.W=function(){var a=this.H;return null!=a?a:this.H=a=yd(this)};
-h.L=function(a,b){if(null!=b&&(b.o&1024||b.Qf)){var c=this.v.length;if(this.F===b.ia(null))for(var d=0;;)if(d<c){var e=b.P(null,this.v[d],re);if(e!==re)if(H.h(this.v[d+1],e))d+=2;else return!1;else return!1}else return!0;else return!1}else return Kg(this,b)};h.Bc=function(){return new Rg({},this.v.length,yb(this.v))};h.qa=function(){return mc(rf,this.meta)};h.za=function(a,b){return Sd.h(b,this)};h.Aa=function(a,b,c){return Sd.l(b,c,this)};
-h.jc=function(a,b){if(0<=Qg(this.v,b)){var c=this.v.length,d=c-2;if(0===d)return Hb(this);for(var d=Array(d),e=0,f=0;;){if(e>=c)return new r(this.meta,this.F-1,d,null);H.h(b,this.v[e])||(d[f]=this.v[e],d[f+1]=this.v[e+1],f+=2);e+=2}}else return this};
-h.Gb=function(a,b,c){a=Qg(this.v,b);if(-1===a){if(this.F<Wg){a=this.v;for(var d=a.length,e=Array(d+2),f=0;;)if(f<d)e[f]=a[f],f+=1;else break;e[d]=b;e[d+1]=c;return new r(this.meta,this.F+1,e,null)}return mc(Wb(Yf.h(Xg,this),b,c),this.meta)}if(c===this.v[a+1])return this;b=yb(this.v);b[a+1]=c;return new r(this.meta,this.F,b,null)};h.Hd=function(a,b){return-1!==Qg(this.v,b)};h.fa=function(){var a=this.v;return 0<=a.length-2?new Sg(a,0,null):null};h.ba=function(a,b){return new r(b,this.F,this.v,this.H)};
-h.ha=function(a,b){if(le(b))return Wb(this,Lb.h(b,0),Lb.h(b,1));for(var c=this,d=K(b);;){if(null==d)return c;var e=C(d);if(le(e))c=Wb(c,Lb.h(e,0),Lb.h(e,1)),d=D(d);else throw Error("conj on a map takes map entries or seqables of map entries");}};
-h.call=function(){var a=null,a=function(a,c,d){switch(arguments.length){case 2:return this.X(null,c);case 3:return this.P(null,c,d)}throw Error("Invalid arity: "+arguments.length);};a.h=function(a,c){return this.X(null,c)};a.l=function(a,c,d){return this.P(null,c,d)};return a}();h.apply=function(a,b){return this.call.apply(this,[this].concat(yb(b)))};h.j=function(a){return this.X(null,a)};h.h=function(a,b){return this.P(null,a,b)};var rf=new r(null,0,[],zd),Wg=8;
-function Yg(a,b,c){a=b?a:yb(a);if(!c){c=[];for(b=0;;)if(b<a.length){var d=a[b],e=a[b+1];-1===Qg(c,d)&&(c.push(d),c.push(e));b+=2}else break;a=c}return new r(null,a.length/2,a,null)}r.prototype[xb]=function(){return sd(this)};Zg;function Rg(a,b,c){this.Gc=a;this.tc=b;this.v=c;this.o=258;this.M=56}h=Rg.prototype;h.ia=function(){if(u(this.Gc))return Je(this.tc);throw Error("count after persistent!");};h.X=function(a,b){return Ub.l(this,b,null)};
-h.P=function(a,b,c){if(u(this.Gc))return a=Qg(this.v,b),-1===a?c:this.v[a+1];throw Error("lookup after persistent!");};h.kc=function(a,b){if(u(this.Gc)){if(null!=b?b.o&2048||b.Rf||(b.o?0:ub($b,b)):ub($b,b))return Ic(this,Oe.j?Oe.j(b):Oe.call(null,b),Pe.j?Pe.j(b):Pe.call(null,b));for(var c=K(b),d=this;;){var e=C(c);if(u(e))c=D(c),d=Ic(d,Oe.j?Oe.j(e):Oe.call(null,e),Pe.j?Pe.j(e):Pe.call(null,e));else return d}}else throw Error("conj! after persistent!");};
-h.Ec=function(){if(u(this.Gc))return this.Gc=!1,new r(null,Je(this.tc),this.v,null);throw Error("persistent! called twice");};h.bd=function(a,b,c){if(u(this.Gc)){a=Qg(this.v,b);if(-1===a){if(this.tc+2<=2*Wg)return this.tc+=2,this.v.push(b),this.v.push(c),this;a=Zg.h?Zg.h(this.tc,this.v):Zg.call(null,this.tc,this.v);return Ic(a,b,c)}c!==this.v[a+1]&&(this.v[a+1]=c);return this}throw Error("assoc! after persistent!");};$g;$d;
-function Zg(a,b){for(var c=Fc(Xg),d=0;;)if(d<a)c=Ic(c,b[d],b[d+1]),d+=2;else return c}function ah(){this.I=!1}bh;ch;Ef;dh;X;Q;function eh(a,b){return a===b?!0:U(a,b)?!0:H.h(a,b)}function fh(a,b,c){a=yb(a);a[b]=c;return a}function gh(a,b){var c=Array(a.length-2);qe(a,0,c,0,2*b);qe(a,2*(b+1),c,2*b,c.length-2*b);return c}function hh(a,b,c,d){a=a.oc(b);a.v[c]=d;return a}
-function ih(a,b,c){for(var d=a.length,e=0,f=c;;)if(e<d){c=a[e];if(null!=c){var g=a[e+1];c=b.l?b.l(f,c,g):b.call(null,f,c,g)}else c=a[e+1],c=null!=c?c.sc(b,f):f;if(Ed(c))return Q.j?Q.j(c):Q.call(null,c);e+=2;f=c}else return f}jh;function kh(a,b,c,d){this.v=a;this.i=b;this.ud=c;this.Lb=d}kh.prototype.advance=function(){for(var a=this.v.length;;)if(this.i<a){var b=this.v[this.i],c=this.v[this.i+1];null!=b?b=this.ud=new V(null,2,5,W,[b,c],null):null!=c?(b=Vc(c),b=b.Fa()?this.Lb=b:!1):b=!1;this.i+=2;if(b)return!0}else return!1};
-kh.prototype.Fa=function(){var a=null!=this.ud;return a?a:(a=null!=this.Lb)?a:this.advance()};kh.prototype.next=function(){if(null!=this.ud){var a=this.ud;this.ud=null;return a}if(null!=this.Lb)return a=this.Lb.next(),this.Lb.Fa()||(this.Lb=null),a;if(this.advance())return this.next();throw Error("No such element");};kh.prototype.remove=function(){return Error("Unsupported operation")};function lh(a,b,c){this.oa=a;this.ua=b;this.v=c}h=lh.prototype;
-h.oc=function(a){if(a===this.oa)return this;var b=Ke(this.ua),c=Array(0>b?4:2*(b+1));qe(this.v,0,c,0,2*b);return new lh(a,this.ua,c)};h.od=function(){return bh.j?bh.j(this.v):bh.call(null,this.v)};h.sc=function(a,b){return ih(this.v,a,b)};h.dc=function(a,b,c,d){var e=1<<(b>>>a&31);if(0===(this.ua&e))return d;var f=Ke(this.ua&e-1),e=this.v[2*f],f=this.v[2*f+1];return null==e?f.dc(a+5,b,c,d):eh(c,e)?f:d};
-h.Kb=function(a,b,c,d,e,f){var g=1<<(c>>>b&31),k=Ke(this.ua&g-1);if(0===(this.ua&g)){var l=Ke(this.ua);if(2*l<this.v.length){a=this.oc(a);b=a.v;f.I=!0;a:for(c=2*(l-k),f=2*k+(c-1),l=2*(k+1)+(c-1);;){if(0===c)break a;b[l]=b[f];--l;--c;--f}b[2*k]=d;b[2*k+1]=e;a.ua|=g;return a}if(16<=l){k=[null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null];k[c>>>b&31]=mh.Kb(a,b+5,c,d,e,f);for(e=d=0;;)if(32>d)0!==
-(this.ua>>>d&1)&&(k[d]=null!=this.v[e]?mh.Kb(a,b+5,id(this.v[e]),this.v[e],this.v[e+1],f):this.v[e+1],e+=2),d+=1;else break;return new jh(a,l+1,k)}b=Array(2*(l+4));qe(this.v,0,b,0,2*k);b[2*k]=d;b[2*k+1]=e;qe(this.v,2*k,b,2*(k+1),2*(l-k));f.I=!0;a=this.oc(a);a.v=b;a.ua|=g;return a}l=this.v[2*k];g=this.v[2*k+1];if(null==l)return l=g.Kb(a,b+5,c,d,e,f),l===g?this:hh(this,a,2*k+1,l);if(eh(d,l))return e===g?this:hh(this,a,2*k+1,e);f.I=!0;f=b+5;d=dh.ta?dh.ta(a,f,l,g,c,d,e):dh.call(null,a,f,l,g,c,d,e);e=
-2*k;k=2*k+1;a=this.oc(a);a.v[e]=null;a.v[k]=d;return a};
-h.Jb=function(a,b,c,d,e){var f=1<<(b>>>a&31),g=Ke(this.ua&f-1);if(0===(this.ua&f)){var k=Ke(this.ua);if(16<=k){g=[null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null];g[b>>>a&31]=mh.Jb(a+5,b,c,d,e);for(d=c=0;;)if(32>c)0!==(this.ua>>>c&1)&&(g[c]=null!=this.v[d]?mh.Jb(a+5,id(this.v[d]),this.v[d],this.v[d+1],e):this.v[d+1],d+=2),c+=1;else break;return new jh(null,k+1,g)}a=Array(2*(k+1));qe(this.v,
-0,a,0,2*g);a[2*g]=c;a[2*g+1]=d;qe(this.v,2*g,a,2*(g+1),2*(k-g));e.I=!0;return new lh(null,this.ua|f,a)}var l=this.v[2*g],f=this.v[2*g+1];if(null==l)return k=f.Jb(a+5,b,c,d,e),k===f?this:new lh(null,this.ua,fh(this.v,2*g+1,k));if(eh(c,l))return d===f?this:new lh(null,this.ua,fh(this.v,2*g+1,d));e.I=!0;e=this.ua;k=this.v;a+=5;a=dh.ra?dh.ra(a,l,f,b,c,d):dh.call(null,a,l,f,b,c,d);c=2*g;g=2*g+1;d=yb(k);d[c]=null;d[g]=a;return new lh(null,e,d)};
-h.pd=function(a,b,c){var d=1<<(b>>>a&31);if(0===(this.ua&d))return this;var e=Ke(this.ua&d-1),f=this.v[2*e],g=this.v[2*e+1];return null==f?(a=g.pd(a+5,b,c),a===g?this:null!=a?new lh(null,this.ua,fh(this.v,2*e+1,a)):this.ua===d?null:new lh(null,this.ua^d,gh(this.v,e))):eh(c,f)?new lh(null,this.ua^d,gh(this.v,e)):this};h.qb=function(){return new kh(this.v,0,null,null)};var mh=new lh(null,0,[]);function nh(a,b,c){this.v=a;this.i=b;this.Lb=c}
-nh.prototype.Fa=function(){for(var a=this.v.length;;){if(null!=this.Lb&&this.Lb.Fa())return!0;if(this.i<a){var b=this.v[this.i];this.i+=1;null!=b&&(this.Lb=Vc(b))}else return!1}};nh.prototype.next=function(){if(this.Fa())return this.Lb.next();throw Error("No such element");};nh.prototype.remove=function(){return Error("Unsupported operation")};function jh(a,b,c){this.oa=a;this.F=b;this.v=c}h=jh.prototype;h.oc=function(a){return a===this.oa?this:new jh(a,this.F,yb(this.v))};
-h.od=function(){return ch.j?ch.j(this.v):ch.call(null,this.v)};h.sc=function(a,b){for(var c=this.v.length,d=0,e=b;;)if(d<c){var f=this.v[d];if(null!=f&&(e=f.sc(a,e),Ed(e)))return Q.j?Q.j(e):Q.call(null,e);d+=1}else return e};h.dc=function(a,b,c,d){var e=this.v[b>>>a&31];return null!=e?e.dc(a+5,b,c,d):d};h.Kb=function(a,b,c,d,e,f){var g=c>>>b&31,k=this.v[g];if(null==k)return a=hh(this,a,g,mh.Kb(a,b+5,c,d,e,f)),a.F+=1,a;b=k.Kb(a,b+5,c,d,e,f);return b===k?this:hh(this,a,g,b)};
-h.Jb=function(a,b,c,d,e){var f=b>>>a&31,g=this.v[f];if(null==g)return new jh(null,this.F+1,fh(this.v,f,mh.Jb(a+5,b,c,d,e)));a=g.Jb(a+5,b,c,d,e);return a===g?this:new jh(null,this.F,fh(this.v,f,a))};
-h.pd=function(a,b,c){var d=b>>>a&31,e=this.v[d];if(null!=e){a=e.pd(a+5,b,c);if(a===e)d=this;else if(null==a)if(8>=this.F)a:{e=this.v;a=e.length;b=Array(2*(this.F-1));c=0;for(var f=1,g=0;;)if(c<a)c!==d&&null!=e[c]&&(b[f]=e[c],f+=2,g|=1<<c),c+=1;else{d=new lh(null,g,b);break a}}else d=new jh(null,this.F-1,fh(this.v,d,a));else d=new jh(null,this.F,fh(this.v,d,a));return d}return this};h.qb=function(){return new nh(this.v,0,null)};
-function oh(a,b,c){b*=2;for(var d=0;;)if(d<b){if(eh(c,a[d]))return d;d+=2}else return-1}function ph(a,b,c,d){this.oa=a;this.Ub=b;this.F=c;this.v=d}h=ph.prototype;h.oc=function(a){if(a===this.oa)return this;var b=Array(2*(this.F+1));qe(this.v,0,b,0,2*this.F);return new ph(a,this.Ub,this.F,b)};h.od=function(){return bh.j?bh.j(this.v):bh.call(null,this.v)};h.sc=function(a,b){return ih(this.v,a,b)};h.dc=function(a,b,c,d){a=oh(this.v,this.F,c);return 0>a?d:eh(c,this.v[a])?this.v[a+1]:d};
-h.Kb=function(a,b,c,d,e,f){if(c===this.Ub){b=oh(this.v,this.F,d);if(-1===b){if(this.v.length>2*this.F)return b=2*this.F,c=2*this.F+1,a=this.oc(a),a.v[b]=d,a.v[c]=e,f.I=!0,a.F+=1,a;c=this.v.length;b=Array(c+2);qe(this.v,0,b,0,c);b[c]=d;b[c+1]=e;f.I=!0;d=this.F+1;a===this.oa?(this.v=b,this.F=d,a=this):a=new ph(this.oa,this.Ub,d,b);return a}return this.v[b+1]===e?this:hh(this,a,b+1,e)}return(new lh(a,1<<(this.Ub>>>b&31),[null,this,null,null])).Kb(a,b,c,d,e,f)};
-h.Jb=function(a,b,c,d,e){return b===this.Ub?(a=oh(this.v,this.F,c),-1===a?(a=2*this.F,b=Array(a+2),qe(this.v,0,b,0,a),b[a]=c,b[a+1]=d,e.I=!0,new ph(null,this.Ub,this.F+1,b)):H.h(this.v[a],d)?this:new ph(null,this.Ub,this.F,fh(this.v,a+1,d))):(new lh(null,1<<(this.Ub>>>a&31),[null,this])).Jb(a,b,c,d,e)};h.pd=function(a,b,c){a=oh(this.v,this.F,c);return-1===a?this:1===this.F?null:new ph(null,this.Ub,this.F-1,gh(this.v,Je(a)))};h.qb=function(){return new kh(this.v,0,null,null)};
-var dh=function dh(b){for(var c=[],d=arguments.length,e=0;;)if(e<d)c.push(arguments[e]),e+=1;else break;switch(c.length){case 6:return dh.ra(arguments[0],arguments[1],arguments[2],arguments[3],arguments[4],arguments[5]);case 7:return dh.ta(arguments[0],arguments[1],arguments[2],arguments[3],arguments[4],arguments[5],arguments[6]);default:throw Error([y("Invalid arity: "),y(c.length)].join(""));}};
-dh.ra=function(a,b,c,d,e,f){var g=id(b);if(g===d)return new ph(null,g,2,[b,c,e,f]);var k=new ah;return mh.Jb(a,g,b,c,k).Jb(a,d,e,f,k)};dh.ta=function(a,b,c,d,e,f,g){var k=id(c);if(k===e)return new ph(null,k,2,[c,d,f,g]);var l=new ah;return mh.Kb(a,b,k,c,d,l).Kb(a,b,e,f,g,l)};dh.J=7;function qh(a,b,c,d,e){this.meta=a;this.ec=b;this.i=c;this.s=d;this.H=e;this.o=32374860;this.M=0}h=qh.prototype;h.toString=function(){return Xc(this)};h.equiv=function(a){return this.L(null,a)};h.Z=function(){return this.meta};
-h.W=function(){var a=this.H;return null!=a?a:this.H=a=wd(this)};h.L=function(a,b){return Ad(this,b)};h.qa=function(){return Bd(nd,this.meta)};h.za=function(a,b){return Sd.h(b,this)};h.Aa=function(a,b,c){return Sd.l(b,c,this)};h.wa=function(){return null==this.s?new V(null,2,5,W,[this.ec[this.i],this.ec[this.i+1]],null):C(this.s)};
-h.Da=function(){if(null==this.s){var a=this.ec,b=this.i+2;return bh.l?bh.l(a,b,null):bh.call(null,a,b,null)}var a=this.ec,b=this.i,c=D(this.s);return bh.l?bh.l(a,b,c):bh.call(null,a,b,c)};h.fa=function(){return this};h.ba=function(a,b){return new qh(b,this.ec,this.i,this.s,this.H)};h.ha=function(a,b){return Md(b,this)};qh.prototype[xb]=function(){return sd(this)};
-var bh=function bh(b){for(var c=[],d=arguments.length,e=0;;)if(e<d)c.push(arguments[e]),e+=1;else break;switch(c.length){case 1:return bh.j(arguments[0]);case 3:return bh.l(arguments[0],arguments[1],arguments[2]);default:throw Error([y("Invalid arity: "),y(c.length)].join(""));}};bh.j=function(a){return bh.l(a,0,null)};
-bh.l=function(a,b,c){if(null==c)for(c=a.length;;)if(b<c){if(null!=a[b])return new qh(null,a,b,null,null);var d=a[b+1];if(u(d)&&(d=d.od(),u(d)))return new qh(null,a,b+2,d,null);b+=2}else return null;else return new qh(null,a,b,c,null)};bh.J=3;function rh(a,b,c,d,e){this.meta=a;this.ec=b;this.i=c;this.s=d;this.H=e;this.o=32374860;this.M=0}h=rh.prototype;h.toString=function(){return Xc(this)};h.equiv=function(a){return this.L(null,a)};h.Z=function(){return this.meta};
-h.W=function(){var a=this.H;return null!=a?a:this.H=a=wd(this)};h.L=function(a,b){return Ad(this,b)};h.qa=function(){return Bd(nd,this.meta)};h.za=function(a,b){return Sd.h(b,this)};h.Aa=function(a,b,c){return Sd.l(b,c,this)};h.wa=function(){return C(this.s)};h.Da=function(){var a=this.ec,b=this.i,c=D(this.s);return ch.G?ch.G(null,a,b,c):ch.call(null,null,a,b,c)};h.fa=function(){return this};h.ba=function(a,b){return new rh(b,this.ec,this.i,this.s,this.H)};h.ha=function(a,b){return Md(b,this)};
-rh.prototype[xb]=function(){return sd(this)};var ch=function ch(b){for(var c=[],d=arguments.length,e=0;;)if(e<d)c.push(arguments[e]),e+=1;else break;switch(c.length){case 1:return ch.j(arguments[0]);case 4:return ch.G(arguments[0],arguments[1],arguments[2],arguments[3]);default:throw Error([y("Invalid arity: "),y(c.length)].join(""));}};ch.j=function(a){return ch.G(null,a,0,null)};
-ch.G=function(a,b,c,d){if(null==d)for(d=b.length;;)if(c<d){var e=b[c];if(u(e)&&(e=e.od(),u(e)))return new rh(a,b,c+1,e,null);c+=1}else return null;else return new rh(a,b,c,d,null)};ch.J=4;$g;function sh(a,b,c){this.Xa=a;this.zf=b;this.Pe=c}sh.prototype.Fa=function(){return this.Pe&&this.zf.Fa()};sh.prototype.next=function(){if(this.Pe)return this.zf.next();this.Pe=!0;return this.Xa};sh.prototype.remove=function(){return Error("Unsupported operation")};
-function $d(a,b,c,d,e,f){this.meta=a;this.F=b;this.root=c;this.Wa=d;this.Xa=e;this.H=f;this.o=16123663;this.M=8196}h=$d.prototype;h.toString=function(){return Xc(this)};h.equiv=function(a){return this.L(null,a)};h.keys=function(){return sd(Tg.j?Tg.j(this):Tg.call(null,this))};h.entries=function(){return Ng(K(this))};h.values=function(){return sd(Ug.j?Ug.j(this):Ug.call(null,this))};h.has=function(a){return ve(this,a)};h.get=function(a,b){return this.P(null,a,b)};
-h.forEach=function(a){for(var b=K(this),c=null,d=0,e=0;;)if(e<d){var f=c.aa(null,e),g=S(f,0,null),f=S(f,1,null);a.h?a.h(f,g):a.call(null,f,g);e+=1}else if(b=K(b))oe(b)?(c=Nc(b),b=Oc(b),g=c,d=R(c),c=g):(c=C(b),g=S(c,0,null),f=S(c,1,null),a.h?a.h(f,g):a.call(null,f,g),b=D(b),c=null,d=0),e=0;else return null};h.X=function(a,b){return Ub.l(this,b,null)};h.P=function(a,b,c){return null==b?this.Wa?this.Xa:c:null==this.root?c:this.root.dc(0,id(b),b,c)};
-h.Cc=function(a,b,c){a=this.Wa?b.l?b.l(c,null,this.Xa):b.call(null,c,null,this.Xa):c;return Ed(a)?Q.j?Q.j(a):Q.call(null,a):null!=this.root?this.root.sc(b,a):a};h.qb=function(){var a=this.root?Vc(this.root):mf;return this.Wa?new sh(this.Xa,a,!1):a};h.Z=function(){return this.meta};h.Za=function(){return new $d(this.meta,this.F,this.root,this.Wa,this.Xa,this.H)};h.ia=function(){return this.F};h.W=function(){var a=this.H;return null!=a?a:this.H=a=yd(this)};h.L=function(a,b){return Kg(this,b)};
-h.Bc=function(){return new $g({},this.root,this.F,this.Wa,this.Xa)};h.qa=function(){return mc(Xg,this.meta)};h.jc=function(a,b){if(null==b)return this.Wa?new $d(this.meta,this.F-1,this.root,!1,null,null):this;if(null==this.root)return this;var c=this.root.pd(0,id(b),b);return c===this.root?this:new $d(this.meta,this.F-1,c,this.Wa,this.Xa,null)};
-h.Gb=function(a,b,c){if(null==b)return this.Wa&&c===this.Xa?this:new $d(this.meta,this.Wa?this.F:this.F+1,this.root,!0,c,null);a=new ah;b=(null==this.root?mh:this.root).Jb(0,id(b),b,c,a);return b===this.root?this:new $d(this.meta,a.I?this.F+1:this.F,b,this.Wa,this.Xa,null)};h.Hd=function(a,b){return null==b?this.Wa:null==this.root?!1:this.root.dc(0,id(b),b,re)!==re};h.fa=function(){if(0<this.F){var a=null!=this.root?this.root.od():null;return this.Wa?Md(new V(null,2,5,W,[null,this.Xa],null),a):a}return null};
-h.ba=function(a,b){return new $d(b,this.F,this.root,this.Wa,this.Xa,this.H)};h.ha=function(a,b){if(le(b))return Wb(this,Lb.h(b,0),Lb.h(b,1));for(var c=this,d=K(b);;){if(null==d)return c;var e=C(d);if(le(e))c=Wb(c,Lb.h(e,0),Lb.h(e,1)),d=D(d);else throw Error("conj on a map takes map entries or seqables of map entries");}};
-h.call=function(){var a=null,a=function(a,c,d){switch(arguments.length){case 2:return this.X(null,c);case 3:return this.P(null,c,d)}throw Error("Invalid arity: "+arguments.length);};a.h=function(a,c){return this.X(null,c)};a.l=function(a,c,d){return this.P(null,c,d)};return a}();h.apply=function(a,b){return this.call.apply(this,[this].concat(yb(b)))};h.j=function(a){return this.X(null,a)};h.h=function(a,b){return this.P(null,a,b)};var Xg=new $d(null,0,null,!1,null,zd);
-function ae(a,b){for(var c=a.length,d=0,e=Fc(Xg);;)if(d<c)var f=d+1,e=e.bd(null,a[d],b[d]),d=f;else return Hc(e)}$d.prototype[xb]=function(){return sd(this)};function $g(a,b,c,d,e){this.oa=a;this.root=b;this.count=c;this.Wa=d;this.Xa=e;this.o=258;this.M=56}
-function th(a,b,c){if(a.oa){if(null==b)a.Xa!==c&&(a.Xa=c),a.Wa||(a.count+=1,a.Wa=!0);else{var d=new ah;b=(null==a.root?mh:a.root).Kb(a.oa,0,id(b),b,c,d);b!==a.root&&(a.root=b);d.I&&(a.count+=1)}return a}throw Error("assoc! after persistent!");}h=$g.prototype;h.ia=function(){if(this.oa)return this.count;throw Error("count after persistent!");};h.X=function(a,b){return null==b?this.Wa?this.Xa:null:null==this.root?null:this.root.dc(0,id(b),b)};
-h.P=function(a,b,c){return null==b?this.Wa?this.Xa:c:null==this.root?c:this.root.dc(0,id(b),b,c)};h.kc=function(a,b){var c;a:if(this.oa)if(null!=b?b.o&2048||b.Rf||(b.o?0:ub($b,b)):ub($b,b))c=th(this,Oe.j?Oe.j(b):Oe.call(null,b),Pe.j?Pe.j(b):Pe.call(null,b));else{c=K(b);for(var d=this;;){var e=C(c);if(u(e))c=D(c),d=th(d,Oe.j?Oe.j(e):Oe.call(null,e),Pe.j?Pe.j(e):Pe.call(null,e));else{c=d;break a}}}else throw Error("conj! after persistent");return c};
-h.Ec=function(){var a;if(this.oa)this.oa=null,a=new $d(null,this.count,this.root,this.Wa,this.Xa,null);else throw Error("persistent! called twice");return a};h.bd=function(a,b,c){return th(this,b,c)};function uh(a,b,c){for(var d=b;;)if(null!=a)b=c?a.left:a.right,d=Vd.h(d,a),a=b;else return d}function wh(a,b,c,d,e){this.meta=a;this.stack=b;this.zd=c;this.F=d;this.H=e;this.o=32374862;this.M=0}h=wh.prototype;h.toString=function(){return Xc(this)};h.equiv=function(a){return this.L(null,a)};h.Z=function(){return this.meta};
-h.ia=function(){return 0>this.F?R(D(this))+1:this.F};h.W=function(){var a=this.H;return null!=a?a:this.H=a=wd(this)};h.L=function(a,b){return Ad(this,b)};h.qa=function(){return Bd(nd,this.meta)};h.za=function(a,b){return Sd.h(b,this)};h.Aa=function(a,b,c){return Sd.l(b,c,this)};h.wa=function(){var a=this.stack;return null==a?null:ec(a)};h.Da=function(){var a=C(this.stack),a=uh(this.zd?a.right:a.left,D(this.stack),this.zd);return null!=a?new wh(null,a,this.zd,this.F-1,null):nd};h.fa=function(){return this};
-h.ba=function(a,b){return new wh(b,this.stack,this.zd,this.F,this.H)};h.ha=function(a,b){return Md(b,this)};wh.prototype[xb]=function(){return sd(this)};function xh(a,b,c){return new wh(null,uh(a,null,b),b,c,null)}yh;zh;
-function Ah(a,b,c,d){return c instanceof yh?c.left instanceof yh?new yh(c.key,c.I,c.left.Rb(),new zh(a,b,c.right,d,null),null):c.right instanceof yh?new yh(c.right.key,c.right.I,new zh(c.key,c.I,c.left,c.right.left,null),new zh(a,b,c.right.right,d,null),null):new zh(a,b,c,d,null):new zh(a,b,c,d,null)}
-function Bh(a,b,c,d){return d instanceof yh?d.right instanceof yh?new yh(d.key,d.I,new zh(a,b,c,d.left,null),d.right.Rb(),null):d.left instanceof yh?new yh(d.left.key,d.left.I,new zh(a,b,c,d.left.left,null),new zh(d.key,d.I,d.left.right,d.right,null),null):new zh(a,b,c,d,null):new zh(a,b,c,d,null)}
-function Ch(a,b,c,d){if(c instanceof yh)return new yh(a,b,c.Rb(),d,null);if(d instanceof zh)return Bh(a,b,c,d.vd());if(d instanceof yh&&d.left instanceof zh)return new yh(d.left.key,d.left.I,new zh(a,b,c,d.left.left,null),Bh(d.key,d.I,d.left.right,d.right.vd()),null);throw Error("red-black tree invariant violation");}
-var Dh=function Dh(b,c,d){d=null!=b.left?Dh(b.left,c,d):d;if(Ed(d))return Q.j?Q.j(d):Q.call(null,d);var e=b.key,f=b.I;d=c.l?c.l(d,e,f):c.call(null,d,e,f);if(Ed(d))return Q.j?Q.j(d):Q.call(null,d);b=null!=b.right?Dh(b.right,c,d):d;return Ed(b)?Q.j?Q.j(b):Q.call(null,b):b};function zh(a,b,c,d,e){this.key=a;this.I=b;this.left=c;this.right=d;this.H=e;this.o=32402207;this.M=0}h=zh.prototype;h.Se=function(a){return a.Ue(this)};h.vd=function(){return new yh(this.key,this.I,this.left,this.right,null)};
-h.Rb=function(){return this};h.Re=function(a){return a.Te(this)};h.replace=function(a,b,c,d){return new zh(a,b,c,d,null)};h.Te=function(a){return new zh(a.key,a.I,this,a.right,null)};h.Ue=function(a){return new zh(a.key,a.I,a.left,this,null)};h.sc=function(a,b){return Dh(this,a,b)};h.X=function(a,b){return Lb.l(this,b,null)};h.P=function(a,b,c){return Lb.l(this,b,c)};h.aa=function(a,b){return 0===b?this.key:1===b?this.I:null};h.kb=function(a,b,c){return 0===b?this.key:1===b?this.I:c};
-h.lc=function(a,b,c){return(new V(null,2,5,W,[this.key,this.I],null)).lc(null,b,c)};h.Z=function(){return null};h.ia=function(){return 2};h.Yc=function(){return this.key};h.Zc=function(){return this.I};h.ac=function(){return this.I};h.bc=function(){return new V(null,1,5,W,[this.key],null)};h.W=function(){var a=this.H;return null!=a?a:this.H=a=wd(this)};h.L=function(a,b){return Ad(this,b)};h.qa=function(){return Wd};h.za=function(a,b){return Fd(this,b)};h.Aa=function(a,b,c){return Gd(this,b,c)};
-h.Gb=function(a,b,c){return T.l(new V(null,2,5,W,[this.key,this.I],null),b,c)};h.fa=function(){return Jb(Jb(nd,this.I),this.key)};h.ba=function(a,b){return Bd(new V(null,2,5,W,[this.key,this.I],null),b)};h.ha=function(a,b){return new V(null,3,5,W,[this.key,this.I,b],null)};
-h.call=function(){var a=null,a=function(a,c,d){switch(arguments.length){case 2:return this.X(null,c);case 3:return this.P(null,c,d)}throw Error("Invalid arity: "+arguments.length);};a.h=function(a,c){return this.X(null,c)};a.l=function(a,c,d){return this.P(null,c,d)};return a}();h.apply=function(a,b){return this.call.apply(this,[this].concat(yb(b)))};h.j=function(a){return this.X(null,a)};h.h=function(a,b){return this.P(null,a,b)};zh.prototype[xb]=function(){return sd(this)};
-function yh(a,b,c,d,e){this.key=a;this.I=b;this.left=c;this.right=d;this.H=e;this.o=32402207;this.M=0}h=yh.prototype;h.Se=function(a){return new yh(this.key,this.I,this.left,a,null)};h.vd=function(){throw Error("red-black tree invariant violation");};h.Rb=function(){return new zh(this.key,this.I,this.left,this.right,null)};h.Re=function(a){return new yh(this.key,this.I,a,this.right,null)};h.replace=function(a,b,c,d){return new yh(a,b,c,d,null)};
-h.Te=function(a){return this.left instanceof yh?new yh(this.key,this.I,this.left.Rb(),new zh(a.key,a.I,this.right,a.right,null),null):this.right instanceof yh?new yh(this.right.key,this.right.I,new zh(this.key,this.I,this.left,this.right.left,null),new zh(a.key,a.I,this.right.right,a.right,null),null):new zh(a.key,a.I,this,a.right,null)};
-h.Ue=function(a){return this.right instanceof yh?new yh(this.key,this.I,new zh(a.key,a.I,a.left,this.left,null),this.right.Rb(),null):this.left instanceof yh?new yh(this.left.key,this.left.I,new zh(a.key,a.I,a.left,this.left.left,null),new zh(this.key,this.I,this.left.right,this.right,null),null):new zh(a.key,a.I,a.left,this,null)};h.sc=function(a,b){return Dh(this,a,b)};h.X=function(a,b){return Lb.l(this,b,null)};h.P=function(a,b,c){return Lb.l(this,b,c)};
-h.aa=function(a,b){return 0===b?this.key:1===b?this.I:null};h.kb=function(a,b,c){return 0===b?this.key:1===b?this.I:c};h.lc=function(a,b,c){return(new V(null,2,5,W,[this.key,this.I],null)).lc(null,b,c)};h.Z=function(){return null};h.ia=function(){return 2};h.Yc=function(){return this.key};h.Zc=function(){return this.I};h.ac=function(){return this.I};h.bc=function(){return new V(null,1,5,W,[this.key],null)};h.W=function(){var a=this.H;return null!=a?a:this.H=a=wd(this)};
-h.L=function(a,b){return Ad(this,b)};h.qa=function(){return Wd};h.za=function(a,b){return Fd(this,b)};h.Aa=function(a,b,c){return Gd(this,b,c)};h.Gb=function(a,b,c){return T.l(new V(null,2,5,W,[this.key,this.I],null),b,c)};h.fa=function(){return Jb(Jb(nd,this.I),this.key)};h.ba=function(a,b){return Bd(new V(null,2,5,W,[this.key,this.I],null),b)};h.ha=function(a,b){return new V(null,3,5,W,[this.key,this.I,b],null)};
-h.call=function(){var a=null,a=function(a,c,d){switch(arguments.length){case 2:return this.X(null,c);case 3:return this.P(null,c,d)}throw Error("Invalid arity: "+arguments.length);};a.h=function(a,c){return this.X(null,c)};a.l=function(a,c,d){return this.P(null,c,d)};return a}();h.apply=function(a,b){return this.call.apply(this,[this].concat(yb(b)))};h.j=function(a){return this.X(null,a)};h.h=function(a,b){return this.P(null,a,b)};yh.prototype[xb]=function(){return sd(this)};
-var Eh=function Eh(b,c,d,e,f){if(null==c)return new yh(d,e,null,null,null);var g;g=c.key;g=b.h?b.h(d,g):b.call(null,d,g);if(0===g)return f[0]=c,null;if(0>g)return b=Eh(b,c.left,d,e,f),null!=b?c.Re(b):null;b=Eh(b,c.right,d,e,f);return null!=b?c.Se(b):null},Fh=function Fh(b,c){if(null==b)return c;if(null==c)return b;if(b instanceof yh){if(c instanceof yh){var d=Fh(b.right,c.left);return d instanceof yh?new yh(d.key,d.I,new yh(b.key,b.I,b.left,d.left,null),new yh(c.key,c.I,d.right,c.right,null),null):
-new yh(b.key,b.I,b.left,new yh(c.key,c.I,d,c.right,null),null)}return new yh(b.key,b.I,b.left,Fh(b.right,c),null)}if(c instanceof yh)return new yh(c.key,c.I,Fh(b,c.left),c.right,null);d=Fh(b.right,c.left);return d instanceof yh?new yh(d.key,d.I,new zh(b.key,b.I,b.left,d.left,null),new zh(c.key,c.I,d.right,c.right,null),null):Ch(b.key,b.I,b.left,new zh(c.key,c.I,d,c.right,null))},Gh=function Gh(b,c,d,e){if(null!=c){var f;f=c.key;f=b.h?b.h(d,f):b.call(null,d,f);if(0===f)return e[0]=c,Fh(c.left,c.right);
-if(0>f)return b=Gh(b,c.left,d,e),null!=b||null!=e[0]?c.left instanceof zh?Ch(c.key,c.I,b,c.right):new yh(c.key,c.I,b,c.right,null):null;b=Gh(b,c.right,d,e);if(null!=b||null!=e[0])if(c.right instanceof zh)if(e=c.key,d=c.I,c=c.left,b instanceof yh)c=new yh(e,d,c,b.Rb(),null);else if(c instanceof zh)c=Ah(e,d,c.vd(),b);else if(c instanceof yh&&c.right instanceof zh)c=new yh(c.right.key,c.right.I,Ah(c.key,c.I,c.left.vd(),c.right.left),new zh(e,d,c.right.right,b,null),null);else throw Error("red-black tree invariant violation");
-else c=new yh(c.key,c.I,c.left,b,null);else c=null;return c}return null},Hh=function Hh(b,c,d,e){var f=c.key,g=b.h?b.h(d,f):b.call(null,d,f);return 0===g?c.replace(f,e,c.left,c.right):0>g?c.replace(f,c.I,Hh(b,c.left,d,e),c.right):c.replace(f,c.I,c.left,Hh(b,c.right,d,e))};Oe;function Ih(a,b,c,d,e){this.xb=a;this.Pb=b;this.F=c;this.meta=d;this.H=e;this.o=418776847;this.M=8192}h=Ih.prototype;
-h.forEach=function(a){for(var b=K(this),c=null,d=0,e=0;;)if(e<d){var f=c.aa(null,e),g=S(f,0,null),f=S(f,1,null);a.h?a.h(f,g):a.call(null,f,g);e+=1}else if(b=K(b))oe(b)?(c=Nc(b),b=Oc(b),g=c,d=R(c),c=g):(c=C(b),g=S(c,0,null),f=S(c,1,null),a.h?a.h(f,g):a.call(null,f,g),b=D(b),c=null,d=0),e=0;else return null};h.get=function(a,b){return this.P(null,a,b)};h.entries=function(){return Ng(K(this))};h.toString=function(){return Xc(this)};h.keys=function(){return sd(Tg.j?Tg.j(this):Tg.call(null,this))};
-h.values=function(){return sd(Ug.j?Ug.j(this):Ug.call(null,this))};h.equiv=function(a){return this.L(null,a)};function Jh(a,b){for(var c=a.Pb;;)if(null!=c){var d;d=c.key;d=a.xb.h?a.xb.h(b,d):a.xb.call(null,b,d);if(0===d)return c;c=0>d?c.left:c.right}else return null}h.has=function(a){return ve(this,a)};h.X=function(a,b){return Ub.l(this,b,null)};h.P=function(a,b,c){a=Jh(this,b);return null!=a?a.I:c};h.Cc=function(a,b,c){return null!=this.Pb?Dh(this.Pb,b,c):c};h.Z=function(){return this.meta};
-h.Za=function(){return new Ih(this.xb,this.Pb,this.F,this.meta,this.H)};h.ia=function(){return this.F};h.Dc=function(){return 0<this.F?xh(this.Pb,!1,this.F):null};h.W=function(){var a=this.H;return null!=a?a:this.H=a=yd(this)};h.L=function(a,b){return Kg(this,b)};h.qa=function(){return new Ih(this.xb,null,0,this.meta,0)};h.jc=function(a,b){var c=[null],d=Gh(this.xb,this.Pb,b,c);return null==d?null==Zd(c,0)?this:new Ih(this.xb,null,0,this.meta,null):new Ih(this.xb,d.Rb(),this.F-1,this.meta,null)};
-h.Gb=function(a,b,c){a=[null];var d=Eh(this.xb,this.Pb,b,c,a);return null==d?(a=Zd(a,0),H.h(c,a.I)?this:new Ih(this.xb,Hh(this.xb,this.Pb,b,c),this.F,this.meta,null)):new Ih(this.xb,d.Rb(),this.F+1,this.meta,null)};h.Hd=function(a,b){return null!=Jh(this,b)};h.fa=function(){return 0<this.F?xh(this.Pb,!0,this.F):null};h.ba=function(a,b){return new Ih(this.xb,this.Pb,this.F,b,this.H)};
-h.ha=function(a,b){if(le(b))return Wb(this,Lb.h(b,0),Lb.h(b,1));for(var c=this,d=K(b);;){if(null==d)return c;var e=C(d);if(le(e))c=Wb(c,Lb.h(e,0),Lb.h(e,1)),d=D(d);else throw Error("conj on a map takes map entries or seqables of map entries");}};
-h.call=function(){var a=null,a=function(a,c,d){switch(arguments.length){case 2:return this.X(null,c);case 3:return this.P(null,c,d)}throw Error("Invalid arity: "+arguments.length);};a.h=function(a,c){return this.X(null,c)};a.l=function(a,c,d){return this.P(null,c,d)};return a}();h.apply=function(a,b){return this.call.apply(this,[this].concat(yb(b)))};h.j=function(a){return this.X(null,a)};h.h=function(a,b){return this.P(null,a,b)};var Kh=new Ih(ed,null,0,null,zd);Ih.prototype[xb]=function(){return sd(this)};
-var P=function P(b){for(var c=[],d=arguments.length,e=0;;)if(e<d)c.push(arguments[e]),e+=1;else break;return P.w(0<c.length?new B(c.slice(0),0):null)};P.w=function(a){for(var b=K(a),c=Fc(Xg);;)if(b){a=D(D(b));var d=C(b),b=Td(b),c=Ic(c,d,b),b=a}else return Hc(c)};P.J=0;P.K=function(a){return P.w(K(a))};var Lh=function Lh(b){for(var c=[],d=arguments.length,e=0;;)if(e<d)c.push(arguments[e]),e+=1;else break;return Lh.w(0<c.length?new B(c.slice(0),0):null)};
-Lh.w=function(a){a=a instanceof B&&0===a.i?a.v:nb.j(a);return Yg(a,!0,!1)};Lh.J=0;Lh.K=function(a){return Lh.w(K(a))};function Mh(a){for(var b=[],c=arguments.length,d=0;;)if(d<c)b.push(arguments[d]),d+=1;else break;a:for(b=K(0<b.length?new B(b.slice(0),0):null),d=Kh;;)if(b)c=D(D(b)),d=T.l(d,C(b),Td(b)),b=c;else break a;return d}function Nh(a,b){this.ca=a;this.jb=b;this.o=32374988;this.M=0}h=Nh.prototype;h.toString=function(){return Xc(this)};h.equiv=function(a){return this.L(null,a)};h.Z=function(){return this.jb};
-h.$a=function(){var a=(null!=this.ca?this.ca.o&128||this.ca.Id||(this.ca.o?0:ub(Sb,this.ca)):ub(Sb,this.ca))?this.ca.$a(null):D(this.ca);return null==a?null:new Nh(a,this.jb)};h.W=function(){return wd(this)};h.L=function(a,b){return Ad(this,b)};h.qa=function(){return Bd(nd,this.jb)};h.za=function(a,b){return Sd.h(b,this)};h.Aa=function(a,b,c){return Sd.l(b,c,this)};h.wa=function(){return this.ca.wa(null).Yc(null)};
-h.Da=function(){var a=(null!=this.ca?this.ca.o&128||this.ca.Id||(this.ca.o?0:ub(Sb,this.ca)):ub(Sb,this.ca))?this.ca.$a(null):D(this.ca);return null!=a?new Nh(a,this.jb):nd};h.fa=function(){return this};h.ba=function(a,b){return new Nh(this.ca,b)};h.ha=function(a,b){return Md(b,this)};Nh.prototype[xb]=function(){return sd(this)};function Tg(a){return(a=K(a))?new Nh(a,null):null}function Oe(a){return ac(a)}function Oh(a,b){this.ca=a;this.jb=b;this.o=32374988;this.M=0}h=Oh.prototype;h.toString=function(){return Xc(this)};
-h.equiv=function(a){return this.L(null,a)};h.Z=function(){return this.jb};h.$a=function(){var a=(null!=this.ca?this.ca.o&128||this.ca.Id||(this.ca.o?0:ub(Sb,this.ca)):ub(Sb,this.ca))?this.ca.$a(null):D(this.ca);return null==a?null:new Oh(a,this.jb)};h.W=function(){return wd(this)};h.L=function(a,b){return Ad(this,b)};h.qa=function(){return Bd(nd,this.jb)};h.za=function(a,b){return Sd.h(b,this)};h.Aa=function(a,b,c){return Sd.l(b,c,this)};h.wa=function(){return this.ca.wa(null).Zc(null)};
-h.Da=function(){var a=(null!=this.ca?this.ca.o&128||this.ca.Id||(this.ca.o?0:ub(Sb,this.ca)):ub(Sb,this.ca))?this.ca.$a(null):D(this.ca);return null!=a?new Oh(a,this.jb):nd};h.fa=function(){return this};h.ba=function(a,b){return new Oh(this.ca,b)};h.ha=function(a,b){return Md(b,this)};Oh.prototype[xb]=function(){return sd(this)};function Ug(a){return(a=K(a))?new Oh(a,null):null}function Pe(a){return bc(a)}
-var Ph=function Ph(b){for(var c=[],d=arguments.length,e=0;;)if(e<d)c.push(arguments[e]),e+=1;else break;return Ph.w(0<c.length?new B(c.slice(0),0):null)};Ph.w=function(a){return u(uf(Be,a))?Ab.h(function(a,c){return Vd.h(u(a)?a:rf,c)},a):null};Ph.J=0;Ph.K=function(a){return Ph.w(K(a))};var Qh=function Qh(b){for(var c=[],d=arguments.length,e=0;;)if(e<d)c.push(arguments[e]),e+=1;else break;return Qh.w(arguments[0],1<c.length?new B(c.slice(1),0):null)};
-Qh.w=function(a,b){return u(uf(Be,b))?Ab.h(function(a){return function(b,e){return Ab.l(a,u(b)?b:rf,K(e))}}(function(b,d){var e=C(d),f=Td(d);return ve(b,e)?T.l(b,e,function(){var d=I.h(b,e);return a.h?a.h(d,f):a.call(null,d,f)}()):T.l(b,e,f)}),b):null};Qh.J=1;Qh.K=function(a){var b=C(a);a=D(a);return Qh.w(b,a)};function Rh(a,b){for(var c=rf,d=K(b);;)if(d)var e=C(d),f=I.l(a,e,Sh),c=H.h(f,Sh)?c:T.l(c,e,f),d=D(d);else return Bd(c,ee(a))}Th;function Uh(a){this.Kc=a}Uh.prototype.Fa=function(){return this.Kc.Fa()};
-Uh.prototype.next=function(){if(this.Kc.Fa())return this.Kc.next().ga[0];throw Error("No such element");};Uh.prototype.remove=function(){return Error("Unsupported operation")};function Vh(a,b,c){this.meta=a;this.Wb=b;this.H=c;this.o=15077647;this.M=8196}h=Vh.prototype;h.toString=function(){return Xc(this)};h.equiv=function(a){return this.L(null,a)};h.keys=function(){return sd(K(this))};h.entries=function(){return Pg(K(this))};h.values=function(){return sd(K(this))};
-h.has=function(a){return ve(this,a)};h.forEach=function(a){for(var b=K(this),c=null,d=0,e=0;;)if(e<d){var f=c.aa(null,e),g=S(f,0,null),f=S(f,1,null);a.h?a.h(f,g):a.call(null,f,g);e+=1}else if(b=K(b))oe(b)?(c=Nc(b),b=Oc(b),g=c,d=R(c),c=g):(c=C(b),g=S(c,0,null),f=S(c,1,null),a.h?a.h(f,g):a.call(null,f,g),b=D(b),c=null,d=0),e=0;else return null};h.X=function(a,b){return Ub.l(this,b,null)};h.P=function(a,b,c){return Vb(this.Wb,b)?b:c};h.qb=function(){return new Uh(Vc(this.Wb))};h.Z=function(){return this.meta};
-h.Za=function(){return new Vh(this.meta,this.Wb,this.H)};h.ia=function(){return Gb(this.Wb)};h.W=function(){var a=this.H;return null!=a?a:this.H=a=yd(this)};h.L=function(a,b){return ie(b)&&R(this)===R(b)&&tf(function(a){return function(b){return ve(a,b)}}(this),b)};h.Bc=function(){return new Th(Fc(this.Wb))};h.qa=function(){return Bd(Wh,this.meta)};h.xe=function(a,b){return new Vh(this.meta,Zb(this.Wb,b),null)};h.fa=function(){return Tg(this.Wb)};h.ba=function(a,b){return new Vh(b,this.Wb,this.H)};
-h.ha=function(a,b){return new Vh(this.meta,T.l(this.Wb,b,null),null)};h.call=function(){var a=null,a=function(a,c,d){switch(arguments.length){case 2:return this.X(null,c);case 3:return this.P(null,c,d)}throw Error("Invalid arity: "+arguments.length);};a.h=function(a,c){return this.X(null,c)};a.l=function(a,c,d){return this.P(null,c,d)};return a}();h.apply=function(a,b){return this.call.apply(this,[this].concat(yb(b)))};h.j=function(a){return this.X(null,a)};
-h.h=function(a,b){return this.P(null,a,b)};var Wh=new Vh(null,rf,zd);Vh.prototype[xb]=function(){return sd(this)};function Th(a){this.Xb=a;this.M=136;this.o=259}h=Th.prototype;h.kc=function(a,b){this.Xb=Ic(this.Xb,b,null);return this};h.Ec=function(){return new Vh(null,Hc(this.Xb),null)};h.ia=function(){return R(this.Xb)};h.X=function(a,b){return Ub.l(this,b,null)};h.P=function(a,b,c){return Ub.l(this.Xb,b,re)===re?c:b};
-h.call=function(){function a(a,b,c){return Ub.l(this.Xb,b,re)===re?c:b}function b(a,b){return Ub.l(this.Xb,b,re)===re?null:b}var c=null,c=function(c,e,f){switch(arguments.length){case 2:return b.call(this,c,e);case 3:return a.call(this,c,e,f)}throw Error("Invalid arity: "+arguments.length);};c.h=b;c.l=a;return c}();h.apply=function(a,b){return this.call.apply(this,[this].concat(yb(b)))};h.j=function(a){return Ub.l(this.Xb,a,re)===re?null:a};h.h=function(a,b){return Ub.l(this.Xb,a,re)===re?b:a};
-function Xh(a,b,c){this.meta=a;this.Qb=b;this.H=c;this.o=417730831;this.M=8192}h=Xh.prototype;h.toString=function(){return Xc(this)};h.equiv=function(a){return this.L(null,a)};h.keys=function(){return sd(K(this))};h.entries=function(){return Pg(K(this))};h.values=function(){return sd(K(this))};h.has=function(a){return ve(this,a)};
-h.forEach=function(a){for(var b=K(this),c=null,d=0,e=0;;)if(e<d){var f=c.aa(null,e),g=S(f,0,null),f=S(f,1,null);a.h?a.h(f,g):a.call(null,f,g);e+=1}else if(b=K(b))oe(b)?(c=Nc(b),b=Oc(b),g=c,d=R(c),c=g):(c=C(b),g=S(c,0,null),f=S(c,1,null),a.h?a.h(f,g):a.call(null,f,g),b=D(b),c=null,d=0),e=0;else return null};h.X=function(a,b){return Ub.l(this,b,null)};h.P=function(a,b,c){a=Jh(this.Qb,b);return null!=a?a.key:c};h.Z=function(){return this.meta};h.Za=function(){return new Xh(this.meta,this.Qb,this.H)};
-h.ia=function(){return R(this.Qb)};h.Dc=function(){return 0<R(this.Qb)?Me.h(Oe,zc(this.Qb)):null};h.W=function(){var a=this.H;return null!=a?a:this.H=a=yd(this)};h.L=function(a,b){return ie(b)&&R(this)===R(b)&&tf(function(a){return function(b){return ve(a,b)}}(this),b)};h.qa=function(){return new Xh(this.meta,Hb(this.Qb),0)};h.xe=function(a,b){return new Xh(this.meta,be.h(this.Qb,b),null)};h.fa=function(){return Tg(this.Qb)};h.ba=function(a,b){return new Xh(b,this.Qb,this.H)};
-h.ha=function(a,b){return new Xh(this.meta,T.l(this.Qb,b,null),null)};h.call=function(){var a=null,a=function(a,c,d){switch(arguments.length){case 2:return this.X(null,c);case 3:return this.P(null,c,d)}throw Error("Invalid arity: "+arguments.length);};a.h=function(a,c){return this.X(null,c)};a.l=function(a,c,d){return this.P(null,c,d)};return a}();h.apply=function(a,b){return this.call.apply(this,[this].concat(yb(b)))};h.j=function(a){return this.X(null,a)};
-h.h=function(a,b){return this.P(null,a,b)};var Yh=new Xh(null,Kh,zd);Xh.prototype[xb]=function(){return sd(this)};function Zh(a){a=K(a);if(null==a)return Wh;if(a instanceof B&&0===a.i){a=a.v;a:for(var b=0,c=Fc(Wh);;)if(b<a.length)var d=b+1,c=c.kc(null,a[b]),b=d;else break a;return c.Ec(null)}for(d=Fc(Wh);;)if(null!=a)b=D(a),d=d.kc(null,a.wa(null)),a=b;else return Hc(d)}
-var $h=function $h(b){for(var c=[],d=arguments.length,e=0;;)if(e<d)c.push(arguments[e]),e+=1;else break;return $h.w(0<c.length?new B(c.slice(0),0):null)};$h.w=function(a){return Ab.l(Jb,Yh,a)};$h.J=0;$h.K=function(a){return $h.w(K(a))};function Ne(a){if(null!=a&&(a.M&4096||a.af))return a.$c(null);if("string"===typeof a)return a;throw Error([y("Doesn't support name: "),y(a)].join(""));}
-function ai(a,b){return new Ze(null,function(){var c=K(b);if(c){var d;d=C(c);d=a.j?a.j(d):a.call(null,d);c=u(d)?Md(C(c),ai(a,N(c))):null}else c=null;return c},null,null)}function bi(a,b,c){this.i=a;this.end=b;this.step=c}bi.prototype.Fa=function(){return 0<this.step?this.i<this.end:this.i>this.end};bi.prototype.next=function(){var a=this.i;this.i+=this.step;return a};function ci(a,b,c,d,e){this.meta=a;this.start=b;this.end=c;this.step=d;this.H=e;this.o=32375006;this.M=8192}h=ci.prototype;
-h.toString=function(){return Xc(this)};h.equiv=function(a){return this.L(null,a)};h.aa=function(a,b){if(b<Gb(this))return this.start+b*this.step;if(this.start>this.end&&0===this.step)return this.start;throw Error("Index out of bounds");};h.kb=function(a,b,c){return b<Gb(this)?this.start+b*this.step:this.start>this.end&&0===this.step?this.start:c};h.qb=function(){return new bi(this.start,this.end,this.step)};h.Z=function(){return this.meta};
-h.Za=function(){return new ci(this.meta,this.start,this.end,this.step,this.H)};h.$a=function(){return 0<this.step?this.start+this.step<this.end?new ci(this.meta,this.start+this.step,this.end,this.step,null):null:this.start+this.step>this.end?new ci(this.meta,this.start+this.step,this.end,this.step,null):null};h.ia=function(){return tb(uc(this))?0:Math.ceil((this.end-this.start)/this.step)};h.W=function(){var a=this.H;return null!=a?a:this.H=a=wd(this)};h.L=function(a,b){return Ad(this,b)};
-h.qa=function(){return Bd(nd,this.meta)};h.za=function(a,b){return Fd(this,b)};h.Aa=function(a,b,c){for(a=this.start;;)if(0<this.step?a<this.end:a>this.end){c=b.h?b.h(c,a):b.call(null,c,a);if(Ed(c))return Q.j?Q.j(c):Q.call(null,c);a+=this.step}else return c};h.wa=function(){return null==uc(this)?null:this.start};h.Da=function(){return null!=uc(this)?new ci(this.meta,this.start+this.step,this.end,this.step,null):nd};
-h.fa=function(){return 0<this.step?this.start<this.end?this:null:0>this.step?this.start>this.end?this:null:this.start===this.end?null:this};h.ba=function(a,b){return new ci(b,this.start,this.end,this.step,this.H)};h.ha=function(a,b){return Md(b,this)};ci.prototype[xb]=function(){return sd(this)};function di(a){return new ci(null,0,a,1,null)}
-function ei(a,b,c){return Md(b,new Ze(null,function(){var d=K(c);if(d){var e=ei,f;f=C(d);f=a.h?a.h(b,f):a.call(null,b,f);d=e(a,f,N(d))}else d=null;return d},null,null))}function fi(a){a:for(var b=a;;)if(K(b))b=D(b);else break a;return a}function gi(a,b){if("string"===typeof b){var c=a.exec(b);return H.h(C(c),b)?1===R(c)?C(c):ze(c):null}throw new TypeError("re-matches must match against a string.");}
-function hi(a){if(a instanceof RegExp)return a;var b;var c=/^\(\?([idmsux]*)\)/;if("string"===typeof a)c=c.exec(a),b=null==c?null:1===R(c)?C(c):ze(c);else throw new TypeError("re-find must match against a string.");c=S(b,0,null);b=S(b,1,null);c=R(c);return new RegExp(a.substring(c),u(b)?b:"")}
-function ug(a,b,c,d,e,f,g){var k=cb;cb=null==cb?null:cb-1;try{if(null!=cb&&0>cb)return Ac(a,"#");Ac(a,c);if(0===mb.j(f))K(g)&&Ac(a,function(){var a=ii.j(f);return u(a)?a:"..."}());else{if(K(g)){var l=C(g);b.l?b.l(l,a,f):b.call(null,l,a,f)}for(var n=D(g),m=mb.j(f)-1;;)if(!n||null!=m&&0===m){K(n)&&0===m&&(Ac(a,d),Ac(a,function(){var a=ii.j(f);return u(a)?a:"..."}()));break}else{Ac(a,d);var t=C(n);c=a;g=f;b.l?b.l(t,c,g):b.call(null,t,c,g);var q=D(n);c=m-1;n=q;m=c}}return Ac(a,e)}finally{cb=k}}
-function ji(a,b){for(var c=K(b),d=null,e=0,f=0;;)if(f<e){var g=d.aa(null,f);Ac(a,g);f+=1}else if(c=K(c))d=c,oe(d)?(c=Nc(d),e=Oc(d),d=c,g=R(c),c=e,e=g):(g=C(d),Ac(a,g),c=D(d),d=null,e=0),f=0;else return null}function ki(a){Za.j?Za.j(a):Za.call(null,a);return null}var li={'"':'\\"',"\\":"\\\\","\b":"\\b","\f":"\\f","\n":"\\n","\r":"\\r","\t":"\\t"};function mi(a){return[y('"'),y(a.replace(RegExp('[\\\\"\b\f\n\r\t]',"g"),function(a){return li[a]})),y('"')].join("")}ni;
-function oi(a,b){var c=te(I.h(a,jb));return c?(c=null!=b?b.o&131072||b.Sf?!0:!1:!1)?null!=ee(b):c:c}
-function pi(a,b,c){if(null==a)return Ac(b,"nil");if(oi(c,a)){Ac(b,"^");var d=ee(a);vg.l?vg.l(d,b,c):vg.call(null,d,b,c);Ac(b," ")}if(a.mc)return a.Fc(a,b,c);if(null!=a&&(a.o&2147483648||a.ja))return a.T(null,b,c);if(!0===a||!1===a||"number"===typeof a)return Ac(b,""+y(a));if(null!=a&&a.constructor===Object)return Ac(b,"#js "),d=Me.h(function(b){return new V(null,2,5,W,[Ye.j(b),a[b]],null)},pe(a)),ni.G?ni.G(d,vg,b,c):ni.call(null,d,vg,b,c);if(rb(a))return ug(b,vg,"#js ["," ","]",c,a);if(ha(a))return u(ib.j(c))?
-Ac(b,mi(a)):Ac(b,a);if(ia(a)){var e=a.name;c=u(function(){var a=null==e;return a?a:/^[\s\xa0]*$/.test(e)}())?"Function":e;return ji(b,J(["#object[",c,' "',""+y(a),'"]'],0))}if(a instanceof Date)return c=function(a,b){for(var c=""+y(a);;)if(R(c)<b)c=[y("0"),y(c)].join("");else return c},ji(b,J(['#inst "',""+y(a.getUTCFullYear()),"-",c(a.getUTCMonth()+1,2),"-",c(a.getUTCDate(),2),"T",c(a.getUTCHours(),2),":",c(a.getUTCMinutes(),2),":",c(a.getUTCSeconds(),2),".",c(a.getUTCMilliseconds(),3),"-",'00:00"'],
-0));if(a instanceof RegExp)return ji(b,J(['#"',a.source,'"'],0));if(null!=a&&(a.o&2147483648||a.ja))return Bc(a,b,c);if(u(a.constructor.Tb))return ji(b,J(["#object[",a.constructor.Tb.replace(RegExp("/","g"),"."),"]"],0));e=a.constructor.name;c=u(function(){var a=null==e;return a?a:/^[\s\xa0]*$/.test(e)}())?"Object":e;return ji(b,J(["#object[",c," ",""+y(a),"]"],0))}function vg(a,b,c){var d=qi.j(c);return u(d)?(c=T.l(c,ri,pi),d.l?d.l(a,b,c):d.call(null,a,b,c)):pi(a,b,c)}
-function si(a,b){var c;if(ge(a))c="";else{c=y;var d=new Ga;a:{var e=new Wc(d);vg(C(a),e,b);for(var f=K(D(a)),g=null,k=0,l=0;;)if(l<k){var n=g.aa(null,l);Ac(e," ");vg(n,e,b);l+=1}else if(f=K(f))g=f,oe(g)?(f=Nc(g),k=Oc(g),g=f,n=R(f),f=k,k=n):(n=C(g),Ac(e," "),vg(n,e,b),f=D(g),g=null,k=0),l=0;else break a}c=""+c(d)}return c}function ti(a){var b=T.l(gb(),ib,!1);return ki(si(a,b))}
-var Df=function Df(b){for(var c=[],d=arguments.length,e=0;;)if(e<d)c.push(arguments[e]),e+=1;else break;return Df.w(0<c.length?new B(c.slice(0),0):null)};Df.w=function(a){return si(a,gb())};Df.J=0;Df.K=function(a){return Df.w(K(a))};var ui=function(){function a(a){var c=null;if(0<arguments.length){for(var c=0,d=Array(arguments.length-0);c<d.length;)d[c]=arguments[c+0],++c;c=new B(d,0)}return ti(c)}a.J=0;a.K=function(a){a=K(a);return ti(a)};a.w=function(a){return ti(a)};return a}();
-function ni(a,b,c,d){return ug(c,function(a,c,d){var k=ac(a);b.l?b.l(k,c,d):b.call(null,k,c,d);Ac(c," ");a=bc(a);return b.l?b.l(a,c,d):b.call(null,a,c,d)},"{",", ","}",d,K(a))}Jf.prototype.ja=!0;Jf.prototype.T=function(a,b,c){Ac(b,"#object [cljs.core.Volatile ");vg(new r(null,1,[vi,this.state],null),b,c);return Ac(b,"]")};B.prototype.ja=!0;B.prototype.T=function(a,b,c){return ug(b,vg,"("," ",")",c,this)};Ze.prototype.ja=!0;Ze.prototype.T=function(a,b,c){return ug(b,vg,"("," ",")",c,this)};
-wh.prototype.ja=!0;wh.prototype.T=function(a,b,c){return ug(b,vg,"("," ",")",c,this)};qh.prototype.ja=!0;qh.prototype.T=function(a,b,c){return ug(b,vg,"("," ",")",c,this)};zh.prototype.ja=!0;zh.prototype.T=function(a,b,c){return ug(b,vg,"["," ","]",c,this)};Sg.prototype.ja=!0;Sg.prototype.T=function(a,b,c){return ug(b,vg,"("," ",")",c,this)};ud.prototype.ja=!0;ud.prototype.T=function(a,b,c){return ug(b,vg,"("," ",")",c,this)};Xh.prototype.ja=!0;
-Xh.prototype.T=function(a,b,c){return ug(b,vg,"#{"," ","}",c,this)};ne.prototype.ja=!0;ne.prototype.T=function(a,b,c){return ug(b,vg,"("," ",")",c,this)};Ve.prototype.ja=!0;Ve.prototype.T=function(a,b,c){return ug(b,vg,"("," ",")",c,this)};Nd.prototype.ja=!0;Nd.prototype.T=function(a,b,c){return ug(b,vg,"("," ",")",c,this)};$d.prototype.ja=!0;$d.prototype.T=function(a,b,c){return ni(this,vg,b,c)};rh.prototype.ja=!0;rh.prototype.T=function(a,b,c){return ug(b,vg,"("," ",")",c,this)};
-Bg.prototype.ja=!0;Bg.prototype.T=function(a,b,c){return ug(b,vg,"["," ","]",c,this)};Ih.prototype.ja=!0;Ih.prototype.T=function(a,b,c){return ni(this,vg,b,c)};Vh.prototype.ja=!0;Vh.prototype.T=function(a,b,c){return ug(b,vg,"#{"," ","}",c,this)};me.prototype.ja=!0;me.prototype.T=function(a,b,c){return ug(b,vg,"("," ",")",c,this)};Bf.prototype.ja=!0;Bf.prototype.T=function(a,b,c){Ac(b,"#object [cljs.core.Atom ");vg(new r(null,1,[vi,this.state],null),b,c);return Ac(b,"]")};Oh.prototype.ja=!0;
-Oh.prototype.T=function(a,b,c){return ug(b,vg,"("," ",")",c,this)};yh.prototype.ja=!0;yh.prototype.T=function(a,b,c){return ug(b,vg,"["," ","]",c,this)};V.prototype.ja=!0;V.prototype.T=function(a,b,c){return ug(b,vg,"["," ","]",c,this)};Fg.prototype.ja=!0;Fg.prototype.T=function(a,b,c){return ug(b,vg,"("," ",")",c,this)};Te.prototype.ja=!0;Te.prototype.T=function(a,b){return Ac(b,"()")};sf.prototype.ja=!0;sf.prototype.T=function(a,b,c){return ug(b,vg,"("," ",")",c,this)};Gg.prototype.ja=!0;
-Gg.prototype.T=function(a,b,c){return ug(b,vg,"#queue ["," ","]",c,K(this))};r.prototype.ja=!0;r.prototype.T=function(a,b,c){return ni(this,vg,b,c)};ci.prototype.ja=!0;ci.prototype.T=function(a,b,c){return ug(b,vg,"("," ",")",c,this)};Nh.prototype.ja=!0;Nh.prototype.T=function(a,b,c){return ug(b,vg,"("," ",")",c,this)};Od.prototype.ja=!0;Od.prototype.T=function(a,b,c){return ug(b,vg,"("," ",")",c,this)};dd.prototype.ic=!0;
-dd.prototype.Sb=function(a,b){if(b instanceof dd)return kd(this,b);throw Error([y("Cannot compare "),y(this),y(" to "),y(b)].join(""));};v.prototype.ic=!0;v.prototype.Sb=function(a,b){if(b instanceof v)return We(this,b);throw Error([y("Cannot compare "),y(this),y(" to "),y(b)].join(""));};Bg.prototype.ic=!0;Bg.prototype.Sb=function(a,b){if(le(b))return we(this,b);throw Error([y("Cannot compare "),y(this),y(" to "),y(b)].join(""));};V.prototype.ic=!0;
-V.prototype.Sb=function(a,b){if(le(b))return we(this,b);throw Error([y("Cannot compare "),y(this),y(" to "),y(b)].join(""));};var wi=null;function xi(a,b){this.lb=a;this.value=b;this.o=32768;this.M=1}xi.prototype.$b=function(){u(this.lb)&&(this.value=this.lb.A?this.lb.A():this.lb.call(null),this.lb=null);return this.value};function yi(a){return function(b,c){var d=a.h?a.h(b,c):a.call(null,b,c);return Ed(d)?new Dd(d):d}}
-function Vf(a){return function(b){return function(){function c(a,c){return Ab.l(b,a,c)}function d(b){return a.j?a.j(b):a.call(null,b)}function e(){return a.A?a.A():a.call(null)}var f=null,f=function(a,b){switch(arguments.length){case 0:return e.call(this);case 1:return d.call(this,a);case 2:return c.call(this,a,b)}throw Error("Invalid arity: "+arguments.length);};f.A=e;f.j=d;f.h=c;return f}()}(yi(a))}zi;function Ai(){}
-var Bi=function Bi(b){if(null!=b&&null!=b.Pf)return b.Pf(b);var c=Bi[p(null==b?null:b)];if(null!=c)return c.j?c.j(b):c.call(null,b);c=Bi._;if(null!=c)return c.j?c.j(b):c.call(null,b);throw x("IEncodeJS.-clj-\x3ejs",b);};Ci;function Di(a){return(null!=a?a.Of||(a.ed?0:ub(Ai,a)):ub(Ai,a))?Bi(a):"string"===typeof a||"number"===typeof a||a instanceof v||a instanceof dd?Ci.j?Ci.j(a):Ci.call(null,a):Df.w(J([a],0))}
-var Ci=function Ci(b){if(null==b)return null;if(null!=b?b.Of||(b.ed?0:ub(Ai,b)):ub(Ai,b))return Bi(b);if(b instanceof v)return Ne(b);if(b instanceof dd)return""+y(b);if(ke(b)){var c={};b=K(b);for(var d=null,e=0,f=0;;)if(f<e){var g=d.aa(null,f),k=S(g,0,null),g=S(g,1,null);c[Di(k)]=Ci(g);f+=1}else if(b=K(b))oe(b)?(e=Nc(b),b=Oc(b),d=e,e=R(e)):(e=C(b),d=S(e,0,null),e=S(e,1,null),c[Di(d)]=Ci(e),b=D(b),d=null,e=0),f=0;else break;return c}if(he(b)){c=[];b=K(Me.h(Ci,b));d=null;for(f=e=0;;)if(f<e)k=d.aa(null,
-f),c.push(k),f+=1;else if(b=K(b))d=b,oe(d)?(b=Nc(d),f=Oc(d),d=b,e=R(b),b=f):(b=C(d),c.push(b),b=D(d),d=null,e=0),f=0;else break;return c}return b};function Gi(){}var Hi=function Hi(b,c){if(null!=b&&null!=b.Nf)return b.Nf(b,c);var d=Hi[p(null==b?null:b)];if(null!=d)return d.h?d.h(b,c):d.call(null,b,c);d=Hi._;if(null!=d)return d.h?d.h(b,c):d.call(null,b,c);throw x("IEncodeClojure.-js-\x3eclj",b);};
-function Ii(a,b){var c=null!=b&&(b.o&64||b.D)?A.h(P,b):b,d=I.h(c,Ji);return function(a,c,d,k){return function n(m){return(null!=m?m.Mf||(m.ed?0:ub(Gi,m)):ub(Gi,m))?Hi(m,A.h(Lh,b)):se(m)?fi(Me.h(n,m)):he(m)?Yf.h(Xd(m),Me.h(n,m)):rb(m)?ze(Me.h(n,m)):vb(m)===Object?Yf.h(rf,function(){return function(a,b,c,d){return function F(e){return new Ze(null,function(a,b,c,d){return function(){for(;;){var a=K(e);if(a){if(oe(a)){var b=Nc(a),c=R(b),f=new bf(Array(c),0);a:for(var g=0;;)if(g<c){var k=Lb.h(b,g),k=new V(null,
-2,5,W,[d.j?d.j(k):d.call(null,k),n(m[k])],null);f.add(k);g+=1}else{b=!0;break a}return b?cf(f.vb(),F(Oc(a))):cf(f.vb(),null)}f=C(a);return Md(new V(null,2,5,W,[d.j?d.j(f):d.call(null,f),n(m[f])],null),F(N(a)))}return null}}}(a,b,c,d),null,null)}}(a,c,d,k)(pe(m))}()):m}}(b,c,d,u(d)?Ye:y)(a)}
-function Ki(a){return function(b){return function(){function c(a){var b=null;if(0<arguments.length){for(var b=0,c=Array(arguments.length-0);b<c.length;)c[b]=arguments[b+0],++b;b=new B(c,0)}return d.call(this,b)}function d(c){var d=I.l(Q.j?Q.j(b):Q.call(null,b),c,re);d===re&&(d=A.h(a,c),If.G(b,T,c,d));return d}c.J=0;c.K=function(a){a=K(a);return d(a)};c.w=d;return c}()}(function(){var a=rf;return X.j?X.j(a):X.call(null,a)}())}
-var zi=function zi(b){for(var c=[],d=arguments.length,e=0;;)if(e<d)c.push(arguments[e]),e+=1;else break;switch(c.length){case 0:return zi.A();case 1:return zi.j(arguments[0]);default:throw Error([y("Invalid arity: "),y(c.length)].join(""));}};zi.A=function(){return zi.j(1)};zi.j=function(a){return Math.random()*a};zi.J=1;var Li=null;function Mi(){if(null==Li){var a=new r(null,3,[Ni,rf,Oi,rf,Pi,rf],null);Li=X.j?X.j(a):X.call(null,a)}return Li}
-function Qi(a,b,c){var d=H.h(b,c);if(!d&&!(d=ve(Pi.j(a).call(null,b),c))&&(d=le(c))&&(d=le(b)))if(d=R(c)===R(b))for(var d=!0,e=0;;)if(d&&e!==R(c))d=Qi(a,b.j?b.j(e):b.call(null,e),c.j?c.j(e):c.call(null,e)),e+=1;else return d;else return d;else return d}function Ri(a){var b;b=Mi();b=Q.j?Q.j(b):Q.call(null,b);return lf(I.h(Ni.j(b),a))}function Si(a,b,c,d){If.h(a,function(){return Q.j?Q.j(b):Q.call(null,b)});If.h(c,function(){return Q.j?Q.j(d):Q.call(null,d)})}
-var Ti=function Ti(b,c,d){var e=(Q.j?Q.j(d):Q.call(null,d)).call(null,b),e=u(u(e)?e.j?e.j(c):e.call(null,c):e)?!0:null;if(u(e))return e;e=function(){for(var e=Ri(c);;)if(0<R(e))Ti(b,C(e),d),e=N(e);else return null}();if(u(e))return e;e=function(){for(var e=Ri(b);;)if(0<R(e))Ti(C(e),c,d),e=N(e);else return null}();return u(e)?e:!1};function Ui(a,b,c){c=Ti(a,b,c);if(u(c))a=c;else{c=Qi;var d;d=Mi();d=Q.j?Q.j(d):Q.call(null,d);a=c(d,a,b)}return a}
-var Vi=function Vi(b,c,d,e,f,g,k){var l=Ab.l(function(e,g){var k=S(g,0,null);S(g,1,null);if(Qi(Q.j?Q.j(d):Q.call(null,d),c,k)){var l;l=(l=null==e)?l:Ui(k,C(e),f);l=u(l)?g:e;if(!u(Ui(C(l),k,f)))throw Error([y("Multiple methods in multimethod '"),y(b),y("' match dispatch value: "),y(c),y(" -\x3e "),y(k),y(" and "),y(C(l)),y(", and neither is preferred")].join(""));return l}return e},null,Q.j?Q.j(e):Q.call(null,e));if(u(l)){if(H.h(Q.j?Q.j(k):Q.call(null,k),Q.j?Q.j(d):Q.call(null,d)))return If.G(g,T,
-c,Td(l)),Td(l);Si(g,e,k,d);return Vi(b,c,d,e,f,g,k)}return null};function Wi(a,b){throw Error([y("No method in multimethod '"),y(a),y("' for dispatch value: "),y(b)].join(""));}function Xi(a,b,c,d,e,f,g,k){this.name=a;this.C=b;this.Wf=c;this.md=d;this.Mc=e;this.kg=f;this.sd=g;this.Tc=k;this.o=4194305;this.M=4352}h=Xi.prototype;
-h.call=function(){function a(a,b,c,d,e,f,g,k,l,m,n,z,w,q,t,E,F,M,O,L,Z,ba){a=this;var Ca=A.w(a.C,b,c,d,e,J([f,g,k,l,m,n,z,w,q,t,E,F,M,O,L,Z,ba],0)),Ra=Yi(this,Ca);u(Ra)||Wi(a.name,Ca);return A.w(Ra,b,c,d,e,J([f,g,k,l,m,n,z,w,q,t,E,F,M,O,L,Z,ba],0))}function b(a,b,c,d,e,f,g,k,l,m,n,z,w,q,t,E,F,M,O,L,Z){a=this;var ba=a.C.Ta?a.C.Ta(b,c,d,e,f,g,k,l,m,n,z,w,q,t,E,F,M,O,L,Z):a.C.call(null,b,c,d,e,f,g,k,l,m,n,z,w,q,t,E,F,M,O,L,Z),Ca=Yi(this,ba);u(Ca)||Wi(a.name,ba);return Ca.Ta?Ca.Ta(b,c,d,e,f,g,k,l,m,n,
-z,w,q,t,E,F,M,O,L,Z):Ca.call(null,b,c,d,e,f,g,k,l,m,n,z,w,q,t,E,F,M,O,L,Z)}function c(a,b,c,d,e,f,g,k,l,m,n,z,w,q,t,E,F,M,O,L){a=this;var Z=a.C.Sa?a.C.Sa(b,c,d,e,f,g,k,l,m,n,z,w,q,t,E,F,M,O,L):a.C.call(null,b,c,d,e,f,g,k,l,m,n,z,w,q,t,E,F,M,O,L),ba=Yi(this,Z);u(ba)||Wi(a.name,Z);return ba.Sa?ba.Sa(b,c,d,e,f,g,k,l,m,n,z,w,q,t,E,F,M,O,L):ba.call(null,b,c,d,e,f,g,k,l,m,n,z,w,q,t,E,F,M,O,L)}function d(a,b,c,d,e,f,g,k,l,m,n,z,w,q,t,E,F,M,O){a=this;var L=a.C.Ra?a.C.Ra(b,c,d,e,f,g,k,l,m,n,z,w,q,t,E,F,M,
-O):a.C.call(null,b,c,d,e,f,g,k,l,m,n,z,w,q,t,E,F,M,O),Z=Yi(this,L);u(Z)||Wi(a.name,L);return Z.Ra?Z.Ra(b,c,d,e,f,g,k,l,m,n,z,w,q,t,E,F,M,O):Z.call(null,b,c,d,e,f,g,k,l,m,n,z,w,q,t,E,F,M,O)}function e(a,b,c,d,e,f,g,k,l,m,n,z,w,q,t,E,F,M){a=this;var O=a.C.Qa?a.C.Qa(b,c,d,e,f,g,k,l,m,n,z,w,q,t,E,F,M):a.C.call(null,b,c,d,e,f,g,k,l,m,n,z,w,q,t,E,F,M),L=Yi(this,O);u(L)||Wi(a.name,O);return L.Qa?L.Qa(b,c,d,e,f,g,k,l,m,n,z,w,q,t,E,F,M):L.call(null,b,c,d,e,f,g,k,l,m,n,z,w,q,t,E,F,M)}function f(a,b,c,d,e,f,
-g,k,l,m,n,z,w,q,t,E,F){a=this;var M=a.C.Pa?a.C.Pa(b,c,d,e,f,g,k,l,m,n,z,w,q,t,E,F):a.C.call(null,b,c,d,e,f,g,k,l,m,n,z,w,q,t,E,F),O=Yi(this,M);u(O)||Wi(a.name,M);return O.Pa?O.Pa(b,c,d,e,f,g,k,l,m,n,z,w,q,t,E,F):O.call(null,b,c,d,e,f,g,k,l,m,n,z,w,q,t,E,F)}function g(a,b,c,d,e,f,g,k,l,m,n,z,w,q,t,E){a=this;var F=a.C.Oa?a.C.Oa(b,c,d,e,f,g,k,l,m,n,z,w,q,t,E):a.C.call(null,b,c,d,e,f,g,k,l,m,n,z,w,q,t,E),M=Yi(this,F);u(M)||Wi(a.name,F);return M.Oa?M.Oa(b,c,d,e,f,g,k,l,m,n,z,w,q,t,E):M.call(null,b,c,d,
-e,f,g,k,l,m,n,z,w,q,t,E)}function k(a,b,c,d,e,f,g,k,l,m,n,z,w,q,t){a=this;var E=a.C.Na?a.C.Na(b,c,d,e,f,g,k,l,m,n,z,w,q,t):a.C.call(null,b,c,d,e,f,g,k,l,m,n,z,w,q,t),F=Yi(this,E);u(F)||Wi(a.name,E);return F.Na?F.Na(b,c,d,e,f,g,k,l,m,n,z,w,q,t):F.call(null,b,c,d,e,f,g,k,l,m,n,z,w,q,t)}function l(a,b,c,d,e,f,g,k,l,m,n,z,w,q){a=this;var t=a.C.Ma?a.C.Ma(b,c,d,e,f,g,k,l,m,n,z,w,q):a.C.call(null,b,c,d,e,f,g,k,l,m,n,z,w,q),E=Yi(this,t);u(E)||Wi(a.name,t);return E.Ma?E.Ma(b,c,d,e,f,g,k,l,m,n,z,w,q):E.call(null,
-b,c,d,e,f,g,k,l,m,n,z,w,q)}function n(a,b,c,d,e,f,g,k,l,m,n,z,w){a=this;var q=a.C.La?a.C.La(b,c,d,e,f,g,k,l,m,n,z,w):a.C.call(null,b,c,d,e,f,g,k,l,m,n,z,w),t=Yi(this,q);u(t)||Wi(a.name,q);return t.La?t.La(b,c,d,e,f,g,k,l,m,n,z,w):t.call(null,b,c,d,e,f,g,k,l,m,n,z,w)}function m(a,b,c,d,e,f,g,k,l,m,n,z){a=this;var w=a.C.Ka?a.C.Ka(b,c,d,e,f,g,k,l,m,n,z):a.C.call(null,b,c,d,e,f,g,k,l,m,n,z),q=Yi(this,w);u(q)||Wi(a.name,w);return q.Ka?q.Ka(b,c,d,e,f,g,k,l,m,n,z):q.call(null,b,c,d,e,f,g,k,l,m,n,z)}function t(a,
-b,c,d,e,f,g,k,l,m,n){a=this;var z=a.C.Ca?a.C.Ca(b,c,d,e,f,g,k,l,m,n):a.C.call(null,b,c,d,e,f,g,k,l,m,n),w=Yi(this,z);u(w)||Wi(a.name,z);return w.Ca?w.Ca(b,c,d,e,f,g,k,l,m,n):w.call(null,b,c,d,e,f,g,k,l,m,n)}function q(a,b,c,d,e,f,g,k,l,m){a=this;var n=a.C.Va?a.C.Va(b,c,d,e,f,g,k,l,m):a.C.call(null,b,c,d,e,f,g,k,l,m),z=Yi(this,n);u(z)||Wi(a.name,n);return z.Va?z.Va(b,c,d,e,f,g,k,l,m):z.call(null,b,c,d,e,f,g,k,l,m)}function z(a,b,c,d,e,f,g,k,l){a=this;var m=a.C.Ua?a.C.Ua(b,c,d,e,f,g,k,l):a.C.call(null,
-b,c,d,e,f,g,k,l),n=Yi(this,m);u(n)||Wi(a.name,m);return n.Ua?n.Ua(b,c,d,e,f,g,k,l):n.call(null,b,c,d,e,f,g,k,l)}function w(a,b,c,d,e,f,g,k){a=this;var l=a.C.ta?a.C.ta(b,c,d,e,f,g,k):a.C.call(null,b,c,d,e,f,g,k),m=Yi(this,l);u(m)||Wi(a.name,l);return m.ta?m.ta(b,c,d,e,f,g,k):m.call(null,b,c,d,e,f,g,k)}function E(a,b,c,d,e,f,g){a=this;var k=a.C.ra?a.C.ra(b,c,d,e,f,g):a.C.call(null,b,c,d,e,f,g),l=Yi(this,k);u(l)||Wi(a.name,k);return l.ra?l.ra(b,c,d,e,f,g):l.call(null,b,c,d,e,f,g)}function F(a,b,c,d,
-e,f){a=this;var g=a.C.N?a.C.N(b,c,d,e,f):a.C.call(null,b,c,d,e,f),k=Yi(this,g);u(k)||Wi(a.name,g);return k.N?k.N(b,c,d,e,f):k.call(null,b,c,d,e,f)}function M(a,b,c,d,e){a=this;var f=a.C.G?a.C.G(b,c,d,e):a.C.call(null,b,c,d,e),g=Yi(this,f);u(g)||Wi(a.name,f);return g.G?g.G(b,c,d,e):g.call(null,b,c,d,e)}function O(a,b,c,d){a=this;var e=a.C.l?a.C.l(b,c,d):a.C.call(null,b,c,d),f=Yi(this,e);u(f)||Wi(a.name,e);return f.l?f.l(b,c,d):f.call(null,b,c,d)}function Z(a,b,c){a=this;var d=a.C.h?a.C.h(b,c):a.C.call(null,
-b,c),e=Yi(this,d);u(e)||Wi(a.name,d);return e.h?e.h(b,c):e.call(null,b,c)}function ba(a,b){a=this;var c=a.C.j?a.C.j(b):a.C.call(null,b),d=Yi(this,c);u(d)||Wi(a.name,c);return d.j?d.j(b):d.call(null,b)}function Ca(a){a=this;var b=a.C.A?a.C.A():a.C.call(null),c=Yi(this,b);u(c)||Wi(a.name,b);return c.A?c.A():c.call(null)}var L=null,L=function(L,oa,wa,xa,ya,Xb,Na,Ta,Wa,eb,$a,kb,sb,Fb,Ob,qc,Oa,bd,Ld,xe,Ff,vh){switch(arguments.length){case 1:return Ca.call(this,L);case 2:return ba.call(this,L,oa);case 3:return Z.call(this,
-L,oa,wa);case 4:return O.call(this,L,oa,wa,xa);case 5:return M.call(this,L,oa,wa,xa,ya);case 6:return F.call(this,L,oa,wa,xa,ya,Xb);case 7:return E.call(this,L,oa,wa,xa,ya,Xb,Na);case 8:return w.call(this,L,oa,wa,xa,ya,Xb,Na,Ta);case 9:return z.call(this,L,oa,wa,xa,ya,Xb,Na,Ta,Wa);case 10:return q.call(this,L,oa,wa,xa,ya,Xb,Na,Ta,Wa,eb);case 11:return t.call(this,L,oa,wa,xa,ya,Xb,Na,Ta,Wa,eb,$a);case 12:return m.call(this,L,oa,wa,xa,ya,Xb,Na,Ta,Wa,eb,$a,kb);case 13:return n.call(this,L,oa,wa,xa,ya,
-Xb,Na,Ta,Wa,eb,$a,kb,sb);case 14:return l.call(this,L,oa,wa,xa,ya,Xb,Na,Ta,Wa,eb,$a,kb,sb,Fb);case 15:return k.call(this,L,oa,wa,xa,ya,Xb,Na,Ta,Wa,eb,$a,kb,sb,Fb,Ob);case 16:return g.call(this,L,oa,wa,xa,ya,Xb,Na,Ta,Wa,eb,$a,kb,sb,Fb,Ob,qc);case 17:return f.call(this,L,oa,wa,xa,ya,Xb,Na,Ta,Wa,eb,$a,kb,sb,Fb,Ob,qc,Oa);case 18:return e.call(this,L,oa,wa,xa,ya,Xb,Na,Ta,Wa,eb,$a,kb,sb,Fb,Ob,qc,Oa,bd);case 19:return d.call(this,L,oa,wa,xa,ya,Xb,Na,Ta,Wa,eb,$a,kb,sb,Fb,Ob,qc,Oa,bd,Ld);case 20:return c.call(this,
-L,oa,wa,xa,ya,Xb,Na,Ta,Wa,eb,$a,kb,sb,Fb,Ob,qc,Oa,bd,Ld,xe);case 21:return b.call(this,L,oa,wa,xa,ya,Xb,Na,Ta,Wa,eb,$a,kb,sb,Fb,Ob,qc,Oa,bd,Ld,xe,Ff);case 22:return a.call(this,L,oa,wa,xa,ya,Xb,Na,Ta,Wa,eb,$a,kb,sb,Fb,Ob,qc,Oa,bd,Ld,xe,Ff,vh)}throw Error("Invalid arity: "+arguments.length);};L.j=Ca;L.h=ba;L.l=Z;L.G=O;L.N=M;L.ra=F;L.ta=E;L.Ua=w;L.Va=z;L.Ca=q;L.Ka=t;L.La=m;L.Ma=n;L.Na=l;L.Oa=k;L.Pa=g;L.Qa=f;L.Ra=e;L.Sa=d;L.Ta=c;L.ue=b;L.Xc=a;return L}();
-h.apply=function(a,b){return this.call.apply(this,[this].concat(yb(b)))};h.A=function(){var a=this.C.A?this.C.A():this.C.call(null),b=Yi(this,a);u(b)||Wi(this.name,a);return b.A?b.A():b.call(null)};h.j=function(a){var b=this.C.j?this.C.j(a):this.C.call(null,a),c=Yi(this,b);u(c)||Wi(this.name,b);return c.j?c.j(a):c.call(null,a)};h.h=function(a,b){var c=this.C.h?this.C.h(a,b):this.C.call(null,a,b),d=Yi(this,c);u(d)||Wi(this.name,c);return d.h?d.h(a,b):d.call(null,a,b)};
-h.l=function(a,b,c){var d=this.C.l?this.C.l(a,b,c):this.C.call(null,a,b,c),e=Yi(this,d);u(e)||Wi(this.name,d);return e.l?e.l(a,b,c):e.call(null,a,b,c)};h.G=function(a,b,c,d){var e=this.C.G?this.C.G(a,b,c,d):this.C.call(null,a,b,c,d),f=Yi(this,e);u(f)||Wi(this.name,e);return f.G?f.G(a,b,c,d):f.call(null,a,b,c,d)};h.N=function(a,b,c,d,e){var f=this.C.N?this.C.N(a,b,c,d,e):this.C.call(null,a,b,c,d,e),g=Yi(this,f);u(g)||Wi(this.name,f);return g.N?g.N(a,b,c,d,e):g.call(null,a,b,c,d,e)};
-h.ra=function(a,b,c,d,e,f){var g=this.C.ra?this.C.ra(a,b,c,d,e,f):this.C.call(null,a,b,c,d,e,f),k=Yi(this,g);u(k)||Wi(this.name,g);return k.ra?k.ra(a,b,c,d,e,f):k.call(null,a,b,c,d,e,f)};h.ta=function(a,b,c,d,e,f,g){var k=this.C.ta?this.C.ta(a,b,c,d,e,f,g):this.C.call(null,a,b,c,d,e,f,g),l=Yi(this,k);u(l)||Wi(this.name,k);return l.ta?l.ta(a,b,c,d,e,f,g):l.call(null,a,b,c,d,e,f,g)};
-h.Ua=function(a,b,c,d,e,f,g,k){var l=this.C.Ua?this.C.Ua(a,b,c,d,e,f,g,k):this.C.call(null,a,b,c,d,e,f,g,k),n=Yi(this,l);u(n)||Wi(this.name,l);return n.Ua?n.Ua(a,b,c,d,e,f,g,k):n.call(null,a,b,c,d,e,f,g,k)};h.Va=function(a,b,c,d,e,f,g,k,l){var n=this.C.Va?this.C.Va(a,b,c,d,e,f,g,k,l):this.C.call(null,a,b,c,d,e,f,g,k,l),m=Yi(this,n);u(m)||Wi(this.name,n);return m.Va?m.Va(a,b,c,d,e,f,g,k,l):m.call(null,a,b,c,d,e,f,g,k,l)};
-h.Ca=function(a,b,c,d,e,f,g,k,l,n){var m=this.C.Ca?this.C.Ca(a,b,c,d,e,f,g,k,l,n):this.C.call(null,a,b,c,d,e,f,g,k,l,n),t=Yi(this,m);u(t)||Wi(this.name,m);return t.Ca?t.Ca(a,b,c,d,e,f,g,k,l,n):t.call(null,a,b,c,d,e,f,g,k,l,n)};h.Ka=function(a,b,c,d,e,f,g,k,l,n,m){var t=this.C.Ka?this.C.Ka(a,b,c,d,e,f,g,k,l,n,m):this.C.call(null,a,b,c,d,e,f,g,k,l,n,m),q=Yi(this,t);u(q)||Wi(this.name,t);return q.Ka?q.Ka(a,b,c,d,e,f,g,k,l,n,m):q.call(null,a,b,c,d,e,f,g,k,l,n,m)};
-h.La=function(a,b,c,d,e,f,g,k,l,n,m,t){var q=this.C.La?this.C.La(a,b,c,d,e,f,g,k,l,n,m,t):this.C.call(null,a,b,c,d,e,f,g,k,l,n,m,t),z=Yi(this,q);u(z)||Wi(this.name,q);return z.La?z.La(a,b,c,d,e,f,g,k,l,n,m,t):z.call(null,a,b,c,d,e,f,g,k,l,n,m,t)};h.Ma=function(a,b,c,d,e,f,g,k,l,n,m,t,q){var z=this.C.Ma?this.C.Ma(a,b,c,d,e,f,g,k,l,n,m,t,q):this.C.call(null,a,b,c,d,e,f,g,k,l,n,m,t,q),w=Yi(this,z);u(w)||Wi(this.name,z);return w.Ma?w.Ma(a,b,c,d,e,f,g,k,l,n,m,t,q):w.call(null,a,b,c,d,e,f,g,k,l,n,m,t,q)};
-h.Na=function(a,b,c,d,e,f,g,k,l,n,m,t,q,z){var w=this.C.Na?this.C.Na(a,b,c,d,e,f,g,k,l,n,m,t,q,z):this.C.call(null,a,b,c,d,e,f,g,k,l,n,m,t,q,z),E=Yi(this,w);u(E)||Wi(this.name,w);return E.Na?E.Na(a,b,c,d,e,f,g,k,l,n,m,t,q,z):E.call(null,a,b,c,d,e,f,g,k,l,n,m,t,q,z)};
-h.Oa=function(a,b,c,d,e,f,g,k,l,n,m,t,q,z,w){var E=this.C.Oa?this.C.Oa(a,b,c,d,e,f,g,k,l,n,m,t,q,z,w):this.C.call(null,a,b,c,d,e,f,g,k,l,n,m,t,q,z,w),F=Yi(this,E);u(F)||Wi(this.name,E);return F.Oa?F.Oa(a,b,c,d,e,f,g,k,l,n,m,t,q,z,w):F.call(null,a,b,c,d,e,f,g,k,l,n,m,t,q,z,w)};
-h.Pa=function(a,b,c,d,e,f,g,k,l,n,m,t,q,z,w,E){var F=this.C.Pa?this.C.Pa(a,b,c,d,e,f,g,k,l,n,m,t,q,z,w,E):this.C.call(null,a,b,c,d,e,f,g,k,l,n,m,t,q,z,w,E),M=Yi(this,F);u(M)||Wi(this.name,F);return M.Pa?M.Pa(a,b,c,d,e,f,g,k,l,n,m,t,q,z,w,E):M.call(null,a,b,c,d,e,f,g,k,l,n,m,t,q,z,w,E)};
-h.Qa=function(a,b,c,d,e,f,g,k,l,n,m,t,q,z,w,E,F){var M=this.C.Qa?this.C.Qa(a,b,c,d,e,f,g,k,l,n,m,t,q,z,w,E,F):this.C.call(null,a,b,c,d,e,f,g,k,l,n,m,t,q,z,w,E,F),O=Yi(this,M);u(O)||Wi(this.name,M);return O.Qa?O.Qa(a,b,c,d,e,f,g,k,l,n,m,t,q,z,w,E,F):O.call(null,a,b,c,d,e,f,g,k,l,n,m,t,q,z,w,E,F)};
-h.Ra=function(a,b,c,d,e,f,g,k,l,n,m,t,q,z,w,E,F,M){var O=this.C.Ra?this.C.Ra(a,b,c,d,e,f,g,k,l,n,m,t,q,z,w,E,F,M):this.C.call(null,a,b,c,d,e,f,g,k,l,n,m,t,q,z,w,E,F,M),Z=Yi(this,O);u(Z)||Wi(this.name,O);return Z.Ra?Z.Ra(a,b,c,d,e,f,g,k,l,n,m,t,q,z,w,E,F,M):Z.call(null,a,b,c,d,e,f,g,k,l,n,m,t,q,z,w,E,F,M)};
-h.Sa=function(a,b,c,d,e,f,g,k,l,n,m,t,q,z,w,E,F,M,O){var Z=this.C.Sa?this.C.Sa(a,b,c,d,e,f,g,k,l,n,m,t,q,z,w,E,F,M,O):this.C.call(null,a,b,c,d,e,f,g,k,l,n,m,t,q,z,w,E,F,M,O),ba=Yi(this,Z);u(ba)||Wi(this.name,Z);return ba.Sa?ba.Sa(a,b,c,d,e,f,g,k,l,n,m,t,q,z,w,E,F,M,O):ba.call(null,a,b,c,d,e,f,g,k,l,n,m,t,q,z,w,E,F,M,O)};
-h.Ta=function(a,b,c,d,e,f,g,k,l,n,m,t,q,z,w,E,F,M,O,Z){var ba=this.C.Ta?this.C.Ta(a,b,c,d,e,f,g,k,l,n,m,t,q,z,w,E,F,M,O,Z):this.C.call(null,a,b,c,d,e,f,g,k,l,n,m,t,q,z,w,E,F,M,O,Z),Ca=Yi(this,ba);u(Ca)||Wi(this.name,ba);return Ca.Ta?Ca.Ta(a,b,c,d,e,f,g,k,l,n,m,t,q,z,w,E,F,M,O,Z):Ca.call(null,a,b,c,d,e,f,g,k,l,n,m,t,q,z,w,E,F,M,O,Z)};
-h.ue=function(a,b,c,d,e,f,g,k,l,n,m,t,q,z,w,E,F,M,O,Z,ba){var Ca=A.w(this.C,a,b,c,d,J([e,f,g,k,l,n,m,t,q,z,w,E,F,M,O,Z,ba],0)),L=Yi(this,Ca);u(L)||Wi(this.name,Ca);return A.w(L,a,b,c,d,J([e,f,g,k,l,n,m,t,q,z,w,E,F,M,O,Z,ba],0))};function Zi(a,b,c){If.G(a.Mc,T,b,c);Si(a.sd,a.Mc,a.Tc,a.md)}
-function Yi(a,b){H.h(Q.j?Q.j(a.Tc):Q.call(null,a.Tc),Q.j?Q.j(a.md):Q.call(null,a.md))||Si(a.sd,a.Mc,a.Tc,a.md);var c=(Q.j?Q.j(a.sd):Q.call(null,a.sd)).call(null,b);if(u(c))return c;c=Vi(a.name,b,a.md,a.Mc,a.kg,a.sd,a.Tc);return u(c)?c:(Q.j?Q.j(a.Mc):Q.call(null,a.Mc)).call(null,a.Wf)}h.$c=function(){return Qc(this.name)};h.ad=function(){return Rc(this.name)};h.W=function(){return ja(this)};function $i(a,b){this.Mb=a;this.H=b;this.o=2153775104;this.M=2048}h=$i.prototype;h.toString=function(){return this.Mb};
-h.equiv=function(a){return this.L(null,a)};h.L=function(a,b){return b instanceof $i&&this.Mb===b.Mb};h.T=function(a,b){return Ac(b,[y('#uuid "'),y(this.Mb),y('"')].join(""))};h.W=function(){null==this.H&&(this.H=id(this.Mb));return this.H};h.Sb=function(a,b){return Xa(this.Mb,b.Mb)};var aj=new v(null,"response","response",-1068424192),bj=new v(null,"y","y",-1757859776),cj=new dd(null,"tag","tag",350170304,null),dj=new v(null,"span.gutter","span.gutter",-700214016),ej=new v(null,"description","description",-1428560544),fj=new v(null,"dcs-param","dcs-param",-971011648),gj=new v(null,"path","path",-188191168),hj=new v(null,"escape","escape",-991601952),ij=new dd(null,"valid-tag?","valid-tag?",1243064160,null),jj=new dd(null,"itm","itm",-713282527,null),kj=new v(null,"tab-index",
-"tab-index",895755393),lj=new v(null,"bold","bold",-116809535),mj=new v(null,"authorImgURL","authorImgURL",-1171541759),nj=new dd(null,".-length",".-length",-280799999,null),oj=new v(null,"finally","finally",1589088705),pj=new v(null,"char-attrs","char-attrs",-1444091455),qj=new v(null,"auto-wrap-mode","auto-wrap-mode",-2049555583),rj=new v(null,"preload?","preload?",445442977),sj=new v(null,"on-set","on-set",-140953470),tj=new dd(null,"body","body",-408674142,null),uj=new v(null,"format","format",
--1306924766),vj=new dd(null,"puts","puts",-1883877054,null),wj=new v(null,"current-time","current-time",-1609407134),xj=new v(null,"span.progressbar","span.progressbar",766750210),yj=new v("internal","rewind","internal/rewind",-31749342),zj=new dd(null,"render-fun","render-fun",-1209513086,null),Aj=new v(null,"bottom-margin","bottom-margin",-701300733),Bj=new v(null,"on-key-press","on-key-press",-399563677),Cj=new v(null,"fast-forward","fast-forward",703093475),Dj=new v(null,"blink","blink",-271985917),
-Ej=new dd(null,"meta25743","meta25743",1830333444,null),Fj=new dd(null,"\x3c","\x3c",993667236,null),Gj=new v(null,"primary","primary",817773892),Hj=new v(null,"api","api",-899839580),Ij=new v(null,"original-text","original-text",744448452),jb=new v(null,"meta","meta",1499536964),Jj=new v(null,"screen","screen",1990059748),Kj=new dd(null,"meta25737","meta25737",-940551292,null),qf=new dd(null,"meta20845","meta20845",1956995173,null),Lj=new v(null,"param-chars","param-chars",38609125),Mj=new v(null,
-"keywords?","keywords?",764949733),Nj=new dd(null,"blockable","blockable",-28395259,null),lb=new v(null,"dup","dup",556298533),Oj=new v(null,"read","read",1140058661),Pj=new v(null,"key","key",-1516042587),Qj=new v(null,"asciicast","asciicast",509526949),Rj=new v(null,"private","private",-558947994),Sj=new v(null,"not-initialized","not-initialized",-1937378906),Tj=new v(null,"tabs","tabs",-779855354),Uj=new v(null,"ground","ground",1193572934),Vj=new v(null,"next-print-wraps","next-print-wraps",-1664999738),
-Wj=new v(null,"font-size","font-size",-1847940346),Xj=new dd(null,"pos?","pos?",-244377722,null),Yj=new v(null,"transition","transition",765692007),Zj=new v(null,"failure","failure",720415879),ak=new v(null,"speed","speed",1257663751),bk=new v(null,"derefed","derefed",590684583),Hf=new dd(null,"new-value","new-value",-1567397401,null),ck=new v(null,"displayName","displayName",-809144601),Cf=new v(null,"validator","validator",-1966190681),dk=new v(null,"method","method",55703592),ek=new v(null,"div.loading",
-"div.loading",-155515768),fk=new v(null,"dcs-passthrough","dcs-passthrough",-671044440),gk=new v(null,"show-hud","show-hud",1983299752),hk=new v(null,"start-at","start-at",-103334680),ik=new v(null,"raw","raw",1604651272),jk=new v(null,"default","default",-1987822328),kk=new v(null,"cljsRender","cljsRender",247449928),lk=new v(null,"csi-param","csi-param",-1120111192),mk=new v(null,"speed-down","speed-down",-1024390712),nk=new v(null,"div.control-bar","div.control-bar",-1316808248),ok=new v(null,
-"finally-block","finally-block",832982472),pk=new dd(null,"cb","cb",-2064487928,null),qk=new v(null,"inverse","inverse",-1623859672),rk=new v(null,"fg","fg",-101797208),sk=new v(null,"dcs-intermediate","dcs-intermediate",480808872),tk=new v(null,"osc-string","osc-string",-486531128),uk=new v(null,"on-enter","on-enter",-928988216),vk=new v(null,"name","name",1843675177),wk=new v(null,"events-ch","events-ch",-763395991),xk=new v(null,"frames","frames",1765687497),yk=new v(null,"div.play-button","div.play-button",
-1020321513),zk=new v(null,"screen-fn","screen-fn",-2109509815),Ak=new v(null,"span.time-elapsed","span.time-elapsed",-1782475638),Bk=new v(null,"time","time",1385887882),Ck=new v(null,"response-format","response-format",1664465322),Dk=new v(null,"status-text","status-text",-1834235478),Ek=new dd(null,"v","v",1661996586,null),Fk=new dd(null,"map?","map?",-1780568534,null),Gk=new v(null,"recording-ch-fn","recording-ch-fn",-902533462),Hk=new v(null,"span.playback-button","span.playback-button",-1136389398),
-Ik=new v(null,"span.title-bar","span.title-bar",-1165872085),Jk=new v(null,"loaded","loaded",-1246482293),Kk=new v(null,"width","width",-384071477),Lk=new v(null,"start","start",-355208981),Mk=new v(null,"aborted","aborted",1775972619),Nk=new v(null,"lines","lines",-700165781),Ok=new v(null,"processing-request","processing-request",-264947221),Pk=new v(null,"params","params",710516235),Qk=new v(null,"sos-pm-apc-string","sos-pm-apc-string",398998091),Rk=new v(null,"component-did-update","component-did-update",
--1468549173),Sk=new v(null,"div.start-prompt","div.start-prompt",-41424788),vi=new v(null,"val","val",128701612),Tk=new v(null,"cursor","cursor",1011937484),Uk=new v(null,"dcs-entry","dcs-entry",216833388),Y=new v(null,"recur","recur",-437573268),Vk=new v(null,"type","type",1174270348),Wk=new v(null,"request-received","request-received",2110590540),Xk=new v(null,"alternate","alternate",-931038644),Yk=new v(null,"catch-block","catch-block",1175212748),Gf=new dd(null,"validate","validate",1439230700,
-null),Zk=new dd(null,"meta25692","meta25692",781155212,null),$k=new v(null,"duration","duration",1444101068),al=new v(null,"src","src",-1651076051),bl=new v(null,"state","state",-1988618099),cl=new v(null,"span.bar","span.bar",-1986926323),dl=new v(null,"xmlns","xmlns",-1862095571),el=new dd(null,"\x3e","\x3e",1085014381,null),fl=new v(null,"on-exit","on-exit",1821961613),ri=new v(null,"fallback-impl","fallback-impl",-1501286995),gl=new v(null,"view-box","view-box",-1792199155),hl=new v(null,"source",
-"source",-433931539),il=new v(null,"csi-entry","csi-entry",-1787942099),Il=new v(null,"handlers","handlers",79528781),hb=new v(null,"flush-on-newline","flush-on-newline",-151457939),Jl=new v(null,"rewind","rewind",-669264915),Kl=new v(null,"command-ch","command-ch",508874766),Ll=new v(null,"componentWillUnmount","componentWillUnmount",1573788814),Ml=new v(null,"span.timer","span.timer",2111534382),Nl=new dd(null,"validator","validator",-325659154,null),Ol=new v(null,"toggle","toggle",1291842030),
-Pl=new dd(null,"meta25546","meta25546",1627665006,null),Ql=new v(null,"parse-error","parse-error",255902478),Rl=new v(null,"cursor-blink-ch","cursor-blink-ch",1063651214),Sl=new dd(null,"alt-flag","alt-flag",-1794972754,null),Tl=new v(null,"on-mouse-down","on-mouse-down",1147755470),Ul=new v(null,"on-click","on-click",1632826543),Oi=new v(null,"descendants","descendants",1824886031),Vl=new v(null,"underline","underline",2018066703),Wl=new v(null,"size","size",1098693007),Xl=new v(null,"title","title",
-636505583),Yl=new v(null,"stop-ch","stop-ch",-219113969),Zl=new v(null,"insert-mode","insert-mode",894811791),$l=new v(null,"toggle-fullscreen","toggle-fullscreen",-1647254833),am=new v(null,"prefix","prefix",-265908465),bm=new v(null,"headers","headers",-835030129),cm=new v(null,"loop","loop",-395552849),dm=new v(null,"author-img-url","author-img-url",2016975920),em=new v(null,"shouldComponentUpdate","shouldComponentUpdate",1795750960),fm=new v(null,"error-handler","error-handler",-484945776),Pi=
-new v(null,"ancestors","ancestors",-776045424),gm=new dd(null,"flag","flag",-1565787888,null),hm=new v(null,"style","style",-496642736),im=new v(null,"theme","theme",-1247880880),jm=new v(null,"stream","stream",1534941648),km=new v(null,"write","write",-1857649168),lm=new v(null,"charset-fn","charset-fn",1374523920),mm=new v(null,"author","author",2111686192),Nf=new dd(null,"n","n",-2092305744,null),nm=new v(null,"escape-intermediate","escape-intermediate",1036490448),om=new v(null,"div","div",1057191632),
-ib=new v(null,"readably","readably",1129599760),pm=new v(null,"change-speed","change-speed",2125740976),qm=new dd(null,"box","box",-1123515375,null),ii=new v(null,"more-marker","more-marker",-14717935),rm=new v(null,"new-line-mode","new-line-mode",1467504785),sm=new v(null,"csi-intermediate","csi-intermediate",-410048175),tm=new v(null,"reagentRender","reagentRender",-358306383),um=new v(null,"started?","started?",-1301062863),vm=new v(null,"other-buffer-saved","other-buffer-saved",-2048065486),wm=
-new v(null,"snapshot","snapshot",-1274785710),xm=new v(null,"preload","preload",1646824722),ym=new v(null,"stop","stop",-2140911342),zm=new v(null,"render","render",-1408033454),Am=new v(null,"parser","parser",-1543495310),Bm=new dd(null,"nil?","nil?",1612038930,null),Cm=new v(null,"poster","poster",-1616913550),Dm=new v(null,"csi-ignore","csi-ignore",-764437550),Em=new v(null,"reagent-render","reagent-render",-985383853),Fm=new v(null,"auto-play","auto-play",-645319501),Gm=new v(null,"pre.asciinema-terminal",
-"pre.asciinema-terminal",832737619),Hm=new v(null,"loading","loading",-737050189),Im=new v(null,"priority","priority",1431093715),Jm=new v(null,"auto-play?","auto-play?",385278451),Km=new dd(null,"val","val",1769233139,null),Lm=new dd(null,"not","not",1044554643,null),Mm=new v(null,"status","status",-1997798413),Nm=new v(null,"span.line","span.line",-1541583788),Om=new v(null,"response-ready","response-ready",245208276),mb=new v(null,"print-length","print-length",1931866356),Pm=new v(null,"writer",
-"writer",-277568236),Qm=new v(null,"saved","saved",288760660),Rm=new v(null,"catch-exception","catch-exception",-1997306795),Sm=new v(null,"intermediate-chars","intermediate-chars",93692085),Tm=new v(null,"auto-run","auto-run",1958400437),Um=new v(null,"reader","reader",169660853),Vm=new v(null,"div.asciinema-player","div.asciinema-player",-1293079051),Wm=new v(null,"cljsName","cljsName",999824949),Ni=new v(null,"parents","parents",-2027538891),Xm=new v(null,"parse","parse",-1162164619),Ym=new v(null,
-"author-url","author-url",1091920533),Zm=new dd(null,"/","/",-1371932971,null),$m=new v(null,"on-mouse-move","on-mouse-move",-1386320874),an=new v(null,"component-will-unmount","component-will-unmount",-2058314698),bn=new v(null,"prev","prev",-1597069226),cn=new v(null,"svg","svg",856789142),dn=new dd(null,"buf-or-n","buf-or-n",-1646815050,null),en=new v(null,"url","url",276297046),fn=new v(null,"authorURL","authorURL",549221782),gn=new v(null,"continue-block","continue-block",-1852047850),hn=new v(null,
-"loop?","loop?",457687798),jn=new v(null,"content-type","content-type",-508222634),kn=new v(null,"autoPlay","autoPlay",-561263241),ln=new v(null,"playing","playing",70013335),mn=new v(null,"display-name","display-name",694513143),nn=new v(null,"random","random",-557811113),on=new dd(null,"ifn?","ifn?",-2106461064,null),pn=new v(null,"on-dispose","on-dispose",2105306360),qn=new dd(null,"c","c",-122660552,null),rn=new v(null,"d","d",1972142424),sn=new v(null,"action","action",-811238024),tn=new v(null,
-"stdout-ch","stdout-ch",825692568),un=new v(null,"error","error",-978969032),vn=new v(null,"span.fullscreen-button","span.fullscreen-button",-1476136392),wn=new v(null,"on","on",173873944),xn=new v(null,"class-name","class-name",945142584),yn=new v(null,"componentFunction","componentFunction",825866104),zn=new v(null,"div.loader","div.loader",-1644603528),An=new v(null,"origin-mode","origin-mode",-1430095912),Bn=new v(null,"exception","exception",-335277064),Cn=new v(null,"toggle-play","toggle-play",
--1781648392),Dn=new v(null,"mouse-move","mouse-move",-1993061223),En=new v(null,"x","x",2099068185),Fn=new v(null,"__html","__html",674048345),Gn=new v(null,"fontSize","fontSize",919623033),Hn=new v(null,"uri","uri",-774711847),In=new v(null,"div.asciinema-player-wrapper","div.asciinema-player-wrapper",2009764409),Jn=new v(null,"tag","tag",-1290361223),Kn=new v(null,"startAt","startAt",849336089),Ln=new v(null,"json","json",1279968570),pf=new dd(null,"quote","quote",1377916282,null),Mn=new v(null,
-"top-margin","top-margin",655579514),Nn=new dd(null,"alt-handler","alt-handler",963786170,null),On=new v(null,"timeout","timeout",-318625318),Pn=new v(null,"seek","seek",758996602),Qn=new v(null,"recording-fn","recording-fn",860963674),of=new v(null,"arglists","arglists",1661989754),Rn=new v(null,"version","version",425292698),nf=new dd(null,"nil-iter","nil-iter",1101030523,null),Sn=new v(null,"visible","visible",-1024216805),Tn=new dd(null,"is-reagent-component","is-reagent-component",-1856228005,
-null),Un=new v(null,"hierarchy","hierarchy",-1053470341),Vn=new v(null,"on-key-down","on-key-down",-1374733765),Wn=new v(null,"connection-established","connection-established",-1403749733),qi=new v(null,"alt-impl","alt-impl",670969595),Xn=new v(null,"bg","bg",-206688421),Yn=new v(null,"other-buffer-lines","other-buffer-lines",-1562366021),Zn=new dd(null,"fn-handler","fn-handler",648785851,null),$n=new v(null,"italic","italic",32599196),ao=new dd(null,"count","count",-514511684,null),bo=new v(null,
-"dcs-ignore","dcs-ignore",198619612),co=new v(null,"handler","handler",-195596612),Ji=new v(null,"keywordize-keys","keywordize-keys",1310784252),eo=new dd(null,"takes","takes",298247964,null),fo=new dd("impl","MAX-QUEUE-SIZE","impl/MAX-QUEUE-SIZE",1508600732,null),go=new dd(null,"deref","deref",1494944732,null),ho=new dd(null,"meta22910","meta22910",1987486813,null),io=new v(null,"span.time-remaining","span.time-remaining",706865437),jo=new v(null,"with-credentials","with-credentials",-1163127235),
-ko=new v(null,"componentWillMount","componentWillMount",-285327619),lo=new v("internal","seek","internal/seek",-1958914115),mo=new v(null,"href","href",-793805698),no=new v(null,"buffer","buffer",617295198),oo=new v(null,"img","img",1442687358),po=new v(null,"speed-up","speed-up",947171774),Mf=new dd(null,"number?","number?",-1747282210,null),qo=new v(null,"stdout","stdout",-531490018),ro=new v(null,"a","a",-2123407586),so=new v(null,"dangerouslySetInnerHTML","dangerouslySetInnerHTML",-554971138),
-to=new v(null,"height","height",1025178622),Sh=new v("cljs.core","not-found","cljs.core/not-found",-1572889185),uo=new v(null,"span","span",1394872991),vo=new dd(null,"f","f",43394975,null);function wo(a){var b=new r(null,6,[kn,Fm,Gn,Wj,wm,Cm,fn,Ym,Kn,hk,mj,dm],null);return Ab.l(function(b,d){var e=S(d,0,null),f=S(d,1,null);return ve(a,e)?T.l(b,f,I.h(a,e)):b},A.l(be,a,Tg(b)),b)};function xo(a){return function(){function b(a){var b=null;if(0<arguments.length){for(var b=0,f=Array(arguments.length-0);b<f.length;)f[b]=arguments[b+0],++b;b=new B(f,0)}return c.call(this,b)}function c(b){b=Pf(b);if(H.h(R(b),1))return b=C(b),a.j?a.j(b):a.call(null,b);b=ze(b);return a.j?a.j(b):a.call(null,b)}b.J=0;b.K=function(a){a=K(a);return c(a)};b.w=c;return b}()}
-function yo(a,b,c){if("string"===typeof b)return a.replace(new RegExp(String(b).replace(/([-()\[\]{}+?*.$\^|,:#<!\\])/g,"\\$1").replace(/\x08/g,"\\x08"),"g"),c);if(b instanceof RegExp)return"string"===typeof c?a.replace(new RegExp(b.source,"g"),c):a.replace(new RegExp(b.source,"g"),xo(c));throw[y("Invalid match arg: "),y(b)].join("");}function zo(a,b){for(var c=new Ga,d=K(b);;)if(null!=d)c.append(""+y(C(d))),d=D(d),null!=d&&c.append(a);else return c.toString()}
-function Ao(a,b){a:for(var c="/(?:)/"===""+y(b)?Vd.h(ze(Md("",Me.h(y,K(a)))),""):ze((""+y(a)).split(b));;)if(""===(null==c?null:ec(c)))c=null==c?null:fc(c);else break a;return c};var Bo="undefined"!==typeof window&&null!=window.document,Co=new Vh(null,new r(null,2,["aria",null,"data",null],null),null);function Do(a){return 2>R(a)?a.toUpperCase():[y(a.substring(0,1).toUpperCase()),y(a.substring(1))].join("")}function Eo(a){if("string"===typeof a)return a;a=Ne(a);var b=Ao(a,/-/),c=S(b,0,null),b=Le(b,1);return u(Co.j?Co.j(c):Co.call(null,c))?a:A.l(y,c,Me.h(Do,b))}var Fo=!1;if("undefined"===typeof Go){var Go,Ho=rf;Go=X.j?X.j(Ho):X.call(null,Ho)}
-function Io(a,b,c){var d=zf(null);try{var e=Fo;Fo=!0;try{return Kf(d,React.render(a.A?a.A():a.call(null),b,function(){return function(){var d=Fo;Fo=!1;try{return If.G(Go,T,b,new V(null,2,5,W,[a,b],null)),null!=c?c.A?c.A():c.call(null):null}finally{Fo=d}}}(e,d)))}finally{Fo=e}}finally{u(Q.j?Q.j(d):Q.call(null,d))||null==b||(b.innerHTML="")}}function Jo(a,b){return Io(a,b,null)};var Ko;Ko;if("undefined"===typeof Lo)var Lo=!1;if("undefined"===typeof Mo)var Mo=X.j?X.j(0):X.call(null,0);function No(a,b){b.Qd=null;var c=Ko;Ko=b;try{return a.A?a.A():a.call(null)}finally{Ko=c}}function Oo(a){var b=a.Qd;a.Qd=null;return b}function Po(a){var b=Ko;if(null!=b){var c=b.Qd;b.Qd=Vd.h(null==c?Wh:c,a)}}function Qo(a,b,c,d){this.state=a;this.meta=b;this.Qc=c;this.Ja=d;this.o=2153938944;this.M=114690}h=Qo.prototype;h.T=function(a,b,c){Ac(b,"#\x3cAtom: ");vg(this.state,b,c);return Ac(b,"\x3e")};
-h.Z=function(){return this.meta};h.W=function(){return ja(this)};h.L=function(a,b){return this===b};h.we=function(a,b){if(null!=this.Qc&&!u(this.Qc.j?this.Qc.j(b):this.Qc.call(null,b)))throw Error([y("Assert failed: "),y("Validator rejected reference state"),y("\n"),y(Df.w(J([G(Nl,Hf)],0)))].join(""));var c=this.state;this.state=b;null!=this.Ja&&Cc(this,c,b);return b};h.ye=function(a,b){return Sc(this,b.j?b.j(this.state):b.call(null,this.state))};
-h.ze=function(a,b,c){return Sc(this,b.h?b.h(this.state,c):b.call(null,this.state,c))};h.Ae=function(a,b,c,d){return Sc(this,b.l?b.l(this.state,c,d):b.call(null,this.state,c,d))};h.Be=function(a,b,c,d,e){return Sc(this,A.N(b,this.state,c,d,e))};h.Kd=function(a,b,c){return Ae(function(a){return function(e,f,g){g.G?g.G(f,a,b,c):g.call(null,f,a,b,c);return null}}(this),null,this.Ja)};h.Jd=function(a,b,c){return this.Ja=T.l(this.Ja,b,c)};h.Ld=function(a,b){return this.Ja=be.h(this.Ja,b)};
-h.$b=function(){Po(this);return this.state};var Ro=function Ro(b){for(var c=[],d=arguments.length,e=0;;)if(e<d)c.push(arguments[e]),e+=1;else break;switch(c.length){case 1:return Ro.j(arguments[0]);default:return Ro.w(arguments[0],new B(c.slice(1),0))}};Ro.j=function(a){return new Qo(a,null,null,null)};Ro.w=function(a,b){var c=null!=b&&(b.o&64||b.D)?A.h(P,b):b,d=I.h(c,jb),c=I.h(c,Cf);return new Qo(a,d,c,null)};Ro.K=function(a){var b=C(a);a=D(a);return Ro.w(b,a)};Ro.J=1;So;
-var Np=function Np(b){if(null!=b&&null!=b.vf)return b.vf();var c=Np[p(null==b?null:b)];if(null!=c)return c.j?c.j(b):c.call(null,b);c=Np._;if(null!=c)return c.j?c.j(b):c.call(null,b);throw x("IDisposable.dispose!",b);},Op=function Op(b){if(null!=b&&null!=b.wf)return b.wf();var c=Op[p(null==b?null:b)];if(null!=c)return c.j?c.j(b):c.call(null,b);c=Op._;if(null!=c)return c.j?c.j(b):c.call(null,b);throw x("IRunnable.run",b);},Pp=function Pp(b,c){if(null!=b&&null!=b.Ne)return b.Ne(0,c);var d=Pp[p(null==
-b?null:b)];if(null!=d)return d.h?d.h(b,c):d.call(null,b,c);d=Pp._;if(null!=d)return d.h?d.h(b,c):d.call(null,b,c);throw x("IComputedImpl.-update-watching",b);},Qp=function Qp(b,c,d,e){if(null!=b&&null!=b.tf)return b.tf(0,0,d,e);var f=Qp[p(null==b?null:b)];if(null!=f)return f.G?f.G(b,c,d,e):f.call(null,b,c,d,e);f=Qp._;if(null!=f)return f.G?f.G(b,c,d,e):f.call(null,b,c,d,e);throw x("IComputedImpl.-handle-change",b);},Rp=function Rp(b){if(null!=b&&null!=b.uf)return b.uf();var c=Rp[p(null==b?null:b)];
-if(null!=c)return c.j?c.j(b):c.call(null,b);c=Rp._;if(null!=c)return c.j?c.j(b):c.call(null,b);throw x("IComputedImpl.-peek-at",b);};function Up(a,b,c,d,e,f,g,k,l){this.lb=a;this.state=b;this.nc=c;this.Sc=d;this.wc=e;this.Ja=f;this.pe=g;this.Xd=k;this.Wd=l;this.o=2153807872;this.M=114690}h=Up.prototype;h.tf=function(a,b,c,d){var e=this;return u(function(){var a=e.Sc;return u(a)?c!==d:a}())?(e.nc=!0,function(){var a=e.pe;return u(a)?a:Op}().call(null,this)):null};
-h.Ne=function(a,b){for(var c=K(b),d=null,e=0,f=0;;)if(f<e){var g=d.aa(null,f);ve(this.wc,g)||Dc(g,this,Qp);f+=1}else if(c=K(c))d=c,oe(d)?(c=Nc(d),f=Oc(d),d=c,e=R(c),c=f):(c=C(d),ve(this.wc,c)||Dc(c,this,Qp),c=D(d),d=null,e=0),f=0;else break;c=K(this.wc);d=null;for(f=e=0;;)if(f<e)g=d.aa(null,f),ve(b,g)||Ec(g,this),f+=1;else if(c=K(c))d=c,oe(d)?(c=Nc(d),f=Oc(d),d=c,e=R(c),c=f):(c=C(d),ve(b,c)||Ec(c,this),c=D(d),d=null,e=0),f=0;else break;return this.wc=b};
-h.uf=function(){if(tb(this.nc))return this.state;var a=Ko;Ko=null;try{return ic(this)}finally{Ko=a}};h.T=function(a,b,c){Ac(b,[y("#\x3cReaction "),y(id(this)),y(": ")].join(""));vg(this.state,b,c);return Ac(b,"\x3e")};h.W=function(){return ja(this)};h.L=function(a,b){return this===b};
-h.vf=function(){for(var a=K(this.wc),b=null,c=0,d=0;;)if(d<c){var e=b.aa(null,d);Ec(e,this);d+=1}else if(a=K(a))b=a,oe(b)?(a=Nc(b),d=Oc(b),b=a,c=R(a),a=d):(a=C(b),Ec(a,this),a=D(b),b=null,c=0),d=0;else break;this.state=this.wc=null;this.nc=!0;u(this.Sc)&&(u(Lo)&&If.h(Mo,He),this.Sc=!1);return u(this.Wd)?this.Wd.A?this.Wd.A():this.Wd.call(null):null};h.we=function(a,b){var c=this.state;this.state=b;u(this.Xd)&&(this.nc=!0,this.Xd.h?this.Xd.h(c,b):this.Xd.call(null,c,b));Cc(this,c,b);return b};
-h.ye=function(a,b){var c;c=Rp(this);c=b.j?b.j(c):b.call(null,c);return Sc(this,c)};h.ze=function(a,b,c){a=Rp(this);b=b.h?b.h(a,c):b.call(null,a,c);return Sc(this,b)};h.Ae=function(a,b,c,d){a=Rp(this);b=b.l?b.l(a,c,d):b.call(null,a,c,d);return Sc(this,b)};h.Be=function(a,b,c,d,e){return Sc(this,A.N(b,Rp(this),c,d,e))};h.wf=function(){var a=this.state,b=No(this.lb,this),c=Oo(this);!H.h(c,this.wc)&&Pp(this,c);u(this.Sc)||(u(Lo)&&If.h(Mo,Cd),this.Sc=!0);this.nc=!1;this.state=b;Cc(this,a,this.state);return b};
-h.Kd=function(a,b,c){return Ae(function(a){return function(e,f,g){g.G?g.G(f,a,b,c):g.call(null,f,a,b,c);return null}}(this),null,this.Ja)};h.Jd=function(a,b,c){return this.Ja=T.l(this.Ja,b,c)};h.Ld=function(a,b){this.Ja=be.h(this.Ja,b);return ge(this.Ja)&&tb(this.pe)?Np(this):null};h.$b=function(){var a=this.pe;if(u(u(a)?a:null!=Ko))return Po(this),u(this.nc)?Op(this):this.state;u(this.nc)&&(a=this.state,this.state=this.lb.A?this.lb.A():this.lb.call(null),a!==this.state&&Cc(this,a,this.state));return this.state};
-var So=function So(b){for(var c=[],d=arguments.length,e=0;;)if(e<d)c.push(arguments[e]),e+=1;else break;return So.w(arguments[0],1<c.length?new B(c.slice(1),0):null)};So.w=function(a,b){var c=null!=b&&(b.o&64||b.D)?A.h(P,b):b,d=I.h(c,Tm),e=I.h(c,sj),f=I.h(c,pn),c=I.h(c,bk),d=H.h(d,!0)?Op:d,g=null!=c,e=new Up(a,null,!g,g,null,null,d,e,f);null!=c&&(u(Lo)&&If.h(Mo,Cd),e.Ne(0,c));return e};So.J=1;So.K=function(a){var b=C(a);a=D(a);return So.w(b,a)};if("undefined"===typeof Vp)var Vp=0;function Wp(a){return setTimeout(a,16)}var Xp=tb(Bo)?Wp:function(){var a=window,b=a.requestAnimationFrame;if(u(b))return b;b=a.webkitRequestAnimationFrame;if(u(b))return b;b=a.mozRequestAnimationFrame;if(u(b))return b;a=a.msRequestAnimationFrame;return u(a)?a:Wp}();function Yp(a,b){return a.cljsMountOrder-b.cljsMountOrder}
-function Zp(){var a=$p;if(u(a.Oe))return null;a.Oe=!0;a=function(a){return function(){var c=a.Me,d=a.ie;a.Me=[];a.ie=[];a.Oe=!1;a:{c.sort(Yp);for(var e=c.length,f=0;;)if(f<e){var g=c[f];u(g.cljsIsDirty)&&g.forceUpdate();f+=1}else break a}a:for(c=d.length,e=0;;)if(e<c)d[e].call(null),e+=1;else break a;return null}}(a);return Xp.j?Xp.j(a):Xp.call(null,a)}var $p=new function(){this.Me=[];this.Oe=!1;this.ie=[]};function aq(a){$p.ie.push(a);Zp()}
-function bq(a){a=null==a?null:a.props;return null==a?null:a.argv}function cq(a,b){if(!u(bq(a)))throw Error([y("Assert failed: "),y(Df.w(J([G(Tn,qn)],0)))].join(""));a.cljsIsDirty=!1;var c=a.cljsRatom;if(null==c){var d=No(b,a),e=Oo(a);null!=e&&(a.cljsRatom=So.w(b,J([Tm,function(){return function(){a.cljsIsDirty=!0;$p.Me.push(a);return Zp()}}(d,e,c),bk,e],0)));return d}return Op(c)};var dq;dq;void 0;function eq(a){return ce(a)&&null!=a.cljsReactClass}
-function fq(a){for(;;){var b=a.cljsRender,c;if(ue(b))c=null;else throw Error([y("Assert failed: "),y(Df.w(J([G(on,vo)],0)))].join(""));var d=a.props,e=null==a.reagentRender?b.j?b.j(a):b.call(null,a):function(){var a=d.argv;switch(R(a)){case 1:return b.A?b.A():b.call(null);case 2:return a=Zd(a,1),b.j?b.j(a):b.call(null,a);case 3:var c=Zd(a,1),a=Zd(a,2);return b.h?b.h(c,a):b.call(null,c,a);case 4:var c=Zd(a,1),e=Zd(a,2),a=Zd(a,3);return b.l?b.l(c,e,a):b.call(null,c,e,a);case 5:var c=Zd(a,1),e=Zd(a,
-2),l=Zd(a,3),a=Zd(a,4);return b.G?b.G(c,e,l,a):b.call(null,c,e,l,a);default:return A.h(b,zg.h(a,1))}}();if(le(e))return gq(e);if(ue(e))c=u(eq(e))?function(a,b,c,d,e){return function(){function a(c){var d=null;if(0<arguments.length){for(var d=0,e=Array(arguments.length-0);d<e.length;)e[d]=arguments[d+0],++d;d=new B(e,0)}return b.call(this,d)}function b(a){a=A.l(yg,e,a);return gq(a)}a.J=0;a.K=function(a){a=K(a);return b(a)};a.w=b;return a}()}(a,b,c,d,e):e,a.cljsRender=c;else return e}}hq;
-function iq(a){var b=dq;dq=a;try{var c=[!1];try{var d=fq(a);c[0]=!0;return d}finally{if(!u(c[0])){var e=[y("Error rendering component "),y(hq.A?hq.A():hq.call(null))].join("");console.error(e)}}}finally{dq=b}}var jq=new r(null,1,[zm,function(){return tb(void 0)?cq(this,function(a){return function(){return iq(a)}}(this)):iq(this)}],null);
-function kq(a,b){var c=a instanceof v?a.ab:null;switch(c){case "getDefaultProps":throw Error([y("Assert failed: "),y("getDefaultProps not supported yet"),y("\n"),y(Df.w(J([!1],0)))].join(""));case "getInitialState":return function(){return function(){var a;a=this.cljsState;a=null!=a?a:this.cljsState=Ro.j(null);var c=b.j?b.j(this):b.call(null,this);return Ef.h?Ef.h(a,c):Ef.call(null,a,c)}}(c);case "componentWillReceiveProps":return function(){return function(a){a=a.argv;return b.h?b.h(this,a):b.call(null,
-this,a)}}(c);case "shouldComponentUpdate":return function(){return function(a){var c=Fo;if(u(c))return c;c=this.props.argv;a=a.argv;return null==b?null==c||null==a||!H.h(c,a):b.l?b.l(this,c,a):b.call(null,this,c,a)}}(c);case "componentWillUpdate":return function(){return function(a){a=a.argv;return b.h?b.h(this,a):b.call(null,this,a)}}(c);case "componentDidUpdate":return function(){return function(a){a=a.argv;return b.h?b.h(this,a):b.call(null,this,a)}}(c);case "componentWillMount":return function(){return function(){this.cljsMountOrder=
-Vp+=1;return null==b?null:b.j?b.j(this):b.call(null,this)}}(c);case "componentWillUnmount":return function(){return function(){var a=this.cljsRatom;null==a||Np(a);this.cljsIsDirty=!1;return null==b?null:b.j?b.j(this):b.call(null,this)}}(c);default:return null}}
-function lq(a){return ue(a)?function(){function b(a){var b=null;if(0<arguments.length){for(var b=0,f=Array(arguments.length-0);b<f.length;)f[b]=arguments[b+0],++b;b=new B(f,0)}return c.call(this,b)}function c(b){return A.l(a,this,b)}b.J=0;b.K=function(a){a=K(a);return c(a)};b.w=c;return b}():a}var mq=new Vh(null,new r(null,4,[kk,null,tm,null,zm,null,Wm,null],null),null);
-function nq(a,b,c){if(u(mq.j?mq.j(a):mq.call(null,a)))return ce(b)&&(b.__reactDontBind=!0),b;var d=kq(a,b);if(u(u(d)?b:d)&&!ue(b))throw Error([y("Assert failed: "),y([y("Expected function in "),y(c),y(a),y(" but got "),y(b)].join("")),y("\n"),y(Df.w(J([G(on,vo)],0)))].join(""));return u(d)?d:lq(b)}
-var oq=new r(null,3,[em,null,ko,null,Ll,null],null),pq=function(a){return function(b){return function(c){var d=I.h(Q.j?Q.j(b):Q.call(null,b),c);if(null!=d)return d;d=a.j?a.j(c):a.call(null,c);If.G(b,T,c,d);return d}}(function(){var a=rf;return X.j?X.j(a):X.call(null,a)}())}(Eo);function qq(a){return Ae(function(a,c,d){return T.l(a,Ye.j(pq.j?pq.j(c):pq.call(null,c)),d)},rf,a)}function rq(a){return Ph.w(J([oq,a],0))}
-function sq(a,b,c){a=T.w(a,kk,b,J([zm,zm.j(jq)],0));return T.l(a,Wm,function(){return function(){return c}}(a))}function tq(a){var b=function(){var b=ce(a);return b?(b=a.displayName,u(b)?b:a.name):b}();if(u(b))return b;b=function(){var b=null!=a?a.M&4096||a.af?!0:!1:!1;return b?Ne(a):b}();if(u(b))return b;b=ee(a);return ke(b)?vk.j(b):null}
-function uq(a){var b=function(){var b=yn.j(a);return null==b?a:be.h(T.l(a,tm,b),yn)}(),c=function(){var a=tm.j(b);return u(a)?a:zm.j(b)}();if(!ue(c))throw Error([y("Assert failed: "),y([y("Render must be a function, not "),y(Df.w(J([c],0)))].join("")),y("\n"),y(Df.w(J([G(on,zj)],0)))].join(""));var d=null,e=""+y(function(){var a=ck.j(b);return u(a)?a:tq(c)}()),f;if(ge(e)){f=y;var g;null==wi&&(wi=X.j?X.j(0):X.call(null,0));g=ld.j([y("reagent"),y(If.h(wi,Cd))].join(""));f=""+f(g)}else f=yo(e,/\$/,".");
-g=sq(T.l(b,ck,f),c,f);return Ae(function(a,b,c,d,e){return function(a,b,c){return T.l(a,b,nq(b,c,e))}}(b,c,d,e,f,g),rf,g)}function vq(a){return Ae(function(a,c,d){a[Ne(c)]=d;return a},{},a)}
-function wq(a){if(!ke(a))throw Error([y("Assert failed: "),y(Df.w(J([G(Fk,tj)],0)))].join(""));var b=vq(uq(rq(qq(a))));a=React.createClass(b);b=function(a,b){return function(){function a(b){var d=null;if(0<arguments.length){for(var d=0,e=Array(arguments.length-0);d<e.length;)e[d]=arguments[d+0],++d;d=new B(e,0)}return c.call(this,d)}function c(a){"undefined"!==typeof console&&console.warn([y("Warning: "),y("Calling the result of create-class as a function is "),y("deprecated in "),y(b.displayName),
-y(". Use a vector "),y("instead.")].join(""));a=A.l(yg,b,a);return gq(a)}a.J=0;a.K=function(a){a=K(a);return c(a)};a.w=c;return a}()}(b,a);b.cljsReactClass=a;a.cljsReactClass=a;return b}
-var xq=function xq(b){var c=function(){var c;c=null==b?null:b._reactInternalInstance;c=u(c)?c:b;return null==c?null:c._currentElement}(),d=function(){var b=null==c?null:c.type;return null==b?null:b.displayName}(),e=function(){var b=null==c?null:c._owner,b=null==b?null:xq(b);return null==b?null:[y(b),y(" \x3e ")].join("")}(),d=[y(e),y(d)].join("");return ge(d)?null:d};function hq(){var a=dq,b=xq(a),a=u(b)?b:null==a?null:a.cljsName();return ge(a)?"":[y(" (in "),y(a),y(")")].join("")};var yq=/([^\s\.#]+)(?:#([^\s\.#]+))?(?:\.([^\s#]+))?/;function zq(a){return a instanceof v||a instanceof dd}function Aq(a){var b=zq(a);return u(b)?b:"string"===typeof a}
-var Bq={"class":"className","for":"htmlFor",charset:"charSet"},Cq=function Cq(b){return"string"===typeof b||"number"===typeof b||ce(b)?b:u(zq(b))?Ne(b):ke(b)?Ae(function(b,d,e){if(u(zq(d))){var f;f=Ne(d);f=u(Bq.hasOwnProperty(f))?Bq[f]:null;d=null==f?Bq[Ne(d)]=Eo(d):f}b[d]=Cq(e);return b},{},b):he(b)?Ci(b):ue(b)?function(){function c(b){var c=null;if(0<arguments.length){for(var c=0,g=Array(arguments.length-0);c<g.length;)g[c]=arguments[c+0],++c;c=new B(g,0)}return d.call(this,c)}function d(c){return A.h(b,
-c)}c.J=0;c.K=function(b){b=K(b);return d(b)};c.w=d;return c}():Ci(b)},Dq=new Vh(null,new r(null,6,["url",null,"tel",null,"text",null,"textarea",null,"password",null,"search",null],null),null);
-function Eq(a){var b=a.cljsInputValue;if(null==b)return null;a.cljsInputDirty=!1;a=a.getDOMNode();var c=a.value;if(!H.h(b,c)){var d;if(d=a===document.activeElement)d=ve(Dq,a.type),d=u(d)?"string"===typeof b&&"string"===typeof c:d;if(tb(d))return a.value=b;c=R(c)-a.selectionStart;c=R(b)-c;a.value=b;a.selectionStart=c;return a.selectionEnd=c}return null}
-function Fq(a,b,c){b=b.j?b.j(c):b.call(null,c);u(a.cljsInputDirty)||(a.cljsInputDirty=!0,aq(function(){return function(){return Eq(a)}}(b)));return b}function Gq(a){var b=dq;if(u(function(){var b=a.hasOwnProperty("onChange");return u(b)?a.hasOwnProperty("value"):b}())){var c=a.value,d=null==c?"":c,e=a.onChange;b.cljsInputValue=d;delete a.value;a.defaultValue=d;a.onChange=function(a,c,d,e){return function(a){return Fq(b,e,a)}}(a,c,d,e)}else b.cljsInputValue=null}var Hq=null;Iq;
-var Jq=new r(null,4,[mn,"ReagentInput",Rk,Eq,an,function(a){return a.cljsInputValue=null},Em,function(a,b,c,d){Gq(c);return Iq.G?Iq.G(a,b,c,d):Iq.call(null,a,b,c,d)}],null);function Kq(a){if(ke(a))try{return I.h(a,Pj)}catch(b){return null}else return null}function Lq(a){var b;b=ee(a);b=null==b?null:Kq(b);return null==b?Kq(S(a,1,null)):b}var Mq={};gq;Nq;Oq;
-function gq(a){if("string"!==typeof a)if(le(a))a:for(;;){if(!(0<R(a)))throw Error([y("Assert failed: "),y([y("Hiccup form should not be empty: "),y(Df.w(J([a],0))),y(hq())].join("")),y("\n"),y(Df.w(J([G(Xj,G(ao,Ek))],0)))].join(""));var b=Zd(a,0),c;c=Aq(b);c=u(c)?c:ue(b)||!1;if(!u(c))throw Error([y("Assert failed: "),y([y("Invalid Hiccup form: "),y(Df.w(J([a],0))),y(hq())].join("")),y("\n"),y(Df.w(J([G(ij,cj)],0)))].join(""));if(u(Aq(b))){c=Ne(b);b=c.indexOf("\x3e");if(-1===b){b=u(Mq.hasOwnProperty(c))?
-Mq[c]:null;if(null==b){var b=c,d=D(gi(yq,Ne(c))),e=S(d,0,null),f=S(d,1,null),d=S(d,2,null),d=u(d)?yo(d,/\./," "):null;if(!u(e))throw Error([y("Assert failed: "),y([y("Invalid tag: '"),y(c),y("'"),y(hq())].join("")),y("\n"),y(Df.w(J([cj],0)))].join(""));b=Mq[b]={name:e,id:f,className:d}}f=b;b=f.name;e=S(a,1,null);c=null==e||ke(e);var g=c?e:null,e=f.id,f=f.className;if((d=null==e&&null==f)&&ge(g))e=null;else{var g=Cq(g),k=void 0;d?k=g:(d=null==g?{}:g,null!=e&&null==d.id&&(d.id=e),null!=f&&(e=d.className,
-d.className=null!=e?[y(f),y(" "),y(e)].join(""):f),k=d);e=k}c=c?2:1;u("input"===b||"textarea"===b)?(f=W,null==Hq&&(Hq=wq(Jq)),a=Bd(new V(null,5,5,f,[Hq,a,b,e,c],null),ee(a)),a=gq.j?gq.j(a):gq.call(null,a)):(f=void 0,f=void 0,f=ee(a),f=null==f?null:Kq(f),null!=f&&(e=null==e?{}:e,e.key=f),f=e,a=Iq.G?Iq.G(a,b,f,c):Iq.call(null,a,b,f,c));break a}a=new V(null,2,5,W,[c.substring(0,b),T.l(a,0,c.substring(b+1))],null)}else{c=b.cljsReactClass;if(null==c){if(!ue(b))throw Error([y("Assert failed: "),y([y("Expected a function, not "),
-y(Df.w(J([b],0)))].join("")),y("\n"),y(Df.w(J([G(on,vo)],0)))].join(""));ce(b)&&null!=b.type&&"undefined"!==typeof console&&console.warn([y("Warning: "),y("Using native React classes directly in Hiccup forms "),y("is not supported. Use create-element or "),y("adapt-react-class instead: "),y(b.type),y(hq())].join(""));c=ee(b);c=T.l(c,Em,b);c=wq(c).cljsReactClass;b.cljsReactClass=c}b=c;c={argv:a};a=null==a?null:Lq(a);null==a||(c.key=a);a=React.createElement(b,c);break a}}else a=se(a)?Oq.j?Oq.j(a):Oq.call(null,
-a):a;return a}function Nq(a){a=nb.j(a);for(var b=a.length,c=0;;)if(c<b)a[c]=gq(a[c]),c+=1;else break;return a}function Pq(a,b){for(var c=nb.j(a),d=c.length,e=0;;)if(e<d){var f=c[e];le(f)&&null==Lq(f)&&(b["no-key"]=!0);c[e]=gq(f);e+=1}else break;return c}
-function Oq(a){var b={},c=null==Ko?Pq(a,b):No(function(b){return function(){return Pq(a,b)}}(b),b);u(Oo(b))&&"undefined"!==typeof console&&console.warn([y("Warning: "),y("Reactive deref not supported in lazy seq, "),y("it should be wrapped in doall"),y(hq()),y(". Value:\n"),y(Df.w(J([a],0)))].join(""));u(function(){var a=tb(void 0);return a?b["no-key"]:a}())&&"undefined"!==typeof console&&console.warn([y("Warning: "),y("Every element in a seq should have a unique "),y(":key"),y(hq()),y(". Value: "),
-y(Df.w(J([a],0)))].join(""));return c}function Iq(a,b,c,d){var e=R(a)-d;switch(e){case 0:return React.createElement(b,c);case 1:return React.createElement(b,c,gq(Zd(a,d)));default:return React.createElement.apply(null,Ae(function(){return function(a,b,c){b>=d&&a.push(gq(c));return a}}(e),[b,c],a))}};function Qq(a){for(var b=[],c=arguments.length,d=0;;)if(d<c)b.push(arguments[d]),d+=1;else break;switch(b.length){case 2:return Rq(arguments[0],arguments[1]);case 3:return Sq(arguments[0],arguments[1],arguments[2]);default:throw Error([y("Invalid arity: "),y(b.length)].join(""));}}function Rq(a,b){return Sq(a,b,null)}function Sq(a,b,c){return Io(function(){var b=ce(a)?a.A?a.A():a.call(null):a;return gq(b)},b,c)}
-da("reagent.core.force_update_all",function(){for(var a=K(Ug(Q.j?Q.j(Go):Q.call(null,Go))),b=null,c=0,d=0;;)if(d<c){var e=b.aa(null,d);A.h(Jo,e);d+=1}else if(a=K(a))b=a,oe(b)?(a=Nc(b),d=Oc(b),b=a,c=R(a),a=d):(a=C(b),A.h(Jo,a),a=D(b),b=null,c=0),d=0;else break;return"Updated"});function Tq(a,b,c){a=a>b?a:b;return c<a?c:a}
-function Uq(a){var b=J([Ji,!0],0);if(null!=a?a.Mf||(a.ed?0:ub(Gi,a)):ub(Gi,a))return Hi(a,A.h(Lh,b));if(K(b)){var c=null!=b&&(b.o&64||b.D)?A.h(P,b):b,d=I.h(c,Ji);return function(a,b,c,d){return function n(m){return se(m)?fi(Me.h(n,m)):he(m)?Yf.l(Xd(m),Me.j(n),m):rb(m)?hf(Ab.l(function(){return function(a,b){return jf.h(a,n(b))}}(a,b,c,d),Fc(Wd),m)):vb(m)===Object?hf(Ab.l(function(a,b,c,d){return function(a,b){var c=d.j?d.j(b):d.call(null,b),e=n(m[b]);return Ic(a,c,e)}}(a,b,c,d),Fc(rf),pe(m))):m}}(b,
-c,d,u(d)?Ye:y)(a)}return null}function Vq(a){return function(b){return function(){return((new Date).getTime()-b.getTime())/1E3*a}}(new Date)}function Wq(a){return document[a]};var Xq=function(){var a=window.requestAnimationFrame;return u(a)?a:function(){return function(a){return a.A?a.A():a.call(null)}}(a)}();var Yq,Zq,$q,ar=function ar(b,c){if(null!=b&&null!=b.Ce)return b.Ce(0,c);var d=ar[p(null==b?null:b)];if(null!=d)return d.h?d.h(b,c):d.call(null,b,c);d=ar._;if(null!=d)return d.h?d.h(b,c):d.call(null,b,c);throw x("ReadPort.take!",b);},br=function br(b,c,d){if(null!=b&&null!=b.Pd)return b.Pd(0,c,d);var e=br[p(null==b?null:b)];if(null!=e)return e.l?e.l(b,c,d):e.call(null,b,c,d);e=br._;if(null!=e)return e.l?e.l(b,c,d):e.call(null,b,c,d);throw x("WritePort.put!",b);},cr=function cr(b){if(null!=b&&null!=
-b.Od)return b.Od();var c=cr[p(null==b?null:b)];if(null!=c)return c.j?c.j(b):c.call(null,b);c=cr._;if(null!=c)return c.j?c.j(b):c.call(null,b);throw x("Channel.close!",b);},dr=function dr(b){if(null!=b&&null!=b.wb)return b.wb(b);var c=dr[p(null==b?null:b)];if(null!=c)return c.j?c.j(b):c.call(null,b);c=dr._;if(null!=c)return c.j?c.j(b):c.call(null,b);throw x("Handler.active?",b);},er=function er(b){if(null!=b&&null!=b.ob)return b.ob(b);var c=er[p(null==b?null:b)];if(null!=c)return c.j?c.j(b):c.call(null,
-b);c=er._;if(null!=c)return c.j?c.j(b):c.call(null,b);throw x("Handler.commit",b);},fr=function fr(b){if(null!=b&&null!=b.cc)return b.cc(b);var c=fr[p(null==b?null:b)];if(null!=c)return c.j?c.j(b):c.call(null,b);c=fr._;if(null!=c)return c.j?c.j(b):c.call(null,b);throw x("Buffer.remove!",b);},gr=function gr(b,c){if(null!=b&&null!=b.Md)return b.Md(b,c);var d=gr[p(null==b?null:b)];if(null!=d)return d.h?d.h(b,c):d.call(null,b,c);d=gr._;if(null!=d)return d.h?d.h(b,c):d.call(null,b,c);throw x("Buffer.add!*",
-b);},hr=function hr(b){for(var c=[],d=arguments.length,e=0;;)if(e<d)c.push(arguments[e]),e+=1;else break;switch(c.length){case 1:return hr.j(arguments[0]);case 2:return hr.h(arguments[0],arguments[1]);default:throw Error([y("Invalid arity: "),y(c.length)].join(""));}};hr.j=function(a){return a};hr.h=function(a,b){if(null==b)throw Error([y("Assert failed: "),y(Df.w(J([G(Lm,G(Bm,jj))],0)))].join(""));return gr(a,b)};hr.J=2;function ir(a,b,c,d,e){for(var f=0;;)if(f<e)c[d+f]=a[b+f],f+=1;else break}function jr(a,b,c,d){this.head=a;this.ga=b;this.length=c;this.v=d}jr.prototype.pop=function(){if(0===this.length)return null;var a=this.v[this.ga];this.v[this.ga]=null;this.ga=(this.ga+1)%this.v.length;--this.length;return a};jr.prototype.unshift=function(a){this.v[this.head]=a;this.head=(this.head+1)%this.v.length;this.length+=1;return null};function kr(a,b){a.length+1===a.v.length&&a.resize();a.unshift(b)}
-jr.prototype.resize=function(){var a=Array(2*this.v.length);return this.ga<this.head?(ir(this.v,this.ga,a,0,this.length),this.ga=0,this.head=this.length,this.v=a):this.ga>this.head?(ir(this.v,this.ga,a,0,this.v.length-this.ga),ir(this.v,0,a,this.v.length-this.ga,this.head),this.ga=0,this.head=this.length,this.v=a):this.ga===this.head?(this.head=this.ga=0,this.v=a):null};function lr(a,b){for(var c=a.length,d=0;;)if(d<c){var e=a.pop();(b.j?b.j(e):b.call(null,e))&&a.unshift(e);d+=1}else break}
-function mr(a){if(!(0<a))throw Error([y("Assert failed: "),y("Can't create a ring buffer of size 0"),y("\n"),y(Df.w(J([G(el,Nf,0)],0)))].join(""));return new jr(0,0,0,Array(a))}function nr(a,b){this.U=a;this.n=b;this.o=2;this.M=0}nr.prototype.Nd=function(){return this.U.length===this.n};nr.prototype.cc=function(){return this.U.pop()};nr.prototype.Md=function(a,b){kr(this.U,b);return this};nr.prototype.ia=function(){return this.U.length};function or(a,b){this.U=a;this.n=b;this.o=2;this.M=0}
-or.prototype.Nd=function(){return!1};or.prototype.cc=function(){return this.U.pop()};or.prototype.Md=function(a,b){this.U.length!==this.n&&this.U.unshift(b);return this};or.prototype.ia=function(){return this.U.length};function pr(a,b){this.U=a;this.n=b;this.o=2;this.M=0}pr.prototype.Nd=function(){return!1};pr.prototype.cc=function(){return this.U.pop()};pr.prototype.Md=function(a,b){this.U.length===this.n&&fr(this);this.U.unshift(b);return this};pr.prototype.ia=function(){return this.U.length};
-if("undefined"===typeof qr)var qr={};var rr;a:{var sr=ca.navigator;if(sr){var tr=sr.userAgent;if(tr){rr=tr;break a}}rr=""}function ur(a){return-1!=rr.indexOf(a)};var vr;
-function wr(){var a=ca.MessageChannel;"undefined"===typeof a&&"undefined"!==typeof window&&window.postMessage&&window.addEventListener&&!ur("Presto")&&(a=function(){var a=document.createElement("IFRAME");a.style.display="none";a.src="";document.documentElement.appendChild(a);var b=a.contentWindow,a=b.document;a.open();a.write("");a.close();var c="callImmediate"+Math.random(),d="file:"==b.location.protocol?"*":b.location.protocol+"//"+b.location.host,a=pa(function(a){if(("*"==d||a.origin==d)&&a.data==
-c)this.port1.onmessage()},this);b.addEventListener("message",a,!1);this.port1={};this.port2={postMessage:function(){b.postMessage(c,d)}}});if("undefined"!==typeof a&&!ur("Trident")&&!ur("MSIE")){var b=new a,c={},d=c;b.port1.onmessage=function(){if(void 0!==c.next){c=c.next;var a=c.Vc;c.Vc=null;a()}};return function(a){d.next={Vc:a};d=d.next;b.port2.postMessage(0)}}return"undefined"!==typeof document&&"onreadystatechange"in document.createElement("SCRIPT")?function(a){var b=document.createElement("SCRIPT");
-b.onreadystatechange=function(){b.onreadystatechange=null;b.parentNode.removeChild(b);b=null;a();a=null};document.documentElement.appendChild(b)}:function(a){ca.setTimeout(a,0)}};var xr=mr(32),yr=!1,zr=!1;Ar;function Br(){yr=!0;zr=!1;for(var a=0;;){var b=xr.pop();if(null!=b&&(b.A?b.A():b.call(null),1024>a)){a+=1;continue}break}yr=!1;return 0<xr.length?Ar.A?Ar.A():Ar.call(null):null}function Ar(){var a=zr;if(u(u(a)?yr:a))return null;zr=!0;!ia(ca.setImmediate)||ca.Window&&ca.Window.prototype&&ca.Window.prototype.setImmediate==ca.setImmediate?(vr||(vr=wr()),vr(Br)):ca.setImmediate(Br)}function Cr(a){kr(xr,a);Ar()}function Dr(a,b){setTimeout(a,b)};var Er,Fr=function Fr(b){"undefined"===typeof Er&&(Er=function(b,d,e){this.Hf=b;this.I=d;this.bg=e;this.o=425984;this.M=0},Er.prototype.ba=function(b,d){return new Er(this.Hf,this.I,d)},Er.prototype.Z=function(){return this.bg},Er.prototype.$b=function(){return this.I},Er.kd=function(){return new V(null,3,5,W,[Bd(qm,new r(null,1,[of,G(pf,G(new V(null,1,5,W,[Km],null)))],null)),Km,ho],null)},Er.mc=!0,Er.Tb="cljs.core.async.impl.channels/t_cljs$core$async$impl$channels22909",Er.Fc=function(b,d){return Ac(d,
-"cljs.core.async.impl.channels/t_cljs$core$async$impl$channels22909")});return new Er(Fr,b,rf)};function Gr(a,b){this.rb=a;this.I=b}function Hr(a){return dr(a.rb)}var Ir=function Ir(b){if(null!=b&&null!=b.ef)return b.ef();var c=Ir[p(null==b?null:b)];if(null!=c)return c.j?c.j(b):c.call(null,b);c=Ir._;if(null!=c)return c.j?c.j(b):c.call(null,b);throw x("MMC.abort",b);};function Jr(a,b,c,d,e,f,g){this.vc=a;this.Sd=b;this.gc=c;this.Rd=d;this.U=e;this.closed=f;this.Ab=g}
-Jr.prototype.ef=function(){for(;;){var a=this.gc.pop();if(null!=a){var b=a.rb,c=a.I;if(b.wb(null)){var d=b.ob(null);Cr(function(a){return function(){return a.j?a.j(!0):a.call(null,!0)}}(d,b,c,a,this))}else continue}break}lr(this.gc,wf());return cr(this)};
-Jr.prototype.Pd=function(a,b,c){var d=this;if(null==b)throw Error([y("Assert failed: "),y("Can't put nil in on a channel"),y("\n"),y(Df.w(J([G(Lm,G(Bm,Km))],0)))].join(""));if((a=d.closed)||!c.wb(null))return Fr(!a);if(u(function(){var a=d.U;return u(a)?tb(d.U.Nd(null)):a}())){c.ob(null);for(c=Ed(d.Ab.h?d.Ab.h(d.U,b):d.Ab.call(null,d.U,b));;){if(0<d.vc.length&&0<R(d.U)){var e=d.vc.pop();if(e.wb(null)){var f=e.ob(null),g=d.U.cc(null);Cr(function(a,b){return function(){return a.j?a.j(b):a.call(null,
-b)}}(f,g,e,c,a,this))}else continue}break}c&&Ir(this);return Fr(!0)}e=function(){for(;;){var a=d.vc.pop();if(u(a)){if(u(a.wb(null)))return a}else return null}}();if(u(e))return f=er(e),c.ob(null),Cr(function(a){return function(){return a.j?a.j(b):a.call(null,b)}}(f,e,a,this)),Fr(!0);64<d.Rd?(d.Rd=0,lr(d.gc,Hr)):d.Rd+=1;if(u(c.cd(null))){if(!(1024>d.gc.length))throw Error([y("Assert failed: "),y([y("No more than "),y(1024),y(" pending puts are allowed on a single channel."),y(" Consider using a windowed buffer.")].join("")),
-y("\n"),y(Df.w(J([G(Fj,G(nj,vj),fo)],0)))].join(""));kr(d.gc,new Gr(c,b))}return null};
-Jr.prototype.Ce=function(a,b){var c=this;if(b.wb(null)){if(null!=c.U&&0<R(c.U)){for(var d=b.ob(null),e=Fr(c.U.cc(null));;){if(!u(c.U.Nd(null))){var f=c.gc.pop();if(null!=f){var g=f.rb,k=f.I;if(g.wb(null)){var l=g.ob(null);b.ob(null);Cr(function(a){return function(){return a.j?a.j(!0):a.call(null,!0)}}(l,g,k,f,d,e,this));Ed(c.Ab.h?c.Ab.h(c.U,k):c.Ab.call(null,c.U,k))&&Ir(this)}continue}}break}return e}d=function(){for(;;){var a=c.gc.pop();if(u(a)){if(dr(a.rb))return a}else return null}}();if(u(d))return e=
-er(d.rb),b.ob(null),Cr(function(a){return function(){return a.j?a.j(!0):a.call(null,!0)}}(e,d,this)),Fr(d.I);if(u(c.closed))return u(c.U)&&(c.Ab.j?c.Ab.j(c.U):c.Ab.call(null,c.U)),u(function(){var a=b.wb(null);return u(a)?b.ob(null):a}())?(d=function(){var a=c.U;return u(a)?0<R(c.U):a}(),d=u(d)?c.U.cc(null):null,Fr(d)):null;64<c.Sd?(c.Sd=0,lr(c.vc,dr)):c.Sd+=1;if(u(b.cd(null))){if(!(1024>c.vc.length))throw Error([y("Assert failed: "),y([y("No more than "),y(1024),y(" pending takes are allowed on a single channel.")].join("")),
-y("\n"),y(Df.w(J([G(Fj,G(nj,eo),fo)],0)))].join(""));kr(c.vc,b)}}return null};Jr.prototype.Od=function(){var a=this;if(!a.closed)for(a.closed=!0,u(function(){var b=a.U;return u(b)?0===a.gc.length:b}())&&(a.Ab.j?a.Ab.j(a.U):a.Ab.call(null,a.U));;){var b=a.vc.pop();if(null==b)break;else if(b.wb(null)){var c=b.ob(null),d=u(function(){var b=a.U;return u(b)?0<R(a.U):b}())?a.U.cc(null):null;Cr(function(a,b){return function(){return a.j?a.j(b):a.call(null,b)}}(c,d,b,this))}}return null};
-function Kr(a){console.log(a);return null}function Lr(a,b){var c=(u(null)?null:Kr).call(null,b);return null==c?a:hr.h(a,c)}
-function Mr(a,b){return new Jr(mr(32),0,mr(32),0,a,!1,function(){return function(a){return function(){function b(d,e){try{return a.h?a.h(d,e):a.call(null,d,e)}catch(f){return Lr(d,f)}}function e(b){try{return a.j?a.j(b):a.call(null,b)}catch(d){return Lr(b,d)}}var f=null,f=function(a,c){switch(arguments.length){case 1:return e.call(this,a);case 2:return b.call(this,a,c)}throw Error("Invalid arity: "+arguments.length);};f.j=e;f.h=b;return f}()}(u(b)?b.j?b.j(hr):b.call(null,hr):hr)}())};var Nr,Or=function Or(b){"undefined"===typeof Nr&&(Nr=function(b,d,e){this.Yf=b;this.lb=d;this.cg=e;this.o=393216;this.M=0},Nr.prototype.ba=function(b,d){return new Nr(this.Yf,this.lb,d)},Nr.prototype.Z=function(){return this.cg},Nr.prototype.wb=function(){return!0},Nr.prototype.cd=function(){return!0},Nr.prototype.ob=function(){return this.lb},Nr.kd=function(){return new V(null,3,5,W,[Bd(Zn,new r(null,2,[Rj,!0,of,G(pf,G(new V(null,1,5,W,[vo],null)))],null)),vo,Pl],null)},Nr.mc=!0,Nr.Tb="cljs.core.async.impl.ioc-helpers/t_cljs$core$async$impl$ioc_helpers25545",
-Nr.Fc=function(b,d){return Ac(d,"cljs.core.async.impl.ioc-helpers/t_cljs$core$async$impl$ioc_helpers25545")});return new Nr(Or,b,rf)};function Pr(a){try{return a[0].call(null,a)}catch(b){throw b instanceof Object&&a[6].Od(),b;}}function Qr(a,b,c){c=c.Ce(0,Or(function(c){a[2]=c;a[1]=b;return Pr(a)}));return u(c)?(a[2]=Q.j?Q.j(c):Q.call(null,c),a[1]=b,Y):null}function Rr(a,b,c,d){c=c.Pd(0,d,Or(function(c){a[2]=c;a[1]=b;return Pr(a)}));return u(c)?(a[2]=Q.j?Q.j(c):Q.call(null,c),a[1]=b,Y):null}
-function Sr(a,b){var c=a[6];null!=b&&c.Pd(0,b,Or(function(){return function(){return null}}(c)));c.Od();return c}
-function Tr(a){for(;;){var b=a[4],c=Yk.j(b),d=Rm.j(b),e=a[5];if(u(function(){var a=e;return u(a)?tb(b):a}()))throw e;if(u(function(){var a=e;return u(a)?(a=c,u(a)?H.h(jk,d)||e instanceof d:a):a}())){a[1]=c;a[2]=e;a[5]=null;a[4]=T.w(b,Yk,null,J([Rm,null],0));break}if(u(function(){var a=e;return u(a)?tb(c)&&tb(ok.j(b)):a}()))a[4]=bn.j(b);else{if(u(function(){var a=e;return u(a)?(a=tb(c))?ok.j(b):a:a}())){a[1]=ok.j(b);a[4]=T.l(b,ok,null);break}if(u(function(){var a=tb(e);return a?ok.j(b):a}())){a[1]=
-ok.j(b);a[4]=T.l(b,ok,null);break}if(tb(e)&&tb(ok.j(b))){a[1]=gn.j(b);a[4]=bn.j(b);break}throw Error("No matching clause");}}};function Ur(a,b,c){this.key=a;this.I=b;this.forward=c;this.o=2155872256;this.M=0}Ur.prototype.fa=function(){return Jb(Jb(nd,this.I),this.key)};Ur.prototype.T=function(a,b,c){return ug(b,vg,"["," ","]",c,this)};function Vr(a,b,c){c=Array(c+1);for(var d=0;;)if(d<c.length)c[d]=null,d+=1;else break;return new Ur(a,b,c)}function Wr(a,b,c,d){for(;;){if(0>c)return a;a:for(;;){var e=a.forward[c];if(u(e))if(e.key<b)a=e;else break a;else break a}null!=d&&(d[c]=a);--c}}
-function Xr(a,b){this.header=a;this.level=b;this.o=2155872256;this.M=0}Xr.prototype.put=function(a,b){var c=Array(15),d=Wr(this.header,a,this.level,c).forward[0];if(null!=d&&d.key===a)return d.I=b;a:for(d=0;;)if(.5>Math.random()&&15>d)d+=1;else break a;if(d>this.level){for(var e=this.level+1;;)if(e<=d+1)c[e]=this.header,e+=1;else break;this.level=d}for(d=Vr(a,b,Array(d));;)return 0<=this.level?(c=c[0].forward,d.forward[0]=c[0],c[0]=d):null};
-Xr.prototype.remove=function(a){var b=Array(15),c=Wr(this.header,a,this.level,b).forward[0];if(null!=c&&c.key===a){for(a=0;;)if(a<=this.level){var d=b[a].forward;d[a]===c&&(d[a]=c.forward[a]);a+=1}else break;for(;;)if(0<this.level&&null==this.header.forward[this.level])--this.level;else return null}else return null};
-function Yr(a){for(var b=Zr,c=b.header,d=b.level;;){if(0>d)return c===b.header?null:c;var e;a:for(e=c;;){e=e.forward[d];if(null==e){e=null;break a}if(e.key>=a)break a}null!=e?(--d,c=e):--d}}Xr.prototype.fa=function(){return function(a){return function c(d){return new Ze(null,function(){return function(){return null==d?null:Md(new V(null,2,5,W,[d.key,d.I],null),c(d.forward[0]))}}(a),null,null)}}(this)(this.header.forward[0])};
-Xr.prototype.T=function(a,b,c){return ug(b,function(){return function(a){return ug(b,vg,""," ","",c,a)}}(this),"{",", ","}",c,this)};var Zr=new Xr(Vr(null,null,0),0);function $r(a){var b=(new Date).valueOf()+a,c=Yr(b),d=u(u(c)?c.key<b+10:c)?c.I:null;if(u(d))return d;var e=Mr(null,null);Zr.put(b,e);Dr(function(a,b,c){return function(){Zr.remove(c);return cr(a)}}(e,d,b,c),a);return e};function as(a){var b=bs;"undefined"===typeof Yq&&(Yq=function(a,b,e){this.lb=a;this.We=b;this.dg=e;this.o=393216;this.M=0},Yq.prototype.ba=function(a,b){return new Yq(this.lb,this.We,b)},Yq.prototype.Z=function(){return this.dg},Yq.prototype.wb=function(){return!0},Yq.prototype.cd=function(){return this.We},Yq.prototype.ob=function(){return this.lb},Yq.kd=function(){return new V(null,3,5,W,[vo,Nj,Zk],null)},Yq.mc=!0,Yq.Tb="cljs.core.async/t_cljs$core$async25691",Yq.Fc=function(a,b){return Ac(b,"cljs.core.async/t_cljs$core$async25691")});
-return new Yq(b,a,rf)}function cs(a){return ds(a,null)}function ds(a,b){var c=H.h(a,0)?null:a;if(u(b)&&!u(c))throw Error([y("Assert failed: "),y("buffer must be supplied when transducer is"),y("\n"),y(Df.w(J([dn],0)))].join(""));c="number"===typeof c?new nr(mr(c),c):c;return Mr(c,b)}function bs(){return null}var es=as(!0);function fs(a,b){var c=br(a,b,es);return u(c)?Q.j?Q.j(c):Q.call(null,c):!0}
-function gs(a){for(var b=Array(a),c=0;;)if(c<a)b[c]=0,c+=1;else break;for(c=1;;){if(H.h(c,a))return b;var d=Math.floor(Math.random()*c);b[c]=b[d];b[d]=c;c+=1}}
-var hs=function hs(){var b=X.j?X.j(!0):X.call(null,!0);"undefined"===typeof Zq&&(Zq=function(b,d,e){this.Ff=b;this.Vb=d;this.eg=e;this.o=393216;this.M=0},Zq.prototype.ba=function(){return function(b,d){return new Zq(this.Ff,this.Vb,d)}}(b),Zq.prototype.Z=function(){return function(){return this.eg}}(b),Zq.prototype.wb=function(){return function(){return Q.j?Q.j(this.Vb):Q.call(null,this.Vb)}}(b),Zq.prototype.cd=function(){return function(){return!0}}(b),Zq.prototype.ob=function(){return function(){Ef.h?
-Ef.h(this.Vb,null):Ef.call(null,this.Vb,null);return!0}}(b),Zq.kd=function(){return function(){return new V(null,3,5,W,[Bd(Sl,new r(null,2,[Rj,!0,of,G(pf,G(Wd))],null)),gm,Kj],null)}}(b),Zq.mc=!0,Zq.Tb="cljs.core.async/t_cljs$core$async25736",Zq.Fc=function(){return function(b,d){return Ac(d,"cljs.core.async/t_cljs$core$async25736")}}(b));return new Zq(hs,b,rf)},is=function is(b,c){"undefined"===typeof $q&&($q=function(b,c,f,g){this.Gf=b;this.Vb=c;this.Vc=f;this.fg=g;this.o=393216;this.M=0},$q.prototype.ba=
-function(b,c){return new $q(this.Gf,this.Vb,this.Vc,c)},$q.prototype.Z=function(){return this.fg},$q.prototype.wb=function(){return dr(this.Vb)},$q.prototype.cd=function(){return!0},$q.prototype.ob=function(){er(this.Vb);return this.Vc},$q.kd=function(){return new V(null,4,5,W,[Bd(Nn,new r(null,2,[Rj,!0,of,G(pf,G(new V(null,2,5,W,[gm,pk],null)))],null)),gm,pk,Ej],null)},$q.mc=!0,$q.Tb="cljs.core.async/t_cljs$core$async25742",$q.Fc=function(b,c){return Ac(c,"cljs.core.async/t_cljs$core$async25742")});
-return new $q(is,b,c,rf)};
-function js(a,b,c){var d=hs(),e=R(b),f=gs(e),g=Im.j(c),k=function(){for(var c=0;;)if(c<e){var k=u(g)?c:f[c],m=Zd(b,k),t=le(m)?m.j?m.j(0):m.call(null,0):null,q=u(t)?function(){var b=m.j?m.j(1):m.call(null,1);return br(t,b,is(d,function(b,c,d,e,f){return function(b){b=new V(null,2,5,W,[b,f],null);return a.j?a.j(b):a.call(null,b)}}(c,b,k,m,t,d,e,f,g)))}():ar(m,is(d,function(b,c,d){return function(b){b=new V(null,2,5,W,[b,d],null);return a.j?a.j(b):a.call(null,b)}}(c,k,m,t,d,e,f,g)));if(u(q))return Fr(new V(null,
-2,5,W,[Q.j?Q.j(q):Q.call(null,q),function(){var a=t;return u(a)?a:m}()],null));c+=1}else return null}();return u(k)?k:ve(c,jk)&&(k=function(){var a=dr(d);return u(a)?er(d):a}(),u(k))?Fr(new V(null,2,5,W,[jk.j(c),jk],null)):null}function ks(a){a=ar(a,as(!1));return u(a)?Q.j?Q.j(a):Q.call(null,a):null}function ls(a){for(var b=[],c=arguments.length,d=0;;)if(d<c)b.push(arguments[d]),d+=1;else break;return ms(arguments[0],arguments[1],arguments[2],3<b.length?new B(b.slice(3),0):null)}
-function ms(a,b,c,d){var e=null!=d&&(d.o&64||d.D)?A.h(P,d):d;a[1]=b;b=js(function(){return function(b){a[2]=b;return Pr(a)}}(d,e,e),c,e);return u(b)?(a[2]=Q.j?Q.j(b):Q.call(null,b),Y):null};var ns=ur("Opera")||ur("OPR"),os=ur("Trident")||ur("MSIE"),ps=ur("Edge"),qs=ur("Gecko")&&!(-1!=rr.toLowerCase().indexOf("webkit")&&!ur("Edge"))&&!(ur("Trident")||ur("MSIE"))&&!ur("Edge"),rs=-1!=rr.toLowerCase().indexOf("webkit")&&!ur("Edge");function ss(){var a=rr;if(qs)return/rv\:([^\);]+)(\)|;)/.exec(a);if(ps)return/Edge\/([\d\.]+)/.exec(a);if(os)return/\b(?:MSIE|rv)[: ]([^\);]+)(\)|;)/.exec(a);if(rs)return/WebKit\/(\S+)/.exec(a)}function ts(){var a=ca.document;return a?a.documentMode:void 0}
-var us=function(){if(ns&&ca.opera){var a;var b=ca.opera.version;try{a=b()}catch(c){a=b}return a}a="";(b=ss())&&(a=b?b[1]:"");return os&&(b=ts(),b>parseFloat(a))?String(b):a}(),vs={};
-function ws(a){var b;if(!(b=vs[a])){b=0;for(var c=ta(String(us)).split("."),d=ta(String(a)).split("."),e=Math.max(c.length,d.length),f=0;0==b&&f<e;f++){var g=c[f]||"",k=d[f]||"",l=RegExp("(\\d*)(\\D*)","g"),n=RegExp("(\\d*)(\\D*)","g");do{var m=l.exec(g)||["","",""],t=n.exec(k)||["","",""];if(0==m[0].length&&0==t[0].length)break;b=ua(0==m[1].length?0:parseInt(m[1],10),0==t[1].length?0:parseInt(t[1],10))||ua(0==m[2].length,0==t[2].length)||ua(m[2],t[2])}while(0==b)}b=vs[a]=0<=b}return b}
-var xs=ca.document,ys=xs&&os?ts()||("CSS1Compat"==xs.compatMode?parseInt(us,10):5):void 0;var zs;(zs=!os)||(zs=9<=ys);var As=zs,Bs=os&&!ws("9");!rs||ws("528");qs&&ws("1.9b")||os&&ws("8")||ns&&ws("9.5")||rs&&ws("528");qs&&!ws("8")||os&&ws("9");function Cs(){0!=Ds&&ja(this);this.Ee=this.Ee;this.ig=this.ig}var Ds=0;Cs.prototype.Ee=!1;function Es(a,b){this.type=a;this.currentTarget=this.target=b;this.defaultPrevented=this.uc=!1;this.yf=!0}Es.prototype.stopPropagation=function(){this.uc=!0};Es.prototype.preventDefault=function(){this.defaultPrevented=!0;this.yf=!1};function Fs(a){Fs[" "](a);return a}Fs[" "]=ea;function Gs(a,b){Es.call(this,a?a.type:"");this.relatedTarget=this.currentTarget=this.target=null;this.charCode=this.keyCode=this.button=this.screenY=this.screenX=this.clientY=this.clientX=this.offsetY=this.offsetX=0;this.metaKey=this.shiftKey=this.altKey=this.ctrlKey=!1;this.hd=this.state=null;a&&this.Jc(a,b)}ra(Gs,Es);
-Gs.prototype.Jc=function(a,b){var c=this.type=a.type,d=a.changedTouches?a.changedTouches[0]:null;this.target=a.target||a.srcElement;this.currentTarget=b;var e=a.relatedTarget;if(e){if(qs){var f;a:{try{Fs(e.nodeName);f=!0;break a}catch(g){}f=!1}f||(e=null)}}else"mouseover"==c?e=a.fromElement:"mouseout"==c&&(e=a.toElement);this.relatedTarget=e;null===d?(this.offsetX=rs||void 0!==a.offsetX?a.offsetX:a.layerX,this.offsetY=rs||void 0!==a.offsetY?a.offsetY:a.layerY,this.clientX=void 0!==a.clientX?a.clientX:
-a.pageX,this.clientY=void 0!==a.clientY?a.clientY:a.pageY,this.screenX=a.screenX||0,this.screenY=a.screenY||0):(this.clientX=void 0!==d.clientX?d.clientX:d.pageX,this.clientY=void 0!==d.clientY?d.clientY:d.pageY,this.screenX=d.screenX||0,this.screenY=d.screenY||0);this.button=a.button;this.keyCode=a.keyCode||0;this.charCode=a.charCode||("keypress"==c?a.keyCode:0);this.ctrlKey=a.ctrlKey;this.altKey=a.altKey;this.shiftKey=a.shiftKey;this.metaKey=a.metaKey;this.state=a.state;this.hd=a;a.defaultPrevented&&
-this.preventDefault()};Gs.prototype.stopPropagation=function(){Gs.Bf.stopPropagation.call(this);this.hd.stopPropagation?this.hd.stopPropagation():this.hd.cancelBubble=!0};Gs.prototype.preventDefault=function(){Gs.Bf.preventDefault.call(this);var a=this.hd;if(a.preventDefault)a.preventDefault();else if(a.returnValue=!1,Bs)try{if(a.ctrlKey||112<=a.keyCode&&123>=a.keyCode)a.keyCode=-1}catch(b){}};var Hs="closure_listenable_"+(1E6*Math.random()|0),Is=0;function Js(a,b,c,d,e){this.listener=a;this.$d=null;this.src=b;this.type=c;this.Uc=!!d;this.rb=e;this.key=++Is;this.Nc=this.Gd=!1}function Ks(a){a.Nc=!0;a.listener=null;a.$d=null;a.src=null;a.rb=null};function Ls(a){this.src=a;this.zb={};this.ce=0}Ls.prototype.add=function(a,b,c,d,e){var f=a.toString();a=this.zb[f];a||(a=this.zb[f]=[],this.ce++);var g=Ms(a,b,d,e);-1<g?(b=a[g],c||(b.Gd=!1)):(b=new Js(b,this.src,f,!!d,e),b.Gd=c,a.push(b));return b};Ls.prototype.remove=function(a,b,c,d){a=a.toString();if(!(a in this.zb))return!1;var e=this.zb[a];b=Ms(e,b,c,d);return-1<b?(Ks(e[b]),Ka.splice.call(e,b,1),0==e.length&&(delete this.zb[a],this.ce--),!0):!1};
-function Ns(a,b){var c=b.type;if(c in a.zb){var d=a.zb[c],e=La(d,b),f;(f=0<=e)&&Ka.splice.call(d,e,1);f&&(Ks(b),0==a.zb[c].length&&(delete a.zb[c],a.ce--))}}Ls.prototype.Ge=function(a,b,c,d){a=this.zb[a.toString()];var e=-1;a&&(e=Ms(a,b,c,d));return-1<e?a[e]:null};Ls.prototype.hasListener=function(a,b){var c=void 0!==a,d=c?a.toString():"",e=void 0!==b;return za(this.zb,function(a){for(var g=0;g<a.length;++g)if(!(c&&a[g].type!=d||e&&a[g].Uc!=b))return!0;return!1})};
-function Ms(a,b,c,d){for(var e=0;e<a.length;++e){var f=a[e];if(!f.Nc&&f.listener==b&&f.Uc==!!c&&f.rb==d)return e}return-1};var Os="closure_lm_"+(1E6*Math.random()|0),Ps={},Qs=0;
-function Rs(a,b,c,d,e){if(fa(b))for(var f=0;f<b.length;f++)Rs(a,b[f],c,d,e);else if(c=Ss(c),a&&a[Hs])a.pc.add(String(b),c,!1,d,e);else{if(!b)throw Error("Invalid event type");var f=!!d,g=Ts(a);g||(a[Os]=g=new Ls(a));c=g.add(b,c,!1,d,e);if(!c.$d){d=Us();c.$d=d;d.src=a;d.listener=c;if(a.addEventListener)a.addEventListener(b.toString(),d,f);else if(a.attachEvent)a.attachEvent(Vs(b.toString()),d);else throw Error("addEventListener and attachEvent are unavailable.");Qs++}}}
-function Us(){var a=Ws,b=As?function(c){return a.call(b.src,b.listener,c)}:function(c){c=a.call(b.src,b.listener,c);if(!c)return c};return b}function Xs(a,b,c,d,e){if(fa(b))for(var f=0;f<b.length;f++)Xs(a,b[f],c,d,e);else c=Ss(c),a&&a[Hs]?a.pc.remove(String(b),c,d,e):a&&(a=Ts(a))&&(b=a.Ge(b,c,!!d,e))&&Ys(b)}
-function Ys(a){if("number"!=typeof a&&a&&!a.Nc){var b=a.src;if(b&&b[Hs])Ns(b.pc,a);else{var c=a.type,d=a.$d;b.removeEventListener?b.removeEventListener(c,d,a.Uc):b.detachEvent&&b.detachEvent(Vs(c),d);Qs--;(c=Ts(b))?(Ns(c,a),0==c.ce&&(c.src=null,b[Os]=null)):Ks(a)}}}function Vs(a){return a in Ps?Ps[a]:Ps[a]="on"+a}function Zs(a,b,c,d){var e=!0;if(a=Ts(a))if(b=a.zb[b.toString()])for(b=b.concat(),a=0;a<b.length;a++){var f=b[a];f&&f.Uc==c&&!f.Nc&&(f=$s(f,d),e=e&&!1!==f)}return e}
-function $s(a,b){var c=a.listener,d=a.rb||a.src;a.Gd&&Ys(a);return c.call(d,b)}
-function Ws(a,b){if(a.Nc)return!0;if(!As){var c;if(!(c=b))a:{c=["window","event"];for(var d=ca,e;e=c.shift();)if(null!=d[e])d=d[e];else{c=null;break a}c=d}e=c;c=new Gs(e,this);d=!0;if(!(0>e.keyCode||void 0!=e.returnValue)){a:{var f=!1;if(0==e.keyCode)try{e.keyCode=-1;break a}catch(l){f=!0}if(f||void 0==e.returnValue)e.returnValue=!0}e=[];for(f=c.currentTarget;f;f=f.parentNode)e.push(f);for(var f=a.type,g=e.length-1;!c.uc&&0<=g;g--){c.currentTarget=e[g];var k=Zs(e[g],f,!0,c),d=d&&k}for(g=0;!c.uc&&
-g<e.length;g++)c.currentTarget=e[g],k=Zs(e[g],f,!1,c),d=d&&k}return d}return $s(a,new Gs(b,this))}function Ts(a){a=a[Os];return a instanceof Ls?a:null}var at="__closure_events_fn_"+(1E9*Math.random()>>>0);function Ss(a){if(ia(a))return a;a[at]||(a[at]=function(b){return a.handleEvent(b)});return a[at]};function bt(){Cs.call(this);this.pc=new Ls(this);this.Ef=this;this.sf=null}ra(bt,Cs);bt.prototype[Hs]=!0;h=bt.prototype;h.addEventListener=function(a,b,c,d){Rs(this,a,b,c,d)};h.removeEventListener=function(a,b,c,d){Xs(this,a,b,c,d)};
-h.dispatchEvent=function(a){var b,c=this.sf;if(c)for(b=[];c;c=c.sf)b.push(c);var c=this.Ef,d=a.type||a;if(ha(a))a=new Es(a,c);else if(a instanceof Es)a.target=a.target||c;else{var e=a;a=new Es(d,c);Fa(a,e)}var e=!0,f;if(b)for(var g=b.length-1;!a.uc&&0<=g;g--)f=a.currentTarget=b[g],e=ct(f,d,!0,a)&&e;a.uc||(f=a.currentTarget=c,e=ct(f,d,!0,a)&&e,a.uc||(e=ct(f,d,!1,a)&&e));if(b)for(g=0;!a.uc&&g<b.length;g++)f=a.currentTarget=b[g],e=ct(f,d,!1,a)&&e;return e};
-function ct(a,b,c,d){b=a.pc.zb[String(b)];if(!b)return!0;b=b.concat();for(var e=!0,f=0;f<b.length;++f){var g=b[f];if(g&&!g.Nc&&g.Uc==c){var k=g.listener,l=g.rb||g.src;g.Gd&&Ns(a.pc,g);e=!1!==k.call(l,d)&&e}}return e&&0!=d.yf}h.Ge=function(a,b,c,d){return this.pc.Ge(String(a),b,c,d)};h.hasListener=function(a,b){return this.pc.hasListener(void 0!==a?String(a):void 0,b)};function dt(a,b,c){if(ia(a))c&&(a=pa(a,c));else if(a&&"function"==typeof a.handleEvent)a=pa(a.handleEvent,a);else throw Error("Invalid listener argument");return 2147483647<b?-1:ca.setTimeout(a,b||0)};function et(a){a=String(a);if(/^\s*$/.test(a)?0:/^[\],:{}\s\u2028\u2029]*$/.test(a.replace(/\\["\\\/bfnrtu]/g,"@").replace(/"[^"\\\n\r\u2028\u2029\x00-\x08\x0a-\x1f]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g,"]").replace(/(?:^|:|,)(?:[\s\u2028\u2029]*\[)+/g,"")))try{return eval("("+a+")")}catch(b){}throw Error("Invalid JSON string: "+a);}function ft(){this.ae=void 0}
-function gt(a,b,c){if(null==b)c.push("null");else{if("object"==typeof b){if(fa(b)){var d=b;b=d.length;c.push("[");for(var e="",f=0;f<b;f++)c.push(e),e=d[f],gt(a,a.ae?a.ae.call(d,String(f),e):e,c),e=",";c.push("]");return}if(b instanceof String||b instanceof Number||b instanceof Boolean)b=b.valueOf();else{c.push("{");f="";for(d in b)Object.prototype.hasOwnProperty.call(b,d)&&(e=b[d],"function"!=typeof e&&(c.push(f),ht(d,c),c.push(":"),gt(a,a.ae?a.ae.call(b,d,e):e,c),f=","));c.push("}");return}}switch(typeof b){case "string":ht(b,
-c);break;case "number":c.push(isFinite(b)&&!isNaN(b)?String(b):"null");break;case "boolean":c.push(String(b));break;case "function":c.push("null");break;default:throw Error("Unknown type: "+typeof b);}}}var it={'"':'\\"',"\\":"\\\\","/":"\\/","\b":"\\b","\f":"\\f","\n":"\\n","\r":"\\r","\t":"\\t","\x0B":"\\u000b"},jt=/\uffff/.test("\uffff")?/[\\\"\x00-\x1f\x7f-\uffff]/g:/[\\\"\x00-\x1f\x7f-\xff]/g;
-function ht(a,b){b.push('"',a.replace(jt,function(a){var b=it[a];b||(b="\\u"+(a.charCodeAt(0)|65536).toString(16).substr(1),it[a]=b);return b}),'"')};function kt(a){if(a.Ib&&"function"==typeof a.Ib)return a.Ib();if(ha(a))return a.split("");if(ga(a)){for(var b=[],c=a.length,d=0;d<c;d++)b.push(a[d]);return b}return Aa(a)}function lt(a){if(a.yb&&"function"==typeof a.yb)return a.yb();if(!a.Ib||"function"!=typeof a.Ib){if(ga(a)||ha(a)){var b=[];a=a.length;for(var c=0;c<a;c++)b.push(c);return b}return Ba(a)}}
-function mt(a,b,c){if(a.forEach&&"function"==typeof a.forEach)a.forEach(b,c);else if(ga(a)||ha(a))Ma(a,b,c);else for(var d=lt(a),e=kt(a),f=e.length,g=0;g<f;g++)b.call(c,e[g],d&&d[g],a)};function nt(a,b){this.Nb={};this.fb=[];this.Ba=0;var c=arguments.length;if(1<c){if(c%2)throw Error("Uneven number of arguments");for(var d=0;d<c;d+=2)this.set(arguments[d],arguments[d+1])}else a&&this.addAll(a)}h=nt.prototype;h.kf=function(){return this.Ba};h.Ib=function(){ot(this);for(var a=[],b=0;b<this.fb.length;b++)a.push(this.Nb[this.fb[b]]);return a};h.yb=function(){ot(this);return this.fb.concat()};h.fd=function(a){return pt(this.Nb,a)};
-h.pb=function(a,b){if(this===a)return!0;if(this.Ba!=a.kf())return!1;var c=b||qt;ot(this);for(var d,e=0;d=this.fb[e];e++)if(!c(this.get(d),a.get(d)))return!1;return!0};function qt(a,b){return a===b}h.clear=function(){this.Nb={};this.Ba=this.fb.length=0};h.remove=function(a){return pt(this.Nb,a)?(delete this.Nb[a],this.Ba--,this.fb.length>2*this.Ba&&ot(this),!0):!1};
-function ot(a){if(a.Ba!=a.fb.length){for(var b=0,c=0;b<a.fb.length;){var d=a.fb[b];pt(a.Nb,d)&&(a.fb[c++]=d);b++}a.fb.length=c}if(a.Ba!=a.fb.length){for(var e={},c=b=0;b<a.fb.length;)d=a.fb[b],pt(e,d)||(a.fb[c++]=d,e[d]=1),b++;a.fb.length=c}}h.get=function(a,b){return pt(this.Nb,a)?this.Nb[a]:b};h.set=function(a,b){pt(this.Nb,a)||(this.Ba++,this.fb.push(a));this.Nb[a]=b};h.addAll=function(a){var b;a instanceof nt?(b=a.yb(),a=a.Ib()):(b=Ba(a),a=Aa(a));for(var c=0;c<b.length;c++)this.set(b[c],a[c])};
-h.forEach=function(a,b){for(var c=this.yb(),d=0;d<c.length;d++){var e=c[d],f=this.get(e);a.call(b,f,e,this)}};h.clone=function(){return new nt(this)};function pt(a,b){return Object.prototype.hasOwnProperty.call(a,b)};function rt(a,b,c,d,e){this.reset(a,b,c,d,e)}rt.prototype.hf=null;var st=0;rt.prototype.reset=function(a,b,c,d,e){"number"==typeof e||st++;d||qa();this.rd=a;this.gg=b;delete this.hf};rt.prototype.Af=function(a){this.rd=a};function tt(a){this.of=a;this.lf=this.qe=this.rd=this.Yd=null}function ut(a,b){this.name=a;this.value=b}ut.prototype.toString=function(){return this.name};var vt=new ut("SEVERE",1E3),wt=new ut("INFO",800),xt=new ut("CONFIG",700),yt=new ut("FINE",500);h=tt.prototype;h.getName=function(){return this.of};h.getParent=function(){return this.Yd};h.Af=function(a){this.rd=a};function zt(a){if(a.rd)return a.rd;if(a.Yd)return zt(a.Yd);Ja("Root logger has no level set.");return null}
-h.log=function(a,b,c){if(a.value>=zt(this).value)for(ia(b)&&(b=b()),a=new rt(a,String(b),this.of),c&&(a.hf=c),c="log:"+a.gg,ca.console&&(ca.console.timeStamp?ca.console.timeStamp(c):ca.console.markTimeline&&ca.console.markTimeline(c)),ca.msWriteProfilerMark&&ca.msWriteProfilerMark(c),c=this;c;){b=c;var d=a;if(b.lf)for(var e=0,f=void 0;f=b.lf[e];e++)f(d);c=c.getParent()}};h.info=function(a,b){this.log(wt,a,b)};var At={},Bt=null;
-function Ct(a){Bt||(Bt=new tt(""),At[""]=Bt,Bt.Af(xt));var b;if(!(b=At[a])){b=new tt(a);var c=a.lastIndexOf("."),d=a.substr(c+1),c=Ct(a.substr(0,c));c.qe||(c.qe={});c.qe[d]=b;b.Yd=c;At[a]=b}return b};function Dt(a,b){a&&a.log(yt,b,void 0)};function Et(){}Et.prototype.Xe=null;function Ft(a){var b;(b=a.Xe)||(b={},Gt(a)&&(b[0]=!0,b[1]=!0),b=a.Xe=b);return b};var Ht;function It(){}ra(It,Et);function Jt(a){return(a=Gt(a))?new ActiveXObject(a):new XMLHttpRequest}function Gt(a){if(!a.mf&&"undefined"==typeof XMLHttpRequest&&"undefined"!=typeof ActiveXObject){for(var b=["MSXML2.XMLHTTP.6.0","MSXML2.XMLHTTP.3.0","MSXML2.XMLHTTP","Microsoft.XMLHTTP"],c=0;c<b.length;c++){var d=b[c];try{return new ActiveXObject(d),a.mf=d}catch(e){}}throw Error("Could not create ActiveXObject. ActiveX might be disabled, or MSXML might not be installed");}return a.mf}Ht=new It;var Kt=/^(?:([^:/?#.]+):)?(?:\/\/(?:([^/?#]*)@)?([^/#?]*?)(?::([0-9]+))?(?=[/#?]|$))?([^?#]+)?(?:\?([^#]*))?(?:#(.*))?$/;function Lt(a,b){if(a)for(var c=a.split("\x26"),d=0;d<c.length;d++){var e=c[d].indexOf("\x3d"),f=null,g=null;0<=e?(f=c[d].substring(0,e),g=c[d].substring(e+1)):f=c[d];b(f,g?decodeURIComponent(g.replace(/\+/g," ")):"")}};function Mt(a){bt.call(this);this.headers=new nt;this.ge=a||null;this.zc=!1;this.fe=this.ea=null;this.nf=this.Vd="";this.Lc=0;this.qd="";this.nd=this.Ie=this.Ud=this.Fe=!1;this.Pc=0;this.be=null;this.xf=Nt;this.ee=this.lg=this.Df=!1}ra(Mt,bt);var Nt="",Ot=Mt.prototype,Pt=Ct("goog.net.XhrIo");Ot.Db=Pt;var Qt=/^https?$/i,Rt=["POST","PUT"];h=Mt.prototype;
-h.send=function(a,b,c,d){if(this.ea)throw Error("[goog.net.XhrIo] Object is active with another request\x3d"+this.Vd+"; newUri\x3d"+a);b=b?b.toUpperCase():"GET";this.Vd=a;this.qd="";this.Lc=0;this.nf=b;this.Fe=!1;this.zc=!0;this.ea=this.ge?Jt(this.ge):Jt(Ht);this.fe=this.ge?Ft(this.ge):Ft(Ht);this.ea.onreadystatechange=pa(this.rf,this);this.lg&&"onprogress"in this.ea&&(this.ea.onprogress=pa(function(a){this.qf(a,!0)},this),this.ea.upload&&(this.ea.upload.onprogress=pa(this.qf,this)));try{Dt(this.Db,
-St(this,"Opening Xhr")),this.Ie=!0,this.ea.open(b,String(a),!0),this.Ie=!1}catch(f){Dt(this.Db,St(this,"Error opening Xhr: "+f.message));Tt(this,f);return}a=c||"";var e=this.headers.clone();d&&mt(d,function(a,b){e.set(b,a)});d=Qa(e.yb());c=ca.FormData&&a instanceof ca.FormData;!(0<=La(Rt,b))||d||c||e.set("Content-Type","application/x-www-form-urlencoded;charset\x3dutf-8");e.forEach(function(a,b){this.ea.setRequestHeader(b,a)},this);this.xf&&(this.ea.responseType=this.xf);Da(this.ea)&&(this.ea.withCredentials=
-this.Df);try{Ut(this),0<this.Pc&&(this.ee=Vt(this.ea),Dt(this.Db,St(this,"Will abort after "+this.Pc+"ms if incomplete, xhr2 "+this.ee)),this.ee?(this.ea.timeout=this.Pc,this.ea.ontimeout=pa(this.Cf,this)):this.be=dt(this.Cf,this.Pc,this)),Dt(this.Db,St(this,"Sending request")),this.Ud=!0,this.ea.send(a),this.Ud=!1}catch(f){Dt(this.Db,St(this,"Send error: "+f.message)),Tt(this,f)}};function Vt(a){return os&&ws(9)&&"number"==typeof a.timeout&&void 0!==a.ontimeout}
-function Sa(a){return"content-type"==a.toLowerCase()}h.Cf=function(){"undefined"!=typeof aa&&this.ea&&(this.qd="Timed out after "+this.Pc+"ms, aborting",this.Lc=8,Dt(this.Db,St(this,this.qd)),this.dispatchEvent("timeout"),this.abort(8))};function Tt(a,b){a.zc=!1;a.ea&&(a.nd=!0,a.ea.abort(),a.nd=!1);a.qd=b;a.Lc=5;Wt(a);Xt(a)}function Wt(a){a.Fe||(a.Fe=!0,a.dispatchEvent("complete"),a.dispatchEvent("error"))}
-h.abort=function(a){this.ea&&this.zc&&(Dt(this.Db,St(this,"Aborting")),this.zc=!1,this.nd=!0,this.ea.abort(),this.nd=!1,this.Lc=a||7,this.dispatchEvent("complete"),this.dispatchEvent("abort"),Xt(this))};h.rf=function(){this.Ee||(this.Ie||this.Ud||this.nd?Yt(this):this.jg())};h.jg=function(){Yt(this)};
-function Yt(a){if(a.zc&&"undefined"!=typeof aa)if(a.fe[1]&&4==Zt(a)&&2==$t(a))Dt(a.Db,St(a,"Local request error detected and ignored"));else if(a.Ud&&4==Zt(a))dt(a.rf,0,a);else if(a.dispatchEvent("readystatechange"),4==Zt(a)){Dt(a.Db,St(a,"Request complete"));a.zc=!1;try{var b=$t(a),c;a:switch(b){case 200:case 201:case 202:case 204:case 206:case 304:case 1223:c=!0;break a;default:c=!1}var d;if(!(d=c)){var e;if(e=0===b){var f=String(a.Vd).match(Kt)[1]||null;if(!f&&ca.self&&ca.self.location)var g=ca.self.location.protocol,
-f=g.substr(0,g.length-1);e=!Qt.test(f?f.toLowerCase():"")}d=e}d?(a.dispatchEvent("complete"),a.dispatchEvent("success")):(a.Lc=6,a.qd=au(a)+" ["+$t(a)+"]",Wt(a))}finally{Xt(a)}}}h.qf=function(a,b){this.dispatchEvent(bu(a,"progress"));this.dispatchEvent(bu(a,b?"downloadprogress":"uploadprogress"))};function bu(a,b){return{type:b,lengthComputable:a.lengthComputable,loaded:a.loaded,total:a.total}}
-function Xt(a){if(a.ea){Ut(a);var b=a.ea,c=a.fe[0]?ea:null;a.ea=null;a.fe=null;a.dispatchEvent("ready");try{b.onreadystatechange=c}catch(d){(a=a.Db)&&a.log(vt,"Problem encountered resetting onreadystatechange: "+d.message,void 0)}}}function Ut(a){a.ea&&a.ee&&(a.ea.ontimeout=null);"number"==typeof a.be&&(ca.clearTimeout(a.be),a.be=null)}function Zt(a){return a.ea?a.ea.readyState:0}function $t(a){try{return 2<Zt(a)?a.ea.status:-1}catch(b){return-1}}
-function au(a){try{return 2<Zt(a)?a.ea.statusText:""}catch(b){return Dt(a.Db,"Can not get status: "+b.message),""}}h.getResponseHeader=function(a){return this.ea&&4==Zt(this)?this.ea.getResponseHeader(a):void 0};h.getAllResponseHeaders=function(){return this.ea&&4==Zt(this)?this.ea.getAllResponseHeaders():""};function St(a,b){return b+" ["+a.nf+" "+a.Vd+" "+$t(a)+"]"};function cu(a,b,c){this.Ba=this.Ga=null;this.Cb=a||null;this.Zf=!!c}function du(a){a.Ga||(a.Ga=new nt,a.Ba=0,a.Cb&&Lt(a.Cb,function(b,c){a.add(decodeURIComponent(b.replace(/\+/g," ")),c)}))}h=cu.prototype;h.kf=function(){du(this);return this.Ba};h.add=function(a,b){du(this);this.Cb=null;a=eu(this,a);var c=this.Ga.get(a);c||this.Ga.set(a,c=[]);c.push(b);this.Ba++;return this};
-h.remove=function(a){du(this);a=eu(this,a);return this.Ga.fd(a)?(this.Cb=null,this.Ba-=this.Ga.get(a).length,this.Ga.remove(a)):!1};h.clear=function(){this.Ga=this.Cb=null;this.Ba=0};h.fd=function(a){du(this);a=eu(this,a);return this.Ga.fd(a)};h.yb=function(){du(this);for(var a=this.Ga.Ib(),b=this.Ga.yb(),c=[],d=0;d<b.length;d++)for(var e=a[d],f=0;f<e.length;f++)c.push(b[d]);return c};
-h.Ib=function(a){du(this);var b=[];if(ha(a))this.fd(a)&&(b=Ua(b,this.Ga.get(eu(this,a))));else{a=this.Ga.Ib();for(var c=0;c<a.length;c++)b=Ua(b,a[c])}return b};h.set=function(a,b){du(this);this.Cb=null;a=eu(this,a);this.fd(a)&&(this.Ba-=this.Ga.get(a).length);this.Ga.set(a,[b]);this.Ba++;return this};h.get=function(a,b){var c=a?this.Ib(a):[];return 0<c.length?String(c[0]):b};
-h.toString=function(){if(this.Cb)return this.Cb;if(!this.Ga)return"";for(var a=[],b=this.Ga.yb(),c=0;c<b.length;c++)for(var d=b[c],e=encodeURIComponent(String(d)),d=this.Ib(d),f=0;f<d.length;f++){var g=e;""!==d[f]&&(g+="\x3d"+encodeURIComponent(String(d[f])));a.push(g)}return this.Cb=a.join("\x26")};h.clone=function(){var a=new cu;a.Cb=this.Cb;this.Ga&&(a.Ga=this.Ga.clone(),a.Ba=this.Ba);return a};function eu(a,b){var c=String(b);a.Zf&&(c=c.toLowerCase());return c}
-h.extend=function(a){for(var b=0;b<arguments.length;b++)mt(arguments[b],function(a,b){this.add(b,a)},this)};var fu="undefined"!=typeof Object.keys?function(a){return Object.keys(a)}:function(a){return Ba(a)},gu="undefined"!=typeof Array.isArray?function(a){return Array.isArray(a)}:function(a){return"array"===p(a)};function hu(){return Math.round(15*Math.random()).toString(16)};var iu=1;function ju(a,b){if(null==a)return null==b;if(a===b)return!0;if("object"===typeof a){if(gu(a)){if(gu(b)&&a.length===b.length){for(var c=0;c<a.length;c++)if(!ju(a[c],b[c]))return!1;return!0}return!1}if(a.Bb)return a.Bb(b);if(null!=b&&"object"===typeof b){if(b.Bb)return b.Bb(a);var c=0,d=fu(b).length,e;for(e in a)if(a.hasOwnProperty(e)&&(c++,!b.hasOwnProperty(e)||!ju(a[e],b[e])))return!1;return c===d}}return!1}function ku(a,b){return a^b+2654435769+(a<<6)+(a>>2)}var lu={},mu=0;
-function nu(a){var b=0;if(null!=a.forEach)a.forEach(function(a,c){b=(b+(ou(c)^ou(a)))%4503599627370496});else for(var c=fu(a),d=0;d<c.length;d++)var e=c[d],f=a[e],b=(b+(ou(e)^ou(f)))%4503599627370496;return b}function pu(a){var b=0;if(gu(a))for(var c=0;c<a.length;c++)b=ku(b,ou(a[c]));else a.forEach&&a.forEach(function(a){b=ku(b,ou(a))});return b}
-function ou(a){if(null==a)return 0;switch(typeof a){case "number":return a;case "boolean":return!0===a?1:0;case "string":var b=lu[a];if(null==b){for(var c=b=0;c<a.length;++c)b=31*b+a.charCodeAt(c),b%=4294967296;mu++;256<=mu&&(lu={},mu=1);lu[a]=b}a=b;return a;case "function":return b=a.transit$hashCode$,b||(b=iu,"undefined"!=typeof Object.defineProperty?Object.defineProperty(a,"transit$hashCode$",{value:b,enumerable:!1}):a.transit$hashCode$=b,iu++),b;default:return a instanceof Date?a.valueOf():gu(a)?
-pu(a):a.Hb?a.Hb():nu(a)}};function qu(a,b){this.xa=a|0;this.ma=b|0}var Ou={},Pu={};function Qu(a){if(-128<=a&&128>a){var b=Ou[a];if(b)return b}b=new qu(a|0,0>a?-1:0);-128<=a&&128>a&&(Ou[a]=b);return b}function Ru(a){isNaN(a)||!isFinite(a)?a=Su():a<=-Tu?a=Uu():a+1>=Tu?(a=Vu,Pu[a]||(Pu[a]=Wu(-1,2147483647)),a=Pu[a]):a=0>a?Xu(Ru(-a)):new qu(a%Yu|0,a/Yu|0);return a}function Wu(a,b){return new qu(a,b)}
-function Zu(a,b){if(0==a.length)throw Error("number format error: empty string");var c=b||10;if(2>c||36<c)throw Error("radix out of range: "+c);if("-"==a.charAt(0))return Xu(Zu(a.substring(1),c));if(0<=a.indexOf("-"))throw Error('number format error: interior "-" character: '+a);for(var d=Ru(Math.pow(c,8)),e=Su(),f=0;f<a.length;f+=8){var g=Math.min(8,a.length-f),k=parseInt(a.substring(f,f+g),c);8>g?(g=Ru(Math.pow(c,g)),e=e.multiply(g).add(Ru(k))):(e=e.multiply(d),e=e.add(Ru(k)))}return e}
-var Yu=4294967296,Tu=Yu*Yu/2;function Su(){var a=$u;Pu[a]||(Pu[a]=Qu(0));return Pu[a]}function av(){var a=bv;Pu[a]||(Pu[a]=Qu(1));return Pu[a]}function cv(){var a=dv;Pu[a]||(Pu[a]=Qu(-1));return Pu[a]}function Uu(){var a=ev;Pu[a]||(Pu[a]=Wu(0,-2147483648));return Pu[a]}function fv(){var a=gv;Pu[a]||(Pu[a]=Qu(16777216));return Pu[a]}function hv(a){return a.ma*Yu+(0<=a.xa?a.xa:Yu+a.xa)}h=qu.prototype;
-h.toString=function(a){a=a||10;if(2>a||36<a)throw Error("radix out of range: "+a);if(iv(this))return"0";if(0>this.ma){if(this.pb(Uu())){var b=Ru(a),c=this.div(b),b=jv(c.multiply(b),this);return c.toString(a)+b.xa.toString(a)}return"-"+Xu(this).toString(a)}for(var c=Ru(Math.pow(a,6)),b=this,d="";;){var e=b.div(c),f=(jv(b,e.multiply(c)).xa>>>0).toString(a),b=e;if(iv(b))return f+d;for(;6>f.length;)f="0"+f;d=""+f+d}};function iv(a){return 0==a.ma&&0==a.xa}
-h.pb=function(a){return this.ma==a.ma&&this.xa==a.xa};h.compare=function(a){if(this.pb(a))return 0;var b=0>this.ma,c=0>a.ma;return b&&!c?-1:!b&&c?1:0>jv(this,a).ma?-1:1};function Xu(a){return a.pb(Uu())?Uu():Wu(~a.xa,~a.ma).add(av())}h.add=function(a){var b=this.ma>>>16,c=this.ma&65535,d=this.xa>>>16,e=a.ma>>>16,f=a.ma&65535,g=a.xa>>>16,k;k=0+((this.xa&65535)+(a.xa&65535));a=0+(k>>>16);a+=d+g;d=0+(a>>>16);d+=c+f;c=0+(d>>>16);c=c+(b+e)&65535;return Wu((a&65535)<<16|k&65535,c<<16|d&65535)};
-function jv(a,b){return a.add(Xu(b))}
-h.multiply=function(a){if(iv(this)||iv(a))return Su();if(this.pb(Uu()))return 1==(a.xa&1)?Uu():Su();if(a.pb(Uu()))return 1==(this.xa&1)?Uu():Su();if(0>this.ma)return 0>a.ma?Xu(this).multiply(Xu(a)):Xu(Xu(this).multiply(a));if(0>a.ma)return Xu(this.multiply(Xu(a)));var b=fv();if(b=0>this.compare(b))b=fv(),b=0>a.compare(b);if(b)return Ru(hv(this)*hv(a));var b=this.ma>>>16,c=this.ma&65535,d=this.xa>>>16,e=this.xa&65535,f=a.ma>>>16,g=a.ma&65535,k=a.xa>>>16;a=a.xa&65535;var l,n,m,t;t=0+e*a;m=0+(t>>>16);
-m+=d*a;n=0+(m>>>16);m=(m&65535)+e*k;n+=m>>>16;m&=65535;n+=c*a;l=0+(n>>>16);n=(n&65535)+d*k;l+=n>>>16;n&=65535;n+=e*g;l+=n>>>16;n&=65535;l=l+(b*a+c*k+d*g+e*f)&65535;return Wu(m<<16|t&65535,l<<16|n)};
-h.div=function(a){if(iv(a))throw Error("division by zero");if(iv(this))return Su();if(this.pb(Uu())){if(a.pb(av())||a.pb(cv()))return Uu();if(a.pb(Uu()))return av();var b;b=1;if(0==b)b=this;else{var c=this.ma;b=32>b?Wu(this.xa>>>b|c<<32-b,c>>b):Wu(c>>b-32,0<=c?0:-1)}b=b.div(a).shiftLeft(1);if(b.pb(Su()))return 0>a.ma?av():cv();c=jv(this,a.multiply(b));return b.add(c.div(a))}if(a.pb(Uu()))return Su();if(0>this.ma)return 0>a.ma?Xu(this).div(Xu(a)):Xu(Xu(this).div(a));if(0>a.ma)return Xu(this.div(Xu(a)));
-for(var d=Su(),c=this;0<=c.compare(a);){b=Math.max(1,Math.floor(hv(c)/hv(a)));for(var e=Math.ceil(Math.log(b)/Math.LN2),e=48>=e?1:Math.pow(2,e-48),f=Ru(b),g=f.multiply(a);0>g.ma||0<g.compare(c);)b-=e,f=Ru(b),g=f.multiply(a);iv(f)&&(f=av());d=d.add(f);c=jv(c,g)}return d};h.shiftLeft=function(a){a&=63;if(0==a)return this;var b=this.xa;return 32>a?Wu(b<<a,this.ma<<a|b>>>32-a):Wu(0,b<<a-32)};
-function kv(a,b){b&=63;if(0==b)return a;var c=a.ma;return 32>b?Wu(a.xa>>>b|c<<32-b,c>>>b):32==b?Wu(c,0):Wu(c>>>b-32,0)}var Vu=1,ev=2,$u=3,bv=4,dv=5,gv=6;var lv="undefined"!=typeof Symbol?Symbol.iterator:"@@iterator";function mv(a,b){this.tag=a;this.da=b;this.pa=-1}mv.prototype.toString=function(){return"[TaggedValue: "+this.tag+", "+this.da+"]"};mv.prototype.equiv=function(a){return ju(this,a)};mv.prototype.equiv=mv.prototype.equiv;mv.prototype.Bb=function(a){return a instanceof mv?this.tag===a.tag&&ju(this.da,a.da):!1};mv.prototype.Hb=function(){-1===this.pa&&(this.pa=ku(ou(this.tag),ou(this.da)));return this.pa};
-function nv(a,b){return new mv(a,b)}var ov=Zu("9007199254740991"),pv=Zu("-9007199254740991");qu.prototype.equiv=function(a){return ju(this,a)};qu.prototype.equiv=qu.prototype.equiv;qu.prototype.Bb=function(a){return a instanceof qu&&this.pb(a)};qu.prototype.Hb=function(){return this.xa};function qv(a){this.ya=a;this.pa=-1}qv.prototype.toString=function(){return":"+this.ya};qv.prototype.namespace=function(){var a=this.ya.indexOf("/");return-1!=a?this.ya.substring(0,a):null};
-qv.prototype.name=function(){var a=this.ya.indexOf("/");return-1!=a?this.ya.substring(a+1,this.ya.length):this.ya};qv.prototype.equiv=function(a){return ju(this,a)};qv.prototype.equiv=qv.prototype.equiv;qv.prototype.Bb=function(a){return a instanceof qv&&this.ya==a.ya};qv.prototype.Hb=function(){-1===this.pa&&(this.pa=ou(this.ya));return this.pa};function rv(a){this.ya=a;this.pa=-1}rv.prototype.namespace=function(){var a=this.ya.indexOf("/");return-1!=a?this.ya.substring(0,a):null};
-rv.prototype.name=function(){var a=this.ya.indexOf("/");return-1!=a?this.ya.substring(a+1,this.ya.length):this.ya};rv.prototype.toString=function(){return this.ya};rv.prototype.equiv=function(a){return ju(this,a)};rv.prototype.equiv=rv.prototype.equiv;rv.prototype.Bb=function(a){return a instanceof rv&&this.ya==a.ya};rv.prototype.Hb=function(){-1===this.pa&&(this.pa=ou(this.ya));return this.pa};
-function sv(a,b,c){var d="";c=c||b+1;for(var e=8*(7-b),f=Qu(255).shiftLeft(e);b<c;b++,e-=8,f=kv(f,8)){var g=kv(Wu(a.xa&f.xa,a.ma&f.ma),e).toString(16);1==g.length&&(g="0"+g);d+=g}return d}function tv(a,b){this.He=a;this.Je=b;this.pa=-1}tv.prototype.toString=function(){var a,b=this.He,c=this.Je;a=""+(sv(b,0,4)+"-");a+=sv(b,4,6)+"-";a+=sv(b,6,8)+"-";a+=sv(c,0,2)+"-";return a+=sv(c,2,8)};tv.prototype.equiv=function(a){return ju(this,a)};tv.prototype.equiv=tv.prototype.equiv;
-tv.prototype.Bb=function(a){return a instanceof tv&&this.He.pb(a.He)&&this.Je.pb(a.Je)};tv.prototype.Hb=function(){-1===this.pa&&(this.pa=ou(this.toString()));return this.pa};Date.prototype.Bb=function(a){return a instanceof Date?this.valueOf()===a.valueOf():!1};Date.prototype.Hb=function(){return this.valueOf()};function uv(a,b){this.entries=a;this.type=b||0;this.ka=0}
-uv.prototype.next=function(){if(this.ka<this.entries.length){var a=null,a=0===this.type?this.entries[this.ka]:1===this.type?this.entries[this.ka+1]:[this.entries[this.ka],this.entries[this.ka+1]],a={value:a,done:!1};this.ka+=2;return a}return{value:null,done:!0}};uv.prototype.next=uv.prototype.next;uv.prototype[lv]=function(){return this};function vv(a,b){this.map=a;this.type=b||0;this.keys=this.map.yb();this.ka=0;this.hc=null;this.Yb=0}
-vv.prototype.next=function(){if(this.ka<this.map.size){null!=this.hc&&this.Yb<this.hc.length||(this.hc=this.map.map[this.keys[this.ka]],this.Yb=0);var a=null,a=0===this.type?this.hc[this.Yb]:1===this.type?this.hc[this.Yb+1]:[this.hc[this.Yb],this.hc[this.Yb+1]],a={value:a,done:!1};this.ka++;this.Yb+=2;return a}return{value:null,done:!0}};vv.prototype.next=vv.prototype.next;vv.prototype[lv]=function(){return this};
-function wv(a,b){if(a instanceof xv&&(b instanceof yv||b instanceof xv)){if(a.size!==b.size)return!1;for(var c in a.map)for(var d=a.map[c],e=0;e<d.length;e+=2)if(!ju(d[e+1],b.get(d[e])))return!1;return!0}if(a instanceof yv&&(b instanceof yv||b instanceof xv)){if(a.size!==b.size)return!1;c=a.na;for(e=0;e<c.length;e+=2)if(!ju(c[e+1],b.get(c[e])))return!1;return!0}if(null!=b&&"object"===typeof b&&(e=fu(b),c=e.length,a.size===c)){for(d=0;d<c;d++){var f=e[d];if(!a.has(f)||!ju(b[f],a.get(f)))return!1}return!0}return!1}
-function zv(a){return null==a?"null":fa(a)?"["+a.toString()+"]":ha(a)?'"'+a+'"':a.toString()}function Av(a){var b=0,c="TransitMap {";a.forEach(function(d,e){c+=zv(e)+" \x3d\x3e "+zv(d);b<a.size-1&&(c+=", ");b++});return c+"}"}function Bv(a){var b=0,c="TransitSet {";a.forEach(function(d){c+=zv(d);b<a.size-1&&(c+=", ");b++});return c+"}"}function yv(a){this.na=a;this.la=null;this.pa=-1;this.size=a.length/2;this.Qe=0}yv.prototype.toString=function(){return Av(this)};yv.prototype.inspect=function(){return this.toString()};
-function Cv(a){if(a.la)throw Error("Invalid operation, already converted");if(8>a.size)return!1;a.Qe++;return 32<a.Qe?(a.la=Dv(a.na,!1,!0),a.na=[],!0):!1}yv.prototype.clear=function(){this.pa=-1;this.la?this.la.clear():this.na=[];this.size=0};yv.prototype.clear=yv.prototype.clear;yv.prototype.keys=function(){return this.la?this.la.keys():new uv(this.na,0)};yv.prototype.keys=yv.prototype.keys;
-yv.prototype.rc=function(){if(this.la)return this.la.rc();for(var a=[],b=0,c=0;c<this.na.length;b++,c+=2)a[b]=this.na[c];return a};yv.prototype.keySet=yv.prototype.rc;yv.prototype.entries=function(){return this.la?this.la.entries():new uv(this.na,2)};yv.prototype.entries=yv.prototype.entries;yv.prototype.values=function(){return this.la?this.la.values():new uv(this.na,1)};yv.prototype.values=yv.prototype.values;
-yv.prototype.forEach=function(a){if(this.la)this.la.forEach(a);else for(var b=0;b<this.na.length;b+=2)a(this.na[b+1],this.na[b])};yv.prototype.forEach=yv.prototype.forEach;yv.prototype.get=function(a,b){if(this.la)return this.la.get(a);if(Cv(this))return this.get(a);for(var c=0;c<this.na.length;c+=2)if(ju(this.na[c],a))return this.na[c+1];return b};yv.prototype.get=yv.prototype.get;
-yv.prototype.has=function(a){if(this.la)return this.la.has(a);if(Cv(this))return this.has(a);for(var b=0;b<this.na.length;b+=2)if(ju(this.na[b],a))return!0;return!1};yv.prototype.has=yv.prototype.has;yv.prototype.set=function(a,b){this.pa=-1;if(this.la)this.la.set(a,b),this.size=this.la.size;else{for(var c=0;c<this.na.length;c+=2)if(ju(this.na[c],a)){this.na[c+1]=b;return}this.na.push(a);this.na.push(b);this.size++;32<this.size&&(this.la=Dv(this.na,!1,!0),this.na=null)}};yv.prototype.set=yv.prototype.set;
-yv.prototype["delete"]=function(a){this.pa=-1;if(this.la)return a=this.la["delete"](a),this.size=this.la.size,a;for(var b=0;b<this.na.length;b+=2)if(ju(this.na[b],a))return a=this.na[b+1],this.na.splice(b,2),this.size--,a};yv.prototype.clone=function(){var a=Dv();this.forEach(function(b,c){a.set(c,b)});return a};yv.prototype.clone=yv.prototype.clone;yv.prototype[lv]=function(){return this.entries()};yv.prototype.Hb=function(){if(this.la)return this.la.Hb();-1===this.pa&&(this.pa=nu(this));return this.pa};
-yv.prototype.Bb=function(a){return this.la?wv(this.la,a):wv(this,a)};function xv(a,b,c){this.map=b||{};this.yc=a||[];this.size=c||0;this.pa=-1}xv.prototype.toString=function(){return Av(this)};xv.prototype.inspect=function(){return this.toString()};xv.prototype.clear=function(){this.pa=-1;this.map={};this.yc=[];this.size=0};xv.prototype.clear=xv.prototype.clear;xv.prototype.yb=function(){return null!=this.yc?this.yc:fu(this.map)};
-xv.prototype["delete"]=function(a){this.pa=-1;this.yc=null;for(var b=ou(a),c=this.map[b],d=0;d<c.length;d+=2)if(ju(a,c[d]))return a=c[d+1],c.splice(d,2),0===c.length&&delete this.map[b],this.size--,a};xv.prototype.entries=function(){return new vv(this,2)};xv.prototype.entries=xv.prototype.entries;xv.prototype.forEach=function(a){for(var b=this.yb(),c=0;c<b.length;c++)for(var d=this.map[b[c]],e=0;e<d.length;e+=2)a(d[e+1],d[e],this)};xv.prototype.forEach=xv.prototype.forEach;
-xv.prototype.get=function(a,b){var c=ou(a),c=this.map[c];if(null!=c)for(var d=0;d<c.length;d+=2){if(ju(a,c[d]))return c[d+1]}else return b};xv.prototype.get=xv.prototype.get;xv.prototype.has=function(a){var b=ou(a),b=this.map[b];if(null!=b)for(var c=0;c<b.length;c+=2)if(ju(a,b[c]))return!0;return!1};xv.prototype.has=xv.prototype.has;xv.prototype.keys=function(){return new vv(this,0)};xv.prototype.keys=xv.prototype.keys;
-xv.prototype.rc=function(){for(var a=this.yb(),b=[],c=0;c<a.length;c++)for(var d=this.map[a[c]],e=0;e<d.length;e+=2)b.push(d[e]);return b};xv.prototype.keySet=xv.prototype.rc;xv.prototype.set=function(a,b){this.pa=-1;var c=ou(a),d=this.map[c];if(null==d)this.yc&&this.yc.push(c),this.map[c]=[a,b],this.size++;else{for(var c=!0,e=0;e<d.length;e+=2)if(ju(b,d[e])){c=!1;d[e]=b;break}c&&(d.push(a),d.push(b),this.size++)}};xv.prototype.set=xv.prototype.set;
-xv.prototype.values=function(){return new vv(this,1)};xv.prototype.values=xv.prototype.values;xv.prototype.clone=function(){var a=Dv();this.forEach(function(b,c){a.set(c,b)});return a};xv.prototype.clone=xv.prototype.clone;xv.prototype[lv]=function(){return this.entries()};xv.prototype.Hb=function(){-1===this.pa&&(this.pa=nu(this));return this.pa};xv.prototype.Bb=function(a){return wv(this,a)};
-function Dv(a,b,c){a=a||[];b=!1===b?b:!0;if((!0!==c||!c)&&64>=a.length){if(b){var d=a;a=[];for(b=0;b<d.length;b+=2){var e=!1;for(c=0;c<a.length;c+=2)if(ju(a[c],d[b])){a[c+1]=d[b+1];e=!0;break}e||(a.push(d[b]),a.push(d[b+1]))}}return new yv(a)}var d={},e=[],f=0;for(b=0;b<a.length;b+=2){c=ou(a[b]);var g=d[c];if(null==g)e.push(c),d[c]=[a[b],a[b+1]],f++;else{var k=!0;for(c=0;c<g.length;c+=2)if(ju(g[c],a[b])){g[c+1]=a[b+1];k=!1;break}k&&(g.push(a[b]),g.push(a[b+1]),f++)}}return new xv(e,d,f)}
-function Ev(a){this.map=a;this.size=a.size}Ev.prototype.toString=function(){return Bv(this)};Ev.prototype.inspect=function(){return this.toString()};Ev.prototype.add=function(a){this.map.set(a,a);this.size=this.map.size};Ev.prototype.add=Ev.prototype.add;Ev.prototype.clear=function(){this.map=new xv;this.size=0};Ev.prototype.clear=Ev.prototype.clear;Ev.prototype["delete"]=function(a){a=this.map["delete"](a);this.size=this.map.size;return a};Ev.prototype.entries=function(){return this.map.entries()};
-Ev.prototype.entries=Ev.prototype.entries;Ev.prototype.forEach=function(a){var b=this;this.map.forEach(function(c,d){a(d,b)})};Ev.prototype.forEach=Ev.prototype.forEach;Ev.prototype.has=function(a){return this.map.has(a)};Ev.prototype.has=Ev.prototype.has;Ev.prototype.keys=function(){return this.map.keys()};Ev.prototype.keys=Ev.prototype.keys;Ev.prototype.rc=function(){return this.map.rc()};Ev.prototype.keySet=Ev.prototype.rc;Ev.prototype.values=function(){return this.map.values()};
-Ev.prototype.values=Ev.prototype.values;Ev.prototype.clone=function(){var a=Fv();this.forEach(function(b){a.add(b)});return a};Ev.prototype.clone=Ev.prototype.clone;Ev.prototype[lv]=function(){return this.values()};Ev.prototype.Bb=function(a){if(a instanceof Ev){if(this.size===a.size)return ju(this.map,a.map)}else return!1};Ev.prototype.Hb=function(){return ou(this.map)};
-function Fv(a){a=a||[];for(var b={},c=[],d=0,e=0;e<a.length;e++){var f=ou(a[e]),g=b[f];if(null==g)c.push(f),b[f]=[a[e],a[e]],d++;else{for(var f=!0,k=0;k<g.length;k+=2)if(ju(g[k],a[e])){f=!1;break}f&&(g.push(a[e]),g.push(a[e]),d++)}}return new Ev(new xv(c,b,d))};function Gv(a,b){if(3<a.length){if(b)return!0;var c=a.charAt(1);return"~"===a.charAt(0)?":"===c||"$"===c||"#"===c:!1}return!1}function Hv(a){var b=Math.floor(a/44);a=String.fromCharCode(a%44+48);return 0===b?"^"+a:"^"+String.fromCharCode(b+48)+a}function Iv(){this.If=this.jd=this.ka=0;this.cache={}}
-Iv.prototype.write=function(a,b){if(Gv(a,b)){4096===this.If?(this.clear(),this.jd=0,this.cache={}):1936===this.ka&&this.clear();var c=this.cache[a];return null==c?(this.cache[a]=[Hv(this.ka),this.jd],this.ka++,a):c[1]!=this.jd?(c[1]=this.jd,c[0]=Hv(this.ka),this.ka++,a):c[0]}return a};Iv.prototype.clear=function(){this.ka=0;this.jd++};function Jv(){this.ka=0;this.cache=[]}Jv.prototype.write=function(a){1936==this.ka&&(this.ka=0);this.cache[this.ka]=a;this.ka++;return a};
-Jv.prototype.read=function(a){return this.cache[2===a.length?a.charCodeAt(1)-48:44*(a.charCodeAt(1)-48)+(a.charCodeAt(2)-48)]};Jv.prototype.clear=function(){this.ka=0};function Kv(a){this.ib=a}
-function Lv(a){this.options=a||{};this.Ea={};for(var b in this.gd.Ea)this.Ea[b]=this.gd.Ea[b];for(b in this.options.handlers){a:{switch(b){case "_":case "s":case "?":case "i":case "d":case "b":case "'":case "array":case "map":a=!0;break a}a=!1}if(a)throw Error('Cannot override handler for ground type "'+b+'"');this.Ea[b]=this.options.handlers[b]}this.Zd=null!=this.options.preferStrings?this.options.preferStrings:this.gd.Zd;this.Le=null!=this.options.preferBuffers?this.options.preferBuffers:this.gd.Le;
-this.De=this.options.defaultHandler||this.gd.De;this.Eb=this.options.mapBuilder;this.Ac=this.options.arrayBuilder}
-Lv.prototype.gd={Ea:{_:function(){return null},"?":function(a){return"t"===a},b:function(a,b){var c;if(b&&!1===b.Le||"undefined"==typeof Buffer)if("undefined"!=typeof Uint8Array){if("undefined"!=typeof atob)c=atob(a);else{c=String(a).replace(/=+$/,"");if(1==c.length%4)throw Error("'atob' failed: The string to be decoded is not correctly encoded.");for(var d=0,e,f,g=0,k="";f=c.charAt(g++);~f&&(e=d%4?64*e+f:f,d++%4)?k+=String.fromCharCode(255&e>>(-2*d&6)):0)f="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/\x3d".indexOf(f);
-c=k}d=c.length;e=new Uint8Array(d);for(f=0;f<d;f++)e[f]=c.charCodeAt(f);c=e}else c=nv("b",a);else c=new Buffer(a,"base64");return c},i:function(a){"number"===typeof a||a instanceof qu||(a=Zu(a,10),a=0<a.compare(ov)||0>a.compare(pv)?a:hv(a));return a},n:function(a){return nv("n",a)},d:function(a){return parseFloat(a)},f:function(a){return nv("f",a)},c:function(a){return a},":":function(a){return new qv(a)},$:function(a){return new rv(a)},r:function(a){return nv("r",a)},z:function(a){a:switch(a){case "-INF":a=
--Infinity;break a;case "INF":a=Infinity;break a;case "NaN":a=NaN;break a;default:throw Error("Invalid special double value "+a);}return a},"'":function(a){return a},m:function(a){a="number"===typeof a?a:parseInt(a,10);return new Date(a)},t:function(a){return new Date(a)},u:function(a){a=a.replace(/-/g,"");for(var b=null,c=null,d=c=0,e=24,f=0,f=c=0,e=24;8>f;f+=2,e-=8)c|=parseInt(a.substring(f,f+2),16)<<e;d=0;f=8;for(e=24;16>f;f+=2,e-=8)d|=parseInt(a.substring(f,f+2),16)<<e;b=Wu(d,c);c=0;f=16;for(e=
-24;24>f;f+=2,e-=8)c|=parseInt(a.substring(f,f+2),16)<<e;d=0;for(e=f=24;32>f;f+=2,e-=8)d|=parseInt(a.substring(f,f+2),16)<<e;c=Wu(d,c);return new tv(b,c)},set:function(a){return Fv(a)},list:function(a){return nv("list",a)},link:function(a){return nv("link",a)},cmap:function(a){return Dv(a,!1)}},De:function(a,b){return nv(a,b)},Zd:!0,Le:!0};
-Lv.prototype.decode=function(a,b,c,d){if(null==a)return null;switch(typeof a){case "string":return Gv(a,c)?(a=Mv(this,a),b&&b.write(a,c),b=a):b="^"===a.charAt(0)&&" "!==a.charAt(1)?b.read(a,c):Mv(this,a),b;case "object":if(gu(a))if("^ "===a[0])if(this.Eb)if(17>a.length&&this.Eb.qc){d=[];for(c=1;c<a.length;c+=2)d.push(this.decode(a[c],b,!0,!1)),d.push(this.decode(a[c+1],b,!1,!1));b=this.Eb.qc(d,a)}else{d=this.Eb.Jc(a);for(c=1;c<a.length;c+=2)d=this.Eb.add(d,this.decode(a[c],b,!0,!1),this.decode(a[c+
-1],b,!1,!1),a);b=this.Eb.Td(d,a)}else{d=[];for(c=1;c<a.length;c+=2)d.push(this.decode(a[c],b,!0,!1)),d.push(this.decode(a[c+1],b,!1,!1));b=Dv(d,!1)}else b=Nv(this,a,b,c,d);else{c=fu(a);var e=c[0];if((d=1==c.length?this.decode(e,b,!1,!1):null)&&d instanceof Kv)a=a[e],c=this.Ea[d.ib],b=null!=c?c(this.decode(a,b,!1,!0),this):nv(d.ib,this.decode(a,b,!1,!1));else if(this.Eb)if(16>c.length&&this.Eb.qc){var f=[];for(d=0;d<c.length;d++)e=c[d],f.push(this.decode(e,b,!0,!1)),f.push(this.decode(a[e],b,!1,!1));
-b=this.Eb.qc(f,a)}else{f=this.Eb.Jc(a);for(d=0;d<c.length;d++)e=c[d],f=this.Eb.add(f,this.decode(e,b,!0,!1),this.decode(a[e],b,!1,!1),a);b=this.Eb.Td(f,a)}else{f=[];for(d=0;d<c.length;d++)e=c[d],f.push(this.decode(e,b,!0,!1)),f.push(this.decode(a[e],b,!1,!1));b=Dv(f,!1)}}return b}return a};Lv.prototype.decode=Lv.prototype.decode;
-function Nv(a,b,c,d,e){if(e){var f=[];for(e=0;e<b.length;e++)f.push(a.decode(b[e],c,d,!1));return f}f=c&&c.ka;if(2===b.length&&"string"===typeof b[0]&&(e=a.decode(b[0],c,!1,!1))&&e instanceof Kv)return b=b[1],f=a.Ea[e.ib],null!=f?f=f(a.decode(b,c,d,!0),a):nv(e.ib,a.decode(b,c,d,!1));c&&f!=c.ka&&(c.ka=f);if(a.Ac){if(32>=b.length&&a.Ac.qc){f=[];for(e=0;e<b.length;e++)f.push(a.decode(b[e],c,d,!1));return a.Ac.qc(f,b)}f=a.Ac.Jc(b);for(e=0;e<b.length;e++)f=a.Ac.add(f,a.decode(b[e],c,d,!1),b);return a.Ac.Td(f,
-b)}f=[];for(e=0;e<b.length;e++)f.push(a.decode(b[e],c,d,!1));return f}function Mv(a,b){if("~"===b.charAt(0)){var c=b.charAt(1);if("~"===c||"^"===c||"`"===c)return b.substring(1);if("#"===c)return new Kv(b.substring(2));var d=a.Ea[c];return null==d?a.De(c,b.substring(2)):d(b.substring(2),a)}return b};function Ov(a){this.Vf=new Lv(a)}function Pv(a,b){this.og=a;this.options=b||{};this.cache=this.options.cache?this.options.cache:new Jv}Pv.prototype.read=function(a){var b=this.cache;a=this.og.Vf.decode(JSON.parse(a),b);this.cache.clear();return a};Pv.prototype.read=Pv.prototype.read;var Rv=0,Sv=(8|3&Math.round(14*Math.random())).toString(16),Tv="transit$guid$"+(hu()+hu()+hu()+hu()+hu()+hu()+hu()+hu()+"-"+hu()+hu()+hu()+hu()+"-4"+hu()+hu()+hu()+"-"+Sv+hu()+hu()+hu()+"-"+hu()+hu()+hu()+hu()+hu()+hu()+hu()+hu()+hu()+hu()+hu()+hu());
-function Uv(a){if(null==a)return"null";if(a===String)return"string";if(a===Boolean)return"boolean";if(a===Number)return"number";if(a===Array)return"array";if(a===Object)return"map";var b=a[Tv];null==b&&("undefined"!=typeof Object.defineProperty?(b=++Rv,Object.defineProperty(a,Tv,{value:b,enumerable:!1})):a[Tv]=b=++Rv);return b}function Vv(a,b){for(var c=a.toString(),d=c.length;d<b;d++)c="0"+c;return c}function Wv(){}Wv.prototype.tag=function(){return"_"};Wv.prototype.da=function(){return null};
-Wv.prototype.va=function(){return"null"};function Xv(){}Xv.prototype.tag=function(){return"s"};Xv.prototype.da=function(a){return a};Xv.prototype.va=function(a){return a};function Yv(){}Yv.prototype.tag=function(){return"i"};Yv.prototype.da=function(a){return a};Yv.prototype.va=function(a){return a.toString()};function Zv(){}Zv.prototype.tag=function(){return"i"};Zv.prototype.da=function(a){return a.toString()};Zv.prototype.va=function(a){return a.toString()};function $v(){}$v.prototype.tag=function(){return"?"};
-$v.prototype.da=function(a){return a};$v.prototype.va=function(a){return a.toString()};function aw(){}aw.prototype.tag=function(){return"array"};aw.prototype.da=function(a){return a};aw.prototype.va=function(){return null};function bw(){}bw.prototype.tag=function(){return"map"};bw.prototype.da=function(a){return a};bw.prototype.va=function(){return null};function cw(){}cw.prototype.tag=function(){return"t"};
-cw.prototype.da=function(a){return a.getUTCFullYear()+"-"+Vv(a.getUTCMonth()+1,2)+"-"+Vv(a.getUTCDate(),2)+"T"+Vv(a.getUTCHours(),2)+":"+Vv(a.getUTCMinutes(),2)+":"+Vv(a.getUTCSeconds(),2)+"."+Vv(a.getUTCMilliseconds(),3)+"Z"};cw.prototype.va=function(a,b){return b.da(a)};function dw(){}dw.prototype.tag=function(){return"m"};dw.prototype.da=function(a){return a.valueOf()};dw.prototype.va=function(a){return a.valueOf().toString()};function ew(){}ew.prototype.tag=function(){return"u"};
-ew.prototype.da=function(a){return a.toString()};ew.prototype.va=function(a){return a.toString()};function fw(){}fw.prototype.tag=function(){return":"};fw.prototype.da=function(a){return a.ya};fw.prototype.va=function(a,b){return b.da(a)};function gw(){}gw.prototype.tag=function(){return"$"};gw.prototype.da=function(a){return a.ya};gw.prototype.va=function(a,b){return b.da(a)};function hw(){}hw.prototype.tag=function(a){return a.tag};hw.prototype.da=function(a){return a.da};hw.prototype.va=function(){return null};
-function iw(){}iw.prototype.tag=function(){return"set"};iw.prototype.da=function(a){var b=[];a.forEach(function(a){b.push(a)});return nv("array",b)};iw.prototype.va=function(){return null};function jw(){}jw.prototype.tag=function(){return"map"};jw.prototype.da=function(a){return a};jw.prototype.va=function(){return null};function kw(){}kw.prototype.tag=function(){return"map"};kw.prototype.da=function(a){return a};kw.prototype.va=function(){return null};function lw(){}lw.prototype.tag=function(){return"b"};
-lw.prototype.da=function(a){return a.toString("base64")};lw.prototype.va=function(){return null};function mw(){}mw.prototype.tag=function(){return"b"};
-mw.prototype.da=function(a){for(var b=0,c=a.length,d="",e=null;b<c;)e=a.subarray(b,Math.min(b+32768,c)),d+=String.fromCharCode.apply(null,e),b+=32768;var f;if("undefined"!=typeof btoa)f=btoa(d);else{a=String(d);c=0;d="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/\x3d";for(e="";a.charAt(c|0)||(d="\x3d",c%1);e+=d.charAt(63&f>>8-c%1*8)){b=a.charCodeAt(c+=.75);if(255<b)throw Error("'btoa' failed: The string to be encoded contains characters outside of the Latin1 range.");f=f<<8|b}f=
-e}return f};mw.prototype.va=function(){return null};
-function nw(){this.Ea={};this.set(null,new Wv);this.set(String,new Xv);this.set(Number,new Yv);this.set(qu,new Zv);this.set(Boolean,new $v);this.set(Array,new aw);this.set(Object,new bw);this.set(Date,new dw);this.set(tv,new ew);this.set(qv,new fw);this.set(rv,new gw);this.set(mv,new hw);this.set(Ev,new iw);this.set(yv,new jw);this.set(xv,new kw);"undefined"!=typeof Buffer&&this.set(Buffer,new lw);"undefined"!=typeof Uint8Array&&this.set(Uint8Array,new mw)}
-nw.prototype.get=function(a){var b=null,b="string"===typeof a?this.Ea[a]:this.Ea[Uv(a)];return null!=b?b:this.Ea["default"]};nw.prototype.get=nw.prototype.get;nw.prototype.set=function(a,b){var c;if(c="string"===typeof a)a:{switch(a){case "null":case "string":case "boolean":case "number":case "array":case "map":c=!1;break a}c=!0}c?this.Ea[a]=b:this.Ea[Uv(a)]=b};function ow(a){this.fc=a||{};this.Zd=null!=this.fc.preferStrings?this.fc.preferStrings:!0;this.pf=this.fc.objectBuilder||null;this.Ea=new nw;if(a=this.fc.handlers){if(gu(a)||!a.forEach)throw Error('transit writer "handlers" option must be a map');var b=this;a.forEach(function(a,d){if(void 0!==d)b.Ea.set(d,a);else throw Error("Cannot create handler for JavaScript undefined");})}this.ld=this.fc.handlerForForeign;this.de=this.fc.unpack||function(a){return a instanceof yv&&null===a.la?a.na:!1};this.xd=
-this.fc&&this.fc.verbose||!1}ow.prototype.rb=function(a){var b=this.Ea.get(null==a?null:a.constructor);return null!=b?b:(a=a&&a.transitTag)?this.Ea.get(a):null};function pw(a,b,c,d,e){a=a+b+c;return e?e.write(a,d):a}function qw(a,b,c){var d=[];if(gu(b))for(var e=0;e<b.length;e++)d.push(rw(a,b[e],!1,c));else b.forEach(function(b){d.push(rw(a,b,!1,c))});return d}function sw(a,b){if("string"!==typeof b){var c=a.rb(b);return c&&1===c.tag(b).length}return!0}
-function tw(a,b){var c=a.de(b),d=!0;if(c){for(var e=0;e<c.length&&(d=sw(a,c[e]),d);e+=2);return d}if(b.keys&&(c=b.keys(),e=null,c.next)){for(e=c.next();!e.done;){d=sw(a,e.value);if(!d)break;e=c.next()}return d}if(b.forEach)return b.forEach(function(b,c){d=d&&sw(a,c)}),d;throw Error("Cannot walk keys of object type "+(null==b?null:b.constructor).name);}
-function uw(a){if(a.constructor.transit$isObject)return!0;var b=a.constructor.toString(),b=b.substr(9),b=b.substr(0,b.indexOf("("));isObject="Object"==b;"undefined"!=typeof Object.defineProperty?Object.defineProperty(a.constructor,"transit$isObject",{value:isObject,enumerable:!1}):a.constructor.transit$isObject=isObject;return isObject}
-function vw(a,b,c){var d=null,e=null,f=null,d=null,g=0;if(b.constructor===Object||null!=b.forEach||a.ld&&uw(b)){if(a.xd){if(null!=b.forEach)if(tw(a,b)){var k={};b.forEach(function(b,d){k[rw(a,d,!0,!1)]=rw(a,b,!1,c)})}else{d=a.de(b);e=[];f=pw("~#","cmap","",!0,c);if(d)for(;g<d.length;g+=2)e.push(rw(a,d[g],!1,!1)),e.push(rw(a,d[g+1],!1,c));else b.forEach(function(b,d){e.push(rw(a,d,!1,!1));e.push(rw(a,b,!1,c))});k={};k[f]=e}else for(d=fu(b),k={};g<d.length;g++)k[rw(a,d[g],!0,!1)]=rw(a,b[d[g]],!1,c);
-return k}if(null!=b.forEach){if(tw(a,b)){d=a.de(b);k=["^ "];if(d)for(;g<d.length;g+=2)k.push(rw(a,d[g],!0,c)),k.push(rw(a,d[g+1],!1,c));else b.forEach(function(b,d){k.push(rw(a,d,!0,c));k.push(rw(a,b,!1,c))});return k}d=a.de(b);e=[];f=pw("~#","cmap","",!0,c);if(d)for(;g<d.length;g+=2)e.push(rw(a,d[g],!1,c)),e.push(rw(a,d[g+1],!1,c));else b.forEach(function(b,d){e.push(rw(a,d,!1,c));e.push(rw(a,b,!1,c))});return[f,e]}k=["^ "];for(d=fu(b);g<d.length;g++)k.push(rw(a,d[g],!0,c)),k.push(rw(a,b[d[g]],!1,
-c));return k}if(null!=a.pf)return a.pf(b,function(b){return rw(a,b,!0,c)},function(b){return rw(a,b,!1,c)});g=(null==b?null:b.constructor).name;d=Error("Cannot write "+g);d.data={Ke:b,type:g};throw d;}
-function rw(a,b,c,d){var e=a.rb(b)||(a.ld?a.ld(b,a.Ea):null),f=e?e.tag(b):null,g=e?e.da(b):null;if(null!=e&&null!=f)switch(f){case "_":return c?pw("~","_","",c,d):null;case "s":return 0<g.length?(a=g.charAt(0),a="~"===a||"^"===a||"`"===a?"~"+g:g):a=g,pw("","",a,c,d);case "?":return c?pw("~","?",g.toString()[0],c,d):g;case "i":return Infinity===g?pw("~","z","INF",c,d):-Infinity===g?pw("~","z","-INF",c,d):isNaN(g)?pw("~","z","NaN",c,d):c||"string"===typeof g||g instanceof qu?pw("~","i",g.toString(),
-c,d):g;case "d":return c?pw(g.pg,"d",g,c,d):g;case "b":return pw("~","b",g,c,d);case "'":return a.xd?(b={},c=pw("~#","'","",!0,d),b[c]=rw(a,g,!1,d),d=b):d=[pw("~#","'","",!0,d),rw(a,g,!1,d)],d;case "array":return qw(a,g,d);case "map":return vw(a,g,d);default:a:{if(1===f.length){if("string"===typeof g){d=pw("~",f,g,c,d);break a}if(c||a.Zd){(a=a.xd&&new cw)?(f=a.tag(b),g=a.va(b,a)):g=e.va(b,e);if(null!==g){d=pw("~",f,g,c,d);break a}d=Error('Tag "'+f+'" cannot be encoded as string');d.data={tag:f,da:g,
-Ke:b};throw d;}}b=f;c=g;a.xd?(g={},g[pw("~#",b,"",!0,d)]=rw(a,c,!1,d),d=g):d=[pw("~#",b,"",!0,d),rw(a,c,!1,d)]}return d}else throw d=(null==b?null:b.constructor).name,a=Error("Cannot write "+d),a.data={Ke:b,type:d},a;}function ww(a,b){var c=a.rb(b)||(a.ld?a.ld(b,a.Ea):null);if(null!=c)return 1===c.tag(b).length?nv("'",b):b;var c=(null==b?null:b.constructor).name,d=Error("Cannot write "+c);d.data={Ke:b,type:c};throw d;}
-function xw(a,b){this.Rc=a;this.options=b||{};this.cache=!1===this.options.cache?null:this.options.cache?this.options.cache:new Iv}xw.prototype.$f=function(){return this.Rc};xw.prototype.marshaller=xw.prototype.$f;xw.prototype.write=function(a,b){var c=null,d=b||{},c=d.asMapKey||!1,e=this.Rc.xd?!1:this.cache;!1===d.marshalTop?c=rw(this.Rc,a,c,e):(d=this.Rc,c=JSON.stringify(rw(d,ww(d,a),c,e)));null!=this.cache&&this.cache.clear();return c};xw.prototype.write=xw.prototype.write;
-xw.prototype.register=function(a,b){this.Rc.Ea.set(a,b)};xw.prototype.register=xw.prototype.register;function yw(a,b){if("json"===a||"json-verbose"===a||null==a){var c=new Ov(b);return new Pv(c,b)}throw Error("Cannot create reader of type "+a);}function zw(a,b){if("json"===a||"json-verbose"===a||null==a){"json-verbose"===a&&(null==b&&(b={}),b.verbose=!0);var c=new ow(b);return new xw(c,b)}c=Error('Type must be "json"');c.data={type:a};throw c;};$i.prototype.L=function(a,b){return b instanceof $i?this.Mb===b.Mb:b instanceof tv?this.Mb===b.toString():!1};$i.prototype.ic=!0;$i.prototype.Sb=function(a,b){if(b instanceof $i||b instanceof tv)return ed(this.toString(),b.toString());throw Error([y("Cannot compare "),y(this),y(" to "),y(b)].join(""));};tv.prototype.ic=!0;tv.prototype.Sb=function(a,b){if(b instanceof $i||b instanceof tv)return ed(this.toString(),b.toString());throw Error([y("Cannot compare "),y(this),y(" to "),y(b)].join(""));};
-qu.prototype.L=function(a,b){return this.equiv(b)};tv.prototype.L=function(a,b){return b instanceof $i?rc(b,this):this.equiv(b)};mv.prototype.L=function(a,b){return this.equiv(b)};qu.prototype.ve=!0;qu.prototype.W=function(){return ou.j?ou.j(this):ou.call(null,this)};tv.prototype.ve=!0;tv.prototype.W=function(){return id(this.toString())};mv.prototype.ve=!0;mv.prototype.W=function(){return ou.j?ou.j(this):ou.call(null,this)};tv.prototype.ja=!0;
-tv.prototype.T=function(a,b){return Ac(b,[y('#uuid "'),y(this.toString()),y('"')].join(""))};function Aw(a,b){for(var c=K(pe(b)),d=null,e=0,f=0;;)if(f<e){var g=d.aa(null,f);a[g]=b[g];f+=1}else if(c=K(c))d=c,oe(d)?(c=Nc(d),f=Oc(d),d=c,e=R(c),c=f):(c=C(d),a[c]=b[c],c=D(d),d=null,e=0),f=0;else break;return a}function Bw(){}Bw.prototype.Jc=function(){return Fc(rf)};Bw.prototype.add=function(a,b,c){return Ic(a,b,c)};Bw.prototype.Td=function(a){return Hc(a)};
-Bw.prototype.qc=function(a){return Yg.l?Yg.l(a,!0,!0):Yg.call(null,a,!0,!0)};function Cw(){}Cw.prototype.Jc=function(){return Fc(Wd)};Cw.prototype.add=function(a,b){return jf.h(a,b)};Cw.prototype.Td=function(a){return Hc(a)};Cw.prototype.qc=function(a){return xg.h?xg.h(a,!0):xg.call(null,a,!0)};
-function Dw(a,b){var c=Ne(a),d=Aw({handlers:Ci(Ph.w(J([new r(null,5,["$",function(){return function(a){return ld.j(a)}}(c),":",function(){return function(a){return Ye.j(a)}}(c),"set",function(){return function(a){return Yf.h(Wh,a)}}(c),"list",function(){return function(a){return Yf.h(nd,a.reverse())}}(c),"cmap",function(){return function(a){for(var b=0,c=Fc(rf);;)if(b<a.length)var d=b+2,c=Ic(c,a[b],a[b+1]),b=d;else return Hc(c)}}(c)],null),Il.j(b)],0))),mapBuilder:new Bw,arrayBuilder:new Cw,prefersStrings:!1},
-Ci(be.h(b,Il)));return yw.h?yw.h(c,d):yw.call(null,c,d)}function Ew(){}Ew.prototype.tag=function(){return":"};Ew.prototype.da=function(a){return a.ab};Ew.prototype.va=function(a){return a.ab};function Fw(){}Fw.prototype.tag=function(){return"$"};Fw.prototype.da=function(a){return a.ib};Fw.prototype.va=function(a){return a.ib};function Gw(){}Gw.prototype.tag=function(){return"list"};
-Gw.prototype.da=function(a){var b=[];a=K(a);for(var c=null,d=0,e=0;;)if(e<d){var f=c.aa(null,e);b.push(f);e+=1}else if(a=K(a))c=a,oe(c)?(a=Nc(c),e=Oc(c),c=a,d=R(a),a=e):(a=C(c),b.push(a),a=D(c),c=null,d=0),e=0;else break;return nv.h?nv.h("array",b):nv.call(null,"array",b)};Gw.prototype.va=function(){return null};function Hw(){}Hw.prototype.tag=function(){return"map"};Hw.prototype.da=function(a){return a};Hw.prototype.va=function(){return null};function Iw(){}Iw.prototype.tag=function(){return"set"};
-Iw.prototype.da=function(a){var b=[];a=K(a);for(var c=null,d=0,e=0;;)if(e<d){var f=c.aa(null,e);b.push(f);e+=1}else if(a=K(a))c=a,oe(c)?(a=Nc(c),e=Oc(c),c=a,d=R(a),a=e):(a=C(c),b.push(a),a=D(c),c=null,d=0),e=0;else break;return nv.h?nv.h("array",b):nv.call(null,"array",b)};Iw.prototype.va=function(){return null};function Jw(){}Jw.prototype.tag=function(){return"array"};
-Jw.prototype.da=function(a){var b=[];a=K(a);for(var c=null,d=0,e=0;;)if(e<d){var f=c.aa(null,e);b.push(f);e+=1}else if(a=K(a))c=a,oe(c)?(a=Nc(c),e=Oc(c),c=a,d=R(a),a=e):(a=C(c),b.push(a),a=D(c),c=null,d=0),e=0;else break;return b};Jw.prototype.va=function(){return null};function Kw(){}Kw.prototype.tag=function(){return"u"};Kw.prototype.da=function(a){return a.Mb};Kw.prototype.va=function(a){return this.da(a)};
-function Lw(a,b){var c=new Ew,d=new Fw,e=new Gw,f=new Hw,g=new Iw,k=new Jw,l=new Kw,n=Ph.w(J([ae([$d,Ve,r,qh,Gg,B,v,Te,Ze,Bg,Fg,rh,Oh,Sg,V,Od,Nd,Vh,Ih,Nh,ne,Xh,me,dd,$i,ci,wh],[f,e,f,e,e,e,c,e,e,k,e,e,e,e,k,e,e,g,f,e,e,g,e,d,l,e,e]),Il.j(b)],0)),m=Ne(a),t=Aw({objectBuilder:function(a,b,c,d,e,f,g,k,l){return function(m,n,t){return Ae(function(){return function(a,b,c){a.push(n.j?n.j(b):n.call(null,b),t.j?t.j(c):t.call(null,c));return a}}(a,b,c,d,e,f,g,k,l),["^ "],m)}}(m,c,d,e,f,g,k,l,n),handlers:function(){var a=
-Db(n);a.forEach=function(){return function(a){for(var b=K(this),c=null,d=0,e=0;;)if(e<d){var f=c.aa(null,e),g=S(f,0,null),f=S(f,1,null);a.h?a.h(f,g):a.call(null,f,g);e+=1}else if(b=K(b))oe(b)?(c=Nc(b),b=Oc(b),g=c,d=R(c),c=g):(c=C(b),g=S(c,0,null),f=S(c,1,null),a.h?a.h(f,g):a.call(null,f,g),b=D(b),c=null,d=0),e=0;else return null}}(a,m,c,d,e,f,g,k,l,n);return a}(),unpack:function(){return function(a){return a instanceof r?a.v:!1}}(m,c,d,e,f,g,k,l,n)},Ci(be.h(b,Il)));return zw.h?zw.h(m,t):zw.call(null,
-m,t)};var Mw=function Mw(b){if(null!=b&&null!=b.ff)return b.ff();var c=Mw[p(null==b?null:b)];if(null!=c)return c.j?c.j(b):c.call(null,b);c=Mw._;if(null!=c)return c.j?c.j(b):c.call(null,b);throw x("PushbackReader.read-char",b);},Nw=function Nw(b,c){if(null!=b&&null!=b.gf)return b.gf(0,c);var d=Nw[p(null==b?null:b)];if(null!=d)return d.h?d.h(b,c):d.call(null,b,c);d=Nw._;if(null!=d)return d.h?d.h(b,c):d.call(null,b,c);throw x("PushbackReader.unread",b);};
-function Ow(a,b,c){this.s=a;this.buffer=b;this.ka=c}Ow.prototype.ff=function(){return 0===this.buffer.length?(this.ka+=1,this.s[this.ka]):this.buffer.pop()};Ow.prototype.gf=function(a,b){return this.buffer.push(b)};function Pw(a){var b=!/[^\t\n\r ]/.test(a);return u(b)?b:","===a}Qw;Rw;Sw;function Tw(a){throw Error(A.h(y,a));}
-function Uw(a,b){for(var c=new Ga(b),d=Mw(a);;){var e;if(!(e=null==d||Pw(d))){e=d;var f="#"!==e;e=f?(f="'"!==e)?(f=":"!==e)?Rw.j?Rw.j(e):Rw.call(null,e):f:f:f}if(e)return Nw(a,d),c.toString();c.append(d);d=Mw(a)}}function Vw(a){for(;;){var b=Mw(a);if("\n"===b||"\r"===b||null==b)return a}}var Ww=hi("^([-+]?)(?:(0)|([1-9][0-9]*)|0[xX]([0-9A-Fa-f]+)|0([0-7]+)|([1-9][0-9]?)[rR]([0-9A-Za-z]+))(N)?$"),Xw=hi("^([-+]?[0-9]+)/([0-9]+)$"),Yw=hi("^([-+]?[0-9]+(\\.[0-9]*)?([eE][-+]?[0-9]+)?)(M)?$"),Zw=hi("^[:]?([^0-9/].*/)?([^0-9/][^/]*)$");
-function $w(a,b){var c=a.exec(b);return null!=c&&c[0]===b?1===c.length?c[0]:c:null}var ax=hi("^[0-9A-Fa-f]{2}$"),bx=hi("^[0-9A-Fa-f]{4}$");function cx(a,b,c){return u(gi(a,c))?c:Tw(J(["Unexpected unicode escape \\",b,c],0))}function dx(a){return String.fromCharCode(parseInt(a,16))}
-function ex(a){var b=Mw(a),c="t"===b?"\t":"r"===b?"\r":"n"===b?"\n":"\\"===b?"\\":'"'===b?'"':"b"===b?"\b":"f"===b?"\f":null;u(c)?b=c:"x"===b?(a=(new Ga(Mw(a),Mw(a))).toString(),b=dx(cx(ax,b,a))):"u"===b?(a=(new Ga(Mw(a),Mw(a),Mw(a),Mw(a))).toString(),b=dx(cx(bx,b,a))):b=/[^0-9]/.test(b)?Tw(J(["Unexpected unicode escape \\",b],0)):String.fromCharCode(b);return b}
-function fx(a,b){for(var c=Fc(Wd);;){var d;a:{d=Pw;for(var e=b,f=Mw(e);;)if(u(d.j?d.j(f):d.call(null,f)))f=Mw(e);else{d=f;break a}}u(d)||Tw(J(["EOF while reading"],0));if(a===d)return Hc(c);e=Rw.j?Rw.j(d):Rw.call(null,d);u(e)?d=e.h?e.h(b,d):e.call(null,b,d):(Nw(b,d),d=Qw.G?Qw.G(b,!0,null,!0):Qw.call(null,b,!0,null));c=d===b?c:jf.h(c,d)}}function gx(a,b){return Tw(J(["Reader for ",b," not implemented yet"],0))}hx;
-function ix(a,b){var c=Mw(a),d=Sw.j?Sw.j(c):Sw.call(null,c);if(u(d))return d.h?d.h(a,b):d.call(null,a,b);d=hx.h?hx.h(a,c):hx.call(null,a,c);return u(d)?d:Tw(J(["No dispatch macro for ",c],0))}function jx(a,b){return Tw(J(["Unmatched delimiter ",b],0))}function kx(a){return A.h(G,fx(")",a))}function lx(a){return fx("]",a)}
-function mx(a){a=fx("}",a);var b=R(a);if("number"!==typeof b||isNaN(b)||Infinity===b||parseFloat(b)!==parseInt(b,10))throw Error([y("Argument must be an integer: "),y(b)].join(""));0!==(b&1)&&Tw(J(["Map literal must contain an even number of forms"],0));return A.h(P,a)}function nx(a){for(var b=new Ga,c=Mw(a);;){if(null==c)return Tw(J(["EOF while reading"],0));if("\\"===c)b.append(ex(a));else{if('"'===c)return b.toString();b.append(c)}c=Mw(a)}}
-function ox(a){for(var b=new Ga,c=Mw(a);;){if(null==c)return Tw(J(["EOF while reading"],0));if("\\"===c){b.append(c);var d=Mw(a);if(null==d)return Tw(J(["EOF while reading"],0));var e=function(){var a=b;a.append(d);return a}(),f=Mw(a)}else{if('"'===c)return b.toString();e=function(){var a=b;a.append(c);return a}();f=Mw(a)}b=e;c=f}}
-function px(a,b){var c=Uw(a,b),d=-1!=c.indexOf("/");u(u(d)?1!==c.length:d)?c=ld.h(c.substring(0,c.indexOf("/")),c.substring(c.indexOf("/")+1,c.length)):(d=ld.j(c),c="nil"===c?null:"true"===c?!0:"false"===c?!1:"/"===c?Zm:d);return c}
-function qx(a,b){var c=Uw(a,b),d=c.substring(1);return 1===d.length?d:"tab"===d?"\t":"return"===d?"\r":"newline"===d?"\n":"space"===d?" ":"backspace"===d?"\b":"formfeed"===d?"\f":"u"===d.charAt(0)?dx(d.substring(1)):"o"===d.charAt(0)?gx(0,c):Tw(J(["Unknown character literal: ",c],0))}
-function rx(a){a=Uw(a,Mw(a));var b=$w(Zw,a);a=b[0];var c=b[1],b=b[2];return void 0!==c&&":/"===c.substring(c.length-2,c.length)||":"===b[b.length-1]||-1!==a.indexOf("::",1)?Tw(J(["Invalid token: ",a],0)):null!=c&&0<c.length?Ye.h(c.substring(0,c.indexOf("/")),b):Ye.j(a)}function sx(a){return function(b){return Jb(Jb(nd,Qw.G?Qw.G(b,!0,null,!0):Qw.call(null,b,!0,null)),a)}}function tx(){return function(){return Tw(J(["Unreadable form"],0))}}
-function ux(a){var b;b=Qw.G?Qw.G(a,!0,null,!0):Qw.call(null,a,!0,null);b=b instanceof dd?new r(null,1,[Jn,b],null):"string"===typeof b?new r(null,1,[Jn,b],null):b instanceof v?Yg([b,!0],!0,!1):b;ke(b)||Tw(J(["Metadata must be Symbol,Keyword,String or Map"],0));a=Qw.G?Qw.G(a,!0,null,!0):Qw.call(null,a,!0,null);return(null!=a?a.o&262144||a.yg||(a.o?0:ub(lc,a)):ub(lc,a))?Bd(a,Ph.w(J([ee(a),b],0))):Tw(J(["Metadata can only be applied to IWithMetas"],0))}function vx(a){return Zh(fx("}",a))}
-function wx(a){return hi(ox(a))}function xx(a){Qw.G?Qw.G(a,!0,null,!0):Qw.call(null,a,!0,null);return a}function Rw(a){return'"'===a?nx:":"===a?rx:";"===a?Vw:"'"===a?sx(pf):"@"===a?sx(go):"^"===a?ux:"`"===a?gx:"~"===a?gx:"("===a?kx:")"===a?jx:"["===a?lx:"]"===a?jx:"{"===a?mx:"}"===a?jx:"\\"===a?qx:"#"===a?ix:null}function Sw(a){return"{"===a?vx:"\x3c"===a?tx():'"'===a?wx:"!"===a?Vw:"_"===a?xx:null}
-function Qw(a,b,c){for(;;){var d=Mw(a);if(null==d)return u(b)?Tw(J(["EOF while reading"],0)):c;if(!Pw(d))if(";"===d)a=Vw.h?Vw.h(a,d):Vw.call(null,a);else{var e=Rw(d);if(u(e))e=e.h?e.h(a,d):e.call(null,a,d);else{var e=a,f=void 0;!(f=!/[^0-9]/.test(d))&&(f=void 0,f="+"===d||"-"===d)&&(f=Mw(e),Nw(e,f),f=!/[^0-9]/.test(f));if(f)a:for(e=a,d=new Ga(d),f=Mw(e);;){var g;g=null==f;g||(g=(g=Pw(f))?g:Rw.j?Rw.j(f):Rw.call(null,f));if(u(g)){Nw(e,f);d=e=d.toString();f=void 0;u($w(Ww,d))?(d=$w(Ww,d),f=d[2],null!=
-(H.h(f,"")?null:f)?f=0:(f=u(d[3])?[d[3],10]:u(d[4])?[d[4],16]:u(d[5])?[d[5],8]:u(d[6])?[d[7],parseInt(d[6],10)]:[null,null],g=f[0],null==g?f=null:(f=parseInt(g,f[1]),f="-"===d[1]?-f:f))):(f=void 0,u($w(Xw,d))?(d=$w(Xw,d),f=parseInt(d[1],10)/parseInt(d[2],10)):f=u($w(Yw,d))?parseFloat(d):null);d=f;e=u(d)?d:Tw(J(["Invalid number format [",e,"]"],0));break a}d.append(f);f=Mw(e)}else e=px(a,d)}if(e!==a)return e}}}
-var yx=function(a,b){return function(c,d){return I.h(u(d)?b:a,c)}}(new V(null,13,5,W,[null,31,28,31,30,31,30,31,31,30,31,30,31],null),new V(null,13,5,W,[null,31,29,31,30,31,30,31,31,30,31,30,31],null)),zx=/(\d\d\d\d)(?:-(\d\d)(?:-(\d\d)(?:[T](\d\d)(?::(\d\d)(?::(\d\d)(?:[.](\d+))?)?)?)?)?)?(?:[Z]|([-+])(\d\d):(\d\d))?/;function Ax(a){a=parseInt(a,10);return tb(isNaN(a))?a:null}
-function Bx(a,b,c,d){a<=b&&b<=c||Tw(J([[y(d),y(" Failed: "),y(a),y("\x3c\x3d"),y(b),y("\x3c\x3d"),y(c)].join("")],0));return b}
-function Cx(a){var b=gi(zx,a);S(b,0,null);var c=S(b,1,null),d=S(b,2,null),e=S(b,3,null),f=S(b,4,null),g=S(b,5,null),k=S(b,6,null),l=S(b,7,null),n=S(b,8,null),m=S(b,9,null),t=S(b,10,null);if(tb(b))return Tw(J([[y("Unrecognized date/time syntax: "),y(a)].join("")],0));var q=Ax(c),z=function(){var a=Ax(d);return u(a)?a:1}();a=function(){var a=Ax(e);return u(a)?a:1}();var b=function(){var a=Ax(f);return u(a)?a:0}(),c=function(){var a=Ax(g);return u(a)?a:0}(),w=function(){var a=Ax(k);return u(a)?a:0}(),
-E=function(){var a;a:if(H.h(3,R(l)))a=l;else if(3<R(l))a=l.substring(0,3);else for(a=new Ga(l);;)if(3>a.Zb.length)a=a.append("0");else{a=a.toString();break a}a=Ax(a);return u(a)?a:0}(),n=(H.h(n,"-")?-1:1)*(60*function(){var a=Ax(m);return u(a)?a:0}()+function(){var a=Ax(t);return u(a)?a:0}());return new V(null,8,5,W,[q,Bx(1,z,12,"timestamp month field must be in range 1..12"),Bx(1,a,function(){var a;a=0===Ie(q,4);u(a)&&(a=tb(0===Ie(q,100)),a=u(a)?a:0===Ie(q,400));return yx.h?yx.h(z,a):yx.call(null,
-z,a)}(),"timestamp day field must be in range 1..last day in month"),Bx(0,b,23,"timestamp hour field must be in range 0..23"),Bx(0,c,59,"timestamp minute field must be in range 0..59"),Bx(0,w,H.h(c,59)?60:59,"timestamp second field must be in range 0..60"),Bx(0,E,999,"timestamp millisecond field must be in range 0..999"),n],null)}
-var Dx,Ex=new r(null,4,["inst",function(a){var b;if("string"===typeof a)if(b=Cx(a),u(b)){a=S(b,0,null);var c=S(b,1,null),d=S(b,2,null),e=S(b,3,null),f=S(b,4,null),g=S(b,5,null),k=S(b,6,null);b=S(b,7,null);b=new Date(Date.UTC(a,c-1,d,e,f,g,k)-6E4*b)}else b=Tw(J([[y("Unrecognized date/time syntax: "),y(a)].join("")],0));else b=Tw(J(["Instance literal expects a string for its timestamp."],0));return b},"uuid",function(a){return"string"===typeof a?new $i(a,null):Tw(J(["UUID literal expects a string as its representation."],
-0))},"queue",function(a){return le(a)?Yf.h(Hg,a):Tw(J(["Queue literal expects a vector for its elements."],0))},"js",function(a){if(le(a)){var b=[];a=K(a);for(var c=null,d=0,e=0;;)if(e<d){var f=c.aa(null,e);b.push(f);e+=1}else if(a=K(a))c=a,oe(c)?(a=Nc(c),e=Oc(c),c=a,d=R(a),a=e):(a=C(c),b.push(a),a=D(c),c=null,d=0),e=0;else break;return b}if(ke(a)){b={};a=K(a);c=null;for(e=d=0;;)if(e<d){var g=c.aa(null,e),f=S(g,0,null),g=S(g,1,null);b[Ne(f)]=g;e+=1}else if(a=K(a))oe(a)?(d=Nc(a),a=Oc(a),c=d,d=R(d)):
-(d=C(a),c=S(d,0,null),d=S(d,1,null),b[Ne(c)]=d,a=D(a),c=null,d=0),e=0;else break;return b}return Tw(J([[y("JS literal expects a vector or map containing "),y("only string or unqualified keyword keys")].join("")],0))}],null);Dx=X.j?X.j(Ex):X.call(null,Ex);var Fx=X.j?X.j(null):X.call(null,null);
-function hx(a,b){var c=px(a,b),d=I.h(Q.j?Q.j(Dx):Q.call(null,Dx),""+y(c)),e=Q.j?Q.j(Fx):Q.call(null,Fx);return u(d)?(c=Qw(a,!0,null),d.j?d.j(c):d.call(null,c)):u(e)?(d=Qw(a,!0,null),e.h?e.h(c,d):e.call(null,c,d)):Tw(J(["Could not find tag parser for ",""+y(c)," in ",Df.w(J([Tg(Q.j?Q.j(Dx):Q.call(null,Dx))],0))],0))};var Gx=function Gx(b,c,d,e,f,g,k){if(null!=b&&null!=b.je)return b.je(b,c,d,e,f,g,k);var l=Gx[p(null==b?null:b)];if(null!=l)return l.ta?l.ta(b,c,d,e,f,g,k):l.call(null,b,c,d,e,f,g,k);l=Gx._;if(null!=l)return l.ta?l.ta(b,c,d,e,f,g,k):l.call(null,b,c,d,e,f,g,k);throw x("AjaxImpl.-js-ajax-request",b);};function Hx(){}
-var Ix=function Ix(b){if(null!=b&&null!=b.me)return b.me(b);var c=Ix[p(null==b?null:b)];if(null!=c)return c.j?c.j(b):c.call(null,b);c=Ix._;if(null!=c)return c.j?c.j(b):c.call(null,b);throw x("AjaxResponse.-status",b);},Jx=function Jx(b){if(null!=b&&null!=b.ne)return b.ne(b);var c=Jx[p(null==b?null:b)];if(null!=c)return c.j?c.j(b):c.call(null,b);c=Jx._;if(null!=c)return c.j?c.j(b):c.call(null,b);throw x("AjaxResponse.-status-text",b);},Kx=function Kx(b){if(null!=b&&null!=b.ke)return b.ke(b);var c=
-Kx[p(null==b?null:b)];if(null!=c)return c.j?c.j(b):c.call(null,b);c=Kx._;if(null!=c)return c.j?c.j(b):c.call(null,b);throw x("AjaxResponse.-body",b);},Lx=function Lx(b,c){if(null!=b&&null!=b.le)return b.le(b,c);var d=Lx[p(null==b?null:b)];if(null!=d)return d.h?d.h(b,c):d.call(null,b,c);d=Lx._;if(null!=d)return d.h?d.h(b,c):d.call(null,b,c);throw x("AjaxResponse.-get-response-header",b);},Mx=function Mx(b){if(null!=b&&null!=b.oe)return b.oe(b);var c=Mx[p(null==b?null:b)];if(null!=c)return c.j?c.j(b):
-c.call(null,b);c=Mx._;if(null!=c)return c.j?c.j(b):c.call(null,b);throw x("AjaxResponse.-was-aborted",b);};"undefined"!==typeof FormData&&(FormData.prototype.yd=!0);"undefined"!==typeof ArrayBufferView&&(ArrayBufferView.prototype.yd=!0);"undefined"!==typeof Blob&&(Blob.prototype.yd=!0);"undefined"!==typeof Document&&(Document.prototype.yd=!0);function Nx(a){var b=null!=a?a.yd?!0:a.ed?!1:ub(Hx,a):ub(Hx,a);return b?b:"string"===typeof a}h=Mt.prototype;
-h.je=function(a,b,c,d,e,f,g){a=null!=g&&(g.o&64||g.D)?A.h(P,g):g;var k=I.l(a,On,0),l=I.l(a,jo,!1);Rs(this,"complete",function(){return function(a){a=a.target;return f.j?f.j(a):f.call(null,a)}}(this,"complete",this,this,g,a,k,l));this.Pc=Math.max(0,k);this.Df=l;this.send(b,c,d,Ci(e));return this};h.ke=function(){var a;try{a=this.ea?this.ea.responseText:""}catch(b){Dt(this.Db,"Can not get responseText: "+b.message),a=""}return a};h.me=function(){return $t(this)};h.ne=function(){return au(this)};
-h.le=function(a,b){return this.getResponseHeader(b)};h.oe=function(){return H.h(this.Lc,7)};h=XMLHttpRequest.prototype;
-h.je=function(a,b,c,d,e,f,g){a=null!=g&&(g.o&64||g.D)?A.h(P,g):g;var k=I.l(a,On,0),l=I.l(a,jo,!1);this.timeout=k;this.withCredentials=l;this.onreadystatechange=function(a){return function(b){return H.h(Om,(new r(null,5,[0,Sj,1,Wn,2,Wk,3,Ok,4,Om],null)).call(null,b.target.readyState))?f.j?f.j(a):f.call(null,a):null}}(this,g,a,k,l);this.open(c,b,!0);var n=this;(function(){for(var a=K(e),b=null,c=0,d=0;;)if(d<c){var f=b.aa(null,d),g=S(f,0,null),f=S(f,1,null);n.setRequestHeader(g,f);d+=1}else if(a=K(a))oe(a)?
-(b=Nc(a),a=Oc(a),g=b,c=R(b),b=g):(b=C(a),g=S(b,0,null),f=S(b,1,null),n.setRequestHeader(g,f),a=D(a),b=null,c=0),d=0;else return null})();this.send(u(d)?d:"");return this};h.ke=function(){return this.response};h.me=function(){return this.status};h.ne=function(){return this.statusText};h.le=function(a,b){return this.getResponseHeader(b)};h.oe=function(){return H.h(0,this.readyState)};
-function Ox(a){a:{a=[a];var b=a.length;if(b<=Wg)for(var c=0,d=Fc(rf);;)if(c<b)var e=c+1,d=Ic(d,a[c],null),c=e;else{a=new Vh(null,Hc(d),null);break a}else for(c=0,d=Fc(Wh);;)if(c<b)e=c+1,d=Gc(d,a[c]),c=e;else{a=Hc(d);break a}}return uf(a,new V(null,6,5,W,[200,201,202,204,205,206],null))}function Px(a){a=Kx(a);if("string"!==typeof a)throw Error("Cannot read from non-string object.");return Qw(new Ow(a,[],-1),!1,null)}
-var Qx=function Qx(b){for(var c=[],d=arguments.length,e=0;;)if(e<d)c.push(arguments[e]),e+=1;else break;switch(c.length){case 0:return Qx.A();case 1:return Qx.j(arguments[0]);default:throw Error([y("Invalid arity: "),y(c.length)].join(""));}};Qx.A=function(){return new r(null,3,[Oj,Px,ej,"EDN",jn,"application/edn"],null)};Qx.j=function(){return Qx.A()};Qx.J=1;function Rx(a){return function(b){return a.write(b)}}
-function Sx(a){a=null!=a&&(a.o&64||a.D)?A.h(P,a):a;var b=I.h(a,Vk),c=I.h(a,Pm);a=u(c)?c:Lw(u(b)?b:Ln,a);return new r(null,2,[km,Rx(a),jn,"application/transit+json; charset\x3dutf-8"],null)}function Tx(a,b){return function(c){c=Kx(c);c=a.read(c);return u(b)?c:Ii(c,J([new r(null,1,[Ji,!1],null)],0))}}
-var Ux=function Ux(b){for(var c=[],d=arguments.length,e=0;;)if(e<d)c.push(arguments[e]),e+=1;else break;switch(c.length){case 0:return Ux.A();case 1:return Ux.j(arguments[0]);default:throw Error([y("Invalid arity: "),y(c.length)].join(""));}};Ux.A=function(){return Ux.j(rf)};Ux.j=function(a){var b=null!=a&&(a.o&64||a.D)?A.h(P,a):a,c=I.h(b,Vk),d=I.h(b,Um);a=I.h(b,ik);b=u(d)?d:Dw(u(c)?c:Ln,b);return new r(null,3,[Oj,Tx(b,a),ej,"Transit",jn,"application/transit+json"],null)};Ux.J=1;
-function Vx(a){if(u(a)){var b=new nt(Ci(a));a=lt(b);if("undefined"==typeof a)throw Error("Keys are undefined");for(var c=new cu(null,0,void 0),b=kt(b),d=0;d<a.length;d++){var e=a[d],f=b[d];if(fa(f)){var g=c;g.remove(e);0<f.length&&(g.Cb=null,g.Ga.set(eu(g,e),Va(f)),g.Ba+=f.length)}else c.add(e,f)}a=c.toString()}else a=null;return a}function Wx(){return new r(null,2,[km,Vx,jn,"application/x-www-form-urlencoded"],null)}
-var Xx=function Xx(b){for(var c=[],d=arguments.length,e=0;;)if(e<d)c.push(arguments[e]),e+=1;else break;switch(c.length){case 0:return Xx.A();case 1:return Xx.j(arguments[0]);default:throw Error([y("Invalid arity: "),y(c.length)].join(""));}};Xx.A=function(){return new r(null,3,[Oj,Kx,ej,"raw text",jn,"*/*"],null)};Xx.j=function(){return Xx.A()};Xx.J=1;function Yx(a){var b=new ft;a=Ci(a);var c=[];gt(b,a,c);return c.join("")}
-function Zx(a,b,c){return function(d){d=Kx(d);d=u(u(a)?H.h(0,d.indexOf(a)):a)?d.substring(a.length()):d;d=et(d);return u(b)?d:Ii(d,J([Ji,c],0))}}var $x=function $x(b){for(var c=[],d=arguments.length,e=0;;)if(e<d)c.push(arguments[e]),e+=1;else break;switch(c.length){case 0:return $x.A();case 1:return $x.j(arguments[0]);default:throw Error([y("Invalid arity: "),y(c.length)].join(""));}};$x.A=function(){return $x.j(rf)};
-$x.j=function(a){var b=null!=a&&(a.o&64||a.D)?A.h(P,a):a;a=I.h(b,am);var c=I.h(b,Mj),b=I.h(b,ik);return new r(null,3,[Oj,Zx(a,b,c),ej,[y("JSON"),y(u(a)?[y(" prefix '"),y(a),y("'")].join(""):null),y(u(c)?" keywordize":null)].join(""),jn,"application/json"],null)};$x.J=1;var ay=new V(null,6,5,W,[$x,Qx,Ux,new V(null,2,5,W,["text/plain",Xx],null),new V(null,2,5,W,["text/html",Xx],null),Xx],null);function by(a,b){return le(b)?by(a,Td(b)):ke(b)?b:b.j?b.j(a):b.call(null,a)}
-function cy(a,b){var c=le(b)?C(b):jn.j(by(a,b));return u(c)?c:"*/*"}function dy(a){return function(b){b=le(b)?C(b):jn.j(by(a,b));return u(b)?b:"*/*"}}function ey(a,b){return function(c){c=cy(b,c);return H.h(c,"*/*")||0<=a.indexOf(c)}}function fy(a,b){var c=null!=b&&(b.o&64||b.D)?A.h(P,b):b,d=I.h(c,Ck),e=Lx(a,"Content-Type");return by(c,C(Wf(ey(u(e)?e:"",c),d)))}function gy(a){return function(b){return Oj.j(fy(b,a)).call(null,b)}}
-function hy(a){var b;b=null!=a&&(a.o&64||a.D)?A.h(P,a):a;var c=I.h(b,Ck);b=le(c)?zo(", ",Me.h(dy(b),c)):cy(b,c);return new r(null,3,[Oj,gy(a),uj,[y("(from "),y(b),y(")")].join(""),jn,b],null)}var iy=function iy(b){for(var c=[],d=arguments.length,e=0;;)if(e<d)c.push(arguments[e]),e+=1;else break;return iy.w(arguments[0],arguments[1],arguments[2],3<c.length?new B(c.slice(3),0):null)};
-iy.w=function(a,b,c,d){return new V(null,2,5,W,[!1,Ab.l(Vd,new r(null,3,[Mm,a,Dk,b,Zj,c],null),Me.h(ze,$f(2,2,d)))],null)};iy.J=3;iy.K=function(a){var b=C(a),c=D(a);a=C(c);var d=D(c),c=C(d),d=D(d);return iy.w(b,a,c,d)};
-function jy(a,b){var c=null!=a&&(a.o&64||a.D)?A.h(P,a):a,d=I.h(c,Oj);try{var e=Ix(b),f=yf.h(iy,e);if(H.h(-1,e))return u(Mx(b))?f.h?f.h("Request aborted by client.",Mk):f.call(null,"Request aborted by client.",Mk):f.h?f.h("Request timed out.",On):f.call(null,"Request timed out.",On);try{var g=d.j?d.j(b):d.call(null,b);if(u(Ox(e)))return new V(null,2,5,W,[!0,g],null);var k=Jx(b);return f.G?f.G(k,un,aj,g):f.call(null,k,un,aj,g)}catch(w){if(w instanceof Object){var f=w,d=W,l,n=null!=c&&(c.o&64||c.D)?
-A.h(P,c):c,m=I.h(n,ej),t=new r(null,3,[Mm,e,Zj,un,aj,null],null),q=[y(f.message),y(" Format should have been "),y(m)].join(""),z=T.w(t,Dk,q,J([Zj,Xm,Ij,Kx(b)],0));l=u(Ox(e))?z:T.w(t,Dk,Jx(b),J([Ql,z],0));return new V(null,2,5,d,[!1,l],null)}throw w;}}catch(w){if(w instanceof Object)return f=w,iy.w(0,f.message,Bn,J([Bn,f],0));throw w;}}function ky(a){return a instanceof v?Ne(a).toUpperCase():a}function ly(a,b){return function(c){c=jy(a,c);return b.j?b.j(c):b.call(null,c)}}
-function my(a,b){if(ke(a))return a;if(ce(a))return new r(null,1,[km,a],null);if(null==a)return Sx(b);switch(a instanceof v?a.ab:null){case "transit":return Sx(b);case "json":return new r(null,2,[km,Yx,jn,"application/json"],null);case "edn":return new r(null,2,[km,Df,jn,"application/edn"],null);case "raw":return Wx();case "url":return Wx();default:return null}}
-var ny=function ny(b,c){if(le(b))return new V(null,2,5,W,[C(b),ny(Td(b),c)],null);if(ke(b))return b;if(ce(b))return new r(null,2,[Oj,b,ej,"custom"],null);if(null==b)return hy(new r(null,1,[Ck,ay],null));switch(b instanceof v?b.ab:null){case "transit":return Ux.j(c);case "json":return $x.j(c);case "edn":return Qx.A();case "raw":return Xx.A();case "detect":return hy(new r(null,1,[Ck,ay],null));default:return null}};function oy(a,b){return le(a)?A.h(yg,Me.h(function(a){return ny(a,b)},a)):ny(a,b)}
-function py(a){var b=null!=a&&(a.o&64||a.D)?A.h(P,a):a,c=I.h(b,co),d=I.h(b,fm),e=I.h(b,oj);return function(a,b,c,d,e){return function(a){var b=S(a,0,null);a=S(a,1,null);b=u(b)?c:d;u(b)&&(b.j?b.j(a):b.call(null,a));return ce(e)?e.A?e.A():e.call(null):null}}(a,b,c,d,e)}
-function qy(a,b){var c=C(b),c=c instanceof v?A.h(P,b):c,c=T.w(c,Hn,a,J([dk,"GET"],0)),c=null!=c&&(c.o&64||c.D)?A.h(P,c):c,d=I.h(c,dk),e=I.h(c,uj),f=I.h(c,Ck),g=I.h(c,Pk),g=Nx(g),d=u(g)?g:H.h(d,"GET"),d=tb(d),e=u(u(e)?e:d)?my(e,c):null,c=T.w(c,co,py(c),J([uj,e,Ck,oy(f,c)],0)),c=null!=c&&(c.o&64||c.D)?A.h(P,c):c,e=I.h(c,dk),f=I.h(c,Hj);d=null!=c&&(c.o&64||c.D)?A.h(P,c):c;g=I.h(d,Ck);if(le(g))d=hy(d);else if(ke(g))d=g;else if(ue(g))d=new r(null,3,[Oj,g,ej,"custom",jn,"*/*"],null);else throw Error([y("unrecognized response format: "),
-y(g)].join(""));var e=ky(e),k;var l=d,n=null!=c&&(c.o&64||c.D)?A.h(P,c):c,g=I.h(n,Hn),m=I.h(n,dk);k=I.h(n,uj);var t=I.h(n,Pk),n=I.h(n,bm),l=null!=l&&(l.o&64||l.D)?A.h(P,l):l,l=I.h(l,jn),n=Ph.w(J([new r(null,1,["Accept",l],null),u(n)?n:rf],0));if(H.h(ky(m),"GET"))k=W,g=u(t)?[y(g),y("?"),y(Vx(t))].join(""):g,k=new V(null,3,5,k,[g,null,n],null);else{m=ke(k)?k:ue(k)?new r(null,2,[km,k,jn,"text/plain"],null):null;m=null!=m&&(m.o&64||m.D)?A.h(P,m):m;l=I.h(m,km);m=I.h(m,jn);if(null!=l)t=l.j?l.j(t):l.call(null,
-t);else if(!u(Nx(t)))throw Error([y("unrecognized request format: "),y(k)].join(""));k=Ph.w(J([n,u(m)?new r(null,1,["Content-Type",m],null):null],0));k=new V(null,3,5,W,[g,t,k],null)}g=S(k,0,null);t=S(k,1,null);k=S(k,2,null);n=null!=c&&(c.o&64||c.D)?A.h(P,c):c;n=I.h(n,co);if(u(n))d=ly(d,n);else throw Error("No ajax handler provided.");f=u(f)?f:new Mt;return Gx(f,g,e,t,k,d,c)};var ry=Error();var sy=rf;function ty(a){return a}var uy=ae([121,110,101,102,106,119,104,116,99,113,117,108,109,118,100,122,111,103,125,107,97,115,112,123,120,126,98,124,96,105,114],[8804,9532,9226,176,9496,9516,9252,9500,9228,9472,9508,9484,9492,9524,9229,8805,9146,177,163,9488,9618,9149,9147,960,9474,8901,9225,8800,9830,9227,9148]);function vy(a,b){return new V(null,2,5,W,[a,b],null)}function wy(a,b){return ze(Sf(a,vy(32,b)))}function xy(a,b,c){a=wy(a,c);return ze(Sf(b,a))}
-var yy=new r(null,4,[Tk,new r(null,2,[En,0,bj,0],null),pj,rf,An,!1,qj,!0],null);function zy(a,b){return ae([pj,qj,Aj,Tj,Vj,Kk,Nk,Tk,Zl,lm,rm,vm,Am,Qm,An,Mn,Yn,no,to],[rf,!0,b-1,A.h($h,new ci(null,8,a,8,null)),!1,a,xy(a,b,sy),new r(null,3,[En,0,bj,0,Sn,!0],null),!1,ty,!1,yy,new r(null,3,[bl,Uj,Sm,Wd,Lj,Wd],null),yy,!1,0,null,Gj,b])}function Ay(a,b){return bg(a,new V(null,2,5,W,[Tk,Sn],null),b)}function By(a,b,c){return T.w(a,Mn,b,J([Aj,c],0))}
-function Cy(a,b,c){var d=R(a);b=b<d?b:d;return gf.h(Of(b,a),Sf(b,c))}function Dy(a,b){var c=null!=a&&(a.o&64||a.D)?A.h(P,a):a,d=I.h(c,Kk),e=I.h(c,Mn),f=I.h(c,Aj),g=I.h(c,pj),k=wy(d,g);return cg.l(c,new V(null,1,5,W,[Nk],null),function(a,c,d,e,f,g,k){return function(c){return ze(gf.w(Lf(g,c),Cy(zg.l(c,g,k+1),b,a),J([Of(k+1,c)],0)))}}(k,a,c,c,d,e,f,g))}function Ey(a,b,c){var d=R(a);b=b<d?b:d;return gf.h(Sf(b,c),Lf(d-b,a))}
-function Fy(a,b){var c=null!=a&&(a.o&64||a.D)?A.h(P,a):a,d=I.h(c,Kk),e=I.h(c,Mn),f=I.h(c,Aj),g=I.h(c,pj),k=wy(d,g);return cg.l(c,new V(null,1,5,W,[Nk],null),function(a,c,d,e,f,g,k){return function(c){return ze(gf.w(Lf(g,c),Ey(zg.l(c,g,k+1),b,a),J([Of(k+1,c)],0)))}}(k,a,c,c,d,e,f,g))}function Gy(a,b){return T.l(bg(a,new V(null,2,5,W,[Tk,En],null),b),Vj,!1)}
-function Hy(a,b){var c=null!=a&&(a.o&64||a.D)?A.h(P,a):a,d=I.h(c,Tk),d=null!=d&&(d.o&64||d.D)?A.h(P,d):d,d=I.h(d,En),e=I.h(c,Kk)-1;return T.l(bg(bg(c,new V(null,2,5,W,[Tk,En],null),d<e?d:e),new V(null,2,5,W,[Tk,bj],null),b),Vj,!1)}function Iy(a){a=null!=a&&(a.o&64||a.D)?A.h(P,a):a;var b=I.h(a,An),c=I.h(a,Mn),b=u(b)?c:0;return Hy(Gy(a,0),b)}
-function Jy(a){a=null!=a&&(a.o&64||a.D)?A.h(P,a):a;var b=I.h(a,Tk),b=null!=b&&(b.o&64||b.D)?A.h(P,b):b,b=I.h(b,bj),c=I.h(a,Aj),d=I.h(a,to)-1;return H.h(b,c)?Dy(a,1):b<d?Hy(a,b+1):a}function Ky(a){a=null!=a&&(a.o&64||a.D)?A.h(P,a):a;var b=I.h(a,no),c=I.h(a,Kk),d=I.h(a,to),e=I.h(a,pj);return H.h(b,Gj)?T.w(a,no,Xk,J([Yn,Nk.j(a),vm,Qm.j(a),Nk,xy(c,d,e),Qm,vm.j(a)],0)):a}
-function Ly(a){a=null!=a&&(a.o&64||a.D)?A.h(P,a):a;var b=I.h(a,no);return H.h(b,Xk)?T.w(a,no,Gj,J([Yn,null,vm,Qm.j(a),Nk,Yn.j(a),Qm,vm.j(a)],0)):a}function My(a){a=null!=a&&(a.o&64||a.D)?A.h(P,a):a;var b=I.h(a,Tk),c=null!=b&&(b.o&64||b.D)?A.h(P,b):b,b=I.h(c,En),c=I.h(c,bj),d=I.h(a,pj),e=I.h(a,An),f=I.h(a,qj);return T.l(a,Qm,new r(null,4,[Tk,new r(null,2,[En,b,bj,c],null),pj,d,An,e,qj,f],null))}
-function Ny(a){a=null!=a&&(a.o&64||a.D)?A.h(P,a):a;var b=I.h(a,Qm),c=null!=b&&(b.o&64||b.D)?A.h(P,b):b,b=I.h(c,Tk),d=I.h(c,pj),e=I.h(c,An),c=I.h(c,qj);return cg.G(T.w(a,pj,d,J([An,e,qj,c],0)),new V(null,1,5,W,[Tk],null),Ph,b)}
-function Oy(a,b,c){try{if(null===b)try{if(4===c)return T.l(a,Zl,!0);throw ry;}catch(f){if(f instanceof Error){var d=f;if(d===ry)try{if(20===c)return T.l(a,rm,!0);throw ry;}catch(g){if(g instanceof Error){var e=g;if(e===ry)throw ry;throw e;}throw g;}else throw d;}else throw f;}else throw ry;}catch(f){if(f instanceof Error)if(d=f,d===ry)try{if(63===b)try{if(6===c)return Iy(T.l(a,An,!0));throw ry;}catch(g){if(g instanceof Error)if(e=g,e===ry)try{if(7===c)return T.l(a,qj,!0);throw ry;}catch(k){if(k instanceof
-Error)if(k===ry)try{if(25===c)return Ay(a,!0);throw ry;}catch(l){if(l instanceof Error)if(l===ry)try{if(47===c)return Ky(a);throw ry;}catch(n){if(n instanceof Error)if(n===ry)try{if(1047===c)return Ky(a);throw ry;}catch(m){if(m instanceof Error)if(m===ry)try{if(1048===c)return My(a);throw ry;}catch(t){if(t instanceof Error)if(t===ry)try{if(1049===c)return Ky(My(a));throw ry;}catch(q){if(q instanceof Error&&q===ry)throw ry;throw q;}else throw t;else throw t;}else throw m;else throw m;}else throw n;
-else throw n;}else throw l;else throw l;}else throw k;else throw k;}else throw e;else throw g;}else throw ry;}catch(g){if(g instanceof Error){e=g;if(e===ry)return a;throw e;}throw g;}else throw d;else throw f;}}
-function Py(a,b,c){try{if(null===b)try{if(4===c)return T.l(a,Zl,!1);throw ry;}catch(f){if(f instanceof Error){var d=f;if(d===ry)try{if(20===c)return T.l(a,rm,!1);throw ry;}catch(g){if(g instanceof Error){var e=g;if(e===ry)throw ry;throw e;}throw g;}else throw d;}else throw f;}else throw ry;}catch(f){if(f instanceof Error)if(d=f,d===ry)try{if(63===b)try{if(6===c)return Iy(T.l(a,An,!1));throw ry;}catch(g){if(g instanceof Error)if(e=g,e===ry)try{if(7===c)return T.l(a,qj,!1);throw ry;}catch(k){if(k instanceof
-Error)if(k===ry)try{if(25===c)return Ay(a,!1);throw ry;}catch(l){if(l instanceof Error)if(l===ry)try{if(47===c)return Ly(a);throw ry;}catch(n){if(n instanceof Error)if(n===ry)try{if(1047===c)return Ly(a);throw ry;}catch(m){if(m instanceof Error)if(m===ry)try{if(1048===c)return Ny(a);throw ry;}catch(t){if(t instanceof Error)if(t===ry)try{if(1049===c)return Ny(Ly(a));throw ry;}catch(q){if(q instanceof Error&&q===ry)throw ry;throw q;}else throw t;else throw t;}else throw m;else throw m;}else throw n;
-else throw n;}else throw l;else throw l;}else throw k;else throw k;}else throw e;else throw g;}else throw ry;}catch(g){if(g instanceof Error){e=g;if(e===ry)return a;throw e;}throw g;}else throw d;else throw f;}}function Qy(a){a=null!=a&&(a.o&64||a.D)?A.h(P,a):a;var b=I.h(a,Tk),b=null!=b&&(b.o&64||b.D)?A.h(P,b):b,b=I.h(b,En)-1;return Gy(a,0<b?b:0)}
-function Ry(a,b){var c=null!=a&&(a.o&64||a.D)?A.h(P,a):a,d=I.h(c,Tk),d=null!=d&&(d.o&64||d.D)?A.h(P,d):d,e=I.h(d,En),f=I.h(c,Tj),g=I.h(c,Kk),d=b-1,g=g-1,e=Qf(yf.h(Ge,e),f),d=S(e,d,g);return Gy(c,d)}function Sy(a){return Ry(a,1)}function Ty(a){return Gy(a,0)}function Uy(a){var b=null!=a&&(a.o&64||a.D)?A.h(P,a):a;a=I.h(b,rm);b=Jy(b);return u(a)?Ty(b):b}function Vy(a){return T.l(a,lm,uy)}function Wy(a){return T.l(a,lm,ty)}function Xy(a){return Ty(Jy(a))}
-function Yy(a){a=null!=a&&(a.o&64||a.D)?A.h(P,a):a;var b=I.h(a,Tk),b=null!=b&&(b.o&64||b.D)?A.h(P,b):b,b=I.h(b,En),c=I.h(a,Kk);return 0<b&&b<c?cg.G(a,new V(null,1,5,W,[Tj],null),Vd,b):a}function Zy(a){a=null!=a&&(a.o&64||a.D)?A.h(P,a):a;var b=I.h(a,Tk),b=null!=b&&(b.o&64||b.D)?A.h(P,b):b,b=I.h(b,bj),c=I.h(a,Mn);return H.h(b,c)?Fy(a,1):0<b?Hy(a,b-1):a}function $y(a){a=null!=a&&(a.o&64||a.D)?A.h(P,a):a;var b=I.h(a,Kk),c=I.h(a,to);return T.l(a,Nk,ze(Sf(c,ze(Sf(b,new V(null,2,5,W,[69,rf],null))))))}
-function az(a){a=Me.h(function(a){return a-48},a);a=Me.l(Ee,Ue(a),Uf(yf.h(Ee,10),1));return Ab.l(De,0,a)}function bz(a){return ag(a,new V(null,3,5,W,[Am,Sm,0],null))}var cz=Ki(function(a){a:for(var b=Wd,c=Wd;;){var d=C(a);if(u(d))H.h(d,59)?(a=N(a),b=Vd.h(b,c),c=Wd):(a=N(a),c=Vd.h(c,d));else{a=K(c)?Vd.h(b,c):b;break a}}return Me.h(az,a)});function dz(a){a=ag(a,new V(null,2,5,W,[Am,Lj],null));return cz.j?cz.j(a):cz.call(null,a)}function ez(a,b,c){a=S(dz(a),b,0);return 0===a?c:a}
-function fz(a){var b=null!=a&&(a.o&64||a.D)?A.h(P,a):a,c=I.h(b,Tk),d=null!=c&&(c.o&64||c.D)?A.h(P,c):c,e=I.h(d,En),f=I.h(d,bj),g=I.h(b,Kk),k=I.h(b,pj),l=ez(b,0,1);return cg.l(b,new V(null,2,5,W,[Nk,f],null),function(a,b,c,d,e,f,g,k,l,O){return function(b){return ze(Lf(l,gf.w(Lf(g,b),Sf(a,new V(null,2,5,W,[32,O],null)),J([Of(g,b)],0))))}}(l,a,b,b,c,d,e,f,g,k))}
-function gz(a){a=null!=a&&(a.o&64||a.D)?A.h(P,a):a;var b=I.h(a,Tk),b=null!=b&&(b.o&64||b.D)?A.h(P,b):b,c=I.h(b,bj),d=I.h(a,Mn),e=ez(a,0,1);return Hy(a,c<d?function(){var a=c-e;return 0>a?0:a}():function(){var a=c-e;return d>a?d:a}())}function hz(a){a=null!=a&&(a.o&64||a.D)?A.h(P,a):a;var b=I.h(a,Tk),b=null!=b&&(b.o&64||b.D)?A.h(P,b):b,c=I.h(b,bj),d=I.h(a,Aj),e=I.h(a,to),f=ez(a,0,1);return Hy(a,c>d?function(){var a=e-1,b=c+f;return a<b?a:b}():function(){var a=c+f;return d<a?d:a}())}
-function iz(a){a=null!=a&&(a.o&64||a.D)?A.h(P,a):a;var b=I.h(a,Tk),b=null!=b&&(b.o&64||b.D)?A.h(P,b):b,c=I.h(b,En),b=I.h(a,Kk),d=ez(a,0,1),c=c+d,b=b-1;return Gy(a,c<b?c:b)}function jz(a){a=null!=a&&(a.o&64||a.D)?A.h(P,a):a;var b=I.h(a,Tk),b=null!=b&&(b.o&64||b.D)?A.h(P,b):b,b=I.h(b,En);I.h(a,Kk);var c=ez(a,0,1),b=b-c;return Gy(a,0<b?b:0)}function kz(a){return Gy(hz(a),0)}function lz(a){return Gy(gz(a),0)}
-function mz(a){a=null!=a&&(a.o&64||a.D)?A.h(P,a):a;var b=I.h(a,Kk),c=ez(a,0,1);return Gy(a,c<=b?c-1:b-1)}function nz(a,b){var c,d=null!=a&&(a.o&64||a.D)?A.h(P,a):a;c=I.h(d,An);d=I.h(d,Mn);c=u(c)?d:0;var e=null!=a&&(a.o&64||a.D)?A.h(P,a):a,d=I.h(e,An),f=I.h(e,Aj),e=I.h(e,to);return Tq(c+b,c,u(d)?f:e-1)}function oz(a){a=null!=a&&(a.o&64||a.D)?A.h(P,a):a;var b=I.h(a,Kk);I.h(a,to);var c=ez(a,0,1),d=ez(a,1,1),b=Tq(d-1,0,b-1),c=nz(a,c-1);return Hy(Gy(T.l(a,Vj,!1),b),c)}
-function pz(a){var b=ez(a,0,1);return Ry(a,b)}function qz(a,b,c){return ze(gf.h(Lf(b,a),Sf(R(a)-b,vy(32,c))))}function rz(a){var b=null!=a&&(a.o&64||a.D)?A.h(P,a):a,c=I.h(b,Tk),d=null!=c&&(c.o&64||c.D)?A.h(P,c):c,e=I.h(d,En),f=I.h(d,bj),g=I.h(b,Kk),k=I.h(b,to),l=I.h(b,pj);return cg.l(b,new V(null,1,5,W,[Nk],null),function(a,b,c,d,e,f,g,k,l,O){return function(a){var b=Lf(g,a);a=qz(Zd(a,g),f,O);var c=Sf(l-g-1,wy(k,O));return ze(gf.w(b,new V(null,1,5,W,[a],null),J([c],0)))}}(a,b,b,c,d,e,f,g,k,l))}
-function sz(a,b,c){return ze(gf.h(Sf(b+1,vy(32,c)),Of(b+1,a)))}function tz(a){var b=null!=a&&(a.o&64||a.D)?A.h(P,a):a,c=I.h(b,Tk),d=null!=c&&(c.o&64||c.D)?A.h(P,c):c,e=I.h(d,En),f=I.h(d,bj),g=I.h(b,Kk),k=I.h(b,to),l=I.h(b,pj);return cg.l(b,new V(null,1,5,W,[Nk],null),function(a,b,c,d,e,f,g,k,l,O){return function(a){var b=Sf(g,wy(k,O)),c=sz(Zd(a,g),f,O);a=Of(g+1,a);return ze(gf.w(b,new V(null,1,5,W,[c],null),J([a],0)))}}(a,b,b,c,d,e,f,g,k,l))}
-function uz(a){var b=ez(a,0,0);if(u(H.h?H.h(0,b):H.call(null,0,b)))a=rz(a);else if(u(H.h?H.h(1,b):H.call(null,1,b)))a=tz(a);else if(u(H.h?H.h(2,b):H.call(null,2,b))){a=null!=a&&(a.o&64||a.D)?A.h(P,a):a;var b=I.h(a,Kk),c=I.h(a,to),d=I.h(a,pj);a=bg(a,new V(null,1,5,W,[Nk],null),xy(b,c,d))}return a}
-function vz(a){var b=null!=a&&(a.o&64||a.D)?A.h(P,a):a,c=I.h(b,Tk),d=null!=c&&(c.o&64||c.D)?A.h(P,c):c,e=I.h(d,En),f=I.h(d,bj),g=I.h(b,Kk),k=I.h(b,to),l=I.h(b,pj),n=ez(b,0,0);return cg.l(b,new V(null,2,5,W,[Nk,f],null),function(a,b,c,d,e,f,g,k,l,n,ba){return function(b){return u(H.h?H.h(0,a):H.call(null,0,a))?qz(b,g,ba):u(H.h?H.h(1,a):H.call(null,1,a))?sz(b,g,ba):u(H.h?H.h(2,a):H.call(null,2,a))?wy(l,ba):b}}(n,a,b,b,c,d,e,f,g,k,l))}function wz(a){var b=ez(a,0,1);return Dy(a,b)}
-function xz(a){var b=ez(a,0,1);return Fy(a,b)}function yz(a){var b=null!=a&&(a.o&64||a.D)?A.h(P,a):a,c=I.h(b,Tk),d=null!=c&&(c.o&64||c.D)?A.h(P,c):c,e=I.h(d,bj),f=I.h(b,Aj),g=I.h(b,Kk),k=I.h(b,to),l=I.h(b,pj),n=ez(b,0,1),m=wy(g,l);return cg.l(b,new V(null,1,5,W,[Nk],null),function(a,b,c,d,e,f,g,k,l){return function(c){return ze(k<=l?gf.w(Lf(k,c),Ey(zg.l(c,k,l+1),a,b),J([Of(l+1,c)],0)):gf.h(Lf(k,c),Ey(Of(k,c),a,b)))}}(n,m,a,b,b,c,d,e,f,g,k,l))}
-function zz(a){var b=null!=a&&(a.o&64||a.D)?A.h(P,a):a,c=I.h(b,Tk),d=null!=c&&(c.o&64||c.D)?A.h(P,c):c,e=I.h(d,bj),f=I.h(b,Aj),g=I.h(b,Kk),k=I.h(b,to),l=I.h(b,pj),n=ez(b,0,1),m=wy(g,l);return cg.l(b,new V(null,1,5,W,[Nk],null),function(a,b,c,d,e,f,g,k,l){return function(c){return ze(k<=l?gf.w(Lf(k,c),Cy(zg.l(c,k,l+1),a,b),J([Of(l+1,c)],0)):gf.h(Lf(k,c),Cy(Of(k,c),a,b)))}}(n,m,a,b,b,c,d,e,f,g,k,l))}
-function Az(a){var b=null!=a&&(a.o&64||a.D)?A.h(P,a):a,c=I.h(b,Tk),d=null!=c&&(c.o&64||c.D)?A.h(P,c):c,e=I.h(d,En),f=I.h(d,bj),g=I.h(b,Kk),k=I.h(b,pj),l=e>=g?Gy(b,g-1):b,n=ag(l,new V(null,2,5,W,[Tk,En],null)),m=function(){var a=ez(l,0,1),b=g-n;return a<b?a:b}();return cg.l(l,new V(null,2,5,W,[Nk,f],null),function(a,b,c,d,e,f,g,k,l,m,n,L){return function(a){return ze(gf.w(Lf(b,a),Of(b+c,a),J([Sf(c,vy(32,L))],0)))}}(l,n,m,a,b,b,c,d,e,f,g,k))}
-function Bz(a){a=null!=a&&(a.o&64||a.D)?A.h(P,a):a;var b=I.h(a,Tk),b=null!=b&&(b.o&64||b.D)?A.h(P,b):b,b=I.h(b,En),c=I.h(a,Kk),d=ez(a,0,0);return u(H.h?H.h(0,d):H.call(null,0,d))?0<b&&b<c?cg.G(a,new V(null,1,5,W,[Tj],null),Vd,b):a:u(H.h?H.h(2,d):H.call(null,2,d))?cg.G(a,new V(null,1,5,W,[Tj],null),fe,b):u(H.h?H.h(5,d):H.call(null,5,d))?cg.l(a,new V(null,1,5,W,[Tj],null),Xd):a}
-function Cz(a){var b=null!=a&&(a.o&64||a.D)?A.h(P,a):a,c=I.h(b,Tk),d=null!=c&&(c.o&64||c.D)?A.h(P,c):c,e=I.h(d,En),f=I.h(d,bj),g=I.h(b,Kk),k=I.h(b,pj),l=function(){var a=ez(b,0,1),c=g-e;return a<c?a:c}();return cg.l(b,new V(null,2,5,W,[Nk,f],null),function(a,b,c,d,e,f,g,k,l,O){return function(b){return ze(gf.w(Lf(g,b),Sf(a,vy(32,O)),J([Of(g+a,b)],0)))}}(l,a,b,b,c,d,e,f,g,k))}
-function Dz(a){var b=ez(a,0,1);a=null!=a&&(a.o&64||a.D)?A.h(P,a):a;var c=I.h(a,Tk),c=null!=c&&(c.o&64||c.D)?A.h(P,c):c,c=I.h(c,En),d=I.h(a,Tj);I.h(a,Kk);--b;c=ai(yf.h(Fe,c),d);b=S(Ue(c),b,0);return Gy(a,b)}function Ez(a){a=null!=a&&(a.o&64||a.D)?A.h(P,a):a;var b=I.h(a,Tk),b=null!=b&&(b.o&64||b.D)?A.h(P,b):b,b=I.h(b,En),c=ez(a,0,0);return u(H.h?H.h(0,c):H.call(null,0,c))?cg.G(a,new V(null,1,5,W,[Tj],null),fe,b):u(H.h?H.h(3,c):H.call(null,3,c))?cg.l(a,new V(null,1,5,W,[Tj],null),Xd):a}
-function Fz(a){var b=bz(a);return Ab.l(function(a){return function(b,e){return Oy(b,a,e)}}(b),a,dz(a))}function Gz(a){var b=bz(a);return Ab.l(function(a){return function(b,e){return Py(b,a,e)}}(b),a,dz(a))}function Hz(a,b,c){return bg(a,new V(null,2,5,W,[pj,b],null),c)}function Iz(a,b){return cg.G(a,new V(null,1,5,W,[pj],null),be,b)}
-function Jz(a){for(var b=function(){var b=K(dz(a));return b?b:new V(null,1,5,W,[0],null)}(),c=S(b,0,null),d=S(b,1,null),e=S(b,2,null),f=Le(b,3),g=a,k=b;;){var l=g,n=k,m=S(n,0,null),t=S(n,1,null),q=S(n,2,null),z=Le(n,3),w=n;if(u(m))if(0===m)var E=cg.l(l,new V(null,1,5,W,[pj],null),Xd),F=N(w),g=E,k=F;else if(1===m)var M=Hz(l,lj,!0),O=N(w),g=M,k=O;else if(3===m)var Z=Hz(l,$n,!0),ba=N(w),g=Z,k=ba;else if(4===m)var Ca=Hz(l,Vl,!0),L=N(w),g=Ca,k=L;else if(5===m)var fb=Hz(l,Dj,!0),oa=N(w),g=fb,k=oa;else if(7===
-m)var wa=Hz(l,qk,!0),xa=N(w),g=wa,k=xa;else if(21===m)var ya=Iz(l,lj),Xb=N(w),g=ya,k=Xb;else if(22===m)var Na=Iz(l,lj),Ta=N(w),g=Na,k=Ta;else if(23===m)var Wa=Iz(l,$n),eb=N(w),g=Wa,k=eb;else if(24===m)var $a=Iz(l,Vl),kb=N(w),g=$a,k=kb;else if(25===m)var sb=Iz(l,Dj),Fb=N(w),g=sb,k=Fb;else if(27===m)var Ob=Iz(l,qk),qc=N(w),g=Ob,k=qc;else if(function(){return function(a){return 30<=a&&37>=a}}(g,k,l,n,m,t,q,z,w,a,b,b,c,d,e,f,b,b).call(null,m))var Oa=m,bd=Hz(l,rk,Oa-30),Ld=N(w),g=bd,k=Ld;else if(38===
-m)if(5===t)if(null!=q)var Oa=q,xe=Hz(l,rk,Oa),Ff=Of(3,w),g=xe,k=Ff;else if(39===m)var vh=Iz(l,rk),Qv=N(w),g=vh,k=Qv;else if(function(){return function(a){return 40<=a&&47>=a}}(g,k,l,n,m,t,q,z,w,a,b,b,c,d,e,f,b,b).call(null,m))var Ra=m,wA=Hz(l,Xn,Ra-40),To=N(w),g=wA,k=To;else if(48===m)if(5===t)if(null!=q)var Ra=q,qg=Hz(l,Xn,Ra),xA=Of(3,w),g=qg,k=xA;else if(49===m)var yA=Iz(l,Xn),Ei=N(w),g=yA,k=Ei;else if(function(){return function(a){return 90<=a&&97>=a}}(g,k,l,n,m,t,q,z,w,a,b,b,c,d,e,f,b,b).call(null,
-m))var Oa=m,jl=Hz(l,rk,Oa-82),Uo=N(w),g=jl,k=Uo;else if(function(){return function(a){return 100<=a&&107>=a}}(g,k,l,n,m,t,q,z,w,a,b,b,c,d,e,f,b,b).call(null,m))var Ra=m,Vo=Hz(l,Xn,Ra-92),Wo=N(w),g=Vo,k=Wo;else var Xo=l,Yo=N(w),g=Xo,k=Yo;else if(49===m)var Zo=Iz(l,Xn),kl=N(w),g=Zo,k=kl;else if(function(){return function(a){return 90<=a&&97>=a}}(g,k,l,n,m,t,q,z,w,a,b,b,c,d,e,f,b,b).call(null,m))var Oa=m,ll=Hz(l,rk,Oa-82),$o=N(w),g=ll,k=$o;else if(function(){return function(a){return 100<=a&&107>=a}}(g,
-k,l,n,m,t,q,z,w,a,b,b,c,d,e,f,b,b).call(null,m))var Ra=m,ap=Hz(l,Xn,Ra-92),bp=N(w),g=ap,k=bp;else var ml=l,nl=N(w),g=ml,k=nl;else if(49===m)var Qb=Iz(l,Xn),Rb=N(w),g=Qb,k=Rb;else if(function(){return function(a){return 90<=a&&97>=a}}(g,k,l,n,m,t,q,z,w,a,b,b,c,d,e,f,b,b).call(null,m))var Oa=m,wc=Hz(l,rk,Oa-82),ob=N(w),g=wc,k=ob;else if(function(){return function(a){return 100<=a&&107>=a}}(g,k,l,n,m,t,q,z,w,a,b,b,c,d,e,f,b,b).call(null,m))var Ra=m,Pa=Hz(l,Xn,Ra-92),ol=N(w),g=Pa,k=ol;else var cp=l,pl=
-N(w),g=cp,k=pl;else if(39===m)var dp=Iz(l,rk),ql=N(w),g=dp,k=ql;else if(function(){return function(a){return 40<=a&&47>=a}}(g,k,l,n,m,t,q,z,w,a,b,b,c,d,e,f,b,b).call(null,m))var Ra=m,rl=Hz(l,Xn,Ra-40),ep=N(w),g=rl,k=ep;else if(48===m)if(5===t)if(null!=q)var Ra=q,fp=Hz(l,Xn,Ra),gp=Of(3,w),g=fp,k=gp;else if(49===m)var pd=Iz(l,Xn),qd=N(w),g=pd,k=qd;else if(function(){return function(a){return 90<=a&&97>=a}}(g,k,l,n,m,t,q,z,w,a,b,b,c,d,e,f,b,b).call(null,m))var Oa=m,rd=Hz(l,rk,Oa-82),sl=N(w),g=rd,k=sl;
-else if(function(){return function(a){return 100<=a&&107>=a}}(g,k,l,n,m,t,q,z,w,a,b,b,c,d,e,f,b,b).call(null,m))var Ra=m,hp=Hz(l,Xn,Ra-92),ip=N(w),g=hp,k=ip;else var tl=l,rg=N(w),g=tl,k=rg;else if(49===m)var jp=Iz(l,Xn),kp=N(w),g=jp,k=kp;else if(function(){return function(a){return 90<=a&&97>=a}}(g,k,l,n,m,t,q,z,w,a,b,b,c,d,e,f,b,b).call(null,m))var Oa=m,ul=Hz(l,rk,Oa-82),Xe=N(w),g=ul,k=Xe;else if(function(){return function(a){return 100<=a&&107>=a}}(g,k,l,n,m,t,q,z,w,a,b,b,c,d,e,f,b,b).call(null,
-m))var Ra=m,lp=Hz(l,Xn,Ra-92),mp=N(w),g=lp,k=mp;else var np=l,op=N(w),g=np,k=op;else if(49===m)var pp=Iz(l,Xn),qp=N(w),g=pp,k=qp;else if(function(){return function(a){return 90<=a&&97>=a}}(g,k,l,n,m,t,q,z,w,a,b,b,c,d,e,f,b,b).call(null,m))var Oa=m,vl=Hz(l,rk,Oa-82),rp=N(w),g=vl,k=rp;else if(function(){return function(a){return 100<=a&&107>=a}}(g,k,l,n,m,t,q,z,w,a,b,b,c,d,e,f,b,b).call(null,m))var Ra=m,sp=Hz(l,Xn,Ra-92),tp=N(w),g=sp,k=tp;else var up=l,wl=N(w),g=up,k=wl;else if(39===m)var vp=Iz(l,rk),
-xl=N(w),g=vp,k=xl;else if(function(){return function(a){return 40<=a&&47>=a}}(g,k,l,n,m,t,q,z,w,a,b,b,c,d,e,f,b,b).call(null,m))var Ra=m,wp=Hz(l,Xn,Ra-40),xp=N(w),g=wp,k=xp;else if(48===m)if(5===t)if(null!=q)var Ra=q,yl=Hz(l,Xn,Ra),yp=Of(3,w),g=yl,k=yp;else if(49===m)var zp=Iz(l,Xn),zl=N(w),g=zp,k=zl;else if(function(){return function(a){return 90<=a&&97>=a}}(g,k,l,n,m,t,q,z,w,a,b,b,c,d,e,f,b,b).call(null,m))var Oa=m,Ap=Hz(l,rk,Oa-82),Al=N(w),g=Ap,k=Al;else if(function(){return function(a){return 100<=
-a&&107>=a}}(g,k,l,n,m,t,q,z,w,a,b,b,c,d,e,f,b,b).call(null,m))var Ra=m,Bl=Hz(l,Xn,Ra-92),Bp=N(w),g=Bl,k=Bp;else var Cp=l,Dp=N(w),g=Cp,k=Dp;else if(49===m)var Fi=Iz(l,Xn),Ep=N(w),g=Fi,k=Ep;else if(function(){return function(a){return 90<=a&&97>=a}}(g,k,l,n,m,t,q,z,w,a,b,b,c,d,e,f,b,b).call(null,m))var Oa=m,Fp=Hz(l,rk,Oa-82),Cl=N(w),g=Fp,k=Cl;else if(function(){return function(a){return 100<=a&&107>=a}}(g,k,l,n,m,t,q,z,w,a,b,b,c,d,e,f,b,b).call(null,m))var Ra=m,Gp=Hz(l,Xn,Ra-92),Dl=N(w),g=Gp,k=Dl;else var El=
-l,Hp=N(w),g=El,k=Hp;else if(49===m)var Ip=Iz(l,Xn),Jp=N(w),g=Ip,k=Jp;else if(function(){return function(a){return 90<=a&&97>=a}}(g,k,l,n,m,t,q,z,w,a,b,b,c,d,e,f,b,b).call(null,m))var Oa=m,Fl=Hz(l,rk,Oa-82),Gl=N(w),g=Fl,k=Gl;else if(function(){return function(a){return 100<=a&&107>=a}}(g,k,l,n,m,t,q,z,w,a,b,b,c,d,e,f,b,b).call(null,m))var Ra=m,Kp=Hz(l,Xn,Ra-92),Lp=N(w),g=Kp,k=Lp;else var Mp=l,Hl=N(w),g=Mp,k=Hl;else return l}}function Kz(a){var b=ez(a,0,1),b=nz(a,b-1);return Hy(a,b)}
-function Lz(a){a=null!=a&&(a.o&64||a.D)?A.h(P,a):a;var b=I.h(a,to);return H.h(bz(a),33)?T.w(By(Ay(a,!0),0,b-1),Zl,!1,J([An,!1,pj,rf,Qm,yy],0)):a}function Mz(a){a=null!=a&&(a.o&64||a.D)?A.h(P,a):a;var b=I.h(a,to),c=ez(a,0,1)-1,d=ez(a,1,b)-1;return-1<c&&c<d&&d<b?Iy(By(a,c,d)):a}function Nz(a){return a}function Oz(a,b,c){return bg(a,new V(null,1,5,W,[b],null),c)}function Pz(a,b,c){return ze(gf.w(Lf(b,a),new V(null,1,5,W,[c],null),J([Lf(R(a)-b-1,Of(b,a))],0)))}
-function Qz(a,b){var c=null!=a&&(a.o&64||a.D)?A.h(P,a):a,d=I.h(c,Tk),e=null!=d&&(d.o&64||d.D)?A.h(P,d):d,d=I.h(e,En),e=I.h(e,bj),f=I.h(c,Kk);I.h(c,to);var g=I.h(c,pj),k=I.h(c,qj),l=I.h(c,Zl),n=I.h(c,lm),n=95<b&&127>b?n.j?n.j(b):n.call(null,b):b,g=vy(n,g);return H.h(f,d+1)?u(k)?T.l(Gy(bg(c,new V(null,3,5,W,[Nk,e,d],null),g),d+1),Vj,!0):bg(c,new V(null,3,5,W,[Nk,e,d],null),g):Gy(cg.N(c,new V(null,2,5,W,[Nk,e],null),u(l)?Pz:Oz,d,g),d+1)}
-function Rz(a,b){var c=u(H.h?H.h(8,b):H.call(null,8,b))?Qy:u(H.h?H.h(9,b):H.call(null,9,b))?Sy:u(H.h?H.h(10,b):H.call(null,10,b))?Uy:u(H.h?H.h(11,b):H.call(null,11,b))?Uy:u(H.h?H.h(12,b):H.call(null,12,b))?Uy:u(H.h?H.h(13,b):H.call(null,13,b))?Ty:u(H.h?H.h(14,b):H.call(null,14,b))?Vy:u(H.h?H.h(15,b):H.call(null,15,b))?Wy:u(H.h?H.h(132,b):H.call(null,132,b))?Uy:u(H.h?H.h(133,b):H.call(null,133,b))?Xy:u(H.h?H.h(136,b):H.call(null,136,b))?Yy:u(H.h?H.h(141,b):H.call(null,141,b))?Zy:null;return u(c)?c.j?
-c.j(a):c.call(null,a):a}function Sz(a){return cg.G(a,new V(null,1,5,W,[Am],null),Ph,new r(null,2,[Sm,Wd,Lj,Wd],null))}function Tz(a,b){return cg.G(a,new V(null,2,5,W,[Am,Sm],null),Vd,b)}function Uz(a,b){return cg.G(a,new V(null,2,5,W,[Am,Lj],null),Vd,b)}
-function Vz(a,b){var c=bz(a);try{if(null===c)try{if(function(){return function(a){return 64<=a&&95>=a}}(c,b).call(null,b))return Rz(a,b+64);throw ry;}catch(g){if(g instanceof Error){var d=g;if(d===ry)try{if(55===b)return My(a);throw ry;}catch(k){if(k instanceof Error){var e=k;if(e===ry)try{if(56===b)return Ny(a);throw ry;}catch(l){if(l instanceof Error){var f=l;if(f===ry)try{if(99===b)return zy(Kk.j(a),to.j(a));throw ry;}catch(n){if(n instanceof Error&&n===ry)throw ry;throw n;}else throw f;}else throw l;
-}else throw e;}else throw k;}else throw d;}else throw g;}else throw ry;}catch(g){if(g instanceof Error)if(d=g,d===ry)try{if(35===c)try{if(56===b)return $y(a);throw ry;}catch(k){if(k instanceof Error){e=k;if(e===ry)throw ry;throw e;}throw k;}else throw ry;}catch(k){if(k instanceof Error)if(e=k,e===ry)try{if(40===c)try{if(48===b)return Vy(a);throw ry;}catch(l){if(l instanceof Error){f=l;if(f===ry)return Wy(a);throw f;}throw l;}else throw ry;}catch(l){if(l instanceof Error){f=l;if(f===ry)return a;throw f;
-}throw l;}else throw e;else throw k;}else throw d;else throw g;}}
-function Wz(a,b){var c=u(H.h?H.h(64,b):H.call(null,64,b))?fz:u(H.h?H.h(65,b):H.call(null,65,b))?gz:u(H.h?H.h(66,b):H.call(null,66,b))?hz:u(H.h?H.h(67,b):H.call(null,67,b))?iz:u(H.h?H.h(68,b):H.call(null,68,b))?jz:u(H.h?H.h(69,b):H.call(null,69,b))?kz:u(H.h?H.h(70,b):H.call(null,70,b))?lz:u(H.h?H.h(71,b):H.call(null,71,b))?mz:u(H.h?H.h(72,b):H.call(null,72,b))?oz:u(H.h?H.h(73,b):H.call(null,73,b))?pz:u(H.h?H.h(74,b):H.call(null,74,b))?uz:u(H.h?H.h(75,b):H.call(null,75,b))?vz:u(H.h?H.h(76,b):H.call(null,
-76,b))?yz:u(H.h?H.h(77,b):H.call(null,77,b))?zz:u(H.h?H.h(80,b):H.call(null,80,b))?Az:u(H.h?H.h(83,b):H.call(null,83,b))?wz:u(H.h?H.h(84,b):H.call(null,84,b))?xz:u(H.h?H.h(87,b):H.call(null,87,b))?Bz:u(H.h?H.h(88,b):H.call(null,88,b))?Cz:u(H.h?H.h(90,b):H.call(null,90,b))?Dz:u(H.h?H.h(96,b):H.call(null,96,b))?mz:u(H.h?H.h(97,b):H.call(null,97,b))?iz:u(H.h?H.h(100,b):H.call(null,100,b))?Kz:u(H.h?H.h(101,b):H.call(null,101,b))?gz:u(H.h?H.h(102,b):H.call(null,102,b))?oz:u(H.h?H.h(103,b):H.call(null,
-103,b))?Ez:u(H.h?H.h(104,b):H.call(null,104,b))?Fz:u(H.h?H.h(108,b):H.call(null,108,b))?Gz:u(H.h?H.h(109,b):H.call(null,109,b))?Jz:u(H.h?H.h(112,b):H.call(null,112,b))?Lz:u(H.h?H.h(114,b):H.call(null,114,b))?Mz:null;return u(c)?c.j?c.j(a):c.call(null,a):a}function Xz(a){return a}
-var Yz=Zh(G(0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,25,28,29,30,31)),Zz=Yg([Zh(G(24,26,128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143,145,146,147,148,149,150,151,153,154)),new r(null,2,[sn,Rz,Yj,Uj],null),Zh(G(156)),new r(null,1,[Yj,Uj],null),Zh(G(27)),new r(null,1,[Yj,hj],null),Zh(G(152,158,159)),new r(null,1,[Yj,Qk],null),Zh(G(144)),new r(null,1,[Yj,Uk],null),Zh(G(157)),new r(null,1,[Yj,tk],null),Zh(G(155)),new r(null,1,[Yj,il],null)],!0,!1),$z=ae([fj,hj,Uj,
-fk,lk,sk,tk,Qk,Uk,il,nm,sm,Dm,bo],[Yg([Yz,new r(null,1,[sn,Nz],null),Zh(G(32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47)),new r(null,2,[sn,Tz,Yj,sk],null),Zh(G(48,49,50,51,52,53,54,55,56,57,59)),new r(null,1,[sn,Uz],null),Zh(G(58,60,61,62,63)),new r(null,1,[Yj,bo],null),Zh(G(64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,100,101,102,103,104,105,106,107,108,109,110,111,112,113,114,115,116,117,118,119,120,121,122,123,124,125,126)),new r(null,
-1,[Yj,fk],null),Zh(G(127)),new r(null,1,[sn,Nz],null)],!0,!1),ae([uk,Zh(G(88,94,95)),Yz,Zh(G(91)),Zh(G(80)),Zh(G(32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47)),Zh(G(127)),Zh(G(48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,81,82,83,84,85,86,87,89,90,92,96,97,98,99,100,101,102,103,104,105,106,107,108,109,110,111,112,113,114,115,116,117,118,119,120,121,122,123,124,125,126)),Zh(G(93))],[Sz,new r(null,1,[Yj,Qk],null),new r(null,1,[sn,Rz],null),new r(null,
-1,[Yj,il],null),new r(null,1,[Yj,Uk],null),new r(null,2,[sn,Tz,Yj,nm],null),new r(null,1,[sn,Nz],null),new r(null,2,[sn,Vz,Yj,Uj],null),new r(null,1,[Yj,tk],null)]),Yg([Yz,new r(null,1,[sn,Rz],null),Zh(G(32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,100,101,102,103,104,105,106,107,108,109,110,111,112,113,114,115,116,117,118,119,120,121,122,123,
-124,125,126,127,160,161,162,163,164,165,166,167,168,169,170,171,172,173,174,175,176,177,178,179,180,181,182,183,184,185,186,187,188,189,190,191,192,193,194,195,196,197,198,199,200,201,202,203,204,205,206,207,208,209,210,211,212,213,214,215,216,217,218,219,220,221,222,223,224,225,226,227,228,229,230,231,232,233,234,235,236,237,238,239,240,241,242,243,244,245,246,247,248,249,250,251,252,253,254,255)),new r(null,1,[sn,function(a,b){var c=null!=a&&(a.o&64||a.D)?A.h(P,a):a,d=I.h(c,qj),e=I.h(c,Vj);u(u(d)?
-e:d)&&(c=null!=c&&(c.o&64||c.D)?A.h(P,c):c,d=I.h(c,Tk),d=null!=d&&(d.o&64||d.D)?A.h(P,d):d,d=I.h(d,bj),e=I.h(c,to),c=Gy(c,0),c=H.h(e,d+1)?Dy(c,1):Hy(c,d+1));return c=Qz(c,b)}],null)],!0,!1),Yg([uk,function(a){return a},Yz,new r(null,1,[sn,Xz],null),Zh(G(32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,100,101,102,103,104,105,106,107,108,109,110,
-111,112,113,114,115,116,117,118,119,120,121,122,123,124,125,126)),new r(null,1,[sn,Xz],null),Zh(G(127)),new r(null,1,[sn,Nz],null),fl,function(a){return a}],!0,!1),Yg([Yz,new r(null,1,[sn,Rz],null),Zh(G(48,49,50,51,52,53,54,55,56,57,59)),new r(null,1,[sn,Uz],null),Zh(G(58,60,61,62,63)),new r(null,1,[Yj,Dm],null),Zh(G(32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47)),new r(null,2,[sn,Tz,Yj,sm],null),Zh(G(64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,
-96,97,98,99,100,101,102,103,104,105,106,107,108,109,110,111,112,113,114,115,116,117,118,119,120,121,122,123,124,125,126)),new r(null,2,[sn,Wz,Yj,Uj],null),Zh(G(127)),new r(null,1,[sn,Nz],null)],!0,!1),Yg([Yz,new r(null,1,[sn,Nz],null),Zh(G(32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47)),new r(null,1,[sn,Tz],null),Zh(G(48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63)),new r(null,1,[Yj,bo],null),Zh(G(64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,
-97,98,99,100,101,102,103,104,105,106,107,108,109,110,111,112,113,114,115,116,117,118,119,120,121,122,123,124,125,126)),new r(null,1,[Yj,fk],null),Zh(G(127)),new r(null,1,[sn,Nz],null)],!0,!1),Yg([uk,function(a){return a},fe.h(Yz,7),new r(null,1,[sn,Nz],null),Zh(G(32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,100,101,102,103,104,105,106,107,108,
-109,110,111,112,113,114,115,116,117,118,119,120,121,122,123,124,125,126,127)),new r(null,1,[sn,function(a){return a}],null),Zh(G(7)),new r(null,1,[Yj,Uj],null),fl,function(a){return a}],!0,!1),Yg([Yz,new r(null,1,[sn,Nz],null),Zh(G(32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,100,101,102,103,104,105,106,107,108,109,110,111,112,113,114,115,116,
-117,118,119,120,121,122,123,124,125,126,127)),new r(null,1,[sn,Nz],null)],!0,!1),Yg([uk,Sz,Yz,new r(null,1,[sn,Nz],null),Zh(G(32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47)),new r(null,2,[sn,Tz,Yj,sk],null),Zh(G(58)),new r(null,1,[Yj,bo],null),Zh(G(48,49,50,51,52,53,54,55,56,57,59)),new r(null,2,[sn,Uz,Yj,fj],null),Zh(G(60,61,62,63)),new r(null,2,[sn,Tz,Yj,fj],null),Zh(G(64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,100,101,102,103,104,
-105,106,107,108,109,110,111,112,113,114,115,116,117,118,119,120,121,122,123,124,125,126)),new r(null,1,[Yj,fk],null),Zh(G(127)),new r(null,1,[sn,Nz],null)],!0,!1),Yg([uk,Sz,Yz,new r(null,1,[sn,Rz],null),Zh(G(64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,100,101,102,103,104,105,106,107,108,109,110,111,112,113,114,115,116,117,118,119,120,121,122,123,124,125,126)),new r(null,2,[sn,Wz,Yj,Uj],null),Zh(G(48,49,50,51,52,53,54,55,56,57,59)),new r(null,
-2,[sn,Uz,Yj,lk],null),Zh(G(60,61,62,63)),new r(null,2,[sn,Tz,Yj,lk],null),Zh(G(58)),new r(null,1,[Yj,Dm],null),Zh(G(32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47)),new r(null,2,[sn,Tz,Yj,sm],null),Zh(G(127)),new r(null,1,[sn,Nz],null)],!0,!1),Yg([Yz,new r(null,1,[sn,Rz],null),Zh(G(32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47)),new r(null,1,[sn,Tz],null),Zh(G(48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,
-93,94,95,96,97,98,99,100,101,102,103,104,105,106,107,108,109,110,111,112,113,114,115,116,117,118,119,120,121,122,123,124,125,126)),new r(null,2,[sn,Vz,Yj,Uj],null),Zh(G(127)),new r(null,1,[sn,Nz],null)],!0,!1),Yg([Yz,new r(null,1,[sn,Rz],null),Zh(G(32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47)),new r(null,1,[sn,Tz],null),Zh(G(64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,100,101,102,103,104,105,106,107,108,109,110,111,112,113,114,115,
-116,117,118,119,120,121,122,123,124,125,126)),new r(null,2,[sn,Wz,Yj,Uj],null),Zh(G(48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63)),new r(null,1,[Yj,Dm],null),Zh(G(127)),new r(null,1,[sn,Nz],null)],!0,!1),Yg([Yz,new r(null,1,[sn,Rz],null),Zh(G(32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63)),new r(null,1,[sn,Nz],null),Zh(G(64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,100,101,102,103,104,
-105,106,107,108,109,110,111,112,113,114,115,116,117,118,119,120,121,122,123,124,125,126)),new r(null,1,[Yj,Uj],null),Zh(G(127)),new r(null,1,[sn,Nz],null)],!0,!1),Yg([Yz,new r(null,1,[sn,Nz],null),Zh(G(32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,100,101,102,103,104,105,106,107,108,109,110,111,112,113,114,115,116,117,118,119,120,121,122,123,
-124,125,126,127)),new r(null,1,[sn,Nz],null)],!0,!1)]);function aA(a,b){return uf(function(a){var d=S(a,0,null);a=S(a,1,null);return u(d.j?d.j(b):d.call(null,b))?a:null},a)}var bA=Ki(function(a,b){var c=I.h($z,a),d,e=aA(Zz,b);d=u(e)?e:aA(c,160<=b?65:b);e=sn.j(d);d=Yj.j(d);if(u(d)){var f=I.h($z,d),c=fl.j(c),f=uk.j(f);return new V(null,2,5,W,[d,Xf(new V(null,3,5,W,[c,e,f],null))],null)}return new V(null,2,5,W,[a,u(e)?new V(null,1,5,W,[e],null):Wd],null)});
-function cA(a,b,c){return Ab.l(function(a,b){return b.h?b.h(a,c):b.call(null,a,c)},a,b)}function dA(a,b){var c=null!=a&&(a.o&64||a.D)?A.h(P,a):a,d=I.h(c,Am),d=null!=d&&(d.o&64||d.D)?A.h(P,d):d,d=I.h(d,bl),e=bA.h?bA.h(d,b):bA.call(null,d,b),d=S(e,0,null),e=S(e,1,null);return cA(bg(c,new V(null,2,5,W,[Am,bl],null),d),e,b)}function eA(a,b){var c=Zf(function(a){return b.charCodeAt(a)},di(R(b)));return Ab.l(dA,a,c)}
-function fA(a){var b=S(a,0,null),c=Le(a,1);a=Wd;for(var d=new V(null,1,5,W,[C(b)],null),e=Ud(b),b=c;;)if(c=C(b),u(c)){var f=c,c=S(f,0,null),f=S(f,1,null);H.h(f,e)?d=Vd.h(d,c):(a=Vd.h(a,new V(null,2,5,W,[A.h(String.fromCharCode,d),e],null)),d=new V(null,1,5,W,[c],null),e=f);b=N(b)}else return Vd.h(a,new V(null,2,5,W,[A.h(String.fromCharCode,d),e],null))};var gA=function gA(b){if(null!=b&&null!=b.Bd)return b.Bd(b);var c=gA[p(null==b?null:b)];if(null!=c)return c.j?c.j(b):c.call(null,b);c=gA._;if(null!=c)return c.j?c.j(b):c.call(null,b);throw x("Source.init",b);},hA=function hA(b){if(null!=b&&null!=b.Dd)return b.Dd(b);var c=hA[p(null==b?null:b)];if(null!=c)return c.j?c.j(b):c.call(null,b);c=hA._;if(null!=c)return c.j?c.j(b):c.call(null,b);throw x("Source.start",b);},iA=function iA(b){if(null!=b&&null!=b.Ed)return b.Ed(b);var c=iA[p(null==b?null:b)];
-if(null!=c)return c.j?c.j(b):c.call(null,b);c=iA._;if(null!=c)return c.j?c.j(b):c.call(null,b);throw x("Source.stop",b);},jA=function jA(b){if(null!=b&&null!=b.Fd)return b.Fd(b);var c=jA[p(null==b?null:b)];if(null!=c)return c.j?c.j(b):c.call(null,b);c=jA._;if(null!=c)return c.j?c.j(b):c.call(null,b);throw x("Source.toggle",b);},kA=function kA(b,c){if(null!=b&&null!=b.Cd)return b.Cd(b,c);var d=kA[p(null==b?null:b)];if(null!=d)return d.h?d.h(b,c):d.call(null,b,c);d=kA._;if(null!=d)return d.h?d.h(b,
-c):d.call(null,b,c);throw x("Source.seek",b);},lA=function lA(b,c){if(null!=b&&null!=b.Ad)return b.Ad(b,c);var d=lA[p(null==b?null:b)];if(null!=d)return d.h?d.h(b,c):d.call(null,b,c);d=lA._;if(null!=d)return d.h?d.h(b,c):d.call(null,b,c);throw x("Source.change-speed",b);};
-if("undefined"===typeof mA)var mA=function(){var a=function(){var a=rf;return X.j?X.j(a):X.call(null,a)}(),b=function(){var a=rf;return X.j?X.j(a):X.call(null,a)}(),c=function(){var a=rf;return X.j?X.j(a):X.call(null,a)}(),d=function(){var a=rf;return X.j?X.j(a):X.call(null,a)}(),e=I.l(rf,Un,Mi());return new Xi(ld.h("asciinema.player.source","make-source"),function(){return function(a){return a}}(a,b,c,d,e),jk,e,a,b,c,d)}();
-function nA(a){return Yf.h(rf,Me.h(function(a){var c=S(a,0,null);a=S(a,1,null);var d=W,c=Ne(c);return new V(null,2,5,d,[parseInt(c,10),a],null)},a))}function oA(a){return Me.h(function(a){return cg.l(a,new V(null,2,5,W,[1,Nk],null),nA)},a)}function pA(a,b){S(a,0,null);var c=S(a,1,null),d=S(b,0,null),e=S(b,1,null);return new V(null,2,5,W,[d,Qh.w(Ph,J([c,e],0))],null)}
-function qA(a){a=oA(a);var b=new r(null,2,[Nk,Mh(),Tk,new r(null,3,[En,0,bj,0,Sn,!0],null)],null);return ei(pA,new V(null,2,5,W,[0,b],null),a)}function rA(a,b){S(a,0,null);var c=S(a,1,null),d=S(b,0,null),e=S(b,1,null);return new V(null,2,5,W,[d,eA(c,e)],null)}function sA(a){var b=null!=a&&(a.o&64||a.D)?A.h(P,a):a;a=I.h(b,qo);var c=I.h(b,Kk),b=I.h(b,to),c=zy(c,b);return ei(rA,new V(null,2,5,W,[0,c],null),a)}
-function tA(a){var b=null!=a&&(a.o&64||a.D)?A.h(P,a):a;a=I.h(b,Nk);b=I.h(b,Tk);return new r(null,2,[Nk,Me.h(fA,a),Tk,b],null)}
-if("undefined"===typeof uA)var uA=function(){var a=function(){var a=rf;return X.j?X.j(a):X.call(null,a)}(),b=function(){var a=rf;return X.j?X.j(a):X.call(null,a)}(),c=function(){var a=rf;return X.j?X.j(a):X.call(null,a)}(),d=function(){var a=rf;return X.j?X.j(a):X.call(null,a)}(),e=I.l(rf,Un,Mi());return new Xi(ld.h("asciinema.player.source","initialize-asciicast"),function(){return function(a){return le(a)?0:Rn.j(a)}}(a,b,c,d,e),jk,e,a,b,c,d)}();
-Zi(uA,0,function(a){var b=Nk.j(Ud(C(a))),c=Ab.h(De,Me.h(function(){return function(a){return R(C(a))}}(b),C(Ug(b)))),d=R(b);return new r(null,5,[Kk,c,to,d,zk,function(a,b,c){return function(d){return new xi(function(){return function(){return cg.l(d,new V(null,1,5,W,[Nk],null),Ug)}}(a,b,c),null)}}(b,c,d),$k,Ab.l(function(){return function(a,b){return a+C(b)}}(b,c,d),0,a),xk,qA(a)],null)});
-Zi(uA,1,function(a){return new r(null,5,[Kk,Kk.j(a),to,to.j(a),zk,function(a){return new xi(function(){return tA(a)},null)},$k,Ab.l(function(a,c){return a+C(c)},0,qo.j(a)),xk,sA(a)],null)});Zi(uA,jk,function(a,b){throw[y("unsupported asciicast version: "),y(Rn.j(b))].join("");});function zA(a,b){return Tf(function(){return new V(null,2,5,W,[.3,a+(b.A?b.A():b.call(null))],null)})}function AA(a,b){return Me.h(function(a){var d=S(a,0,null);a=S(a,1,null);return new V(null,2,5,W,[d/b,a],null)},a)}
-function BA(a){var b=cs(null),c=cs(null),d=X.j?X.j(null):X.call(null,null),e=cs(1);Cr(function(b,c,d,e){return function(){var n=function(){return function(a){return function(){function b(c){for(;;){var d;a:try{for(;;){var e=a(c);if(!U(e,Y)){d=e;break a}}}catch(f){if(f instanceof Object)c[5]=f,Tr(c),d=Y;else throw f;}if(!U(d,Y))return d}}function c(){var a=[null,null,null,null,null,null,null,null];a[0]=d;a[1]=1;return a}var d=null,d=function(a){switch(arguments.length){case 0:return c.call(this);case 1:return b.call(this,
-a)}throw Error("Invalid arity: "+arguments.length);};d.A=c;d.j=b;return d}()}(function(b,c,d,e){return function(f){var g=f[1];if(1===g)return Qr(f,2,c);if(2===g){var k=f[2],l=function(){return function(a,b,c,d,e,f){return function(a){Ef.h?Ef.h(f,a):Ef.call(null,f,a);return cr(e)}}(k,g,b,c,d,e)}(),l=a.j?a.j(l):a.call(null,l);f[7]=k;return Sr(f,l)}return null}}(b,c,d,e),b,c,d,e)}(),m=function(){var a=n.A?n.A():n.call(null);a[6]=b;return a}();return Pr(m)}}(e,b,c,d));return function(a,b,c){return function(d){u(d)&&
-cr(a);d=cs(null);var e=cs(1);Cr(function(a,b,c,d,e){return function(){var f=function(){return function(a){return function(){function b(c){for(;;){var d;a:try{for(;;){var e=a(c);if(!U(e,Y)){d=e;break a}}}catch(f){if(f instanceof Object)c[5]=f,Tr(c),d=Y;else throw f;}if(!U(d,Y))return d}}function c(){var a=[null,null,null,null,null,null,null,null];a[0]=d;a[1]=1;return a}var d=null,d=function(a){switch(arguments.length){case 0:return c.call(this);case 1:return b.call(this,a)}throw Error("Invalid arity: "+
-arguments.length);};d.A=c;d.j=b;return d}()}(function(a,b,c,d,e){return function(a){var c=a[1];if(1===c)return Qr(a,2,d);if(2===c){var c=a[2],f=Q.j?Q.j(e):Q.call(null,e);a[7]=c;return Rr(a,3,b,f)}return 3===c?(c=a[2],Sr(a,c)):null}}(a,b,c,d,e),a,b,c,d,e)}(),g=function(){var b=f.A?f.A():f.call(null);b[6]=a;return b}();return Pr(g)}}(e,d,a,b,c));return d}}(b,c,d)}
-function CA(a,b){return BA(function(c){return qy(a,J([new r(null,3,[Ck,ik,co,function(a){a=b.j?b.j(a):b.call(null,a);return c.j?c.j(a):c.call(null,a)},fm,function(a){a=J([a],0);ti(a);u(bb)?(a=gb(),ki("\n"),a=(I.h(a,hb),null)):a=null;return a}],null)],0))})}
-function DA(a){var b=null!=a&&(a.o&64||a.D)?A.h(P,a):a,c=I.h(b,wk),d=I.h(b,Gk),e=cs(1);Cr(function(a,b,c,d,e){return function(){var m=function(){return function(a){return function(){function b(c){for(;;){var d;a:try{for(;;){var e=a(c);if(!U(e,Y)){d=e;break a}}}catch(f){if(f instanceof Object)c[5]=f,Tr(c),d=Y;else throw f;}if(!U(d,Y))return d}}function c(){var a=[null,null,null,null,null,null,null,null,null,null,null];a[0]=d;a[1]=1;return a}var d=null,d=function(a){switch(arguments.length){case 0:return c.call(this);
-case 1:return b.call(this,a)}throw Error("Invalid arity: "+arguments.length);};d.A=c;d.j=b;return d}()}(function(a,b,c,d,e){return function(a){var b=a[1];if(7===b)return a[2]=!1,a[1]=8,Y;if(1===b)return b=Q.j?Q.j(e):Q.call(null,e),b=b.j?b.j(!1):b.call(null,!1),Qr(a,2,b);if(4===b)return a[2]=!1,a[1]=5,Y;if(13===b)return b=a[2],Sr(a,b);if(6===b)return a[2]=!0,a[1]=8,Y;if(3===b){var b=a[7],c=b.D,b=b.o&64||c;a[1]=u(b)?6:7;return Y}if(12===b)return b=a[8],c=a[9],b=new V(null,3,5,W,[Wl,b,c],null),a[10]=
-a[2],Rr(a,13,d,b);if(2===b)return b=a[2],c=tb(null==b),a[7]=b,a[1]=c?3:4,Y;if(11===b){var c=a[2],f=I.h(c,$k),b=I.h(c,Kk),c=I.h(c,to),f=new V(null,2,5,W,[$k,f],null);a[8]=b;a[9]=c;return Rr(a,12,d,f)}return 9===b?(b=a[7],b=A.h(P,b),a[2]=b,a[1]=11,Y):5===b?(b=a[2],a[1]=u(b)?9:10,Y):10===b?(b=a[7],a[2]=b,a[1]=11,Y):8===b?(b=a[2],a[2]=b,a[1]=5,Y):null}}(a,b,c,d,e),a,b,c,d,e)}(),t=function(){var b=m.A?m.A():m.call(null);b[6]=a;return b}();return Pr(t)}}(e,a,b,c,d))}
-function EA(a){var b=null!=a&&(a.o&64||a.D)?A.h(P,a):a,c=I.h(b,wk),d=I.h(b,Gk);if(u(ks((Q.j?Q.j(d):Q.call(null,d)).call(null,!1))))return null;var e=cs(1);Cr(function(a,b,c,d,e){return function(){var m=function(){return function(a){return function(){function b(c){for(;;){var d;a:try{for(;;){var e=a(c);if(!U(e,Y)){d=e;break a}}}catch(f){if(f instanceof Object)c[5]=f,Tr(c),d=Y;else throw f;}if(!U(d,Y))return d}}function c(){var a=[null,null,null,null,null,null,null,null,null];a[0]=d;a[1]=1;return a}
-var d=null,d=function(a){switch(arguments.length){case 0:return c.call(this);case 1:return b.call(this,a)}throw Error("Invalid arity: "+arguments.length);};d.A=c;d.j=b;return d}()}(function(a,b,c,d,e){return function(a){var b=a[1];if(1===b)return Rr(a,2,d,new V(null,2,5,W,[Hm,!0],null));if(2===b){var b=a[2],c=Q.j?Q.j(e):Q.call(null,e),c=c.j?c.j(!1):c.call(null,!1);a[7]=b;return Qr(a,3,c)}return 3===b?(b=new V(null,2,5,W,[Hm,!1],null),a[8]=a[2],Rr(a,4,d,b)):4===b?(b=a[2],Sr(a,b)):null}}(a,b,c,d,e),
-a,b,c,d,e)}(),t=function(){var b=m.A?m.A():m.call(null);b[6]=a;return b}();return Pr(t)}}(e,a,b,c,d));return e}
-function FA(a,b,c,d,e){var f=Vq(1),g=cs(1);Cr(function(f,g){return function(){var n=function(){return function(a){return function(){function b(c){for(;;){var d;a:try{for(;;){var e=a(c);if(!U(e,Y)){d=e;break a}}}catch(f){if(f instanceof Object)c[5]=f,Tr(c),d=Y;else throw f;}if(!U(d,Y))return d}}function c(){var a=[null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null];a[0]=d;a[1]=1;return a}var d=null,d=function(a){switch(arguments.length){case 0:return c.call(this);
-case 1:return b.call(this,a)}throw Error("Invalid arity: "+arguments.length);};d.A=c;d.j=b;return d}()}(function(f,g){return function(f){var k=f[1];if(7===k){var l=f[8],k=$r(1E3*f[7]),l=new V(null,2,5,W,[e,k],null);f[8]=k;return ms(f,10,l,J([Im,!0],0))}if(1===k){var k=g.A?g.A():g.call(null),m=b;f[9]=k;f[10]=0;f[11]=m;f[2]=null;f[1]=2;return Y}if(4===k){var n=f[12],l=f[13],k=f[9],m=f[10],t=S(n,0,null),l=S(n,1,null),m=m+t,k=m-k;f[7]=k;f[13]=m;f[14]=l;f[1]=u(0<k)?7:8;return Y}return 15===k?(l=f[13],
-k=f[9],m=f[11],n=f[2],m=N(m),f[15]=n,f[9]=k,f[10]=l,f[11]=m,f[2]=null,f[1]=2,Y):13===k?(k=f[2],f[2]=k,f[1]=9,Y):6===k?(k=f[2],f[2]=k,f[1]=3,Y):3===k?(k=f[2],Sr(f,k)):12===k?(f[2]=null,f[1]=13,Y):2===k?(m=f[11],k=C(m),f[12]=k,f[1]=u(k)?4:5,Y):11===k?(l=f[14],k=W,l=c.j?c.j(l):c.call(null,l),k=new V(null,2,5,k,[a,l],null),Rr(f,14,d,k)):9===k?(k=f[2],f[2]=k,f[1]=6,Y):5===k?(f[2]=null,f[1]=6,Y):14===k?(l=f[13],m=f[11],k=f[2],m=N(m),n=g.A?g.A():g.call(null),f[16]=k,f[9]=n,f[10]=l,f[11]=m,f[2]=null,f[1]=
-2,Y):10===k?(l=f[8],m=f[2],k=S(m,0,null),m=S(m,1,null),l=H.h(m,l),f[17]=k,f[1]=l?11:12,Y):8===k?(l=f[14],k=W,l=c.j?c.j(l):c.call(null,l),k=new V(null,2,5,k,[a,l],null),Rr(f,15,d,k)):null}}(f,g),f,g)}(),m=function(){var a=n.A?n.A():n.call(null);a[6]=f;return a}();return Pr(m)}}(g,f));return g}
-function GA(a,b,c,d,e,f,g,k){var l=cs(1);Cr(function(l){return function(){var m=function(){return function(a){return function(){function b(c){for(;;){var d;a:try{for(;;){var e=a(c);if(!U(e,Y)){d=e;break a}}}catch(f){if(f instanceof Object)c[5]=f,Tr(c),d=Y;else throw f;}if(!U(d,Y))return d}}function c(){var a=[null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null];a[0]=d;a[1]=1;return a}var d=null,d=function(a){switch(arguments.length){case 0:return c.call(this);
-case 1:return b.call(this,a)}throw Error("Invalid arity: "+arguments.length);};d.A=c;d.j=b;return d}()}(function(){return function(l){var m=l[1];if(7===m)return Rr(l,14,a,new V(null,2,5,W,[ln,!1],null));if(1===m)return m=new V(null,2,5,W,[ln,!0],null),Rr(l,2,a,m);if(4===m)return m=l[2],Sr(l,m);if(13===m)return l[7]=l[2],l[2]=0,l[1]=11,Y;if(6===m)return l[1]=u(k)?9:10,Y;if(3===m){var n=l[8],t=l[9],n=l[10],m=l[11],t=Vq(g);a:for(var n=b,F=m;;)if(K(n)){var M=C(n),O=S(M,0,null),M=S(M,1,null);if(O<F)n=
-N(n),F-=O;else{n=Md(new V(null,2,5,W,[O-F,M],null),N(n));break a}}else break a;n=AA(n,g);F=zA(m,t);m=cs(null);n=FA(Jj,n,c,a,m);F=FA(Bk,F,Be,a,m);O=new V(null,2,5,W,[n,e],null);l[8]=m;l[9]=n;l[10]=t;l[12]=F;return ls(l,5,O)}return 12===m?(m=new V(null,2,5,W,[ln,!1],null),l[13]=l[2],Rr(l,13,a,m)):2===m?(t=l[2],m=f,l[14]=t,l[11]=m,l[2]=null,l[1]=3,Y):11===m?(m=l[2],l[2]=m,l[1]=8,Y):9===m?(l[11]=0,l[2]=null,l[1]=3,Y):5===m?(n=l[8],t=l[9],F=l[2],m=S(F,0,null),F=S(F,1,null),n=cr(n),t=H.h(F,t),l[15]=n,l[16]=
-m,l[1]=t?6:7,Y):14===m?(n=l[10],m=l[11],t=l[2],n=n.A?n.A():n.call(null),l[17]=t,l[2]=m+n,l[1]=8,Y):10===m?(m=new V(null,2,5,W,[Bk,d],null),Rr(l,12,a,m)):8===m?(m=l[2],l[2]=m,l[1]=4,Y):null}}(l),l)}(),t=function(){var a=m.A?m.A():m.call(null);a[6]=l;return a}();return Pr(t)}}(l));return l}
-function HA(a){var b=null!=a&&(a.o&64||a.D)?A.h(P,a):a,c=I.h(b,wk),d=I.h(b,Gk),e=I.h(b,hk),f=I.h(b,ak),g=I.h(b,hn),k=cs(10),l=cs(10),n=cs(1);Cr(function(a,b,c,d,e,f,g,k,l,n,ba){return function(){var Ca=function(){return function(a){return function(){function b(c){for(;;){var d;a:try{for(;;){var e=a(c);if(!U(e,Y)){d=e;break a}}}catch(f){if(f instanceof Object)c[5]=f,Tr(c),d=Y;else throw f;}if(!U(d,Y))return d}}function c(){var a=[null,null,null,null,null,null,null,null,null,null,null,null,null,null,
-null,null,null,null,null,null,null,null,null,null,null,null,null,null];a[0]=d;a[1]=1;return a}var d=null,d=function(a){switch(arguments.length){case 0:return c.call(this);case 1:return b.call(this,a)}throw Error("Invalid arity: "+arguments.length);};d.A=c;d.j=b;return d}()}(function(a,b,c,d,e,f,g,k,l,m,n){return function(a){var d=a[1];if(65===d){var e=a[7],t=e.D,q=a;q[1]=u(e.o&64||t)?68:69;return Y}if(70===d){var w=a[2],z=q=a;z[2]=w;z[1]=67;return Y}if(62===d){var L=a[8],E=[y("No matching clause: "),
-y(L)].join("");throw Error(E);}if(74===d){var F=a[9],M=a[10],O=a[11],xa=a[2],Z=W,ba;a:for(var fb=O,Ca=M,ya=null;;){var Ei=C(fb),jl=S(Ei,0,null),Uo=S(Ei,1,null);if(null==Ei||Ca<jl){ba=ya;break a}var Vo=N(fb),Wo=Ca-jl,Xo=Uo,fb=Vo,Ca=Wo,ya=Xo}var Yo=F.j?F.j(ba):F.call(null,ba),Zo=new V(null,2,5,Z,[Jj,Yo],null);a[12]=xa;q=a;return Rr(q,75,g,Zo)}if(7===d){var L=a[8],kl=a[2],ll=S(kl,0,null),M=S(kl,1,null),$o=H.h(Lk,ll);a[10]=M;a[8]=ll;q=a;q[1]=$o?8:9;return Y}if(59===d){var L=a[8],ap=H.h(lo,L),q=a;q[1]=
-ap?61:62;return Y}if(20===d){var bp=a[2],ml=q=a;ml[2]=bp;ml[1]=17;return Y}if(72===d){var e=a[7],nl=q=a;nl[2]=e;nl[1]=73;return Y}if(58===d){var Qb=a[13],Rb=0,wc=Qb,ob=null,Pa=null;a[14]=Rb;a[15]=Pa;a[16]=ob;a[13]=wc;var ol=q=a;ol[2]=null;ol[1]=2;return Y}if(60===d){var cp=a[2],pl=q=a;pl[2]=cp;pl[1]=52;return Y}if(27===d){var Pa=a[15],ob=a[16],dp=cr(Pa);a[17]=dp;q=a;return Qr(q,30,ob)}if(1===d){Rb=l;Qb=m;Pa=ob=null;a[14]=Rb;a[15]=Pa;a[16]=ob;a[13]=Qb;var ql=q=a;ql[2]=null;ql[1]=2;return Y}if(69===
-d){var rl=q=a;rl[2]=!1;rl[1]=70;return Y}if(24===d)return Pa=a[15],q=a,q[1]=u(Pa)?27:28,Y;if(55===d){var M=a[10],Rb=a[14],Pa=a[15],ob=a[16],ep=a[2],fp=Pa,gp=ob,pd=Rb,Qb=M,qd=gp,rd=fp;a[18]=ep;a[14]=pd;a[15]=rd;a[16]=qd;a[13]=Qb;var sl=q=a;sl[2]=null;sl[1]=2;return Y}if(39===d){var L=a[8],hp=H.h(pm,L),q=a;q[1]=hp?50:51;return Y}if(46===d){var ip=new V(null,1,5,W,[Lk],null),q=a;return Rr(q,49,c,ip)}if(4===d){var ob=a[16],tl=a[2],rg=S(tl,0,null),jp=S(tl,1,null),kp=H.h(jp,ob);a[19]=rg;q=a;q[1]=kp?5:6;
-return Y}if(54===d){var ul=q=a;ul[2]=null;ul[1]=55;return Y}if(15===d){var Xe=a[20],lp=Xe.D,mp=Xe.o&64||lp,q=a;q[1]=u(mp)?18:19;return Y}if(48===d){var Rb=a[14],Pa=a[15],ob=a[16],Qb=a[13],np=a[2],op=Pa,pp=ob,qp=Qb,pd=Rb,wc=qp,qd=pp,rd=op;a[14]=pd;a[15]=rd;a[16]=qd;a[13]=wc;a[21]=np;var vl=q=a;vl[2]=null;vl[1]=2;return Y}if(50===d)return Pa=a[15],q=a,q[1]=u(Pa)?53:54,Y;if(75===d){var M=a[10],Pa=a[15],ob=a[16],Qb=a[13],rp=a[2],sp=Pa,tp=ob,up=Qb,Rb=M,wc=up,qd=tp,rd=sp;a[22]=rp;a[14]=Rb;a[15]=rd;a[16]=
-qd;a[13]=wc;var wl=q=a;wl[2]=null;wl[1]=2;return Y}if(21===d){var Xe=a[20],vp=A.h(P,Xe),xl=q=a;xl[2]=vp;xl[1]=23;return Y}if(31===d)return Pa=a[15],q=a,q[1]=u(Pa)?34:35,Y;if(32===d){var L=a[8],wp=H.h(Pn,L),q=a;q[1]=wp?38:39;return Y}if(40===d){var xp=a[2],yl=q=a;yl[2]=xp;yl[1]=33;return Y}if(56===d){var yp=new V(null,1,5,W,[Lk],null);a[23]=a[2];q=a;return Rr(q,57,c,yp)}if(33===d){var zp=a[2],zl=q=a;zl[2]=zp;zl[1]=26;return Y}if(13===d){var Ap=a[2],Al=q=a;Al[2]=Ap;Al[1]=10;return Y}if(22===d){var Xe=
-a[20],Bl=q=a;Bl[2]=Xe;Bl[1]=23;return Y}if(36===d){var Bp=new V(null,1,5,W,[a[2]],null),q=a;return Rr(q,37,c,Bp)}if(41===d){var Cp=new V(null,1,5,W,[ym],null),q=a;return Rr(q,44,c,Cp)}if(43===d){var M=a[10],Dp=new V(null,2,5,W,[lo,M],null);a[24]=a[2];q=a;return Rr(q,45,c,Dp)}if(61===d){var Fi=Q.j?Q.j(k):Q.call(null,k),Ep=Fi.j?Fi.j(!0):Fi.call(null,!0),q=a;return Qr(q,64,Ep)}if(29===d){var Fp=a[2],Cl=q=a;Cl[2]=Fp;Cl[1]=26;return Y}if(44===d){var Gp=a[2],Dl=q=a;Dl[2]=Gp;Dl[1]=43;return Y}if(6===d){var rg=
-a[19],El=q=a;El[2]=rg;El[1]=7;return Y}if(28===d){var Rb=a[14],Pa=a[15],ob=a[16],Qb=a[13],Hp=Pa,Ip=ob,Jp=Qb,pd=Rb,wc=Jp,qd=Ip,rd=Hp;a[14]=pd;a[15]=rd;a[16]=qd;a[13]=wc;var Fl=q=a;Fl[2]=null;Fl[1]=2;return Y}if(64===d){var e=a[7],Gl=a[2],Kp=tb(null==Gl);a[7]=Gl;q=a;q[1]=Kp?65:66;return Y}if(51===d){var L=a[8],Lp=H.h(yj,L),q=a;q[1]=Lp?58:59;return Y}if(25===d){var L=a[8],Mp=H.h(Ol,L),q=a;q[1]=Mp?31:32;return Y}if(34===d){var Hl=q=a;Hl[2]=ym;Hl[1]=36;return Y}if(17===d){var vA=a[2],q=a;q[1]=u(vA)?21:
-22;return Y}if(3===d){var WA=a[2],q=a;return Sr(q,WA)}if(12===d){var XA=EA(f),Sp=Q.j?Q.j(k):Q.call(null,k),YA=Sp.j?Sp.j(!0):Sp.call(null,!0);a[25]=XA;q=a;return Qr(q,14,YA)}if(2===d){var ob=a[16],ZA=Xf(new V(null,3,5,W,[c,b,ob],null)),q=a;return ms(q,4,ZA,J([Im,!0],0))}if(66===d){var ru=q=a;ru[2]=!1;ru[1]=67;return Y}if(23===d){var Rb=a[14],Qb=a[13],Tp=a[2],$A=I.h(Tp,xk),aB=I.h(Tp,zk),bB=I.h(Tp,$k),su=cs(null),cB=GA(g,$A,aB,bB,su,Rb,Qb,n),dB=Qb,pd=null,wc=dB,ob=cB,Pa=su;a[14]=pd;a[15]=Pa;a[16]=ob;
-a[13]=wc;var tu=q=a;tu[2]=null;tu[1]=2;return Y}if(47===d){var uu=q=a;uu[2]=null;uu[1]=48;return Y}if(35===d){var vu=q=a;vu[2]=Lk;vu[1]=36;return Y}if(19===d){var wu=q=a;wu[2]=!1;wu[1]=20;return Y}if(57===d){var eB=a[2],xu=q=a;xu[2]=eB;xu[1]=55;return Y}if(68===d){var yu=q=a;yu[2]=!0;yu[1]=70;return Y}if(11===d){var Rb=a[14],Pa=a[15],ob=a[16],Qb=a[13],fB=Pa,gB=ob,hB=Qb,pd=Rb,wc=hB,qd=gB,rd=fB;a[14]=pd;a[15]=rd;a[16]=qd;a[13]=wc;var zu=q=a;zu[2]=null;zu[1]=2;return Y}if(9===d){var L=a[8],iB=H.h(ym,
-L),q=a;q[1]=iB?24:25;return Y}if(5===d){var rg=a[19],jB=new V(null,2,5,W,[yj,rg],null),Au=q=a;Au[2]=jB;Au[1]=7;return Y}if(14===d){var Xe=a[20],Bu=a[2],kB=tb(null==Bu);a[20]=Bu;q=a;q[1]=kB?15:16;return Y}if(45===d)return Pa=a[15],a[26]=a[2],q=a,q[1]=u(Pa)?46:47,Y;if(53===d){var lB=new V(null,1,5,W,[ym],null),q=a;return Rr(q,56,c,lB)}if(26===d){var mB=a[2],Cu=q=a;Cu[2]=mB;Cu[1]=10;return Y}if(16===d){var Du=q=a;Du[2]=!1;Du[1]=17;return Y}if(38===d)return Pa=a[15],q=a,q[1]=u(Pa)?41:42,Y;if(30===d){var nB=
-Qb=a[13],Rb=a[2],wc=nB,Pa=ob=null;a[14]=Rb;a[15]=Pa;a[16]=ob;a[13]=wc;var Eu=q=a;Eu[2]=null;Eu[1]=2;return Y}if(73===d){var M=a[10],Fu=a[2],O=I.h(Fu,xk),F=I.h(Fu,zk),oB=new V(null,2,5,W,[Bk,M],null);a[9]=F;a[11]=O;q=a;return Rr(q,74,g,oB)}if(10===d){var pB=a[2],Gu=q=a;Gu[2]=pB;Gu[1]=3;return Y}if(18===d){var Hu=q=a;Hu[2]=!0;Hu[1]=20;return Y}if(52===d){var qB=a[2],Iu=q=a;Iu[2]=qB;Iu[1]=40;return Y}if(67===d){var rB=a[2],q=a;q[1]=u(rB)?71:72;return Y}if(71===d){var e=a[7],sB=A.h(P,e),Ju=q=a;Ju[2]=
-sB;Ju[1]=73;return Y}if(42===d){var Ku=q=a;Ku[2]=null;Ku[1]=43;return Y}if(37===d){var Rb=a[14],Pa=a[15],ob=a[16],Qb=a[13],tB=a[2],uB=Pa,vB=ob,wB=Qb,pd=Rb,wc=wB,qd=vB,rd=uB;a[27]=tB;a[14]=pd;a[15]=rd;a[16]=qd;a[13]=wc;var Lu=q=a;Lu[2]=null;Lu[1]=2;return Y}if(63===d){var xB=a[2],Mu=q=a;Mu[2]=xB;Mu[1]=60;return Y}if(8===d)return Pa=a[15],q=a,q[1]=u(Pa)?11:12,Y;if(49===d){var yB=a[2],Nu=q=a;Nu[2]=yB;Nu[1]=48;return Y}return null}}(a,b,c,d,e,f,g,k,l,n,ba),a,b,c,d,e,f,g,k,l,n,ba)}(),L=function(){var b=
-Ca.A?Ca.A():Ca.call(null);b[6]=a;return b}();return Pr(L)}}(n,k,l,a,b,b,c,d,e,f,g));return k}function IA(a,b,c,d,e,f,g,k,l,n,m,t,q,z){this.S=a;this.url=b;this.hb=c;this.speed=d;this.R=e;this.gb=f;this.cb=g;this.eb=k;this.Ha=l;this.V=n;this.sa=m;this.Y=t;this.O=q;this.H=z;this.o=2229667594;this.M=8192}h=IA.prototype;
-h.Bd=function(){var a=this.sa,b=HA(this);Ef.h?Ef.h(a,b):Ef.call(null,a,b);a=CA(this.url,this.eb);Ef.h?Ef.h(this.Ha,a):Ef.call(null,this.Ha,a);DA(this);u(this.cb)&&(Q.j?Q.j(this.Ha):Q.call(null,this.Ha)).call(null,!0);return u(this.R)?hA(this):null};h.Dd=function(){return fs(Q.j?Q.j(this.sa):Q.call(null,this.sa),new V(null,1,5,W,[Lk],null))};h.Ed=function(){return fs(Q.j?Q.j(this.sa):Q.call(null,this.sa),new V(null,1,5,W,[ym],null))};
-h.Fd=function(){return fs(Q.j?Q.j(this.sa):Q.call(null,this.sa),new V(null,1,5,W,[Ol],null))};h.Cd=function(a,b){return fs(Q.j?Q.j(this.sa):Q.call(null,this.sa),new V(null,2,5,W,[Pn,b],null))};h.Ad=function(a,b){return fs(Q.j?Q.j(this.sa):Q.call(null,this.sa),new V(null,2,5,W,[pm,b],null))};h.X=function(a,b){return Ub.l(this,b,null)};
-h.P=function(a,b,c){switch(b instanceof v?b.ab:null){case "preload?":return this.cb;case "speed":return this.speed;case "start-at":return this.hb;case "events-ch":return this.S;case "recording-ch-fn":return this.Ha;case "command-ch":return this.sa;case "stop-ch":return this.V;case "auto-play?":return this.R;case "url":return this.url;case "loop?":return this.gb;case "recording-fn":return this.eb;default:return I.l(this.O,b,c)}};
-h.T=function(a,b,c){return ug(b,function(){return function(a){return ug(b,vg,""," ","",c,a)}}(this),"#asciinema.player.source.PrerecordedSource{",", ","}",c,gf.h(new V(null,11,5,W,[new V(null,2,5,W,[wk,this.S],null),new V(null,2,5,W,[en,this.url],null),new V(null,2,5,W,[hk,this.hb],null),new V(null,2,5,W,[ak,this.speed],null),new V(null,2,5,W,[Jm,this.R],null),new V(null,2,5,W,[hn,this.gb],null),new V(null,2,5,W,[rj,this.cb],null),new V(null,2,5,W,[Qn,this.eb],null),new V(null,2,5,W,[Gk,this.Ha],
-null),new V(null,2,5,W,[Yl,this.V],null),new V(null,2,5,W,[Kl,this.sa],null)],null),this.O))};h.qb=function(){return new Lg(0,this,11,new V(null,11,5,W,[wk,en,hk,ak,Jm,hn,rj,Qn,Gk,Yl,Kl],null),Vc(this.O))};h.Z=function(){return this.Y};h.Za=function(){return new IA(this.S,this.url,this.hb,this.speed,this.R,this.gb,this.cb,this.eb,this.Ha,this.V,this.sa,this.Y,this.O,this.H)};h.ia=function(){return 11+R(this.O)};h.W=function(){var a=this.H;return null!=a?a:this.H=a=Qe(this)};
-h.L=function(a,b){var c;c=u(b)?(c=this.constructor===b.constructor)?Kg(this,b):c:b;return u(c)?!0:!1};h.jc=function(a,b){return ve(new Vh(null,new r(null,11,[rj,null,ak,null,hk,null,wk,null,Gk,null,Kl,null,Yl,null,Jm,null,en,null,hn,null,Qn,null],null),null),b)?be.h(Bd(Yf.h(rf,this),this.Y),b):new IA(this.S,this.url,this.hb,this.speed,this.R,this.gb,this.cb,this.eb,this.Ha,this.V,this.sa,this.Y,lf(be.h(this.O,b)),null)};
-h.Gb=function(a,b,c){return u(U.h?U.h(wk,b):U.call(null,wk,b))?new IA(c,this.url,this.hb,this.speed,this.R,this.gb,this.cb,this.eb,this.Ha,this.V,this.sa,this.Y,this.O,null):u(U.h?U.h(en,b):U.call(null,en,b))?new IA(this.S,c,this.hb,this.speed,this.R,this.gb,this.cb,this.eb,this.Ha,this.V,this.sa,this.Y,this.O,null):u(U.h?U.h(hk,b):U.call(null,hk,b))?new IA(this.S,this.url,c,this.speed,this.R,this.gb,this.cb,this.eb,this.Ha,this.V,this.sa,this.Y,this.O,null):u(U.h?U.h(ak,b):U.call(null,ak,b))?new IA(this.S,
-this.url,this.hb,c,this.R,this.gb,this.cb,this.eb,this.Ha,this.V,this.sa,this.Y,this.O,null):u(U.h?U.h(Jm,b):U.call(null,Jm,b))?new IA(this.S,this.url,this.hb,this.speed,c,this.gb,this.cb,this.eb,this.Ha,this.V,this.sa,this.Y,this.O,null):u(U.h?U.h(hn,b):U.call(null,hn,b))?new IA(this.S,this.url,this.hb,this.speed,this.R,c,this.cb,this.eb,this.Ha,this.V,this.sa,this.Y,this.O,null):u(U.h?U.h(rj,b):U.call(null,rj,b))?new IA(this.S,this.url,this.hb,this.speed,this.R,this.gb,c,this.eb,this.Ha,this.V,
-this.sa,this.Y,this.O,null):u(U.h?U.h(Qn,b):U.call(null,Qn,b))?new IA(this.S,this.url,this.hb,this.speed,this.R,this.gb,this.cb,c,this.Ha,this.V,this.sa,this.Y,this.O,null):u(U.h?U.h(Gk,b):U.call(null,Gk,b))?new IA(this.S,this.url,this.hb,this.speed,this.R,this.gb,this.cb,this.eb,c,this.V,this.sa,this.Y,this.O,null):u(U.h?U.h(Yl,b):U.call(null,Yl,b))?new IA(this.S,this.url,this.hb,this.speed,this.R,this.gb,this.cb,this.eb,this.Ha,c,this.sa,this.Y,this.O,null):u(U.h?U.h(Kl,b):U.call(null,Kl,b))?new IA(this.S,
-this.url,this.hb,this.speed,this.R,this.gb,this.cb,this.eb,this.Ha,this.V,c,this.Y,this.O,null):new IA(this.S,this.url,this.hb,this.speed,this.R,this.gb,this.cb,this.eb,this.Ha,this.V,this.sa,this.Y,T.l(this.O,b,c),null)};
-h.fa=function(){return K(gf.h(new V(null,11,5,W,[new V(null,2,5,W,[wk,this.S],null),new V(null,2,5,W,[en,this.url],null),new V(null,2,5,W,[hk,this.hb],null),new V(null,2,5,W,[ak,this.speed],null),new V(null,2,5,W,[Jm,this.R],null),new V(null,2,5,W,[hn,this.gb],null),new V(null,2,5,W,[rj,this.cb],null),new V(null,2,5,W,[Qn,this.eb],null),new V(null,2,5,W,[Gk,this.Ha],null),new V(null,2,5,W,[Yl,this.V],null),new V(null,2,5,W,[Kl,this.sa],null)],null),this.O))};
-h.ba=function(a,b){return new IA(this.S,this.url,this.hb,this.speed,this.R,this.gb,this.cb,this.eb,this.Ha,this.V,this.sa,b,this.O,this.H)};h.ha=function(a,b){return le(b)?Wb(this,Lb.h(b,0),Lb.h(b,1)):Ab.l(Jb,this,b)};function JA(a,b,c,d,e,f,g,k){var l=X.j?X.j(null):X.call(null,null),n=X.j?X.j(null):X.call(null,null),m=X.j?X.j(null):X.call(null,null);return new IA(a,b,c,d,e,f,g,k,l,n,m,null,null,null)}
-Zi(mA,Qj,function(a,b,c,d,e,f,g,k,l,n){return JA(b,c,f,g,k,l,n,function(a){a=Uq(JSON.parse(a));return uA.j?uA.j(a):uA.call(null,a)})});
-function KA(a,b,c){var d=cs(null),e=cs(1);Cr(function(d,e){return function(){var k=function(){return function(a){return function(){function b(c){for(;;){var d;a:try{for(;;){var e=a(c);if(!U(e,Y)){d=e;break a}}}catch(f){if(f instanceof Object)c[5]=f,Tr(c),d=Y;else throw f;}if(!U(d,Y))return d}}function c(){var a=[null,null,null,null,null,null,null,null,null,null,null];a[0]=d;a[1]=1;return a}var d=null,d=function(a){switch(arguments.length){case 0:return c.call(this);case 1:return b.call(this,a)}throw Error("Invalid arity: "+
-arguments.length);};d.A=c;d.j=b;return d}()}(function(d,e){return function(f){var g=f[1];if(1===g){var k=zy(a,b);f[7]=k;f[2]=null;f[1]=2;return Y}if(2===g)return Qr(f,4,e);if(3===g){var l=f[2];return Sr(f,l)}if(4===g){var E=f[8],l=f[2];f[8]=l;f[1]=u(l)?5:6;return Y}if(5===g){var F=f[9],k=f[7],E=f[8],M=eA(k,E),O=W,l=new V(null,2,5,O,[Jj,new xi(function(){return function(a,b,c,d){return function(){return tA(d)}}(k,E,E,M,F,k,E,M,O,g,d,e)}(),null)],null);f[9]=M;return Rr(f,8,c,l)}return 6===g?(f[2]=null,
-f[1]=7,Y):7===g?(l=f[2],f[2]=l,f[1]=3,Y):8===g?(F=f[9],l=f[2],k=F,f[7]=k,f[10]=l,f[2]=null,f[1]=2,Y):null}}(d,e),d,e)}(),l=function(){var a=k.A?k.A():k.call(null);a[6]=d;return a}();return Pr(l)}}(e,d));return d}
-function LA(a,b,c,d){var e=cs(1);Cr(function(e){return function(){var g=function(){return function(a){return function(){function b(c){for(;;){var d;a:try{for(;;){var e=a(c);if(!U(e,Y)){d=e;break a}}}catch(f){if(f instanceof Object)c[5]=f,Tr(c),d=Y;else throw f;}if(!U(d,Y))return d}}function c(){var a=[null,null,null,null,null,null,null,null,null,null,null];a[0]=d;a[1]=1;return a}var d=null,d=function(a){switch(arguments.length){case 0:return c.call(this);case 1:return b.call(this,a)}throw Error("Invalid arity: "+
-arguments.length);};d.A=c;d.j=b;return d}()}(function(){return function(e){var f=e[1];if(7===f)return Rr(e,9,b,String.fromCharCode(Math.floor(160*Math.random())));if(1===f)return f=new V(null,2,5,W,[ln,!0],null),Rr(e,2,a,f);if(4===f)return f=new V(null,2,5,W,[ln,!1],null),e[7]=e[2],Rr(e,10,a,f);if(6===f)return e[2]=null,e[1]=8,Y;if(3===f){var f=W,g=100*zi.A()/c,g=$r(g),f=new V(null,2,5,f,[d,g],null);return ls(e,5,f)}return 2===f?(e[8]=e[2],e[2]=null,e[1]=3,Y):9===f?(e[9]=e[2],e[2]=null,e[1]=3,Y):
-5===f?(g=e[2],f=S(g,0,null),g=S(g,1,null),g=H.h(g,d),e[10]=f,e[1]=g?6:7,Y):10===f?(f=e[2],Sr(e,f)):8===f?(f=e[2],e[2]=f,e[1]=4,Y):null}}(e),e)}(),k=function(){var a=g.A?g.A():g.call(null);a[6]=e;return a}();return Pr(k)}}(e));return e}function MA(a,b,c,d,e,f,g,k,l,n){this.S=a;this.speed=b;this.R=c;this.width=d;this.height=e;this.nb=f;this.V=g;this.Y=k;this.O=l;this.H=n;this.o=2229667594;this.M=8192}h=MA.prototype;
-h.Bd=function(){var a=this.nb,b=KA(this.width,this.height,this.S);Ef.h?Ef.h(a,b):Ef.call(null,a,b);return u(this.R)?hA(this):null};h.Dd=function(){if(u(Q.j?Q.j(this.V):Q.call(null,this.V)))return null;var a=cs(null);Ef.h?Ef.h(this.V,a):Ef.call(null,this.V,a);return LA(this.S,Q.j?Q.j(this.nb):Q.call(null,this.nb),this.speed,a)};h.Ed=function(){if(u(Q.j?Q.j(this.V):Q.call(null,this.V))){var a=Q.j?Q.j(this.V):Q.call(null,this.V);cr(a);return Ef.h?Ef.h(this.V,null):Ef.call(null,this.V,null)}return null};
-h.Fd=function(){return u(Q.j?Q.j(this.V):Q.call(null,this.V))?iA(this):hA(this)};h.Cd=function(){return null};h.Ad=function(){return null};h.X=function(a,b){return Ub.l(this,b,null)};h.P=function(a,b,c){switch(b instanceof v?b.ab:null){case "events-ch":return this.S;case "speed":return this.speed;case "auto-play?":return this.R;case "width":return this.width;case "height":return this.height;case "stdout-ch":return this.nb;case "stop-ch":return this.V;default:return I.l(this.O,b,c)}};
-h.T=function(a,b,c){return ug(b,function(){return function(a){return ug(b,vg,""," ","",c,a)}}(this),"#asciinema.player.source.RandomSource{",", ","}",c,gf.h(new V(null,7,5,W,[new V(null,2,5,W,[wk,this.S],null),new V(null,2,5,W,[ak,this.speed],null),new V(null,2,5,W,[Jm,this.R],null),new V(null,2,5,W,[Kk,this.width],null),new V(null,2,5,W,[to,this.height],null),new V(null,2,5,W,[tn,this.nb],null),new V(null,2,5,W,[Yl,this.V],null)],null),this.O))};
-h.qb=function(){return new Lg(0,this,7,new V(null,7,5,W,[wk,ak,Jm,Kk,to,tn,Yl],null),Vc(this.O))};h.Z=function(){return this.Y};h.Za=function(){return new MA(this.S,this.speed,this.R,this.width,this.height,this.nb,this.V,this.Y,this.O,this.H)};h.ia=function(){return 7+R(this.O)};h.W=function(){var a=this.H;return null!=a?a:this.H=a=Qe(this)};h.L=function(a,b){var c;c=u(b)?(c=this.constructor===b.constructor)?Kg(this,b):c:b;return u(c)?!0:!1};
-h.jc=function(a,b){return ve(new Vh(null,new r(null,7,[ak,null,wk,null,Kk,null,Yl,null,Jm,null,tn,null,to,null],null),null),b)?be.h(Bd(Yf.h(rf,this),this.Y),b):new MA(this.S,this.speed,this.R,this.width,this.height,this.nb,this.V,this.Y,lf(be.h(this.O,b)),null)};
-h.Gb=function(a,b,c){return u(U.h?U.h(wk,b):U.call(null,wk,b))?new MA(c,this.speed,this.R,this.width,this.height,this.nb,this.V,this.Y,this.O,null):u(U.h?U.h(ak,b):U.call(null,ak,b))?new MA(this.S,c,this.R,this.width,this.height,this.nb,this.V,this.Y,this.O,null):u(U.h?U.h(Jm,b):U.call(null,Jm,b))?new MA(this.S,this.speed,c,this.width,this.height,this.nb,this.V,this.Y,this.O,null):u(U.h?U.h(Kk,b):U.call(null,Kk,b))?new MA(this.S,this.speed,this.R,c,this.height,this.nb,this.V,this.Y,this.O,null):u(U.h?
-U.h(to,b):U.call(null,to,b))?new MA(this.S,this.speed,this.R,this.width,c,this.nb,this.V,this.Y,this.O,null):u(U.h?U.h(tn,b):U.call(null,tn,b))?new MA(this.S,this.speed,this.R,this.width,this.height,c,this.V,this.Y,this.O,null):u(U.h?U.h(Yl,b):U.call(null,Yl,b))?new MA(this.S,this.speed,this.R,this.width,this.height,this.nb,c,this.Y,this.O,null):new MA(this.S,this.speed,this.R,this.width,this.height,this.nb,this.V,this.Y,T.l(this.O,b,c),null)};
-h.fa=function(){return K(gf.h(new V(null,7,5,W,[new V(null,2,5,W,[wk,this.S],null),new V(null,2,5,W,[ak,this.speed],null),new V(null,2,5,W,[Jm,this.R],null),new V(null,2,5,W,[Kk,this.width],null),new V(null,2,5,W,[to,this.height],null),new V(null,2,5,W,[tn,this.nb],null),new V(null,2,5,W,[Yl,this.V],null)],null),this.O))};h.ba=function(a,b){return new MA(this.S,this.speed,this.R,this.width,this.height,this.nb,this.V,b,this.O,this.H)};
-h.ha=function(a,b){return le(b)?Wb(this,Lb.h(b,0),Lb.h(b,1)):Ab.l(Jb,this,b)};Zi(mA,nn,function(a,b,c,d,e,f,g,k){a=X.j?X.j(null):X.call(null,null);c=X.j?X.j(null):X.call(null,null);return new MA(b,g,k,d,e,a,c,null,null,null)});function NA(a){return Uq(JSON.parse(a))}
-function OA(a,b){var c=cs(1);Cr(function(c){return function(){var e=function(){return function(a){return function(){function b(c){for(;;){var d;a:try{for(;;){var e=a(c);if(!U(e,Y)){d=e;break a}}}catch(f){if(f instanceof Object)c[5]=f,Tr(c),d=Y;else throw f;}if(!U(d,Y))return d}}function c(){var a=[null,null,null,null,null,null,null,null,null,null,null,null,null];a[0]=d;a[1]=1;return a}var d=null,d=function(a){switch(arguments.length){case 0:return c.call(this);case 1:return b.call(this,a)}throw Error("Invalid arity: "+
-arguments.length);};d.A=c;d.j=b;return d}()}(function(){return function(c){var d=c[1];if(7===d)return c[2]=!1,c[1]=8,Y;if(20===d)return c[2]=!1,c[1]=21,Y;if(27===d){var d=c[7],e=I.h(c[2],qo);return Rr(c,28,d,e)}if(1===d)return Qr(c,2,a);if(24===d)return d=c[2],c[2]=d,c[1]=21,Y;if(4===d)return c[2]=!1,c[1]=5,Y;if(15===d)return d=c[8],d=c[2],c[8]=d,c[1]=u(d)?16:17,Y;if(21===d)return d=c[2],c[1]=u(d)?25:26,Y;if(13===d)return Qr(c,15,a);if(22===d)return c[2]=!0,c[1]=24,Y;if(6===d)return c[2]=!0,c[1]=
-8,Y;if(28===d)return c[9]=c[2],c[2]=null,c[1]=13,Y;if(25===d)return d=c[8],d=A.h(P,d),c[2]=d,c[1]=27,Y;if(17===d)return c[2]=null,c[1]=18,Y;if(3===d)return d=c[10],e=d.D,d=d.o&64||e,c[1]=u(d)?6:7,Y;if(12===d)return c[11]=c[2],c[2]=null,c[1]=13,Y;if(2===d)return d=c[2],e=tb(null==d),c[10]=d,c[1]=e?3:4,Y;if(23===d)return c[2]=!1,c[1]=24,Y;if(19===d)return d=c[8],e=d.D,d=d.o&64||e,c[1]=u(d)?22:23,Y;if(11===d){var f=c[2],d=I.h(f,Bk),e=I.h(f,Kk),m=I.h(f,to),f=I.h(f,qo),e=KA(e,m,b);c[7]=e;c[12]=d;return Rr(c,
-12,e,f)}return 9===d?(d=c[10],d=A.h(P,d),c[2]=d,c[1]=11,Y):5===d?(d=c[2],c[1]=u(d)?9:10,Y):14===d?(d=c[2],Sr(c,d)):26===d?(d=c[8],c[2]=d,c[1]=27,Y):16===d?(d=c[8],d=tb(null==d),c[1]=d?19:20,Y):10===d?(d=c[10],c[2]=d,c[1]=11,Y):18===d?(d=c[2],c[2]=d,c[1]=14,Y):8===d?(d=c[2],c[2]=d,c[1]=5,Y):null}}(c),c)}(),f=function(){var a=e.A?e.A():e.call(null);a[6]=c;return a}();return Pr(f)}}(c))}
-function PA(a,b){var c=new EventSource(a),d=X.j?X.j(null):X.call(null,null);fs(b,new V(null,2,5,W,[Hm,!0],null));c.onopen=function(a,c){return function(){var a;a=Me.j(NA);a=ds(1E4,a);Ef.h?Ef.h(c,a):Ef.call(null,c,a);OA(a,b);fs(b,new V(null,2,5,W,[ln,!0],null));return fs(b,new V(null,2,5,W,[Hm,!1],null))}}(c,d);c.onerror=function(a,c){return function(){var a=Q.j?Q.j(c):Q.call(null,c);cr(a);Ef.h?Ef.h(c,null):Ef.call(null,c,null);return fs(b,new V(null,2,5,W,[Hm,!0],null))}}(c,d);return c.onmessage=
-function(a,b){return function(a){var c=Q.j?Q.j(b):Q.call(null,b);return u(c)?fs(c,a.data):null}}(c,d)}function QA(a,b,c,d,e,f,g){this.S=a;this.url=b;this.R=c;this.sb=d;this.Y=e;this.O=f;this.H=g;this.o=2229667594;this.M=8192}h=QA.prototype;h.Bd=function(){return u(this.R)?hA(this):null};h.Dd=function(){if(u(Q.j?Q.j(this.sb):Q.call(null,this.sb)))return null;Ef.h?Ef.h(this.sb,!0):Ef.call(null,this.sb,!0);return PA(this.url,this.S)};h.Ed=function(){return null};h.Fd=function(){return hA(this)};
-h.Cd=function(){return null};h.Ad=function(){return null};h.X=function(a,b){return Ub.l(this,b,null)};h.P=function(a,b,c){switch(b instanceof v?b.ab:null){case "events-ch":return this.S;case "url":return this.url;case "auto-play?":return this.R;case "started?":return this.sb;default:return I.l(this.O,b,c)}};
-h.T=function(a,b,c){return ug(b,function(){return function(a){return ug(b,vg,""," ","",c,a)}}(this),"#asciinema.player.source.StreamSource{",", ","}",c,gf.h(new V(null,4,5,W,[new V(null,2,5,W,[wk,this.S],null),new V(null,2,5,W,[en,this.url],null),new V(null,2,5,W,[Jm,this.R],null),new V(null,2,5,W,[um,this.sb],null)],null),this.O))};h.qb=function(){return new Lg(0,this,4,new V(null,4,5,W,[wk,en,Jm,um],null),Vc(this.O))};h.Z=function(){return this.Y};
-h.Za=function(){return new QA(this.S,this.url,this.R,this.sb,this.Y,this.O,this.H)};h.ia=function(){return 4+R(this.O)};h.W=function(){var a=this.H;return null!=a?a:this.H=a=Qe(this)};h.L=function(a,b){var c;c=u(b)?(c=this.constructor===b.constructor)?Kg(this,b):c:b;return u(c)?!0:!1};h.jc=function(a,b){return ve(new Vh(null,new r(null,4,[wk,null,um,null,Jm,null,en,null],null),null),b)?be.h(Bd(Yf.h(rf,this),this.Y),b):new QA(this.S,this.url,this.R,this.sb,this.Y,lf(be.h(this.O,b)),null)};
-h.Gb=function(a,b,c){return u(U.h?U.h(wk,b):U.call(null,wk,b))?new QA(c,this.url,this.R,this.sb,this.Y,this.O,null):u(U.h?U.h(en,b):U.call(null,en,b))?new QA(this.S,c,this.R,this.sb,this.Y,this.O,null):u(U.h?U.h(Jm,b):U.call(null,Jm,b))?new QA(this.S,this.url,c,this.sb,this.Y,this.O,null):u(U.h?U.h(um,b):U.call(null,um,b))?new QA(this.S,this.url,this.R,c,this.Y,this.O,null):new QA(this.S,this.url,this.R,this.sb,this.Y,T.l(this.O,b,c),null)};
-h.fa=function(){return K(gf.h(new V(null,4,5,W,[new V(null,2,5,W,[wk,this.S],null),new V(null,2,5,W,[en,this.url],null),new V(null,2,5,W,[Jm,this.R],null),new V(null,2,5,W,[um,this.sb],null)],null),this.O))};h.ba=function(a,b){return new QA(this.S,this.url,this.R,this.sb,b,this.O,this.H)};h.ha=function(a,b){return le(b)?Wb(this,Lb.h(b,0),Lb.h(b,1)):Ab.l(Jb,this,b)};Zi(mA,jm,function(a,b,c,d,e,f,g,k){a=X.j?X.j(!1):X.call(null,!1);return new QA(b,c,k,a,null,null,null)});function RA(a){var b;b=new V(null,5,5,W,["fullscreenElement","mozFullScreenElement","webkitFullscreenElement","webkitCurrentFullScreenElement","msFullscreenElement"],null);b=uf(xf.h(te,Wq),b);u(b)?(a=uf(Wq,new V(null,5,5,W,["exitFullscreen","webkitExitFullscreen","webkitCancelFullScreen","mozCancelFullScreen","msExitFullscreen"],null)),a=u(a)?a.call(document):null):(b=new V(null,5,5,W,["requestFullscreen","webkitRequestFullscreen","webkitRequestFullScreen","mozRequestFullScreen","msRequestFullscreen"],
-null),b=uf(yf.h(zb,a),b),a=u(b)?b.call(a):null);return a};var SA=Ki(function(a,b){var c=null!=a&&(a.o&64||a.D)?A.h(P,a):a,d=I.h(c,rk),e=I.h(c,Xn),f=I.h(c,lj),g=I.h(c,Dj),k=I.h(c,Vl),l=I.h(c,qk),c=I.h(c,Tk),d=u(u(d)?u(f)?8>d:f:d)?d+8:d,e=u(u(e)?u(g)?8>e:g:e)?e+8:e,g=u(u(c)?b:c)?tb(l):l,l=u(g)?u(e)?e:"bg":d,d=u(g)?u(d)?d:"fg":e,l=u(l)?[y("fg-"),y(l)].join(""):null,d=u(d)?[y("bg-"),y(d)].join(""):null;return zo(" ",Xf(new V(null,5,5,W,[l,d,u(f)?"bright":null,u(k)?"underline":null,u(c)?"cursor":null],null)))}),TA=Ki(function(a,b){var c=S(a,0,null),d=S(a,1,null);
-return new V(null,3,5,W,[uo,new r(null,1,[xn,SA.h?SA.h(d,b):SA.call(null,d,b)],null),c],null)});function UA(a,b){return new V(null,2,5,W,[Nm,Af(function(a,d){return Bd(new V(null,3,5,W,[TA,d,b],null),new r(null,1,[Pj,a],null))},a)],null)}function VA(a,b){var c=S(a,0,null),d=S(a,1,null),e=Lf(b,c),e=K(e)?new V(null,2,5,W,[A.h(y,e),d],null):null,f=T.l(d,Tk,!0),f=new V(null,2,5,W,[Zd(c,b),f],null),c=Of(b+1,c),d=K(c)?new V(null,2,5,W,[A.h(y,c),d],null):null;return Xf(new V(null,3,5,W,[e,f,d],null))}
-var zB=new Vh(null,new r(null,3,["small",null,"medium",null,"big",null],null),null);
-function AB(a,b,c,d){var e=So(function(){var a=Q.j?Q.j(c):Q.call(null,c);return u(zB.j?zB.j(a):zB.call(null,a))?[y("font-"),y(a)].join(""):null}),f=So(function(){return function(){var d=Q.j?Q.j(a):Q.call(null,a),e=Q.j?Q.j(b):Q.call(null,b),f=Q.j?Q.j(c):Q.call(null,c),f=u(zB.j?zB.j(f):zB.call(null,f))?null:new r(null,1,[Wj,f],null);return Ph.w(J([new r(null,2,[Kk,[y(d),y("ch")].join(""),to,[y(1.3333333333*e),y("em")].join("")],null),f],0))}}(e)),g=So(function(){return function(){return Tk.j(Q.j?Q.j(d):
-Q.call(null,d))}}(e,f)),k=So(function(){return function(){return Nk.j(Q.j?Q.j(d):Q.call(null,d))}}(e,f,g));return function(a,b,c,d){return function(){var e=Q.j?Q.j(c):Q.call(null,c),f=null!=e&&(e.o&64||e.D)?A.h(P,e):e,g=I.h(f,En),k=I.h(f,bj),F=I.h(f,Sn),M=I.h(f,wn);return new V(null,3,5,W,[Gm,new r(null,2,[xn,Q.j?Q.j(a):Q.call(null,a),hm,Q.j?Q.j(b):Q.call(null,b)],null),Af(function(a,b,c,d,e,f){return function(a,b){var g=u(u(e)?H.h(a,d):e)?c:null,k;if(u(g))a:{k=Wd;for(var l=b;;)if(K(l)){var m=C(l),
-n=S(m,0,null);S(m,1,null);n=R(n);if(n<=g)k=Vd.h(k,m),l=N(l),g-=n;else{k=gf.w(k,VA(m,g),J([N(l)],0));break a}}else break a}else k=b;return Bd(new V(null,3,5,W,[UA,k,f],null),new r(null,1,[Pj,a],null))}}(e,f,g,k,F,M,a,b,c,d),Q.j?Q.j(d):Q.call(null,d))],null)}}(e,f,g,k)}
-function BB(){return new V(null,2,5,W,[cn,new r(null,5,[Rn,"1.1",dl,"http://www.w3.org/2000/svg",gl,"0 0 866.0254037844387 866.0254037844387",xn,"icon",so,new r(null,1,[Fn,'\x3cdefs\x3e \x3cmask id\x3d"small-triangle-mask"\x3e \x3crect width\x3d"100%" height\x3d"100%" fill\x3d"white"/\x3e \x3cpolygon points\x3d"508.01270189221935 433.01270189221935, 208.0127018922194 259.8076211353316, 208.01270189221927 606.217782649107" fill\x3d"black"\x3e\x3c/polygon\x3e \x3c/mask\x3e \x3c/defs\x3e \x3cpolygon points\x3d"808.0127018922194 433.01270189221935, 58.01270189221947 -1.1368683772161603e-13, 58.01270189221913 866.0254037844386" mask\x3d"url(#small-triangle-mask)" fill\x3d"white"\x3e\x3c/polygon\x3e \x3cpolyline points\x3d"481.2177826491071 333.0127018922194, 134.80762113533166 533.0127018922194" stroke\x3d"white" stroke-width\x3d"90"\x3e\x3c/polyline\x3e'],null)],
-null)],null)}function CB(){return new V(null,3,5,W,[cn,new r(null,4,[Rn,"1.1",dl,"http://www.w3.org/2000/svg",gl,"0 0 12 12",xn,"icon"],null),new V(null,2,5,W,[gj,new r(null,1,[rn,"M1,0 L11,6 L1,12 Z"],null)],null)],null)}
-function DB(){return new V(null,4,5,W,[cn,new r(null,4,[Rn,"1.1",dl,"http://www.w3.org/2000/svg",gl,"0 0 12 12",xn,"icon"],null),new V(null,2,5,W,[gj,new r(null,1,[rn,"M1,0 L4,0 L4,12 L1,12 Z"],null)],null),new V(null,2,5,W,[gj,new r(null,1,[rn,"M8,0 L11,0 L11,12 L8,12 Z"],null)],null)],null)}
-function EB(){return new V(null,4,5,W,[cn,new r(null,4,[Rn,"1.1",dl,"http://www.w3.org/2000/svg",gl,"0 0 12 12",xn,"icon"],null),new V(null,2,5,W,[gj,new r(null,1,[rn,"M12,0 L7,0 L9,2 L7,4 L8,5 L10,3 L12,5 Z"],null)],null),new V(null,2,5,W,[gj,new r(null,1,[rn,"M0,12 L0,7 L2,9 L4,7 L5,8 L3,10 L5,12 Z"],null)],null)],null)}
-function FB(){return new V(null,4,5,W,[cn,new r(null,4,[Rn,"1.1",dl,"http://www.w3.org/2000/svg",gl,"0 0 12 12",xn,"icon"],null),new V(null,2,5,W,[gj,new r(null,1,[rn,"M7,5 L7,0 L9,2 L11,0 L12,1 L10,3 L12,5 Z"],null)],null),new V(null,2,5,W,[gj,new r(null,1,[rn,"M5,7 L0,7 L2,9 L0,11 L1,12 L3,10 L5,12 Z"],null)],null)],null)}
-function GB(a,b){function c(a){a.preventDefault();a=new V(null,1,5,W,[Cn],null);return b.j?b.j(a):b.call(null,a)}return function(){return new V(null,3,5,W,[Hk,new r(null,1,[Ul,c],null),new V(null,1,5,W,[u(Q.j?Q.j(a):Q.call(null,a))?DB:CB],null)],null)}}function HB(a){return 10>a?[y("0"),y(a)].join(""):a}function IB(a){var b=Math.floor(Ie(a,60));return[y(HB(Math.floor(a/60))),y(":"),y(HB(b))].join("")}
-function JB(a,b){var c=W,d=W,e;e=Q.j?Q.j(a):Q.call(null,a);e=IB(e);d=new V(null,2,5,d,[Ak,e],null);e=W;var f;f=Q.j?Q.j(a):Q.call(null,a);var g=Q.j?Q.j(b):Q.call(null,b);f=[y("-"),y(IB(g-f))].join("");return new V(null,3,5,c,[Ml,d,new V(null,2,5,e,[io,f],null)],null)}
-function KB(){function a(a){a.preventDefault();return RA(a.currentTarget.parentNode.parentNode.parentNode)}return function(){return new V(null,4,5,W,[vn,new r(null,1,[Ul,a],null),new V(null,1,5,W,[EB],null),new V(null,1,5,W,[FB],null)],null)}}
-function LB(a,b){function c(a){a.preventDefault();var c=a.currentTarget.offsetWidth,d=a.currentTarget.getBoundingClientRect();a=new V(null,2,5,W,[Pn,Tq(a.clientX-d.left,0,c)/c],null);return b.j?b.j(a):b.call(null,a)}var d=So(function(){return function(){return[y(100*(Q.j?Q.j(a):Q.call(null,a))),y("%")].join("")}}(c));return function(a,b){return function(){return new V(null,2,5,W,[xj,new V(null,3,5,W,[cl,new r(null,1,[Tl,a],null),new V(null,2,5,W,[dj,new V(null,2,5,W,[uo,new r(null,1,[hm,new r(null,
-1,[Kk,Q.j?Q.j(b):Q.call(null,b)],null)],null)],null)],null)],null)],null)}}(c,d)}function MB(a,b,c,d){return function(e){return function(){return new V(null,5,5,W,[nk,new V(null,3,5,W,[GB,a,d],null),new V(null,3,5,W,[JB,b,c],null),new V(null,1,5,W,[KB],null),new V(null,3,5,W,[LB,e,d],null)],null)}}(So(function(){return(Q.j?Q.j(b):Q.call(null,b))/(Q.j?Q.j(c):Q.call(null,c))}))}
-function NB(a){function b(b){b.preventDefault();b=new V(null,1,5,W,[Cn],null);return a.j?a.j(b):a.call(null,b)}return function(){return new V(null,3,5,W,[Sk,new r(null,1,[Ul,b],null),new V(null,2,5,W,[yk,new V(null,2,5,W,[om,new V(null,2,5,W,[uo,new V(null,1,5,W,[BB],null)],null)],null)],null)],null)}}function OB(){return new V(null,2,5,W,[ek,new V(null,1,5,W,[zn],null)],null)}
-function PB(a,b,c){b=b.j?b.j(c):b.call(null,c);if(u(b)){var d=S(b,0,null);Le(b,1);c.preventDefault();return H.h(d,$l)?RA(c.currentTarget):a.j?a.j(b):a.call(null,b)}return null}
-function QB(a){switch(a.key){case " ":return new V(null,1,5,W,[Cn],null);case "f":return new V(null,1,5,W,[$l],null);case "0":return new V(null,2,5,W,[Pn,0],null);case "1":return new V(null,2,5,W,[Pn,.1],null);case "2":return new V(null,2,5,W,[Pn,.2],null);case "3":return new V(null,2,5,W,[Pn,.3],null);case "4":return new V(null,2,5,W,[Pn,.4],null);case "5":return new V(null,2,5,W,[Pn,.5],null);case "6":return new V(null,2,5,W,[Pn,.6],null);case "7":return new V(null,2,5,W,[Pn,.7],null);case "8":return new V(null,
-2,5,W,[Pn,.8],null);case "9":return new V(null,2,5,W,[Pn,.9],null);case "\x3e":return new V(null,1,5,W,[po],null);case "\x3c":return new V(null,1,5,W,[mk],null);default:return null}}function RB(a){switch(a.which){case 37:return new V(null,1,5,W,[Jl],null);case 39:return new V(null,1,5,W,[Cj],null);default:return null}}
-function SB(a,b,c,d){a=u(a)?[y('"'),y(a),y('"')].join(""):"untitled";return new V(null,4,5,W,[Ik,u(d)?new V(null,2,5,W,[oo,new r(null,1,[al,d],null)],null):null,a,u(b)?new V(null,3,5,W,[uo," by ",u(c)?new V(null,3,5,W,[ro,new r(null,1,[mo,c],null),b],null):b],null):null],null)}
-function TB(a,b){var c=yf.l(PB,b,QB),d=yf.l(PB,b,RB),e=function(){return function(){var a=new V(null,1,5,W,[Dn],null);return b.j?b.j(a):b.call(null,a)}}(c,d),f=So(function(){return function(){return u(gk.j(Q.j?Q.j(a):Q.call(null,a)))?"hud":null}}(c,d,e)),g=So(function(){return function(){var b=im.j(Q.j?Q.j(a):Q.call(null,a));return[y("asciinema-theme-"),y(b)].join("")}}(c,d,e,f)),k=So(function(){return function(){var b=Kk.j(Q.j?Q.j(a):Q.call(null,a));return u(b)?b:80}}(c,d,e,f,g)),l=So(function(){return function(){var b=
-to.j(Q.j?Q.j(a):Q.call(null,a));return u(b)?b:24}}(c,d,e,f,g,k)),n=So(function(){return function(){return Wj.j(Q.j?Q.j(a):Q.call(null,a))}}(c,d,e,f,g,k,l)),m=So(function(){return function(){return Rh(Q.j?Q.j(a):Q.call(null,a),new V(null,2,5,W,[Nk,Tk],null))}}(c,d,e,f,g,k,l,n)),t=So(function(){return function(){return ln.j(Q.j?Q.j(a):Q.call(null,a))}}(c,d,e,f,g,k,l,n,m)),q=So(function(){return function(){return wj.j(Q.j?Q.j(a):Q.call(null,a))}}(c,d,e,f,g,k,l,n,m,t)),z=So(function(){return function(){return $k.j(Q.j?
-Q.j(a):Q.call(null,a))}}(c,d,e,f,g,k,l,n,m,t,q)),w=So(function(){return function(){return Hm.j(Q.j?Q.j(a):Q.call(null,a))}}(c,d,e,f,g,k,l,n,m,t,q,z)),E=So(function(){return function(){return Jk.j(Q.j?Q.j(a):Q.call(null,a))}}(c,d,e,f,g,k,l,n,m,t,q,z,w)),F=Q.j?Q.j(a):Q.call(null,a),M=null!=F&&(F.o&64||F.D)?A.h(P,F):F,O=I.h(M,Xl),Z=I.h(M,mm),ba=I.h(M,Ym),Ca=I.h(M,dm);return function(a,c,d,e,f,g,k,l,m,n,q,t,w,z,E,F,M,O,Z,ba){return function(){var E=W,F=new r(null,5,[kj,-1,Bj,a,Vn,c,$m,d,xn,Q.j?Q.j(e):
-Q.call(null,e)],null),Ca=W,Fb=new r(null,1,[xn,Q.j?Q.j(f):Q.call(null,f)],null),Ra=new V(null,5,5,W,[AB,g,k,l,m],null),Ob=new V(null,5,5,W,[MB,n,q,t,b],null),To=u(u(M)?M:O)?new V(null,5,5,W,[SB,M,O,Z,ba],null):null,qg;qg=Q.j?Q.j(w):Q.call(null,w);qg=u(qg)?qg:Q.j?Q.j(z):Q.call(null,z);return new V(null,3,5,E,[In,F,new V(null,7,5,Ca,[Vm,Fb,Ra,Ob,To,u(qg)?null:new V(null,2,5,W,[NB,b],null),u(Q.j?Q.j(w):Q.call(null,w))?new V(null,1,5,W,[OB],null):null],null)],null)}}(c,d,e,f,g,k,l,n,m,t,q,z,w,E,F,M,O,
-Z,ba,Ca)};function UB(a){if("number"===typeof a)return a;a=Me.h(parseFloat,Ao(a,/:/));a=Me.l(Ee,Ue(a),Uf(yf.h(Ee,60),1));return A.h(De,a)}
-function VB(a,b){var c=null!=b&&(b.o&64||b.D)?A.h(P,b):b,d=I.h(c,to),e=I.l(c,Wj,"small"),f=I.l(c,ak,1),g=I.h(c,hk),k=I.h(c,Kk),l=I.l(c,Vk,Qj),n=I.h(c,cm),m=I.l(c,im,"asciinema"),t=I.h(c,Cm),q=I.h(c,xm),z=I.h(c,Fm),g=UB(u(g)?g:0),w=cs(null),E=u(k)?k:80,F=u(d)?d:24,l=mA.Ca?mA.Ca(l,w,a,E,F,g,f,z,n,q):mA.call(null,l,w,a,E,F,g,f,z,n,q);return Ph.w(J([ae([wj,Wj,ak,gk,wk,Jk,Kk,Nk,Tk,$k,hl,Rl,im,Hm,ln,to],[g,e,f,!1,w,!1,k,function(){var a;"string"===typeof t?u(H.h(t.indexOf("data:application/json;base64,"),
-0))?(a=t.substring(29).replace(RegExp("\\s","g"),""),a=JSON.parse(atob(a)),a=Ii(a,J([Ji,!0],0))):u(H.h(t.indexOf("data:text/plain,"),0))?(a=t.substring(16),a=Nk.j(eA(zy(E,F),a)),a=Me.h(fA,a)):a=null:a=t;return u(a)?a:Wd}(),new r(null,1,[Sn,!1],null),null,l,null,m,!1,!1,d]),Rh(c,new V(null,4,5,W,[Xl,mm,Ym,dm],null))],0))}
-var XB=function WB(b){return new Ze(null,function(){var c=K(b);return c?gf.h(c,WB(c)):null},null,null)}(new V(null,2,5,W,[new V(null,2,5,W,[.5,!1],null),new V(null,2,5,W,[.5,!0],null)],null));function YB(a){a=null!=a&&(a.o&64||a.D)?A.h(P,a):a;var b=I.h(a,wk),c=cs(null);FA(Dj,XB,Be,b,c);return T.l(bg(a,new V(null,2,5,W,[Tk,wn],null),!0),Rl,c)}function ZB(a){a=null!=a&&(a.o&64||a.D)?A.h(P,a):a;var b=I.h(a,Rl);cr(b);return T.l(bg(a,new V(null,2,5,W,[Tk,wn],null),!0),Rl,null)}
-function $B(a,b){var c=null!=b&&(b.o&64||b.D)?A.h(P,b):b;I.h(c,ln);var d=I.h(c,ak),e=I.h(c,hl),d=a.j?a.j(d):a.call(null,d);lA(e,d);return T.l(c,ak,d)}
-var aC=ae([Cj,Dj,Jj,mk,Bk,$k,Jl,Wl,Hm,ln,Cn,Pn,po],[function(a){a=null!=a&&(a.o&64||a.D)?A.h(P,a):a;var b=I.h(a,wj),c=I.h(a,$k),d=I.h(a,hl);u(c)&&kA(d,Tq(b+5,0,c));return a},function(a,b){var c=S(b,0,null);return bg(a,new V(null,2,5,W,[Tk,wn],null),c)},function(a,b){var c=S(b,0,null),c=Q.j?Q.j(c):Q.call(null,c),d=null!=c&&(c.o&64||c.D)?A.h(P,c):c,c=I.h(d,Nk),d=I.h(d,Tk),c=cg.G(T.l(a,Nk,c),new V(null,1,5,W,[Tk],null),Ph,d),c=null!=c&&(c.o&64||c.D)?A.h(P,c):c,d=I.h(c,Rl);return u(d)?YB(ZB(c)):c},yf.h($B,
-function(a){return a/2}),function(a,b){var c=S(b,0,null);return T.l(a,wj,c)},function(a,b){var c=S(b,0,null);return T.l(a,$k,c)},function(a){a=null!=a&&(a.o&64||a.D)?A.h(P,a):a;var b=I.h(a,wj),c=I.h(a,$k),d=I.h(a,hl);u(c)&&kA(d,Tq(b+-5,0,c));return a},function(a,b){var c=null!=a&&(a.o&64||a.D)?A.h(P,a):a,d=I.h(c,Kk),e=I.h(c,to),f=S(b,0,null),g=S(b,1,null);return T.l(T.l(c,Kk,u(d)?d:f),to,u(e)?e:g)},function(a,b){var c=S(b,0,null);return T.l(a,Hm,c)},function(a,b){var c=S(b,0,null),d=T.w(a,ln,c,J([Jk,
-!0],0));return u(c)?YB(d):ZB(d)},function(a){a=null!=a&&(a.o&64||a.D)?A.h(P,a):a;var b=I.h(a,hl);jA(b);return a},function(a,b){var c=null!=a&&(a.o&64||a.D)?A.h(P,a):a,d=I.h(c,$k),e=I.h(c,hl),f=S(b,0,null);u(d)&&kA(e,f*d);return c},yf.h($B,function(a){return 2*a})]);function bC(a,b){var c=S(b,0,null),d=Le(b,1),e=I.h(aC,c);if(u(e))return e.h?e.h(a,d):e.call(null,a,d);ui.w(J(["unhandled event:",c],0));return a}
-function cC(a){var b=cs(null),c=cs(1);Cr(function(b,c){return function(){var f=function(){return function(a){return function(){function b(c){for(;;){var d;a:try{for(;;){var e=a(c);if(!U(e,Y)){d=e;break a}}}catch(f){if(f instanceof Object)c[5]=f,Tr(c),d=Y;else throw f;}if(!U(d,Y))return d}}function c(){var a=[null,null,null,null,null,null,null,null,null,null,null,null];a[0]=d;a[1]=1;return a}var d=null,d=function(a){switch(arguments.length){case 0:return c.call(this);case 1:return b.call(this,a)}throw Error("Invalid arity: "+
-arguments.length);};d.A=c;d.j=b;return d}()}(function(b,c){return function(b){var d=b[1];if(7===d)return b[7]=b[2],Rr(b,12,c,!1);if(1===d)return b[2]=null,b[1]=2,Y;if(4===d)return b[8]=b[2],Rr(b,5,c,!0);if(6===d)return d=$r(3E3),d=new V(null,2,5,W,[a,d],null),ls(b,8,d);if(3===d)return d=b[2],Sr(b,d);if(12===d)return b[9]=b[2],b[2]=null,b[1]=2,Y;if(2===d)return Qr(b,4,a);if(11===d)return d=b[2],b[2]=d,b[1]=7,Y;if(9===d)return b[2]=null,b[1]=6,Y;if(5===d)return b[10]=b[2],b[2]=null,b[1]=6,Y;if(10===
-d)return b[2]=null,b[1]=11,Y;if(8===d){var e=b[2],d=S(e,0,null),e=S(e,1,null),e=H.h(e,a);b[11]=d;b[1]=e?9:10;return Y}return null}}(b,c),b,c)}(),g=function(){var a=f.A?f.A():f.call(null);a[6]=b;return a}();return Pr(g)}}(c,b));return b}
-function dC(a){var b=wk.j(Q.j?Q.j(a):Q.call(null,a)),c=cs(new pr(mr(1),1)),d=cs(new or(mr(1),1)),e=cC(d),f=cs(1E3),g=Ro.j(null),k=cs(1);Cr(function(b,c,d,e,f,g,k){return function(){var E=function(){return function(a){return function(){function b(c){for(;;){var d;a:try{for(;;){var e=a(c);if(!U(e,Y)){d=e;break a}}}catch(f){if(f instanceof Object)c[5]=f,Tr(c),d=Y;else throw f;}if(!U(d,Y))return d}}function c(){var a=[null,null,null,null,null,null,null,null,null,null,null,null,null,null];a[0]=d;a[1]=
-1;return a}var d=null,d=function(a){switch(arguments.length){case 0:return c.call(this);case 1:return b.call(this,a)}throw Error("Invalid arity: "+arguments.length);};d.A=c;d.j=b;return d}()}(function(b,c,d,e,f,g,k){return function(l){var m=l[1];if(7===m)return l[7]=l[2],l[2]=null,l[1]=2,Y;if(20===m){var n=l[2];l[2]=n;l[1]=16;return Y}if(1===m)return l[2]=null,l[1]=2,Y;if(4===m){var q=l[8],t=l[9],w=l[10],n=l[2],z=S(n,0,null),E=S(z,0,null),F=Le(z,1),$a=S(n,1,null),kb=H.h($a,c);l[8]=n;l[9]=$a;l[11]=
-F;l[12]=E;l[10]=z;l[1]=kb?5:6;return Y}if(15===m)return E=l[12],n=H.h(Dn,E),l[1]=n?18:19,Y;if(21===m)return n=l[2],l[2]=n,l[1]=20,Y;if(13===m)return n=l[2],l[2]=n,l[1]=10,Y;if(22===m)return n=l[2],l[2]=n,l[1]=20,Y;if(6===m)return w=l[10],n=If.l(a,bC,w),l[2]=n,l[1]=7,Y;if(17===m)return n=l[2],l[2]=n,l[1]=16,Y;if(3===m)return n=l[2],Sr(l,n);if(12===m){var q=l[8],t=l[9],F=l[11],E=l[12],w=l[10],sb=Ef.h?Ef.h(k,w):Ef.call(null,k,w),n=function(){return function(a,b,c,d,e,f,g,k,l,m,n,q,t,w,z,E,F,L,M,O,Z,
-ba){return function(){fs(Z,Q.j?Q.j(ba):Q.call(null,ba));return Ef.h?Ef.h(ba,null):Ef.call(null,ba,null)}}(q,w,E,F,w,t,H,E,q,t,F,E,w,sb,m,b,c,d,e,f,g,k)}(),n=Xq.j?Xq.j(n):Xq.call(null,n);l[13]=sb;l[2]=n;l[1]=13;return Y}return 2===m?(n=new V(null,3,5,W,[c,g,d],null),ls(l,4,n)):19===m?(w=l[10],Rr(l,22,g,w)):11===m?(w=l[10],n=Ef.h?Ef.h(k,w):Ef.call(null,k,w),l[2]=n,l[1]=13,Y):9===m?(E=l[12],n=H.h(Bk,E),l[1]=n?14:15,Y):5===m?(E=l[12],n=H.h(Jj,E),l[1]=n?8:9,Y):14===m?(w=l[10],Rr(l,17,d,w)):16===m?(n=l[2],
-l[2]=n,l[1]=10,Y):10===m?(n=l[2],l[2]=n,l[1]=7,Y):18===m?Rr(l,21,e,!0):8===m?(n=Q.j?Q.j(k):Q.call(null,k),l[1]=u(n)?11:12,Y):null}}(b,c,d,e,f,g,k),b,c,d,e,f,g,k)}(),F=function(){var a=E.A?E.A():E.call(null);a[6]=b;return a}();return Pr(F)}}(k,b,c,d,e,f,g));k=cs(1);Cr(function(b,c,d,e,f,g,k){return function(){var E=function(){return function(a){return function(){function b(c){for(;;){var d;a:try{for(;;){var e=a(c);if(!U(e,Y)){d=e;break a}}}catch(f){if(f instanceof Object)c[5]=f,Tr(c),d=Y;else throw f;
-}if(!U(d,Y))return d}}function c(){var a=[null,null,null,null,null,null,null,null,null];a[0]=d;a[1]=1;return a}var d=null,d=function(a){switch(arguments.length){case 0:return c.call(this);case 1:return b.call(this,a)}throw Error("Invalid arity: "+arguments.length);};d.A=c;d.j=b;return d}()}(function(b,c,d,e,f){return function(b){var c=b[1];return 1===c?(b[2]=null,b[1]=2,Y):2===c?Qr(b,4,f):3===c?(c=b[2],Sr(b,c)):4===c?(c=b[7],c=b[2],b[7]=c,b[1]=u(null==c)?5:6,Y):5===c?(b[2]=null,b[1]=7,Y):6===c?(c=
-b[7],c=If.G(a,T,gk,c),b[8]=c,b[2]=null,b[1]=2,Y):7===c?(c=b[2],b[2]=c,b[1]=3,Y):null}}(b,c,d,e,f,g,k),b,c,d,e,f,g,k)}(),F=function(){var a=E.A?E.A():E.call(null);a[6]=b;return a}();return Pr(F)}}(k,b,c,d,e,f,g))}function eC(a,b){dC(a);var c=new V(null,3,5,W,[TB,a,function(b){var c=Q.j?Q.j(a):Q.call(null,a);fs(wk.j(c),b);return null}],null);Rq?Rq(c,b):Qq.call(null,c,b);return null}bb=!1;
-Za=function(){function a(a){var d=null;if(0<arguments.length){for(var d=0,e=Array(arguments.length-0);d<e.length;)e[d]=arguments[d+0],++d;d=new B(e,0)}return b.call(this,d)}function b(a){return console.log.apply(console,nb.j?nb.j(a):nb.call(null,a))}a.J=0;a.K=function(a){a=K(a);return b(a)};a.w=b;return a}();
-ab=function(){function a(a){var d=null;if(0<arguments.length){for(var d=0,e=Array(arguments.length-0);d<e.length;)e[d]=arguments[d+0],++d;d=new B(e,0)}return b.call(this,d)}function b(a){return console.error.apply(console,nb.j?nb.j(a):nb.call(null,a))}a.J=0;a.K=function(a){a=K(a);return b(a)};a.w=b;return a}();var fC=function fC(b){for(var c=[],d=arguments.length,e=0;;)if(e<d)c.push(arguments[e]),e+=1;else break;switch(c.length){case 2:return fC.h(arguments[0],arguments[1]);case 3:return fC.l(arguments[0],arguments[1],arguments[2]);default:throw Error([y("Invalid arity: "),y(c.length)].join(""));}};da("asciinema.player.js.CreatePlayer",fC);fC.h=function(a,b){return fC.l(a,b,rf)};
-fC.l=function(a,b,c){c=wo(Ii(c,J([Ji,!0],0)));a="string"===typeof a?document.getElementById(a):a;b=J([b,c],0);b=A.h(VB,b);b=Ro.j(b);gA(hl.j(Q.j?Q.j(b):Q.call(null,b)));return eC(b,a)};fC.J=3;
+var g,aa=aa||{},ba=this;function ca(a){return"string"==typeof a}function da(a,b){var c=a.split("."),d=ba;c[0]in d||!d.execScript||d.execScript("var "+c[0]);for(var e;c.length&&(e=c.shift());)c.length||void 0===b?d=d[e]&&d[e]!==Object.prototype[e]?d[e]:d[e]={}:d[e]=b}function ea(){}
+function n(a){var b=typeof a;if("object"==b)if(a){if(a instanceof Array)return"array";if(a instanceof Object)return b;var c=Object.prototype.toString.call(a);if("[object Window]"==c)return"object";if("[object Array]"==c||"number"==typeof a.length&&"undefined"!=typeof a.splice&&"undefined"!=typeof a.propertyIsEnumerable&&!a.propertyIsEnumerable("splice"))return"array";if("[object Function]"==c||"undefined"!=typeof a.call&&"undefined"!=typeof a.propertyIsEnumerable&&!a.propertyIsEnumerable("call"))return"function"}else return"null";
+else if("function"==b&&"undefined"==typeof a.call)return"object";return b}function fa(a){var b=n(a);return"array"==b||"object"==b&&"number"==typeof a.length}function ha(a){return"function"==n(a)}function ia(a){var b=typeof a;return"object"==b&&null!=a||"function"==b}function ja(a){return a[la]||(a[la]=++ma)}var la="closure_uid_"+(1E9*Math.random()>>>0),ma=0;function na(a,b,c){return a.call.apply(a.bind,arguments)}
+function oa(a,b,c){if(!a)throw Error();if(2<arguments.length){var d=Array.prototype.slice.call(arguments,2);return function(){var c=Array.prototype.slice.call(arguments);Array.prototype.unshift.apply(c,d);return a.apply(b,c)}}return function(){return a.apply(b,arguments)}}function pa(a,b,c){pa=Function.prototype.bind&&-1!=Function.prototype.bind.toString().indexOf("native code")?na:oa;return pa.apply(null,arguments)}
+function qa(a,b){function c(){}c.prototype=b.prototype;a.Zd=b.prototype;a.prototype=new c;a.prototype.constructor=a;a.base=function(a,c,f){for(var d=Array(arguments.length-2),e=2;e<arguments.length;e++)d[e-2]=arguments[e];return b.prototype[c].apply(a,d)}};var ra=String.prototype.trim?function(a){return a.trim()}:function(a){return a.replace(/^[\s\xa0]+|[\s\xa0]+$/g,"")},sa=String.prototype.repeat?function(a,b){return a.repeat(b)}:function(a,b){return Array(b+1).join(a)};function ta(a,b){return a<b?-1:a>b?1:0};var ua=Array.prototype.indexOf?function(a,b,c){return Array.prototype.indexOf.call(a,b,c)}:function(a,b,c){c=null==c?0:0>c?Math.max(0,a.length+c):c;if(ca(a))return ca(b)&&1==b.length?a.indexOf(b,c):-1;for(;c<a.length;c++)if(c in a&&a[c]===b)return c;return-1},va=Array.prototype.forEach?function(a,b,c){Array.prototype.forEach.call(a,b,c)}:function(a,b,c){for(var d=a.length,e=ca(a)?a.split(""):a,f=0;f<d;f++)f in e&&b.call(c,e[f],f,a)};
+function wa(a){a:{var b=xa;for(var c=a.length,d=ca(a)?a.split(""):a,e=0;e<c;e++)if(e in d&&b.call(void 0,d[e],e,a)){b=e;break a}b=-1}return 0>b?null:ca(a)?a.charAt(b):a[b]}function ya(a,b){var c=ua(a,b),d;(d=0<=c)&&Array.prototype.splice.call(a,c,1);return d}function za(a,b){a.sort(b||Aa)}function Ca(a,b){for(var c=Array(a.length),d=0;d<a.length;d++)c[d]={index:d,value:a[d]};var e=b||Aa;za(c,function(a,b){return e(a.value,b.value)||a.index-b.index});for(d=0;d<a.length;d++)a[d]=c[d].value}
+function Aa(a,b){return a>b?1:a<b?-1:0};function Da(a){var b=[],c=0,d;for(d in a)b[c++]=a[d];return b}function Ea(a){var b=[],c=0,d;for(d in a)b[c++]=d;return b}var Fa="constructor hasOwnProperty isPrototypeOf propertyIsEnumerable toLocaleString toString valueOf".split(" ");function Ia(a,b){for(var c,d,e=1;e<arguments.length;e++){d=arguments[e];for(c in d)a[c]=d[c];for(var f=0;f<Fa.length;f++)c=Fa[f],Object.prototype.hasOwnProperty.call(d,c)&&(a[c]=d[c])}};function Ka(a){if(a.Yc&&"function"==typeof a.Yc)return a.Yc();if(ca(a))return a.split("");if(fa(a)){for(var b=[],c=a.length,d=0;d<c;d++)b.push(a[d]);return b}return Da(a)}
+function La(a,b){if(a.forEach&&"function"==typeof a.forEach)a.forEach(b,void 0);else if(fa(a)||ca(a))va(a,b,void 0);else{if(a.Xc&&"function"==typeof a.Xc)var c=a.Xc();else if(a.Yc&&"function"==typeof a.Yc)c=void 0;else if(fa(a)||ca(a)){c=[];for(var d=a.length,e=0;e<d;e++)c.push(e)}else c=Ea(a);d=Ka(a);e=d.length;for(var f=0;f<e;f++)b.call(void 0,d[f],c&&c[f],a)}};function Ma(a,b){this.ic={};this.ib=[];this.Fc=0;var c=arguments.length;if(1<c){if(c%2)throw Error("Uneven number of arguments");for(var d=0;d<c;d+=2)this.set(arguments[d],arguments[d+1])}else a&&this.addAll(a)}g=Ma.prototype;g.Yc=function(){Na(this);for(var a=[],b=0;b<this.ib.length;b++)a.push(this.ic[this.ib[b]]);return a};g.Xc=function(){Na(this);return this.ib.concat()};g.Td=function(){return 0==this.Fc};g.clear=function(){this.ic={};this.Fc=this.ib.length=0};
+g.remove=function(a){return Object.prototype.hasOwnProperty.call(this.ic,a)?(delete this.ic[a],this.Fc--,this.ib.length>2*this.Fc&&Na(this),!0):!1};function Na(a){if(a.Fc!=a.ib.length){for(var b=0,c=0;b<a.ib.length;){var d=a.ib[b];Object.prototype.hasOwnProperty.call(a.ic,d)&&(a.ib[c++]=d);b++}a.ib.length=c}if(a.Fc!=a.ib.length){var e={};for(c=b=0;b<a.ib.length;)d=a.ib[b],Object.prototype.hasOwnProperty.call(e,d)||(a.ib[c++]=d,e[d]=1),b++;a.ib.length=c}}
+g.get=function(a,b){return Object.prototype.hasOwnProperty.call(this.ic,a)?this.ic[a]:b};g.set=function(a,b){Object.prototype.hasOwnProperty.call(this.ic,a)||(this.Fc++,this.ib.push(a));this.ic[a]=b};g.addAll=function(a){if(a instanceof Ma){var b=a.Xc();a=a.Yc()}else b=Ea(a),a=Da(a);for(var c=0;c<b.length;c++)this.set(b[c],a[c])};g.forEach=function(a,b){for(var c=this.Xc(),d=0;d<c.length;d++){var e=c[d],f=this.get(e);a.call(b,f,e,this)}};g.clone=function(){return new Ma(this)};var Pa=/^(?:([^:/?#.]+):)?(?:\/\/(?:([^/?#]*)@)?([^/#?]*?)(?::([0-9]+))?(?=[/#?]|$))?([^?#]+)?(?:\?([^#]*))?(?:#([\s\S]*))?$/;function Qa(a,b){this.Ma=[];this.Lc=b;for(var c=!0,d=a.length-1;0<=d;d--){var e=a[d]|0;c&&e==b||(this.Ma[d]=e,c=!1)}}var Ra={};function Sa(a){if(-128<=a&&128>a){var b=Ra[a];if(b)return b}b=new Qa([a|0],0>a?-1:0);-128<=a&&128>a&&(Ra[a]=b);return b}function Ta(a){if(isNaN(a)||!isFinite(a))return Ua;if(0>a)return Ta(-a).kb();for(var b=[],c=1,d=0;a>=c;d++)b[d]=a/c|0,c*=Va;return new Qa(b,0)}var Va=4294967296,Ua=Sa(0),Wa=Sa(1),Xa=Sa(16777216);g=Qa.prototype;
+g.Of=function(){return 0<this.Ma.length?this.Ma[0]:this.Lc};g.vd=function(){if(this.Eb())return-this.kb().vd();for(var a=0,b=1,c=0;c<this.Ma.length;c++){var d=Ya(this,c);a+=(0<=d?d:Va+d)*b;b*=Va}return a};
+g.toString=function(a){a=a||10;if(2>a||36<a)throw Error("radix out of range: "+a);if(this.hc())return"0";if(this.Eb())return"-"+this.kb().toString(a);for(var b=Ta(Math.pow(a,6)),c=this,d="";;){var e=Za(c,b),f=(c.ze(e.multiply(b)).Of()>>>0).toString(a);c=e;if(c.hc())return f+d;for(;6>f.length;)f="0"+f;d=""+f+d}};function Ya(a,b){return 0>b?0:b<a.Ma.length?a.Ma[b]:a.Lc}g.hc=function(){if(0!=this.Lc)return!1;for(var a=0;a<this.Ma.length;a++)if(0!=this.Ma[a])return!1;return!0};
+g.Eb=function(){return-1==this.Lc};g.xf=function(a){return 0<this.compare(a)};g.yf=function(a){return 0<=this.compare(a)};g.Ue=function(){return 0>this.compare(Xa)};g.Ve=function(a){return 0>=this.compare(a)};g.compare=function(a){a=this.ze(a);return a.Eb()?-1:a.hc()?0:1};g.kb=function(){return this.Hf().add(Wa)};
+g.add=function(a){for(var b=Math.max(this.Ma.length,a.Ma.length),c=[],d=0,e=0;e<=b;e++){var f=d+(Ya(this,e)&65535)+(Ya(a,e)&65535),h=(f>>>16)+(Ya(this,e)>>>16)+(Ya(a,e)>>>16);d=h>>>16;f&=65535;h&=65535;c[e]=h<<16|f}return new Qa(c,c[c.length-1]&-2147483648?-1:0)};g.ze=function(a){return this.add(a.kb())};
+g.multiply=function(a){if(this.hc()||a.hc())return Ua;if(this.Eb())return a.Eb()?this.kb().multiply(a.kb()):this.kb().multiply(a).kb();if(a.Eb())return this.multiply(a.kb()).kb();if(this.Ue()&&a.Ue())return Ta(this.vd()*a.vd());for(var b=this.Ma.length+a.Ma.length,c=[],d=0;d<2*b;d++)c[d]=0;for(d=0;d<this.Ma.length;d++)for(var e=0;e<a.Ma.length;e++){var f=Ya(this,d)>>>16,h=Ya(this,d)&65535,k=Ya(a,e)>>>16,l=Ya(a,e)&65535;c[2*d+2*e]+=h*l;ab(c,2*d+2*e);c[2*d+2*e+1]+=f*l;ab(c,2*d+2*e+1);c[2*d+2*e+1]+=
+h*k;ab(c,2*d+2*e+1);c[2*d+2*e+2]+=f*k;ab(c,2*d+2*e+2)}for(d=0;d<b;d++)c[d]=c[2*d+1]<<16|c[2*d];for(d=b;d<2*b;d++)c[d]=0;return new Qa(c,0)};function ab(a,b){for(;(a[b]&65535)!=a[b];)a[b+1]+=a[b]>>>16,a[b]&=65535,b++}
+function Za(a,b){if(b.hc())throw Error("division by zero");if(a.hc())return Ua;if(a.Eb())return b.Eb()?Za(a.kb(),b.kb()):Za(a.kb(),b).kb();if(b.Eb())return Za(a,b.kb()).kb();if(30<a.Ma.length){if(a.Eb()||b.Eb())throw Error("slowDivide_ only works with positive integers.");for(var c=Wa,d=b;d.Ve(a);)c=c.shiftLeft(1),d=d.shiftLeft(1);var e=c.ad(1),f=d.ad(1);d=d.ad(2);for(c=c.ad(2);!d.hc();){var h=f.add(d);h.Ve(a)&&(e=e.add(c),f=h);d=d.ad(1);c=c.ad(1)}return e}c=Ua;for(d=a;d.yf(b);){e=Math.max(1,Math.floor(d.vd()/
+b.vd()));f=Math.ceil(Math.log(e)/Math.LN2);f=48>=f?1:Math.pow(2,f-48);h=Ta(e);for(var k=h.multiply(b);k.Eb()||k.xf(d);)e-=f,h=Ta(e),k=h.multiply(b);h.hc()&&(h=Wa);c=c.add(h);d=d.ze(k)}return c}g.Hf=function(){for(var a=this.Ma.length,b=[],c=0;c<a;c++)b[c]=~this.Ma[c];return new Qa(b,~this.Lc)};g.shiftLeft=function(a){var b=a>>5;a%=32;for(var c=this.Ma.length+b+(0<a?1:0),d=[],e=0;e<c;e++)d[e]=0<a?Ya(this,e-b)<<a|Ya(this,e-b-1)>>>32-a:Ya(this,e-b);return new Qa(d,this.Lc)};
+g.ad=function(a){var b=a>>5;a%=32;for(var c=this.Ma.length-b,d=[],e=0;e<c;e++)d[e]=0<a?Ya(this,e+b)>>>a|Ya(this,e+b+1)<<32-a:Ya(this,e+b);return new Qa(d,this.Lc)};function cb(a,b){null!=a&&this.append.apply(this,arguments)}g=cb.prototype;g.xc="";g.set=function(a){this.xc=""+a};g.append=function(a,b,c){this.xc+=String(a);if(null!=b)for(var d=1;d<arguments.length;d++)this.xc+=arguments[d];return this};g.clear=function(){this.xc=""};g.toString=function(){return this.xc};function eb(a){eb[" "](a);return a}eb[" "]=ea;function fb(a,b){var c=gb;return Object.prototype.hasOwnProperty.call(c,a)?c[a]:c[a]=b(a)};var hb;if("undefined"===typeof q)var q={};if("undefined"===typeof ib)var ib=null;if("undefined"===typeof kb)var kb=null;var lb=null;if("undefined"===typeof mb)var mb=null;function ob(){return new r(null,5,[pb,!0,qb,!0,rb,!1,sb,!1,tb,null],null)}function t(a){return null!=a&&!1!==a}function ub(a){return null==a}function vb(a){return a instanceof Array}function wb(a){return null==a?!0:!1===a?!0:!1}function yb(a){return ca(a)}function Ab(a,b){return a[n(null==b?null:b)]?!0:a._?!0:!1}
+function Bb(a){return null==a?null:a.constructor}function Cb(a,b){var c=Bb(b);c=t(t(c)?c.qc:c)?c.Tb:n(b);return Error(["No protocol method ",a," defined for type ",c,": ",b].join(""))}function Db(a){var b=a.Tb;return t(b)?b:""+v.h(a)}var Fb="undefined"!==typeof Symbol&&"function"===n(Symbol)?Symbol.iterator:"@@iterator";function Gb(a){for(var b=a.length,c=Array(b),d=0;;)if(d<b)c[d]=a[d],d+=1;else break;return c}
+var Hb=function Hb(a){switch(arguments.length){case 2:return Hb.c(arguments[0],arguments[1]);default:for(var c=[],d=arguments.length,e=0;;)if(e<d)c.push(arguments[e]),e+=1;else break;return Hb.A(arguments[0],arguments[1],new Jb(c.slice(2),0,null))}};Hb.c=function(a,b){return a[b]};Hb.A=function(a,b,c){return Kb(Hb,a[b],c)};Hb.N=function(a){var b=y(a),c=z(a);a=y(c);c=z(c);return Hb.A(b,a,c)};Hb.L=2;function Lb(a){return Mb(function(a,c){a.push(c);return a},[],a)}function Nb(){}function Ob(){}
+function Pb(){}var Qb=function Qb(a){if(null!=a&&null!=a.W)return a.W(a);var c=Qb[n(null==a?null:a)];if(null!=c)return c.h?c.h(a):c.call(null,a);c=Qb._;if(null!=c)return c.h?c.h(a):c.call(null,a);throw Cb("ICounted.-count",a);},Rb=function Rb(a){if(null!=a&&null!=a.oa)return a.oa(a);var c=Rb[n(null==a?null:a)];if(null!=c)return c.h?c.h(a):c.call(null,a);c=Rb._;if(null!=c)return c.h?c.h(a):c.call(null,a);throw Cb("IEmptyableCollection.-empty",a);};function Sb(){}
+var Tb=function Tb(a,b){if(null!=a&&null!=a.X)return a.X(a,b);var d=Tb[n(null==a?null:a)];if(null!=d)return d.c?d.c(a,b):d.call(null,a,b);d=Tb._;if(null!=d)return d.c?d.c(a,b):d.call(null,a,b);throw Cb("ICollection.-conj",a);};function Ub(){}var A=function A(a){switch(arguments.length){case 2:return A.c(arguments[0],arguments[1]);case 3:return A.l(arguments[0],arguments[1],arguments[2]);default:throw Error(["Invalid arity: ",v.h(arguments.length)].join(""));}};
+A.c=function(a,b){if(null!=a&&null!=a.$)return a.$(a,b);var c=A[n(null==a?null:a)];if(null!=c)return c.c?c.c(a,b):c.call(null,a,b);c=A._;if(null!=c)return c.c?c.c(a,b):c.call(null,a,b);throw Cb("IIndexed.-nth",a);};A.l=function(a,b,c){if(null!=a&&null!=a.ka)return a.ka(a,b,c);var d=A[n(null==a?null:a)];if(null!=d)return d.l?d.l(a,b,c):d.call(null,a,b,c);d=A._;if(null!=d)return d.l?d.l(a,b,c):d.call(null,a,b,c);throw Cb("IIndexed.-nth",a);};A.L=3;function Vb(){}
+var Wb=function Wb(a){if(null!=a&&null!=a.Ia)return a.Ia(a);var c=Wb[n(null==a?null:a)];if(null!=c)return c.h?c.h(a):c.call(null,a);c=Wb._;if(null!=c)return c.h?c.h(a):c.call(null,a);throw Cb("ISeq.-first",a);},Yb=function Yb(a){if(null!=a&&null!=a.bb)return a.bb(a);var c=Yb[n(null==a?null:a)];if(null!=c)return c.h?c.h(a):c.call(null,a);c=Yb._;if(null!=c)return c.h?c.h(a):c.call(null,a);throw Cb("ISeq.-rest",a);};function Zb(){}function $b(){}
+var cc=function cc(a){switch(arguments.length){case 2:return cc.c(arguments[0],arguments[1]);case 3:return cc.l(arguments[0],arguments[1],arguments[2]);default:throw Error(["Invalid arity: ",v.h(arguments.length)].join(""));}};cc.c=function(a,b){if(null!=a&&null!=a.V)return a.V(a,b);var c=cc[n(null==a?null:a)];if(null!=c)return c.c?c.c(a,b):c.call(null,a,b);c=cc._;if(null!=c)return c.c?c.c(a,b):c.call(null,a,b);throw Cb("ILookup.-lookup",a);};
+cc.l=function(a,b,c){if(null!=a&&null!=a.I)return a.I(a,b,c);var d=cc[n(null==a?null:a)];if(null!=d)return d.l?d.l(a,b,c):d.call(null,a,b,c);d=cc._;if(null!=d)return d.l?d.l(a,b,c):d.call(null,a,b,c);throw Cb("ILookup.-lookup",a);};cc.L=3;
+var dc=function dc(a,b){if(null!=a&&null!=a.yc)return a.yc(a,b);var d=dc[n(null==a?null:a)];if(null!=d)return d.c?d.c(a,b):d.call(null,a,b);d=dc._;if(null!=d)return d.c?d.c(a,b):d.call(null,a,b);throw Cb("IAssociative.-contains-key?",a);},ec=function ec(a,b,c){if(null!=a&&null!=a.O)return a.O(a,b,c);var e=ec[n(null==a?null:a)];if(null!=e)return e.l?e.l(a,b,c):e.call(null,a,b,c);e=ec._;if(null!=e)return e.l?e.l(a,b,c):e.call(null,a,b,c);throw Cb("IAssociative.-assoc",a);};function fc(){}
+var gc=function gc(a,b){if(null!=a&&null!=a.ga)return a.ga(a,b);var d=gc[n(null==a?null:a)];if(null!=d)return d.c?d.c(a,b):d.call(null,a,b);d=gc._;if(null!=d)return d.c?d.c(a,b):d.call(null,a,b);throw Cb("IMap.-dissoc",a);};function hc(){}
+var jc=function jc(a){if(null!=a&&null!=a.fd)return a.fd(a);var c=jc[n(null==a?null:a)];if(null!=c)return c.h?c.h(a):c.call(null,a);c=jc._;if(null!=c)return c.h?c.h(a):c.call(null,a);throw Cb("IMapEntry.-key",a);},kc=function kc(a){if(null!=a&&null!=a.gd)return a.gd(a);var c=kc[n(null==a?null:a)];if(null!=c)return c.h?c.h(a):c.call(null,a);c=kc._;if(null!=c)return c.h?c.h(a):c.call(null,a);throw Cb("IMapEntry.-val",a);};function lc(){}
+var mc=function mc(a,b){if(null!=a&&null!=a.ie)return a.ie(a,b);var d=mc[n(null==a?null:a)];if(null!=d)return d.c?d.c(a,b):d.call(null,a,b);d=mc._;if(null!=d)return d.c?d.c(a,b):d.call(null,a,b);throw Cb("ISet.-disjoin",a);},nc=function nc(a){if(null!=a&&null!=a.Ac)return a.Ac(a);var c=nc[n(null==a?null:a)];if(null!=c)return c.h?c.h(a):c.call(null,a);c=nc._;if(null!=c)return c.h?c.h(a):c.call(null,a);throw Cb("IStack.-peek",a);},oc=function oc(a){if(null!=a&&null!=a.Bc)return a.Bc(a);var c=oc[n(null==
+a?null:a)];if(null!=c)return c.h?c.h(a):c.call(null,a);c=oc._;if(null!=c)return c.h?c.h(a):c.call(null,a);throw Cb("IStack.-pop",a);};function pc(){}
+var qc=function qc(a,b,c){if(null!=a&&null!=a.dc)return a.dc(a,b,c);var e=qc[n(null==a?null:a)];if(null!=e)return e.l?e.l(a,b,c):e.call(null,a,b,c);e=qc._;if(null!=e)return e.l?e.l(a,b,c):e.call(null,a,b,c);throw Cb("IVector.-assoc-n",a);},B=function B(a){if(null!=a&&null!=a.pc)return a.pc(a);var c=B[n(null==a?null:a)];if(null!=c)return c.h?c.h(a):c.call(null,a);c=B._;if(null!=c)return c.h?c.h(a):c.call(null,a);throw Cb("IDeref.-deref",a);};function rc(){}
+var sc=function sc(a){if(null!=a&&null!=a.P)return a.P(a);var c=sc[n(null==a?null:a)];if(null!=c)return c.h?c.h(a):c.call(null,a);c=sc._;if(null!=c)return c.h?c.h(a):c.call(null,a);throw Cb("IMeta.-meta",a);},tc=function tc(a,b){if(null!=a&&null!=a.T)return a.T(a,b);var d=tc[n(null==a?null:a)];if(null!=d)return d.c?d.c(a,b):d.call(null,a,b);d=tc._;if(null!=d)return d.c?d.c(a,b):d.call(null,a,b);throw Cb("IWithMeta.-with-meta",a);};function uc(){}
+var vc=function vc(a){switch(arguments.length){case 2:return vc.c(arguments[0],arguments[1]);case 3:return vc.l(arguments[0],arguments[1],arguments[2]);default:throw Error(["Invalid arity: ",v.h(arguments.length)].join(""));}};vc.c=function(a,b){if(null!=a&&null!=a.Fa)return a.Fa(a,b);var c=vc[n(null==a?null:a)];if(null!=c)return c.c?c.c(a,b):c.call(null,a,b);c=vc._;if(null!=c)return c.c?c.c(a,b):c.call(null,a,b);throw Cb("IReduce.-reduce",a);};
+vc.l=function(a,b,c){if(null!=a&&null!=a.Ga)return a.Ga(a,b,c);var d=vc[n(null==a?null:a)];if(null!=d)return d.l?d.l(a,b,c):d.call(null,a,b,c);d=vc._;if(null!=d)return d.l?d.l(a,b,c):d.call(null,a,b,c);throw Cb("IReduce.-reduce",a);};vc.L=3;function wc(){}
+var yc=function yc(a,b,c){if(null!=a&&null!=a.Qc)return a.Qc(a,b,c);var e=yc[n(null==a?null:a)];if(null!=e)return e.l?e.l(a,b,c):e.call(null,a,b,c);e=yc._;if(null!=e)return e.l?e.l(a,b,c):e.call(null,a,b,c);throw Cb("IKVReduce.-kv-reduce",a);},zc=function zc(a,b){if(null!=a&&null!=a.K)return a.K(a,b);var d=zc[n(null==a?null:a)];if(null!=d)return d.c?d.c(a,b):d.call(null,a,b);d=zc._;if(null!=d)return d.c?d.c(a,b):d.call(null,a,b);throw Cb("IEquiv.-equiv",a);},Ac=function Ac(a){if(null!=a&&null!=a.U)return a.U(a);
+var c=Ac[n(null==a?null:a)];if(null!=c)return c.h?c.h(a):c.call(null,a);c=Ac._;if(null!=c)return c.h?c.h(a):c.call(null,a);throw Cb("IHash.-hash",a);};function Bc(){}var Cc=function Cc(a){if(null!=a&&null!=a.S)return a.S(a);var c=Cc[n(null==a?null:a)];if(null!=c)return c.h?c.h(a):c.call(null,a);c=Cc._;if(null!=c)return c.h?c.h(a):c.call(null,a);throw Cb("ISeqable.-seq",a);};function Ec(){}function Fc(){}function Gc(){}function Hc(){}
+var Ic=function Ic(a){if(null!=a&&null!=a.Rc)return a.Rc(a);var c=Ic[n(null==a?null:a)];if(null!=c)return c.h?c.h(a):c.call(null,a);c=Ic._;if(null!=c)return c.h?c.h(a):c.call(null,a);throw Cb("IReversible.-rseq",a);},Jc=function Jc(a,b){if(null!=a&&null!=a.Re)return a.Re(0,b);var d=Jc[n(null==a?null:a)];if(null!=d)return d.c?d.c(a,b):d.call(null,a,b);d=Jc._;if(null!=d)return d.c?d.c(a,b):d.call(null,a,b);throw Cb("IWriter.-write",a);};function Kc(){}
+var Lc=function Lc(a,b,c){if(null!=a&&null!=a.Kd)return a.Kd(a,b,c);var e=Lc[n(null==a?null:a)];if(null!=e)return e.l?e.l(a,b,c):e.call(null,a,b,c);e=Lc._;if(null!=e)return e.l?e.l(a,b,c):e.call(null,a,b,c);throw Cb("IWatchable.-notify-watches",a);},Mc=function Mc(a,b,c){if(null!=a&&null!=a.Jd)return a.Jd(a,b,c);var e=Mc[n(null==a?null:a)];if(null!=e)return e.l?e.l(a,b,c):e.call(null,a,b,c);e=Mc._;if(null!=e)return e.l?e.l(a,b,c):e.call(null,a,b,c);throw Cb("IWatchable.-add-watch",a);},Nc=function Nc(a,
+b){if(null!=a&&null!=a.Ld)return a.Ld(a,b);var d=Nc[n(null==a?null:a)];if(null!=d)return d.c?d.c(a,b):d.call(null,a,b);d=Nc._;if(null!=d)return d.c?d.c(a,b):d.call(null,a,b);throw Cb("IWatchable.-remove-watch",a);},Oc=function Oc(a){if(null!=a&&null!=a.Pc)return a.Pc(a);var c=Oc[n(null==a?null:a)];if(null!=c)return c.h?c.h(a):c.call(null,a);c=Oc._;if(null!=c)return c.h?c.h(a):c.call(null,a);throw Cb("IEditableCollection.-as-transient",a);},Pc=function Pc(a,b){if(null!=a&&null!=a.Dc)return a.Dc(a,
+b);var d=Pc[n(null==a?null:a)];if(null!=d)return d.c?d.c(a,b):d.call(null,a,b);d=Pc._;if(null!=d)return d.c?d.c(a,b):d.call(null,a,b);throw Cb("ITransientCollection.-conj!",a);},Qc=function Qc(a){if(null!=a&&null!=a.kd)return a.kd(a);var c=Qc[n(null==a?null:a)];if(null!=c)return c.h?c.h(a):c.call(null,a);c=Qc._;if(null!=c)return c.h?c.h(a):c.call(null,a);throw Cb("ITransientCollection.-persistent!",a);},Rc=function Rc(a,b,c){if(null!=a&&null!=a.Cc)return a.Cc(a,b,c);var e=Rc[n(null==a?null:a)];if(null!=
+e)return e.l?e.l(a,b,c):e.call(null,a,b,c);e=Rc._;if(null!=e)return e.l?e.l(a,b,c):e.call(null,a,b,c);throw Cb("ITransientAssociative.-assoc!",a);};function Tc(){}
+var Uc=function Uc(a,b){if(null!=a&&null!=a.cc)return a.cc(a,b);var d=Uc[n(null==a?null:a)];if(null!=d)return d.c?d.c(a,b):d.call(null,a,b);d=Uc._;if(null!=d)return d.c?d.c(a,b):d.call(null,a,b);throw Cb("IComparable.-compare",a);},Vc=function Vc(a){if(null!=a&&null!=a.Le)return a.Le();var c=Vc[n(null==a?null:a)];if(null!=c)return c.h?c.h(a):c.call(null,a);c=Vc._;if(null!=c)return c.h?c.h(a):c.call(null,a);throw Cb("IChunk.-drop-first",a);},Wc=function Wc(a){if(null!=a&&null!=a.ge)return a.ge(a);
+var c=Wc[n(null==a?null:a)];if(null!=c)return c.h?c.h(a):c.call(null,a);c=Wc._;if(null!=c)return c.h?c.h(a):c.call(null,a);throw Cb("IChunkedSeq.-chunked-first",a);},Xc=function Xc(a){if(null!=a&&null!=a.Hd)return a.Hd(a);var c=Xc[n(null==a?null:a)];if(null!=c)return c.h?c.h(a):c.call(null,a);c=Xc._;if(null!=c)return c.h?c.h(a):c.call(null,a);throw Cb("IChunkedSeq.-chunked-rest",a);},Yc=function Yc(a){if(null!=a&&null!=a.hd)return a.hd(a);var c=Yc[n(null==a?null:a)];if(null!=c)return c.h?c.h(a):c.call(null,
+a);c=Yc._;if(null!=c)return c.h?c.h(a):c.call(null,a);throw Cb("INamed.-name",a);},Zc=function Zc(a){if(null!=a&&null!=a.jd)return a.jd(a);var c=Zc[n(null==a?null:a)];if(null!=c)return c.h?c.h(a):c.call(null,a);c=Zc._;if(null!=c)return c.h?c.h(a):c.call(null,a);throw Cb("INamed.-namespace",a);},$c=function $c(a,b){if(null!=a&&null!=a.Gb)return a.Gb(a,b);var d=$c[n(null==a?null:a)];if(null!=d)return d.c?d.c(a,b):d.call(null,a,b);d=$c._;if(null!=d)return d.c?d.c(a,b):d.call(null,a,b);throw Cb("IReset.-reset!",
+a);},ad=function ad(a){switch(arguments.length){case 2:return ad.c(arguments[0],arguments[1]);case 3:return ad.l(arguments[0],arguments[1],arguments[2]);case 4:return ad.M(arguments[0],arguments[1],arguments[2],arguments[3]);case 5:return ad.Z(arguments[0],arguments[1],arguments[2],arguments[3],arguments[4]);default:throw Error(["Invalid arity: ",v.h(arguments.length)].join(""));}};
+ad.c=function(a,b){if(null!=a&&null!=a.je)return a.je(a,b);var c=ad[n(null==a?null:a)];if(null!=c)return c.c?c.c(a,b):c.call(null,a,b);c=ad._;if(null!=c)return c.c?c.c(a,b):c.call(null,a,b);throw Cb("ISwap.-swap!",a);};ad.l=function(a,b,c){if(null!=a&&null!=a.ke)return a.ke(a,b,c);var d=ad[n(null==a?null:a)];if(null!=d)return d.l?d.l(a,b,c):d.call(null,a,b,c);d=ad._;if(null!=d)return d.l?d.l(a,b,c):d.call(null,a,b,c);throw Cb("ISwap.-swap!",a);};
+ad.M=function(a,b,c,d){if(null!=a&&null!=a.le)return a.le(a,b,c,d);var e=ad[n(null==a?null:a)];if(null!=e)return e.M?e.M(a,b,c,d):e.call(null,a,b,c,d);e=ad._;if(null!=e)return e.M?e.M(a,b,c,d):e.call(null,a,b,c,d);throw Cb("ISwap.-swap!",a);};ad.Z=function(a,b,c,d,e){if(null!=a&&null!=a.me)return a.me(a,b,c,d,e);var f=ad[n(null==a?null:a)];if(null!=f)return f.Z?f.Z(a,b,c,d,e):f.call(null,a,b,c,d,e);f=ad._;if(null!=f)return f.Z?f.Z(a,b,c,d,e):f.call(null,a,b,c,d,e);throw Cb("ISwap.-swap!",a);};
+ad.L=5;var bd=function bd(a,b){if(null!=a&&null!=a.Qe)return a.Qe(0,b);var d=bd[n(null==a?null:a)];if(null!=d)return d.c?d.c(a,b):d.call(null,a,b);d=bd._;if(null!=d)return d.c?d.c(a,b):d.call(null,a,b);throw Cb("IVolatile.-vreset!",a);};function cd(){}var dd=function dd(a){if(null!=a&&null!=a.ba)return a.ba(a);var c=dd[n(null==a?null:a)];if(null!=c)return c.h?c.h(a):c.call(null,a);c=dd._;if(null!=c)return c.h?c.h(a):c.call(null,a);throw Cb("IIterable.-iterator",a);};
+function ed(a){this.Nf=a;this.m=1073741824;this.J=0}ed.prototype.Re=function(a,b){return this.Nf.append(b)};function fd(a){var b=new cb;a.R(null,new ed(b),ob());return""+v.h(b)}var gd="undefined"!==typeof Math.imul&&0!==Math.imul(4294967295,5)?function(a,b){return Math.imul(a,b)}:function(a,b){var c=a&65535,d=b&65535;return c*d+((a>>>16&65535)*d+c*(b>>>16&65535)<<16>>>0)|0};function hd(a){a=gd(a|0,-862048943);return gd(a<<15|a>>>-15,461845907)}
+function id(a,b){var c=(a|0)^(b|0);return gd(c<<13|c>>>-13,5)+-430675100|0}function jd(a,b){var c=(a|0)^b;c=gd(c^c>>>16,-2048144789);c=gd(c^c>>>13,-1028477387);return c^c>>>16}function kd(a){a:{var b=1;for(var c=0;;)if(b<a.length){var d=b+2;c=id(c,hd(a.charCodeAt(b-1)|a.charCodeAt(b)<<16));b=d}else{b=c;break a}}b=1===(a.length&1)?b^hd(a.charCodeAt(a.length-1)):b;return jd(b,gd(2,a.length))}var ld={},md=0;
+function nd(a){255<md&&(ld={},md=0);if(null==a)return 0;var b=ld[a];if("number"!==typeof b){a:if(null!=a)if(b=a.length,0<b)for(var c=0,d=0;;)if(c<b){var e=c+1;d=gd(31,d)+a.charCodeAt(c);c=e}else{b=d;break a}else b=0;else b=0;ld[a]=b;md+=1}return a=b}
+function od(a){if(null!=a&&(a.m&4194304||q===a.Sf))return a.U(null)^0;if("number"===typeof a){if(t(isFinite(a)))return Math.floor(a)%2147483647;switch(a){case Infinity:return 2146435072;case -Infinity:return-1048576;default:return 2146959360}}else return!0===a?a=1231:!1===a?a=1237:"string"===typeof a?(a=nd(a),0!==a&&(a=hd(a),a=id(0,a),a=jd(a,4))):a=a instanceof Date?a.valueOf()^0:null==a?0:Ac(a)^0,a}function pd(a,b){return a^b+2654435769+(a<<6)+(a>>2)}function qd(a){return a instanceof rd}
+function sd(a,b){if(a.Zb===b.Zb)return 0;var c=wb(a.fb);if(t(c?b.fb:c))return-1;if(t(a.fb)){if(wb(b.fb))return 1;c=Aa(a.fb,b.fb);return 0===c?Aa(a.name,b.name):c}return Aa(a.name,b.name)}function rd(a,b,c,d,e){this.fb=a;this.name=b;this.Zb=c;this.Oc=d;this.hb=e;this.m=2154168321;this.J=4096}g=rd.prototype;g.toString=function(){return this.Zb};g.equiv=function(a){return this.K(null,a)};g.K=function(a,b){return b instanceof rd?this.Zb===b.Zb:!1};
+g.call=function(){var a=null;a=function(a,c,d){switch(arguments.length){case 2:return D.c(c,this);case 3:return D.l(c,this,d)}throw Error("Invalid arity: "+(arguments.length-1));};a.c=function(a,c){return D.c(c,this)};a.l=function(a,c,d){return D.l(c,this,d)};return a}();g.apply=function(a,b){return this.call.apply(this,[this].concat(Gb(b)))};g.h=function(a){return D.c(a,this)};g.c=function(a,b){return D.l(a,this,b)};g.P=function(){return this.hb};
+g.T=function(a,b){return new rd(this.fb,this.name,this.Zb,this.Oc,b)};g.U=function(){var a=this.Oc;return null!=a?a:this.Oc=a=pd(kd(this.name),nd(this.fb))};g.hd=function(){return this.name};g.jd=function(){return this.fb};g.R=function(a,b){return Jc(b,this.Zb)};var td=function td(a){switch(arguments.length){case 1:return td.h(arguments[0]);case 2:return td.c(arguments[0],arguments[1]);default:throw Error(["Invalid arity: ",v.h(arguments.length)].join(""));}};
+td.h=function(a){if(a instanceof rd)return a;var b=a.indexOf("/");return 1>b?td.c(null,a):td.c(a.substring(0,b),a.substring(b+1,a.length))};td.c=function(a,b){var c=null!=a?[v.h(a),"/",v.h(b)].join(""):b;return new rd(a,b,c,null,null)};td.L=2;function ud(a){return null!=a?a.J&131072||q===a.Tf?!0:a.J?!1:Ab(cd,a):Ab(cd,a)}
+function E(a){if(null==a)return null;if(null!=a&&(a.m&8388608||q===a.Pe))return a.S(null);if(vb(a)||"string"===typeof a)return 0===a.length?null:new Jb(a,0,null);if(Ab(Bc,a))return Cc(a);throw Error([v.h(a)," is not ISeqable"].join(""));}function y(a){if(null==a)return null;if(null!=a&&(a.m&64||q===a.G))return a.Ia(null);a=E(a);return null==a?null:Wb(a)}function vd(a){return null!=a?null!=a&&(a.m&64||q===a.G)?a.bb(null):(a=E(a))?Yb(a):wd:wd}
+function z(a){return null==a?null:null!=a&&(a.m&128||q===a.Id)?a.Ka(null):E(vd(a))}var G=function G(a){switch(arguments.length){case 1:return G.h(arguments[0]);case 2:return G.c(arguments[0],arguments[1]);default:for(var c=[],d=arguments.length,e=0;;)if(e<d)c.push(arguments[e]),e+=1;else break;return G.A(arguments[0],arguments[1],new Jb(c.slice(2),0,null))}};G.h=function(){return!0};G.c=function(a,b){return null==a?null==b:a===b||zc(a,b)};
+G.A=function(a,b,c){for(;;)if(G.c(a,b))if(z(c))a=b,b=y(c),c=z(c);else return G.c(b,y(c));else return!1};G.N=function(a){var b=y(a),c=z(a);a=y(c);c=z(c);return G.A(b,a,c)};G.L=2;function xd(a){this.s=a}xd.prototype.next=function(){if(null!=this.s){var a=y(this.s);this.s=z(this.s);return{value:a,done:!1}}return{value:null,done:!0}};function yd(a){return new xd(E(a))}function zd(a,b){var c=hd(a);c=id(0,c);return jd(c,b)}
+function Ad(a){var b=0,c=1;for(a=E(a);;)if(null!=a)b+=1,c=gd(31,c)+od(y(a))|0,a=z(a);else return zd(c,b)}var Cd=zd(1,0);function Dd(a){var b=0,c=0;for(a=E(a);;)if(null!=a)b+=1,c=c+od(y(a))|0,a=z(a);else return zd(c,b)}var Ed=zd(0,0);Pb["null"]=!0;Qb["null"]=function(){return 0};Date.prototype.K=function(a,b){return b instanceof Date&&this.valueOf()===b.valueOf()};Date.prototype.zc=q;
+Date.prototype.cc=function(a,b){if(b instanceof Date)return Aa(this.valueOf(),b.valueOf());throw Error(["Cannot compare ",v.h(this)," to ",v.h(b)].join(""));};zc.number=function(a,b){return a===b};Nb["function"]=!0;rc["function"]=!0;sc["function"]=function(){return null};Ac._=function(a){return ja(a)};function Fd(a){return a+1}function Gd(a){this.H=a;this.m=32768;this.J=0}Gd.prototype.pc=function(){return this.H};function Hd(a){return a instanceof Gd}function Id(a){return Hd(a)?a:new Gd(a)}
+function Jd(a){return Hd(a)?B(a):a}function Kd(a,b){var c=Qb(a);if(0===c)return b.B?b.B():b.call(null);for(var d=A.c(a,0),e=1;;)if(e<c){var f=A.c(a,e);d=b.c?b.c(d,f):b.call(null,d,f);if(Hd(d))return B(d);e+=1}else return d}function Ld(a,b,c){var d=Qb(a),e=c;for(c=0;;)if(c<d){var f=A.c(a,c);e=b.c?b.c(e,f):b.call(null,e,f);if(Hd(e))return B(e);c+=1}else return e}
+function Md(a,b){var c=a.length;if(0===a.length)return b.B?b.B():b.call(null);for(var d=a[0],e=1;;)if(e<c){var f=a[e];d=b.c?b.c(d,f):b.call(null,d,f);if(Hd(d))return B(d);e+=1}else return d}function Nd(a,b,c){var d=a.length,e=c;for(c=0;;)if(c<d){var f=a[c];e=b.c?b.c(e,f):b.call(null,e,f);if(Hd(e))return B(e);c+=1}else return e}function Od(a,b,c,d){for(var e=a.length;;)if(d<e){var f=a[d];c=b.c?b.c(c,f):b.call(null,c,f);if(Hd(c))return B(c);d+=1}else return c}
+function Pd(a){return null!=a?a.m&2||q===a.jf?!0:a.m?!1:Ab(Pb,a):Ab(Pb,a)}function Qd(a){return null!=a?a.m&16||q===a.Ne?!0:a.m?!1:Ab(Ub,a):Ab(Ub,a)}function Ud(a,b,c){var d=H(a);if(c>=d)return-1;!(0<c)&&0>c&&(c+=d,c=0>c?0:c);for(;;)if(c<d){if(G.c(Vd(a,c),b))return c;c+=1}else return-1}function Xd(a,b,c){var d=H(a);if(0===d)return-1;0<c?(--d,c=d<c?d:c):c=0>c?d+c:c;for(;;)if(0<=c){if(G.c(Vd(a,c),b))return c;--c}else return-1}function Yd(a,b){this.o=a;this.i=b}
+Yd.prototype.ja=function(){return this.i<this.o.length};Yd.prototype.next=function(){var a=this.o[this.i];this.i+=1;return a};function Jb(a,b,c){this.o=a;this.i=b;this.meta=c;this.m=166592766;this.J=139264}g=Jb.prototype;g.toString=function(){return fd(this)};g.equiv=function(a){return this.K(null,a)};
+g.indexOf=function(){var a=null;a=function(a,c){switch(arguments.length){case 1:return Ud(this,a,0);case 2:return Ud(this,a,c)}throw Error("Invalid arity: "+(arguments.length-1));};a.h=function(a){return Ud(this,a,0)};a.c=function(a,c){return Ud(this,a,c)};return a}();
+g.lastIndexOf=function(){function a(a){return Xd(this,a,H(this))}var b=null;b=function(b,d){switch(arguments.length){case 1:return a.call(this,b);case 2:return Xd(this,b,d)}throw Error("Invalid arity: "+(arguments.length-1));};b.h=a;b.c=function(a,b){return Xd(this,a,b)};return b}();g.$=function(a,b){var c=b+this.i;if(0<=c&&c<this.o.length)return this.o[c];throw Error("Index out of bounds");};g.ka=function(a,b,c){a=b+this.i;return 0<=a&&a<this.o.length?this.o[a]:c};
+g.ba=function(){return new Yd(this.o,this.i)};g.P=function(){return this.meta};g.Ka=function(){return this.i+1<this.o.length?new Jb(this.o,this.i+1,null):null};g.W=function(){var a=this.o.length-this.i;return 0>a?0:a};g.Rc=function(){var a=this.W(null);return 0<a?new Zd(this,a-1,null):null};g.U=function(){return Ad(this)};g.K=function(a,b){return $d(this,b)};g.oa=function(){return wd};g.Fa=function(a,b){return Od(this.o,b,this.o[this.i],this.i+1)};g.Ga=function(a,b,c){return Od(this.o,b,c,this.i)};
+g.Ia=function(){return this.o[this.i]};g.bb=function(){return this.i+1<this.o.length?new Jb(this.o,this.i+1,null):wd};g.S=function(){return this.i<this.o.length?this:null};g.T=function(a,b){return new Jb(this.o,this.i,b)};g.X=function(a,b){return ae(b,this)};Jb.prototype[Fb]=function(){return yd(this)};function be(a){return 0<a.length?new Jb(a,0,null):null}function Zd(a,b,c){this.Gd=a;this.i=b;this.meta=c;this.m=32374990;this.J=8192}g=Zd.prototype;g.toString=function(){return fd(this)};
+g.equiv=function(a){return this.K(null,a)};g.indexOf=function(){var a=null;a=function(a,c){switch(arguments.length){case 1:return Ud(this,a,0);case 2:return Ud(this,a,c)}throw Error("Invalid arity: "+(arguments.length-1));};a.h=function(a){return Ud(this,a,0)};a.c=function(a,c){return Ud(this,a,c)};return a}();
+g.lastIndexOf=function(){function a(a){return Xd(this,a,H(this))}var b=null;b=function(b,d){switch(arguments.length){case 1:return a.call(this,b);case 2:return Xd(this,b,d)}throw Error("Invalid arity: "+(arguments.length-1));};b.h=a;b.c=function(a,b){return Xd(this,a,b)};return b}();g.P=function(){return this.meta};g.Ka=function(){return 0<this.i?new Zd(this.Gd,this.i-1,null):null};g.W=function(){return this.i+1};g.U=function(){return Ad(this)};g.K=function(a,b){return $d(this,b)};
+g.oa=function(){return tc(wd,this.meta)};g.Fa=function(a,b){return ce(b,this)};g.Ga=function(a,b,c){return de(b,c,this)};g.Ia=function(){return A.c(this.Gd,this.i)};g.bb=function(){return 0<this.i?new Zd(this.Gd,this.i-1,null):wd};g.S=function(){return this};g.T=function(a,b){return new Zd(this.Gd,this.i,b)};g.X=function(a,b){return ae(b,this)};Zd.prototype[Fb]=function(){return yd(this)};function ee(a){return y(z(a))}function fe(a){for(;;){var b=z(a);if(null!=b)a=b;else return y(a)}}
+zc._=function(a,b){return a===b};var ge=function ge(a){switch(arguments.length){case 0:return ge.B();case 1:return ge.h(arguments[0]);case 2:return ge.c(arguments[0],arguments[1]);default:for(var c=[],d=arguments.length,e=0;;)if(e<d)c.push(arguments[e]),e+=1;else break;return ge.A(arguments[0],arguments[1],new Jb(c.slice(2),0,null))}};ge.B=function(){return he};ge.h=function(a){return a};ge.c=function(a,b){return null!=a?Tb(a,b):Tb(wd,b)};
+ge.A=function(a,b,c){for(;;)if(t(c))a=ge.c(a,b),b=y(c),c=z(c);else return ge.c(a,b)};ge.N=function(a){var b=y(a),c=z(a);a=y(c);c=z(c);return ge.A(b,a,c)};ge.L=2;function ie(a){return null==a?null:Rb(a)}function H(a){if(null!=a)if(null!=a&&(a.m&2||q===a.jf))a=a.W(null);else if(vb(a))a=a.length;else if("string"===typeof a)a=a.length;else if(null!=a&&(a.m&8388608||q===a.Pe))a:{a=E(a);for(var b=0;;){if(Pd(a)){a=b+Qb(a);break a}a=z(a);b+=1}}else a=Qb(a);else a=0;return a}
+function je(a,b,c){for(;;){if(null==a)return c;if(0===b)return E(a)?y(a):c;if(Qd(a))return A.l(a,b,c);if(E(a))a=z(a),--b;else return c}}
+function Vd(a,b){if("number"!==typeof b)throw Error("Index argument to nth must be a number");if(null==a)return a;if(null!=a&&(a.m&16||q===a.Ne))return a.$(null,b);if(vb(a)){if(0<=b&&b<a.length)return a[b];throw Error("Index out of bounds");}if("string"===typeof a){if(0<=b&&b<a.length)return a.charAt(b);throw Error("Index out of bounds");}if(null!=a&&(a.m&64||q===a.G)){a:{var c=a;for(var d=b;;){if(null==c)throw Error("Index out of bounds");if(0===d){if(E(c)){c=y(c);break a}throw Error("Index out of bounds");
+}if(Qd(c)){c=A.c(c,d);break a}if(E(c))c=z(c),--d;else throw Error("Index out of bounds");}}return c}if(Ab(Ub,a))return A.c(a,b);throw Error(["nth not supported on this type ",v.h(Db(Bb(a)))].join(""));}
+function J(a,b,c){if("number"!==typeof b)throw Error("Index argument to nth must be a number.");if(null==a)return c;if(null!=a&&(a.m&16||q===a.Ne))return a.ka(null,b,c);if(vb(a))return 0<=b&&b<a.length?a[b]:c;if("string"===typeof a)return 0<=b&&b<a.length?a.charAt(b):c;if(null!=a&&(a.m&64||q===a.G))return je(a,b,c);if(Ab(Ub,a))return A.l(a,b,c);throw Error(["nth not supported on this type ",v.h(Db(Bb(a)))].join(""));}
+var D=function D(a){switch(arguments.length){case 2:return D.c(arguments[0],arguments[1]);case 3:return D.l(arguments[0],arguments[1],arguments[2]);default:throw Error(["Invalid arity: ",v.h(arguments.length)].join(""));}};D.c=function(a,b){return null==a?null:null!=a&&(a.m&256||q===a.rf)?a.V(null,b):vb(a)?null!=b&&b<a.length?a[b|0]:null:"string"===typeof a?null!=b&&b<a.length?a.charAt(b|0):null:Ab($b,a)?cc.c(a,b):null};
+D.l=function(a,b,c){return null!=a?null!=a&&(a.m&256||q===a.rf)?a.I(null,b,c):vb(a)?null!=b&&0<=b&&b<a.length?a[b|0]:c:"string"===typeof a?null!=b&&0<=b&&b<a.length?a.charAt(b|0):c:Ab($b,a)?cc.l(a,b,c):c:c};D.L=3;var K=function K(a){switch(arguments.length){case 3:return K.l(arguments[0],arguments[1],arguments[2]);default:for(var c=[],d=arguments.length,e=0;;)if(e<d)c.push(arguments[e]),e+=1;else break;return K.A(arguments[0],arguments[1],arguments[2],new Jb(c.slice(3),0,null))}};
+K.l=function(a,b,c){return null!=a?ec(a,b,c):ke([b,c])};K.A=function(a,b,c,d){for(;;)if(a=K.l(a,b,c),t(d))b=y(d),c=ee(d),d=z(z(d));else return a};K.N=function(a){var b=y(a),c=z(a);a=y(c);var d=z(c);c=y(d);d=z(d);return K.A(b,a,c,d)};K.L=3;
+var le=function le(a){switch(arguments.length){case 1:return le.h(arguments[0]);case 2:return le.c(arguments[0],arguments[1]);default:for(var c=[],d=arguments.length,e=0;;)if(e<d)c.push(arguments[e]),e+=1;else break;return le.A(arguments[0],arguments[1],new Jb(c.slice(2),0,null))}};le.h=function(a){return a};le.c=function(a,b){return null==a?null:gc(a,b)};le.A=function(a,b,c){for(;;){if(null==a)return null;a=le.c(a,b);if(t(c))b=y(c),c=z(c);else return a}};
+le.N=function(a){var b=y(a),c=z(a);a=y(c);c=z(c);return le.A(b,a,c)};le.L=2;function me(a){var b=ha(a);return b?b:null!=a?q===a.hf?!0:a.Tc?!1:Ab(Nb,a):Ab(Nb,a)}function ne(a,b){this.C=a;this.meta=b;this.m=393217;this.J=0}g=ne.prototype;g.P=function(){return this.meta};g.T=function(a,b){return new ne(this.C,b)};g.hf=q;
+g.call=function(){function a(a,b,c,d,e,f,h,k,m,l,p,u,w,x,C,F,I,M,S,X,Q,Ga){return oe(this.C,b,c,d,e,be([f,h,k,m,l,p,u,w,x,C,F,I,M,S,X,Q,Ga]))}function b(a,b,c,d,e,f,h,k,m,l,p,u,w,x,C,F,I,M,S,X,Q){a=this;return a.C.Xa?a.C.Xa(b,c,d,e,f,h,k,m,l,p,u,w,x,C,F,I,M,S,X,Q):a.C.call(null,b,c,d,e,f,h,k,m,l,p,u,w,x,C,F,I,M,S,X,Q)}function c(a,b,c,d,e,f,h,k,m,l,p,u,w,x,C,F,I,M,S,X){a=this;return a.C.Wa?a.C.Wa(b,c,d,e,f,h,k,m,l,p,u,w,x,C,F,I,M,S,X):a.C.call(null,b,c,d,e,f,h,k,m,l,p,u,w,x,C,F,I,M,S,X)}function d(a,
+b,c,d,e,f,h,k,m,l,p,u,w,x,C,F,I,M,S){a=this;return a.C.Va?a.C.Va(b,c,d,e,f,h,k,m,l,p,u,w,x,C,F,I,M,S):a.C.call(null,b,c,d,e,f,h,k,m,l,p,u,w,x,C,F,I,M,S)}function e(a,b,c,d,e,f,h,k,m,l,p,u,w,x,C,F,I,M){a=this;return a.C.Ua?a.C.Ua(b,c,d,e,f,h,k,m,l,p,u,w,x,C,F,I,M):a.C.call(null,b,c,d,e,f,h,k,m,l,p,u,w,x,C,F,I,M)}function f(a,b,c,d,e,f,h,k,m,l,p,u,w,x,C,F,I){a=this;return a.C.Ta?a.C.Ta(b,c,d,e,f,h,k,m,l,p,u,w,x,C,F,I):a.C.call(null,b,c,d,e,f,h,k,m,l,p,u,w,x,C,F,I)}function h(a,b,c,d,e,f,h,k,m,l,p,u,
+w,x,C,F){a=this;return a.C.Sa?a.C.Sa(b,c,d,e,f,h,k,m,l,p,u,w,x,C,F):a.C.call(null,b,c,d,e,f,h,k,m,l,p,u,w,x,C,F)}function k(a,b,c,d,e,f,h,k,m,l,p,u,w,x,C){a=this;return a.C.Ra?a.C.Ra(b,c,d,e,f,h,k,m,l,p,u,w,x,C):a.C.call(null,b,c,d,e,f,h,k,m,l,p,u,w,x,C)}function l(a,b,c,d,e,f,h,k,m,l,p,u,w,x){a=this;return a.C.Qa?a.C.Qa(b,c,d,e,f,h,k,m,l,p,u,w,x):a.C.call(null,b,c,d,e,f,h,k,m,l,p,u,w,x)}function p(a,b,c,d,e,f,h,k,m,l,p,u,w){a=this;return a.C.Pa?a.C.Pa(b,c,d,e,f,h,k,m,l,p,u,w):a.C.call(null,b,c,d,
+e,f,h,k,m,l,p,u,w)}function m(a,b,c,d,e,f,h,k,m,l,p,u){a=this;return a.C.Oa?a.C.Oa(b,c,d,e,f,h,k,m,l,p,u):a.C.call(null,b,c,d,e,f,h,k,m,l,p,u)}function u(a,b,c,d,e,f,h,k,m,l,p){a=this;return a.C.Na?a.C.Na(b,c,d,e,f,h,k,m,l,p):a.C.call(null,b,c,d,e,f,h,k,m,l,p)}function w(a,b,c,d,e,f,h,k,m,l){a=this;return a.C.Za?a.C.Za(b,c,d,e,f,h,k,m,l):a.C.call(null,b,c,d,e,f,h,k,m,l)}function x(a,b,c,d,e,f,h,k,m){a=this;return a.C.Ha?a.C.Ha(b,c,d,e,f,h,k,m):a.C.call(null,b,c,d,e,f,h,k,m)}function C(a,b,c,d,e,f,
+h,k){a=this;return a.C.Ya?a.C.Ya(b,c,d,e,f,h,k):a.C.call(null,b,c,d,e,f,h,k)}function F(a,b,c,d,e,f,h){a=this;return a.C.Ca?a.C.Ca(b,c,d,e,f,h):a.C.call(null,b,c,d,e,f,h)}function I(a,b,c,d,e,f){a=this;return a.C.Z?a.C.Z(b,c,d,e,f):a.C.call(null,b,c,d,e,f)}function M(a,b,c,d,e){a=this;return a.C.M?a.C.M(b,c,d,e):a.C.call(null,b,c,d,e)}function S(a,b,c,d){a=this;return a.C.l?a.C.l(b,c,d):a.C.call(null,b,c,d)}function X(a,b,c){a=this;return a.C.c?a.C.c(b,c):a.C.call(null,b,c)}function Ga(a,b){a=this;
+return a.C.h?a.C.h(b):a.C.call(null,b)}function db(a){a=this;return a.C.B?a.C.B():a.C.call(null)}var Q=null;Q=function(xb,Ha,Ja,Oa,Ba,W,$a,ka,jb,nb,zb,Ib,Q,Xb,ic,xc,Sc,Bd,se,Lf,Ih,kl){switch(arguments.length){case 1:return db.call(this,xb);case 2:return Ga.call(this,xb,Ha);case 3:return X.call(this,xb,Ha,Ja);case 4:return S.call(this,xb,Ha,Ja,Oa);case 5:return M.call(this,xb,Ha,Ja,Oa,Ba);case 6:return I.call(this,xb,Ha,Ja,Oa,Ba,W);case 7:return F.call(this,xb,Ha,Ja,Oa,Ba,W,$a);case 8:return C.call(this,
+xb,Ha,Ja,Oa,Ba,W,$a,ka);case 9:return x.call(this,xb,Ha,Ja,Oa,Ba,W,$a,ka,jb);case 10:return w.call(this,xb,Ha,Ja,Oa,Ba,W,$a,ka,jb,nb);case 11:return u.call(this,xb,Ha,Ja,Oa,Ba,W,$a,ka,jb,nb,zb);case 12:return m.call(this,xb,Ha,Ja,Oa,Ba,W,$a,ka,jb,nb,zb,Ib);case 13:return p.call(this,xb,Ha,Ja,Oa,Ba,W,$a,ka,jb,nb,zb,Ib,Q);case 14:return l.call(this,xb,Ha,Ja,Oa,Ba,W,$a,ka,jb,nb,zb,Ib,Q,Xb);case 15:return k.call(this,xb,Ha,Ja,Oa,Ba,W,$a,ka,jb,nb,zb,Ib,Q,Xb,ic);case 16:return h.call(this,xb,Ha,Ja,Oa,Ba,
+W,$a,ka,jb,nb,zb,Ib,Q,Xb,ic,xc);case 17:return f.call(this,xb,Ha,Ja,Oa,Ba,W,$a,ka,jb,nb,zb,Ib,Q,Xb,ic,xc,Sc);case 18:return e.call(this,xb,Ha,Ja,Oa,Ba,W,$a,ka,jb,nb,zb,Ib,Q,Xb,ic,xc,Sc,Bd);case 19:return d.call(this,xb,Ha,Ja,Oa,Ba,W,$a,ka,jb,nb,zb,Ib,Q,Xb,ic,xc,Sc,Bd,se);case 20:return c.call(this,xb,Ha,Ja,Oa,Ba,W,$a,ka,jb,nb,zb,Ib,Q,Xb,ic,xc,Sc,Bd,se,Lf);case 21:return b.call(this,xb,Ha,Ja,Oa,Ba,W,$a,ka,jb,nb,zb,Ib,Q,Xb,ic,xc,Sc,Bd,se,Lf,Ih);case 22:return a.call(this,0,Ha,Ja,Oa,Ba,W,$a,ka,jb,nb,
+zb,Ib,Q,Xb,ic,xc,Sc,Bd,se,Lf,Ih,kl)}throw Error("Invalid arity: "+(arguments.length-1));};Q.h=db;Q.c=Ga;Q.l=X;Q.M=S;Q.Z=M;Q.Ca=I;Q.Ya=F;Q.Ha=C;Q.Za=x;Q.Na=w;Q.Oa=u;Q.Pa=m;Q.Qa=p;Q.Ra=l;Q.Sa=k;Q.Ta=h;Q.Ua=f;Q.Va=e;Q.Wa=d;Q.Xa=c;Q.he=b;Q.qf=a;return Q}();g.apply=function(a,b){return this.call.apply(this,[this].concat(Gb(b)))};g.B=function(){return this.C.B?this.C.B():this.C.call(null)};g.h=function(a){return this.C.h?this.C.h(a):this.C.call(null,a)};
+g.c=function(a,b){return this.C.c?this.C.c(a,b):this.C.call(null,a,b)};g.l=function(a,b,c){return this.C.l?this.C.l(a,b,c):this.C.call(null,a,b,c)};g.M=function(a,b,c,d){return this.C.M?this.C.M(a,b,c,d):this.C.call(null,a,b,c,d)};g.Z=function(a,b,c,d,e){return this.C.Z?this.C.Z(a,b,c,d,e):this.C.call(null,a,b,c,d,e)};g.Ca=function(a,b,c,d,e,f){return this.C.Ca?this.C.Ca(a,b,c,d,e,f):this.C.call(null,a,b,c,d,e,f)};
+g.Ya=function(a,b,c,d,e,f,h){return this.C.Ya?this.C.Ya(a,b,c,d,e,f,h):this.C.call(null,a,b,c,d,e,f,h)};g.Ha=function(a,b,c,d,e,f,h,k){return this.C.Ha?this.C.Ha(a,b,c,d,e,f,h,k):this.C.call(null,a,b,c,d,e,f,h,k)};g.Za=function(a,b,c,d,e,f,h,k,l){return this.C.Za?this.C.Za(a,b,c,d,e,f,h,k,l):this.C.call(null,a,b,c,d,e,f,h,k,l)};g.Na=function(a,b,c,d,e,f,h,k,l,p){return this.C.Na?this.C.Na(a,b,c,d,e,f,h,k,l,p):this.C.call(null,a,b,c,d,e,f,h,k,l,p)};
+g.Oa=function(a,b,c,d,e,f,h,k,l,p,m){return this.C.Oa?this.C.Oa(a,b,c,d,e,f,h,k,l,p,m):this.C.call(null,a,b,c,d,e,f,h,k,l,p,m)};g.Pa=function(a,b,c,d,e,f,h,k,l,p,m,u){return this.C.Pa?this.C.Pa(a,b,c,d,e,f,h,k,l,p,m,u):this.C.call(null,a,b,c,d,e,f,h,k,l,p,m,u)};g.Qa=function(a,b,c,d,e,f,h,k,l,p,m,u,w){return this.C.Qa?this.C.Qa(a,b,c,d,e,f,h,k,l,p,m,u,w):this.C.call(null,a,b,c,d,e,f,h,k,l,p,m,u,w)};
+g.Ra=function(a,b,c,d,e,f,h,k,l,p,m,u,w,x){return this.C.Ra?this.C.Ra(a,b,c,d,e,f,h,k,l,p,m,u,w,x):this.C.call(null,a,b,c,d,e,f,h,k,l,p,m,u,w,x)};g.Sa=function(a,b,c,d,e,f,h,k,l,p,m,u,w,x,C){return this.C.Sa?this.C.Sa(a,b,c,d,e,f,h,k,l,p,m,u,w,x,C):this.C.call(null,a,b,c,d,e,f,h,k,l,p,m,u,w,x,C)};g.Ta=function(a,b,c,d,e,f,h,k,l,p,m,u,w,x,C,F){return this.C.Ta?this.C.Ta(a,b,c,d,e,f,h,k,l,p,m,u,w,x,C,F):this.C.call(null,a,b,c,d,e,f,h,k,l,p,m,u,w,x,C,F)};
+g.Ua=function(a,b,c,d,e,f,h,k,l,p,m,u,w,x,C,F,I){return this.C.Ua?this.C.Ua(a,b,c,d,e,f,h,k,l,p,m,u,w,x,C,F,I):this.C.call(null,a,b,c,d,e,f,h,k,l,p,m,u,w,x,C,F,I)};g.Va=function(a,b,c,d,e,f,h,k,l,p,m,u,w,x,C,F,I,M){return this.C.Va?this.C.Va(a,b,c,d,e,f,h,k,l,p,m,u,w,x,C,F,I,M):this.C.call(null,a,b,c,d,e,f,h,k,l,p,m,u,w,x,C,F,I,M)};
+g.Wa=function(a,b,c,d,e,f,h,k,l,p,m,u,w,x,C,F,I,M,S){return this.C.Wa?this.C.Wa(a,b,c,d,e,f,h,k,l,p,m,u,w,x,C,F,I,M,S):this.C.call(null,a,b,c,d,e,f,h,k,l,p,m,u,w,x,C,F,I,M,S)};g.Xa=function(a,b,c,d,e,f,h,k,l,p,m,u,w,x,C,F,I,M,S,X){return this.C.Xa?this.C.Xa(a,b,c,d,e,f,h,k,l,p,m,u,w,x,C,F,I,M,S,X):this.C.call(null,a,b,c,d,e,f,h,k,l,p,m,u,w,x,C,F,I,M,S,X)};g.he=function(a,b,c,d,e,f,h,k,l,p,m,u,w,x,C,F,I,M,S,X,Ga){return oe(this.C,a,b,c,d,be([e,f,h,k,l,p,m,u,w,x,C,F,I,M,S,X,Ga]))};
+function pe(a,b){return ha(a)?new ne(a,b):null==a?null:tc(a,b)}function qe(a){var b=null!=a;return(b?null!=a?a.m&131072||q===a.tf||(a.m?0:Ab(rc,a)):Ab(rc,a):b)?sc(a):null}var re=function re(a){switch(arguments.length){case 1:return re.h(arguments[0]);case 2:return re.c(arguments[0],arguments[1]);default:for(var c=[],d=arguments.length,e=0;;)if(e<d)c.push(arguments[e]),e+=1;else break;return re.A(arguments[0],arguments[1],new Jb(c.slice(2),0,null))}};re.h=function(a){return a};
+re.c=function(a,b){return null==a?null:mc(a,b)};re.A=function(a,b,c){for(;;){if(null==a)return null;a=re.c(a,b);if(t(c))b=y(c),c=z(c);else return a}};re.N=function(a){var b=y(a),c=z(a);a=y(c);c=z(c);return re.A(b,a,c)};re.L=2;function te(a){return null==a||wb(E(a))}function ue(a){return null==a?!1:null!=a?a.m&8||q===a.Qf?!0:a.m?!1:Ab(Sb,a):Ab(Sb,a)}function ve(a){return null==a?!1:null!=a?a.m&4096||q===a.$f?!0:a.m?!1:Ab(lc,a):Ab(lc,a)}
+function we(a){return null!=a?a.m&16777216||q===a.Zf?!0:a.m?!1:Ab(Ec,a):Ab(Ec,a)}function xe(a){return null==a?!1:null!=a?a.m&1024||q===a.Wf?!0:a.m?!1:Ab(fc,a):Ab(fc,a)}function ye(a){return null!=a?a.m&67108864||q===a.Xf?!0:a.m?!1:Ab(Gc,a):Ab(Gc,a)}function ze(a){return null!=a?a.m&16384||q===a.ag?!0:a.m?!1:Ab(pc,a):Ab(pc,a)}function Ae(a){return null!=a?a.J&512||q===a.Pf?!0:!1:!1}function Be(a,b,c,d,e){for(;0!==e;)c[d]=a[b],d+=1,--e,b+=1}var Ce={};
+function De(a){return null==a?!1:null!=a?a.m&64||q===a.G?!0:a.m?!1:Ab(Vb,a):Ab(Vb,a)}function Ee(a){return null==a?!1:!1===a?!1:!0}function Fe(a){var b=me(a);return b?b:null!=a?a.m&1||q===a.Rf?!0:a.m?!1:Ab(Ob,a):Ab(Ob,a)}function Ge(a){return"number"===typeof a&&!isNaN(a)&&Infinity!==a&&parseFloat(a)===parseInt(a,10)}function He(a,b){return D.l(a,b,Ce)===Ce?!1:!0}
+var Ie=function Ie(a){switch(arguments.length){case 1:return Ie.h(arguments[0]);case 2:return Ie.c(arguments[0],arguments[1]);default:for(var c=[],d=arguments.length,e=0;;)if(e<d)c.push(arguments[e]),e+=1;else break;return Ie.A(arguments[0],arguments[1],new Jb(c.slice(2),0,null))}};Ie.h=function(){return!0};Ie.c=function(a,b){return!G.c(a,b)};Ie.A=function(a,b,c){if(G.c(a,b))return!1;a=Je([a,b]);for(b=c;;){var d=y(b);c=z(b);if(t(b)){if(He(a,d))return!1;a=ge.c(a,d);b=c}else return!0}};
+Ie.N=function(a){var b=y(a),c=z(a);a=y(c);c=z(c);return Ie.A(b,a,c)};Ie.L=2;function Ke(a,b){if(a===b)return 0;if(null==a)return-1;if(null==b)return 1;if("number"===typeof a){if("number"===typeof b)return Aa(a,b);throw Error(["Cannot compare ",v.h(a)," to ",v.h(b)].join(""));}if(null!=a?a.J&2048||q===a.zc||(a.J?0:Ab(Tc,a)):Ab(Tc,a))return Uc(a,b);if("string"!==typeof a&&!vb(a)&&!0!==a&&!1!==a||Bb(a)!==Bb(b))throw Error(["Cannot compare ",v.h(a)," to ",v.h(b)].join(""));return Aa(a,b)}
+function Le(a,b){var c=H(a),d=H(b);if(c<d)c=-1;else if(c>d)c=1;else if(0===c)c=0;else a:for(d=0;;){var e=Ke(Vd(a,d),Vd(b,d));if(0===e&&d+1<c)d+=1;else{c=e;break a}}return c}function Me(a){return G.c(a,Ke)?Ke:function(b,c){var d=a.c?a.c(b,c):a.call(null,b,c);return"number"===typeof d?d:t(d)?-1:t(a.c?a.c(c,b):a.call(null,c,b))?1:0}}function Ne(a,b){if(E(b)){a:{var c=[];for(var d=E(b);;)if(null!=d)c.push(y(d)),d=z(d);else break a}d=Me(a);Ca(c,d);return E(c)}return wd}
+function Oe(a){var b=Pe("@!\"#%\x26'*+-/:[{\x3c\\|\x3d]}\x3e^~?".split(""),"_CIRCA_ _BANG_ _DOUBLEQUOTE_ _SHARP_ _PERCENT_ _AMPERSAND_ _SINGLEQUOTE_ _STAR_ _PLUS_ _ _SLASH_ _COLON_ _LBRACK_ _LBRACE_ _LT_ _BSLASH_ _BAR_ _EQ_ _RBRACK_ _RBRACE_ _GT_ _CARET_ _TILDE_ _QMARK_".split(" "));return Qe(a,b)}function Qe(a,b){return Ne(function(b,d){var c=a.h?a.h(b):a.call(null,b),f=a.h?a.h(d):a.call(null,d),h=Me(Ke);return h.c?h.c(c,f):h.call(null,c,f)},b)}
+function ce(a,b){var c=E(b);return c?Mb(a,y(c),z(c)):a.B?a.B():a.call(null)}function de(a,b,c){for(c=E(c);;)if(c){var d=y(c);b=a.c?a.c(b,d):a.call(null,b,d);if(Hd(b))return B(b);c=z(c)}else return b}function Re(a,b){var c=dd(a);if(t(c.ja()))for(var d=c.next();;)if(c.ja()){var e=c.next();d=b.c?b.c(d,e):b.call(null,d,e);if(Hd(d))return B(d)}else return d;else return b.B?b.B():b.call(null)}
+function Se(a,b,c){for(a=dd(a);;)if(a.ja()){var d=a.next();c=b.c?b.c(c,d):b.call(null,c,d);if(Hd(c))return B(c)}else return c}function Te(a,b){return null!=b&&(b.m&524288||q===b.uf)?b.Fa(null,a):vb(b)?Md(b,a):"string"===typeof b?Md(b,a):Ab(uc,b)?vc.c(b,a):ud(b)?Re(b,a):ce(a,b)}function Mb(a,b,c){return null!=c&&(c.m&524288||q===c.uf)?c.Ga(null,a,b):vb(c)?Nd(c,a,b):"string"===typeof c?Nd(c,a,b):Ab(uc,c)?vc.l(c,a,b):ud(c)?Se(c,a,b):de(a,b,c)}function Ue(a,b,c){return null!=c?yc(c,a,b):b}
+function Ve(a){return a}function We(a,b,c,d){a=a.h?a.h(b):a.call(null,b);c=Mb(a,c,d);return a.h?a.h(c):a.call(null,c)}var Xe=function Xe(a){switch(arguments.length){case 0:return Xe.B();case 1:return Xe.h(arguments[0]);case 2:return Xe.c(arguments[0],arguments[1]);default:for(var c=[],d=arguments.length,e=0;;)if(e<d)c.push(arguments[e]),e+=1;else break;return Xe.A(arguments[0],arguments[1],new Jb(c.slice(2),0,null))}};Xe.B=function(){return 0};Xe.h=function(a){return a};
+Xe.c=function(a,b){return a+b};Xe.A=function(a,b,c){return Mb(Xe,a+b,c)};Xe.N=function(a){var b=y(a),c=z(a);a=y(c);c=z(c);return Xe.A(b,a,c)};Xe.L=2;var Ye=function Ye(a){switch(arguments.length){case 0:return Ye.B();case 1:return Ye.h(arguments[0]);case 2:return Ye.c(arguments[0],arguments[1]);default:for(var c=[],d=arguments.length,e=0;;)if(e<d)c.push(arguments[e]),e+=1;else break;return Ye.A(arguments[0],arguments[1],new Jb(c.slice(2),0,null))}};Ye.B=function(){return 1};Ye.h=function(a){return a};
+Ye.c=function(a,b){return a*b};Ye.A=function(a,b,c){return Mb(Ye,a*b,c)};Ye.N=function(a){var b=y(a),c=z(a);a=y(c);c=z(c);return Ye.A(b,a,c)};Ye.L=2;function Ze(a){a=(a-a%2)/2;return 0<=a?Math.floor(a):Math.ceil(a)}function $e(a){a-=a>>1&1431655765;a=(a&858993459)+(a>>2&858993459);return 16843009*(a+(a>>4)&252645135)>>24}
+var v=function v(a){switch(arguments.length){case 0:return v.B();case 1:return v.h(arguments[0]);default:for(var c=[],d=arguments.length,e=0;;)if(e<d)c.push(arguments[e]),e+=1;else break;return v.A(arguments[0],new Jb(c.slice(1),0,null))}};v.B=function(){return""};v.h=function(a){return null==a?"":""+a};v.A=function(a,b){for(var c=new cb(""+v.h(a)),d=b;;)if(t(d))c=c.append(""+v.h(y(d))),d=z(d);else return c.toString()};v.N=function(a){var b=y(a);a=z(a);return v.A(b,a)};v.L=1;
+function $d(a,b){if(we(b))if(Pd(a)&&Pd(b)&&H(a)!==H(b))var c=!1;else a:{c=E(a);for(var d=E(b);;){if(null==c){c=null==d;break a}if(null!=d&&G.c(y(c),y(d)))c=z(c),d=z(d);else{c=!1;break a}}}else c=null;return Ee(c)}function af(a,b,c,d,e){this.meta=a;this.first=b;this.kc=c;this.count=d;this.w=e;this.m=65937646;this.J=8192}g=af.prototype;g.toString=function(){return fd(this)};g.equiv=function(a){return this.K(null,a)};
+g.indexOf=function(){var a=null;a=function(a,c){switch(arguments.length){case 1:return Ud(this,a,0);case 2:return Ud(this,a,c)}throw Error("Invalid arity: "+(arguments.length-1));};a.h=function(a){return Ud(this,a,0)};a.c=function(a,c){return Ud(this,a,c)};return a}();
+g.lastIndexOf=function(){function a(a){return Xd(this,a,this.count)}var b=null;b=function(b,d){switch(arguments.length){case 1:return a.call(this,b);case 2:return Xd(this,b,d)}throw Error("Invalid arity: "+(arguments.length-1));};b.h=a;b.c=function(a,b){return Xd(this,a,b)};return b}();g.P=function(){return this.meta};g.Ka=function(){return 1===this.count?null:this.kc};g.W=function(){return this.count};g.Ac=function(){return this.first};g.Bc=function(){return this.bb(null)};
+g.U=function(){var a=this.w;return null!=a?a:this.w=a=Ad(this)};g.K=function(a,b){return $d(this,b)};g.oa=function(){return tc(wd,this.meta)};g.Fa=function(a,b){return ce(b,this)};g.Ga=function(a,b,c){return de(b,c,this)};g.Ia=function(){return this.first};g.bb=function(){return 1===this.count?wd:this.kc};g.S=function(){return this};g.T=function(a,b){return new af(b,this.first,this.kc,this.count,this.w)};g.X=function(a,b){return new af(this.meta,b,this,this.count+1,null)};af.prototype[Fb]=function(){return yd(this)};
+function bf(a){this.meta=a;this.m=65937614;this.J=8192}g=bf.prototype;g.toString=function(){return fd(this)};g.equiv=function(a){return this.K(null,a)};g.indexOf=function(){var a=null;a=function(a,c){switch(arguments.length){case 1:return Ud(this,a,0);case 2:return Ud(this,a,c)}throw Error("Invalid arity: "+(arguments.length-1));};a.h=function(a){return Ud(this,a,0)};a.c=function(a,c){return Ud(this,a,c)};return a}();
+g.lastIndexOf=function(){function a(a){return Xd(this,a,H(this))}var b=null;b=function(b,d){switch(arguments.length){case 1:return a.call(this,b);case 2:return Xd(this,b,d)}throw Error("Invalid arity: "+(arguments.length-1));};b.h=a;b.c=function(a,b){return Xd(this,a,b)};return b}();g.P=function(){return this.meta};g.Ka=function(){return null};g.W=function(){return 0};g.Ac=function(){return null};g.Bc=function(){throw Error("Can't pop empty list");};g.U=function(){return Cd};
+g.K=function(a,b){return(null!=b?b.m&33554432||q===b.Vf||(b.m?0:Ab(Fc,b)):Ab(Fc,b))||we(b)?null==E(b):!1};g.oa=function(){return this};g.Fa=function(a,b){return ce(b,this)};g.Ga=function(a,b,c){return de(b,c,this)};g.Ia=function(){return null};g.bb=function(){return wd};g.S=function(){return null};g.T=function(a,b){return new bf(b)};g.X=function(a,b){return new af(this.meta,b,null,1,null)};var wd=new bf(null);bf.prototype[Fb]=function(){return yd(this)};
+function cf(a){return(null!=a?a.m&134217728||q===a.Yf||(a.m?0:Ab(Hc,a)):Ab(Hc,a))?Ic(a):Mb(ge,wd,a)}var df=function df(a){for(var c=[],d=arguments.length,e=0;;)if(e<d)c.push(arguments[e]),e+=1;else break;return df.A(0<c.length?new Jb(c.slice(0),0,null):null)};df.A=function(a){if(a instanceof Jb&&0===a.i)var b=a.o;else a:for(b=[];;)if(null!=a)b.push(a.Ia(null)),a=a.Ka(null);else break a;a=b.length;for(var c=wd;;)if(0<a){var d=a-1;c=c.X(null,b[a-1]);a=d}else return c};df.L=0;df.N=function(a){return df.A(E(a))};
+function ef(a,b,c,d){this.meta=a;this.first=b;this.kc=c;this.w=d;this.m=65929452;this.J=8192}g=ef.prototype;g.toString=function(){return fd(this)};g.equiv=function(a){return this.K(null,a)};g.indexOf=function(){var a=null;a=function(a,c){switch(arguments.length){case 1:return Ud(this,a,0);case 2:return Ud(this,a,c)}throw Error("Invalid arity: "+(arguments.length-1));};a.h=function(a){return Ud(this,a,0)};a.c=function(a,c){return Ud(this,a,c)};return a}();
+g.lastIndexOf=function(){function a(a){return Xd(this,a,H(this))}var b=null;b=function(b,d){switch(arguments.length){case 1:return a.call(this,b);case 2:return Xd(this,b,d)}throw Error("Invalid arity: "+(arguments.length-1));};b.h=a;b.c=function(a,b){return Xd(this,a,b)};return b}();g.P=function(){return this.meta};g.Ka=function(){return null==this.kc?null:E(this.kc)};g.U=function(){var a=this.w;return null!=a?a:this.w=a=Ad(this)};g.K=function(a,b){return $d(this,b)};
+g.oa=function(){return tc(wd,this.meta)};g.Fa=function(a,b){return ce(b,this)};g.Ga=function(a,b,c){return de(b,c,this)};g.Ia=function(){return this.first};g.bb=function(){return null==this.kc?wd:this.kc};g.S=function(){return this};g.T=function(a,b){return new ef(b,this.first,this.kc,this.w)};g.X=function(a,b){return new ef(null,b,this,null)};ef.prototype[Fb]=function(){return yd(this)};function ae(a,b){return null==b||null!=b&&(b.m&64||q===b.G)?new ef(null,a,b,null):new ef(null,a,E(b),null)}
+function ff(a,b){if(a.ea===b.ea)return 0;var c=wb(a.fb);if(t(c?b.fb:c))return-1;if(t(a.fb)){if(wb(b.fb))return 1;c=Aa(a.fb,b.fb);return 0===c?Aa(a.name,b.name):c}return Aa(a.name,b.name)}function L(a,b,c,d){this.fb=a;this.name=b;this.ea=c;this.Oc=d;this.m=2153775105;this.J=4096}g=L.prototype;g.toString=function(){return[":",v.h(this.ea)].join("")};g.equiv=function(a){return this.K(null,a)};g.K=function(a,b){return b instanceof L?this.ea===b.ea:!1};
+g.call=function(){var a=null;a=function(a,c,d){switch(arguments.length){case 2:return D.c(c,this);case 3:return D.l(c,this,d)}throw Error("Invalid arity: "+(arguments.length-1));};a.c=function(a,c){return D.c(c,this)};a.l=function(a,c,d){return D.l(c,this,d)};return a}();g.apply=function(a,b){return this.call.apply(this,[this].concat(Gb(b)))};g.h=function(a){return D.c(a,this)};g.c=function(a,b){return D.l(a,this,b)};
+g.U=function(){var a=this.Oc;return null!=a?a:this.Oc=a=pd(kd(this.name),nd(this.fb))+2654435769|0};g.hd=function(){return this.name};g.jd=function(){return this.fb};g.R=function(a,b){return Jc(b,[":",v.h(this.ea)].join(""))};function gf(a){return a instanceof L}function N(a,b){return a===b?!0:a instanceof L&&b instanceof L?a.ea===b.ea:!1}
+var hf=function hf(a){switch(arguments.length){case 1:return hf.h(arguments[0]);case 2:return hf.c(arguments[0],arguments[1]);default:throw Error(["Invalid arity: ",v.h(arguments.length)].join(""));}};
+hf.h=function(a){if(a instanceof L)return a;if(a instanceof rd){if(null!=a&&(a.J&4096||q===a.Oe))var b=a.jd(null);else throw Error(["Doesn't support namespace: ",v.h(a)].join(""));return new L(b,jf(a),a.Zb,null)}return"string"===typeof a?(b=a.split("/"),2===b.length?new L(b[0],b[1],a,null):new L(null,b[0],a,null)):null};
+hf.c=function(a,b){var c=a instanceof L?jf(a):a instanceof rd?jf(a):a,d=b instanceof L?jf(b):b instanceof rd?jf(b):b;return new L(c,d,[v.h(t(c)?[v.h(c),"/"].join(""):null),v.h(d)].join(""),null)};hf.L=2;function kf(a,b,c,d){this.meta=a;this.Vc=b;this.s=c;this.w=d;this.m=32374988;this.J=1}g=kf.prototype;g.toString=function(){return fd(this)};g.equiv=function(a){return this.K(null,a)};function lf(a){null!=a.Vc&&(a.s=a.Vc.B?a.Vc.B():a.Vc.call(null),a.Vc=null);return a.s}
+g.indexOf=function(){var a=null;a=function(a,c){switch(arguments.length){case 1:return Ud(this,a,0);case 2:return Ud(this,a,c)}throw Error("Invalid arity: "+(arguments.length-1));};a.h=function(a){return Ud(this,a,0)};a.c=function(a,c){return Ud(this,a,c)};return a}();
+g.lastIndexOf=function(){function a(a){return Xd(this,a,H(this))}var b=null;b=function(b,d){switch(arguments.length){case 1:return a.call(this,b);case 2:return Xd(this,b,d)}throw Error("Invalid arity: "+(arguments.length-1));};b.h=a;b.c=function(a,b){return Xd(this,a,b)};return b}();g.P=function(){return this.meta};g.Ka=function(){this.S(null);return null==this.s?null:z(this.s)};g.U=function(){var a=this.w;return null!=a?a:this.w=a=Ad(this)};g.K=function(a,b){return $d(this,b)};
+g.oa=function(){return tc(wd,this.meta)};g.Fa=function(a,b){return ce(b,this)};g.Ga=function(a,b,c){return de(b,c,this)};g.Ia=function(){this.S(null);return null==this.s?null:y(this.s)};g.bb=function(){this.S(null);return null!=this.s?vd(this.s):wd};g.S=function(){lf(this);if(null==this.s)return null;for(var a=this.s;;)if(a instanceof kf)a=lf(a);else return this.s=a,E(this.s)};g.T=function(a,b){return new kf(b,this.Vc,this.s,this.w)};g.X=function(a,b){return ae(b,this)};kf.prototype[Fb]=function(){return yd(this)};
+function mf(a,b){this.aa=a;this.end=b;this.m=2;this.J=0}mf.prototype.add=function(a){this.aa[this.end]=a;return this.end+=1};mf.prototype.Da=function(){var a=new nf(this.aa,0,this.end);this.aa=null;return a};mf.prototype.W=function(){return this.end};function of(a){return new mf(Array(a),0)}function nf(a,b,c){this.o=a;this.ab=b;this.end=c;this.m=524306;this.J=0}g=nf.prototype;g.W=function(){return this.end-this.ab};g.$=function(a,b){return this.o[this.ab+b]};
+g.ka=function(a,b,c){return 0<=b&&b<this.end-this.ab?this.o[this.ab+b]:c};g.Le=function(){if(this.ab===this.end)throw Error("-drop-first of empty chunk");return new nf(this.o,this.ab+1,this.end)};g.Fa=function(a,b){return Od(this.o,b,this.o[this.ab],this.ab+1)};g.Ga=function(a,b,c){return Od(this.o,b,c,this.ab)};function pf(a,b,c,d){this.Da=a;this.Wb=b;this.meta=c;this.w=d;this.m=31850732;this.J=1536}g=pf.prototype;g.toString=function(){return fd(this)};g.equiv=function(a){return this.K(null,a)};
+g.indexOf=function(){var a=null;a=function(a,c){switch(arguments.length){case 1:return Ud(this,a,0);case 2:return Ud(this,a,c)}throw Error("Invalid arity: "+(arguments.length-1));};a.h=function(a){return Ud(this,a,0)};a.c=function(a,c){return Ud(this,a,c)};return a}();
+g.lastIndexOf=function(){function a(a){return Xd(this,a,H(this))}var b=null;b=function(b,d){switch(arguments.length){case 1:return a.call(this,b);case 2:return Xd(this,b,d)}throw Error("Invalid arity: "+(arguments.length-1));};b.h=a;b.c=function(a,b){return Xd(this,a,b)};return b}();g.P=function(){return this.meta};g.Ka=function(){if(1<Qb(this.Da))return new pf(Vc(this.Da),this.Wb,this.meta,null);var a=Cc(this.Wb);return null==a?null:a};g.U=function(){var a=this.w;return null!=a?a:this.w=a=Ad(this)};
+g.K=function(a,b){return $d(this,b)};g.oa=function(){return tc(wd,this.meta)};g.Ia=function(){return A.c(this.Da,0)};g.bb=function(){return 1<Qb(this.Da)?new pf(Vc(this.Da),this.Wb,this.meta,null):null==this.Wb?wd:this.Wb};g.S=function(){return this};g.ge=function(){return this.Da};g.Hd=function(){return null==this.Wb?wd:this.Wb};g.T=function(a,b){return new pf(this.Da,this.Wb,b,this.w)};g.X=function(a,b){return ae(b,this)};g.Me=function(){return null==this.Wb?null:this.Wb};pf.prototype[Fb]=function(){return yd(this)};
+function qf(a,b){return 0===Qb(a)?b:new pf(a,b,null,null)}function rf(a,b){a.add(b)}function sf(a,b){if(Pd(b))return H(b);for(var c=0,d=E(b);;)if(null!=d&&c<a)c+=1,d=z(d);else return c}
+var tf=function tf(a){if(null==a)return null;var c=z(a);return null==c?E(y(a)):ae(y(a),tf.h?tf.h(c):tf.call(null,c))},O=function O(a){switch(arguments.length){case 0:return O.B();case 1:return O.h(arguments[0]);case 2:return O.c(arguments[0],arguments[1]);default:for(var c=[],d=arguments.length,e=0;;)if(e<d)c.push(arguments[e]),e+=1;else break;return O.A(arguments[0],arguments[1],new Jb(c.slice(2),0,null))}};O.B=function(){return new kf(null,function(){return null},null,null)};
+O.h=function(a){return new kf(null,function(){return a},null,null)};O.c=function(a,b){return new kf(null,function(){var c=E(a);return c?Ae(c)?qf(Wc(c),O.c(Xc(c),b)):ae(y(c),O.c(vd(c),b)):b},null,null)};O.A=function(a,b,c){return function h(a,b){return new kf(null,function(){var c=E(a);return c?Ae(c)?qf(Wc(c),h(Xc(c),b)):ae(y(c),h(vd(c),b)):t(b)?h(y(b),z(b)):null},null,null)}(O.c(a,b),c)};O.N=function(a){var b=y(a),c=z(a);a=y(c);c=z(c);return O.A(b,a,c)};O.L=2;
+var uf=function uf(a){switch(arguments.length){case 0:return uf.B();case 1:return uf.h(arguments[0]);case 2:return uf.c(arguments[0],arguments[1]);default:for(var c=[],d=arguments.length,e=0;;)if(e<d)c.push(arguments[e]),e+=1;else break;return uf.A(arguments[0],arguments[1],new Jb(c.slice(2),0,null))}};uf.B=function(){return Oc(he)};uf.h=function(a){return a};uf.c=function(a,b){return Pc(a,b)};uf.A=function(a,b,c){for(;;)if(a=Pc(a,b),t(c))b=y(c),c=z(c);else return a};
+uf.N=function(a){var b=y(a),c=z(a);a=y(c);c=z(c);return uf.A(b,a,c)};uf.L=2;
+function vf(a,b,c){var d=E(c);if(0===b)return a.B?a.B():a.call(null);c=Wb(d);var e=Yb(d);if(1===b)return a.h?a.h(c):a.call(null,c);d=Wb(e);var f=Yb(e);if(2===b)return a.c?a.c(c,d):a.call(null,c,d);e=Wb(f);var h=Yb(f);if(3===b)return a.l?a.l(c,d,e):a.call(null,c,d,e);f=Wb(h);var k=Yb(h);if(4===b)return a.M?a.M(c,d,e,f):a.call(null,c,d,e,f);h=Wb(k);var l=Yb(k);if(5===b)return a.Z?a.Z(c,d,e,f,h):a.call(null,c,d,e,f,h);k=Wb(l);var p=Yb(l);if(6===b)return a.Ca?a.Ca(c,d,e,f,h,k):a.call(null,c,d,e,f,h,k);
+l=Wb(p);var m=Yb(p);if(7===b)return a.Ya?a.Ya(c,d,e,f,h,k,l):a.call(null,c,d,e,f,h,k,l);p=Wb(m);var u=Yb(m);if(8===b)return a.Ha?a.Ha(c,d,e,f,h,k,l,p):a.call(null,c,d,e,f,h,k,l,p);m=Wb(u);var w=Yb(u);if(9===b)return a.Za?a.Za(c,d,e,f,h,k,l,p,m):a.call(null,c,d,e,f,h,k,l,p,m);u=Wb(w);var x=Yb(w);if(10===b)return a.Na?a.Na(c,d,e,f,h,k,l,p,m,u):a.call(null,c,d,e,f,h,k,l,p,m,u);w=Wb(x);var C=Yb(x);if(11===b)return a.Oa?a.Oa(c,d,e,f,h,k,l,p,m,u,w):a.call(null,c,d,e,f,h,k,l,p,m,u,w);x=Wb(C);var F=Yb(C);
+if(12===b)return a.Pa?a.Pa(c,d,e,f,h,k,l,p,m,u,w,x):a.call(null,c,d,e,f,h,k,l,p,m,u,w,x);C=Wb(F);var I=Yb(F);if(13===b)return a.Qa?a.Qa(c,d,e,f,h,k,l,p,m,u,w,x,C):a.call(null,c,d,e,f,h,k,l,p,m,u,w,x,C);F=Wb(I);var M=Yb(I);if(14===b)return a.Ra?a.Ra(c,d,e,f,h,k,l,p,m,u,w,x,C,F):a.call(null,c,d,e,f,h,k,l,p,m,u,w,x,C,F);I=Wb(M);var S=Yb(M);if(15===b)return a.Sa?a.Sa(c,d,e,f,h,k,l,p,m,u,w,x,C,F,I):a.call(null,c,d,e,f,h,k,l,p,m,u,w,x,C,F,I);M=Wb(S);var X=Yb(S);if(16===b)return a.Ta?a.Ta(c,d,e,f,h,k,l,
+p,m,u,w,x,C,F,I,M):a.call(null,c,d,e,f,h,k,l,p,m,u,w,x,C,F,I,M);S=Wb(X);var Ga=Yb(X);if(17===b)return a.Ua?a.Ua(c,d,e,f,h,k,l,p,m,u,w,x,C,F,I,M,S):a.call(null,c,d,e,f,h,k,l,p,m,u,w,x,C,F,I,M,S);X=Wb(Ga);var db=Yb(Ga);if(18===b)return a.Va?a.Va(c,d,e,f,h,k,l,p,m,u,w,x,C,F,I,M,S,X):a.call(null,c,d,e,f,h,k,l,p,m,u,w,x,C,F,I,M,S,X);Ga=Wb(db);db=Yb(db);if(19===b)return a.Wa?a.Wa(c,d,e,f,h,k,l,p,m,u,w,x,C,F,I,M,S,X,Ga):a.call(null,c,d,e,f,h,k,l,p,m,u,w,x,C,F,I,M,S,X,Ga);var Q=Wb(db);Yb(db);if(20===b)return a.Xa?
+a.Xa(c,d,e,f,h,k,l,p,m,u,w,x,C,F,I,M,S,X,Ga,Q):a.call(null,c,d,e,f,h,k,l,p,m,u,w,x,C,F,I,M,S,X,Ga,Q);throw Error("Only up to 20 arguments supported on functions");}function wf(a,b,c){return null==c?a.h?a.h(b):a.call(a,b):xf(a,b,Wb(c),z(c))}function xf(a,b,c,d){return null==d?a.c?a.c(b,c):a.call(a,b,c):yf(a,b,c,Wb(d),z(d))}function yf(a,b,c,d,e){return null==e?a.l?a.l(b,c,d):a.call(a,b,c,d):zf(a,b,c,d,Wb(e),z(e))}
+function zf(a,b,c,d,e,f){if(null==f)return a.M?a.M(b,c,d,e):a.call(a,b,c,d,e);var h=Wb(f),k=z(f);if(null==k)return a.Z?a.Z(b,c,d,e,h):a.call(a,b,c,d,e,h);f=Wb(k);var l=z(k);if(null==l)return a.Ca?a.Ca(b,c,d,e,h,f):a.call(a,b,c,d,e,h,f);k=Wb(l);var p=z(l);if(null==p)return a.Ya?a.Ya(b,c,d,e,h,f,k):a.call(a,b,c,d,e,h,f,k);l=Wb(p);var m=z(p);if(null==m)return a.Ha?a.Ha(b,c,d,e,h,f,k,l):a.call(a,b,c,d,e,h,f,k,l);p=Wb(m);var u=z(m);if(null==u)return a.Za?a.Za(b,c,d,e,h,f,k,l,p):a.call(a,b,c,d,e,h,f,k,
+l,p);m=Wb(u);var w=z(u);if(null==w)return a.Na?a.Na(b,c,d,e,h,f,k,l,p,m):a.call(a,b,c,d,e,h,f,k,l,p,m);u=Wb(w);var x=z(w);if(null==x)return a.Oa?a.Oa(b,c,d,e,h,f,k,l,p,m,u):a.call(a,b,c,d,e,h,f,k,l,p,m,u);w=Wb(x);var C=z(x);if(null==C)return a.Pa?a.Pa(b,c,d,e,h,f,k,l,p,m,u,w):a.call(a,b,c,d,e,h,f,k,l,p,m,u,w);x=Wb(C);var F=z(C);if(null==F)return a.Qa?a.Qa(b,c,d,e,h,f,k,l,p,m,u,w,x):a.call(a,b,c,d,e,h,f,k,l,p,m,u,w,x);C=Wb(F);var I=z(F);if(null==I)return a.Ra?a.Ra(b,c,d,e,h,f,k,l,p,m,u,w,x,C):a.call(a,
+b,c,d,e,h,f,k,l,p,m,u,w,x,C);F=Wb(I);var M=z(I);if(null==M)return a.Sa?a.Sa(b,c,d,e,h,f,k,l,p,m,u,w,x,C,F):a.call(a,b,c,d,e,h,f,k,l,p,m,u,w,x,C,F);I=Wb(M);var S=z(M);if(null==S)return a.Ta?a.Ta(b,c,d,e,h,f,k,l,p,m,u,w,x,C,F,I):a.call(a,b,c,d,e,h,f,k,l,p,m,u,w,x,C,F,I);M=Wb(S);var X=z(S);if(null==X)return a.Ua?a.Ua(b,c,d,e,h,f,k,l,p,m,u,w,x,C,F,I,M):a.call(a,b,c,d,e,h,f,k,l,p,m,u,w,x,C,F,I,M);S=Wb(X);var Ga=z(X);if(null==Ga)return a.Va?a.Va(b,c,d,e,h,f,k,l,p,m,u,w,x,C,F,I,M,S):a.call(a,b,c,d,e,h,f,
+k,l,p,m,u,w,x,C,F,I,M,S);X=Wb(Ga);var db=z(Ga);if(null==db)return a.Wa?a.Wa(b,c,d,e,h,f,k,l,p,m,u,w,x,C,F,I,M,S,X):a.call(a,b,c,d,e,h,f,k,l,p,m,u,w,x,C,F,I,M,S,X);Ga=Wb(db);db=z(db);if(null==db)return a.Xa?a.Xa(b,c,d,e,h,f,k,l,p,m,u,w,x,C,F,I,M,S,X,Ga):a.call(a,b,c,d,e,h,f,k,l,p,m,u,w,x,C,F,I,M,S,X,Ga);b=[b,c,d,e,h,f,k,l,p,m,u,w,x,C,F,I,M,S,X,Ga];for(c=db;;)if(c)b.push(Wb(c)),c=z(c);else break;return a.apply(a,b)}
+function P(a,b){if(a.N){var c=a.L,d=sf(c+1,b);return d<=c?vf(a,d,b):a.N(b)}c=E(b);return null==c?a.B?a.B():a.call(a):wf(a,Wb(c),z(c))}function Kb(a,b,c){if(a.N){b=ae(b,c);var d=a.L;c=sf(d,c)+1;return c<=d?vf(a,c,b):a.N(b)}return wf(a,b,E(c))}function Af(a,b,c,d,e){return a.N?(b=ae(b,ae(c,ae(d,e))),c=a.L,e=3+sf(c-2,e),e<=c?vf(a,e,b):a.N(b)):yf(a,b,c,d,E(e))}function oe(a,b,c,d,e,f){return a.N?(f=tf(f),b=ae(b,ae(c,ae(d,ae(e,f)))),c=a.L,f=4+sf(c-3,f),f<=c?vf(a,f,b):a.N(b)):zf(a,b,c,d,e,tf(f))}
+function Bf(a){return E(a)?a:null}
+function Cf(){"undefined"===typeof hb&&(hb=function(a){this.zf=a;this.m=393216;this.J=0},hb.prototype.T=function(a,b){return new hb(b)},hb.prototype.P=function(){return this.zf},hb.prototype.ja=function(){return!1},hb.prototype.next=function(){return Error("No such element")},hb.prototype.remove=function(){return Error("Unsupported operation")},hb.Wc=function(){return new R(null,1,5,T,[Df],null)},hb.qc=!0,hb.Tb="cljs.core/t_cljs$core34616",hb.Ec=function(a,b){return Jc(b,"cljs.core/t_cljs$core34616")});
+return new hb(Ef)}function Ff(a,b){this.s=a;this.i=b}Ff.prototype.ja=function(){return this.i<this.s.length};Ff.prototype.next=function(){var a=this.s.charAt(this.i);this.i+=1;return a};Ff.prototype.remove=function(){return Error("Unsupported operation")};function Gf(a,b){this.o=a;this.i=b}Gf.prototype.ja=function(){return this.i<this.o.length};Gf.prototype.next=function(){var a=this.o[this.i];this.i+=1;return a};Gf.prototype.remove=function(){return Error("Unsupported operation")};var Hf={},If={};
+function Jf(a,b){this.cd=a;this.ub=b}Jf.prototype.ja=function(){this.cd===Hf?(this.cd=If,this.ub=E(this.ub)):this.cd===this.ub&&(this.ub=z(this.cd));return null!=this.ub};Jf.prototype.next=function(){if(this.ja())return this.cd=this.ub,y(this.ub);throw Error("No such element");};Jf.prototype.remove=function(){return Error("Unsupported operation")};
+function Kf(a){if(ud(a))return dd(a);if(null==a)return Cf();if("string"===typeof a)return new Ff(a,0);if(vb(a))return new Gf(a,0);if((null!=a?a.m&8388608||q===a.Pe||(a.m?0:Ab(Bc,a)):Ab(Bc,a))||vb(a)||"string"===typeof a)return new Jf(Hf,a);throw Error(["Cannot create iterator from ",v.h(a)].join(""));}function Mf(a){this.ae=a}Mf.prototype.add=function(a){this.ae.push(a);return this};Mf.prototype.remove=function(){return this.ae.shift()};Mf.prototype.Td=function(){return 0===this.ae.length};
+Mf.prototype.toString=function(){return["Many: ",v.h(this.ae)].join("")};var Nf={};function Of(a){this.H=a}Of.prototype.add=function(a){return this.H===Nf?(this.H=a,this):new Mf([this.H,a])};Of.prototype.remove=function(){if(this.H===Nf)throw Error("Removing object from empty buffer");var a=this.H;this.H=Nf;return a};Of.prototype.Td=function(){return this.H===Nf};Of.prototype.toString=function(){return["Single: ",v.h(this.H)].join("")};function Pf(){}Pf.prototype.add=function(a){return new Of(a)};
+Pf.prototype.remove=function(){throw Error("Removing object from empty buffer");};Pf.prototype.Td=function(){return!0};Pf.prototype.toString=function(){return"Empty"};var Qf=new Pf,Rf=function Rf(a){return new kf(null,function(){if(a.ja())for(var c=[],d=0;;){var e=a.ja();if(t(t(e)?32>d:e))c[d]=a.next(),d+=1;else return qf(new nf(c,0,d),Rf.h?Rf.h(a):Rf.call(null,a))}else return null},null,null)};function Sf(a,b,c,d,e,f){this.buffer=a;this.ub=b;this.pe=c;this.Rb=d;this.ye=e;this.Gf=f}
+Sf.prototype.step=function(){if(this.ub!==Nf)return!0;for(;;)if(this.ub===Nf)if(this.buffer.Td()){if(this.pe)return!1;if(this.ye.ja()){if(this.Gf)var a=P(this.Rb,ae(null,this.ye.next()));else a=this.ye.next(),a=this.Rb.c?this.Rb.c(null,a):this.Rb.call(null,null,a);Hd(a)&&(this.Rb.h?this.Rb.h(null):this.Rb.call(null,null),this.pe=!0)}else this.Rb.h?this.Rb.h(null):this.Rb.call(null,null),this.pe=!0}else this.ub=this.buffer.remove();else return!0};Sf.prototype.ja=function(){return this.step()};
+Sf.prototype.next=function(){if(this.ja()){var a=this.ub;this.ub=Nf;return a}throw Error("No such element");};Sf.prototype.remove=function(){return Error("Unsupported operation")};Sf.prototype[Fb]=function(){return yd(this)};
+function Tf(a,b){var c=new Sf(Qf,Nf,!1,null,b,!1);c.Rb=function(){var b=function(a){return function(){function b(b,c){a.buffer=a.buffer.add(c);return b}var c=null;c=function(a,c){switch(arguments.length){case 0:return null;case 1:return a;case 2:return b.call(this,a,c)}throw Error("Invalid arity: "+(arguments.length-1));};c.B=function(){return null};c.h=function(a){return a};c.c=b;return c}()}(c);return a.h?a.h(b):a.call(null,b)}();return c}
+function Uf(a,b){var c=Kf(b);c=Tf(a,c);c=Rf(c);return t(c)?c:wd}function Vf(a,b){for(;;){if(null==E(b))return!0;var c=y(b);c=a.h?a.h(c):a.call(null,c);if(t(c)){c=a;var d=z(b);a=c;b=d}else return!1}}function Wf(a,b){for(;;)if(E(b)){var c=y(b);c=a.h?a.h(c):a.call(null,c);if(t(c))return c;c=a;var d=z(b);a=c;b=d}else return null}function Xf(a){if(Ge(a))return 0===(a&1);throw Error(["Argument must be an integer: ",v.h(a)].join(""));}
+function Yf(a){return function(){function b(b,c){return wb(a.c?a.c(b,c):a.call(null,b,c))}function c(b){return wb(a.h?a.h(b):a.call(null,b))}function d(){return wb(a.B?a.B():a.call(null))}var e=null,f=function(){function b(a,b,d){var e=null;if(2<arguments.length){e=0;for(var f=Array(arguments.length-2);e<f.length;)f[e]=arguments[e+2],++e;e=new Jb(f,0,null)}return c.call(this,a,b,e)}function c(b,c,d){a.N?(b=ae(b,ae(c,d)),c=a.L,d=2+sf(c-1,d),d=d<=c?vf(a,d,b):a.N(b)):d=xf(a,b,c,E(d));return wb(d)}b.L=
+2;b.N=function(a){var b=y(a);a=z(a);var d=y(a);a=vd(a);return c(b,d,a)};b.A=c;return b}();e=function(a,e,l){switch(arguments.length){case 0:return d.call(this);case 1:return c.call(this,a);case 2:return b.call(this,a,e);default:var h=null;if(2<arguments.length){h=0;for(var k=Array(arguments.length-2);h<k.length;)k[h]=arguments[h+2],++h;h=new Jb(k,0,null)}return f.A(a,e,h)}throw Error("Invalid arity: "+(arguments.length-1));};e.L=2;e.N=f.N;e.B=d;e.h=c;e.c=b;e.A=f.A;return e}()}
+function Zf(a){return function(){function b(b){if(0<arguments.length)for(var c=0,e=Array(arguments.length-0);c<e.length;)e[c]=arguments[c+0],++c;return a}b.L=0;b.N=function(b){E(b);return a};b.A=function(){return a};return b}()}
+var $f=function $f(a){switch(arguments.length){case 0:return $f.B();case 1:return $f.h(arguments[0]);case 2:return $f.c(arguments[0],arguments[1]);case 3:return $f.l(arguments[0],arguments[1],arguments[2]);default:for(var c=[],d=arguments.length,e=0;;)if(e<d)c.push(arguments[e]),e+=1;else break;return $f.A(arguments[0],arguments[1],arguments[2],new Jb(c.slice(3),0,null))}};$f.B=function(){return Ve};$f.h=function(a){return a};
+$f.c=function(a,b){return function(){function c(c,d,e){c=b.l?b.l(c,d,e):b.call(null,c,d,e);return a.h?a.h(c):a.call(null,c)}function d(c,d){var e=b.c?b.c(c,d):b.call(null,c,d);return a.h?a.h(e):a.call(null,e)}function e(c){c=b.h?b.h(c):b.call(null,c);return a.h?a.h(c):a.call(null,c)}function f(){var c=b.B?b.B():b.call(null);return a.h?a.h(c):a.call(null,c)}var h=null,k=function(){function c(a,b,c,e){var f=null;if(3<arguments.length){f=0;for(var h=Array(arguments.length-3);f<h.length;)h[f]=arguments[f+
+3],++f;f=new Jb(h,0,null)}return d.call(this,a,b,c,f)}function d(c,d,e,f){c=Af(b,c,d,e,f);return a.h?a.h(c):a.call(null,c)}c.L=3;c.N=function(a){var b=y(a);a=z(a);var c=y(a);a=z(a);var e=y(a);a=vd(a);return d(b,c,e,a)};c.A=d;return c}();h=function(a,b,h,u){switch(arguments.length){case 0:return f.call(this);case 1:return e.call(this,a);case 2:return d.call(this,a,b);case 3:return c.call(this,a,b,h);default:var m=null;if(3<arguments.length){m=0;for(var p=Array(arguments.length-3);m<p.length;)p[m]=
+arguments[m+3],++m;m=new Jb(p,0,null)}return k.A(a,b,h,m)}throw Error("Invalid arity: "+(arguments.length-1));};h.L=3;h.N=k.N;h.B=f;h.h=e;h.c=d;h.l=c;h.A=k.A;return h}()};
+$f.l=function(a,b,c){return function(){function d(d,e,f){d=c.l?c.l(d,e,f):c.call(null,d,e,f);d=b.h?b.h(d):b.call(null,d);return a.h?a.h(d):a.call(null,d)}function e(d,e){var f=c.c?c.c(d,e):c.call(null,d,e);f=b.h?b.h(f):b.call(null,f);return a.h?a.h(f):a.call(null,f)}function f(d){d=c.h?c.h(d):c.call(null,d);d=b.h?b.h(d):b.call(null,d);return a.h?a.h(d):a.call(null,d)}function h(){var d=c.B?c.B():c.call(null);d=b.h?b.h(d):b.call(null,d);return a.h?a.h(d):a.call(null,d)}var k=null,l=function(){function d(a,
+b,c,d){var f=null;if(3<arguments.length){f=0;for(var h=Array(arguments.length-3);f<h.length;)h[f]=arguments[f+3],++f;f=new Jb(h,0,null)}return e.call(this,a,b,c,f)}function e(d,e,f,h){d=Af(c,d,e,f,h);d=b.h?b.h(d):b.call(null,d);return a.h?a.h(d):a.call(null,d)}d.L=3;d.N=function(a){var b=y(a);a=z(a);var c=y(a);a=z(a);var d=y(a);a=vd(a);return e(b,c,d,a)};d.A=e;return d}();k=function(a,b,c,k){switch(arguments.length){case 0:return h.call(this);case 1:return f.call(this,a);case 2:return e.call(this,
+a,b);case 3:return d.call(this,a,b,c);default:var m=null;if(3<arguments.length){m=0;for(var p=Array(arguments.length-3);m<p.length;)p[m]=arguments[m+3],++m;m=new Jb(p,0,null)}return l.A(a,b,c,m)}throw Error("Invalid arity: "+(arguments.length-1));};k.L=3;k.N=l.N;k.B=h;k.h=f;k.c=e;k.l=d;k.A=l.A;return k}()};
+$f.A=function(a,b,c,d){return function(a){return function(){function b(a){var b=null;if(0<arguments.length){b=0;for(var d=Array(arguments.length-0);b<d.length;)d[b]=arguments[b+0],++b;b=new Jb(d,0,null)}return c.call(this,b)}function c(b){b=P(y(a),b);for(var c=z(a);;)if(c){var d=y(c);b=d.h?d.h(b):d.call(null,b);c=z(c)}else return b}b.L=0;b.N=function(a){a=E(a);return c(a)};b.A=c;return b}()}(cf(ae(a,ae(b,ae(c,d)))))};
+$f.N=function(a){var b=y(a),c=z(a);a=y(c);var d=z(c);c=y(d);d=z(d);return $f.A(b,a,c,d)};$f.L=3;
+var ag=function ag(a){switch(arguments.length){case 1:return ag.h(arguments[0]);case 2:return ag.c(arguments[0],arguments[1]);case 3:return ag.l(arguments[0],arguments[1],arguments[2]);case 4:return ag.M(arguments[0],arguments[1],arguments[2],arguments[3]);default:for(var c=[],d=arguments.length,e=0;;)if(e<d)c.push(arguments[e]),e+=1;else break;return ag.A(arguments[0],arguments[1],arguments[2],arguments[3],new Jb(c.slice(4),0,null))}};ag.h=function(a){return a};
+ag.c=function(a,b){return function(){function c(c,d,e){return a.M?a.M(b,c,d,e):a.call(null,b,c,d,e)}function d(c,d){return a.l?a.l(b,c,d):a.call(null,b,c,d)}function e(c){return a.c?a.c(b,c):a.call(null,b,c)}function f(){return a.h?a.h(b):a.call(null,b)}var h=null,k=function(){function c(a,b,c,e){var f=null;if(3<arguments.length){f=0;for(var h=Array(arguments.length-3);f<h.length;)h[f]=arguments[f+3],++f;f=new Jb(h,0,null)}return d.call(this,a,b,c,f)}function d(c,d,e,f){return oe(a,b,c,d,e,be([f]))}
+c.L=3;c.N=function(a){var b=y(a);a=z(a);var c=y(a);a=z(a);var e=y(a);a=vd(a);return d(b,c,e,a)};c.A=d;return c}();h=function(a,b,h,u){switch(arguments.length){case 0:return f.call(this);case 1:return e.call(this,a);case 2:return d.call(this,a,b);case 3:return c.call(this,a,b,h);default:var m=null;if(3<arguments.length){m=0;for(var l=Array(arguments.length-3);m<l.length;)l[m]=arguments[m+3],++m;m=new Jb(l,0,null)}return k.A(a,b,h,m)}throw Error("Invalid arity: "+(arguments.length-1));};h.L=3;h.N=k.N;
+h.B=f;h.h=e;h.c=d;h.l=c;h.A=k.A;return h}()};
+ag.l=function(a,b,c){return function(){function d(d,e,f){return a.Z?a.Z(b,c,d,e,f):a.call(null,b,c,d,e,f)}function e(d,e){return a.M?a.M(b,c,d,e):a.call(null,b,c,d,e)}function f(d){return a.l?a.l(b,c,d):a.call(null,b,c,d)}function h(){return a.c?a.c(b,c):a.call(null,b,c)}var k=null,l=function(){function d(a,b,c,d){var f=null;if(3<arguments.length){f=0;for(var h=Array(arguments.length-3);f<h.length;)h[f]=arguments[f+3],++f;f=new Jb(h,0,null)}return e.call(this,a,b,c,f)}function e(d,e,f,h){return oe(a,
+b,c,d,e,be([f,h]))}d.L=3;d.N=function(a){var b=y(a);a=z(a);var c=y(a);a=z(a);var d=y(a);a=vd(a);return e(b,c,d,a)};d.A=e;return d}();k=function(a,b,c,k){switch(arguments.length){case 0:return h.call(this);case 1:return f.call(this,a);case 2:return e.call(this,a,b);case 3:return d.call(this,a,b,c);default:var m=null;if(3<arguments.length){m=0;for(var p=Array(arguments.length-3);m<p.length;)p[m]=arguments[m+3],++m;m=new Jb(p,0,null)}return l.A(a,b,c,m)}throw Error("Invalid arity: "+(arguments.length-
+1));};k.L=3;k.N=l.N;k.B=h;k.h=f;k.c=e;k.l=d;k.A=l.A;return k}()};
+ag.M=function(a,b,c,d){return function(){function e(e,f,h){return a.Ca?a.Ca(b,c,d,e,f,h):a.call(null,b,c,d,e,f,h)}function f(e,f){return a.Z?a.Z(b,c,d,e,f):a.call(null,b,c,d,e,f)}function h(e){return a.M?a.M(b,c,d,e):a.call(null,b,c,d,e)}function k(){return a.l?a.l(b,c,d):a.call(null,b,c,d)}var l=null,p=function(){function e(a,b,c,d){var e=null;if(3<arguments.length){e=0;for(var h=Array(arguments.length-3);e<h.length;)h[e]=arguments[e+3],++e;e=new Jb(h,0,null)}return f.call(this,a,b,c,e)}function f(e,
+f,h,k){return oe(a,b,c,d,e,be([f,h,k]))}e.L=3;e.N=function(a){var b=y(a);a=z(a);var c=y(a);a=z(a);var d=y(a);a=vd(a);return f(b,c,d,a)};e.A=f;return e}();l=function(a,b,c,d){switch(arguments.length){case 0:return k.call(this);case 1:return h.call(this,a);case 2:return f.call(this,a,b);case 3:return e.call(this,a,b,c);default:var m=null;if(3<arguments.length){m=0;for(var l=Array(arguments.length-3);m<l.length;)l[m]=arguments[m+3],++m;m=new Jb(l,0,null)}return p.A(a,b,c,m)}throw Error("Invalid arity: "+
+(arguments.length-1));};l.L=3;l.N=p.N;l.B=k;l.h=h;l.c=f;l.l=e;l.A=p.A;return l}()};ag.A=function(a,b,c,d,e){return function(){function f(a){var b=null;if(0<arguments.length){b=0;for(var c=Array(arguments.length-0);b<c.length;)c[b]=arguments[b+0],++b;b=new Jb(c,0,null)}return h.call(this,b)}function h(f){return Af(a,b,c,d,O.c(e,f))}f.L=0;f.N=function(a){a=E(a);return h(a)};f.A=h;return f}()};ag.N=function(a){var b=y(a),c=z(a);a=y(c);var d=z(c);c=y(d);var e=z(d);d=y(e);e=z(e);return ag.A(b,a,c,d,e)};
+ag.L=4;function bg(a,b){return function f(b,e){return new kf(null,function(){var d=E(e);if(d){if(Ae(d)){for(var k=Wc(d),l=H(k),p=of(l),m=0;;)if(m<l)rf(p,function(){var d=b+m,e=A.c(k,m);return a.c?a.c(d,e):a.call(null,d,e)}()),m+=1;else break;return qf(p.Da(),f(b+l,Xc(d)))}return ae(function(){var e=y(d);return a.c?a.c(b,e):a.call(null,b,e)}(),f(b+1,vd(d)))}return null},null,null)}(0,b)}function cg(a,b,c,d){this.state=a;this.meta=b;this.df=c;this.gb=d;this.J=16386;this.m=6455296}g=cg.prototype;
+g.equiv=function(a){return this.K(null,a)};g.K=function(a,b){return this===b};g.pc=function(){return this.state};g.P=function(){return this.meta};g.Kd=function(a,b,c){a=E(this.gb);for(var d=null,e=0,f=0;;)if(f<e){var h=d.$(null,f),k=J(h,0,null);h=J(h,1,null);h.M?h.M(k,this,b,c):h.call(null,k,this,b,c);f+=1}else if(a=E(a))Ae(a)?(d=Wc(a),a=Xc(a),k=d,e=H(d),d=k):(d=y(a),k=J(d,0,null),h=J(d,1,null),h.M?h.M(k,this,b,c):h.call(null,k,this,b,c),a=z(a),d=null,e=0),f=0;else return null};
+g.Jd=function(a,b,c){this.gb=K.l(this.gb,b,c);return this};g.Ld=function(a,b){return this.gb=le.c(this.gb,b)};g.U=function(){return ja(this)};var dg=function dg(a){switch(arguments.length){case 1:return dg.h(arguments[0]);default:for(var c=[],d=arguments.length,e=0;;)if(e<d)c.push(arguments[e]),e+=1;else break;return dg.A(arguments[0],new Jb(c.slice(1),0,null))}};dg.h=function(a){return new cg(a,null,null,null)};
+dg.A=function(a,b){var c=null!=b&&(b.m&64||q===b.G)?P(U,b):b,d=D.c(c,rb);c=D.c(c,eg);return new cg(a,d,c,null)};dg.N=function(a){var b=y(a);a=z(a);return dg.A(b,a)};dg.L=1;function fg(a,b){if(a instanceof cg){var c=a.df;if(null!=c&&!t(c.h?c.h(b):c.call(null,b)))throw Error("Validator rejected reference state");c=a.state;a.state=b;null!=a.gb&&Lc(a,c,b);return b}return $c(a,b)}
+var gg=function gg(a){switch(arguments.length){case 2:return gg.c(arguments[0],arguments[1]);case 3:return gg.l(arguments[0],arguments[1],arguments[2]);case 4:return gg.M(arguments[0],arguments[1],arguments[2],arguments[3]);default:for(var c=[],d=arguments.length,e=0;;)if(e<d)c.push(arguments[e]),e+=1;else break;return gg.A(arguments[0],arguments[1],arguments[2],arguments[3],new Jb(c.slice(4),0,null))}};
+gg.c=function(a,b){if(a instanceof cg){var c=a.state;c=b.h?b.h(c):b.call(null,c);c=fg(a,c)}else c=ad.c(a,b);return c};gg.l=function(a,b,c){if(a instanceof cg){var d=a.state;b=b.c?b.c(d,c):b.call(null,d,c);a=fg(a,b)}else a=ad.l(a,b,c);return a};gg.M=function(a,b,c,d){if(a instanceof cg){var e=a.state;b=b.l?b.l(e,c,d):b.call(null,e,c,d);a=fg(a,b)}else a=ad.M(a,b,c,d);return a};gg.A=function(a,b,c,d,e){return a instanceof cg?fg(a,Af(b,a.state,c,d,e)):ad.Z(a,b,c,d,e)};
+gg.N=function(a){var b=y(a),c=z(a);a=y(c);var d=z(c);c=y(d);var e=z(d);d=y(e);e=z(e);return gg.A(b,a,c,d,e)};gg.L=4;function hg(a){this.state=a;this.m=32768;this.J=0}hg.prototype.Qe=function(a,b){return this.state=b};hg.prototype.pc=function(){return this.state};
+var ig=function ig(a){switch(arguments.length){case 1:return ig.h(arguments[0]);case 2:return ig.c(arguments[0],arguments[1]);case 3:return ig.l(arguments[0],arguments[1],arguments[2]);case 4:return ig.M(arguments[0],arguments[1],arguments[2],arguments[3]);default:for(var c=[],d=arguments.length,e=0;;)if(e<d)c.push(arguments[e]),e+=1;else break;return ig.A(arguments[0],arguments[1],arguments[2],arguments[3],new Jb(c.slice(4),0,null))}};
+ig.h=function(a){return function(b){return function(){function c(c,d){var e=a.h?a.h(d):a.call(null,d);return b.c?b.c(c,e):b.call(null,c,e)}function d(a){return b.h?b.h(a):b.call(null,a)}function e(){return b.B?b.B():b.call(null)}var f=null,h=function(){function c(a,b,c){var e=null;if(2<arguments.length){e=0;for(var f=Array(arguments.length-2);e<f.length;)f[e]=arguments[e+2],++e;e=new Jb(f,0,null)}return d.call(this,a,b,e)}function d(c,d,e){d=Kb(a,d,e);return b.c?b.c(c,d):b.call(null,c,d)}c.L=2;c.N=
+function(a){var b=y(a);a=z(a);var c=y(a);a=vd(a);return d(b,c,a)};c.A=d;return c}();f=function(a,b,f){switch(arguments.length){case 0:return e.call(this);case 1:return d.call(this,a);case 2:return c.call(this,a,b);default:var k=null;if(2<arguments.length){k=0;for(var l=Array(arguments.length-2);k<l.length;)l[k]=arguments[k+2],++k;k=new Jb(l,0,null)}return h.A(a,b,k)}throw Error("Invalid arity: "+(arguments.length-1));};f.L=2;f.N=h.N;f.B=e;f.h=d;f.c=c;f.A=h.A;return f}()}};
+ig.c=function(a,b){return new kf(null,function(){var c=E(b);if(c){if(Ae(c)){for(var d=Wc(c),e=H(d),f=of(e),h=0;;)if(h<e)rf(f,function(){var b=A.c(d,h);return a.h?a.h(b):a.call(null,b)}()),h+=1;else break;return qf(f.Da(),ig.c(a,Xc(c)))}return ae(function(){var b=y(c);return a.h?a.h(b):a.call(null,b)}(),ig.c(a,vd(c)))}return null},null,null)};
+ig.l=function(a,b,c){return new kf(null,function(){var d=E(b),e=E(c);if(d&&e){var f=ae;var h=y(d);var k=y(e);h=a.c?a.c(h,k):a.call(null,h,k);d=f(h,ig.l(a,vd(d),vd(e)))}else d=null;return d},null,null)};ig.M=function(a,b,c,d){return new kf(null,function(){var e=E(b),f=E(c),h=E(d);if(e&&f&&h){var k=ae;var l=y(e);var p=y(f),m=y(h);l=a.l?a.l(l,p,m):a.call(null,l,p,m);e=k(l,ig.M(a,vd(e),vd(f),vd(h)))}else e=null;return e},null,null)};
+ig.A=function(a,b,c,d,e){var f=function l(a){return new kf(null,function(){var b=ig.c(E,a);return Vf(Ve,b)?ae(ig.c(y,b),l(ig.c(vd,b))):null},null,null)};return ig.c(function(){return function(b){return P(a,b)}}(f),f(ge.A(e,d,be([c,b]))))};ig.N=function(a){var b=y(a),c=z(a);a=y(c);var d=z(c);c=y(d);var e=z(d);d=y(e);e=z(e);return ig.A(b,a,c,d,e)};ig.L=4;function jg(a,b){return new kf(null,function(){if(0<a){var c=E(b);return c?ae(y(c),jg(a-1,vd(c))):null}return null},null,null)}
+function kg(a,b){return new kf(null,function(c){return function(){return c(a,b)}}(function(a,b){for(;;){var c=E(b);if(0<a&&c){var d=a-1;c=vd(c);a=d;b=c}else return c}}),null,null)}function lg(a){return ig.l(function(a){return a},a,kg(2,a))}
+function mg(a){return function(b){return function(c){return function(){function d(d,e){var f=B(c);if(t(t(f)?a.h?a.h(e):a.call(null,e):f))return d;bd(c,null);return b.c?b.c(d,e):b.call(null,d,e)}function e(a){return b.h?b.h(a):b.call(null,a)}function f(){return b.B?b.B():b.call(null)}var h=null;h=function(a,b){switch(arguments.length){case 0:return f.call(this);case 1:return e.call(this,a);case 2:return d.call(this,a,b)}throw Error("Invalid arity: "+(arguments.length-1));};h.B=f;h.h=e;h.c=d;return h}()}(new hg(!0))}}
+function ng(a,b){return new kf(null,function(c){return function(){return c(a,b)}}(function(a,b){for(;;){var c=E(b),d;if(d=c)d=y(c),d=a.h?a.h(d):a.call(null,d);if(t(d))d=a,c=vd(c),a=d,b=c;else return c}}),null,null)}var og=function og(a){return new kf(null,function(){var c=E(a);return c?O.c(c,og.h?og.h(c):og.call(null,c)):null},null,null)};function pg(a){return new kf(null,function(){return ae(a,pg(a))},null,null)}function qg(a,b){return jg(a,pg(b))}
+var rg=function rg(a,b){return ae(b,new kf(null,function(){var d=a.h?a.h(b):a.call(null,b);return rg.c?rg.c(a,d):rg.call(null,a,d)},null,null))};function sg(a,b){return P(O,Kb(ig,a,b))}
+function tg(a){return function(b){return function(){function c(c,d){return t(a.h?a.h(d):a.call(null,d))?b.c?b.c(c,d):b.call(null,c,d):c}function d(a){return b.h?b.h(a):b.call(null,a)}function e(){return b.B?b.B():b.call(null)}var f=null;f=function(a,b){switch(arguments.length){case 0:return e.call(this);case 1:return d.call(this,a);case 2:return c.call(this,a,b)}throw Error("Invalid arity: "+(arguments.length-1));};f.B=e;f.h=d;f.c=c;return f}()}}
+function ug(a,b){return new kf(null,function(){var c=E(b);if(c){if(Ae(c)){for(var d=Wc(c),e=H(d),f=of(e),h=0;;)if(h<e){var k=A.c(d,h);k=a.h?a.h(k):a.call(null,k);t(k)&&(k=A.c(d,h),f.add(k));h+=1}else break;return qf(f.Da(),ug(a,Xc(c)))}d=y(c);c=vd(c);return t(a.h?a.h(d):a.call(null,d))?ae(d,ug(a,c)):ug(a,c)}return null},null,null)}function vg(a,b){return ug(Yf(a),b)}
+var wg=function wg(a){switch(arguments.length){case 0:return wg.B();case 1:return wg.h(arguments[0]);case 2:return wg.c(arguments[0],arguments[1]);case 3:return wg.l(arguments[0],arguments[1],arguments[2]);default:throw Error(["Invalid arity: ",v.h(arguments.length)].join(""));}};wg.B=function(){return he};wg.h=function(a){return a};wg.c=function(a,b){return null!=a?null!=a&&(a.J&4||q===a.kf)?tc(Qc(Mb(Pc,Oc(a),b)),qe(a)):Mb(Tb,a,b):Mb(ge,wd,b)};
+wg.l=function(a,b,c){return null!=a&&(a.J&4||q===a.kf)?tc(Qc(We(b,uf,Oc(a),c)),qe(a)):We(b,ge,a,c)};wg.L=3;function xg(a,b){return Qc(Mb(function(b,d){return uf.c(b,a.h?a.h(d):a.call(null,d))},Oc(he),b))}function yg(a,b,c){return new kf(null,function(){var d=E(c);if(d){var e=jg(a,d);return a===H(e)?ae(e,yg(a,b,kg(b,d))):null}return null},null,null)}
+var zg=function zg(a,b,c){b=E(b);var e=y(b),f=z(b);return f?K.l(a,e,function(){var b=D.c(a,e);return zg.l?zg.l(b,f,c):zg.call(null,b,f,c)}()):K.l(a,e,c)},Ag=function Ag(a){switch(arguments.length){case 3:return Ag.l(arguments[0],arguments[1],arguments[2]);case 4:return Ag.M(arguments[0],arguments[1],arguments[2],arguments[3]);case 5:return Ag.Z(arguments[0],arguments[1],arguments[2],arguments[3],arguments[4]);case 6:return Ag.Ca(arguments[0],arguments[1],arguments[2],arguments[3],arguments[4],arguments[5]);
+default:for(var c=[],d=arguments.length,e=0;;)if(e<d)c.push(arguments[e]),e+=1;else break;return Ag.A(arguments[0],arguments[1],arguments[2],arguments[3],arguments[4],arguments[5],new Jb(c.slice(6),0,null))}};Ag.l=function(a,b,c){b=E(b);var d=y(b);return(b=z(b))?K.l(a,d,Ag.l(D.c(a,d),b,c)):K.l(a,d,function(){var b=D.c(a,d);return c.h?c.h(b):c.call(null,b)}())};
+Ag.M=function(a,b,c,d){b=E(b);var e=y(b);return(b=z(b))?K.l(a,e,Ag.M(D.c(a,e),b,c,d)):K.l(a,e,function(){var b=D.c(a,e);return c.c?c.c(b,d):c.call(null,b,d)}())};Ag.Z=function(a,b,c,d,e){b=E(b);var f=y(b);return(b=z(b))?K.l(a,f,Ag.Z(D.c(a,f),b,c,d,e)):K.l(a,f,function(){var b=D.c(a,f);return c.l?c.l(b,d,e):c.call(null,b,d,e)}())};
+Ag.Ca=function(a,b,c,d,e,f){b=E(b);var h=y(b);return(b=z(b))?K.l(a,h,Ag.Ca(D.c(a,h),b,c,d,e,f)):K.l(a,h,function(){var b=D.c(a,h);return c.M?c.M(b,d,e,f):c.call(null,b,d,e,f)}())};Ag.A=function(a,b,c,d,e,f,h){var k=E(b);b=y(k);return(k=z(k))?K.l(a,b,oe(Ag,D.c(a,b),k,c,d,be([e,f,h]))):K.l(a,b,oe(c,D.c(a,b),d,e,f,be([h])))};Ag.N=function(a){var b=y(a),c=z(a);a=y(c);var d=z(c);c=y(d);var e=z(d);d=y(e);var f=z(e);e=y(f);var h=z(f);f=y(h);h=z(h);return Ag.A(b,a,c,d,e,f,h)};Ag.L=6;
+function Bg(a,b,c){return K.l(a,b,function(){var d=D.c(a,b);return c.h?c.h(d):c.call(null,d)}())}function Cg(a,b,c,d){return K.l(a,b,function(){var e=D.c(a,b);return c.c?c.c(e,d):c.call(null,e,d)}())}function Dg(a,b,c){var d=V,e=Eg;return K.l(a,d,function(){var f=D.c(a,d);return e.l?e.l(f,b,c):e.call(null,f,b,c)}())}function Fg(a,b){this.la=a;this.o=b}
+function Gg(a){return new Fg(a,[null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null])}function Hg(a){return new Fg(a.la,Gb(a.o))}function Ig(a){a=a.F;return 32>a?0:a-1>>>5<<5}function Jg(a,b,c){for(;;){if(0===b)return c;var d=Gg(a);d.o[0]=c;c=d;b-=5}}
+var Kg=function Kg(a,b,c,d){var f=Hg(c),h=a.F-1>>>b&31;5===b?f.o[h]=d:(c=c.o[h],null!=c?(b-=5,a=Kg.M?Kg.M(a,b,c,d):Kg.call(null,a,b,c,d)):a=Jg(null,b-5,d),f.o[h]=a);return f};function Lg(a,b){throw Error(["No item ",v.h(a)," in vector of length ",v.h(b)].join(""));}function Mg(a,b){if(b>=Ig(a))return a.fa;for(var c=a.root,d=a.shift;;)if(0<d){var e=d-5;c=c.o[b>>>d&31];d=e}else return c.o}
+var Ng=function Ng(a,b,c,d,e){var h=Hg(c);if(0===b)h.o[d&31]=e;else{var k=d>>>b&31;b-=5;c=c.o[k];a=Ng.Z?Ng.Z(a,b,c,d,e):Ng.call(null,a,b,c,d,e);h.o[k]=a}return h},Og=function Og(a,b,c){var e=a.F-2>>>b&31;if(5<b){b-=5;var f=c.o[e];a=Og.l?Og.l(a,b,f):Og.call(null,a,b,f);if(null==a&&0===e)return null;c=Hg(c);c.o[e]=a;return c}if(0===e)return null;c=Hg(c);c.o[e]=null;return c};function Pg(a,b,c,d,e,f){this.i=a;this.base=b;this.o=c;this.Ja=d;this.start=e;this.end=f}
+Pg.prototype.ja=function(){return this.i<this.end};Pg.prototype.next=function(){32===this.i-this.base&&(this.o=Mg(this.Ja,this.i),this.base+=32);var a=this.o[this.i&31];this.i+=1;return a};function Qg(a,b,c){return new Pg(b,b-b%32,b<H(a)?Mg(a,b):null,a,b,c)}function Rg(a,b,c,d){return c<d?Sg(a,b,Vd(a,c),c+1,d):b.B?b.B():b.call(null)}
+function Sg(a,b,c,d,e){var f=c;c=d;for(d=Mg(a,d);;)if(c<e){var h=c&31;d=0===h?Mg(a,c):d;h=d[h];f=b.c?b.c(f,h):b.call(null,f,h);if(Hd(f))return B(f);c+=1}else return f}function R(a,b,c,d,e,f){this.meta=a;this.F=b;this.shift=c;this.root=d;this.fa=e;this.w=f;this.m=167668511;this.J=139268}g=R.prototype;g.toString=function(){return fd(this)};g.equiv=function(a){return this.K(null,a)};
+g.indexOf=function(){var a=null;a=function(a,c){switch(arguments.length){case 1:return Ud(this,a,0);case 2:return Ud(this,a,c)}throw Error("Invalid arity: "+(arguments.length-1));};a.h=function(a){return Ud(this,a,0)};a.c=function(a,c){return Ud(this,a,c)};return a}();
+g.lastIndexOf=function(){function a(a){return Xd(this,a,H(this))}var b=null;b=function(b,d){switch(arguments.length){case 1:return a.call(this,b);case 2:return Xd(this,b,d)}throw Error("Invalid arity: "+(arguments.length-1));};b.h=a;b.c=function(a,b){return Xd(this,a,b)};return b}();g.V=function(a,b){return this.I(null,b,null)};g.I=function(a,b,c){return"number"===typeof b?this.ka(null,b,c):c};
+g.Qc=function(a,b,c){a=0;for(var d=c;;)if(a<this.F){var e=Mg(this,a);c=e.length;a:for(var f=0;;)if(f<c){var h=f+a,k=e[f];d=b.l?b.l(d,h,k):b.call(null,d,h,k);if(Hd(d)){e=d;break a}f+=1}else{e=d;break a}if(Hd(e))return B(e);a+=c;d=e}else return d};g.fe=q;g.$=function(a,b){return(0<=b&&b<this.F?Mg(this,b):Lg(b,this.F))[b&31]};g.ka=function(a,b,c){return 0<=b&&b<this.F?Mg(this,b)[b&31]:c};
+g.dc=function(a,b,c){if(0<=b&&b<this.F)return Ig(this)<=b?(a=Gb(this.fa),a[b&31]=c,new R(this.meta,this.F,this.shift,this.root,a,null)):new R(this.meta,this.F,this.shift,Ng(this,this.shift,this.root,b,c),this.fa,null);if(b===this.F)return this.X(null,c);throw Error(["Index ",v.h(b)," out of bounds [0,",v.h(this.F),"]"].join(""));};g.ba=function(){return Qg(this,0,this.F)};g.P=function(){return this.meta};g.W=function(){return this.F};g.fd=function(){return this.$(null,0)};
+g.gd=function(){return this.$(null,1)};g.Ac=function(){return 0<this.F?this.$(null,this.F-1):null};
+g.Bc=function(){if(0===this.F)throw Error("Can't pop empty vector");if(1===this.F)return tc(he,this.meta);if(1<this.F-Ig(this))return new R(this.meta,this.F-1,this.shift,this.root,this.fa.slice(0,-1),null);var a=Mg(this,this.F-2),b=Og(this,this.shift,this.root);b=null==b?T:b;var c=this.F-1;return 5<this.shift&&null==b.o[1]?new R(this.meta,c,this.shift-5,b.o[0],a,null):new R(this.meta,c,this.shift,b,a,null)};g.Rc=function(){return 0<this.F?new Zd(this,this.F-1,null):null};
+g.U=function(){var a=this.w;return null!=a?a:this.w=a=Ad(this)};g.K=function(a,b){if(b instanceof R)if(this.F===H(b))for(var c=this.ba(null),d=dd(b);;)if(c.ja()){var e=c.next(),f=d.next();if(!G.c(e,f))return!1}else return!0;else return!1;else return $d(this,b)};
+g.Pc=function(){var a=this.F,b=this.shift,c=new Fg({},Gb(this.root.o)),d=this.fa,e=[null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null];Be(d,0,e,0,d.length);return new Tg(a,b,c,e)};g.oa=function(){return tc(he,this.meta)};g.Fa=function(a,b){return Rg(this,b,0,this.F)};
+g.Ga=function(a,b,c){a=0;for(var d=c;;)if(a<this.F){var e=Mg(this,a);c=e.length;a:for(var f=0;;)if(f<c){var h=e[f];d=b.c?b.c(d,h):b.call(null,d,h);if(Hd(d)){e=d;break a}f+=1}else{e=d;break a}if(Hd(e))return B(e);a+=c;d=e}else return d};g.O=function(a,b,c){if("number"===typeof b)return this.dc(null,b,c);throw Error("Vector's key for assoc must be a number.");};g.yc=function(a,b){return Ge(b)?0<=b&&b<this.F:!1};
+g.S=function(){if(0===this.F)var a=null;else if(32>=this.F)a=new Jb(this.fa,0,null);else{a:{a=this.root;for(var b=this.shift;;)if(0<b)b-=5,a=a.o[0];else{a=a.o;break a}}a=new Ug(this,a,0,0,null,null)}return a};g.T=function(a,b){return new R(b,this.F,this.shift,this.root,this.fa,this.w)};
+g.X=function(a,b){if(32>this.F-Ig(this)){for(var c=this.fa.length,d=Array(c+1),e=0;;)if(e<c)d[e]=this.fa[e],e+=1;else break;d[c]=b;return new R(this.meta,this.F+1,this.shift,this.root,d,null)}c=(d=this.F>>>5>1<<this.shift)?this.shift+5:this.shift;d?(d=Gg(null),d.o[0]=this.root,e=Jg(null,this.shift,new Fg(null,this.fa)),d.o[1]=e):d=Kg(this,this.shift,this.root,new Fg(null,this.fa));return new R(this.meta,this.F+1,c,d,[b],null)};
+g.call=function(){var a=null;a=function(a,c,d){switch(arguments.length){case 2:return this.$(null,c);case 3:return this.ka(null,c,d)}throw Error("Invalid arity: "+(arguments.length-1));};a.c=function(a,c){return this.$(null,c)};a.l=function(a,c,d){return this.ka(null,c,d)};return a}();g.apply=function(a,b){return this.call.apply(this,[this].concat(Gb(b)))};g.h=function(a){return this.$(null,a)};g.c=function(a,b){return this.ka(null,a,b)};
+var T=new Fg(null,[null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null]),he=new R(null,0,5,T,[],Cd);function Vg(a){var b=a.length;if(32>b)return new R(null,b,5,T,a,null);for(var c=32,d=(new R(null,32,5,T,a.slice(0,32),null)).Pc(null);;)if(c<b){var e=c+1;d=uf.c(d,a[c]);c=e}else return Qc(d)}R.prototype[Fb]=function(){return yd(this)};function Wg(a){return vb(a)?Vg(a):Qc(Mb(Pc,Oc(he),a))}
+var Xg=function Xg(a){for(var c=[],d=arguments.length,e=0;;)if(e<d)c.push(arguments[e]),e+=1;else break;return Xg.A(0<c.length?new Jb(c.slice(0),0,null):null)};Xg.A=function(a){return a instanceof Jb&&0===a.i?Vg(a.o):Wg(a)};Xg.L=0;Xg.N=function(a){return Xg.A(E(a))};function Ug(a,b,c,d,e,f){this.zb=a;this.node=b;this.i=c;this.ab=d;this.meta=e;this.w=f;this.m=32375020;this.J=1536}g=Ug.prototype;g.toString=function(){return fd(this)};g.equiv=function(a){return this.K(null,a)};
+g.indexOf=function(){var a=null;a=function(a,c){switch(arguments.length){case 1:return Ud(this,a,0);case 2:return Ud(this,a,c)}throw Error("Invalid arity: "+(arguments.length-1));};a.h=function(a){return Ud(this,a,0)};a.c=function(a,c){return Ud(this,a,c)};return a}();
+g.lastIndexOf=function(){function a(a){return Xd(this,a,H(this))}var b=null;b=function(b,d){switch(arguments.length){case 1:return a.call(this,b);case 2:return Xd(this,b,d)}throw Error("Invalid arity: "+(arguments.length-1));};b.h=a;b.c=function(a,b){return Xd(this,a,b)};return b}();g.P=function(){return this.meta};g.Ka=function(){if(this.ab+1<this.node.length){var a=new Ug(this.zb,this.node,this.i,this.ab+1,null,null);return null==a?null:a}return this.Me(null)};
+g.U=function(){var a=this.w;return null!=a?a:this.w=a=Ad(this)};g.K=function(a,b){return $d(this,b)};g.oa=function(){return tc(he,this.meta)};g.Fa=function(a,b){return Rg(this.zb,b,this.i+this.ab,H(this.zb))};g.Ga=function(a,b,c){return Sg(this.zb,b,c,this.i+this.ab,H(this.zb))};g.Ia=function(){return this.node[this.ab]};g.bb=function(){if(this.ab+1<this.node.length){var a=new Ug(this.zb,this.node,this.i,this.ab+1,null,null);return null==a?wd:a}return this.Hd(null)};g.S=function(){return this};
+g.ge=function(){var a=this.node;return new nf(a,this.ab,a.length)};g.Hd=function(){var a=this.i+this.node.length;return a<Qb(this.zb)?new Ug(this.zb,Mg(this.zb,a),a,0,null,null):wd};g.T=function(a,b){return new Ug(this.zb,this.node,this.i,this.ab,b,null)};g.X=function(a,b){return ae(b,this)};g.Me=function(){var a=this.i+this.node.length;return a<Qb(this.zb)?new Ug(this.zb,Mg(this.zb,a),a,0,null,null):null};Ug.prototype[Fb]=function(){return yd(this)};
+function Yg(a,b,c,d,e){this.meta=a;this.Ja=b;this.start=c;this.end=d;this.w=e;this.m=167666463;this.J=139264}g=Yg.prototype;g.toString=function(){return fd(this)};g.equiv=function(a){return this.K(null,a)};g.indexOf=function(){var a=null;a=function(a,c){switch(arguments.length){case 1:return Ud(this,a,0);case 2:return Ud(this,a,c)}throw Error("Invalid arity: "+(arguments.length-1));};a.h=function(a){return Ud(this,a,0)};a.c=function(a,c){return Ud(this,a,c)};return a}();
+g.lastIndexOf=function(){function a(a){return Xd(this,a,H(this))}var b=null;b=function(b,d){switch(arguments.length){case 1:return a.call(this,b);case 2:return Xd(this,b,d)}throw Error("Invalid arity: "+(arguments.length-1));};b.h=a;b.c=function(a,b){return Xd(this,a,b)};return b}();g.V=function(a,b){return this.I(null,b,null)};g.I=function(a,b,c){return"number"===typeof b?this.ka(null,b,c):c};
+g.Qc=function(a,b,c){a=this.start;for(var d=0;;)if(a<this.end){var e=d,f=A.c(this.Ja,a);c=b.l?b.l(c,e,f):b.call(null,c,e,f);if(Hd(c))return B(c);d+=1;a+=1}else return c};g.$=function(a,b){return 0>b||this.end<=this.start+b?Lg(b,this.end-this.start):A.c(this.Ja,this.start+b)};g.ka=function(a,b,c){return 0>b||this.end<=this.start+b?c:A.l(this.Ja,this.start+b,c)};
+g.dc=function(a,b,c){a=this.start+b;if(0>b||this.end+1<=a)throw Error(["Index ",v.h(b)," out of bounds [0,",v.h(this.W(null)),"]"].join(""));b=this.meta;c=K.l(this.Ja,a,c);var d=this.end;a+=1;return Zg(b,c,this.start,d>a?d:a,null)};g.ba=function(){return null!=this.Ja&&q===this.Ja.fe?Qg(this.Ja,this.start,this.end):new Jf(Hf,this)};g.P=function(){return this.meta};g.W=function(){return this.end-this.start};g.Ac=function(){return A.c(this.Ja,this.end-1)};
+g.Bc=function(){if(this.start===this.end)throw Error("Can't pop empty vector");return Zg(this.meta,this.Ja,this.start,this.end-1,null)};g.Rc=function(){return this.start!==this.end?new Zd(this,this.end-this.start-1,null):null};g.U=function(){var a=this.w;return null!=a?a:this.w=a=Ad(this)};g.K=function(a,b){return $d(this,b)};g.oa=function(){return tc(he,this.meta)};g.Fa=function(a,b){return null!=this.Ja&&q===this.Ja.fe?Rg(this.Ja,b,this.start,this.end):Kd(this,b)};
+g.Ga=function(a,b,c){return null!=this.Ja&&q===this.Ja.fe?Sg(this.Ja,b,c,this.start,this.end):Ld(this,b,c)};g.O=function(a,b,c){if("number"===typeof b)return this.dc(null,b,c);throw Error("Subvec's key for assoc must be a number.");};g.S=function(){var a=this;return function(b){return function e(d){return d===a.end?null:ae(A.c(a.Ja,d),new kf(null,function(){return function(){return e(d+1)}}(b),null,null))}}(this)(a.start)};g.T=function(a,b){return Zg(b,this.Ja,this.start,this.end,this.w)};
+g.X=function(a,b){return Zg(this.meta,qc(this.Ja,this.end,b),this.start,this.end+1,null)};g.call=function(){var a=null;a=function(a,c,d){switch(arguments.length){case 2:return this.$(null,c);case 3:return this.ka(null,c,d)}throw Error("Invalid arity: "+(arguments.length-1));};a.c=function(a,c){return this.$(null,c)};a.l=function(a,c,d){return this.ka(null,c,d)};return a}();g.apply=function(a,b){return this.call.apply(this,[this].concat(Gb(b)))};g.h=function(a){return this.$(null,a)};
+g.c=function(a,b){return this.ka(null,a,b)};Yg.prototype[Fb]=function(){return yd(this)};function Zg(a,b,c,d,e){for(;;)if(b instanceof Yg)c=b.start+c,d=b.start+d,b=b.Ja;else{if(!ze(b))throw Error("v must satisfy IVector");var f=H(b);if(0>c||0>d||c>f||d>f)throw Error("Index out of bounds");return new Yg(a,b,c,d,e)}}function $g(a,b){return a===b.la?b:new Fg(a,Gb(b.o))}
+var ah=function ah(a,b,c,d){c=$g(a.root.la,c);var f=a.F-1>>>b&31;if(5===b)a=d;else{var h=c.o[f];null!=h?(b-=5,a=ah.M?ah.M(a,b,h,d):ah.call(null,a,b,h,d)):a=Jg(a.root.la,b-5,d)}c.o[f]=a;return c};function Tg(a,b,c,d){this.F=a;this.shift=b;this.root=c;this.fa=d;this.J=88;this.m=275}g=Tg.prototype;
+g.Dc=function(a,b){if(this.root.la){if(32>this.F-Ig(this))this.fa[this.F&31]=b;else{var c=new Fg(this.root.la,this.fa),d=[null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null];d[0]=b;this.fa=d;if(this.F>>>5>1<<this.shift){d=[null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null];var e=this.shift+
+5;d[0]=this.root;d[1]=Jg(this.root.la,this.shift,c);this.root=new Fg(this.root.la,d);this.shift=e}else this.root=ah(this,this.shift,this.root,c)}this.F+=1;return this}throw Error("conj! after persistent!");};g.kd=function(){if(this.root.la){this.root.la=null;var a=this.F-Ig(this),b=Array(a);Be(this.fa,0,b,0,a);return new R(null,this.F,this.shift,this.root,b,null)}throw Error("persistent! called twice");};
+g.Cc=function(a,b,c){if("number"===typeof b)return bh(this,b,c);throw Error("TransientVector's key for assoc! must be a number.");};
+function bh(a,b,c){if(a.root.la){if(0<=b&&b<a.F){if(Ig(a)<=b)a.fa[b&31]=c;else{var d=function(){return function(){return function k(d,h){var f=$g(a.root.la,h);if(0===d)f.o[b&31]=c;else{var p=b>>>d&31,m=k(d-5,f.o[p]);f.o[p]=m}return f}}(a)(a.shift,a.root)}();a.root=d}return a}if(b===a.F)return a.Dc(null,c);throw Error(["Index ",v.h(b)," out of bounds for TransientVector of length",v.h(a.F)].join(""));}throw Error("assoc! after persistent!");}
+g.W=function(){if(this.root.la)return this.F;throw Error("count after persistent!");};g.$=function(a,b){if(this.root.la)return(0<=b&&b<this.F?Mg(this,b):Lg(b,this.F))[b&31];throw Error("nth after persistent!");};g.ka=function(a,b,c){return 0<=b&&b<this.F?this.$(null,b):c};g.V=function(a,b){return this.I(null,b,null)};g.I=function(a,b,c){return"number"===typeof b?this.ka(null,b,c):c};
+g.call=function(){var a=null;a=function(a,c,d){switch(arguments.length){case 2:return this.V(null,c);case 3:return this.I(null,c,d)}throw Error("Invalid arity: "+(arguments.length-1));};a.c=function(a,c){return this.V(null,c)};a.l=function(a,c,d){return this.I(null,c,d)};return a}();g.apply=function(a,b){return this.call.apply(this,[this].concat(Gb(b)))};g.h=function(a){return this.V(null,a)};g.c=function(a,b){return this.I(null,a,b)};function ch(){this.m=2097152;this.J=0}
+ch.prototype.equiv=function(a){return this.K(null,a)};ch.prototype.K=function(){return!1};var dh=new ch;function eh(a,b){return Ee(xe(b)&&!ye(b)?H(a)===H(b)?(null!=a?a.m&1048576||q===a.Uf||(a.m?0:Ab(wc,a)):Ab(wc,a))?Ue(function(a,d,e){return G.c(D.l(b,d,dh),e)?!0:new Gd(!1)},!0,a):Vf(function(a){return G.c(D.l(b,y(a),dh),ee(a))},a):null:null)}function fh(a,b,c,d,e){this.i=a;this.Mf=b;this.Ie=c;this.wf=d;this.Se=e}fh.prototype.ja=function(){var a=this.i<this.Ie;return a?a:this.Se.ja()};
+fh.prototype.next=function(){if(this.i<this.Ie){var a=Vd(this.wf,this.i);this.i+=1;return new R(null,2,5,T,[a,cc.c(this.Mf,a)],null)}return this.Se.next()};fh.prototype.remove=function(){return Error("Unsupported operation")};function gh(a){this.s=a}gh.prototype.next=function(){if(null!=this.s){var a=y(this.s),b=J(a,0,null);a=J(a,1,null);this.s=z(this.s);return{value:[b,a],done:!1}}return{value:null,done:!0}};function hh(a){this.s=a}
+hh.prototype.next=function(){if(null!=this.s){var a=y(this.s);this.s=z(this.s);return{value:[a,a],done:!1}}return{value:null,done:!0}};
+function ih(a,b){if(b instanceof L)a:{var c=a.length;for(var d=b.ea,e=0;;){if(c<=e){c=-1;break a}if(a[e]instanceof L&&d===a[e].ea){c=e;break a}e+=2}}else if(ca(b)||"number"===typeof b)a:for(c=a.length,d=0;;){if(c<=d){c=-1;break a}if(b===a[d]){c=d;break a}d+=2}else if(b instanceof rd)a:for(c=a.length,d=b.Zb,e=0;;){if(c<=e){c=-1;break a}if(a[e]instanceof rd&&d===a[e].Zb){c=e;break a}e+=2}else if(null==b)a:for(c=a.length,d=0;;){if(c<=d){c=-1;break a}if(null==a[d]){c=d;break a}d+=2}else a:for(c=a.length,
+d=0;;){if(c<=d){c=-1;break a}if(G.c(b,a[d])){c=d;break a}d+=2}return c}function jh(a,b,c){this.o=a;this.i=b;this.hb=c;this.m=32374990;this.J=0}g=jh.prototype;g.toString=function(){return fd(this)};g.equiv=function(a){return this.K(null,a)};g.indexOf=function(){var a=null;a=function(a,c){switch(arguments.length){case 1:return Ud(this,a,0);case 2:return Ud(this,a,c)}throw Error("Invalid arity: "+(arguments.length-1));};a.h=function(a){return Ud(this,a,0)};a.c=function(a,c){return Ud(this,a,c)};return a}();
+g.lastIndexOf=function(){function a(a){return Xd(this,a,H(this))}var b=null;b=function(b,d){switch(arguments.length){case 1:return a.call(this,b);case 2:return Xd(this,b,d)}throw Error("Invalid arity: "+(arguments.length-1));};b.h=a;b.c=function(a,b){return Xd(this,a,b)};return b}();g.P=function(){return this.hb};g.Ka=function(){return this.i<this.o.length-2?new jh(this.o,this.i+2,this.hb):null};g.W=function(){return(this.o.length-this.i)/2};g.U=function(){return Ad(this)};
+g.K=function(a,b){return $d(this,b)};g.oa=function(){return tc(wd,this.hb)};g.Fa=function(a,b){return ce(b,this)};g.Ga=function(a,b,c){return de(b,c,this)};g.Ia=function(){return new R(null,2,5,T,[this.o[this.i],this.o[this.i+1]],null)};g.bb=function(){return this.i<this.o.length-2?new jh(this.o,this.i+2,this.hb):wd};g.S=function(){return this};g.T=function(a,b){return new jh(this.o,this.i,b)};g.X=function(a,b){return ae(b,this)};jh.prototype[Fb]=function(){return yd(this)};
+function kh(a,b,c){this.o=a;this.i=b;this.F=c}kh.prototype.ja=function(){return this.i<this.F};kh.prototype.next=function(){var a=new R(null,2,5,T,[this.o[this.i],this.o[this.i+1]],null);this.i+=2;return a};function r(a,b,c,d){this.meta=a;this.F=b;this.o=c;this.w=d;this.m=16647951;this.J=139268}g=r.prototype;g.toString=function(){return fd(this)};g.equiv=function(a){return this.K(null,a)};g.keys=function(){return yd(lh(this))};g.entries=function(){return new gh(E(E(this)))};g.values=function(){return yd(mh(this))};
+g.has=function(a){return He(this,a)};g.get=function(a,b){return this.I(null,a,b)};g.forEach=function(a){for(var b=E(this),c=null,d=0,e=0;;)if(e<d){var f=c.$(null,e),h=J(f,0,null);f=J(f,1,null);a.c?a.c(f,h):a.call(null,f,h);e+=1}else if(b=E(b))Ae(b)?(c=Wc(b),b=Xc(b),h=c,d=H(c),c=h):(c=y(b),h=J(c,0,null),f=J(c,1,null),a.c?a.c(f,h):a.call(null,f,h),b=z(b),c=null,d=0),e=0;else return null};g.V=function(a,b){return this.I(null,b,null)};g.I=function(a,b,c){a=ih(this.o,b);return-1===a?c:this.o[a+1]};
+g.Qc=function(a,b,c){a=this.o.length;for(var d=0;;)if(d<a){var e=this.o[d],f=this.o[d+1];c=b.l?b.l(c,e,f):b.call(null,c,e,f);if(Hd(c))return B(c);d+=2}else return c};g.ba=function(){return new kh(this.o,0,2*this.F)};g.P=function(){return this.meta};g.W=function(){return this.F};g.U=function(){var a=this.w;return null!=a?a:this.w=a=Dd(this)};
+g.K=function(a,b){if(xe(b)&&!ye(b)){var c=this.o.length;if(this.F===b.W(null))for(var d=0;;)if(d<c){var e=b.I(null,this.o[d],Ce);if(e!==Ce)if(G.c(this.o[d+1],e))d+=2;else return!1;else return!1}else return!0;else return!1}else return!1};g.Pc=function(){return new nh({},this.o.length,Gb(this.o))};g.oa=function(){return tc(Ef,this.meta)};g.Fa=function(a,b){return Re(this,b)};g.Ga=function(a,b,c){return Se(this,b,c)};
+g.ga=function(a,b){if(0<=ih(this.o,b)){var c=this.o.length,d=c-2;if(0===d)return this.oa(null);d=Array(d);for(var e=0,f=0;;){if(e>=c)return new r(this.meta,this.F-1,d,null);G.c(b,this.o[e])||(d[f]=this.o[e],d[f+1]=this.o[e+1],f+=2);e+=2}}else return this};
+g.O=function(a,b,c){a=ih(this.o,b);if(-1===a){if(this.F<oh){a=this.o;for(var d=a.length,e=Array(d+2),f=0;;)if(f<d)e[f]=a[f],f+=1;else break;e[d]=b;e[d+1]=c;return new r(this.meta,this.F+1,e,null)}return tc(ec(wg.c(ph,this),b,c),this.meta)}if(c===this.o[a+1])return this;b=Gb(this.o);b[a+1]=c;return new r(this.meta,this.F,b,null)};g.yc=function(a,b){return-1!==ih(this.o,b)};g.S=function(){var a=this.o;return 0<=a.length-2?new jh(a,0,null):null};g.T=function(a,b){return new r(b,this.F,this.o,this.w)};
+g.X=function(a,b){if(ze(b))return this.O(null,A.c(b,0),A.c(b,1));for(var c=this,d=E(b);;){if(null==d)return c;var e=y(d);if(ze(e))c=c.O(null,A.c(e,0),A.c(e,1)),d=z(d);else throw Error("conj on a map takes map entries or seqables of map entries");}};
+g.call=function(){var a=null;a=function(a,c,d){switch(arguments.length){case 2:return this.V(null,c);case 3:return this.I(null,c,d)}throw Error("Invalid arity: "+(arguments.length-1));};a.c=function(a,c){return this.V(null,c)};a.l=function(a,c,d){return this.I(null,c,d)};return a}();g.apply=function(a,b){return this.call.apply(this,[this].concat(Gb(b)))};g.h=function(a){return this.V(null,a)};g.c=function(a,b){return this.I(null,a,b)};var Ef=new r(null,0,[],Ed),oh=8;
+function ke(a){for(var b=[],c=0;;)if(c<a.length){var d=a[c],e=a[c+1],f=ih(b,d);-1===f?(f=b,f.push(d),f.push(e)):b[f+1]=e;c+=2}else break;return new r(null,b.length/2,b,null)}r.prototype[Fb]=function(){return yd(this)};function nh(a,b,c){this.Uc=a;this.Zc=b;this.o=c;this.m=258;this.J=56}g=nh.prototype;g.W=function(){if(t(this.Uc))return Ze(this.Zc);throw Error("count after persistent!");};g.V=function(a,b){return this.I(null,b,null)};
+g.I=function(a,b,c){if(t(this.Uc))return a=ih(this.o,b),-1===a?c:this.o[a+1];throw Error("lookup after persistent!");};g.Dc=function(a,b){if(t(this.Uc)){if(null!=b?b.m&2048||q===b.sf||(b.m?0:Ab(hc,b)):Ab(hc,b))return this.Cc(null,jc(b),kc(b));for(var c=E(b),d=this;;){var e=y(c);if(t(e))c=z(c),d=d.Cc(null,jc(e),kc(e));else return d}}else throw Error("conj! after persistent!");};
+g.kd=function(){if(t(this.Uc))return this.Uc=!1,new r(null,Ze(this.Zc),this.o,null);throw Error("persistent! called twice");};g.Cc=function(a,b,c){if(t(this.Uc)){a=ih(this.o,b);if(-1===a){if(this.Zc+2<=2*oh)return this.Zc+=2,this.o.push(b),this.o.push(c),this;a:{a=this.Zc;var d=this.o;var e=Oc(ph);for(var f=0;;)if(f<a)e=Rc(e,d[f],d[f+1]),f+=2;else break a}return Rc(e,b,c)}c!==this.o[a+1]&&(this.o[a+1]=c);return this}throw Error("assoc! after persistent!");};function qh(){this.H=!1}
+function rh(a,b){return a===b?!0:N(a,b)?!0:G.c(a,b)}function sh(a,b,c){a=Gb(a);a[b]=c;return a}function th(a,b){var c=Array(a.length-2);Be(a,0,c,0,2*b);Be(a,2*(b+1),c,2*b,c.length-2*b);return c}function uh(a,b,c,d){a=a.Gc(b);a.o[c]=d;return a}function vh(a,b,c){for(var d=a.length,e=0,f=c;;)if(e<d){c=a[e];if(null!=c){var h=a[e+1];c=b.l?b.l(f,c,h):b.call(null,f,c,h)}else c=a[e+1],c=null!=c?c.Jc(b,f):f;if(Hd(c))return c;e+=2;f=c}else return f}
+function wh(a,b,c,d){this.o=a;this.i=b;this.sd=c;this.Lb=d}wh.prototype.advance=function(){for(var a=this.o.length;;)if(this.i<a){var b=this.o[this.i],c=this.o[this.i+1];null!=b?b=this.sd=new R(null,2,5,T,[b,c],null):null!=c?(b=dd(c),b=b.ja()?this.Lb=b:!1):b=!1;this.i+=2;if(b)return!0}else return!1};wh.prototype.ja=function(){var a=null!=this.sd;return a?a:(a=null!=this.Lb)?a:this.advance()};
+wh.prototype.next=function(){if(null!=this.sd){var a=this.sd;this.sd=null;return a}if(null!=this.Lb)return a=this.Lb.next(),this.Lb.ja()||(this.Lb=null),a;if(this.advance())return this.next();throw Error("No such element");};wh.prototype.remove=function(){return Error("Unsupported operation")};function xh(a,b,c){this.la=a;this.na=b;this.o=c;this.J=131072;this.m=0}g=xh.prototype;
+g.Gc=function(a){if(a===this.la)return this;var b=$e(this.na),c=Array(0>b?4:2*(b+1));Be(this.o,0,c,0,2*b);return new xh(a,this.na,c)};g.qd=function(){return yh(this.o,0,null)};g.Jc=function(a,b){return vh(this.o,a,b)};g.sc=function(a,b,c,d){var e=1<<(b>>>a&31);if(0===(this.na&e))return d;var f=$e(this.na&e-1);e=this.o[2*f];f=this.o[2*f+1];return null==e?f.sc(a+5,b,c,d):rh(c,e)?f:d};
+g.Kb=function(a,b,c,d,e,f){var h=1<<(c>>>b&31),k=$e(this.na&h-1);if(0===(this.na&h)){var l=$e(this.na);if(2*l<this.o.length){a=this.Gc(a);b=a.o;f.H=!0;a:for(c=2*(l-k),f=2*k+(c-1),l=2*(k+1)+(c-1);;){if(0===c)break a;b[l]=b[f];--l;--c;--f}b[2*k]=d;b[2*k+1]=e;a.na|=h;return a}if(16<=l){k=[null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null];k[c>>>b&31]=zh.Kb(a,b+5,c,d,e,f);for(e=d=0;;)if(32>d)0!==
+(this.na>>>d&1)&&(k[d]=null!=this.o[e]?zh.Kb(a,b+5,od(this.o[e]),this.o[e],this.o[e+1],f):this.o[e+1],e+=2),d+=1;else break;return new Ah(a,l+1,k)}b=Array(2*(l+4));Be(this.o,0,b,0,2*k);b[2*k]=d;b[2*k+1]=e;Be(this.o,2*k,b,2*(k+1),2*(l-k));f.H=!0;a=this.Gc(a);a.o=b;a.na|=h;return a}l=this.o[2*k];h=this.o[2*k+1];if(null==l)return l=h.Kb(a,b+5,c,d,e,f),l===h?this:uh(this,a,2*k+1,l);if(rh(d,l))return e===h?this:uh(this,a,2*k+1,e);f.H=!0;f=b+5;b=od(l);if(b===c)e=new Bh(null,b,2,[l,h,d,e]);else{var p=new qh;
+e=zh.Kb(a,f,b,l,h,p).Kb(a,f,c,d,e,p)}d=2*k;k=2*k+1;a=this.Gc(a);a.o[d]=null;a.o[k]=e;return a};
+g.Jb=function(a,b,c,d,e){var f=1<<(b>>>a&31),h=$e(this.na&f-1);if(0===(this.na&f)){var k=$e(this.na);if(16<=k){h=[null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null];h[b>>>a&31]=zh.Jb(a+5,b,c,d,e);for(d=c=0;;)if(32>c)0!==(this.na>>>c&1)&&(h[c]=null!=this.o[d]?zh.Jb(a+5,od(this.o[d]),this.o[d],this.o[d+1],e):this.o[d+1],d+=2),c+=1;else break;return new Ah(null,k+1,h)}a=Array(2*(k+1));Be(this.o,
+0,a,0,2*h);a[2*h]=c;a[2*h+1]=d;Be(this.o,2*h,a,2*(h+1),2*(k-h));e.H=!0;return new xh(null,this.na|f,a)}var l=this.o[2*h];f=this.o[2*h+1];if(null==l)return k=f.Jb(a+5,b,c,d,e),k===f?this:new xh(null,this.na,sh(this.o,2*h+1,k));if(rh(c,l))return d===f?this:new xh(null,this.na,sh(this.o,2*h+1,d));e.H=!0;e=this.na;k=this.o;a+=5;var p=od(l);if(p===b)c=new Bh(null,p,2,[l,f,c,d]);else{var m=new qh;c=zh.Jb(a,p,l,f,m).Jb(a,b,c,d,m)}a=2*h;h=2*h+1;d=Gb(k);d[a]=null;d[h]=c;return new xh(null,e,d)};
+g.rd=function(a,b,c){var d=1<<(b>>>a&31);if(0===(this.na&d))return this;var e=$e(this.na&d-1),f=this.o[2*e],h=this.o[2*e+1];return null==f?(a=h.rd(a+5,b,c),a===h?this:null!=a?new xh(null,this.na,sh(this.o,2*e+1,a)):this.na===d?null:new xh(null,this.na^d,th(this.o,e))):rh(c,f)?new xh(null,this.na^d,th(this.o,e)):this};g.ba=function(){return new wh(this.o,0,null,null)};var zh=new xh(null,0,[]);function Ch(a,b,c){this.o=a;this.i=b;this.Lb=c}
+Ch.prototype.ja=function(){for(var a=this.o.length;;){if(null!=this.Lb&&this.Lb.ja())return!0;if(this.i<a){var b=this.o[this.i];this.i+=1;null!=b&&(this.Lb=dd(b))}else return!1}};Ch.prototype.next=function(){if(this.ja())return this.Lb.next();throw Error("No such element");};Ch.prototype.remove=function(){return Error("Unsupported operation")};function Ah(a,b,c){this.la=a;this.F=b;this.o=c;this.J=131072;this.m=0}g=Ah.prototype;g.Gc=function(a){return a===this.la?this:new Ah(a,this.F,Gb(this.o))};
+g.qd=function(){return Dh(this.o,0,null)};g.Jc=function(a,b){for(var c=this.o.length,d=0,e=b;;)if(d<c){var f=this.o[d];if(null!=f&&(e=f.Jc(a,e),Hd(e)))return e;d+=1}else return e};g.sc=function(a,b,c,d){var e=this.o[b>>>a&31];return null!=e?e.sc(a+5,b,c,d):d};g.Kb=function(a,b,c,d,e,f){var h=c>>>b&31,k=this.o[h];if(null==k)return a=uh(this,a,h,zh.Kb(a,b+5,c,d,e,f)),a.F+=1,a;b=k.Kb(a,b+5,c,d,e,f);return b===k?this:uh(this,a,h,b)};
+g.Jb=function(a,b,c,d,e){var f=b>>>a&31,h=this.o[f];if(null==h)return new Ah(null,this.F+1,sh(this.o,f,zh.Jb(a+5,b,c,d,e)));a=h.Jb(a+5,b,c,d,e);return a===h?this:new Ah(null,this.F,sh(this.o,f,a))};
+g.rd=function(a,b,c){var d=b>>>a&31,e=this.o[d];if(null!=e){a=e.rd(a+5,b,c);if(a===e)d=this;else if(null==a)if(8>=this.F)a:{e=this.o;a=e.length;b=Array(2*(this.F-1));c=0;for(var f=1,h=0;;)if(c<a)c!==d&&null!=e[c]&&(b[f]=e[c],f+=2,h|=1<<c),c+=1;else{d=new xh(null,h,b);break a}}else d=new Ah(null,this.F-1,sh(this.o,d,a));else d=new Ah(null,this.F,sh(this.o,d,a));return d}return this};g.ba=function(){return new Ch(this.o,0,null)};
+function Eh(a,b,c){b*=2;for(var d=0;;)if(d<b){if(rh(c,a[d]))return d;d+=2}else return-1}function Bh(a,b,c,d){this.la=a;this.ec=b;this.F=c;this.o=d;this.J=131072;this.m=0}g=Bh.prototype;g.Gc=function(a){if(a===this.la)return this;var b=Array(2*(this.F+1));Be(this.o,0,b,0,2*this.F);return new Bh(a,this.ec,this.F,b)};g.qd=function(){return yh(this.o,0,null)};g.Jc=function(a,b){return vh(this.o,a,b)};g.sc=function(a,b,c,d){a=Eh(this.o,this.F,c);return 0>a?d:rh(c,this.o[a])?this.o[a+1]:d};
+g.Kb=function(a,b,c,d,e,f){if(c===this.ec){b=Eh(this.o,this.F,d);if(-1===b){if(this.o.length>2*this.F)return b=2*this.F,c=2*this.F+1,a=this.Gc(a),a.o[b]=d,a.o[c]=e,f.H=!0,a.F+=1,a;c=this.o.length;b=Array(c+2);Be(this.o,0,b,0,c);b[c]=d;b[c+1]=e;f.H=!0;d=this.F+1;a===this.la?(this.o=b,this.F=d,a=this):a=new Bh(this.la,this.ec,d,b);return a}return this.o[b+1]===e?this:uh(this,a,b+1,e)}return(new xh(a,1<<(this.ec>>>b&31),[null,this,null,null])).Kb(a,b,c,d,e,f)};
+g.Jb=function(a,b,c,d,e){return b===this.ec?(a=Eh(this.o,this.F,c),-1===a?(a=2*this.F,b=Array(a+2),Be(this.o,0,b,0,a),b[a]=c,b[a+1]=d,e.H=!0,new Bh(null,this.ec,this.F+1,b)):G.c(this.o[a+1],d)?this:new Bh(null,this.ec,this.F,sh(this.o,a+1,d))):(new xh(null,1<<(this.ec>>>a&31),[null,this])).Jb(a,b,c,d,e)};g.rd=function(a,b,c){a=Eh(this.o,this.F,c);return-1===a?this:1===this.F?null:new Bh(null,this.ec,this.F-1,th(this.o,Ze(a)))};g.ba=function(){return new wh(this.o,0,null,null)};
+function Fh(a,b,c,d,e){this.meta=a;this.Mb=b;this.i=c;this.s=d;this.w=e;this.m=32374988;this.J=0}g=Fh.prototype;g.toString=function(){return fd(this)};g.equiv=function(a){return this.K(null,a)};g.indexOf=function(){var a=null;a=function(a,c){switch(arguments.length){case 1:return Ud(this,a,0);case 2:return Ud(this,a,c)}throw Error("Invalid arity: "+(arguments.length-1));};a.h=function(a){return Ud(this,a,0)};a.c=function(a,c){return Ud(this,a,c)};return a}();
+g.lastIndexOf=function(){function a(a){return Xd(this,a,H(this))}var b=null;b=function(b,d){switch(arguments.length){case 1:return a.call(this,b);case 2:return Xd(this,b,d)}throw Error("Invalid arity: "+(arguments.length-1));};b.h=a;b.c=function(a,b){return Xd(this,a,b)};return b}();g.P=function(){return this.meta};g.Ka=function(){return null==this.s?yh(this.Mb,this.i+2,null):yh(this.Mb,this.i,z(this.s))};g.U=function(){var a=this.w;return null!=a?a:this.w=a=Ad(this)};
+g.K=function(a,b){return $d(this,b)};g.oa=function(){return tc(wd,this.meta)};g.Fa=function(a,b){return ce(b,this)};g.Ga=function(a,b,c){return de(b,c,this)};g.Ia=function(){return null==this.s?new R(null,2,5,T,[this.Mb[this.i],this.Mb[this.i+1]],null):y(this.s)};g.bb=function(){var a=null==this.s?yh(this.Mb,this.i+2,null):yh(this.Mb,this.i,z(this.s));return null!=a?a:wd};g.S=function(){return this};g.T=function(a,b){return new Fh(b,this.Mb,this.i,this.s,this.w)};g.X=function(a,b){return ae(b,this)};
+Fh.prototype[Fb]=function(){return yd(this)};function yh(a,b,c){if(null==c)for(c=a.length;;)if(b<c){if(null!=a[b])return new Fh(null,a,b,null,null);var d=a[b+1];if(t(d)&&(d=d.qd(),t(d)))return new Fh(null,a,b+2,d,null);b+=2}else return null;else return new Fh(null,a,b,c,null)}function Gh(a,b,c,d,e){this.meta=a;this.Mb=b;this.i=c;this.s=d;this.w=e;this.m=32374988;this.J=0}g=Gh.prototype;g.toString=function(){return fd(this)};g.equiv=function(a){return this.K(null,a)};
+g.indexOf=function(){var a=null;a=function(a,c){switch(arguments.length){case 1:return Ud(this,a,0);case 2:return Ud(this,a,c)}throw Error("Invalid arity: "+(arguments.length-1));};a.h=function(a){return Ud(this,a,0)};a.c=function(a,c){return Ud(this,a,c)};return a}();
+g.lastIndexOf=function(){function a(a){return Xd(this,a,H(this))}var b=null;b=function(b,d){switch(arguments.length){case 1:return a.call(this,b);case 2:return Xd(this,b,d)}throw Error("Invalid arity: "+(arguments.length-1));};b.h=a;b.c=function(a,b){return Xd(this,a,b)};return b}();g.P=function(){return this.meta};g.Ka=function(){return Dh(this.Mb,this.i,z(this.s))};g.U=function(){var a=this.w;return null!=a?a:this.w=a=Ad(this)};g.K=function(a,b){return $d(this,b)};g.oa=function(){return tc(wd,this.meta)};
+g.Fa=function(a,b){return ce(b,this)};g.Ga=function(a,b,c){return de(b,c,this)};g.Ia=function(){return y(this.s)};g.bb=function(){var a=Dh(this.Mb,this.i,z(this.s));return null!=a?a:wd};g.S=function(){return this};g.T=function(a,b){return new Gh(b,this.Mb,this.i,this.s,this.w)};g.X=function(a,b){return ae(b,this)};Gh.prototype[Fb]=function(){return yd(this)};
+function Dh(a,b,c){if(null==c)for(c=a.length;;)if(b<c){var d=a[b];if(t(d)&&(d=d.qd(),t(d)))return new Gh(null,a,b+1,d,null);b+=1}else return null;else return new Gh(null,a,b,c,null)}function Hh(a,b,c){this.eb=a;this.bf=b;this.xe=c}Hh.prototype.ja=function(){return!this.xe||this.bf.ja()};Hh.prototype.next=function(){if(this.xe)return this.bf.next();this.xe=!0;return new R(null,2,5,T,[null,this.eb],null)};Hh.prototype.remove=function(){return Error("Unsupported operation")};
+function Jh(a,b,c,d,e,f){this.meta=a;this.F=b;this.root=c;this.cb=d;this.eb=e;this.w=f;this.m=16123663;this.J=139268}g=Jh.prototype;g.toString=function(){return fd(this)};g.equiv=function(a){return this.K(null,a)};g.keys=function(){return yd(lh(this))};g.entries=function(){return new gh(E(E(this)))};g.values=function(){return yd(mh(this))};g.has=function(a){return He(this,a)};g.get=function(a,b){return this.I(null,a,b)};
+g.forEach=function(a){for(var b=E(this),c=null,d=0,e=0;;)if(e<d){var f=c.$(null,e),h=J(f,0,null);f=J(f,1,null);a.c?a.c(f,h):a.call(null,f,h);e+=1}else if(b=E(b))Ae(b)?(c=Wc(b),b=Xc(b),h=c,d=H(c),c=h):(c=y(b),h=J(c,0,null),f=J(c,1,null),a.c?a.c(f,h):a.call(null,f,h),b=z(b),c=null,d=0),e=0;else return null};g.V=function(a,b){return this.I(null,b,null)};g.I=function(a,b,c){return null==b?this.cb?this.eb:c:null==this.root?c:this.root.sc(0,od(b),b,c)};
+g.Qc=function(a,b,c){a=this.cb?b.l?b.l(c,null,this.eb):b.call(null,c,null,this.eb):c;return Hd(a)?B(a):null!=this.root?Jd(this.root.Jc(b,a)):a};g.ba=function(){var a=this.root?dd(this.root):Cf();return this.cb?new Hh(this.eb,a,!1):a};g.P=function(){return this.meta};g.W=function(){return this.F};g.U=function(){var a=this.w;return null!=a?a:this.w=a=Dd(this)};g.K=function(a,b){return eh(this,b)};g.Pc=function(){return new Kh({},this.root,this.F,this.cb,this.eb)};g.oa=function(){return tc(ph,this.meta)};
+g.ga=function(a,b){if(null==b)return this.cb?new Jh(this.meta,this.F-1,this.root,!1,null,null):this;if(null==this.root)return this;var c=this.root.rd(0,od(b),b);return c===this.root?this:new Jh(this.meta,this.F-1,c,this.cb,this.eb,null)};
+g.O=function(a,b,c){if(null==b)return this.cb&&c===this.eb?this:new Jh(this.meta,this.cb?this.F:this.F+1,this.root,!0,c,null);a=new qh;b=(null==this.root?zh:this.root).Jb(0,od(b),b,c,a);return b===this.root?this:new Jh(this.meta,a.H?this.F+1:this.F,b,this.cb,this.eb,null)};g.yc=function(a,b){return null==b?this.cb:null==this.root?!1:this.root.sc(0,od(b),b,Ce)!==Ce};g.S=function(){if(0<this.F){var a=null!=this.root?this.root.qd():null;return this.cb?ae(new R(null,2,5,T,[null,this.eb],null),a):a}return null};
+g.T=function(a,b){return new Jh(b,this.F,this.root,this.cb,this.eb,this.w)};g.X=function(a,b){if(ze(b))return this.O(null,A.c(b,0),A.c(b,1));for(var c=this,d=E(b);;){if(null==d)return c;var e=y(d);if(ze(e))c=c.O(null,A.c(e,0),A.c(e,1)),d=z(d);else throw Error("conj on a map takes map entries or seqables of map entries");}};
+g.call=function(){var a=null;a=function(a,c,d){switch(arguments.length){case 2:return this.V(null,c);case 3:return this.I(null,c,d)}throw Error("Invalid arity: "+(arguments.length-1));};a.c=function(a,c){return this.V(null,c)};a.l=function(a,c,d){return this.I(null,c,d)};return a}();g.apply=function(a,b){return this.call.apply(this,[this].concat(Gb(b)))};g.h=function(a){return this.V(null,a)};g.c=function(a,b){return this.I(null,a,b)};var ph=new Jh(null,0,null,!1,null,Ed);
+function Pe(a,b){for(var c=a.length,d=0,e=Oc(ph);;)if(d<c){var f=d+1;e=e.Cc(null,a[d],b[d]);d=f}else return Qc(e)}Jh.prototype[Fb]=function(){return yd(this)};function Kh(a,b,c,d,e){this.la=a;this.root=b;this.count=c;this.cb=d;this.eb=e;this.m=258;this.J=56}
+function Lh(a,b,c){if(a.la){if(null==b)a.eb!==c&&(a.eb=c),a.cb||(a.count+=1,a.cb=!0);else{var d=new qh;b=(null==a.root?zh:a.root).Kb(a.la,0,od(b),b,c,d);b!==a.root&&(a.root=b);d.H&&(a.count+=1)}return a}throw Error("assoc! after persistent!");}g=Kh.prototype;g.W=function(){if(this.la)return this.count;throw Error("count after persistent!");};g.V=function(a,b){return null==b?this.cb?this.eb:null:null==this.root?null:this.root.sc(0,od(b),b)};
+g.I=function(a,b,c){return null==b?this.cb?this.eb:c:null==this.root?c:this.root.sc(0,od(b),b,c)};g.Dc=function(a,b){a:if(this.la)if(null!=b?b.m&2048||q===b.sf||(b.m?0:Ab(hc,b)):Ab(hc,b))var c=Lh(this,jc(b),kc(b));else{c=E(b);for(var d=this;;){var e=y(c);if(t(e))c=z(c),d=Lh(d,jc(e),kc(e));else{c=d;break a}}}else throw Error("conj! after persistent");return c};
+g.kd=function(){if(this.la){this.la=null;var a=new Jh(null,this.count,this.root,this.cb,this.eb,null)}else throw Error("persistent! called twice");return a};g.Cc=function(a,b,c){return Lh(this,b,c)};function Mh(a,b,c){for(var d=b;;)if(null!=a)b=c?a.left:a.right,d=ge.c(d,a),a=b;else return d}function Nh(a,b,c,d,e){this.meta=a;this.stack=b;this.vc=c;this.F=d;this.w=e;this.m=32374990;this.J=0}g=Nh.prototype;g.toString=function(){return fd(this)};g.equiv=function(a){return this.K(null,a)};
+g.indexOf=function(){var a=null;a=function(a,c){switch(arguments.length){case 1:return Ud(this,a,0);case 2:return Ud(this,a,c)}throw Error("Invalid arity: "+(arguments.length-1));};a.h=function(a){return Ud(this,a,0)};a.c=function(a,c){return Ud(this,a,c)};return a}();
+g.lastIndexOf=function(){function a(a){return Xd(this,a,H(this))}var b=null;b=function(b,d){switch(arguments.length){case 1:return a.call(this,b);case 2:return Xd(this,b,d)}throw Error("Invalid arity: "+(arguments.length-1));};b.h=a;b.c=function(a,b){return Xd(this,a,b)};return b}();g.P=function(){return this.meta};g.Ka=function(){var a=y(this.stack);a=Mh(this.vc?a.right:a.left,z(this.stack),this.vc);return null==a?null:new Nh(null,a,this.vc,this.F-1,null)};
+g.W=function(){return 0>this.F?H(z(this))+1:this.F};g.U=function(){var a=this.w;return null!=a?a:this.w=a=Ad(this)};g.K=function(a,b){return $d(this,b)};g.oa=function(){return tc(wd,this.meta)};g.Fa=function(a,b){return ce(b,this)};g.Ga=function(a,b,c){return de(b,c,this)};g.Ia=function(){var a=this.stack;return null==a?null:nc(a)};g.bb=function(){var a=y(this.stack);a=Mh(this.vc?a.right:a.left,z(this.stack),this.vc);return null!=a?new Nh(null,a,this.vc,this.F-1,null):wd};g.S=function(){return this};
+g.T=function(a,b){return new Nh(b,this.stack,this.vc,this.F,this.w)};g.X=function(a,b){return ae(b,this)};Nh.prototype[Fb]=function(){return yd(this)};function Oh(a,b,c){return new Nh(null,Mh(a,null,b),b,c,null)}
+function Ph(a,b,c,d){return c instanceof Qh?c.left instanceof Qh?new Qh(c.key,c.H,c.left.bc(),new Rh(a,b,c.right,d,null),null):c.right instanceof Qh?new Qh(c.right.key,c.right.H,new Rh(c.key,c.H,c.left,c.right.left,null),new Rh(a,b,c.right.right,d,null),null):new Rh(a,b,c,d,null):new Rh(a,b,c,d,null)}
+function Sh(a,b,c,d){return d instanceof Qh?d.right instanceof Qh?new Qh(d.key,d.H,new Rh(a,b,c,d.left,null),d.right.bc(),null):d.left instanceof Qh?new Qh(d.left.key,d.left.H,new Rh(a,b,c,d.left.left,null),new Rh(d.key,d.H,d.left.right,d.right,null),null):new Rh(a,b,c,d,null):new Rh(a,b,c,d,null)}
+function Th(a,b,c,d){if(c instanceof Qh)return new Qh(a,b,c.bc(),d,null);if(d instanceof Rh)return Sh(a,b,c,d.ud());if(d instanceof Qh&&d.left instanceof Rh)return new Qh(d.left.key,d.left.H,new Rh(a,b,c,d.left.left,null),Sh(d.key,d.H,d.left.right,d.right.ud()),null);throw Error("red-black tree invariant violation");}
+function Uh(a,b,c,d){if(d instanceof Qh)return new Qh(a,b,c,d.bc(),null);if(c instanceof Rh)return Ph(a,b,c.ud(),d);if(c instanceof Qh&&c.right instanceof Rh)return new Qh(c.right.key,c.right.H,Ph(c.key,c.H,c.left.ud(),c.right.left),new Rh(a,b,c.right.right,d,null),null);throw Error("red-black tree invariant violation");}
+var Vh=function Vh(a,b,c){var e=null!=a.left?function(){var e=a.left;return Vh.l?Vh.l(e,b,c):Vh.call(null,e,b,c)}():c;if(Hd(e))return e;var f=function(){var c=a.key,f=a.H;return b.l?b.l(e,c,f):b.call(null,e,c,f)}();if(Hd(f))return f;if(null!=a.right){var h=a.right;return Vh.l?Vh.l(h,b,f):Vh.call(null,h,b,f)}return f};function Rh(a,b,c,d,e){this.key=a;this.H=b;this.left=c;this.right=d;this.w=e;this.m=32402207;this.J=0}g=Rh.prototype;
+g.lastIndexOf=function(){function a(a){return Xd(this,a,H(this))}var b=null;b=function(b,d){switch(arguments.length){case 1:return a.call(this,b);case 2:return Xd(this,b,d)}throw Error("Invalid arity: "+(arguments.length-1));};b.h=a;b.c=function(a,b){return Xd(this,a,b)};return b}();
+g.indexOf=function(){var a=null;a=function(a,c){switch(arguments.length){case 1:return Ud(this,a,0);case 2:return Ud(this,a,c)}throw Error("Invalid arity: "+(arguments.length-1));};a.h=function(a){return Ud(this,a,0)};a.c=function(a,c){return Ud(this,a,c)};return a}();g.Ee=function(a){return a.He(this)};g.ud=function(){return new Qh(this.key,this.H,this.left,this.right,null)};g.bc=function(){return this};g.De=function(a){return a.Ge(this)};g.replace=function(a,b,c,d){return new Rh(a,b,c,d,null)};
+g.Ge=function(a){return new Rh(a.key,a.H,this,a.right,null)};g.He=function(a){return new Rh(a.key,a.H,a.left,this,null)};g.Jc=function(a,b){return Vh(this,a,b)};g.V=function(a,b){return this.ka(null,b,null)};g.I=function(a,b,c){return this.ka(null,b,c)};g.$=function(a,b){if(0===b)return this.key;if(1===b)return this.H;throw Error("Index out of bounds");};g.ka=function(a,b,c){return 0===b?this.key:1===b?this.H:c};g.dc=function(a,b,c){return(new R(null,2,5,T,[this.key,this.H],null)).dc(null,b,c)};
+g.P=function(){return null};g.W=function(){return 2};g.fd=function(){return this.key};g.gd=function(){return this.H};g.Ac=function(){return this.H};g.Bc=function(){return new R(null,1,5,T,[this.key],null)};g.U=function(){var a=this.w;return null!=a?a:this.w=a=Ad(this)};g.K=function(a,b){return $d(this,b)};g.oa=function(){return he};g.Fa=function(a,b){return Kd(this,b)};g.Ga=function(a,b,c){return Ld(this,b,c)};g.O=function(a,b,c){return K.l(new R(null,2,5,T,[this.key,this.H],null),b,c)};
+g.yc=function(a,b){return 0===b||1===b};g.S=function(){var a=this.key;return Tb(Tb(wd,this.H),a)};g.T=function(a,b){return tc(new R(null,2,5,T,[this.key,this.H],null),b)};g.X=function(a,b){return new R(null,3,5,T,[this.key,this.H,b],null)};
+g.call=function(){var a=null;a=function(a,c,d){switch(arguments.length){case 2:return this.$(null,c);case 3:return this.ka(null,c,d)}throw Error("Invalid arity: "+(arguments.length-1));};a.c=function(a,c){return this.$(null,c)};a.l=function(a,c,d){return this.ka(null,c,d)};return a}();g.apply=function(a,b){return this.call.apply(this,[this].concat(Gb(b)))};g.h=function(a){return this.$(null,a)};g.c=function(a,b){return this.ka(null,a,b)};Rh.prototype[Fb]=function(){return yd(this)};
+function Qh(a,b,c,d,e){this.key=a;this.H=b;this.left=c;this.right=d;this.w=e;this.m=32402207;this.J=0}g=Qh.prototype;g.lastIndexOf=function(){function a(a){return Xd(this,a,H(this))}var b=null;b=function(b,d){switch(arguments.length){case 1:return a.call(this,b);case 2:return Xd(this,b,d)}throw Error("Invalid arity: "+(arguments.length-1));};b.h=a;b.c=function(a,b){return Xd(this,a,b)};return b}();
+g.indexOf=function(){var a=null;a=function(a,c){switch(arguments.length){case 1:return Ud(this,a,0);case 2:return Ud(this,a,c)}throw Error("Invalid arity: "+(arguments.length-1));};a.h=function(a){return Ud(this,a,0)};a.c=function(a,c){return Ud(this,a,c)};return a}();g.Ee=function(a){return new Qh(this.key,this.H,this.left,a,null)};g.ud=function(){throw Error("red-black tree invariant violation");};g.bc=function(){return new Rh(this.key,this.H,this.left,this.right,null)};
+g.De=function(a){return new Qh(this.key,this.H,a,this.right,null)};g.replace=function(a,b,c,d){return new Qh(a,b,c,d,null)};g.Ge=function(a){return this.left instanceof Qh?new Qh(this.key,this.H,this.left.bc(),new Rh(a.key,a.H,this.right,a.right,null),null):this.right instanceof Qh?new Qh(this.right.key,this.right.H,new Rh(this.key,this.H,this.left,this.right.left,null),new Rh(a.key,a.H,this.right.right,a.right,null),null):new Rh(a.key,a.H,this,a.right,null)};
+g.He=function(a){return this.right instanceof Qh?new Qh(this.key,this.H,new Rh(a.key,a.H,a.left,this.left,null),this.right.bc(),null):this.left instanceof Qh?new Qh(this.left.key,this.left.H,new Rh(a.key,a.H,a.left,this.left.left,null),new Rh(this.key,this.H,this.left.right,this.right,null),null):new Rh(a.key,a.H,a.left,this,null)};g.Jc=function(a,b){return Vh(this,a,b)};g.V=function(a,b){return this.ka(null,b,null)};g.I=function(a,b,c){return this.ka(null,b,c)};
+g.$=function(a,b){if(0===b)return this.key;if(1===b)return this.H;throw Error("Index out of bounds");};g.ka=function(a,b,c){return 0===b?this.key:1===b?this.H:c};g.dc=function(a,b,c){return(new R(null,2,5,T,[this.key,this.H],null)).dc(null,b,c)};g.P=function(){return null};g.W=function(){return 2};g.fd=function(){return this.key};g.gd=function(){return this.H};g.Ac=function(){return this.H};g.Bc=function(){return new R(null,1,5,T,[this.key],null)};
+g.U=function(){var a=this.w;return null!=a?a:this.w=a=Ad(this)};g.K=function(a,b){return $d(this,b)};g.oa=function(){return he};g.Fa=function(a,b){return Kd(this,b)};g.Ga=function(a,b,c){return Ld(this,b,c)};g.O=function(a,b,c){return K.l(new R(null,2,5,T,[this.key,this.H],null),b,c)};g.yc=function(a,b){return 0===b||1===b};g.S=function(){var a=this.key;return Tb(Tb(wd,this.H),a)};g.T=function(a,b){return tc(new R(null,2,5,T,[this.key,this.H],null),b)};
+g.X=function(a,b){return new R(null,3,5,T,[this.key,this.H,b],null)};g.call=function(){var a=null;a=function(a,c,d){switch(arguments.length){case 2:return this.$(null,c);case 3:return this.ka(null,c,d)}throw Error("Invalid arity: "+(arguments.length-1));};a.c=function(a,c){return this.$(null,c)};a.l=function(a,c,d){return this.ka(null,c,d)};return a}();g.apply=function(a,b){return this.call.apply(this,[this].concat(Gb(b)))};g.h=function(a){return this.$(null,a)};
+g.c=function(a,b){return this.ka(null,a,b)};Qh.prototype[Fb]=function(){return yd(this)};
+var Wh=function Wh(a,b,c,d,e){if(null==b)return new Qh(c,d,null,null,null);var h=function(){var d=b.key;return a.c?a.c(c,d):a.call(null,c,d)}();if(0===h)return e[0]=b,null;if(0>h)return h=function(){var h=b.left;return Wh.Z?Wh.Z(a,h,c,d,e):Wh.call(null,a,h,c,d,e)}(),null!=h?b.De(h):null;h=function(){var h=b.right;return Wh.Z?Wh.Z(a,h,c,d,e):Wh.call(null,a,h,c,d,e)}();return null!=h?b.Ee(h):null},Xh=function Xh(a,b){if(null==a)return b;if(null==b)return a;if(a instanceof Qh){if(b instanceof Qh){var d=
+function(){var d=a.right,f=b.left;return Xh.c?Xh.c(d,f):Xh.call(null,d,f)}();return d instanceof Qh?new Qh(d.key,d.H,new Qh(a.key,a.H,a.left,d.left,null),new Qh(b.key,b.H,d.right,b.right,null),null):new Qh(a.key,a.H,a.left,new Qh(b.key,b.H,d,b.right,null),null)}return new Qh(a.key,a.H,a.left,function(){var d=a.right;return Xh.c?Xh.c(d,b):Xh.call(null,d,b)}(),null)}if(b instanceof Qh)return new Qh(b.key,b.H,function(){var d=b.left;return Xh.c?Xh.c(a,d):Xh.call(null,a,d)}(),b.right,null);d=function(){var d=
+a.right,f=b.left;return Xh.c?Xh.c(d,f):Xh.call(null,d,f)}();return d instanceof Qh?new Qh(d.key,d.H,new Rh(a.key,a.H,a.left,d.left,null),new Rh(b.key,b.H,d.right,b.right,null),null):Th(a.key,a.H,a.left,new Rh(b.key,b.H,d,b.right,null))},Yh=function Yh(a,b,c,d){if(null!=b){var f=function(){var d=b.key;return a.c?a.c(c,d):a.call(null,c,d)}();if(0===f)return d[0]=b,Xh(b.left,b.right);if(0>f)return f=function(){var f=b.left;return Yh.M?Yh.M(a,f,c,d):Yh.call(null,a,f,c,d)}(),null!=f||null!=d[0]?b.left instanceof
+Rh?Th(b.key,b.H,f,b.right):new Qh(b.key,b.H,f,b.right,null):null;f=function(){var f=b.right;return Yh.M?Yh.M(a,f,c,d):Yh.call(null,a,f,c,d)}();return null!=f||null!=d[0]?b.right instanceof Rh?Uh(b.key,b.H,b.left,f):new Qh(b.key,b.H,b.left,f,null):null}return null},Zh=function Zh(a,b,c,d){var f=b.key,h=a.c?a.c(c,f):a.call(null,c,f);return 0===h?b.replace(f,d,b.left,b.right):0>h?b.replace(f,b.H,function(){var f=b.left;return Zh.M?Zh.M(a,f,c,d):Zh.call(null,a,f,c,d)}(),b.right):b.replace(f,b.H,b.left,
+function(){var f=b.right;return Zh.M?Zh.M(a,f,c,d):Zh.call(null,a,f,c,d)}())};function $h(a,b,c,d,e){this.Bb=a;this.mc=b;this.F=c;this.meta=d;this.w=e;this.m=418776847;this.J=8192}g=$h.prototype;g.forEach=function(a){for(var b=E(this),c=null,d=0,e=0;;)if(e<d){var f=c.$(null,e),h=J(f,0,null);f=J(f,1,null);a.c?a.c(f,h):a.call(null,f,h);e+=1}else if(b=E(b))Ae(b)?(c=Wc(b),b=Xc(b),h=c,d=H(c),c=h):(c=y(b),h=J(c,0,null),f=J(c,1,null),a.c?a.c(f,h):a.call(null,f,h),b=z(b),c=null,d=0),e=0;else return null};
+g.get=function(a,b){return this.I(null,a,b)};g.entries=function(){return new gh(E(E(this)))};g.toString=function(){return fd(this)};g.keys=function(){return yd(lh(this))};g.values=function(){return yd(mh(this))};g.equiv=function(a){return this.K(null,a)};function ai(a,b){for(var c=a.mc;;)if(null!=c){var d=c.key;d=a.Bb.c?a.Bb.c(b,d):a.Bb.call(null,b,d);if(0===d)return c;c=0>d?c.left:c.right}else return null}g.has=function(a){return He(this,a)};g.V=function(a,b){return this.I(null,b,null)};
+g.I=function(a,b,c){a=ai(this,b);return null!=a?a.H:c};g.Qc=function(a,b,c){return null!=this.mc?Jd(Vh(this.mc,b,c)):c};g.P=function(){return this.meta};g.W=function(){return this.F};g.Rc=function(){return 0<this.F?Oh(this.mc,!1,this.F):null};g.U=function(){var a=this.w;return null!=a?a:this.w=a=Dd(this)};g.K=function(a,b){return eh(this,b)};g.oa=function(){return new $h(this.Bb,null,0,this.meta,0)};
+g.ga=function(a,b){var c=[null],d=Yh(this.Bb,this.mc,b,c);return null==d?null==Vd(c,0)?this:new $h(this.Bb,null,0,this.meta,null):new $h(this.Bb,d.bc(),this.F-1,this.meta,null)};g.O=function(a,b,c){a=[null];var d=Wh(this.Bb,this.mc,b,c,a);return null==d?(a=Vd(a,0),G.c(c,a.H)?this:new $h(this.Bb,Zh(this.Bb,this.mc,b,c),this.F,this.meta,null)):new $h(this.Bb,d.bc(),this.F+1,this.meta,null)};g.yc=function(a,b){return null!=ai(this,b)};g.S=function(){return 0<this.F?Oh(this.mc,!0,this.F):null};
+g.T=function(a,b){return new $h(this.Bb,this.mc,this.F,b,this.w)};g.X=function(a,b){if(ze(b))return this.O(null,A.c(b,0),A.c(b,1));for(var c=this,d=E(b);;){if(null==d)return c;var e=y(d);if(ze(e))c=c.O(null,A.c(e,0),A.c(e,1)),d=z(d);else throw Error("conj on a map takes map entries or seqables of map entries");}};
+g.call=function(){var a=null;a=function(a,c,d){switch(arguments.length){case 2:return this.V(null,c);case 3:return this.I(null,c,d)}throw Error("Invalid arity: "+(arguments.length-1));};a.c=function(a,c){return this.V(null,c)};a.l=function(a,c,d){return this.I(null,c,d)};return a}();g.apply=function(a,b){return this.call.apply(this,[this].concat(Gb(b)))};g.h=function(a){return this.V(null,a)};g.c=function(a,b){return this.I(null,a,b)};var bi=new $h(Ke,null,0,null,Ed);$h.prototype[Fb]=function(){return yd(this)};
+var U=function U(a){for(var c=[],d=arguments.length,e=0;;)if(e<d)c.push(arguments[e]),e+=1;else break;return U.A(0<c.length?new Jb(c.slice(0),0,null):null)};U.A=function(a){for(var b=E(a),c=Oc(ph);;)if(b){a=z(z(b));var d=y(b);b=ee(b);c=Rc(c,d,b);b=a}else return Qc(c)};U.L=0;U.N=function(a){return U.A(E(a))};var ci=function ci(a){for(var c=[],d=arguments.length,e=0;;)if(e<d)c.push(arguments[e]),e+=1;else break;return ci.A(0<c.length?new Jb(c.slice(0),0,null):null)};
+ci.A=function(a){a=a instanceof Jb&&0===a.i?a.o:Lb(a);return ke(a)};ci.L=0;ci.N=function(a){return ci.A(E(a))};function di(a){for(var b=[],c=arguments.length,d=0;;)if(d<c)b.push(arguments[d]),d+=1;else break;a:for(b=E(0<b.length?new Jb(b.slice(0),0,null):null),d=bi;;)if(b)c=z(z(b)),d=K.l(d,y(b),ee(b)),b=c;else break a;return d}function ei(a,b){this.da=a;this.hb=b;this.m=32374988;this.J=0}g=ei.prototype;g.toString=function(){return fd(this)};g.equiv=function(a){return this.K(null,a)};
+g.indexOf=function(){var a=null;a=function(a,c){switch(arguments.length){case 1:return Ud(this,a,0);case 2:return Ud(this,a,c)}throw Error("Invalid arity: "+(arguments.length-1));};a.h=function(a){return Ud(this,a,0)};a.c=function(a,c){return Ud(this,a,c)};return a}();
+g.lastIndexOf=function(){function a(a){return Xd(this,a,H(this))}var b=null;b=function(b,d){switch(arguments.length){case 1:return a.call(this,b);case 2:return Xd(this,b,d)}throw Error("Invalid arity: "+(arguments.length-1));};b.h=a;b.c=function(a,b){return Xd(this,a,b)};return b}();g.P=function(){return this.hb};g.Ka=function(){var a=(null!=this.da?this.da.m&128||q===this.da.Id||(this.da.m?0:Ab(Zb,this.da)):Ab(Zb,this.da))?this.da.Ka(null):z(this.da);return null==a?null:new ei(a,this.hb)};g.U=function(){return Ad(this)};
+g.K=function(a,b){return $d(this,b)};g.oa=function(){return tc(wd,this.hb)};g.Fa=function(a,b){return ce(b,this)};g.Ga=function(a,b,c){return de(b,c,this)};g.Ia=function(){return this.da.Ia(null).fd(null)};g.bb=function(){var a=(null!=this.da?this.da.m&128||q===this.da.Id||(this.da.m?0:Ab(Zb,this.da)):Ab(Zb,this.da))?this.da.Ka(null):z(this.da);return null!=a?new ei(a,this.hb):wd};g.S=function(){return this};g.T=function(a,b){return new ei(this.da,b)};g.X=function(a,b){return ae(b,this)};
+ei.prototype[Fb]=function(){return yd(this)};function lh(a){return(a=E(a))?new ei(a,null):null}function fi(a){return jc(a)}function gi(a,b){this.da=a;this.hb=b;this.m=32374988;this.J=0}g=gi.prototype;g.toString=function(){return fd(this)};g.equiv=function(a){return this.K(null,a)};
+g.indexOf=function(){var a=null;a=function(a,c){switch(arguments.length){case 1:return Ud(this,a,0);case 2:return Ud(this,a,c)}throw Error("Invalid arity: "+(arguments.length-1));};a.h=function(a){return Ud(this,a,0)};a.c=function(a,c){return Ud(this,a,c)};return a}();
+g.lastIndexOf=function(){function a(a){return Xd(this,a,H(this))}var b=null;b=function(b,d){switch(arguments.length){case 1:return a.call(this,b);case 2:return Xd(this,b,d)}throw Error("Invalid arity: "+(arguments.length-1));};b.h=a;b.c=function(a,b){return Xd(this,a,b)};return b}();g.P=function(){return this.hb};g.Ka=function(){var a=(null!=this.da?this.da.m&128||q===this.da.Id||(this.da.m?0:Ab(Zb,this.da)):Ab(Zb,this.da))?this.da.Ka(null):z(this.da);return null==a?null:new gi(a,this.hb)};g.U=function(){return Ad(this)};
+g.K=function(a,b){return $d(this,b)};g.oa=function(){return tc(wd,this.hb)};g.Fa=function(a,b){return ce(b,this)};g.Ga=function(a,b,c){return de(b,c,this)};g.Ia=function(){return this.da.Ia(null).gd(null)};g.bb=function(){var a=(null!=this.da?this.da.m&128||q===this.da.Id||(this.da.m?0:Ab(Zb,this.da)):Ab(Zb,this.da))?this.da.Ka(null):z(this.da);return null!=a?new gi(a,this.hb):wd};g.S=function(){return this};g.T=function(a,b){return new gi(this.da,b)};g.X=function(a,b){return ae(b,this)};
+gi.prototype[Fb]=function(){return yd(this)};function mh(a){return(a=E(a))?new gi(a,null):null}var hi=function hi(a){for(var c=[],d=arguments.length,e=0;;)if(e<d)c.push(arguments[e]),e+=1;else break;return hi.A(0<c.length?new Jb(c.slice(0),0,null):null)};hi.A=function(a){return t(Wf(Ve,a))?Te(function(a,c){return ge.c(t(a)?a:Ef,c)},a):null};hi.L=0;hi.N=function(a){return hi.A(E(a))};
+var ii=function ii(a){for(var c=[],d=arguments.length,e=0;;)if(e<d)c.push(arguments[e]),e+=1;else break;return ii.A(arguments[0],1<c.length?new Jb(c.slice(1),0,null):null)};ii.A=function(a,b){return t(Wf(Ve,b))?Te(function(a){return function(b,c){return Mb(a,t(b)?b:Ef,E(c))}}(function(b,d){var c=y(d),f=ee(d);return He(b,c)?K.l(b,c,function(){var d=D.c(b,c);return a.c?a.c(d,f):a.call(null,d,f)}()):K.l(b,c,f)}),b):null};ii.L=1;ii.N=function(a){var b=y(a);a=z(a);return ii.A(b,a)};
+function ji(a){for(var b=Ef,c=E(new R(null,7,5,T,[ki,li,mi,ni,oi,pi,qi],null));;)if(c){var d=y(c),e=D.l(a,d,ri);b=G.c(e,ri)?b:K.l(b,d,e);c=z(c)}else return tc(b,qe(a))}function si(a){this.te=a}si.prototype.ja=function(){return this.te.ja()};si.prototype.next=function(){if(this.te.ja())return this.te.next().fa[0];throw Error("No such element");};si.prototype.remove=function(){return Error("Unsupported operation")};function ti(a,b,c){this.meta=a;this.gc=b;this.w=c;this.m=15077647;this.J=139268}g=ti.prototype;
+g.toString=function(){return fd(this)};g.equiv=function(a){return this.K(null,a)};g.keys=function(){return yd(E(this))};g.entries=function(){return new hh(E(E(this)))};g.values=function(){return yd(E(this))};g.has=function(a){return He(this,a)};
+g.forEach=function(a){for(var b=E(this),c=null,d=0,e=0;;)if(e<d){var f=c.$(null,e),h=J(f,0,null);f=J(f,1,null);a.c?a.c(f,h):a.call(null,f,h);e+=1}else if(b=E(b))Ae(b)?(c=Wc(b),b=Xc(b),h=c,d=H(c),c=h):(c=y(b),h=J(c,0,null),f=J(c,1,null),a.c?a.c(f,h):a.call(null,f,h),b=z(b),c=null,d=0),e=0;else return null};g.V=function(a,b){return this.I(null,b,null)};g.I=function(a,b,c){return dc(this.gc,b)?b:c};g.ba=function(){return new si(dd(this.gc))};g.P=function(){return this.meta};g.W=function(){return Qb(this.gc)};
+g.U=function(){var a=this.w;return null!=a?a:this.w=a=Dd(this)};g.K=function(a,b){return ve(b)&&H(this)===H(b)&&Ue(function(){return function(a,d){var c=He(b,d);return c?c:new Gd(!1)}}(this),!0,this.gc)};g.Pc=function(){return new ui(Oc(this.gc))};g.oa=function(){return tc(vi,this.meta)};g.ie=function(a,b){return new ti(this.meta,gc(this.gc,b),null)};g.S=function(){return lh(this.gc)};g.T=function(a,b){return new ti(b,this.gc,this.w)};
+g.X=function(a,b){return new ti(this.meta,K.l(this.gc,b,null),null)};g.call=function(){var a=null;a=function(a,c,d){switch(arguments.length){case 2:return this.V(null,c);case 3:return this.I(null,c,d)}throw Error("Invalid arity: "+(arguments.length-1));};a.c=function(a,c){return this.V(null,c)};a.l=function(a,c,d){return this.I(null,c,d)};return a}();g.apply=function(a,b){return this.call.apply(this,[this].concat(Gb(b)))};g.h=function(a){return this.V(null,a)};
+g.c=function(a,b){return this.I(null,a,b)};var vi=new ti(null,Ef,Ed);function Je(a){for(var b=a.length,c=Oc(vi),d=0;;)if(d<b)Pc(c,a[d]),d+=1;else break;return Qc(c)}ti.prototype[Fb]=function(){return yd(this)};function ui(a){this.lc=a;this.J=136;this.m=259}g=ui.prototype;g.Dc=function(a,b){this.lc=Rc(this.lc,b,null);return this};g.kd=function(){return new ti(null,Qc(this.lc),null)};g.W=function(){return H(this.lc)};g.V=function(a,b){return this.I(null,b,null)};
+g.I=function(a,b,c){return cc.l(this.lc,b,Ce)===Ce?c:b};g.call=function(){function a(a,b,c){return cc.l(this.lc,b,Ce)===Ce?c:b}function b(a,b){return cc.l(this.lc,b,Ce)===Ce?null:b}var c=null;c=function(c,e,f){switch(arguments.length){case 2:return b.call(this,0,e);case 3:return a.call(this,0,e,f)}throw Error("Invalid arity: "+(arguments.length-1));};c.c=b;c.l=a;return c}();g.apply=function(a,b){return this.call.apply(this,[this].concat(Gb(b)))};
+g.h=function(a){return cc.l(this.lc,a,Ce)===Ce?null:a};g.c=function(a,b){return cc.l(this.lc,a,Ce)===Ce?b:a};function wi(a,b,c){this.meta=a;this.$b=b;this.w=c;this.m=417730831;this.J=8192}g=wi.prototype;g.toString=function(){return fd(this)};g.equiv=function(a){return this.K(null,a)};g.keys=function(){return yd(E(this))};g.entries=function(){return new hh(E(E(this)))};g.values=function(){return yd(E(this))};g.has=function(a){return He(this,a)};
+g.forEach=function(a){for(var b=E(this),c=null,d=0,e=0;;)if(e<d){var f=c.$(null,e),h=J(f,0,null);f=J(f,1,null);a.c?a.c(f,h):a.call(null,f,h);e+=1}else if(b=E(b))Ae(b)?(c=Wc(b),b=Xc(b),h=c,d=H(c),c=h):(c=y(b),h=J(c,0,null),f=J(c,1,null),a.c?a.c(f,h):a.call(null,f,h),b=z(b),c=null,d=0),e=0;else return null};g.V=function(a,b){return this.I(null,b,null)};g.I=function(a,b,c){a=ai(this.$b,b);return null!=a?a.key:c};g.P=function(){return this.meta};g.W=function(){return H(this.$b)};
+g.Rc=function(){return 0<H(this.$b)?ig.c(fi,Ic(this.$b)):null};g.U=function(){var a=this.w;return null!=a?a:this.w=a=Dd(this)};g.K=function(a,b){return ve(b)&&H(this)===H(b)&&Ue(function(){return function(a,d){var c=He(b,d);return c?c:new Gd(!1)}}(this),!0,this.$b)};g.oa=function(){return new wi(this.meta,Rb(this.$b),0)};g.ie=function(a,b){return new wi(this.meta,le.c(this.$b,b),null)};g.S=function(){return lh(this.$b)};g.T=function(a,b){return new wi(b,this.$b,this.w)};
+g.X=function(a,b){return new wi(this.meta,K.l(this.$b,b,null),null)};g.call=function(){var a=null;a=function(a,c,d){switch(arguments.length){case 2:return this.V(null,c);case 3:return this.I(null,c,d)}throw Error("Invalid arity: "+(arguments.length-1));};a.c=function(a,c){return this.V(null,c)};a.l=function(a,c,d){return this.I(null,c,d)};return a}();g.apply=function(a,b){return this.call.apply(this,[this].concat(Gb(b)))};g.h=function(a){return this.V(null,a)};
+g.c=function(a,b){return this.I(null,a,b)};var xi=new wi(null,bi,Ed);wi.prototype[Fb]=function(){return yd(this)};function yi(a){a=E(a);if(null==a)return vi;if(a instanceof Jb&&0===a.i)return Je(a.o);for(var b=Oc(vi);;)if(null!=a){var c=z(a);b=b.Dc(null,a.Ia(null));a=c}else return Qc(b)}var zi=function zi(a){for(var c=[],d=arguments.length,e=0;;)if(e<d)c.push(arguments[e]),e+=1;else break;return zi.A(0<c.length?new Jb(c.slice(0),0,null):null)};zi.A=function(a){return Mb(Tb,xi,a)};zi.L=0;zi.N=function(a){return zi.A(E(a))};
+function jf(a){if(null!=a&&(a.J&4096||q===a.Oe))return a.hd(null);if("string"===typeof a)return a;throw Error(["Doesn't support name: ",v.h(a)].join(""));}var Ai=function Ai(a){switch(arguments.length){case 2:return Ai.c(arguments[0],arguments[1]);case 3:return Ai.l(arguments[0],arguments[1],arguments[2]);default:for(var c=[],d=arguments.length,e=0;;)if(e<d)c.push(arguments[e]),e+=1;else break;return Ai.A(arguments[0],arguments[1],arguments[2],new Jb(c.slice(3),0,null))}};Ai.c=function(a,b){return b};
+Ai.l=function(a,b,c){return(a.h?a.h(b):a.call(null,b))>(a.h?a.h(c):a.call(null,c))?b:c};Ai.A=function(a,b,c,d){return Mb(function(b,c){return Ai.l(a,b,c)},Ai.l(a,b,c),d)};Ai.N=function(a){var b=y(a),c=z(a);a=y(c);var d=z(c);c=y(d);d=z(d);return Ai.A(b,a,c,d)};Ai.L=3;function Bi(a,b){return new kf(null,function(){var c=E(b);if(c){var d=y(c);d=a.h?a.h(d):a.call(null,d);c=t(d)?ae(y(c),Bi(a,vd(c))):null}else c=null;return c},null,null)}function Di(a,b,c){this.i=a;this.end=b;this.step=c}
+Di.prototype.ja=function(){return 0<this.step?this.i<this.end:this.i>this.end};Di.prototype.next=function(){var a=this.i;this.i+=this.step;return a};function Ei(a,b,c,d,e){this.meta=a;this.start=b;this.end=c;this.step=d;this.w=e;this.m=32375006;this.J=139264}g=Ei.prototype;g.toString=function(){return fd(this)};g.equiv=function(a){return this.K(null,a)};
+g.indexOf=function(){var a=null;a=function(a,c){switch(arguments.length){case 1:return Ud(this,a,0);case 2:return Ud(this,a,c)}throw Error("Invalid arity: "+(arguments.length-1));};a.h=function(a){return Ud(this,a,0)};a.c=function(a,c){return Ud(this,a,c)};return a}();
+g.lastIndexOf=function(){function a(a){return Xd(this,a,H(this))}var b=null;b=function(b,d){switch(arguments.length){case 1:return a.call(this,b);case 2:return Xd(this,b,d)}throw Error("Invalid arity: "+(arguments.length-1));};b.h=a;b.c=function(a,b){return Xd(this,a,b)};return b}();g.$=function(a,b){if(0<=b&&b<this.W(null))return this.start+b*this.step;if(0<=b&&this.start>this.end&&0===this.step)return this.start;throw Error("Index out of bounds");};
+g.ka=function(a,b,c){return 0<=b&&b<this.W(null)?this.start+b*this.step:0<=b&&this.start>this.end&&0===this.step?this.start:c};g.ba=function(){return new Di(this.start,this.end,this.step)};g.P=function(){return this.meta};g.Ka=function(){return 0<this.step?this.start+this.step<this.end?new Ei(this.meta,this.start+this.step,this.end,this.step,null):null:this.start+this.step>this.end?new Ei(this.meta,this.start+this.step,this.end,this.step,null):null};
+g.W=function(){return wb(this.S(null))?0:Math.ceil((this.end-this.start)/this.step)};g.U=function(){var a=this.w;return null!=a?a:this.w=a=Ad(this)};g.K=function(a,b){return $d(this,b)};g.oa=function(){return tc(wd,this.meta)};g.Fa=function(a,b){return Kd(this,b)};g.Ga=function(a,b,c){for(a=this.start;;)if(0<this.step?a<this.end:a>this.end){c=b.c?b.c(c,a):b.call(null,c,a);if(Hd(c))return B(c);a+=this.step}else return c};g.Ia=function(){return null==this.S(null)?null:this.start};
+g.bb=function(){return null!=this.S(null)?new Ei(this.meta,this.start+this.step,this.end,this.step,null):wd};g.S=function(){return 0<this.step?this.start<this.end?this:null:0>this.step?this.start>this.end?this:null:this.start===this.end?null:this};g.T=function(a,b){return new Ei(b,this.start,this.end,this.step,this.w)};g.X=function(a,b){return ae(b,this)};Ei.prototype[Fb]=function(){return yd(this)};function Fi(a,b,c){return new Ei(null,a,b,c,null)}
+function Gi(a,b){return new R(null,2,5,T,[Bi(a,b),ng(a,b)],null)}
+function Hi(a){var b=y;return function(){function c(c,d,e){return new R(null,2,5,T,[b.l?b.l(c,d,e):b.call(null,c,d,e),a.l?a.l(c,d,e):a.call(null,c,d,e)],null)}function d(c,d){return new R(null,2,5,T,[b.c?b.c(c,d):b.call(null,c,d),a.c?a.c(c,d):a.call(null,c,d)],null)}function e(c){return new R(null,2,5,T,[b.h?b.h(c):b.call(null,c),a.h?a.h(c):a.call(null,c)],null)}function f(){return new R(null,2,5,T,[b.B?b.B():b.call(null),a.B?a.B():a.call(null)],null)}var h=null,k=function(){function c(a,b,c,e){var f=
+null;if(3<arguments.length){f=0;for(var h=Array(arguments.length-3);f<h.length;)h[f]=arguments[f+3],++f;f=new Jb(h,0,null)}return d.call(this,a,b,c,f)}function d(c,d,e,f){return new R(null,2,5,T,[Af(b,c,d,e,f),Af(a,c,d,e,f)],null)}c.L=3;c.N=function(a){var b=y(a);a=z(a);var c=y(a);a=z(a);var e=y(a);a=vd(a);return d(b,c,e,a)};c.A=d;return c}();h=function(a,b,h,u){switch(arguments.length){case 0:return f.call(this);case 1:return e.call(this,a);case 2:return d.call(this,a,b);case 3:return c.call(this,
+a,b,h);default:var m=null;if(3<arguments.length){m=0;for(var l=Array(arguments.length-3);m<l.length;)l[m]=arguments[m+3],++m;m=new Jb(l,0,null)}return k.A(a,b,h,m)}throw Error("Invalid arity: "+(arguments.length-1));};h.L=3;h.N=k.N;h.B=f;h.h=e;h.c=d;h.l=c;h.A=k.A;return h}()}function Ii(a){a:for(var b=a;;)if(E(b))b=z(b);else break a;return a}
+function Ji(a,b){if("string"===typeof b){var c=a.exec(b);return G.c(y(c),b)?1===H(c)?y(c):Wg(c):null}throw new TypeError("re-matches must match against a string.");}
+function Y(a,b,c,d,e,f,h){var k=lb;lb=null==lb?null:lb-1;try{if(null!=lb&&0>lb)return Jc(a,"#");Jc(a,c);if(0===tb.h(f))E(h)&&Jc(a,function(){var a=Ki.h(f);return t(a)?a:"..."}());else{if(E(h)){var l=y(h);b.l?b.l(l,a,f):b.call(null,l,a,f)}for(var p=z(h),m=tb.h(f)-1;;)if(!p||null!=m&&0===m){E(p)&&0===m&&(Jc(a,d),Jc(a,function(){var a=Ki.h(f);return t(a)?a:"..."}()));break}else{Jc(a,d);var u=y(p);c=a;h=f;b.l?b.l(u,c,h):b.call(null,u,c,h);var w=z(p);c=m-1;p=w;m=c}}return Jc(a,e)}finally{lb=k}}
+function Li(a,b){for(var c=E(b),d=null,e=0,f=0;;)if(f<e){var h=d.$(null,f);Jc(a,h);f+=1}else if(c=E(c))d=c,Ae(d)?(c=Wc(d),e=Xc(d),d=c,h=H(c),c=e,e=h):(h=y(d),Jc(a,h),c=z(d),d=null,e=0),f=0;else return null}var Mi={'"':'\\"',"\\":"\\\\","\b":"\\b","\f":"\\f","\n":"\\n","\r":"\\r","\t":"\\t"};function Ni(a){return[v.h('"'),v.h(a.replace(RegExp('[\\\\"\b\f\n\r\t]',"g"),function(a){return Mi[a]})),v.h('"')].join("")}
+function Oi(a,b){var c=Ee(D.c(a,rb));return c?(c=null!=b?b.m&131072||q===b.tf?!0:!1:!1)?null!=qe(b):c:c}
+function Pi(a,b,c){if(null==a)return Jc(b,"nil");Oi(c,a)&&(Jc(b,"^"),Qi(qe(a),b,c),Jc(b," "));if(a.qc)return a.Ec(a,b,c);if(null!=a&&(a.m&2147483648||q===a.ma))return a.R(null,b,c);if(!0===a||!1===a)return Jc(b,""+v.h(a));if("number"===typeof a)return Jc(b,isNaN(a)?"##NaN":a===Number.POSITIVE_INFINITY?"##Inf":a===Number.NEGATIVE_INFINITY?"##-Inf":""+v.h(a));if(null!=a&&a.constructor===Object)return Jc(b,"#js "),Ri(ig.c(function(b){return new R(null,2,5,T,[null!=Ji(/[A-Za-z_\*\+\?!\-'][\w\*\+\?!\-']*/,
+b)?hf.h(b):b,a[b]],null)},Ea(a)),b,c);if(vb(a))return Y(b,Qi,"#js ["," ","]",c,a);if(ca(a))return t(qb.h(c))?Jc(b,Ni(a)):Jc(b,a);if(ha(a)){var d=a.name;c=t(function(){var a=null==d;return a?a:/^[\s\xa0]*$/.test(d)}())?"Function":d;return Li(b,be(["#object[",c,"","]"]))}if(a instanceof Date)return c=function(a,b){for(var c=""+v.h(a);;)if(H(c)<b)c=["0",v.h(c)].join("");else return c},Li(b,be(['#inst "',""+v.h(a.getUTCFullYear()),"-",c(a.getUTCMonth()+1,2),"-",c(a.getUTCDate(),2),"T",c(a.getUTCHours(),
+2),":",c(a.getUTCMinutes(),2),":",c(a.getUTCSeconds(),2),".",c(a.getUTCMilliseconds(),3),"-",'00:00"']));if(a instanceof RegExp)return Li(b,be(['#"',a.source,'"']));if(t(function(){var b=null==a?null:a.constructor;return null==b?null:b.Tb}()))return Li(b,be(["#object[",a.constructor.Tb.replace(RegExp("/","g"),"."),"]"]));d=function(){var b=null==a?null:a.constructor;return null==b?null:b.name}();c=t(function(){var a=null==d;return a?a:/^[\s\xa0]*$/.test(d)}())?"Object":d;return null==a.constructor?
+Li(b,be(["#object[",c,"]"])):Li(b,be(["#object[",c," ",""+v.h(a),"]"]))}function Qi(a,b,c){var d=Si.h(c);return t(d)?(c=K.l(c,Ti,Pi),d.l?d.l(a,b,c):d.call(null,a,b,c)):Pi(a,b,c)}function Ui(a,b){var c=new cb;a:{var d=new ed(c);Qi(y(a),d,b);for(var e=E(z(a)),f=null,h=0,k=0;;)if(k<h){var l=f.$(null,k);Jc(d," ");Qi(l,d,b);k+=1}else if(e=E(e))f=e,Ae(f)?(e=Wc(f),h=Xc(f),f=e,l=H(e),e=h,h=l):(l=y(f),Jc(d," "),Qi(l,d,b),e=z(f),f=null,h=0),k=0;else break a}return c}
+function Vi(a){var b=ob();return te(a)?"":""+v.h(Ui(a,b))}function Wi(a,b,c,d,e){return Y(d,function(a,b,d){var e=jc(a);c.l?c.l(e,b,d):c.call(null,e,b,d);Jc(b," ");a=kc(a);return c.l?c.l(a,b,d):c.call(null,a,b,d)},[v.h(a),"{"].join(""),", ","}",e,E(b))}function Ri(a,b,c){var d=Qi,e=(xe(a),null),f=J(e,0,null);e=J(e,1,null);return t(f)?Wi(["#:",v.h(f)].join(""),e,d,b,c):Wi(null,a,d,b,c)}hg.prototype.ma=q;
+hg.prototype.R=function(a,b,c){Jc(b,"#object [cljs.core.Volatile ");Qi(new r(null,1,[Xi,this.state],null),b,c);return Jc(b,"]")};Jb.prototype.ma=q;Jb.prototype.R=function(a,b,c){return Y(b,Qi,"("," ",")",c,this)};kf.prototype.ma=q;kf.prototype.R=function(a,b,c){return Y(b,Qi,"("," ",")",c,this)};Nh.prototype.ma=q;Nh.prototype.R=function(a,b,c){return Y(b,Qi,"("," ",")",c,this)};Fh.prototype.ma=q;Fh.prototype.R=function(a,b,c){return Y(b,Qi,"("," ",")",c,this)};Rh.prototype.ma=q;
+Rh.prototype.R=function(a,b,c){return Y(b,Qi,"["," ","]",c,this)};jh.prototype.ma=q;jh.prototype.R=function(a,b,c){return Y(b,Qi,"("," ",")",c,this)};wi.prototype.ma=q;wi.prototype.R=function(a,b,c){return Y(b,Qi,"#{"," ","}",c,this)};Ug.prototype.ma=q;Ug.prototype.R=function(a,b,c){return Y(b,Qi,"("," ",")",c,this)};ef.prototype.ma=q;ef.prototype.R=function(a,b,c){return Y(b,Qi,"("," ",")",c,this)};Zd.prototype.ma=q;Zd.prototype.R=function(a,b,c){return Y(b,Qi,"("," ",")",c,this)};
+Jh.prototype.ma=q;Jh.prototype.R=function(a,b,c){return Ri(this,b,c)};Gh.prototype.ma=q;Gh.prototype.R=function(a,b,c){return Y(b,Qi,"("," ",")",c,this)};Yg.prototype.ma=q;Yg.prototype.R=function(a,b,c){return Y(b,Qi,"["," ","]",c,this)};$h.prototype.ma=q;$h.prototype.R=function(a,b,c){return Ri(this,b,c)};ti.prototype.ma=q;ti.prototype.R=function(a,b,c){return Y(b,Qi,"#{"," ","}",c,this)};pf.prototype.ma=q;pf.prototype.R=function(a,b,c){return Y(b,Qi,"("," ",")",c,this)};cg.prototype.ma=q;
+cg.prototype.R=function(a,b,c){Jc(b,"#object [cljs.core.Atom ");Qi(new r(null,1,[Xi,this.state],null),b,c);return Jc(b,"]")};gi.prototype.ma=q;gi.prototype.R=function(a,b,c){return Y(b,Qi,"("," ",")",c,this)};Qh.prototype.ma=q;Qh.prototype.R=function(a,b,c){return Y(b,Qi,"["," ","]",c,this)};R.prototype.ma=q;R.prototype.R=function(a,b,c){return Y(b,Qi,"["," ","]",c,this)};bf.prototype.ma=q;bf.prototype.R=function(a,b){return Jc(b,"()")};r.prototype.ma=q;
+r.prototype.R=function(a,b,c){return Ri(this,b,c)};Ei.prototype.ma=q;Ei.prototype.R=function(a,b,c){return Y(b,Qi,"("," ",")",c,this)};Sf.prototype.ma=q;Sf.prototype.R=function(a,b,c){return Y(b,Qi,"("," ",")",c,this)};ei.prototype.ma=q;ei.prototype.R=function(a,b,c){return Y(b,Qi,"("," ",")",c,this)};af.prototype.ma=q;af.prototype.R=function(a,b,c){return Y(b,Qi,"("," ",")",c,this)};rd.prototype.zc=q;
+rd.prototype.cc=function(a,b){if(b instanceof rd)return sd(this,b);throw Error(["Cannot compare ",v.h(this)," to ",v.h(b)].join(""));};L.prototype.zc=q;L.prototype.cc=function(a,b){if(b instanceof L)return ff(this,b);throw Error(["Cannot compare ",v.h(this)," to ",v.h(b)].join(""));};Yg.prototype.zc=q;Yg.prototype.cc=function(a,b){if(ze(b))return Le(this,b);throw Error(["Cannot compare ",v.h(this)," to ",v.h(b)].join(""));};R.prototype.zc=q;
+R.prototype.cc=function(a,b){if(ze(b))return Le(this,b);throw Error(["Cannot compare ",v.h(this)," to ",v.h(b)].join(""));};Rh.prototype.zc=q;Rh.prototype.cc=function(a,b){if(ze(b))return Le(this,b);throw Error(["Cannot compare ",v.h(this)," to ",v.h(b)].join(""));};Qh.prototype.zc=q;Qh.prototype.cc=function(a,b){if(ze(b))return Le(this,b);throw Error(["Cannot compare ",v.h(this)," to ",v.h(b)].join(""));};var Yi=null;
+function Zi(){null==Yi&&(Yi=dg.h(0));return td.h([v.h("reagent"),v.h(gg.c(Yi,Fd))].join(""))}function $i(){}var aj=function aj(a){if(null!=a&&null!=a.pf)return a.pf(a);var c=aj[n(null==a?null:a)];if(null!=c)return c.h?c.h(a):c.call(null,a);c=aj._;if(null!=c)return c.h?c.h(a):c.call(null,a);throw Cb("IEncodeJS.-clj-\x3ejs",a);};function bj(a){return(null!=a?q===a.nf||(a.Tc?0:Ab($i,a)):Ab($i,a))?aj(a):"string"===typeof a||"number"===typeof a||a instanceof L||a instanceof rd?cj(a):Vi(be([a]))}
+var cj=function cj(a){if(null==a)return null;if(null!=a?q===a.nf||(a.Tc?0:Ab($i,a)):Ab($i,a))return aj(a);if(a instanceof L)return jf(a);if(a instanceof rd)return""+v.h(a);if(xe(a)){var c={};a=E(a);for(var d=null,e=0,f=0;;)if(f<e){var h=d.$(null,f),k=J(h,0,null),l=J(h,1,null);h=c;k=bj(k);l=cj.h?cj.h(l):cj.call(null,l);h[k]=l;f+=1}else if(a=E(a))Ae(a)?(e=Wc(a),a=Xc(a),d=e,e=H(e)):(d=y(a),e=J(d,0,null),f=J(d,1,null),d=c,e=bj(e),f=cj.h?cj.h(f):cj.call(null,f),d[e]=f,a=z(a),d=null,e=0),f=0;else break;
+return c}if(ue(a)){c=[];a=E(ig.c(cj,a));d=null;for(f=e=0;;)if(f<e)h=d.$(null,f),c.push(h),f+=1;else if(a=E(a))d=a,Ae(d)?(a=Wc(d),f=Xc(d),d=a,e=H(a),a=f):(a=y(d),c.push(a),a=z(d),d=null,e=0),f=0;else break;return c}return a};function dj(){}var ej=function ej(a,b){if(null!=a&&null!=a.mf)return a.mf(a,b);var d=ej[n(null==a?null:a)];if(null!=d)return d.c?d.c(a,b):d.call(null,a,b);d=ej._;if(null!=d)return d.c?d.c(a,b):d.call(null,a,b);throw Cb("IEncodeClojure.-js-\x3eclj",a);};
+function fj(a){var b=be([gj,!0]),c=null!=b&&(b.m&64||q===b.G)?P(U,b):b,d=D.c(c,gj);return function(a,c,d,k){return function m(e){return(null!=e?q===e.lf||(e.Tc?0:Ab(dj,e)):Ab(dj,e))?ej(e,P(ci,b)):De(e)?Ii(ig.c(m,e)):ue(e)?wg.c(ie(e),ig.c(m,e)):vb(e)?Wg(ig.c(m,e)):Bb(e)===Object?wg.c(Ef,function(){return function(a,b,c,d){return function M(f){return new kf(null,function(a,b,c,d){return function(){for(;;){var a=E(f);if(a){if(Ae(a)){var b=Wc(a),c=H(b),h=of(c);a:for(var k=0;;)if(k<c){var p=A.c(b,k);p=
+new R(null,2,5,T,[d.h?d.h(p):d.call(null,p),m(e[p])],null);h.add(p);k+=1}else{b=!0;break a}return b?qf(h.Da(),M(Xc(a))):qf(h.Da(),null)}h=y(a);return ae(new R(null,2,5,T,[d.h?d.h(h):d.call(null,h),m(e[h])],null),M(vd(a)))}return null}}}(a,b,c,d),null,null)}}(a,c,d,k)(Ea(e))}()):e}}(b,c,d,t(d)?hf:v)(a)}
+function hj(a){return function(b){return function(){function c(a){var b=null;if(0<arguments.length){b=0;for(var c=Array(arguments.length-0);b<c.length;)c[b]=arguments[b+0],++b;b=new Jb(c,0,null)}return d.call(this,b)}function d(c){var d=D.l(B(b),c,Ce);d===Ce&&(d=P(a,c),gg.M(b,K,c,d));return d}c.L=0;c.N=function(a){a=E(a);return d(a)};c.A=d;return c}()}(dg.h(Ef))}var ij=null;function jj(){null==ij&&(ij=dg.h(new r(null,3,[kj,Ef,lj,Ef,mj,Ef],null)));return ij}
+function nj(a,b,c){var d=G.c(b,c);if(d)return d;d=mj.h(a);d=d.h?d.h(b):d.call(null,b);if(!(d=He(d,c))&&(d=ze(c)))if(d=ze(b))if(d=H(c)===H(b)){d=!0;for(var e=0;;)if(d&&e!==H(c))d=nj(a,b.h?b.h(e):b.call(null,e),c.h?c.h(e):c.call(null,e)),e+=1;else return d}else return d;else return d;else return d}function oj(a){var b=B(jj());return Bf(D.c(kj.h(b),a))}function pj(a,b,c,d){gg.c(a,function(){return B(b)});gg.c(c,function(){return B(d)})}
+var qj=function qj(a,b,c){var e=function(){var b=B(c);return b.h?b.h(a):b.call(null,a)}();e=t(t(e)?e.h?e.h(b):e.call(null,b):e)?!0:null;if(t(e))return e;e=function(){for(var e=oj(b);;)if(0<H(e)){var h=y(e);qj.l?qj.l(a,h,c):qj.call(null,a,h,c);e=vd(e)}else return null}();if(t(e))return e;e=function(){for(var e=oj(a);;)if(0<H(e)){var h=y(e);qj.l?qj.l(h,b,c):qj.call(null,h,b,c);e=vd(e)}else return null}();return t(e)?e:!1};function rj(a,b,c,d){c=qj(a,b,c);return t(c)?c:nj(d,a,b)}
+var sj=function sj(a,b,c,d,e,f,h,k){var p=Mb(function(d,f){var h=J(f,0,null);J(f,1,null);if(nj(B(c),b,h)){var k=(k=null==d)?k:rj(h,y(d),e,B(c));k=t(k)?f:d;if(!t(rj(y(k),h,e,B(c))))throw Error(["Multiple methods in multimethod '",v.h(a),"' match dispatch value: ",v.h(b)," -\x3e ",v.h(h)," and ",v.h(y(k)),", and neither is preferred"].join(""));return k}return d},null,B(d)),m=function(){var a;if(a=null==p)a=B(d),a=a.h?a.h(k):a.call(null,k);return t(a)?new R(null,2,5,T,[k,a],null):p}();if(t(m)){if(G.c(B(h),
+B(c)))return gg.M(f,K,b,ee(m)),ee(m);pj(f,d,h,c);return sj.Ha?sj.Ha(a,b,c,d,e,f,h,k):sj.call(null,a,b,c,d,e,f,h,k)}return null};function tj(a,b){throw Error(["No method in multimethod '",v.h(a),"' for dispatch value: ",v.h(b)].join(""));}function uj(a,b,c,d,e,f,h,k){this.name=a;this.D=b;this.vf=c;this.Rd=d;this.Vd=e;this.Kf=f;this.Ud=h;this.Ed=k;this.m=4194305;this.J=4352}g=uj.prototype;
+g.call=function(){function a(a,b,c,d,e,f,h,k,m,l,p,u,w,x,F,C,I,M,S,X,Q,Ga){a=this;var W=oe(a.D,b,c,d,e,be([f,h,k,m,l,p,u,w,x,F,C,I,M,S,X,Q,Ga])),ka=vj(this,W);t(ka)||tj(a.name,W);return oe(ka,b,c,d,e,be([f,h,k,m,l,p,u,w,x,F,C,I,M,S,X,Q,Ga]))}function b(a,b,c,d,e,f,h,k,m,l,p,u,w,x,F,C,I,M,S,X,Q){a=this;var W=a.D.Xa?a.D.Xa(b,c,d,e,f,h,k,m,l,p,u,w,x,F,C,I,M,S,X,Q):a.D.call(null,b,c,d,e,f,h,k,m,l,p,u,w,x,F,C,I,M,S,X,Q),ka=vj(this,W);t(ka)||tj(a.name,W);return ka.Xa?ka.Xa(b,c,d,e,f,h,k,m,l,p,u,w,x,F,C,
+I,M,S,X,Q):ka.call(null,b,c,d,e,f,h,k,m,l,p,u,w,x,F,C,I,M,S,X,Q)}function c(a,b,c,d,e,f,h,k,m,l,p,u,w,x,F,C,I,M,S,X){a=this;var W=a.D.Wa?a.D.Wa(b,c,d,e,f,h,k,m,l,p,u,w,x,F,C,I,M,S,X):a.D.call(null,b,c,d,e,f,h,k,m,l,p,u,w,x,F,C,I,M,S,X),ka=vj(this,W);t(ka)||tj(a.name,W);return ka.Wa?ka.Wa(b,c,d,e,f,h,k,m,l,p,u,w,x,F,C,I,M,S,X):ka.call(null,b,c,d,e,f,h,k,m,l,p,u,w,x,F,C,I,M,S,X)}function d(a,b,c,d,e,f,h,k,m,l,p,u,w,x,F,C,I,M,S){a=this;var W=a.D.Va?a.D.Va(b,c,d,e,f,h,k,m,l,p,u,w,x,F,C,I,M,S):a.D.call(null,
+b,c,d,e,f,h,k,m,l,p,u,w,x,F,C,I,M,S),ka=vj(this,W);t(ka)||tj(a.name,W);return ka.Va?ka.Va(b,c,d,e,f,h,k,m,l,p,u,w,x,F,C,I,M,S):ka.call(null,b,c,d,e,f,h,k,m,l,p,u,w,x,F,C,I,M,S)}function e(a,b,c,d,e,f,h,k,m,l,p,u,w,x,F,C,I,M){a=this;var W=a.D.Ua?a.D.Ua(b,c,d,e,f,h,k,m,l,p,u,w,x,F,C,I,M):a.D.call(null,b,c,d,e,f,h,k,m,l,p,u,w,x,F,C,I,M),ka=vj(this,W);t(ka)||tj(a.name,W);return ka.Ua?ka.Ua(b,c,d,e,f,h,k,m,l,p,u,w,x,F,C,I,M):ka.call(null,b,c,d,e,f,h,k,m,l,p,u,w,x,F,C,I,M)}function f(a,b,c,d,e,f,h,k,m,
+l,p,u,w,x,F,C,I){a=this;var W=a.D.Ta?a.D.Ta(b,c,d,e,f,h,k,m,l,p,u,w,x,F,C,I):a.D.call(null,b,c,d,e,f,h,k,m,l,p,u,w,x,F,C,I),ka=vj(this,W);t(ka)||tj(a.name,W);return ka.Ta?ka.Ta(b,c,d,e,f,h,k,m,l,p,u,w,x,F,C,I):ka.call(null,b,c,d,e,f,h,k,m,l,p,u,w,x,F,C,I)}function h(a,b,c,d,e,f,h,k,m,l,p,u,w,x,F,C){a=this;var W=a.D.Sa?a.D.Sa(b,c,d,e,f,h,k,m,l,p,u,w,x,F,C):a.D.call(null,b,c,d,e,f,h,k,m,l,p,u,w,x,F,C),ka=vj(this,W);t(ka)||tj(a.name,W);return ka.Sa?ka.Sa(b,c,d,e,f,h,k,m,l,p,u,w,x,F,C):ka.call(null,b,
+c,d,e,f,h,k,m,l,p,u,w,x,F,C)}function k(a,b,c,d,e,f,h,k,m,l,p,u,w,x,F){a=this;var W=a.D.Ra?a.D.Ra(b,c,d,e,f,h,k,m,l,p,u,w,x,F):a.D.call(null,b,c,d,e,f,h,k,m,l,p,u,w,x,F),C=vj(this,W);t(C)||tj(a.name,W);return C.Ra?C.Ra(b,c,d,e,f,h,k,m,l,p,u,w,x,F):C.call(null,b,c,d,e,f,h,k,m,l,p,u,w,x,F)}function l(a,b,c,d,e,f,h,k,m,l,p,u,w,x){a=this;var W=a.D.Qa?a.D.Qa(b,c,d,e,f,h,k,m,l,p,u,w,x):a.D.call(null,b,c,d,e,f,h,k,m,l,p,u,w,x),F=vj(this,W);t(F)||tj(a.name,W);return F.Qa?F.Qa(b,c,d,e,f,h,k,m,l,p,u,w,x):F.call(null,
+b,c,d,e,f,h,k,m,l,p,u,w,x)}function p(a,b,c,d,e,f,h,k,m,l,p,u,w){a=this;var x=a.D.Pa?a.D.Pa(b,c,d,e,f,h,k,m,l,p,u,w):a.D.call(null,b,c,d,e,f,h,k,m,l,p,u,w),W=vj(this,x);t(W)||tj(a.name,x);return W.Pa?W.Pa(b,c,d,e,f,h,k,m,l,p,u,w):W.call(null,b,c,d,e,f,h,k,m,l,p,u,w)}function m(a,b,c,d,e,f,h,k,m,l,p,u){a=this;var w=a.D.Oa?a.D.Oa(b,c,d,e,f,h,k,m,l,p,u):a.D.call(null,b,c,d,e,f,h,k,m,l,p,u),x=vj(this,w);t(x)||tj(a.name,w);return x.Oa?x.Oa(b,c,d,e,f,h,k,m,l,p,u):x.call(null,b,c,d,e,f,h,k,m,l,p,u)}function u(a,
+b,c,d,e,f,h,k,m,l,p){a=this;var u=a.D.Na?a.D.Na(b,c,d,e,f,h,k,m,l,p):a.D.call(null,b,c,d,e,f,h,k,m,l,p),w=vj(this,u);t(w)||tj(a.name,u);return w.Na?w.Na(b,c,d,e,f,h,k,m,l,p):w.call(null,b,c,d,e,f,h,k,m,l,p)}function w(a,b,c,d,e,f,h,k,m,l){a=this;var p=a.D.Za?a.D.Za(b,c,d,e,f,h,k,m,l):a.D.call(null,b,c,d,e,f,h,k,m,l),u=vj(this,p);t(u)||tj(a.name,p);return u.Za?u.Za(b,c,d,e,f,h,k,m,l):u.call(null,b,c,d,e,f,h,k,m,l)}function x(a,b,c,d,e,f,h,k,m){a=this;var l=a.D.Ha?a.D.Ha(b,c,d,e,f,h,k,m):a.D.call(null,
+b,c,d,e,f,h,k,m),p=vj(this,l);t(p)||tj(a.name,l);return p.Ha?p.Ha(b,c,d,e,f,h,k,m):p.call(null,b,c,d,e,f,h,k,m)}function C(a,b,c,d,e,f,h,k){a=this;var m=a.D.Ya?a.D.Ya(b,c,d,e,f,h,k):a.D.call(null,b,c,d,e,f,h,k),l=vj(this,m);t(l)||tj(a.name,m);return l.Ya?l.Ya(b,c,d,e,f,h,k):l.call(null,b,c,d,e,f,h,k)}function F(a,b,c,d,e,f,h){a=this;var k=a.D.Ca?a.D.Ca(b,c,d,e,f,h):a.D.call(null,b,c,d,e,f,h),m=vj(this,k);t(m)||tj(a.name,k);return m.Ca?m.Ca(b,c,d,e,f,h):m.call(null,b,c,d,e,f,h)}function I(a,b,c,d,
+e,f){a=this;var h=a.D.Z?a.D.Z(b,c,d,e,f):a.D.call(null,b,c,d,e,f),k=vj(this,h);t(k)||tj(a.name,h);return k.Z?k.Z(b,c,d,e,f):k.call(null,b,c,d,e,f)}function M(a,b,c,d,e){a=this;var f=a.D.M?a.D.M(b,c,d,e):a.D.call(null,b,c,d,e),h=vj(this,f);t(h)||tj(a.name,f);return h.M?h.M(b,c,d,e):h.call(null,b,c,d,e)}function S(a,b,c,d){a=this;var e=a.D.l?a.D.l(b,c,d):a.D.call(null,b,c,d),f=vj(this,e);t(f)||tj(a.name,e);return f.l?f.l(b,c,d):f.call(null,b,c,d)}function X(a,b,c){a=this;var d=a.D.c?a.D.c(b,c):a.D.call(null,
+b,c),e=vj(this,d);t(e)||tj(a.name,d);return e.c?e.c(b,c):e.call(null,b,c)}function Ga(a,b){a=this;var c=a.D.h?a.D.h(b):a.D.call(null,b),d=vj(this,c);t(d)||tj(a.name,c);return d.h?d.h(b):d.call(null,b)}function db(a){a=this;var b=a.D.B?a.D.B():a.D.call(null),c=vj(this,b);t(c)||tj(a.name,b);return c.B?c.B():c.call(null)}var Q=null;Q=function(Q,Ha,Ja,Oa,Ba,W,$a,ka,jb,nb,zb,Ib,Wd,Xb,ic,xc,Sc,Bd,se,Lf,Ih,kl){switch(arguments.length){case 1:return db.call(this,Q);case 2:return Ga.call(this,Q,Ha);case 3:return X.call(this,
+Q,Ha,Ja);case 4:return S.call(this,Q,Ha,Ja,Oa);case 5:return M.call(this,Q,Ha,Ja,Oa,Ba);case 6:return I.call(this,Q,Ha,Ja,Oa,Ba,W);case 7:return F.call(this,Q,Ha,Ja,Oa,Ba,W,$a);case 8:return C.call(this,Q,Ha,Ja,Oa,Ba,W,$a,ka);case 9:return x.call(this,Q,Ha,Ja,Oa,Ba,W,$a,ka,jb);case 10:return w.call(this,Q,Ha,Ja,Oa,Ba,W,$a,ka,jb,nb);case 11:return u.call(this,Q,Ha,Ja,Oa,Ba,W,$a,ka,jb,nb,zb);case 12:return m.call(this,Q,Ha,Ja,Oa,Ba,W,$a,ka,jb,nb,zb,Ib);case 13:return p.call(this,Q,Ha,Ja,Oa,Ba,W,$a,
+ka,jb,nb,zb,Ib,Wd);case 14:return l.call(this,Q,Ha,Ja,Oa,Ba,W,$a,ka,jb,nb,zb,Ib,Wd,Xb);case 15:return k.call(this,Q,Ha,Ja,Oa,Ba,W,$a,ka,jb,nb,zb,Ib,Wd,Xb,ic);case 16:return h.call(this,Q,Ha,Ja,Oa,Ba,W,$a,ka,jb,nb,zb,Ib,Wd,Xb,ic,xc);case 17:return f.call(this,Q,Ha,Ja,Oa,Ba,W,$a,ka,jb,nb,zb,Ib,Wd,Xb,ic,xc,Sc);case 18:return e.call(this,Q,Ha,Ja,Oa,Ba,W,$a,ka,jb,nb,zb,Ib,Wd,Xb,ic,xc,Sc,Bd);case 19:return d.call(this,Q,Ha,Ja,Oa,Ba,W,$a,ka,jb,nb,zb,Ib,Wd,Xb,ic,xc,Sc,Bd,se);case 20:return c.call(this,Q,
+Ha,Ja,Oa,Ba,W,$a,ka,jb,nb,zb,Ib,Wd,Xb,ic,xc,Sc,Bd,se,Lf);case 21:return b.call(this,Q,Ha,Ja,Oa,Ba,W,$a,ka,jb,nb,zb,Ib,Wd,Xb,ic,xc,Sc,Bd,se,Lf,Ih);case 22:return a.call(this,Q,Ha,Ja,Oa,Ba,W,$a,ka,jb,nb,zb,Ib,Wd,Xb,ic,xc,Sc,Bd,se,Lf,Ih,kl)}throw Error("Invalid arity: "+(arguments.length-1));};Q.h=db;Q.c=Ga;Q.l=X;Q.M=S;Q.Z=M;Q.Ca=I;Q.Ya=F;Q.Ha=C;Q.Za=x;Q.Na=w;Q.Oa=u;Q.Pa=m;Q.Qa=p;Q.Ra=l;Q.Sa=k;Q.Ta=h;Q.Ua=f;Q.Va=e;Q.Wa=d;Q.Xa=c;Q.he=b;Q.qf=a;return Q}();
+g.apply=function(a,b){return this.call.apply(this,[this].concat(Gb(b)))};g.B=function(){var a=this.D.B?this.D.B():this.D.call(null),b=vj(this,a);t(b)||tj(this.name,a);return b.B?b.B():b.call(null)};g.h=function(a){var b=this.D.h?this.D.h(a):this.D.call(null,a),c=vj(this,b);t(c)||tj(this.name,b);return c.h?c.h(a):c.call(null,a)};g.c=function(a,b){var c=this.D.c?this.D.c(a,b):this.D.call(null,a,b),d=vj(this,c);t(d)||tj(this.name,c);return d.c?d.c(a,b):d.call(null,a,b)};
+g.l=function(a,b,c){var d=this.D.l?this.D.l(a,b,c):this.D.call(null,a,b,c),e=vj(this,d);t(e)||tj(this.name,d);return e.l?e.l(a,b,c):e.call(null,a,b,c)};g.M=function(a,b,c,d){var e=this.D.M?this.D.M(a,b,c,d):this.D.call(null,a,b,c,d),f=vj(this,e);t(f)||tj(this.name,e);return f.M?f.M(a,b,c,d):f.call(null,a,b,c,d)};g.Z=function(a,b,c,d,e){var f=this.D.Z?this.D.Z(a,b,c,d,e):this.D.call(null,a,b,c,d,e),h=vj(this,f);t(h)||tj(this.name,f);return h.Z?h.Z(a,b,c,d,e):h.call(null,a,b,c,d,e)};
+g.Ca=function(a,b,c,d,e,f){var h=this.D.Ca?this.D.Ca(a,b,c,d,e,f):this.D.call(null,a,b,c,d,e,f),k=vj(this,h);t(k)||tj(this.name,h);return k.Ca?k.Ca(a,b,c,d,e,f):k.call(null,a,b,c,d,e,f)};g.Ya=function(a,b,c,d,e,f,h){var k=this.D.Ya?this.D.Ya(a,b,c,d,e,f,h):this.D.call(null,a,b,c,d,e,f,h),l=vj(this,k);t(l)||tj(this.name,k);return l.Ya?l.Ya(a,b,c,d,e,f,h):l.call(null,a,b,c,d,e,f,h)};
+g.Ha=function(a,b,c,d,e,f,h,k){var l=this.D.Ha?this.D.Ha(a,b,c,d,e,f,h,k):this.D.call(null,a,b,c,d,e,f,h,k),p=vj(this,l);t(p)||tj(this.name,l);return p.Ha?p.Ha(a,b,c,d,e,f,h,k):p.call(null,a,b,c,d,e,f,h,k)};g.Za=function(a,b,c,d,e,f,h,k,l){var p=this.D.Za?this.D.Za(a,b,c,d,e,f,h,k,l):this.D.call(null,a,b,c,d,e,f,h,k,l),m=vj(this,p);t(m)||tj(this.name,p);return m.Za?m.Za(a,b,c,d,e,f,h,k,l):m.call(null,a,b,c,d,e,f,h,k,l)};
+g.Na=function(a,b,c,d,e,f,h,k,l,p){var m=this.D.Na?this.D.Na(a,b,c,d,e,f,h,k,l,p):this.D.call(null,a,b,c,d,e,f,h,k,l,p),u=vj(this,m);t(u)||tj(this.name,m);return u.Na?u.Na(a,b,c,d,e,f,h,k,l,p):u.call(null,a,b,c,d,e,f,h,k,l,p)};g.Oa=function(a,b,c,d,e,f,h,k,l,p,m){var u=this.D.Oa?this.D.Oa(a,b,c,d,e,f,h,k,l,p,m):this.D.call(null,a,b,c,d,e,f,h,k,l,p,m),w=vj(this,u);t(w)||tj(this.name,u);return w.Oa?w.Oa(a,b,c,d,e,f,h,k,l,p,m):w.call(null,a,b,c,d,e,f,h,k,l,p,m)};
+g.Pa=function(a,b,c,d,e,f,h,k,l,p,m,u){var w=this.D.Pa?this.D.Pa(a,b,c,d,e,f,h,k,l,p,m,u):this.D.call(null,a,b,c,d,e,f,h,k,l,p,m,u),x=vj(this,w);t(x)||tj(this.name,w);return x.Pa?x.Pa(a,b,c,d,e,f,h,k,l,p,m,u):x.call(null,a,b,c,d,e,f,h,k,l,p,m,u)};g.Qa=function(a,b,c,d,e,f,h,k,l,p,m,u,w){var x=this.D.Qa?this.D.Qa(a,b,c,d,e,f,h,k,l,p,m,u,w):this.D.call(null,a,b,c,d,e,f,h,k,l,p,m,u,w),C=vj(this,x);t(C)||tj(this.name,x);return C.Qa?C.Qa(a,b,c,d,e,f,h,k,l,p,m,u,w):C.call(null,a,b,c,d,e,f,h,k,l,p,m,u,w)};
+g.Ra=function(a,b,c,d,e,f,h,k,l,p,m,u,w,x){var C=this.D.Ra?this.D.Ra(a,b,c,d,e,f,h,k,l,p,m,u,w,x):this.D.call(null,a,b,c,d,e,f,h,k,l,p,m,u,w,x),F=vj(this,C);t(F)||tj(this.name,C);return F.Ra?F.Ra(a,b,c,d,e,f,h,k,l,p,m,u,w,x):F.call(null,a,b,c,d,e,f,h,k,l,p,m,u,w,x)};
+g.Sa=function(a,b,c,d,e,f,h,k,l,p,m,u,w,x,C){var F=this.D.Sa?this.D.Sa(a,b,c,d,e,f,h,k,l,p,m,u,w,x,C):this.D.call(null,a,b,c,d,e,f,h,k,l,p,m,u,w,x,C),I=vj(this,F);t(I)||tj(this.name,F);return I.Sa?I.Sa(a,b,c,d,e,f,h,k,l,p,m,u,w,x,C):I.call(null,a,b,c,d,e,f,h,k,l,p,m,u,w,x,C)};
+g.Ta=function(a,b,c,d,e,f,h,k,l,p,m,u,w,x,C,F){var I=this.D.Ta?this.D.Ta(a,b,c,d,e,f,h,k,l,p,m,u,w,x,C,F):this.D.call(null,a,b,c,d,e,f,h,k,l,p,m,u,w,x,C,F),M=vj(this,I);t(M)||tj(this.name,I);return M.Ta?M.Ta(a,b,c,d,e,f,h,k,l,p,m,u,w,x,C,F):M.call(null,a,b,c,d,e,f,h,k,l,p,m,u,w,x,C,F)};
+g.Ua=function(a,b,c,d,e,f,h,k,l,p,m,u,w,x,C,F,I){var M=this.D.Ua?this.D.Ua(a,b,c,d,e,f,h,k,l,p,m,u,w,x,C,F,I):this.D.call(null,a,b,c,d,e,f,h,k,l,p,m,u,w,x,C,F,I),S=vj(this,M);t(S)||tj(this.name,M);return S.Ua?S.Ua(a,b,c,d,e,f,h,k,l,p,m,u,w,x,C,F,I):S.call(null,a,b,c,d,e,f,h,k,l,p,m,u,w,x,C,F,I)};
+g.Va=function(a,b,c,d,e,f,h,k,l,p,m,u,w,x,C,F,I,M){var S=this.D.Va?this.D.Va(a,b,c,d,e,f,h,k,l,p,m,u,w,x,C,F,I,M):this.D.call(null,a,b,c,d,e,f,h,k,l,p,m,u,w,x,C,F,I,M),X=vj(this,S);t(X)||tj(this.name,S);return X.Va?X.Va(a,b,c,d,e,f,h,k,l,p,m,u,w,x,C,F,I,M):X.call(null,a,b,c,d,e,f,h,k,l,p,m,u,w,x,C,F,I,M)};
+g.Wa=function(a,b,c,d,e,f,h,k,l,p,m,u,w,x,C,F,I,M,S){var X=this.D.Wa?this.D.Wa(a,b,c,d,e,f,h,k,l,p,m,u,w,x,C,F,I,M,S):this.D.call(null,a,b,c,d,e,f,h,k,l,p,m,u,w,x,C,F,I,M,S),Ga=vj(this,X);t(Ga)||tj(this.name,X);return Ga.Wa?Ga.Wa(a,b,c,d,e,f,h,k,l,p,m,u,w,x,C,F,I,M,S):Ga.call(null,a,b,c,d,e,f,h,k,l,p,m,u,w,x,C,F,I,M,S)};
+g.Xa=function(a,b,c,d,e,f,h,k,l,p,m,u,w,x,C,F,I,M,S,X){var Ga=this.D.Xa?this.D.Xa(a,b,c,d,e,f,h,k,l,p,m,u,w,x,C,F,I,M,S,X):this.D.call(null,a,b,c,d,e,f,h,k,l,p,m,u,w,x,C,F,I,M,S,X),db=vj(this,Ga);t(db)||tj(this.name,Ga);return db.Xa?db.Xa(a,b,c,d,e,f,h,k,l,p,m,u,w,x,C,F,I,M,S,X):db.call(null,a,b,c,d,e,f,h,k,l,p,m,u,w,x,C,F,I,M,S,X)};
+g.he=function(a,b,c,d,e,f,h,k,l,p,m,u,w,x,C,F,I,M,S,X,Ga){var db=oe(this.D,a,b,c,d,be([e,f,h,k,l,p,m,u,w,x,C,F,I,M,S,X,Ga])),Q=vj(this,db);t(Q)||tj(this.name,db);return oe(Q,a,b,c,d,be([e,f,h,k,l,p,m,u,w,x,C,F,I,M,S,X,Ga]))};function wj(a,b){var c=xj;gg.M(c.Vd,K,a,b);pj(c.Ud,c.Vd,c.Ed,c.Rd)}function vj(a,b){G.c(B(a.Ed),B(a.Rd))||pj(a.Ud,a.Vd,a.Ed,a.Rd);var c=B(a.Ud);c=c.h?c.h(b):c.call(null,b);return t(c)?c:sj(a.name,b,a.Rd,a.Vd,a.Kf,a.Ud,a.Ed,a.vf)}g.hd=function(){return Yc(this.name)};g.jd=function(){return Zc(this.name)};
+g.U=function(){return ja(this)};function yj(a,b){this.Mc=a;this.w=b;this.m=2153775104;this.J=2048}g=yj.prototype;g.toString=function(){return this.Mc};g.equiv=function(a){return this.K(null,a)};g.K=function(a,b){return b instanceof yj&&this.Mc===b.Mc};g.R=function(a,b){return Jc(b,['#uuid "',v.h(this.Mc),'"'].join(""))};g.U=function(){null==this.w&&(this.w=od(this.Mc));return this.w};g.cc=function(a,b){return Aa(this.Mc,b.Mc)};var zj=new L(null,"hook","hook",750265408),Aj=new L(null,"y","y",-1757859776),Bj=new L(null,"setCurrentTime","setCurrentTime",-623552),Cj=new L(null,"span.gutter","span.gutter",-700214016),Dj=new rd(null,"\x26","\x26",-2144855648,null),Ej=new L(null,"dcs-param","dcs-param",-971011648),Fj=new L(null,"path","path",-188191168),Gj=new L(null,"escape","escape",-991601952),Df=new rd(null,"meta34617","meta34617",-1789836320,null),Hj=new L(null,"force-load-ch","force-load-ch",-1689229247),Ij=new rd("schema.core",
+"Any","schema.core/Any",-1891898271,null),Jj=new L(null,"tab-index","tab-index",895755393),Kj=new L(null,"bold","bold",-116809535),Lj=new L(null,"authorImgURL","authorImgURL",-1171541759),Mj=new L(null,"schema","schema",-1582001791),Nj=new rd(null,"optional-key","optional-key",988406145,null),Oj=new L(null,"char-attrs","char-attrs",-1444091455),Pj=new L(null,"esc-dispatch","esc-dispatch",17832481),Qj=new L(null,"idle_time_limit","idle_time_limit",-1837919647),Rj=new L(null,"auto-wrap-mode","auto-wrap-mode",
+-2049555583),Sj=new L(null,"preload?","preload?",445442977),Tj=new L(null,"on-set","on-set",-140953470),Uj=new L(null,"current-time","current-time",-1609407134),Vj=new L(null,"span.progressbar","span.progressbar",766750210),Wj=new L(null,"osc-end","osc-end",1762953954),Xj=new L("internal","rewind","internal/rewind",-31749342),Yj=new L(null,"bottom-margin","bottom-margin",-701300733),Zj=new L(null,"on-key-press","on-key-press",-399563677),ak=new L(null,"osc-put","osc-put",-1827844733),bk=new L(null,
+"cljsLegacyRender","cljsLegacyRender",-1527295613),ck=new L(null,"klass","klass",-1386752349),dk=new L(null,"blink","blink",-271985917),ek=new rd(null,"meta43127","meta43127",166183907,null),fk=new L(null,"primary","primary",817773892),gk=new rd(null,"meta43105","meta43105",-531987068,null),rb=new L(null,"meta","meta",1499536964),V=new L(null,"screen","screen",1990059748),hk=new rd(null,"Symbol","Symbol",716452869,null),ik=new L(null,"color","color",1011675173),jk=new rd(null,"blockable","blockable",
+-28395259,null),sb=new L(null,"dup","dup",556298533),kk=new L(null,"parser-params","parser-params",36457893),lk=new rd(null,"height","height",-1629257147,null),mk=new L(null,"key","key",-1516042587),nk=new rd(null,"CellLine","CellLine",-317574363,null),ok=new L(null,"asciicast","asciicast",509526949),pk=new rd(null,"conditional","conditional",-1212542970,null),qk=new L(null,"exit","exit",351849638),rk=new L(null,"parser-intermediates","parser-intermediates",-169100058),sk=new L(null,"else","else",
+-1508377146),tk=new L(null,"tabs","tabs",-779855354),uk=new L(null,"ground","ground",1193572934),vk=new L(null,"next-print-wraps","next-print-wraps",-1664999738),wk=new L(null,"font-size","font-size",-1847940346),xk=new rd(null,"Bool","Bool",195910502,null),yk=new L(null,"transition","transition",765692007),zk=new rd(null,"one","one",-1719427865,null),Ak=new L(null,"speed","speed",1257663751),Bk=new L(null,"displayName","displayName",-809144601),Ck=new L(null,"_","_",1453416199),eg=new L(null,"validator",
+"validator",-1966190681),Dk=new rd(null,"char-attrs","char-attrs",196440072,null),Ek=new L(null,"div.loading","div.loading",-155515768),Fk=new L(null,"dcs-passthrough","dcs-passthrough",-671044440),Gk=new L(null,"show-hud","show-hud",1983299752),Hk=new L(null,"start-at","start-at",-103334680),Ik=new L(null,"default","default",-1987822328),Jk=new L(null,"csi-param","csi-param",-1120111192),Kk=new L(null,"div.control-bar","div.control-bar",-1316808248),Lk=new L(null,"finally-block","finally-block",
+832982472),Mk=new rd(null,"cb","cb",-2064487928,null),Nk=new L(null,"inverse","inverse",-1623859672),Ok=new L(null,"fg","fg",-101797208),Pk=new L(null,"warn","warn",-436710552),Qk=new L(null,"dcs-intermediate","dcs-intermediate",480808872),Rk=new L(null,"osc-string","osc-string",-486531128),Sk=new L(null,"on-enter","on-enter",-928988216),Tk=new L(null,"name","name",1843675177),Uk=new L(null,"frames","frames",1765687497),Vk=new L(null,"extra-validator-fn","extra-validator-fn",1562905865),Wk=new L(null,
+"output-schema","output-schema",272504137),Xk=new L(null,"div.play-button","div.play-button",1020321513),Yk=new L(null,"span.time-elapsed","span.time-elapsed",-1782475638),Zk=new L(null,"time","time",1385887882),$k=new L(null,"component-did-mount","component-did-mount",-1126910518),al=new L(null,"background-color","background-color",570434026),bl=new L(null,"recording-ch-fn","recording-ch-fn",-902533462),cl=new L(null,"span.playback-button","span.playback-button",-1136389398),dl=new L(null,"span.title-bar",
+"span.title-bar",-1165872085),el=new L(null,"loaded","loaded",-1246482293),fl=new L(null,"width","width",-384071477),gl=new L(null,"start","start",-355208981),hl=new rd(null,"meta43130","meta43130",1056327947,null),il=new L(null,"lines","lines",-700165781),jl=new L(null,"input-schemas","input-schemas",-982154805),ll=new L(null,"sos-pm-apc-string","sos-pm-apc-string",398998091),ml=new L(null,"cursor-on","cursor-on",302555051),nl=new L(null,"component-did-update","component-did-update",-1468549173),
+ol=new L(null,"div.start-prompt","div.start-prompt",-41424788),Xi=new L(null,"val","val",128701612),pl=new L(null,"cursor","cursor",1011937484),ql=new L(null,"dcs-entry","dcs-entry",216833388),Z=new L(null,"recur","recur",-437573268),rl=new L(null,"type","type",1174270348),sl=new rd(null,"Num","Num",-2044934708,null),tl=new L(null,"alternate","alternate",-931038644),ul=new L(null,"catch-block","catch-block",1175212748),vl=new L(null,"onPlay","onPlay",150417132),wl=new L(null,"duration","duration",
+1444101068),xl=new L(null,"execute","execute",-129499188),yl=new rd(null,"pred","pred",-727012372,null),zl=new L(null,"src","src",-1651076051),Al=new rd(null,"Any","Any",1277492269,null),Bl=new L(null,"span.bar","span.bar",-1986926323),Cl=new rd(null,"Regex","Regex",205914413,null),Dl=new L(null,"msg-ch","msg-ch",-1840176755),El=new L(null,"on-exit","on-exit",1821961613),Ti=new L(null,"fallback-impl","fallback-impl",-1501286995),Fl=new L(null,"view-box","view-box",-1792199155),Gl=new L(null,"source",
+"source",-433931539),Hl=new L(null,"csi-entry","csi-entry",-1787942099),pb=new L(null,"flush-on-newline","flush-on-newline",-151457939),Il=new L(null,"preds-and-schemas","preds-and-schemas",-1306766355),Jl=new L(null,"command-ch","command-ch",508874766),Kl=new L(null,"componentWillUnmount","componentWillUnmount",1573788814),Ll=new rd(null,"Inst","Inst",292408622,null),Ml=new L(null,"span.timer","span.timer",2111534382),Nl=new L(null,"toggle","toggle",1291842030),Ol=new L(null,"cursor-blink-ch","cursor-blink-ch",
+1063651214),Pl=new L(null,"print","print",1299562414),Ql=new L(null,"on-mouse-down","on-mouse-down",1147755470),Rl=new L(null,"csi-dispatch","csi-dispatch",-126857169),Sl=new L(null,"on-click","on-click",1632826543),Tl=new L(null,"parser-state","parser-state",594493647),Ul=new L(null,"ignore","ignore",-1631542033),lj=new L(null,"descendants","descendants",1824886031),Vl=new L(null,"underline","underline",2018066703),Wl=new rd(null,"Str","Str",907970895,null),Xl=new L(null,"param","param",2013631823),
+Yl=new L(null,"k","k",-2146297393),ki=new L(null,"title","title",636505583),Zl=new L(null,"stop-ch","stop-ch",-219113969),$l=new L(null,"insert-mode","insert-mode",894811791),am=new rd(null,"maybe","maybe",1326133967,null),bm=new L(null,"toggle-fullscreen","toggle-fullscreen",-1647254833),cm=new L(null,"loop","loop",-395552849),ni=new L(null,"author-img-url","author-img-url",2016975920),dm=new L(null,"shouldComponentUpdate","shouldComponentUpdate",1795750960),mj=new L(null,"ancestors","ancestors",
+-776045424),em=new rd(null,"flag","flag",-1565787888,null),fm=new L(null,"style","style",-496642736),gm=new L(null,"theme","theme",-1247880880),hm=new L(null,"stream","stream",1534941648),im=new L(null,"charset-fn","charset-fn",1374523920),li=new L(null,"author","author",2111686192),jm=new L(null,"escape-intermediate","escape-intermediate",1036490448),km=new L(null,"div","div",1057191632),qb=new L(null,"readably","readably",1129599760),lm=new L(null,"change-speed","change-speed",2125740976),Ki=new L(null,
+"more-marker","more-marker",-14717935),mm=new L(null,"new-line-mode","new-line-mode",1467504785),nm=new L(null,"optional?","optional?",1184638129),om=new L(null,"csi-intermediate","csi-intermediate",-410048175),pm=new L(null,"reagentRender","reagentRender",-358306383),qm=new L(null,"idle-time-limit","idle-time-limit",-928369231),rm=new L(null,"started?","started?",-1301062863),sm=new L(null,"other-buffer-saved","other-buffer-saved",-2048065486),tm=new L(null,"snapshot","snapshot",-1274785710),um=
+new L(null,"osc-start","osc-start",-1717437326),vm=new L(null,"preload","preload",1646824722),wm=new L(null,"stop","stop",-2140911342),xm=new L(null,"no-cache","no-cache",1588056370),ym=new rd(null,"Uuid","Uuid",-1866694318,null),zm=new L(null,"render","render",-1408033454),Am=new rd(null,"width","width",1256460050,null),Bm=new L(null,"poster","poster",-1616913550),Cm=new L(null,"csi-ignore","csi-ignore",-764437550),Dm=new L(null,"reagent-render","reagent-render",-985383853),Em=new L(null,"auto-play",
+"auto-play",-645319501),Fm=new L(null,"collect","collect",-284321549),Gm=new L(null,"pre.asciinema-terminal","pre.asciinema-terminal",832737619),Hm=new L(null,"loading","loading",-737050189),Im=new L(null,"priority","priority",1431093715),Jm=new L(null,"auto-play?","auto-play?",385278451),Km=new rd(null,"val","val",1769233139,null),Lm=new L(null,"span.line","span.line",-1541583788),tb=new L(null,"print-length","print-length",1931866356),Mm=new L(null,"poster-time","poster-time",1478579796),Nm=new L(null,
+"saved","saved",288760660),Om=new L(null,"error-symbol","error-symbol",-823480428),oi=new L(null,"on-can-play","on-can-play",1481578549),Pm=new L(null,"catch-exception","catch-exception",-1997306795),Qm=new L(null,"constructor","constructor",-1953928811),Rm=new L(null,"auto-run","auto-run",1958400437),Sm=new L(null,"div.asciinema-player","div.asciinema-player",-1293079051),kj=new L(null,"parents","parents",-2027538891),mi=new L(null,"author-url","author-url",1091920533),Tm=new L(null,"pred-name",
+"pred-name",-3677451),Um=new rd(null,"meta42957","meta42957",-1080714315,null),Vm=new L(null,"on-mouse-move","on-mouse-move",-1386320874),Wm=new L(null,"component-will-unmount","component-will-unmount",-2058314698),Xm=new L(null,"prev","prev",-1597069226),Ym=new L(null,"svg","svg",856789142),Zm=new L(null,"getDuration","getDuration",-995932010),$m=new L(null,"url","url",276297046),an=new L(null,"authorURL","authorURL",549221782),bn=new rd(null,"meta38850","meta38850",1963771318,null),cn=new L(null,
+"continue-block","continue-block",-1852047850),dn=new L(null,"loop?","loop?",457687798),en=new rd(null,"ch","ch",1085813622,null),fn=new rd(null,"CodePoint","CodePoint",-132710345,null),gn=new L(null,"autoPlay","autoPlay",-561263241),hn=new rd(null,"\x3d\x3e","\x3d\x3e",-813269641,null),jn=new L(null,"playing","playing",70013335),kn=new rd(null,"Keyword","Keyword",-850065993,null),ln=new L(null,"display-name","display-name",694513143),mn=new L(null,"random","random",-557811113),nn=new L(null,"position",
+"position",-2011731912),on=new L(null,"on-dispose","on-dispose",2105306360),pn=new L(null,"d","d",1972142424),qn=new L(null,"action","action",-811238024),rn=new L(null,"stdout-ch","stdout-ch",825692568),sn=new L(null,"pause","pause",-2095325672),tn=new L(null,"error","error",-978969032),un=new L(null,"span.fullscreen-button","span.fullscreen-button",-1476136392),vn=new L(null,"class-name","class-name",945142584),wn=new L(null,"componentFunction","componentFunction",825866104),xn=new L(null,"div.loader",
+"div.loader",-1644603528),yn=new L(null,"origin-mode","origin-mode",-1430095912),zn=new L(null,"x","x",2099068185),An=new L(null,"__html","__html",674048345),Bn=new L(null,"fontSize","fontSize",919623033),Cn=new L(null,"div.asciinema-player-wrapper","div.asciinema-player-wrapper",2009764409),Dn=new L(null,"startAt","startAt",849336089),En=new L(null,"getCurrentTime","getCurrentTime",697283642),Fn=new L(null,"put","put",1299772570),Gn=new rd(null,"CharAttrs","CharAttrs",1533586778,null),Hn=new L(null,
+"top-margin","top-margin",655579514),In=new L(null,"unhook","unhook",1440586234),Jn=new L(null,"play","play",-580418022),Kn=new L(null,"seek","seek",758996602),Ln=new rd(null,"chars","chars",545901210,null),Mn=new L(null,"version","version",425292698),Nn=new rd(null,"line","line",1852876762,null),qi=new L(null,"on-pause","on-pause",1839279163),On=new L(null,"visible","visible",-1024216805),Pn=new L(null,"autobind","autobind",-570650245),Qn=new L(null,"hierarchy","hierarchy",-1053470341),Rn=new L(null,
+"on-key-down","on-key-down",-1374733765),pi=new L(null,"on-play","on-play",-188934501),Sn=new rd(null,"\x3d\x3e*","\x3d\x3e*",1909690043,null),Si=new L(null,"alt-impl","alt-impl",670969595),Tn=new L(null,"bg","bg",-206688421),Un=new L(null,"p?","p?",-1172161701),Vn=new L(null,"onCanPlay","onCanPlay",197552027),Wn=new L(null,"other-buffer-lines","other-buffer-lines",-1562366021),Xn=new rd(null,"record","record",861424668,null),Yn=new L(null,"italic","italic",32599196),Zn=new rd(null,"required-key",
+"required-key",1624616412,null),$n=new L(null,"dcs-ignore","dcs-ignore",198619612),ao=new rd(null,"optional","optional",-600484260,null),gj=new L(null,"keywordize-keys","keywordize-keys",1310784252),bo=new rd(null,"Int","Int",-2116888740,null),co=new L(null,"span.time-remaining","span.time-remaining",706865437),eo=new L(null,"componentWillMount","componentWillMount",-285327619),fo=new L(null,"idleTimeLimit","idleTimeLimit",-867712227),go=new L("internal","seek","internal/seek",-1958914115),ho=new L(null,
+"href","href",-793805698),io=new L(null,"buffer","buffer",617295198),jo=new L(null,"img","img",1442687358),ko=new L(null,"stdout","stdout",-531490018),lo=new L(null,"a","a",-2123407586),mo=new L(null,"dangerouslySetInnerHTML","dangerouslySetInnerHTML",-554971138),no=new L(null,"height","height",1025178622),oo=new rd("s","Num","s/Num",-2044935073,null),po=new L(null,"clear","clear",1877104959),ri=new L("cljs.core","not-found","cljs.core/not-found",-1572889185),qo=new rd(null,"meta36583","meta36583",
+-346463841,null),ro=new L(null,"span","span",1394872991),so=new L(null,"show","show",-576705889),to=new rd(null,"f","f",43394975,null),uo=new L(null,"onPause","onPause",-470027297);function vo(a,b){var c=Kb(Ai,a,b);return ae(c,vg(function(a){return function(b){return a===b}}(c),b))}var wo=function wo(a){switch(arguments.length){case 0:return wo.B();case 1:return wo.h(arguments[0]);case 2:return wo.c(arguments[0],arguments[1]);default:for(var c=[],d=arguments.length,e=0;;)if(e<d)c.push(arguments[e]),e+=1;else break;return wo.A(arguments[0],arguments[1],new Jb(c.slice(2),0,null))}};wo.B=function(){return vi};wo.h=function(a){return a};
+wo.c=function(a,b){return H(a)<H(b)?Mb(ge,b,a):Mb(ge,a,b)};wo.A=function(a,b,c){a=vo(H,ge.A(c,b,be([a])));return Mb(wg,y(a),vd(a))};wo.N=function(a){var b=y(a),c=z(a);a=y(c);c=z(c);return wo.A(b,a,c)};wo.L=2;var xo=function xo(a){switch(arguments.length){case 1:return xo.h(arguments[0]);case 2:return xo.c(arguments[0],arguments[1]);default:for(var c=[],d=arguments.length,e=0;;)if(e<d)c.push(arguments[e]),e+=1;else break;return xo.A(arguments[0],arguments[1],new Jb(c.slice(2),0,null))}};xo.h=function(a){return a};
+xo.c=function(a,b){return H(a)<H(b)?Mb(function(a,d){return He(b,d)?re.c(a,d):a},a,a):Mb(re,a,b)};xo.A=function(a,b,c){return Mb(xo,a,ge.c(c,b))};xo.N=function(a){var b=y(a),c=z(a);a=y(c);c=z(c);return xo.A(b,a,c)};xo.L=2;function yo(a){var b=Pe([Lj,vl,tm,an,gn,Bn,Dn,Vn,fo,uo],[ni,pi,Bm,mi,Em,wk,Hk,oi,qm,qi]);return Mb(function(b,d){var c=J(d,0,null),f=J(d,1,null);return He(a,c)?K.l(b,f,D.c(a,c)):b},Kb(le,a,lh(b)),b)};if("undefined"===typeof zo)var zo=dg.h(null);
+if("undefined"===typeof Ao)var Ao=function(){var a={};a.warn=function(){return function(){function a(a){var b=null;if(0<arguments.length){b=0;for(var d=Array(arguments.length-0);b<d.length;)d[b]=arguments[b+0],++b;b=new Jb(d,0,null)}return c.call(this,b)}function c(a){return gg.A(zo,Ag,new R(null,1,5,T,[Pk],null),ge,be([P(v,a)]))}a.L=0;a.N=function(a){a=E(a);return c(a)};a.A=c;return a}()}(a);a.error=function(){return function(){function a(a){var b=null;if(0<arguments.length){b=0;for(var d=Array(arguments.length-
+0);b<d.length;)d[b]=arguments[b+0],++b;b=new Jb(d,0,null)}return c.call(this,b)}function c(a){return gg.A(zo,Ag,new R(null,1,5,T,[tn],null),ge,be([P(v,a)]))}a.L=0;a.N=function(a){a=E(a);return c(a)};a.A=c;return a}()}(a);return a}();function Bo(a,b,c){var d=RegExp,e=b.source,f=t(b.ignoreCase)?[v.h("g"),"i"].join(""):"g";f=t(b.multiline)?[v.h(f),"m"].join(""):f;b=t(b.cg)?[v.h(f),"u"].join(""):f;d=new d(e,b);return a.replace(d,c)}
+function Co(a){return function(){function b(a){var b=null;if(0<arguments.length){b=0;for(var d=Array(arguments.length-0);b<d.length;)d[b]=arguments[b+0],++b;b=new Jb(d,0,null)}return c.call(this,b)}function c(b){b=lg(b);if(G.c(H(b),1))return b=y(b),a.h?a.h(b):a.call(null,b);b=Wg(b);return a.h?a.h(b):a.call(null,b)}b.L=0;b.N=function(a){a=E(a);return c(a)};b.A=c;return b}()}
+function Do(a,b,c){if("string"===typeof b)return a.replace(new RegExp(String(b).replace(/([-()\[\]{}+?*.$\^|,:#<!\\])/g,"\\$1").replace(/\x08/g,"\\x08"),"g"),c);if(b instanceof RegExp)return"string"===typeof c?Bo(a,b,c):Bo(a,b,Co(c));throw["Invalid match arg: ",v.h(b)].join("");}function Eo(a){var b=new cb;for(a=E(a);;)if(null!=a)b=b.append(""+v.h(y(a))),a=z(a);else return b.toString()}
+function Fo(a,b){var c="/(?:)/"===""+v.h(b)?ge.c(Wg(ae("",ig.c(v,E(a)))),""):Wg((""+v.h(a)).split(b));if(1<H(c))a:for(;;)if(""===(null==c?null:nc(c)))c=null==c?null:oc(c);else break a;return c};if("undefined"===typeof Go){var Ho;if("undefined"!==typeof React)Ho=React;else{var Io;if("undefined"!==typeof require){var Jo=require("react");if(t(Jo))Io=Jo;else throw Error("require('react') failed");}else throw Error("js/React is missing");Ho=Io}var Go=Ho}
+if("undefined"===typeof Ko){var Lo;if("undefined"!==typeof createReactClass)Lo=createReactClass;else{var Mo;if("undefined"!==typeof require){var No=require("create-react-class");if(t(No))Mo=No;else throw Error("require('create-react-class') failed");}else throw Error("js/createReactClass is missing");Lo=Mo}var Ko=Lo}var Oo=new ti(null,new r(null,2,["aria",null,"data",null],null),null);function Po(a){return 2>H(a)?a.toUpperCase():[v.h(a.substring(0,1).toUpperCase()),v.h(a.substring(1))].join("")}
+function Qo(a){if("string"===typeof a)return a;a=jf(a);var b=Fo(a,/-/),c=E(b);b=y(c);c=z(c);return t(Oo.h?Oo.h(b):Oo.call(null,b))?a:Kb(v,b,ig.c(Po,c))}function Ro(a){var b=function(){var b=function(){var b=me(a);return b?(b=a.displayName,t(b)?b:a.name):b}();if(t(b))return b;b=function(){var b=null!=a?a.J&4096||q===a.Oe?!0:!1:!1;return b?jf(a):b}();if(t(b))return b;b=qe(a);return xe(b)?Tk.h(b):null}();return Do(""+v.h(b),"$",".")}var So=!1;if("undefined"===typeof To)var To=0;function Uo(a){return setTimeout(a,16)}var Vo="undefined"===typeof window||null==window.document?Uo:function(){var a=window,b=a.requestAnimationFrame;if(t(b))return b;b=a.webkitRequestAnimationFrame;if(t(b))return b;b=a.mozRequestAnimationFrame;if(t(b))return b;a=a.msRequestAnimationFrame;return t(a)?a:Uo}();function Wo(a,b){return a.cljsMountOrder-b.cljsMountOrder}if("undefined"===typeof Xo)var Xo=function(){return null};function Yo(a){this.Yd=a}
+function Zo(a,b){var c=a[b];if(null==c)return null;a[b]=null;for(var d=c.length,e=0;;)if(e<d){var f=c[e];f.B?f.B():f.call(null);e+=1}else return null}function $o(a){if(a.Yd)return null;a.Yd=!0;a=function(a){return function(){a.Yd=!1;return ap(a)}}(a);return Vo.h?Vo.h(a):Vo.call(null,a)}
+function ap(a){Zo(a,"beforeFlush");Xo();var b=a.componentQueue;if(null!=b)a:{a.componentQueue=null,b.sort(Wo);for(var c=b.length,d=0;;)if(d<c){var e=b[d];!0===e.cljsIsDirty&&e.forceUpdate();d+=1}else break a}return Zo(a,"afterRender")}Yo.prototype.enqueue=function(a,b){null==this[a]&&(this[a]=[]);this[a].push(b);return $o(this)};if("undefined"===typeof bp)var bp=new Yo(!1);function cp(a){if(t(a.cljsIsDirty))return null;a.cljsIsDirty=!0;return bp.enqueue("componentQueue",a)};var dp;if("undefined"===typeof ep)var ep=!1;if("undefined"===typeof fp)var fp=0;if("undefined"===typeof gp)var gp=dg.h(0);
+function hp(a,b){b.captured=null;a:{var c=dp;dp=b;try{var d=a.B?a.B():a.call(null);break a}finally{dp=c}d=void 0}var e=b.captured;b.rc=!1;a:{c=b.Nc;var f=null==e?0:e.length,h=f===(null==c?0:c.length);if(h)for(h=0;;){var k=h===f;if(k){c=k;break a}if(e[h]===c[h])h+=1;else{c=!1;break a}}else c=h}if(!c)a:{c=yi(e);f=yi(b.Nc);b.Nc=e;e=E(xo.c(c,f));h=null;for(var l=k=0;;)if(l<k){var p=h.$(null,l);Mc(p,b,ip);l+=1}else if(e=E(e))h=e,Ae(h)?(e=Wc(h),l=Xc(h),h=e,k=H(e),e=l):(e=y(h),Mc(e,b,ip),e=z(h),h=null,k=
+0),l=0;else break;c=E(xo.c(f,c));f=null;for(k=h=0;;)if(k<h)e=f.$(null,k),Nc(e,b),k+=1;else if(c=E(c))f=c,Ae(f)?(c=Wc(f),h=Xc(f),f=c,e=H(c),c=h,h=e):(e=y(f),Nc(e,b),c=z(f),f=null,h=0),k=0;else break a}return d}function jp(a){var b=dp;if(null!=b){var c=b.captured;null==c?b.captured=[a]:c.push(a)}}function kp(a,b){ep&&gg.l(gp,Xe,H(b)-H(a));return b}function lp(a,b,c){var d=a.gb;a.gb=kp(d,K.l(d,b,c));return a.Ce=null}function mp(a,b){var c=a.gb;a.gb=kp(c,le.c(c,b));return a.Ce=null}
+function np(a,b,c){var d=a.Ce;d=null==d?a.Ce=Ue(function(){return function(a,b,c){a.push(b);a.push(c);return a}}(d),[],a.gb):d;for(var e=d.length,f=0;;)if(f<e){var h=d[f],k=d[f+1];k.M?k.M(h,a,b,c):k.call(null,h,a,b,c);f=2+f}else return null}function op(a,b,c,d){Jc(b,["#\x3c",v.h(d)," "].join(""));a:{d=dp;dp=null;try{var e=B(a);break a}finally{dp=d}e=void 0}Qi(e,b,c);return Jc(b,"\x3e")}if("undefined"===typeof pp)var pp=null;
+function qp(){for(;;){var a=pp;if(null==a)return null;pp=null;for(var b=a.length,c=0;;)if(c<b){var d=a[c];d.rc&&null!=d.Nc&&rp(d,!0);c+=1}else break}}Xo=qp;function sp(a,b,c,d){this.state=a;this.meta=b;this.df=c;this.gb=d;this.m=2153938944;this.J=114690}g=sp.prototype;g.R=function(a,b,c){return op(this,b,c,"Atom:")};g.P=function(){return this.meta};g.U=function(){return ja(this)};g.K=function(a,b){return this===b};g.Gb=function(a,b){var c=this.state;this.state=b;null!=this.gb&&np(this,c,b);return b};
+g.je=function(a,b){return this.Gb(null,b.h?b.h(this.state):b.call(null,this.state))};g.ke=function(a,b,c){return this.Gb(null,b.c?b.c(this.state,c):b.call(null,this.state,c))};g.le=function(a,b,c,d){return this.Gb(null,b.l?b.l(this.state,c,d):b.call(null,this.state,c,d))};g.me=function(a,b,c,d,e){return this.Gb(null,Af(b,this.state,c,d,e))};g.Kd=function(a,b,c){return np(this,b,c)};g.Jd=function(a,b,c){return lp(this,b,c)};g.Ld=function(a,b){return mp(this,b)};g.pc=function(){jp(this);return this.state};
+var tp=function tp(a){switch(arguments.length){case 1:return tp.h(arguments[0]);default:for(var c=[],d=arguments.length,e=0;;)if(e<d)c.push(arguments[e]),e+=1;else break;return tp.A(arguments[0],new Jb(c.slice(1),0,null))}};tp.h=function(a){return new sp(a,null,null,null)};tp.A=function(a,b){var c=null!=b&&(b.m&64||q===b.G)?P(U,b):b,d=D.c(c,rb);c=D.c(c,eg);return new sp(a,d,c,null)};tp.N=function(a){var b=y(a);a=z(a);return tp.A(b,a)};tp.L=1;
+var up=function up(a){if(null!=a&&null!=a.we)return a.we();var c=up[n(null==a?null:a)];if(null!=c)return c.h?c.h(a):c.call(null,a);c=up._;if(null!=c)return c.h?c.h(a):c.call(null,a);throw Cb("IDisposable.dispose!",a);};function ip(a,b,c,d){c===d||a.rc?a=null:null==a.Sb?(a.rc=!0,null==pp&&(pp=[],!1===bp.Yd&&$o(bp)),a=pp.push(a)):a=!0===a.Sb?rp(a,!1):a.Sb.h?a.Sb.h(a):a.Sb.call(null,a);return a}
+function vp(a,b,c,d,e,f,h,k){this.Cb=a;this.state=b;this.rc=c;this.We=d;this.Nc=e;this.gb=f;this.Sb=h;this.ee=k;this.m=2153807872;this.J=114690}function wp(a){var b=dp;dp=null;try{return a.pc(null)}finally{dp=b}}function rp(a,b){var c=a.state;if(t(b)){var d=a.Cb;try{a.ee=null;var e=hp(d,a)}catch(f){e=f,a.state=e,a.ee=e,e=a.rc=!1}}else e=hp(a.Cb,a);a.We||(a.state=e,null==a.gb||G.c(c,e)||np(a,c,e));return e}
+function xp(a,b){var c=null!=b&&(b.m&64||q===b.G)?P(U,b):b,d=D.c(c,Rm),e=D.c(c,Tj),f=D.c(c,on);c=D.c(c,xm);null!=d&&(a.Sb=d);null!=e&&(a.Jf=e);null!=f&&(a.Ze=f);null!=c&&(a.We=c)}g=vp.prototype;g.R=function(a,b,c){return op(this,b,c,["Reaction ",v.h(od(this)),":"].join(""))};g.U=function(){return ja(this)};g.K=function(a,b){return this===b};
+g.we=function(){var a=this.state,b=this.Nc;this.Sb=this.state=this.Nc=null;this.rc=!0;b=E(yi(b));for(var c=null,d=0,e=0;;)if(e<d){var f=c.$(null,e);Nc(f,this);e+=1}else if(b=E(b))c=b,Ae(c)?(b=Wc(c),e=Xc(c),c=b,d=H(b),b=e):(b=y(c),Nc(b,this),b=z(c),c=null,d=0),e=0;else break;null!=this.Ze&&this.Ze(a);a=this.bg;if(null==a)return null;b=a.length;for(c=0;;)if(c<b)d=a[c],d.h?d.h(this):d.call(null,this),c+=1;else return null};g.Gb=function(a,b){var c=this.state;this.state=b;this.Jf(c,b);np(this,c,b);return b};
+g.je=function(a,b){var c=this;return c.Gb(null,function(){var a=wp(c);return b.h?b.h(a):b.call(null,a)}())};g.ke=function(a,b,c){var d=this;return d.Gb(null,function(){var a=wp(d);return b.c?b.c(a,c):b.call(null,a,c)}())};g.le=function(a,b,c,d){var e=this;return e.Gb(null,function(){var a=wp(e);return b.l?b.l(a,c,d):b.call(null,a,c,d)}())};g.me=function(a,b,c,d,e){return this.Gb(null,Af(b,wp(this),c,d,e))};g.Kd=function(a,b,c){return np(this,b,c)};g.Jd=function(a,b,c){return lp(this,b,c)};
+g.Ld=function(a,b){var c=te(this.gb);mp(this,b);return!c&&te(this.gb)&&null==this.Sb?this.we():null};g.pc=function(){var a=this.ee;if(null!=a)throw a;(a=null==dp)&&qp();a&&null==this.Sb?this.rc&&(a=this.state,this.state=this.Cb.B?this.Cb.B():this.Cb.call(null),null==this.gb||G.c(a,this.state)||np(this,a,this.state)):(jp(this),this.rc&&rp(this,!1));return this.state};
+function yp(a){for(var b=[],c=arguments.length,d=0;;)if(d<c)b.push(arguments[d]),d+=1;else break;c=arguments[0];b=1<b.length?new Jb(b.slice(1),0,null):null;var e=null!=b&&(b.m&64||q===b.G)?P(U,b):b;b=D.c(e,Rm);d=D.c(e,Tj);e=D.c(e,on);c=new vp(c,null,!0,!1,null,null,null,null);xp(c,new r(null,3,[Rm,b,Tj,d,on,e],null));return c}var zp=yp(null);
+function Ap(a,b){var c=Bp,d=zp,e=hp(a,d);null!=d.Nc&&(zp=yp(null),xp(d,c),d.Cb=a,d.Sb=function(){return function(){return cp.h?cp.h(b):cp.call(null,b)}}(d,e),b.cljsRatom=d);return e};var Cp;function Dp(a,b){var c=b.argv;if(null==c){c=T;var d=a.constructor;a:for(var e=Ea(b),f=e.length,h=Ef,k=0;;)if(k<f){var l=e[k];h=K.l(h,hf.h(l),b[l]);k+=1}else break a;c=new R(null,2,5,c,[d,h],null)}return c}function Ep(a){var b;if(b=me(a))a=null==a?null:a.prototype,b=null!=(null==a?null:a.reagentRender);return b}if("undefined"===typeof Fp)var Fp=null;
+function Gp(a){for(;;){var b=a.reagentRender,c=!0===a.cljsLegacyRender?b.call(a,a):function(){var c=Dp(a,a.props);switch(H(c)){case 1:return b.call(a);case 2:return b.call(a,Vd(c,1));case 3:return b.call(a,Vd(c,1),Vd(c,2));case 4:return b.call(a,Vd(c,1),Vd(c,2),Vd(c,3));case 5:return b.call(a,Vd(c,1),Vd(c,2),Vd(c,3),Vd(c,4));default:return b.apply(a,Lb(c).slice(1))}}();if(ze(c))return Fp.h?Fp.h(c):Fp.call(null,c);if(Fe(c))c=Ep(c)?function(a,b,c,h){return function(){function a(a){var c=null;if(0<arguments.length){c=
+0;for(var d=Array(arguments.length-0);c<d.length;)d[c]=arguments[c+0],++c;c=new Jb(d,0,null)}return b.call(this,c)}function b(a){a=Kb(Xg,h,a);return Fp.h?Fp.h(a):Fp.call(null,a)}a.L=0;a.N=function(a){a=E(a);return b(a)};a.A=b;return a}()}(a,b,null,c):c,a.reagentRender=c;else return c}}
+var Bp=new r(null,1,[xm,!0],null),Hp=new r(null,1,[zm,function(){var a=this.cljsRatom;this.cljsIsDirty=!1;return null==a?Ap(function(a,c){return function(){a:{var a=Cp;Cp=c;try{var b=Gp(c);break a}finally{Cp=a}b=void 0}return b}}(a,this),this):rp(a,!1)}],null);
+function Ip(a,b){var c=a instanceof L?a.ea:null;switch(c){case "getDefaultProps":throw Error("getDefaultProps not supported");case "getInitialState":return function(){return function(){var a=this.cljsState;a=null!=a?a:this.cljsState=tp.h(null);return fg(a,b.call(this,this))}}(a,c);case "componentWillReceiveProps":return function(){return function(a){return b.call(this,this,Dp(this,a))}}(a,c);case "shouldComponentUpdate":return function(){return function(a){var c=So;if(c)return c;c=this.props.argv;
+var d=a.argv,h=null==c||null==d;return null==b?h||!G.c(c,d):h?b.call(this,this,Dp(this,this.props),Dp(this,a)):b.call(this,this,c,d)}}(a,c);case "componentWillUpdate":return function(){return function(a){return b.call(this,this,Dp(this,a))}}(a,c);case "componentDidUpdate":return function(){return function(a){return b.call(this,this,Dp(this,a))}}(a,c);case "componentWillMount":return function(){return function(){this.cljsMountOrder=To+=1;return null==b?null:b.call(this,this)}}(a,c);case "componentDidMount":return function(){return function(){return b.call(this,
+this)}}(a,c);case "componentWillUnmount":return function(){return function(){var a=this.cljsRatom;null!=a&&up(a);this.cljsIsDirty=!1;return null==b?null:b.call(this,this)}}(a,c);default:return null}}function Jp(a,b){var c=Ip(a,b);return t(c)?c:b}var Kp=new r(null,3,[dm,null,eo,null,Kl,null],null),Lp=function(a){return function(b){return function(c){var d=D.c(B(b),c);if(null!=d)return d;d=a.h?a.h(c):a.call(null,c);gg.M(b,K,c,d);return d}}(dg.h(Ef))}(Qo);
+function Mp(a){return Ue(function(a,c,d){return K.l(a,hf.h(Lp.h?Lp.h(c):Lp.call(null,c)),d)},Ef,a)}function Np(a){var b=function(){var b=pm.h(a);return t(b)?b:wn.h(a)}(),c=null==b,d=t(b)?b:zm.h(a),e=""+v.h(function(){var b=Bk.h(a);return t(b)?b:Ro(d)}());a:switch(e){case "":var f=""+v.h(Zi());break a;default:f=e}b=Ue(function(){return function(a,b,c){return K.l(a,b,Jp(b,c))}}(b,c,d,e,f),Ef,a);return K.A(b,Bk,f,be([Pn,!1,bk,c,pm,d,zm,zm.h(Hp)]))}
+function Op(a){return Ue(function(a,c,d){a[jf(c)]=d;return a},{},a)}function Pp(a){a=Op(Np(hi.A(be([Kp,Mp(a)]))));return Ko.h?Ko.h(a):Ko.call(null,a)};var Qp=/([^\s\.#]+)(?:#([^\s\.#]+))?(?:\.([^\s#]+))?/;function Rp(a){return a instanceof L||a instanceof rd}var Sp={"class":"className","for":"htmlFor",charset:"charSet"};function Tp(a,b,c){if(Rp(b)){var d=jf(b);d=Sp.hasOwnProperty(d)?Sp[d]:null;b=null==d?Sp[jf(b)]=Qo(b):d}a[b]=Up.h?Up.h(c):Up.call(null,c);return a}
+function Up(a){return"object"!==n(a)?a:Rp(a)?jf(a):xe(a)?Ue(Tp,{},a):ue(a)?cj(a):Fe(a)?function(){function b(a){var b=null;if(0<arguments.length){b=0;for(var d=Array(arguments.length-0);b<d.length;)d[b]=arguments[b+0],++b;b=new Jb(d,0,null)}return c.call(this,b)}function c(b){return P(a,b)}b.L=0;b.N=function(a){a=E(a);return c(a)};b.A=c;return b}():cj(a)}function Vp(a,b,c){a=null==a?{}:a;a[b]=c;return a}if("undefined"===typeof Wp)var Wp=null;
+var Xp=new ti(null,new r(null,6,["url",null,"tel",null,"text",null,"textarea",null,"password",null,"search",null],null),null),Yp=function Yp(a){if(t(a.cljsInputLive)){a.cljsInputDirty=!1;var c=a.cljsRenderedValue,d=a.cljsDOMValue,e=Wp.h?Wp.h(a):Wp.call(null,a);if(!G.c(c,d)){if(e===document.activeElement&&He(Xp,e.type)&&"string"===typeof c&&"string"===typeof d){var f=e.value;if(!G.c(f,d))return bp.enqueue("afterRender",function(){return function(){return Yp.h?Yp.h(a):Yp.call(null,a)}}(f,c,d,e));d=
+H(f)-e.selectionStart;d=H(c)-d;a.cljsDOMValue=c;e.value=c;e.selectionStart=d;return e.selectionEnd=d}a.cljsDOMValue=c;return e.value=c}}return null};function Zp(a,b,c){a.cljsDOMValue=c.target.value;t(a.cljsInputDirty)||(a.cljsInputDirty=!0,bp.enqueue("afterRender",function(){return Yp(a)}));return b.h?b.h(c):b.call(null,c)}
+function $p(a){var b=Cp;if(t(function(){var b=null!=a;return b?(b=a.hasOwnProperty("onChange"),t(b)?a.hasOwnProperty("value"):b):b}())){var c=a.value,d=null==c?"":c,e=a.onChange;t(b.cljsInputLive)||(b.cljsInputLive=!0,b.cljsDOMValue=d);b.cljsRenderedValue=d;delete a.value;a.defaultValue=d;a.onChange=function(a,c,d,e){return function(a){return Zp(b,e,a)}}(a,c,d,e)}}
+var aq=null,cq=new r(null,4,[ln,"ReagentInput",nl,Yp,Wm,function(a){return a.cljsInputLive=null},Dm,function(a,b,c,d){$p(c);return bq.M?bq.M(a,b,c,d):bq.call(null,a,b,c,d)}],null);function dq(a){if(xe(a))try{var b=D.c(a,mk)}catch(c){b=null}else b=null;return b}var eq={};
+function fq(a,b,c){var d=a.name,e=J(b,c,null),f=null==e||xe(e);e=Up(f?e:null);var h=a.id;e=null!=h&&null==(null==e?null:e.id)?Vp(e,"id",h):e;a=a.className;null==a?a=e:(h=null==e?null:e.className,a=Vp(e,"className",null==h?a:[v.h(a)," ",v.h(h)].join("")));c+=f?1:0;a:switch(d){case "input":case "textarea":f=!0;break a;default:f=!1}if(f)return f=T,null==aq&&(aq=Pp(cq)),b=pe(new R(null,5,5,f,[aq,b,d,a,c],null),qe(b)),gq.h?gq.h(b):gq.call(null,b);f=dq(qe(b));f=null==f?a:Vp(a,"key",f);return bq.M?bq.M(b,
+d,f,c):bq.call(null,b,d,f,c)}
+function hq(a){for(;;){var b=J(a,0,null);if(Rp(b)||"string"===typeof b){b=jf(b);var c=b.indexOf("\x3e");switch(c){case -1:c=b;b=eq;var d=c;b=b.hasOwnProperty(d)?b[d]:null;if(null==b){b=c;var e=z(Ji(Qp,jf(c)));c=J(e,0,null);d=J(e,1,null);e=J(e,2,null);e=null==e?null:Do(e,/\./," ");b=eq[b]={name:c,id:d,className:e}}return fq(b,a,1);case 0:return b=J(a,1,null),fq({name:b},a,2);default:a=new R(null,2,5,T,[b.substring(0,c),K.l(a,0,b.substring(c+1))],null)}}else return c=b.cljsReactClass,null==c?Ep(b)?
+b=b.cljsReactClass=b:(c=qe(b),c=K.l(c,Dm,b),c=Pp(c),b=b.cljsReactClass=c):b=c,c={argv:a},d=dq(qe(a)),a=null==d?dq(J(a,1,null)):d,null!=a&&(c.key=a),Go.createElement(b,c)}}function gq(a){return"object"!==n(a)?a:ze(a)?hq(a):De(a)?iq.h?iq.h(a):iq.call(null,a):Rp(a)?jf(a):(null!=a?a.m&2147483648||q===a.ma||(a.m?0:Ab(Kc,a)):Ab(Kc,a))?Vi(be([a])):a}Fp=gq;function iq(a){a=Lb(a);for(var b=a.length,c=0;;)if(c<b)a[c]=gq(a[c]),c+=1;else break;return a}
+function bq(a,b,c,d){var e=H(a)-d;switch(e){case 0:return Go.createElement(b,c);case 1:return Go.createElement(b,c,gq(J(a,d,null)));default:return Go.createElement.apply(null,Ue(function(){return function(a,b,c){b>=d&&a.push(gq(c));return a}}(e),[b,c],a))}};if("undefined"===typeof jq)var jq=null;function kq(){if(null!=jq)return jq;if("undefined"!==typeof ReactDOM)return jq=ReactDOM;if("undefined"!==typeof require){var a=jq=require("react-dom");if(t(a))return a;throw Error("require('react-dom') failed");}throw Error("js/ReactDOM is missing");}if("undefined"===typeof lq)var lq=dg.h(Ef);
+function mq(a,b,c){var d=So;So=!0;try{return kq().render(a.B?a.B():a.call(null),b,function(){return function(){var d=So;So=!1;try{return gg.M(lq,K,b,new R(null,2,5,T,[a,b],null)),Zo(bp,"afterRender"),null!=c?c.B?c.B():c.call(null):null}finally{So=d}}}(d))}finally{So=d}}function nq(a,b){return mq(a,b,null)}function oq(a,b,c){qp();return mq(function(){return gq(me(a)?a.B?a.B():a.call(null):a)},b,c)}Wp=function(a){return kq().findDOMNode(a)};function pq(a){switch(arguments.length){case 2:return oq(arguments[0],arguments[1],null);case 3:return oq(arguments[0],arguments[1],arguments[2]);default:throw Error(["Invalid arity: ",v.h(arguments.length)].join(""));}}function qq(a,b){return oq(a,b,null)}
+da("reagent.core.force_update_all",function(){qp();qp();for(var a=E(mh(B(lq))),b=null,c=0,d=0;;)if(d<c){var e=b.$(null,d);P(nq,e);d+=1}else if(a=E(a))b=a,Ae(b)?(a=Wc(b),d=Xc(b),b=a,c=H(a),a=d):(a=y(b),P(nq,a),a=z(b),b=null,c=0),d=0;else break;return Zo(bp,"afterRender")});var rq=yi(df(0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,25,28,29,30,31)),sq=ke([yi(df(24,26,128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143,145,146,147,148,149,150,151,153,154)),new r(null,2,[qn,xl,yk,uk],null),yi(df(156)),new r(null,1,[yk,uk],null),yi(df(27)),new r(null,1,[yk,Gj],null),yi(df(152,158,159)),new r(null,1,[yk,ll],null),yi(df(144)),new r(null,1,[yk,ql],null),yi(df(157)),new r(null,1,[yk,Rk],null),yi(df(155)),new r(null,1,[yk,Hl],null)]),tq=Pe([Ej,Gj,
+uk,Fk,Jk,Qk,Rk,ll,ql,Hl,jm,om,Cm,$n],[ke([rq,new r(null,1,[qn,Ul],null),yi(df(32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47)),new r(null,2,[qn,Fm,yk,Qk],null),yi(df(48,49,50,51,52,53,54,55,56,57,59)),new r(null,1,[qn,Xl],null),yi(df(58,60,61,62,63)),new r(null,1,[yk,$n],null),yi(df(64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,100,101,102,103,104,105,106,107,108,109,110,111,112,113,114,115,116,117,118,119,120,121,122,123,124,125,126)),
+new r(null,1,[yk,Fk],null),yi(df(127)),new r(null,1,[qn,Ul],null)]),Pe([Sk,yi(df(88,94,95)),rq,yi(df(91)),yi(df(80)),yi(df(32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47)),yi(df(127)),yi(df(48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,81,82,83,84,85,86,87,89,90,92,96,97,98,99,100,101,102,103,104,105,106,107,108,109,110,111,112,113,114,115,116,117,118,119,120,121,122,123,124,125,126)),yi(df(93))],[po,new r(null,1,[yk,ll],null),new r(null,1,[qn,xl],
+null),new r(null,1,[yk,Hl],null),new r(null,1,[yk,ql],null),new r(null,2,[qn,Fm,yk,jm],null),new r(null,1,[qn,Ul],null),new r(null,2,[qn,Pj,yk,uk],null),new r(null,1,[yk,Rk],null)]),ke([rq,new r(null,1,[qn,xl],null),yi(df(32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,100,101,102,103,104,105,106,107,108,109,110,111,112,113,114,115,116,117,118,
+119,120,121,122,123,124,125,126,127,160,161,162,163,164,165,166,167,168,169,170,171,172,173,174,175,176,177,178,179,180,181,182,183,184,185,186,187,188,189,190,191,192,193,194,195,196,197,198,199,200,201,202,203,204,205,206,207,208,209,210,211,212,213,214,215,216,217,218,219,220,221,222,223,224,225,226,227,228,229,230,231,232,233,234,235,236,237,238,239,240,241,242,243,244,245,246,247,248,249,250,251,252,253,254,255)),new r(null,1,[qn,Pl],null)]),ke([Sk,zj,rq,new r(null,1,[qn,Fn],null),yi(df(32,33,
+34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,100,101,102,103,104,105,106,107,108,109,110,111,112,113,114,115,116,117,118,119,120,121,122,123,124,125,126)),new r(null,1,[qn,Fn],null),yi(df(127)),new r(null,1,[qn,Ul],null),El,In]),ke([rq,new r(null,1,[qn,xl],null),yi(df(48,49,50,51,52,53,54,55,56,57,59)),new r(null,1,[qn,Xl],null),yi(df(58,60,61,62,
+63)),new r(null,1,[yk,Cm],null),yi(df(32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47)),new r(null,2,[qn,Fm,yk,om],null),yi(df(64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,100,101,102,103,104,105,106,107,108,109,110,111,112,113,114,115,116,117,118,119,120,121,122,123,124,125,126)),new r(null,2,[qn,Rl,yk,uk],null),yi(df(127)),new r(null,1,[qn,Ul],null)]),ke([rq,new r(null,1,[qn,Ul],null),yi(df(32,33,34,35,36,37,38,39,40,41,42,43,44,45,
+46,47)),new r(null,1,[qn,Fm],null),yi(df(48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63)),new r(null,1,[yk,$n],null),yi(df(64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,100,101,102,103,104,105,106,107,108,109,110,111,112,113,114,115,116,117,118,119,120,121,122,123,124,125,126)),new r(null,1,[yk,Fk],null),yi(df(127)),new r(null,1,[qn,Ul],null)]),ke([Sk,um,re.c(rq,7),new r(null,1,[qn,Ul],null),yi(df(32,33,34,35,36,37,38,39,40,41,42,43,44,
+45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,100,101,102,103,104,105,106,107,108,109,110,111,112,113,114,115,116,117,118,119,120,121,122,123,124,125,126,127)),new r(null,1,[qn,ak],null),yi(df(7)),new r(null,1,[yk,uk],null),El,Wj]),ke([rq,new r(null,1,[qn,Ul],null),yi(df(32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,
+69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,100,101,102,103,104,105,106,107,108,109,110,111,112,113,114,115,116,117,118,119,120,121,122,123,124,125,126,127)),new r(null,1,[qn,Ul],null)]),ke([Sk,po,rq,new r(null,1,[qn,Ul],null),yi(df(32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47)),new r(null,2,[qn,Fm,yk,Qk],null),yi(df(58)),new r(null,1,[yk,$n],null),yi(df(48,49,50,51,52,53,54,55,56,57,59)),new r(null,2,[qn,Xl,yk,Ej],null),yi(df(60,61,62,63)),new r(null,
+2,[qn,Fm,yk,Ej],null),yi(df(64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,100,101,102,103,104,105,106,107,108,109,110,111,112,113,114,115,116,117,118,119,120,121,122,123,124,125,126)),new r(null,1,[yk,Fk],null),yi(df(127)),new r(null,1,[qn,Ul],null)]),ke([Sk,po,rq,new r(null,1,[qn,xl],null),yi(df(64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,100,101,102,103,104,105,106,107,108,
+109,110,111,112,113,114,115,116,117,118,119,120,121,122,123,124,125,126)),new r(null,2,[qn,Rl,yk,uk],null),yi(df(48,49,50,51,52,53,54,55,56,57,59)),new r(null,2,[qn,Xl,yk,Jk],null),yi(df(60,61,62,63)),new r(null,2,[qn,Fm,yk,Jk],null),yi(df(58)),new r(null,1,[yk,Cm],null),yi(df(32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47)),new r(null,2,[qn,Fm,yk,om],null),yi(df(127)),new r(null,1,[qn,Ul],null)]),ke([rq,new r(null,1,[qn,xl],null),yi(df(32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47)),new r(null,
+1,[qn,Fm],null),yi(df(48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,100,101,102,103,104,105,106,107,108,109,110,111,112,113,114,115,116,117,118,119,120,121,122,123,124,125,126)),new r(null,2,[qn,Pj,yk,uk],null),yi(df(127)),new r(null,1,[qn,Ul],null)]),ke([rq,new r(null,1,[qn,xl],null),yi(df(32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47)),new r(null,1,[qn,Fm],null),yi(df(64,65,66,67,68,69,
+70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,100,101,102,103,104,105,106,107,108,109,110,111,112,113,114,115,116,117,118,119,120,121,122,123,124,125,126)),new r(null,2,[qn,Rl,yk,uk],null),yi(df(48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63)),new r(null,1,[yk,Cm],null),yi(df(127)),new r(null,1,[qn,Ul],null)]),ke([rq,new r(null,1,[qn,xl],null),yi(df(32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63)),new r(null,
+1,[qn,Ul],null),yi(df(64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,100,101,102,103,104,105,106,107,108,109,110,111,112,113,114,115,116,117,118,119,120,121,122,123,124,125,126)),new r(null,1,[yk,uk],null),yi(df(127)),new r(null,1,[qn,Ul],null)]),ke([rq,new r(null,1,[qn,Ul],null),yi(df(32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,
+83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,100,101,102,103,104,105,106,107,108,109,110,111,112,113,114,115,116,117,118,119,120,121,122,123,124,125,126,127)),new r(null,1,[qn,Ul],null)])]);function uq(a,b){return Wf(function(a){var c=J(a,0,null);a=J(a,1,null);return t(c.h?c.h(b):c.call(null,b))?a:null},a)}
+function vq(a,b){var c=D.c(tq,a),d=uq(sq,b);var e=t(d)?d:uq(c,160<=b?65:b);d=qn.h(e);e=yk.h(e);if(t(e)){var f=D.c(tq,e);c=El.h(c);f=Sk.h(f);d=Wg(vg(ub,new R(null,3,5,T,[c,d,f],null)));return new R(null,2,5,T,[e,d],null)}return new R(null,2,5,T,[a,t(d)?new R(null,1,5,T,[d],null):he],null)}
+var xq=P(hi,function wq(a){return new kf(null,function(){for(;;){var c=E(a);if(c){if(Ae(c)){var d=Wc(c),e=H(d),f=of(e);a:for(var h=0;;)if(h<e){var k=A.c(d,h);k=ke([k,xg(ag.c(vq,k),Fi(0,160,1))]);f.add(k);h+=1}else{d=!0;break a}return d?qf(f.Da(),wq(Xc(c))):qf(f.Da(),null)}f=y(c);return ae(ke([f,xg(ag.c(vq,f),Fi(0,160,1))]),wq(vd(c)))}return null}},null,null)}(lh(tq)));function yq(a,b){var c=Array.prototype.slice.call(arguments),d=c.shift();if("undefined"==typeof d)throw Error("[goog.string.format] Template required");return d.replace(/%([0\-\ \+]*)(\d+)?(\.(\d+))?([%sfdiu])/g,function(a,b,d,k,l,p,m,u){if("%"==p)return"%";var e=c.shift();if("undefined"==typeof e)throw Error("[goog.string.format] Not enough arguments");arguments[0]=e;return yq.fc[p].apply(null,arguments)})}yq.fc={};
+yq.fc.s=function(a,b,c){return isNaN(c)||""==c||a.length>=Number(c)?a:a=-1<b.indexOf("-",0)?a+sa(" ",Number(c)-a.length):sa(" ",Number(c)-a.length)+a};
+yq.fc.f=function(a,b,c,d,e){d=a.toString();isNaN(e)||""==e||(d=parseFloat(a).toFixed(e));var f=0>Number(a)?"-":0<=b.indexOf("+")?"+":0<=b.indexOf(" ")?" ":"";0<=Number(a)&&(d=f+d);if(isNaN(c)||d.length>=Number(c))return d;d=isNaN(e)?Math.abs(Number(a)).toString():Math.abs(Number(a)).toFixed(e);a=Number(c)-d.length-f.length;0<=b.indexOf("-",0)?d=f+d+sa(" ",a):(b=0<=b.indexOf("0",0)?"0":" ",d=f+sa(b,a)+d);return d};yq.fc.d=function(a,b,c,d,e,f,h,k){return yq.fc.f(parseInt(a,10),b,c,d,0,f,h,k)};
+yq.fc.i=yq.fc.d;yq.fc.u=yq.fc.d;function zq(a){var b=be([Vk,null]);return wg.c(t(a)?a:Ef,function(){return function e(a){return new kf(null,function(){for(var b=a;;)if(b=E(b)){if(Ae(b)){var d=Wc(b),k=H(d),l=of(k);a:for(var p=0;;)if(p<k){var m=A.c(d,p),u=J(m,0,null);m=J(m,1,null);t(m)&&l.add(new R(null,2,5,T,[u,m],null));p+=1}else{d=!0;break a}return d?qf(l.Da(),e(Xc(b))):qf(l.Da(),null)}d=y(b);l=J(d,0,null);d=J(d,1,null);if(t(d))return ae(new R(null,2,5,T,[l,d],null),e(vd(b)));b=vd(b)}else return null},null,null)}(yg(2,2,b))}())}
+function Aq(a){for(var b=[],c=arguments.length,d=0;;)if(d<c)b.push(arguments[d]),d+=1;else break;return Bq(arguments[0],1<b.length?new Jb(b.slice(1),0,null):null)}function Bq(a,b){return Kb(yq,a,b)}dg.h(19);function Cq(a){return Mb(function(a,c){var b=J(c,0,null),e=J(c,1,null);return Do(a,e,""+v.h(b))},a,Oe(function(a){return-H(ee(a))}))}
+function Dq(a){a=""+v.h(a);var b=/function ([^\(]*)\(/;if("string"===typeof a)a=b.exec(a),a=null==a?null:1===H(a)?y(a):Wg(a);else throw new TypeError("re-find must match against a string.");a=Bf(ee(a));return Cq(t(a)?a:"function")}function Eq(a,b){a.schema$utils$schema=b}dg.h(!1);var Fq,Gq=function Gq(a){if(null!=a&&null!=a.xb)return a.xb(a);var c=Gq[n(null==a?null:a)];if(null!=c)return c.h?c.h(a):c.call(null,a);c=Gq._;if(null!=c)return c.h?c.h(a):c.call(null,a);throw Cb("Schema.explain",a);};
+Gq["function"]=function(a){var b=a.schema$utils$schema;return t(b)?Gq(b):t(G.c?G.c(null,a):G.call(null,null,a))?Wl:t(G.c?G.c(Boolean,a):G.call(null,Boolean,a))?xk:t(G.c?G.c(Number,a):G.call(null,Number,a))?sl:t(G.c?G.c(null,a):G.call(null,null,a))?Cl:t(G.c?G.c(Date,a):G.call(null,Date,a))?Ll:t(G.c?G.c(yj,a):G.call(null,yj,a))?ym:a};function Hq(a,b,c,d){this.nc=a;this.v=b;this.j=c;this.w=d;this.m=2229667594;this.J=139264}g=Hq.prototype;g.V=function(a,b){return this.I(null,b,null)};
+g.I=function(a,b,c){switch(b instanceof L?b.ea:null){case "_":return this.nc;default:return D.l(this.j,b,c)}};g.R=function(a,b,c){return Y(b,function(){return function(a){return Y(b,Qi,""," ","",c,a)}}(this),"#schema.core.AnythingSchema{",", ","}",c,O.c(new R(null,1,5,T,[new R(null,2,5,T,[Ck,this.nc],null)],null),this.j))};g.ba=function(){return new fh(0,this,1,new R(null,1,5,T,[Ck],null),t(this.j)?dd(this.j):Cf())};g.P=function(){return this.v};g.W=function(){return 1+H(this.j)};
+g.U=function(){var a=this,b=this.w;if(null!=b)return b;var c=function(){return function(){return function(a){return-1432036169^Dd(a)}}(b,a)(a)}();return this.w=c};g.K=function(a,b){return null!=b&&this.constructor===b.constructor&&G.c(this.nc,b.nc)&&G.c(this.j,b.j)};g.ga=function(a,b){return He(new ti(null,new r(null,1,[Ck,null],null),null),b)?le.c(tc(wg.c(Ef,this),this.v),b):new Hq(this.nc,this.v,Bf(le.c(this.j,b)),null)};
+g.O=function(a,b,c){return t(N.c?N.c(Ck,b):N.call(null,Ck,b))?new Hq(c,this.v,this.j,null):new Hq(this.nc,this.v,K.l(this.j,b,c),null)};g.S=function(){return E(O.c(new R(null,1,5,T,[new R(null,2,5,T,[Ck,this.nc],null)],null),this.j))};g.T=function(a,b){return new Hq(this.nc,b,this.j,this.w)};g.X=function(a,b){return ze(b)?this.O(null,A.c(b,0),A.c(b,1)):Mb(Tb,this,b)};g.xb=function(){return Al};var Iq=new Hq(null,null,null,null);
+function Jq(a,b,c,d,e){this.wb=a;this.Xb=b;this.v=c;this.j=d;this.w=e;this.m=2229667594;this.J=139264}g=Jq.prototype;g.V=function(a,b){return this.I(null,b,null)};g.I=function(a,b,c){switch(b instanceof L?b.ea:null){case "p?":return this.wb;case "pred-name":return this.Xb;default:return D.l(this.j,b,c)}};
+g.R=function(a,b,c){return Y(b,function(){return function(a){return Y(b,Qi,""," ","",c,a)}}(this),"#schema.core.Predicate{",", ","}",c,O.c(new R(null,2,5,T,[new R(null,2,5,T,[Un,this.wb],null),new R(null,2,5,T,[Tm,this.Xb],null)],null),this.j))};g.ba=function(){return new fh(0,this,2,new R(null,2,5,T,[Un,Tm],null),t(this.j)?dd(this.j):Cf())};g.P=function(){return this.v};g.W=function(){return 2+H(this.j)};
+g.U=function(){var a=this,b=this.w;if(null!=b)return b;var c=function(){return function(){return function(a){return 2041221968^Dd(a)}}(b,a)(a)}();return this.w=c};g.K=function(a,b){return null!=b&&this.constructor===b.constructor&&G.c(this.wb,b.wb)&&G.c(this.Xb,b.Xb)&&G.c(this.j,b.j)};g.ga=function(a,b){return He(new ti(null,new r(null,2,[Tm,null,Un,null],null),null),b)?le.c(tc(wg.c(Ef,this),this.v),b):new Jq(this.wb,this.Xb,this.v,Bf(le.c(this.j,b)),null)};
+g.O=function(a,b,c){return t(N.c?N.c(Un,b):N.call(null,Un,b))?new Jq(c,this.Xb,this.v,this.j,null):t(N.c?N.c(Tm,b):N.call(null,Tm,b))?new Jq(this.wb,c,this.v,this.j,null):new Jq(this.wb,this.Xb,this.v,K.l(this.j,b,c),null)};g.S=function(){return E(O.c(new R(null,2,5,T,[new R(null,2,5,T,[Un,this.wb],null),new R(null,2,5,T,[Tm,this.Xb],null)],null),this.j))};g.T=function(a,b){return new Jq(this.wb,this.Xb,b,this.j,this.w)};g.X=function(a,b){return ze(b)?this.O(null,A.c(b,0),A.c(b,1)):Mb(Tb,this,b)};
+g.xb=function(){return G.c(this.wb,Ge)?bo:G.c(this.wb,gf)?kn:G.c(this.wb,qd)?hk:G.c(this.wb,yb)?Wl:Tb(Tb(wd,this.Xb),yl)};function Kq(a){var b=td.h(Dq(a));if(!Fe(a))throw Error(Bq("Not a function: %s",be([a])));return new Jq(a,b,null,null,null)}RegExp.prototype.xb=function(){return td.h(['#"',v.h((""+v.h(this)).slice(1,-1)),'"'].join(""))};var Lq=Kq(yb),Mq=Boolean,Nq=Number,Oq=Kq(Ge),Pq=Kq(gf);Kq(qd);
+"undefined"===typeof Fq&&(Fq=function(a){this.Bf=a;this.m=393216;this.J=0},Fq.prototype.T=function(a,b){return new Fq(b)},Fq.prototype.P=function(){return this.Bf},Fq.prototype.xb=function(){return Cl},Fq.Wc=function(){return new R(null,1,5,T,[bn],null)},Fq.qc=!0,Fq.Tb="schema.core/t_schema$core38849",Fq.Ec=function(a,b){return Jc(b,"schema.core/t_schema$core38849")});function Qq(a,b,c,d){this.ia=a;this.v=b;this.j=c;this.w=d;this.m=2229667594;this.J=139264}g=Qq.prototype;
+g.V=function(a,b){return this.I(null,b,null)};g.I=function(a,b,c){switch(b instanceof L?b.ea:null){case "schema":return this.ia;default:return D.l(this.j,b,c)}};g.R=function(a,b,c){return Y(b,function(){return function(a){return Y(b,Qi,""," ","",c,a)}}(this),"#schema.core.Maybe{",", ","}",c,O.c(new R(null,1,5,T,[new R(null,2,5,T,[Mj,this.ia],null)],null),this.j))};g.ba=function(){return new fh(0,this,1,new R(null,1,5,T,[Mj],null),t(this.j)?dd(this.j):Cf())};g.P=function(){return this.v};
+g.W=function(){return 1+H(this.j)};g.U=function(){var a=this,b=this.w;if(null!=b)return b;var c=function(){return function(){return function(a){return-805411239^Dd(a)}}(b,a)(a)}();return this.w=c};g.K=function(a,b){return null!=b&&this.constructor===b.constructor&&G.c(this.ia,b.ia)&&G.c(this.j,b.j)};g.ga=function(a,b){return He(new ti(null,new r(null,1,[Mj,null],null),null),b)?le.c(tc(wg.c(Ef,this),this.v),b):new Qq(this.ia,this.v,Bf(le.c(this.j,b)),null)};
+g.O=function(a,b,c){return t(N.c?N.c(Mj,b):N.call(null,Mj,b))?new Qq(c,this.v,this.j,null):new Qq(this.ia,this.v,K.l(this.j,b,c),null)};g.S=function(){return E(O.c(new R(null,1,5,T,[new R(null,2,5,T,[Mj,this.ia],null)],null),this.j))};g.T=function(a,b){return new Qq(this.ia,b,this.j,this.w)};g.X=function(a,b){return ze(b)?this.O(null,A.c(b,0),A.c(b,1)):Mb(Tb,this,b)};g.xb=function(){var a=Gq(this.ia);a=Tb(wd,a);return Tb(a,am)};
+function Rq(a,b,c,d,e){this.Yb=a;this.Hb=b;this.v=c;this.j=d;this.w=e;this.m=2229667594;this.J=139264}g=Rq.prototype;g.V=function(a,b){return this.I(null,b,null)};g.I=function(a,b,c){switch(b instanceof L?b.ea:null){case "preds-and-schemas":return this.Yb;case "error-symbol":return this.Hb;default:return D.l(this.j,b,c)}};
+g.R=function(a,b,c){return Y(b,function(){return function(a){return Y(b,Qi,""," ","",c,a)}}(this),"#schema.core.ConditionalSchema{",", ","}",c,O.c(new R(null,2,5,T,[new R(null,2,5,T,[Il,this.Yb],null),new R(null,2,5,T,[Om,this.Hb],null)],null),this.j))};g.ba=function(){return new fh(0,this,2,new R(null,2,5,T,[Il,Om],null),t(this.j)?dd(this.j):Cf())};g.P=function(){return this.v};g.W=function(){return 2+H(this.j)};
+g.U=function(){var a=this,b=this.w;if(null!=b)return b;var c=function(){return function(){return function(a){return 1418435858^Dd(a)}}(b,a)(a)}();return this.w=c};g.K=function(a,b){return null!=b&&this.constructor===b.constructor&&G.c(this.Yb,b.Yb)&&G.c(this.Hb,b.Hb)&&G.c(this.j,b.j)};g.ga=function(a,b){return He(new ti(null,new r(null,2,[Il,null,Om,null],null),null),b)?le.c(tc(wg.c(Ef,this),this.v),b):new Rq(this.Yb,this.Hb,this.v,Bf(le.c(this.j,b)),null)};
+g.O=function(a,b,c){return t(N.c?N.c(Il,b):N.call(null,Il,b))?new Rq(c,this.Hb,this.v,this.j,null):t(N.c?N.c(Om,b):N.call(null,Om,b))?new Rq(this.Yb,c,this.v,this.j,null):new Rq(this.Yb,this.Hb,this.v,K.l(this.j,b,c),null)};g.S=function(){return E(O.c(new R(null,2,5,T,[new R(null,2,5,T,[Il,this.Yb],null),new R(null,2,5,T,[Om,this.Hb],null)],null),this.j))};g.T=function(a,b){return new Rq(this.Yb,this.Hb,b,this.j,this.w)};g.X=function(a,b){return ze(b)?this.O(null,A.c(b,0),A.c(b,1)):Mb(Tb,this,b)};
+g.xb=function(){return ae(pk,O.c(sg(function(){return function(a){var b=J(a,0,null);a=J(a,1,null);return new R(null,2,5,T,[td.h(Dq(b)),Gq(a)],null)}}(this),be([this.Yb])),t(this.Hb)?new R(null,1,5,T,[this.Hb],null):null))};function Sq(a){return a instanceof L||!1}function Tq(a,b,c,d){this.k=a;this.v=b;this.j=c;this.w=d;this.m=2229667594;this.J=139264}g=Tq.prototype;g.V=function(a,b){return this.I(null,b,null)};
+g.I=function(a,b,c){switch(b instanceof L?b.ea:null){case "k":return this.k;default:return D.l(this.j,b,c)}};g.R=function(a,b,c){return Y(b,function(){return function(a){return Y(b,Qi,""," ","",c,a)}}(this),"#schema.core.OptionalKey{",", ","}",c,O.c(new R(null,1,5,T,[new R(null,2,5,T,[Yl,this.k],null)],null),this.j))};g.ba=function(){return new fh(0,this,1,new R(null,1,5,T,[Yl],null),t(this.j)?dd(this.j):Cf())};g.P=function(){return this.v};g.W=function(){return 1+H(this.j)};
+g.U=function(){var a=this,b=this.w;if(null!=b)return b;var c=function(){return function(){return function(a){return-1508333161^Dd(a)}}(b,a)(a)}();return this.w=c};g.K=function(a,b){return null!=b&&this.constructor===b.constructor&&G.c(this.k,b.k)&&G.c(this.j,b.j)};g.ga=function(a,b){return He(new ti(null,new r(null,1,[Yl,null],null),null),b)?le.c(tc(wg.c(Ef,this),this.v),b):new Tq(this.k,this.v,Bf(le.c(this.j,b)),null)};
+g.O=function(a,b,c){return t(N.c?N.c(Yl,b):N.call(null,Yl,b))?new Tq(c,this.v,this.j,null):new Tq(this.k,this.v,K.l(this.j,b,c),null)};g.S=function(){return E(O.c(new R(null,1,5,T,[new R(null,2,5,T,[Yl,this.k],null)],null),this.j))};g.T=function(a,b){return new Tq(this.k,b,this.j,this.w)};g.X=function(a,b){return ze(b)?this.O(null,A.c(b,0),A.c(b,1)):Mb(Tb,this,b)};function Uq(a){return new Tq(a,null,null,null)}
+function Vq(a){var b=Sq(a);if(t(t(b)?b:a instanceof Tq)){if(a instanceof L)return a;b=t(Sq(a))?Zn:t(a instanceof Tq)?Nj:null;if(!(a instanceof L))if(t(a instanceof Tq))a=a.k;else throw Error(Bq("Bad explicit key: %s",be([a])));a=Tb(wd,a);return Tb(a,b)}return Gq(a)}
+function Wq(a){return wg.c(Ef,function(){return function d(a){return new kf(null,function(){for(;;){var c=E(a);if(c){if(Ae(c)){var f=Wc(c),h=H(f),k=of(h);a:for(var l=0;;)if(l<h){var p=A.c(f,l),m=J(p,0,null);p=J(p,1,null);m=new R(null,2,5,T,[Vq(m),Gq(p)],null);k.add(m);l+=1}else{f=!0;break a}return f?qf(k.Da(),d(Xc(c))):qf(k.Da(),null)}f=y(c);k=J(f,0,null);f=J(f,1,null);return ae(new R(null,2,5,T,[Vq(k),Gq(f)],null),d(vd(c)))}return null}},null,null)}(a)}())}r.prototype.xb=function(){return Wq(this)};
+Jh.prototype.xb=function(){return Wq(this)};ti.prototype.xb=function(){return yi(new R(null,1,5,T,[Gq(y(this))],null))};function Xq(a,b,c,d,e,f){this.ia=a;this.Fb=b;this.name=c;this.v=d;this.j=e;this.w=f;this.m=2229667594;this.J=139264}g=Xq.prototype;g.V=function(a,b){return this.I(null,b,null)};g.I=function(a,b,c){switch(b instanceof L?b.ea:null){case "schema":return this.ia;case "optional?":return this.Fb;case "name":return this.name;default:return D.l(this.j,b,c)}};
+g.R=function(a,b,c){return Y(b,function(){return function(a){return Y(b,Qi,""," ","",c,a)}}(this),"#schema.core.One{",", ","}",c,O.c(new R(null,3,5,T,[new R(null,2,5,T,[Mj,this.ia],null),new R(null,2,5,T,[nm,this.Fb],null),new R(null,2,5,T,[Tk,this.name],null)],null),this.j))};g.ba=function(){return new fh(0,this,3,new R(null,3,5,T,[Mj,nm,Tk],null),t(this.j)?dd(this.j):Cf())};g.P=function(){return this.v};g.W=function(){return 3+H(this.j)};
+g.U=function(){var a=this,b=this.w;if(null!=b)return b;var c=function(){return function(){return function(a){return-197981045^Dd(a)}}(b,a)(a)}();return this.w=c};g.K=function(a,b){return null!=b&&this.constructor===b.constructor&&G.c(this.ia,b.ia)&&G.c(this.Fb,b.Fb)&&G.c(this.name,b.name)&&G.c(this.j,b.j)};g.ga=function(a,b){return He(new ti(null,new r(null,3,[Mj,null,Tk,null,nm,null],null),null),b)?le.c(tc(wg.c(Ef,this),this.v),b):new Xq(this.ia,this.Fb,this.name,this.v,Bf(le.c(this.j,b)),null)};
+g.O=function(a,b,c){return t(N.c?N.c(Mj,b):N.call(null,Mj,b))?new Xq(c,this.Fb,this.name,this.v,this.j,null):t(N.c?N.c(nm,b):N.call(null,nm,b))?new Xq(this.ia,c,this.name,this.v,this.j,null):t(N.c?N.c(Tk,b):N.call(null,Tk,b))?new Xq(this.ia,this.Fb,c,this.v,this.j,null):new Xq(this.ia,this.Fb,this.name,this.v,K.l(this.j,b,c),null)};g.S=function(){return E(O.c(new R(null,3,5,T,[new R(null,2,5,T,[Mj,this.ia],null),new R(null,2,5,T,[nm,this.Fb],null),new R(null,2,5,T,[Tk,this.name],null)],null),this.j))};
+g.T=function(a,b){return new Xq(this.ia,this.Fb,this.name,b,this.j,this.w)};g.X=function(a,b){return ze(b)?this.O(null,A.c(b,0),A.c(b,1)):Mb(Tb,this,b)};function Yq(a,b){return new Xq(a,!1,b,null,null,null)}
+function Zq(a){var b=Gi(function(a){return a instanceof Xq&&wb(nm.h(a))},a),c=J(b,0,null),d=J(b,1,null),e=Gi(function(){return function(a){var b=a instanceof Xq;return b?nm.h(a):b}}(b,c,d),d),f=J(e,0,null),h=J(e,1,null);if(!(1>=H(h)&&Vf(function(){return function(a){return!(a instanceof Xq)}}(b,c,d,e,f,h),h)))throw Error(Bq("%s is not a valid sequence schema; %s%s%s",be([a,"a valid sequence schema consists of zero or more `one` elements, ","followed by zero or more `optional` elements, followed by an optional ",
+"schema that will match the remaining elements."])));return new R(null,2,5,T,[O.c(c,f),y(h)],null)}
+R.prototype.xb=function(){var a=this,b=Zq(a),c=J(b,0,null),d=J(b,1,null);return Wg(O.c(function(){return function(a,b,c,d){return function m(e){return new kf(null,function(){return function(){for(;;){var a=E(e);if(a){if(Ae(a)){var b=Wc(a),c=H(b),d=of(c);return function(){for(var a=0;;)if(a<c){var e=A.c(b,a),f=d;var h=t(e.Fb)?ao:zk;var k=Gq(Mj.h(e));e=Tk.h(e);e=Tb(wd,e);k=Tb(e,k);h=Tb(k,h);f.add(h);a+=1}else return!0}()?qf(d.Da(),m(Xc(a))):qf(d.Da(),null)}var f=y(a);return ae(function(){var a=t(f.Fb)?
+ao:zk;var b=Gq(Mj.h(f));var c=Tk.h(f);c=Tb(wd,c);b=Tb(c,b);return Tb(b,a)}(),m(vd(a)))}return null}}}(a,b,c,d),null,null)}}(b,c,d,a)(c)}(),t(d)?new R(null,1,5,T,[Gq(d)],null):null))};function $q(a,b,c,d,e){this.Vb=a;this.ia=b;this.v=c;this.j=d;this.w=e;this.m=2229667594;this.J=139264}g=$q.prototype;g.V=function(a,b){return this.I(null,b,null)};g.I=function(a,b,c){switch(b instanceof L?b.ea:null){case "klass":return this.Vb;case "schema":return this.ia;default:return D.l(this.j,b,c)}};
+g.R=function(a,b,c){return Y(b,function(){return function(a){return Y(b,Qi,""," ","",c,a)}}(this),"#schema.core.Record{",", ","}",c,O.c(new R(null,2,5,T,[new R(null,2,5,T,[ck,this.Vb],null),new R(null,2,5,T,[Mj,this.ia],null)],null),this.j))};g.ba=function(){return new fh(0,this,2,new R(null,2,5,T,[ck,Mj],null),t(this.j)?dd(this.j):Cf())};g.P=function(){return this.v};g.W=function(){return 2+H(this.j)};
+g.U=function(){var a=this,b=this.w;if(null!=b)return b;var c=function(){return function(){return function(a){return-1486476872^Dd(a)}}(b,a)(a)}();return this.w=c};g.K=function(a,b){return null!=b&&this.constructor===b.constructor&&G.c(this.Vb,b.Vb)&&G.c(this.ia,b.ia)&&G.c(this.j,b.j)};g.ga=function(a,b){return He(new ti(null,new r(null,2,[Mj,null,ck,null],null),null),b)?le.c(tc(wg.c(Ef,this),this.v),b):new $q(this.Vb,this.ia,this.v,Bf(le.c(this.j,b)),null)};
+g.O=function(a,b,c){return t(N.c?N.c(ck,b):N.call(null,ck,b))?new $q(c,this.ia,this.v,this.j,null):t(N.c?N.c(Mj,b):N.call(null,Mj,b))?new $q(this.Vb,c,this.v,this.j,null):new $q(this.Vb,this.ia,this.v,K.l(this.j,b,c),null)};g.S=function(){return E(O.c(new R(null,2,5,T,[new R(null,2,5,T,[ck,this.Vb],null),new R(null,2,5,T,[Mj,this.ia],null)],null),this.j))};g.T=function(a,b){return new $q(this.Vb,this.ia,b,this.j,this.w)};g.X=function(a,b){return ze(b)?this.O(null,A.c(b,0),A.c(b,1)):Mb(Tb,this,b)};
+g.xb=function(){var a=td.h(Vi(be([this.Vb])));var b=Gq(this.ia);b=Tb(wd,b);a=Tb(b,a);return Tb(a,Xn)};function ar(a,b,c){if(!xe(b))throw Error(Bq("Expected map, got %s",be([typeof b])));return pe(new $q(a,b,null,null,null),new r(null,1,[Qm,c],null))}function br(a){a=Gi(function(a){return a instanceof Xq},a);var b=J(a,0,null),c=J(a,1,null);return O.c(ig.c(function(){return function(a){return Gq(a.ia)}}(a,b,c),b),E(c)?new R(null,2,5,T,[Dj,xg(Gq,c)],null):null)}
+function cr(a,b,c,d,e){this.Nb=a;this.Db=b;this.v=c;this.j=d;this.w=e;this.m=2229667594;this.J=139264}g=cr.prototype;g.V=function(a,b){return this.I(null,b,null)};g.I=function(a,b,c){switch(b instanceof L?b.ea:null){case "output-schema":return this.Nb;case "input-schemas":return this.Db;default:return D.l(this.j,b,c)}};
+g.R=function(a,b,c){return Y(b,function(){return function(a){return Y(b,Qi,""," ","",c,a)}}(this),"#schema.core.FnSchema{",", ","}",c,O.c(new R(null,2,5,T,[new R(null,2,5,T,[Wk,this.Nb],null),new R(null,2,5,T,[jl,this.Db],null)],null),this.j))};g.ba=function(){return new fh(0,this,2,new R(null,2,5,T,[Wk,jl],null),t(this.j)?dd(this.j):Cf())};g.P=function(){return this.v};g.W=function(){return 2+H(this.j)};
+g.U=function(){var a=this,b=this.w;if(null!=b)return b;var c=function(){return function(){return function(a){return-2054647546^Dd(a)}}(b,a)(a)}();return this.w=c};g.K=function(a,b){return null!=b&&this.constructor===b.constructor&&G.c(this.Nb,b.Nb)&&G.c(this.Db,b.Db)&&G.c(this.j,b.j)};g.ga=function(a,b){return He(new ti(null,new r(null,2,[Wk,null,jl,null],null),null),b)?le.c(tc(wg.c(Ef,this),this.v),b):new cr(this.Nb,this.Db,this.v,Bf(le.c(this.j,b)),null)};
+g.O=function(a,b,c){return t(N.c?N.c(Wk,b):N.call(null,Wk,b))?new cr(c,this.Db,this.v,this.j,null):t(N.c?N.c(jl,b):N.call(null,jl,b))?new cr(this.Nb,c,this.v,this.j,null):new cr(this.Nb,this.Db,this.v,K.l(this.j,b,c),null)};g.S=function(){return E(O.c(new R(null,2,5,T,[new R(null,2,5,T,[Wk,this.Nb],null),new R(null,2,5,T,[jl,this.Db],null)],null),this.j))};g.T=function(a,b){return new cr(this.Nb,this.Db,b,this.j,this.w)};g.X=function(a,b){return ze(b)?this.O(null,A.c(b,0),A.c(b,1)):Mb(Tb,this,b)};
+g.xb=function(){if(1<H(this.Db)){var a=Gq(this.Nb);var b=ig.c(br,this.Db);a=ae(Sn,ae(a,b))}else a=Gq(this.Nb),b=br(y(this.Db)),a=ae(hn,ae(a,b));return a};function dr(a,b){return new cr(a,b,null,null,null)}function er(a){return E(a)?fe(a)instanceof Xq?H(a):Number.MAX_VALUE:0}
+function fr(a,b){if(!E(b))throw Error(Aq("Function must have at least one input schema"));if(!Vf(ze,b))throw Error(Aq("Each arity must be a vector."));if(!t(P(Ie,ig.c(er,b))))throw Error(Aq("Arities must be distinct"));return new cr(a,Qe(er,b),null,null,null)};var gr,hr,ir=Kq(Fe),jr=new r(null,3,[zn,Nq,Aj,Nq,On,Mq],null),kr;
+kr=function(a){if(!E(a)||!(Xf(H(a))||fe(a)instanceof rd))throw Error(Bq("Expected even, nonzero number of args (with optional trailing symbol); got %s",be([H(a)])));return new Rq(Wg(function(){return function d(a){return new kf(null,function(){for(;;){var c=E(a);if(c){if(Ae(c)){var f=Wc(c),h=H(f),k=of(h);a:for(var l=0;;)if(l<h){var p=A.c(f,l),m=J(p,0,null),u=J(p,1,null);p=k;if(!Fe(m))throw Error(Aq(["Conditional predicate ",v.h(m)," must be a function"].join("")));m=new R(null,2,5,T,[G.c(m,sk)?Zf(!0):
+m,u],null);p.add(m);l+=1}else{f=!0;break a}return f?qf(k.Da(),d(Xc(c))):qf(k.Da(),null)}f=y(c);k=J(f,0,null);h=J(f,1,null);f=ae;if(!Fe(k))throw Error(Aq(["Conditional predicate ",v.h(k)," must be a function"].join("")));k=new R(null,2,5,T,[G.c(k,sk)?Zf(!0):k,h],null);return f(k,d(vd(c)))}return null}},null,null)}(yg(2,2,a))}()),Xf(H(a))?null:fe(a),null,null,null)}(be([ze,new R(null,3,5,T,[Yq(Nq,"r"),Yq(Nq,"g"),Yq(Nq,"b")],null),Zf(!0),Nq]));
+var lr=ke([Uq(Ok),kr,Uq(Tn),kr,Uq(Kj),Mq,Uq(Yn),Mq,Uq(Vl),Mq,Uq(dk),Mq,Uq(Nk),Mq]),mr=new r(null,4,[pl,new r(null,2,[zn,Nq,Aj,Nq],null),Oj,lr,yn,Mq,Rj,Mq],null),nr=new R(null,2,5,T,[Yq(Nq,"unicode codepoint"),Yq(lr,"text attributes")],null),or=new R(null,1,5,T,[nr],null),pr=E(ug(function(a){return Sq(a)},lh(null)));if(!wb(pr))throw Error(Bq("extra-key-schema? can not contain required keys: %s",be([Wg(pr)])));
+function qr(a,b,c,d,e,f,h,k,l,p,m,u,w,x,C,F,I,M,S,X,Ga){this.width=a;this.height=b;this.Ba=c;this.qa=d;this.Aa=e;this.cursor=f;this.ra=h;this.sa=k;this.ta=l;this.pa=p;this.ua=m;this.va=u;this.wa=w;this.buffer=x;this.lines=C;this.za=F;this.xa=I;this.ya=M;this.v=S;this.j=X;this.w=Ga;this.m=2229667594;this.J=139264}g=qr.prototype;g.V=function(a,b){return this.I(null,b,null)};
+g.I=function(a,b,c){switch(b instanceof L?b.ea:null){case "width":return this.width;case "height":return this.height;case "top-margin":return this.Ba;case "bottom-margin":return this.qa;case "tabs":return this.Aa;case "cursor":return this.cursor;case "char-attrs":return this.ra;case "charset-fn":return this.sa;case "insert-mode":return this.ta;case "auto-wrap-mode":return this.pa;case "new-line-mode":return this.ua;case "next-print-wraps":return this.va;case "origin-mode":return this.wa;case "buffer":return this.buffer;
+case "lines":return this.lines;case "saved":return this.za;case "other-buffer-lines":return this.xa;case "other-buffer-saved":return this.ya;default:return D.l(this.j,b,c)}};
+g.R=function(a,b,c){return Y(b,function(){return function(a){return Y(b,Qi,""," ","",c,a)}}(this),"#asciinema.vt.screen.Screen{",", ","}",c,O.c(new R(null,18,5,T,[new R(null,2,5,T,[fl,this.width],null),new R(null,2,5,T,[no,this.height],null),new R(null,2,5,T,[Hn,this.Ba],null),new R(null,2,5,T,[Yj,this.qa],null),new R(null,2,5,T,[tk,this.Aa],null),new R(null,2,5,T,[pl,this.cursor],null),new R(null,2,5,T,[Oj,this.ra],null),new R(null,2,5,T,[im,this.sa],null),new R(null,2,5,T,[$l,this.ta],null),new R(null,
+2,5,T,[Rj,this.pa],null),new R(null,2,5,T,[mm,this.ua],null),new R(null,2,5,T,[vk,this.va],null),new R(null,2,5,T,[yn,this.wa],null),new R(null,2,5,T,[io,this.buffer],null),new R(null,2,5,T,[il,this.lines],null),new R(null,2,5,T,[Nm,this.za],null),new R(null,2,5,T,[Wn,this.xa],null),new R(null,2,5,T,[sm,this.ya],null)],null),this.j))};g.ba=function(){return new fh(0,this,18,new R(null,18,5,T,[fl,no,Hn,Yj,tk,pl,Oj,im,$l,Rj,mm,vk,yn,io,il,Nm,Wn,sm],null),t(this.j)?dd(this.j):Cf())};g.P=function(){return this.v};
+g.W=function(){return 18+H(this.j)};g.U=function(){var a=this,b=this.w;if(null!=b)return b;var c=function(){return function(){return function(a){return-1452363486^Dd(a)}}(b,a)(a)}();return this.w=c};
+g.K=function(a,b){return null!=b&&this.constructor===b.constructor&&G.c(this.width,b.width)&&G.c(this.height,b.height)&&G.c(this.Ba,b.Ba)&&G.c(this.qa,b.qa)&&G.c(this.Aa,b.Aa)&&G.c(this.cursor,b.cursor)&&G.c(this.ra,b.ra)&&G.c(this.sa,b.sa)&&G.c(this.ta,b.ta)&&G.c(this.pa,b.pa)&&G.c(this.ua,b.ua)&&G.c(this.va,b.va)&&G.c(this.wa,b.wa)&&G.c(this.buffer,b.buffer)&&G.c(this.lines,b.lines)&&G.c(this.za,b.za)&&G.c(this.xa,b.xa)&&G.c(this.ya,b.ya)&&G.c(this.j,b.j)};
+g.ga=function(a,b){return He(new ti(null,new r(null,18,[Oj,null,Rj,null,Yj,null,tk,null,vk,null,fl,null,il,null,pl,null,$l,null,im,null,mm,null,sm,null,Nm,null,yn,null,Hn,null,Wn,null,io,null,no,null],null),null),b)?le.c(tc(wg.c(Ef,this),this.v),b):new qr(this.width,this.height,this.Ba,this.qa,this.Aa,this.cursor,this.ra,this.sa,this.ta,this.pa,this.ua,this.va,this.wa,this.buffer,this.lines,this.za,this.xa,this.ya,this.v,Bf(le.c(this.j,b)),null)};
+g.O=function(a,b,c){return t(N.c?N.c(fl,b):N.call(null,fl,b))?new qr(c,this.height,this.Ba,this.qa,this.Aa,this.cursor,this.ra,this.sa,this.ta,this.pa,this.ua,this.va,this.wa,this.buffer,this.lines,this.za,this.xa,this.ya,this.v,this.j,null):t(N.c?N.c(no,b):N.call(null,no,b))?new qr(this.width,c,this.Ba,this.qa,this.Aa,this.cursor,this.ra,this.sa,this.ta,this.pa,this.ua,this.va,this.wa,this.buffer,this.lines,this.za,this.xa,this.ya,this.v,this.j,null):t(N.c?N.c(Hn,b):N.call(null,Hn,b))?new qr(this.width,
+this.height,c,this.qa,this.Aa,this.cursor,this.ra,this.sa,this.ta,this.pa,this.ua,this.va,this.wa,this.buffer,this.lines,this.za,this.xa,this.ya,this.v,this.j,null):t(N.c?N.c(Yj,b):N.call(null,Yj,b))?new qr(this.width,this.height,this.Ba,c,this.Aa,this.cursor,this.ra,this.sa,this.ta,this.pa,this.ua,this.va,this.wa,this.buffer,this.lines,this.za,this.xa,this.ya,this.v,this.j,null):t(N.c?N.c(tk,b):N.call(null,tk,b))?new qr(this.width,this.height,this.Ba,this.qa,c,this.cursor,this.ra,this.sa,this.ta,
+this.pa,this.ua,this.va,this.wa,this.buffer,this.lines,this.za,this.xa,this.ya,this.v,this.j,null):t(N.c?N.c(pl,b):N.call(null,pl,b))?new qr(this.width,this.height,this.Ba,this.qa,this.Aa,c,this.ra,this.sa,this.ta,this.pa,this.ua,this.va,this.wa,this.buffer,this.lines,this.za,this.xa,this.ya,this.v,this.j,null):t(N.c?N.c(Oj,b):N.call(null,Oj,b))?new qr(this.width,this.height,this.Ba,this.qa,this.Aa,this.cursor,c,this.sa,this.ta,this.pa,this.ua,this.va,this.wa,this.buffer,this.lines,this.za,this.xa,
+this.ya,this.v,this.j,null):t(N.c?N.c(im,b):N.call(null,im,b))?new qr(this.width,this.height,this.Ba,this.qa,this.Aa,this.cursor,this.ra,c,this.ta,this.pa,this.ua,this.va,this.wa,this.buffer,this.lines,this.za,this.xa,this.ya,this.v,this.j,null):t(N.c?N.c($l,b):N.call(null,$l,b))?new qr(this.width,this.height,this.Ba,this.qa,this.Aa,this.cursor,this.ra,this.sa,c,this.pa,this.ua,this.va,this.wa,this.buffer,this.lines,this.za,this.xa,this.ya,this.v,this.j,null):t(N.c?N.c(Rj,b):N.call(null,Rj,b))?new qr(this.width,
+this.height,this.Ba,this.qa,this.Aa,this.cursor,this.ra,this.sa,this.ta,c,this.ua,this.va,this.wa,this.buffer,this.lines,this.za,this.xa,this.ya,this.v,this.j,null):t(N.c?N.c(mm,b):N.call(null,mm,b))?new qr(this.width,this.height,this.Ba,this.qa,this.Aa,this.cursor,this.ra,this.sa,this.ta,this.pa,c,this.va,this.wa,this.buffer,this.lines,this.za,this.xa,this.ya,this.v,this.j,null):t(N.c?N.c(vk,b):N.call(null,vk,b))?new qr(this.width,this.height,this.Ba,this.qa,this.Aa,this.cursor,this.ra,this.sa,this.ta,
+this.pa,this.ua,c,this.wa,this.buffer,this.lines,this.za,this.xa,this.ya,this.v,this.j,null):t(N.c?N.c(yn,b):N.call(null,yn,b))?new qr(this.width,this.height,this.Ba,this.qa,this.Aa,this.cursor,this.ra,this.sa,this.ta,this.pa,this.ua,this.va,c,this.buffer,this.lines,this.za,this.xa,this.ya,this.v,this.j,null):t(N.c?N.c(io,b):N.call(null,io,b))?new qr(this.width,this.height,this.Ba,this.qa,this.Aa,this.cursor,this.ra,this.sa,this.ta,this.pa,this.ua,this.va,this.wa,c,this.lines,this.za,this.xa,this.ya,
+this.v,this.j,null):t(N.c?N.c(il,b):N.call(null,il,b))?new qr(this.width,this.height,this.Ba,this.qa,this.Aa,this.cursor,this.ra,this.sa,this.ta,this.pa,this.ua,this.va,this.wa,this.buffer,c,this.za,this.xa,this.ya,this.v,this.j,null):t(N.c?N.c(Nm,b):N.call(null,Nm,b))?new qr(this.width,this.height,this.Ba,this.qa,this.Aa,this.cursor,this.ra,this.sa,this.ta,this.pa,this.ua,this.va,this.wa,this.buffer,this.lines,c,this.xa,this.ya,this.v,this.j,null):t(N.c?N.c(Wn,b):N.call(null,Wn,b))?new qr(this.width,
+this.height,this.Ba,this.qa,this.Aa,this.cursor,this.ra,this.sa,this.ta,this.pa,this.ua,this.va,this.wa,this.buffer,this.lines,this.za,c,this.ya,this.v,this.j,null):t(N.c?N.c(sm,b):N.call(null,sm,b))?new qr(this.width,this.height,this.Ba,this.qa,this.Aa,this.cursor,this.ra,this.sa,this.ta,this.pa,this.ua,this.va,this.wa,this.buffer,this.lines,this.za,this.xa,c,this.v,this.j,null):new qr(this.width,this.height,this.Ba,this.qa,this.Aa,this.cursor,this.ra,this.sa,this.ta,this.pa,this.ua,this.va,this.wa,
+this.buffer,this.lines,this.za,this.xa,this.ya,this.v,K.l(this.j,b,c),null)};
+g.S=function(){return E(O.c(new R(null,18,5,T,[new R(null,2,5,T,[fl,this.width],null),new R(null,2,5,T,[no,this.height],null),new R(null,2,5,T,[Hn,this.Ba],null),new R(null,2,5,T,[Yj,this.qa],null),new R(null,2,5,T,[tk,this.Aa],null),new R(null,2,5,T,[pl,this.cursor],null),new R(null,2,5,T,[Oj,this.ra],null),new R(null,2,5,T,[im,this.sa],null),new R(null,2,5,T,[$l,this.ta],null),new R(null,2,5,T,[Rj,this.pa],null),new R(null,2,5,T,[mm,this.ua],null),new R(null,2,5,T,[vk,this.va],null),new R(null,
+2,5,T,[yn,this.wa],null),new R(null,2,5,T,[io,this.buffer],null),new R(null,2,5,T,[il,this.lines],null),new R(null,2,5,T,[Nm,this.za],null),new R(null,2,5,T,[Wn,this.xa],null),new R(null,2,5,T,[sm,this.ya],null)],null),this.j))};g.T=function(a,b){return new qr(this.width,this.height,this.Ba,this.qa,this.Aa,this.cursor,this.ra,this.sa,this.ta,this.pa,this.ua,this.va,this.wa,this.buffer,this.lines,this.za,this.xa,this.ya,b,this.j,this.w)};
+g.X=function(a,b){return ze(b)?this.O(null,A.c(b,0),A.c(b,1)):Mb(Tb,this,b)};function rr(a){return new qr(fl.h(a),no.h(a),Hn.h(a),Yj.h(a),tk.h(a),pl.h(a),Oj.h(a),im.h(a),$l.h(a),Rj.h(a),mm.h(a),vk.h(a),yn.h(a),io.h(a),il.h(a),Nm.h(a),Wn.h(a),sm.h(a),null,Bf(le.A(a,fl,be([no,Hn,Yj,tk,pl,Oj,im,$l,Rj,mm,vk,yn,io,il,Nm,Wn,sm]))),null)}
+Eq(qr,zq(ar(qr,hi.A(be([Pe([Oj,Rj,Yj,tk,vk,fl,il,pl,$l,im,mm,sm,Nm,yn,Hn,Wn,io,no],[lr,Mq,Nq,wi,Mq,Nq,new R(null,1,5,T,[or],null),jr,Mq,ir,Mq,mr,mr,Mq,Nq,new Qq(new R(null,1,5,T,[or],null),null,null,null),Pq,Nq]),null])),function(a){return rr(wg.c(Ef,a))})));var sr=new R(null,2,5,T,[Yq(Nq,pe(en,new r(null,1,[Mj,fn],null))),Yq(lr,pe(Dk,new r(null,1,[Mj,Gn],null)))],null),tr;tr=function(a,b){return new R(null,2,5,T,[a,b],null)};Eq(tr,dr(nr,new R(null,1,5,T,[sr],null)));
+var ur=new R(null,1,5,T,[Yq(Iq,pe(Dk,new r(null,1,[Mj,Ij],null)))],null),vr;vr=function(a){return tr(32,a)};Eq(vr,dr(nr,new R(null,1,5,T,[ur],null)));var wr=new R(null,1,5,T,[Yq(Iq,pe(Am,new r(null,1,[Mj,Ij],null)))],null),xr=new R(null,2,5,T,[Yq(Iq,pe(Am,new r(null,1,[Mj,Ij],null))),Yq(Iq,pe(Dk,new r(null,1,[Mj,Ij],null)))],null);
+gr=function yr(a){switch(arguments.length){case 1:return yr.h(arguments[0]);case 2:return yr.c(arguments[0],arguments[1]);default:throw Error(["Invalid arity: ",v.h(arguments.length)].join(""));}};gr.h=function(a){return gr.c(a,Ef)};gr.c=function(a,b){return Wg(qg(a,vr(b)))};gr.L=2;Eq(gr,fr(or,new R(null,2,5,T,[wr,xr],null)));
+var zr=new R(null,1,5,T,[or],null),Ar=new R(null,2,5,T,[Yq(Iq,pe(Am,new r(null,1,[Mj,Ij],null))),Yq(Iq,pe(lk,new r(null,1,[Mj,Ij],null)))],null),Br=new R(null,3,5,T,[Yq(Iq,pe(Am,new r(null,1,[Mj,Ij],null))),Yq(Iq,pe(lk,new r(null,1,[Mj,Ij],null))),Yq(Iq,pe(Dk,new r(null,1,[Mj,Ij],null)))],null);
+hr=function Cr(a){switch(arguments.length){case 2:return Cr.c(arguments[0],arguments[1]);case 3:return Cr.l(arguments[0],arguments[1],arguments[2]);default:throw Error(["Invalid arity: ",v.h(arguments.length)].join(""));}};hr.c=function(a,b){return hr.l(a,b,Ef)};hr.l=function(a,b,c){a=gr.c(a,c);return Wg(qg(b,a))};hr.L=3;Eq(hr,fr(zr,new R(null,2,5,T,[Ar,Br],null)));var Dr=new R(null,1,5,T,[Yq(Iq,pe(Am,new r(null,1,[Mj,Ij],null)))],null),Er;Er=function(a){return P(zi,Fi(8,a,8))};
+Eq(Er,dr(wi,new R(null,1,5,T,[Dr],null)));
+var Fr=new r(null,3,[zn,0,Aj,0,On,!0],null),Gr=new r(null,4,[pl,new r(null,2,[zn,0,Aj,0],null),Oj,Ef,yn,!1,Rj,!0],null),Hr=Pe([121,110,101,102,106,119,104,116,99,113,117,108,109,118,100,122,111,103,125,107,97,115,112,123,120,126,98,124,96,105,114],[8804,9532,9226,176,9496,9516,9252,9500,9228,9472,9508,9484,9492,9524,9229,8805,9146,177,163,9488,9618,9149,9147,960,9474,8901,9225,8800,9830,9227,9148]),Ir=new R(null,2,5,T,[Yq(Nq,pe(Am,new r(null,1,[Mj,oo],null))),Yq(Nq,pe(lk,new r(null,1,[Mj,oo],null)))],
+null),Jr;Jr=function(a,b){return rr(Pe([Oj,Rj,Yj,tk,vk,fl,il,pl,$l,im,mm,sm,Nm,yn,Hn,Wn,io,no],[Ef,!0,b-1,Er(a),!1,a,hr.c(a,b),Fr,!1,Ve,!1,Gr,Gr,!1,0,null,fk,b]))};Eq(Jr,dr(qr,new R(null,1,5,T,[Ir],null)));function Kr(a){return K.l(a,$l,!0)}function Lr(a){return K.l(a,$l,!1)}function Mr(a){return K.l(a,mm,!0)}function Nr(a){return K.l(a,mm,!1)}function Or(a){return K.l(a,Rj,!0)}function Pr(a){return K.l(a,Rj,!1)}function Qr(a,b,c){return zg(a,new R(null,2,5,T,[Oj,b],null),c)}
+function Rr(a,b){return Cg(a,Oj,le,b)}function Sr(a,b,c){var d=H(a);b=b<d?b:d;return O.c(kg(b,a),qg(b,c))}var Tr=function Tr(a){switch(arguments.length){case 1:return Tr.h(arguments[0]);case 2:return Tr.c(arguments[0],arguments[1]);default:throw Error(["Invalid arity: ",v.h(arguments.length)].join(""));}};Tr.h=function(a){return Tr.c(a,1)};
+Tr.c=function(a,b){var c=null!=a&&(a.m&64||q===a.G)?P(U,a):a,d=D.c(c,fl),e=D.c(c,Hn),f=D.c(c,Yj),h=D.c(c,Oj),k=gr.c(d,h);return Bg(c,il,function(a,c,d,e,f,h,k){return function(c){return Wg(O.A(jg(h,c),Sr(Zg(null,c,h,k+1,null),b,a),be([kg(k+1,c)])))}}(k,a,c,c,d,e,f,h))};Tr.L=2;function Ur(a,b,c){var d=H(a);b=b<d?b:d;return O.c(qg(b,c),jg(d-b,a))}
+var Vr=function Vr(a){switch(arguments.length){case 1:return Vr.h(arguments[0]);case 2:return Vr.c(arguments[0],arguments[1]);default:throw Error(["Invalid arity: ",v.h(arguments.length)].join(""));}};Vr.h=function(a){return Vr.c(a,1)};
+Vr.c=function(a,b){var c=null!=a&&(a.m&64||q===a.G)?P(U,a):a,d=D.c(c,fl),e=D.c(c,Hn),f=D.c(c,Yj),h=D.c(c,Oj),k=gr.c(d,h);return Bg(c,il,function(a,c,d,e,f,h,k){return function(c){return Wg(O.A(jg(h,c),Ur(Zg(null,c,h,k+1,null),b,a),be([kg(k+1,c)])))}}(k,a,c,c,d,e,f,h))};Vr.L=2;function Wr(a){return zg(a,new R(null,2,5,T,[pl,On],null),!0)}function Xr(a){return zg(a,new R(null,2,5,T,[pl,On],null),!1)}function Yr(a,b){return K.l(zg(a,new R(null,2,5,T,[pl,zn],null),b),vk,!1)}
+function Zr(a,b){var c=null!=a&&(a.m&64||q===a.G)?P(U,a):a,d=D.c(c,fl),e=0<b?b:0;--d;return Yr(c,e<d?e:d)}function $r(a,b){var c=null!=a&&(a.m&64||q===a.G)?P(U,a):a,d=D.c(c,pl);d=null!=d&&(d.m&64||q===d.G)?P(U,d):d;d=D.c(d,zn);var e=D.c(c,fl)-1;return K.l(zg(zg(c,new R(null,2,5,T,[pl,zn],null),d<e?d:e),new R(null,2,5,T,[pl,Aj],null),b),vk,!1)}function as(a){var b=null!=a&&(a.m&64||q===a.G)?P(U,a):a;a=D.c(b,yn);b=D.c(b,Hn);return t(a)?b:0}
+function bs(a,b){var c=as(a),d=null!=a&&(a.m&64||q===a.G)?P(U,a):a;var e=D.c(d,yn);var f=D.c(d,Yj);d=D.c(d,no);e=t(e)?f:d-1;f=c+b;c=f>c?f:c;return $r(a,e<c?e:c)}function cs(a){return $r(Yr(a,0),as(a))}function Eg(a,b,c){return bs(Zr(a,b),c)}function ds(a){a=null!=a&&(a.m&64||q===a.G)?P(U,a):a;var b=D.c(a,pl);b=null!=b&&(b.m&64||q===b.G)?P(U,b):b;b=D.c(b,Aj);var c=D.c(a,Yj),d=D.c(a,no)-1;return G.c(b,c)?Tr.h(a):b<d?$r(a,b+1):a}
+function es(a){a=null!=a&&(a.m&64||q===a.G)?P(U,a):a;var b=D.c(a,pl);b=null!=b&&(b.m&64||q===b.G)?P(U,b):b;b=D.c(b,zn);return Zr(a,b-1)}function fs(a,b){var c=null!=a&&(a.m&64||q===a.G)?P(U,a):a,d=D.c(c,pl);d=null!=d&&(d.m&64||q===d.G)?P(U,d):d;var e=D.c(d,Aj),f=D.c(c,Hn);return $r(c,e<f?function(){var a=e-b;return 0>a?0:a}():function(){var a=e-b;return f>a?f:a}())}
+function gs(a,b){var c=null!=a&&(a.m&64||q===a.G)?P(U,a):a,d=D.c(c,pl);d=null!=d&&(d.m&64||q===d.G)?P(U,d):d;var e=D.c(d,Aj),f=D.c(c,Yj),h=D.c(c,no);return $r(c,e>f?function(){var a=h-1,c=e+b;return a<c?a:c}():function(){var a=e+b;return f<a?f:a}())}function hs(a,b){var c=null!=a&&(a.m&64||q===a.G)?P(U,a):a,d=D.c(c,pl);d=null!=d&&(d.m&64||q===d.G)?P(U,d):d;d=D.c(d,zn);return Zr(c,d+b)}
+function is(a,b){var c=null!=a&&(a.m&64||q===a.G)?P(U,a):a,d=D.c(c,pl);d=null!=d&&(d.m&64||q===d.G)?P(U,d):d;d=D.c(d,zn);return Zr(c,d-b)}function js(a){var b=null!=a&&(a.m&64||q===a.G)?P(U,a):a;a=D.c(b,mm);b=ds(b);return t(a)?Yr(b,0):b}function ks(a){return Yr(ds(a),0)}function ls(a){a=null!=a&&(a.m&64||q===a.G)?P(U,a):a;var b=D.c(a,pl);b=null!=b&&(b.m&64||q===b.G)?P(U,b):b;b=D.c(b,Aj);var c=D.c(a,Hn);return G.c(b,c)?Vr.h(a):0<b?$r(a,b-1):a}
+function ms(a){a=null!=a&&(a.m&64||q===a.G)?P(U,a):a;var b=D.c(a,pl),c=null!=b&&(b.m&64||q===b.G)?P(U,b):b;b=D.c(c,zn);c=D.c(c,Aj);var d=D.c(a,Oj),e=D.c(a,yn),f=D.c(a,Rj);return K.l(a,Nm,new r(null,4,[pl,new r(null,2,[zn,b,Aj,c],null),Oj,d,yn,e,Rj,f],null))}function ns(a){a=null!=a&&(a.m&64||q===a.G)?P(U,a):a;var b=D.c(a,Nm),c=null!=b&&(b.m&64||q===b.G)?P(U,b):b;b=D.c(c,pl);var d=D.c(c,Oj),e=D.c(c,yn);c=D.c(c,Rj);return Cg(K.A(a,Oj,d,be([vk,!1,yn,e,Rj,c])),pl,hi,b)}
+function os(a){a=null!=a&&(a.m&64||q===a.G)?P(U,a):a;var b=D.c(a,io),c=D.c(a,fl),d=D.c(a,no),e=D.c(a,Oj);return G.c(b,fk)?K.A(a,io,tl,be([Wn,il.h(a),sm,Nm.h(a),il,hr.l(c,d,e),Nm,sm.h(a)])):a}function dt(a){a=null!=a&&(a.m&64||q===a.G)?P(U,a):a;var b=D.c(a,io);return G.c(b,tl)?K.A(a,io,fk,be([Wn,null,sm,Nm.h(a),il,Wn.h(a),Nm,sm.h(a)])):a}
+function et(a){a=null!=a&&(a.m&64||q===a.G)?P(U,a):a;var b=D.c(a,pl);b=null!=b&&(b.m&64||q===b.G)?P(U,b):b;b=D.c(b,zn);var c=D.c(a,fl);return 0<b&&b<c?Cg(a,tk,ge,b):a}function ft(a){a=null!=a&&(a.m&64||q===a.G)?P(U,a):a;var b=D.c(a,pl);b=null!=b&&(b.m&64||q===b.G)?P(U,b):b;b=D.c(b,zn);return Cg(a,tk,re,b)}function gt(a){return Bg(a,tk,ie)}
+function ht(a,b){var c=null!=a&&(a.m&64||q===a.G)?P(U,a):a,d=D.c(c,pl),e=null!=d&&(d.m&64||q===d.G)?P(U,d):d,f=D.c(e,zn),h=D.c(c,tk),k=D.c(c,fl),l=b-1,p=k-1;d=J(ng(function(a,b,c,d,e,f,h,k){return function(a){return k>=a}}(l,p,a,c,c,d,e,f,h,k),h),l,p);return Zr(c,d)}
+function it(a,b){var c=null!=a&&(a.m&64||q===a.G)?P(U,a):a,d=D.c(c,pl),e=null!=d&&(d.m&64||q===d.G)?P(U,d):d,f=D.c(e,zn),h=D.c(c,tk),k=D.c(c,fl),l=b-1;d=J(cf(Bi(function(a,b,c,d,e,f,h){return function(a){return h>a}}(l,a,c,c,d,e,f,h,k),h)),l,0);return Zr(c,d)}function jt(a){return K.l(a,im,Ve)}function kt(a){return K.l(a,im,Hr)}function lt(a,b,c){return K.l(a,b,c)}function mt(a,b,c){return Wg(O.A(jg(b,a),new R(null,1,5,T,[c],null),be([jg(H(a)-b-1,kg(b,a))])))}
+function nt(a,b){var c=null!=a&&(a.m&64||q===a.G)?P(U,a):a,d=D.c(c,pl),e=null!=d&&(d.m&64||q===d.G)?P(U,d):d;d=D.c(e,zn);e=D.c(e,Aj);var f=D.c(c,fl);D.c(c,no);var h=D.c(c,Oj),k=D.c(c,Rj),l=D.c(c,$l),p=D.c(c,im);p=95<b&&127>b?p.h?p.h(b):p.call(null,b):b;h=tr(p,h);return G.c(f,d+1)?t(k)?K.l(Yr(zg(c,new R(null,3,5,T,[il,e,d],null),h),d+1),vk,!0):zg(c,new R(null,3,5,T,[il,e,d],null),h):Yr(Ag.Z(c,new R(null,2,5,T,[il,e],null),t(l)?mt:lt,d,h),d+1)}
+function ot(a,b){var c=null!=a&&(a.m&64||q===a.G)?P(U,a):a,d=D.c(c,Rj),e=D.c(c,vk);t(t(d)?e:d)&&(c=null!=c&&(c.m&64||q===c.G)?P(U,c):c,d=D.c(c,pl),d=null!=d&&(d.m&64||q===d.G)?P(U,d):d,d=D.c(d,Aj),e=D.c(c,no),c=Yr(c,0),c=G.c(e,d+1)?Tr.h(c):$r(c,d+1));return c=nt(c,b)}function pt(a){a=null!=a&&(a.m&64||q===a.G)?P(U,a):a;var b=D.c(a,fl),c=D.c(a,no);return K.l(a,il,Wg(qg(c,Wg(qg(b,new R(null,2,5,T,[69,Ef],null))))))}
+function qt(a){a=null!=a&&(a.m&64||q===a.G)?P(U,a):a;var b=D.c(a,pl);b=null!=b&&(b.m&64||q===b.G)?P(U,b):b;b=D.c(b,Aj);var c=D.c(a,fl),d=D.c(a,Oj);return zg(a,new R(null,2,5,T,[il,b],null),gr.c(c,d))}function rt(a,b,c){return Wg(O.c(jg(b,a),qg(H(a)-b,vr(c))))}function st(a,b,c){return Wg(O.c(qg(b+1,vr(c)),kg(b+1,a)))}
+function tt(a){a=null!=a&&(a.m&64||q===a.G)?P(U,a):a;var b=D.c(a,pl),c=null!=b&&(b.m&64||q===b.G)?P(U,b):b;b=D.c(c,zn);c=D.c(c,Aj);var d=D.c(a,fl),e=D.c(a,Oj);--d;return Ag.Z(a,new R(null,2,5,T,[il,c],null),rt,b<d?b:d,e)}function ut(a){a=null!=a&&(a.m&64||q===a.G)?P(U,a):a;var b=D.c(a,pl),c=null!=b&&(b.m&64||q===b.G)?P(U,b):b;b=D.c(c,zn);c=D.c(c,Aj);var d=D.c(a,fl),e=D.c(a,Oj);--d;return Ag.Z(a,new R(null,2,5,T,[il,c],null),st,b<d?b:d,e)}
+function vt(a){a=null!=a&&(a.m&64||q===a.G)?P(U,a):a;var b=D.c(a,fl),c=D.c(a,no),d=D.c(a,Oj);return K.l(a,il,hr.l(b,c,d))}function wt(a){var b=null!=a&&(a.m&64||q===a.G)?P(U,a):a,c=D.c(b,pl),d=null!=c&&(c.m&64||q===c.G)?P(U,c):c,e=D.c(d,zn),f=D.c(d,Aj),h=D.c(b,fl),k=D.c(b,no),l=D.c(b,Oj);return Bg(b,il,function(a,b,c,d,e,f,h,k,l,S){return function(a){var b=jg(h,a);a=rt(Vd(a,h),f,S);var c=qg(l-h-1,gr.c(k,S));return Wg(O.A(b,new R(null,1,5,T,[a],null),be([c])))}}(a,b,b,c,d,e,f,h,k,l))}
+function xt(a){var b=null!=a&&(a.m&64||q===a.G)?P(U,a):a,c=D.c(b,pl),d=null!=c&&(c.m&64||q===c.G)?P(U,c):c,e=D.c(d,zn),f=D.c(d,Aj),h=D.c(b,fl),k=D.c(b,no),l=D.c(b,Oj);return Bg(b,il,function(a,b,c,d,e,f,h,k,l,S,X){return function(b){var c=qg(k,gr.c(l,X)),d=st(Vd(b,k),a,X);return Wg(O.A(c,new R(null,1,5,T,[d],null),be([kg(k+1,b)])))}}(function(){var a=h-1;return e<a?e:a}(),a,b,b,c,d,e,f,h,k,l))}
+function yt(a,b){var c=null!=a&&(a.m&64||q===a.G)?P(U,a):a,d=D.c(c,pl),e=null!=d&&(d.m&64||q===d.G)?P(U,d):d,f=D.c(e,zn),h=D.c(e,Aj),k=D.c(c,fl),l=D.c(c,Oj);return Ag.l(c,new R(null,2,5,T,[il,h],null),function(a,b,c,d,e,f,h,k,l,S){return function(b){return Wg(O.A(jg(h,b),qg(a,vr(S)),be([kg(h+a,b)])))}}(function(){var a=k-f;return b<a?b:a}(),a,c,c,d,e,f,h,k,l))}
+function zt(a,b){var c=null!=a&&(a.m&64||q===a.G)?P(U,a):a,d=D.c(c,pl),e=null!=d&&(d.m&64||q===d.G)?P(U,d):d,f=D.c(e,zn),h=D.c(e,Aj),k=D.c(c,fl),l=D.c(c,Oj);return Ag.l(c,new R(null,2,5,T,[il,h],null),function(a,c,d,e,f,h,k,l,M){return function(a){return Wg(jg(l,O.A(jg(h,a),qg(b,new R(null,2,5,T,[32,M],null)),be([kg(h,a)]))))}}(a,c,c,d,e,f,h,k,l))}
+function At(a,b){var c=null!=a&&(a.m&64||q===a.G)?P(U,a):a,d=D.c(c,pl),e=null!=d&&(d.m&64||q===d.G)?P(U,d):d,f=D.c(e,Aj),h=D.c(c,Yj),k=D.c(c,fl),l=D.c(c,no),p=D.c(c,Oj),m=gr.c(k,p);return Bg(c,il,function(a,c,d,e,f,h,k,m){return function(c){return Wg(k<=m?O.A(jg(k,c),Ur(Zg(null,c,k,m+1,null),b,a),be([kg(m+1,c)])):O.c(jg(k,c),Ur(kg(k,c),b,a)))}}(m,a,c,c,d,e,f,h,k,l,p))}
+function Bt(a,b){var c=null!=a&&(a.m&64||q===a.G)?P(U,a):a,d=D.c(c,pl),e=null!=d&&(d.m&64||q===d.G)?P(U,d):d,f=D.c(e,Aj),h=D.c(c,Yj),k=D.c(c,fl),l=D.c(c,no),p=D.c(c,Oj),m=gr.c(k,p);return Bg(c,il,function(a,c,d,e,f,h,k,m){return function(c){return Wg(k<=m?O.A(jg(k,c),Sr(Zg(null,c,k,m+1,null),b,a),be([kg(m+1,c)])):O.c(jg(k,c),Sr(kg(k,c),b,a)))}}(m,a,c,c,d,e,f,h,k,l,p))}
+function Ct(a,b){var c=null!=a&&(a.m&64||q===a.G)?P(U,a):a,d=D.c(c,pl),e=null!=d&&(d.m&64||q===d.G)?P(U,d):d,f=D.c(e,zn),h=D.c(e,Aj),k=D.c(c,fl),l=D.c(c,Oj),p=f>=k?Zr(c,k-1):c,m=Mb(D,p,new R(null,2,5,T,[pl,zn],null));return Ag.l(p,new R(null,2,5,T,[il,h],null),function(a,b,c,d,e,f,h,k,m,l,p,Q){return function(a){return Wg(O.A(jg(b,a),kg(b+c,a),be([qg(c,vr(Q))])))}}(p,m,function(){var a=k-m;return b<a?b:a}(),a,c,c,d,e,f,h,k,l))}
+var Dt=new R(null,1,5,T,[Yq(new R(null,1,5,T,[Nq],null),pe(Ln,new r(null,1,[Mj,new R(null,1,5,T,[fn],null)],null)))],null),Et;Et=function(a){return P(String.fromCodePoint,a)};Eq(Et,dr(Lq,new R(null,1,5,T,[Dt],null)));var Ft=new R(null,1,5,T,[new R(null,2,5,T,[Yq(Lq,"text"),Yq(lr,"text attributes")],null)],null),Gt=new R(null,1,5,T,[Yq(or,pe(Nn,new r(null,1,[Mj,nk],null)))],null),Ht;
+Ht=function(a){a=E(a);var b=y(a),c=z(a);a=he;var d=new R(null,1,5,T,[y(b)],null),e=fe(b);for(b=c;;)if(c=y(b),t(c)){var f=c;c=J(f,0,null);f=J(f,1,null);G.c(f,e)?d=ge.c(d,c):(a=ge.c(a,new R(null,2,5,T,[Et(d),e],null)),d=new R(null,1,5,T,[c],null),e=f);b=vd(b)}else return ge.c(a,new R(null,2,5,T,[Et(d),e],null))};Eq(Ht,dr(Ft,new R(null,1,5,T,[Gt],null)));
+function It(a){a=Wr(a);a=null!=a&&(a.m&64||q===a.G)?P(U,a):a;var b=D.c(a,no);a=K.A(a,Hn,0,be([Yj,b-1]));return K.l(K.l(K.l(Lr(a),yn,!1),Oj,Ef),Nm,Gr)};var Jt=Error();var Kt=E(ug(function(a){return Sq(a)},lh(null)));if(!wb(Kt))throw Error(Bq("extra-key-schema? can not contain required keys: %s",be([Wg(Kt)])));function Lt(a,b,c,d,e,f,h){this.Qb=a;this.Pb=b;this.Ob=c;this.screen=d;this.v=e;this.j=f;this.w=h;this.m=2229667594;this.J=139264}g=Lt.prototype;g.V=function(a,b){return this.I(null,b,null)};
+g.I=function(a,b,c){switch(b instanceof L?b.ea:null){case "parser-state":return this.Qb;case "parser-params":return this.Pb;case "parser-intermediates":return this.Ob;case "screen":return this.screen;default:return D.l(this.j,b,c)}};
+g.R=function(a,b,c){return Y(b,function(){return function(a){return Y(b,Qi,""," ","",c,a)}}(this),"#asciinema.vt.VT{",", ","}",c,O.c(new R(null,4,5,T,[new R(null,2,5,T,[Tl,this.Qb],null),new R(null,2,5,T,[kk,this.Pb],null),new R(null,2,5,T,[rk,this.Ob],null),new R(null,2,5,T,[V,this.screen],null)],null),this.j))};g.ba=function(){return new fh(0,this,4,new R(null,4,5,T,[Tl,kk,rk,V],null),t(this.j)?dd(this.j):Cf())};g.P=function(){return this.v};g.W=function(){return 4+H(this.j)};
+g.U=function(){var a=this,b=this.w;if(null!=b)return b;var c=function(){return function(){return function(a){return-156373259^Dd(a)}}(b,a)(a)}();return this.w=c};g.K=function(a,b){return null!=b&&this.constructor===b.constructor&&G.c(this.Qb,b.Qb)&&G.c(this.Pb,b.Pb)&&G.c(this.Ob,b.Ob)&&G.c(this.screen,b.screen)&&G.c(this.j,b.j)};
+g.ga=function(a,b){return He(new ti(null,new r(null,4,[V,null,kk,null,rk,null,Tl,null],null),null),b)?le.c(tc(wg.c(Ef,this),this.v),b):new Lt(this.Qb,this.Pb,this.Ob,this.screen,this.v,Bf(le.c(this.j,b)),null)};
+g.O=function(a,b,c){return t(N.c?N.c(Tl,b):N.call(null,Tl,b))?new Lt(c,this.Pb,this.Ob,this.screen,this.v,this.j,null):t(N.c?N.c(kk,b):N.call(null,kk,b))?new Lt(this.Qb,c,this.Ob,this.screen,this.v,this.j,null):t(N.c?N.c(rk,b):N.call(null,rk,b))?new Lt(this.Qb,this.Pb,c,this.screen,this.v,this.j,null):t(N.c?N.c(V,b):N.call(null,V,b))?new Lt(this.Qb,this.Pb,this.Ob,c,this.v,this.j,null):new Lt(this.Qb,this.Pb,this.Ob,this.screen,this.v,K.l(this.j,b,c),null)};
+g.S=function(){return E(O.c(new R(null,4,5,T,[new R(null,2,5,T,[Tl,this.Qb],null),new R(null,2,5,T,[kk,this.Pb],null),new R(null,2,5,T,[rk,this.Ob],null),new R(null,2,5,T,[V,this.screen],null)],null),this.j))};g.T=function(a,b){return new Lt(this.Qb,this.Pb,this.Ob,this.screen,b,this.j,this.w)};g.X=function(a,b){return ze(b)?this.O(null,A.c(b,0),A.c(b,1)):Mb(Tb,this,b)};function Mt(a){return new Lt(Tl.h(a),kk.h(a),rk.h(a),V.h(a),null,Bf(le.A(a,Tl,be([kk,rk,V]))),null)}
+Eq(Lt,zq(ar(Lt,hi.A(be([new r(null,4,[Tl,Pq,kk,new R(null,1,5,T,[Oq],null),rk,new R(null,1,5,T,[Oq],null),V,qr],null),null])),function(a){return Mt(wg.c(Ef,a))})));var Nt=new R(null,2,5,T,[Yq(Nq,pe(Am,new r(null,1,[Mj,oo],null))),Yq(Nq,pe(lk,new r(null,1,[Mj,oo],null)))],null),Ot;Ot=function(a,b){return Mt(new r(null,4,[Tl,uk,kk,he,rk,he,V,Jr(a,b)],null))};Eq(Ot,dr(Lt,new R(null,1,5,T,[Nt],null)));
+function Pt(a,b,c){try{if(null===b)try{if(4===c)return Bg(a,V,Kr);throw Jt;}catch(p){if(p instanceof Error){var d=p;if(d===Jt)try{if(20===c)return Bg(a,V,Mr);throw Jt;}catch(m){if(m instanceof Error){var e=m;if(e===Jt)throw Jt;throw e;}throw m;}else throw d;}else throw p;}else throw Jt;}catch(p){if(p instanceof Error)if(d=p,d===Jt)try{if(63===b)try{if(6===c)return Bg(a,V,function(){return function(a){return cs(K.l(a,yn,!0))}}(d));throw Jt;}catch(m){if(m instanceof Error)if(e=m,e===Jt)try{if(7===c)return Bg(a,
+V,Or);throw Jt;}catch(u){if(u instanceof Error)if(b=u,b===Jt)try{if(25===c)return Bg(a,V,Wr);throw Jt;}catch(w){if(w instanceof Error){var f=w;if(f===Jt)try{if(47===c)return Bg(a,V,os);throw Jt;}catch(x){if(x instanceof Error){var h=x;if(h===Jt)try{if(1047===c)return Bg(a,V,os);throw Jt;}catch(C){if(C instanceof Error){var k=C;if(k===Jt)try{if(1048===c)return Bg(a,V,ms);throw Jt;}catch(F){if(F instanceof Error){var l=F;if(l===Jt)try{if(1049===c)return Bg(a,V,function(){return function(a){return os(ms(a))}}(l,
+k,h,f,b,e,d));throw Jt;}catch(I){if(I instanceof Error){c=I;if(c===Jt)throw Jt;throw c;}throw I;}else throw l;}else throw F;}else throw k;}else throw C;}else throw h;}else throw x;}else throw f;}else throw w;}else throw b;else throw u;}else throw e;else throw m;}else throw Jt;}catch(m){if(m instanceof Error){e=m;if(e===Jt)return a;throw e;}throw m;}else throw d;else throw p;}}
+function Qt(a,b,c){try{if(null===b)try{if(4===c)return Bg(a,V,Lr);throw Jt;}catch(p){if(p instanceof Error){var d=p;if(d===Jt)try{if(20===c)return Bg(a,V,Nr);throw Jt;}catch(m){if(m instanceof Error){var e=m;if(e===Jt)throw Jt;throw e;}throw m;}else throw d;}else throw p;}else throw Jt;}catch(p){if(p instanceof Error)if(d=p,d===Jt)try{if(63===b)try{if(6===c)return Bg(a,V,function(){return function(a){return cs(K.l(a,yn,!1))}}(d));throw Jt;}catch(m){if(m instanceof Error)if(e=m,e===Jt)try{if(7===c)return Bg(a,
+V,Pr);throw Jt;}catch(u){if(u instanceof Error)if(b=u,b===Jt)try{if(25===c)return Bg(a,V,Xr);throw Jt;}catch(w){if(w instanceof Error){var f=w;if(f===Jt)try{if(47===c)return Bg(a,V,dt);throw Jt;}catch(x){if(x instanceof Error){var h=x;if(h===Jt)try{if(1047===c)return Bg(a,V,dt);throw Jt;}catch(C){if(C instanceof Error){var k=C;if(k===Jt)try{if(1048===c)return Bg(a,V,ns);throw Jt;}catch(F){if(F instanceof Error){var l=F;if(l===Jt)try{if(1049===c)return Bg(a,V,function(){return function(a){return ns(dt(a))}}(l,
+k,h,f,b,e,d));throw Jt;}catch(I){if(I instanceof Error){c=I;if(c===Jt)throw Jt;throw c;}throw I;}else throw l;}else throw F;}else throw k;}else throw C;}else throw h;}else throw x;}else throw f;}else throw w;}else throw b;else throw u;}else throw e;else throw m;}else throw Jt;}catch(m){if(m instanceof Error){e=m;if(e===Jt)return a;throw e;}throw m;}else throw d;else throw p;}}
+function Rt(a){a=ig.c(function(a){return a-48},a);a=ig.l(Ye,cf(a),rg(function(){return function(a){return 10*a}}(a),1));return Mb(Xe,0,a)}var St=hj(function(a){a:for(var b=he,c=he;;){var d=y(a);if(t(d))G.c(d,59)?(a=vd(a),b=ge.c(b,c),c=he):(a=vd(a),c=ge.c(c,d));else{a=E(c)?ge.c(b,c):b;break a}}return ig.c(Rt,a)});function Tt(a){a=kk.h(a);return St.h?St.h(a):St.call(null,a)}function Ut(a,b,c){a=J(Tt(a),b,0);return 0===a?c:a}function Vt(a){return Bg(a,V,es)}function Wt(a){return Cg(a,V,ht,1)}
+function Xt(a){return Cg(a,V,Yr,0)}function Yt(a){return Bg(a,V,js)}function Zt(a){return Bg(a,V,kt)}function $t(a){return Bg(a,V,jt)}function au(a){return Bg(a,V,ks)}function bu(a){return Bg(a,V,et)}function cu(a){return Bg(a,V,ls)}function du(a){return Ot(fl.h(V.h(a)),no.h(V.h(a)))}function eu(a){var b=Ut(a,0,1);return Cg(a,V,zt,b)}function fu(a){var b=Ut(a,0,1);return Cg(a,V,fs,b)}function gu(a){var b=Ut(a,0,1);return Cg(a,V,gs,b)}function hu(a){var b=Ut(a,0,1);return Cg(a,V,hs,b)}
+function iu(a){var b=Ut(a,0,1);return Cg(a,V,is,b)}function ju(a){var b=Ut(a,0,1);return Bg(a,V,function(a){return function(b){return Yr(gs(b,a),0)}}(b))}function ku(a){var b=Ut(a,0,1);return Bg(a,V,function(a){return function(b){return Yr(fs(b,a),0)}}(b))}function lu(a){var b=Ut(a,0,1)-1;return Cg(a,V,Zr,b)}function mu(a){var b=Ut(a,0,1)-1,c=Ut(a,1,1)-1;return Dg(a,c,b)}function nu(a){var b=Ut(a,0,1);return Cg(a,V,ht,b)}
+function ou(a){var b=Ut(a,0,0);return Bg(a,V,function(){switch(b){case 0:return wt;case 1:return xt;case 2:return vt;default:return Ve}}())}function pu(a){var b=Ut(a,0,0);return Bg(a,V,function(){switch(b){case 0:return tt;case 1:return ut;case 2:return qt;default:return Ve}}())}function qu(a){var b=Ut(a,0,1);return Cg(a,V,Tr,b)}function ru(a){var b=Ut(a,0,1);return Cg(a,V,Vr,b)}function su(a){var b=Ut(a,0,1);return Cg(a,V,At,b)}function tu(a){var b=Ut(a,0,1);return Cg(a,V,Bt,b)}
+function uu(a){var b=Ut(a,0,1);return Cg(a,V,Ct,b)}function vu(a){switch(Ut(a,0,0)){case 0:return Bg(a,V,et);case 2:return Bg(a,V,ft);case 5:return Bg(a,V,gt);default:return a}}function wu(a){var b=Ut(a,0,1);return Cg(a,V,yt,b)}function xu(a){var b=Ut(a,0,1);return Cg(a,V,it,b)}function yu(a){switch(Ut(a,0,0)){case 0:return Bg(a,V,ft);case 3:return Bg(a,V,gt);default:return a}}function zu(a){var b=D.c(rk.h(a),0);return Mb(function(a){return function(b,c){return Pt(b,a,c)}}(b),a,Tt(a))}
+function Au(a){var b=D.c(rk.h(a),0);return Mb(function(a){return function(b,c){return Qt(b,a,c)}}(b),a,Tt(a))}
+function Bu(a,b){for(var c=a,d=b;;)if(E(d)){var e=y(d);switch(e){case 0:c=K.l(c,Oj,Ef);d=vd(d);continue;case 1:c=Qr(c,Kj,!0);d=vd(d);continue;case 3:c=Qr(c,Yn,!0);d=vd(d);continue;case 4:c=Qr(c,Vl,!0);d=vd(d);continue;case 5:c=Qr(c,dk,!0);d=vd(d);continue;case 7:c=Qr(c,Nk,!0);d=vd(d);continue;case 21:c=Rr(c,Kj);d=vd(d);continue;case 22:c=Rr(c,Kj);d=vd(d);continue;case 23:c=Rr(c,Yn);d=vd(d);continue;case 24:c=Rr(c,Vl);d=vd(d);continue;case 25:c=Rr(c,dk);d=vd(d);continue;case 27:c=Rr(c,Nk);d=vd(d);
+continue;case 30:case 31:case 32:case 33:case 34:case 35:case 36:case 37:c=Qr(c,Ok,e-30);d=vd(d);continue;case 38:switch(ee(d)){case 2:var f=jg(3,kg(2,d));e=J(f,0,null);var h=J(f,1,null);f=J(f,2,null);t(f)?(c=Qr(c,Ok,new R(null,3,5,T,[e,h,f],null)),d=kg(5,d)):d=kg(2,d);continue;case 5:e=y(kg(2,d));t(e)?(c=Qr(c,Ok,e),d=kg(3,d)):d=kg(2,d);continue;default:d=vd(d);continue}case 39:c=Rr(c,Ok);d=vd(d);continue;case 40:case 41:case 42:case 43:case 44:case 45:case 46:case 47:c=Qr(c,Tn,e-40);d=vd(d);continue;
+case 48:switch(ee(d)){case 2:f=jg(3,kg(2,d));e=J(f,0,null);h=J(f,1,null);f=J(f,2,null);t(f)?(c=Qr(c,Tn,new R(null,3,5,T,[e,h,f],null)),d=kg(5,d)):d=kg(2,d);continue;case 5:e=y(kg(2,d));t(e)?(c=Qr(c,Tn,e),d=kg(3,d)):d=kg(2,d);continue;default:d=vd(d);continue}case 49:c=Rr(c,Tn);d=vd(d);continue;case 90:case 91:case 92:case 93:case 94:case 95:case 96:case 97:c=Qr(c,Ok,e-82);d=vd(d);continue;case 100:case 101:case 102:case 103:case 104:case 105:case 106:case 107:c=Qr(c,Tn,e-92);d=vd(d);continue;default:d=
+vd(d)}}else return c}function Cu(a){var b=E(Tt(a));return Cg(a,V,Bu,b?b:new R(null,1,5,T,[0],null))}function Du(a){var b=Ut(a,0,1)-1;return Cg(a,V,bs,b)}function Eu(a){return G.c(D.c(rk.h(a),0),33)?Bg(a,V,It):a}function Fu(a){var b=Ut(a,0,1)-1,c=function(){var b=null==a?null:Ut(a,1,null);return null==b?null:b-1}();return Bg(a,V,function(a,b){return function(c){c=null!=c&&(c.m&64||q===c.G)?P(U,c):c;var d=D.c(c,no),e=t(b)?b:d-1;c=-1<a&&a<e&&e<d?K.A(c,Hn,a,be([Yj,e])):c;return cs(c)}}(b,c))}
+function Gu(a,b){var c=function(){switch(b){case 8:return Vt;case 9:return Wt;case 10:return Yt;case 11:return Yt;case 12:return Yt;case 13:return Xt;case 14:return Zt;case 15:return $t;case 132:return Yt;case 133:return au;case 136:return bu;case 141:return cu;default:return null}}();return t(c)?c.h?c.h(a):c.call(null,a):a}
+var Hu=Pe([zj,Pj,Wj,ak,xl,Pl,Rl,Ul,Xl,um,Fm,Fn,In,po],[function(a){return a},function(a,b){var c=D.c(rk.h(a),0);try{if(null===c)try{if(t(function(){return function(){return function(a){return 64<=a&&95>=a}}(c,b)(b)}()))return Gu(a,b+64);throw Jt;}catch(h){if(h instanceof Error){var d=h;if(d===Jt)try{if(55===b)return Bg(a,V,ms);throw Jt;}catch(k){if(k instanceof Error){var e=k;if(e===Jt)try{if(56===b)return Bg(a,V,ns);throw Jt;}catch(l){if(l instanceof Error){var f=l;if(f===Jt)try{if(99===b)return du(a);
+throw Jt;}catch(p){if(p instanceof Error){d=p;if(d===Jt)throw Jt;throw d;}throw p;}else throw f;}else throw l;}else throw e;}else throw k;}else throw d;}else throw h;}else throw Jt;}catch(h){if(h instanceof Error)if(d=h,d===Jt)try{if(35===c)try{if(56===b)return Bg(a,V,pt);throw Jt;}catch(k){if(k instanceof Error){e=k;if(e===Jt)throw Jt;throw e;}throw k;}else throw Jt;}catch(k){if(k instanceof Error)if(e=k,e===Jt)try{if(40===c)try{if(48===b)return Zt(a);throw Jt;}catch(l){if(l instanceof Error){f=
+l;if(f===Jt)return $t(a);throw f;}throw l;}else throw Jt;}catch(l){if(l instanceof Error){f=l;if(f===Jt)return a;throw f;}throw l;}else throw e;else throw k;}else throw d;else throw h;}},function(a){return a},function(a){return a},Gu,function(a,b){return Cg(a,V,ot,b)},function(a,b){var c=function(){switch(b){case 64:return eu;case 65:return fu;case 66:return gu;case 67:return hu;case 68:return iu;case 69:return ju;case 70:return ku;case 71:return lu;case 72:return mu;case 73:return nu;case 74:return ou;
+case 75:return pu;case 76:return su;case 77:return tu;case 80:return uu;case 83:return qu;case 84:return ru;case 87:return vu;case 88:return wu;case 90:return xu;case 96:return lu;case 97:return hu;case 100:return Du;case 101:return fu;case 102:return mu;case 103:return yu;case 104:return zu;case 108:return Au;case 109:return Cu;case 112:return Eu;case 114:return Fu;default:return null}}();return t(c)?c.h?c.h(a):c.call(null,a):a},function(a){return a},function(a,b){return K.l(a,kk,ge.c(kk.h(a),b))},
+function(a){return a},function(a,b){return K.l(a,rk,ge.c(rk.h(a),b))},function(a){return a},function(a){return a},function(a){return K.A(a,rk,he,be([kk,he]))}]);function Iu(a,b){for(var c=a,d=Tl.h(c),e=b;;){var f=y(e);if(t(f)){var h=160<=f?65:f;h=D.c(d.h?d.h(xq):d.call(null,xq),h);d=J(h,0,null);h=J(h,1,null);a:for(;;)if(E(h)){var k=y(h);k=Hu.h?Hu.h(k):Hu.call(null,k);c=k.c?k.c(c,f):k.call(null,c,f);h=z(h)}else break a;e=vd(e)}else return K.l(c,Tl,d)}}
+function Ju(a,b){var c=xg(function(a){return a.codePointAt(0)},b);return Iu(a,c)}
+function Ku(a,b){try{if(ze(b)&&3===H(b)){var c=Vd(b,0),d=Vd(b,1),e=Vd(b,2);return[v.h(a+8),";2;",v.h(c),";",v.h(d),";",v.h(e)].join("")}throw Jt;}catch(k){if(k instanceof Error){var f=k;if(f===Jt)try{if(t(function(){return function(){return function(a){return 8>a}}(f)(b)}()))return""+v.h(a+b);throw Jt;}catch(l){if(l instanceof Error){var h=l;if(h===Jt)try{if(t(function(){return function(){return function(a){return 16>a}}(h,f)(b)}()))return""+v.h(a+52+b);throw Jt;}catch(p){if(p instanceof Error){c=
+p;if(c===Jt)return[v.h(a+8),";5;",v.h(b)].join("");throw c;}throw p;}else throw h;}else throw l;}else throw f;}else throw k;}}ag.c(Ku,30);ag.c(Ku,40);var Lu=function Lu(a){if(null!=a&&null!=a.yd)return a.yd(a);var c=Lu[n(null==a?null:a)];if(null!=c)return c.h?c.h(a):c.call(null,a);c=Lu._;if(null!=c)return c.h?c.h(a):c.call(null,a);throw Cb("Screen.lines",a);},Mu=function Mu(a){if(null!=a&&null!=a.xd)return a.xd(a);var c=Mu[n(null==a?null:a)];if(null!=c)return c.h?c.h(a):c.call(null,a);c=Mu._;if(null!=c)return c.h?c.h(a):c.call(null,a);throw Cb("Screen.cursor",a);};function Nu(a,b){var c=0<a?a:0;return b<c?b:c}function Ou(a){return function(b){return function(){return((new Date).getTime()-b.getTime())/1E3*a}}(new Date)}function Pu(a){return document[a]}
+function Qu(a){return function(b){var c=new hg(null);bd(c,c);return function(c){return function(){function d(d,e){if(B(c)===c){var f=bd(c,e);return b.c?b.c(d,f):b.call(null,d,f)}var h=bd(c,function(){var b=B(c);return a.c?a.c(b,e):a.call(null,b,e)}());return Hd(h)?Id(function(){var a=B(h);return b.c?b.c(d,a):b.call(null,d,a)}()):b.c?b.c(d,h):b.call(null,d,h)}function f(a){return B(c)===c?a:b.h?b.h(a):b.call(null,a)}function h(){return b.B?b.B():b.call(null)}var k=null;k=function(a,b){switch(arguments.length){case 0:return h.call(this);
+case 1:return f.call(this,a);case 2:return d.call(this,a,b)}throw Error("Invalid arity: "+(arguments.length-1));};k.B=h;k.h=f;k.c=d;return k}()}(c)}}
+function Ru(a,b){return function(c){var d=new hg(null);bd(d,d);return function(d){return function(){function e(e,f){for(;;)if(B(d)===d){var h=function(){var a=e,f=bd(d,b);return c.c?c.c(a,f):c.call(null,a,f)}();if(Hd(h))return h;var k=f;e=h;f=k}else{var m=bd(d,function(){var b=B(d),c=f;return a.c?a.c(b,c):a.call(null,b,c)}());return Hd(m)?Id(function(){var a=e,b=B(m);return c.c?c.c(a,b):c.call(null,a,b)}()):c.c?c.c(e,m):c.call(null,e,m)}}function h(a){B(d)===d&&(a=Jd(c.c?c.c(a,b):c.call(null,a,b)));
+return c.h?c.h(a):c.call(null,a)}function k(){return c.B?c.B():c.call(null)}var l=null;l=function(a,b){switch(arguments.length){case 0:return k.call(this);case 1:return h.call(this,a);case 2:return e.call(this,a,b)}throw Error("Invalid arity: "+(arguments.length-1));};l.B=k;l.h=h;l.c=e;return l}()}(d)}};function Su(a,b){return ig.c(function(b){var c=J(b,0,null);b=J(b,1,null);return new R(null,2,5,T,[c,a.h?a.h(b):a.call(null,b)],null)},b)}var Tu=function Tu(a,b){return new kf(null,function(){if(E(a)){if(E(b)){var d=y(a),e=J(d,0,null);J(d,1,null);var f=y(b),h=J(f,0,null);J(f,1,null);return e<h?ae(d,function(){var d=vd(a);return Tu.c?Tu.c(d,b):Tu.call(null,d,b)}()):ae(f,function(){var d=vd(b);return Tu.c?Tu.c(a,d):Tu.call(null,a,d)}())}return a}return null},null,null)};
+function Uu(a,b){var c=J(b,0,null),d=J(b,1,null);return new R(null,2,5,T,[c+a,d],null)}function Vu(a,b){var c=J(b,0,null),d=J(b,1,null);return new R(null,2,5,T,[c/a,d],null)}function Wu(a){return ig.h(function(b){var c=J(b,0,null),d=J(b,1,null);return t(a)?new R(null,2,5,T,[c<a?c:a,d],null):b})}function Xu(a,b){return y(b)<a}function Yu(a,b,c){return Uf($f.l(mg(ag.c(Xu,a)),ig.h(ag.c(Uu,-a)),ig.h(ag.c(Vu,b))),c)}function Zu(a,b){return y(b)<=a}function $u(a,b){return fe(Bi(ag.c(Zu,a),b))}
+function av(a,b){return Ru(function(b,d){J(b,0,null);var c=J(b,1,null),f=J(d,0,null),h=J(d,1,null);return new R(null,2,5,T,[f,a.c?a.c(c,h):a.call(null,c,h)],null)},new R(null,2,5,T,[0,b],null))}function bv(){return Qu(function(a,b){var c=J(a,0,null);J(a,1,null);var d=J(b,0,null),e=J(b,1,null);return new R(null,2,5,T,[c+d,e],null)})}
+function cv(){return function(a){return function(b){return function(){function c(c,d){var e=J(d,0,null),f=J(d,1,null),h=e-B(b);bd(b,e);e=new R(null,2,5,T,[h,f],null);return a.c?a.c(c,e):a.call(null,c,e)}function d(b){return a.h?a.h(b):a.call(null,b)}function e(){return a.B?a.B():a.call(null)}var f=null;f=function(a,b){switch(arguments.length){case 0:return e.call(this);case 1:return d.call(this,a);case 2:return c.call(this,a,b)}throw Error("Invalid arity: "+(arguments.length-1));};f.B=e;f.h=d;f.c=
+c;return f}()}(new hg(0))}};function dv(a,b,c,d){return Uf($f.A(tg(function(a){return G.c(ee(a),"o")}),ig.h(Hi(function(a){return Vd(a,2)})),cv(),be([Wu(d),bv(),av(Ju,Ot(b,c))])),a)};function ev(a){var b=be([gj,!0]);if(null!=a?q===a.lf||(a.Tc?0:Ab(dj,a)):Ab(dj,a))return ej(a,P(ci,b));if(E(b)){var c=null!=b&&(b.m&64||q===b.G)?P(U,b):b,d=D.c(c,gj);return function(a,b,c,d){return function m(e){return De(e)?Ii(ig.c(m,e)):ue(e)?wg.l(ie(e),ig.h(m),e):vb(e)?Qc(Mb(function(){return function(a,b){return uf.c(a,m(b))}}(a,b,c,d),Oc(he),e)):Bb(e)===Object?Qc(Mb(function(a,b,c,d){return function(a,b){var c=d.h?d.h(b):d.call(null,b),f=m(e[b]);return Rc(a,c,f)}}(a,b,c,d),Oc(Ef),Ea(e))):e}}(b,
+c,d,t(d)?hf:v)(a)}return null};function fv(a,b,c,d,e){this.cursor=a;this.lines=b;this.v=c;this.j=d;this.w=e;this.m=2229667594;this.J=139264}g=fv.prototype;g.V=function(a,b){return this.I(null,b,null)};g.I=function(a,b,c){switch(b instanceof L?b.ea:null){case "cursor":return this.cursor;case "lines":return this.lines;default:return D.l(this.j,b,c)}};
+g.R=function(a,b,c){return Y(b,function(){return function(a){return Y(b,Qi,""," ","",c,a)}}(this),"#asciinema.player.asciicast.v0.LegacyScreen{",", ","}",c,O.c(new R(null,2,5,T,[new R(null,2,5,T,[pl,this.cursor],null),new R(null,2,5,T,[il,this.lines],null)],null),this.j))};g.ba=function(){return new fh(0,this,2,new R(null,2,5,T,[pl,il],null),t(this.j)?dd(this.j):Cf())};g.P=function(){return this.v};g.W=function(){return 2+H(this.j)};
+g.U=function(){var a=this,b=this.w;if(null!=b)return b;var c=function(){return function(){return function(a){return 1528554851^Dd(a)}}(b,a)(a)}();return this.w=c};g.K=function(a,b){return null!=b&&this.constructor===b.constructor&&G.c(this.cursor,b.cursor)&&G.c(this.lines,b.lines)&&G.c(this.j,b.j)};g.ga=function(a,b){return He(new ti(null,new r(null,2,[il,null,pl,null],null),null),b)?le.c(tc(wg.c(Ef,this),this.v),b):new fv(this.cursor,this.lines,this.v,Bf(le.c(this.j,b)),null)};
+g.O=function(a,b,c){return t(N.c?N.c(pl,b):N.call(null,pl,b))?new fv(c,this.lines,this.v,this.j,null):t(N.c?N.c(il,b):N.call(null,il,b))?new fv(this.cursor,c,this.v,this.j,null):new fv(this.cursor,this.lines,this.v,K.l(this.j,b,c),null)};g.S=function(){return E(O.c(new R(null,2,5,T,[new R(null,2,5,T,[pl,this.cursor],null),new R(null,2,5,T,[il,this.lines],null)],null),this.j))};g.T=function(a,b){return new fv(this.cursor,this.lines,b,this.j,this.w)};
+g.X=function(a,b){return ze(b)?this.O(null,A.c(b,0),A.c(b,1)):Mb(Tb,this,b)};function gv(a,b){return y(fe(Uf($f.c(Wu(b),bv()),a)))}function hv(a){return wg.c(Ef,ig.c(function(a){var b=J(a,0,null);a=J(a,1,null);var d=T;b=jf(b);return new R(null,2,5,d,[parseInt(b,10),a],null)},a))}function iv(a,b){var c=Bg(b,il,hv);return ii.A(hi,be([a,c]))}
+function jv(a,b){var c=new r(null,2,[il,di(),pl,new r(null,3,[zn,0,Aj,0,On,!0],null)],null);c=new fv(pl.h(c),il.h(c),null,Bf(le.A(c,pl,be([il]))),null);return Uf($f.l(Wu(b),bv(),av(iv,c)),a)}function kv(a,b){var c=il.h(fe(y(a))),d=Te(Xe,ig.c(function(){return function(a){return H(y(a))}}(c),y(mh(c))));c=H(c);return new r(null,5,[Mn,0,fl,d,no,c,wl,gv(a,b),Uk,jv(a,b)],null)}g.yd=function(){return Wg(mh(il.h(this)))};g.xd=function(){return pl.h(this)};function lv(a){return ev(JSON.parse(a))}function mv(a,b,c,d){if(G.c(Mn.h(a),1)){b=t(b)?b:fl.h(a);c=t(c)?c:no.h(a);var e=ko.h(a);a=y(fe(Uf($f.c(Wu(d),bv()),e)));d=Uf($f.l(Wu(d),bv(),av(Ju,Ot(b,c))),e);d=new r(null,5,[Mn,1,fl,b,no,c,wl,a,Uk,d],null)}else d=null;return d}
+function nv(a,b,c,d){var e=y(a);G.c(Mn.h(e),2)?(e=y(a),a=vd(a),b=t(b)?b:fl.h(e),c=t(c)?c:no.h(e),d=t(d)?d:Qj.h(e),e=y(fe(Uf($f.l(cv(),Wu(d),bv()),a))),d=new r(null,5,[Mn,2,fl,b,no,c,wl,e,Uk,dv(a,b,c,d)],null)):d=t(il.h(ee(e)))?kv(a,d):null;return d}function ov(a,b,c,d){try{var e=lv(a);return we(e)?nv(e,b,c,d):xe(e)?mv(e,b,c,d):null}catch(k){try{var f=Fo(ra(a),"\n");var h=ig.c(lv,f);return nv(h,b,c,d)}catch(l){return null}}}
+function pv(a,b,c,d){var e="string"===typeof a?ov:we(a)?nv:xe(a)?mv:null;a=t(e)?e.M?e.M(a,b,c,d):e.call(null,a,b,c,d):null;if(t(a))return a;throw"only asciicast v1 and v2 formats can be opened";}Lt.prototype.yd=function(){return xg(Ht,il.h(V.h(this)))};Lt.prototype.xd=function(){return pl.h(V.h(this))};var qv;a:{var rv=ba.navigator;if(rv){var sv=rv.userAgent;if(sv){qv=sv;break a}}qv=""}function tv(a){return-1!=qv.indexOf(a)};var uv;
+function vv(){var a=ba.MessageChannel;"undefined"===typeof a&&"undefined"!==typeof window&&window.postMessage&&window.addEventListener&&!tv("Presto")&&(a=function(){var a=document.createElement("IFRAME");a.style.display="none";a.src="";document.documentElement.appendChild(a);var b=a.contentWindow;a=b.document;a.open();a.write("");a.close();var c="callImmediate"+Math.random(),d="file:"==b.location.protocol?"*":b.location.protocol+"//"+b.location.host;a=pa(function(a){if(("*"==d||a.origin==d)&&a.data==
+c)this.port1.onmessage()},this);b.addEventListener("message",a,!1);this.port1={};this.port2={postMessage:function(){b.postMessage(c,d)}}});if("undefined"!==typeof a&&!tv("Trident")&&!tv("MSIE")){var b=new a,c={},d=c;b.port1.onmessage=function(){if(void 0!==c.next){c=c.next;var a=c.ed;c.ed=null;a()}};return function(a){d.next={ed:a};d=d.next;b.port2.postMessage(0)}}return"undefined"!==typeof document&&"onreadystatechange"in document.createElement("SCRIPT")?function(a){var b=document.createElement("SCRIPT");
+b.onreadystatechange=function(){b.onreadystatechange=null;b.parentNode.removeChild(b);b=null;a();a=null};document.documentElement.appendChild(b)}:function(a){ba.setTimeout(a,0)}};function wv(){0!=xv&&(yv[ja(this)]=this);this.od=this.od;this.Wd=this.Wd}var xv=0,yv={};wv.prototype.od=!1;wv.prototype.nd=function(){if(this.Wd)for(;this.Wd.length;)this.Wd.shift()()};function zv(){return tv("iPhone")&&!tv("iPod")&&!tv("iPad")};var Av=tv("Opera"),Bv=tv("Trident")||tv("MSIE"),Cv=tv("Edge"),Dv=tv("Gecko")&&!(-1!=qv.toLowerCase().indexOf("webkit")&&!tv("Edge"))&&!(tv("Trident")||tv("MSIE"))&&!tv("Edge"),Ev=-1!=qv.toLowerCase().indexOf("webkit")&&!tv("Edge");Ev&&tv("Mobile");tv("Macintosh");tv("Windows");tv("Linux")||tv("CrOS");var Fv=ba.navigator||null;Fv&&(Fv.appVersion||"").indexOf("X11");tv("Android");zv();tv("iPad");tv("iPod");zv()||tv("iPad")||tv("iPod");function Gv(){var a=ba.document;return a?a.documentMode:void 0}var Hv;
+a:{var Iv="",Jv=function(){var a=qv;if(Dv)return/rv\:([^\);]+)(\)|;)/.exec(a);if(Cv)return/Edge\/([\d\.]+)/.exec(a);if(Bv)return/\b(?:MSIE|rv)[: ]([^\);]+)(\)|;)/.exec(a);if(Ev)return/WebKit\/(\S+)/.exec(a);if(Av)return/(?:Version)[ \/]?(\S+)/.exec(a)}();Jv&&(Iv=Jv?Jv[1]:"");if(Bv){var Kv=Gv();if(null!=Kv&&Kv>parseFloat(Iv)){Hv=String(Kv);break a}}Hv=Iv}var gb={};
+function Lv(a){return fb(a,function(){for(var b=0,c=ra(String(Hv)).split("."),d=ra(String(a)).split("."),e=Math.max(c.length,d.length),f=0;0==b&&f<e;f++){var h=c[f]||"",k=d[f]||"";do{h=/(\d*)(\D*)(.*)/.exec(h)||["","","",""];k=/(\d*)(\D*)(.*)/.exec(k)||["","","",""];if(0==h[0].length&&0==k[0].length)break;b=ta(0==h[1].length?0:parseInt(h[1],10),0==k[1].length?0:parseInt(k[1],10))||ta(0==h[2].length,0==k[2].length)||ta(h[2],k[2]);h=h[3];k=k[3]}while(0==b)}return 0<=b})}var Mv;var Nv=ba.document;
+Mv=Nv&&Bv?Gv()||("CSS1Compat"==Nv.compatMode?parseInt(Hv,10):5):void 0;var Ov;(Ov=!Bv)||(Ov=9<=Number(Mv));var Pv=Ov,Qv=Bv&&!Lv("9");!Ev||Lv("528");Dv&&Lv("1.9b")||Bv&&Lv("8")||Av&&Lv("9.5")||Ev&&Lv("528");Dv&&!Lv("8")||Bv&&Lv("9");var Rv=function(){if(!ba.addEventListener||!Object.defineProperty)return!1;var a=!1,b=Object.defineProperty({},"passive",{get:function(){a=!0}});ba.addEventListener("test",ea,b);ba.removeEventListener("test",ea,b);return a}();function Sv(a,b){this.type=a;this.currentTarget=this.target=b;this.defaultPrevented=this.Kc=!1;this.af=!0}Sv.prototype.stopPropagation=function(){this.Kc=!0};Sv.prototype.preventDefault=function(){this.defaultPrevented=!0;this.af=!1};function Tv(a,b){Sv.call(this,a?a.type:"");this.relatedTarget=this.currentTarget=this.target=null;this.button=this.screenY=this.screenX=this.clientY=this.clientX=this.offsetY=this.offsetX=0;this.key="";this.charCode=this.keyCode=0;this.metaKey=this.shiftKey=this.altKey=this.ctrlKey=!1;this.pd=this.state=null;if(a){var c=this.type=a.type,d=a.changedTouches?a.changedTouches[0]:null;this.target=a.target||a.srcElement;this.currentTarget=b;var e=a.relatedTarget;if(e){if(Dv){a:{try{eb(e.nodeName);var f=
+!0;break a}catch(h){}f=!1}f||(e=null)}}else"mouseover"==c?e=a.fromElement:"mouseout"==c&&(e=a.toElement);this.relatedTarget=e;null===d?(this.offsetX=Ev||void 0!==a.offsetX?a.offsetX:a.layerX,this.offsetY=Ev||void 0!==a.offsetY?a.offsetY:a.layerY,this.clientX=void 0!==a.clientX?a.clientX:a.pageX,this.clientY=void 0!==a.clientY?a.clientY:a.pageY,this.screenX=a.screenX||0,this.screenY=a.screenY||0):(this.clientX=void 0!==d.clientX?d.clientX:d.pageX,this.clientY=void 0!==d.clientY?d.clientY:d.pageY,this.screenX=
+d.screenX||0,this.screenY=d.screenY||0);this.button=a.button;this.keyCode=a.keyCode||0;this.key=a.key||"";this.charCode=a.charCode||("keypress"==c?a.keyCode:0);this.ctrlKey=a.ctrlKey;this.altKey=a.altKey;this.shiftKey=a.shiftKey;this.metaKey=a.metaKey;this.state=a.state;this.pd=a;a.defaultPrevented&&this.preventDefault()}}qa(Tv,Sv);Tv.prototype.stopPropagation=function(){Tv.Zd.stopPropagation.call(this);this.pd.stopPropagation?this.pd.stopPropagation():this.pd.cancelBubble=!0};
+Tv.prototype.preventDefault=function(){Tv.Zd.preventDefault.call(this);var a=this.pd;if(a.preventDefault)a.preventDefault();else if(a.returnValue=!1,Qv)try{if(a.ctrlKey||112<=a.keyCode&&123>=a.keyCode)a.keyCode=-1}catch(b){}};var Uv="closure_listenable_"+(1E6*Math.random()|0),Vv=0;function Wv(a,b,c,d,e){this.listener=a;this.Xd=null;this.src=b;this.type=c;this.capture=!!d;this.Ub=e;this.key=++Vv;this.$c=this.Fd=!1}function Xv(a){a.$c=!0;a.listener=null;a.Xd=null;a.src=null;a.Ub=null};function Yv(a){this.src=a;this.rb={};this.wd=0}Yv.prototype.add=function(a,b,c,d,e){var f=a.toString();a=this.rb[f];a||(a=this.rb[f]=[],this.wd++);var h=Zv(a,b,d,e);-1<h?(b=a[h],c||(b.Fd=!1)):(b=new Wv(b,this.src,f,!!d,e),b.Fd=c,a.push(b));return b};Yv.prototype.remove=function(a,b,c,d){a=a.toString();if(!(a in this.rb))return!1;var e=this.rb[a];b=Zv(e,b,c,d);return-1<b?(Xv(e[b]),Array.prototype.splice.call(e,b,1),0==e.length&&(delete this.rb[a],this.wd--),!0):!1};
+function $v(a,b){var c=b.type;c in a.rb&&ya(a.rb[c],b)&&(Xv(b),0==a.rb[c].length&&(delete a.rb[c],a.wd--))}Yv.prototype.re=function(a,b,c,d){a=this.rb[a.toString()];var e=-1;a&&(e=Zv(a,b,c,d));return-1<e?a[e]:null};function Zv(a,b,c,d){for(var e=0;e<a.length;++e){var f=a[e];if(!f.$c&&f.listener==b&&f.capture==!!c&&f.Ub==d)return e}return-1};var aw="closure_lm_"+(1E6*Math.random()|0),bw={},cw=0;function dw(a,b,c,d,e){if(d&&d.once)ew(a,b,c,d,e);else if("array"==n(b))for(var f=0;f<b.length;f++)dw(a,b[f],c,d,e);else c=fw(c),a&&a[Uv]?a.Ib.add(String(b),c,!1,ia(d)?!!d.capture:!!d,e):gw(a,b,c,!1,d,e)}
+function gw(a,b,c,d,e,f){if(!b)throw Error("Invalid event type");var h=ia(e)?!!e.capture:!!e,k=hw(a);k||(a[aw]=k=new Yv(a));c=k.add(b,c,d,h,f);if(!c.Xd){d=iw();c.Xd=d;d.src=a;d.listener=c;if(a.addEventListener)Rv||(e=h),void 0===e&&(e=!1),a.addEventListener(b.toString(),d,e);else if(a.attachEvent)a.attachEvent(jw(b.toString()),d);else throw Error("addEventListener and attachEvent are unavailable.");cw++}}
+function iw(){var a=kw,b=Pv?function(c){return a.call(b.src,b.listener,c)}:function(c){c=a.call(b.src,b.listener,c);if(!c)return c};return b}function ew(a,b,c,d,e){if("array"==n(b))for(var f=0;f<b.length;f++)ew(a,b[f],c,d,e);else c=fw(c),a&&a[Uv]?a.Ib.add(String(b),c,!0,ia(d)?!!d.capture:!!d,e):gw(a,b,c,!0,d,e)}
+function lw(a,b,c,d,e){if("array"==n(b))for(var f=0;f<b.length;f++)lw(a,b[f],c,d,e);else d=ia(d)?!!d.capture:!!d,c=fw(c),a&&a[Uv]?a.Ib.remove(String(b),c,d,e):a&&(a=hw(a))&&(b=a.re(b,c,d,e))&&mw(b)}function mw(a){if("number"!=typeof a&&a&&!a.$c){var b=a.src;if(b&&b[Uv])$v(b.Ib,a);else{var c=a.type,d=a.Xd;b.removeEventListener?b.removeEventListener(c,d,a.capture):b.detachEvent&&b.detachEvent(jw(c),d);cw--;(c=hw(b))?($v(c,a),0==c.wd&&(c.src=null,b[aw]=null)):Xv(a)}}}
+function jw(a){return a in bw?bw[a]:bw[a]="on"+a}function nw(a,b,c,d){var e=!0;if(a=hw(a))if(b=a.rb[b.toString()])for(b=b.concat(),a=0;a<b.length;a++){var f=b[a];f&&f.capture==c&&!f.$c&&(f=ow(f,d),e=e&&!1!==f)}return e}function ow(a,b){var c=a.listener,d=a.Ub||a.src;a.Fd&&mw(a);return c.call(d,b)}
+function kw(a,b){if(a.$c)return!0;if(!Pv){var c;if(!(c=b))a:{c=["window","event"];for(var d=ba,e;e=c.shift();)if(null!=d[e])d=d[e];else{c=null;break a}c=d}e=c;c=new Tv(e,this);d=!0;if(!(0>e.keyCode||void 0!=e.returnValue)){a:{var f=!1;if(0==e.keyCode)try{e.keyCode=-1;break a}catch(l){f=!0}if(f||void 0==e.returnValue)e.returnValue=!0}e=[];for(f=c.currentTarget;f;f=f.parentNode)e.push(f);f=a.type;for(var h=e.length-1;!c.Kc&&0<=h;h--){c.currentTarget=e[h];var k=nw(e[h],f,!0,c);d=d&&k}for(h=0;!c.Kc&&
+h<e.length;h++)c.currentTarget=e[h],k=nw(e[h],f,!1,c),d=d&&k}return d}return ow(a,new Tv(b,this))}function hw(a){a=a[aw];return a instanceof Yv?a:null}var pw="__closure_events_fn_"+(1E9*Math.random()>>>0);function fw(a){if(ha(a))return a;a[pw]||(a[pw]=function(b){return a.handleEvent(b)});return a[pw]};function qw(){wv.call(this);this.Ib=new Yv(this);this.ff=this;this.ve=null}qa(qw,wv);qw.prototype[Uv]=!0;g=qw.prototype;g.addEventListener=function(a,b,c,d){dw(this,a,b,c,d)};g.removeEventListener=function(a,b,c,d){lw(this,a,b,c,d)};
+g.dispatchEvent=function(a){var b,c=this.ve;if(c)for(b=[];c;c=c.ve)b.push(c);c=this.ff;var d=a.type||a;if(ca(a))a=new Sv(a,c);else if(a instanceof Sv)a.target=a.target||c;else{var e=a;a=new Sv(d,c);Ia(a,e)}e=!0;if(b)for(var f=b.length-1;!a.Kc&&0<=f;f--){var h=a.currentTarget=b[f];e=rw(h,d,!0,a)&&e}a.Kc||(h=a.currentTarget=c,e=rw(h,d,!0,a)&&e,a.Kc||(e=rw(h,d,!1,a)&&e));if(b)for(f=0;!a.Kc&&f<b.length;f++)h=a.currentTarget=b[f],e=rw(h,d,!1,a)&&e;return e};
+g.nd=function(){qw.Zd.nd.call(this);if(this.Ib){var a=this.Ib,b=0,c;for(c in a.rb){for(var d=a.rb[c],e=0;e<d.length;e++)++b,Xv(d[e]);delete a.rb[c];a.wd--}}this.ve=null};function rw(a,b,c,d){b=a.Ib.rb[String(b)];if(!b)return!0;b=b.concat();for(var e=!0,f=0;f<b.length;++f){var h=b[f];if(h&&!h.$c&&h.capture==c){var k=h.listener,l=h.Ub||h.src;h.Fd&&$v(a.Ib,h);e=!1!==k.call(l,d)&&e}}return e&&0!=d.af}g.re=function(a,b,c,d){return this.Ib.re(String(a),b,c,d)};function sw(a,b,c){if(ha(a))c&&(a=pa(a,c));else if(a&&"function"==typeof a.handleEvent)a=pa(a.handleEvent,a);else throw Error("Invalid listener argument");return 2147483647<Number(b)?-1:ba.setTimeout(a,b||0)};function tw(){}tw.prototype.Ke=null;function uw(a){var b;(b=a.Ke)||(b={},vw(a)&&(b[0]=!0,b[1]=!0),b=a.Ke=b);return b};var ww;function xw(){}qa(xw,tw);function yw(a){return(a=vw(a))?new ActiveXObject(a):new XMLHttpRequest}function vw(a){if(!a.Te&&"undefined"==typeof XMLHttpRequest&&"undefined"!=typeof ActiveXObject){for(var b=["MSXML2.XMLHTTP.6.0","MSXML2.XMLHTTP.3.0","MSXML2.XMLHTTP","Microsoft.XMLHTTP"],c=0;c<b.length;c++){var d=b[c];try{return new ActiveXObject(d),a.Te=d}catch(e){}}throw Error("Could not create ActiveXObject. ActiveX might be disabled, or MSXML might not be installed");}return a.Te}ww=new xw;function zw(a){qw.call(this);this.headers=new Ma;this.ce=a||null;this.oc=!1;this.be=this.ca=null;this.ue="";this.Ic=this.se=this.Sd=this.qe=!1;this.Ae=0;this.$d=null;this.$e=Aw;this.Be=this.Lf=this.ef=!1}qa(zw,qw);var Aw="",Bw=/^https?$/i,Cw=["POST","PUT"],Dw=[];function Ew(a,b){var c=new zw;Dw.push(c);b&&c.Ib.add("complete",b,!1,void 0,void 0);c.Ib.add("ready",c.gf,!0,void 0,void 0);c.send(a,void 0,void 0,void 0);return c}g=zw.prototype;
+g.gf=function(){if(!this.od&&(this.od=!0,this.nd(),0!=xv)){var a=ja(this);delete yv[a]}ya(Dw,this)};
+g.send=function(a,b,c,d){if(this.ca)throw Error("[goog.net.XhrIo] Object is active with another request\x3d"+this.ue+"; newUri\x3d"+a);b=b?b.toUpperCase():"GET";this.ue=a;this.qe=!1;this.oc=!0;this.ca=this.ce?yw(this.ce):yw(ww);this.be=this.ce?uw(this.ce):uw(ww);this.ca.onreadystatechange=pa(this.Ye,this);this.Lf&&"onprogress"in this.ca&&(this.ca.onprogress=pa(function(a){this.Xe(a,!0)},this),this.ca.upload&&(this.ca.upload.onprogress=pa(this.Xe,this)));try{this.se=!0,this.ca.open(b,String(a),!0),
+this.se=!1}catch(f){Fw(this);return}a=c||"";var e=this.headers.clone();d&&La(d,function(a,b){e.set(b,a)});d=wa(e.Xc());c=ba.FormData&&a instanceof ba.FormData;!(0<=ua(Cw,b))||d||c||e.set("Content-Type","application/x-www-form-urlencoded;charset\x3dutf-8");e.forEach(function(a,b){this.ca.setRequestHeader(b,a)},this);this.$e&&(this.ca.responseType=this.$e);"withCredentials"in this.ca&&this.ca.withCredentials!==this.ef&&(this.ca.withCredentials=this.ef);try{Gw(this),0<this.Ae&&((this.Be=Hw(this.ca))?
+(this.ca.timeout=this.Ae,this.ca.ontimeout=pa(this.cf,this)):this.$d=sw(this.cf,this.Ae,this)),this.Sd=!0,this.ca.send(a),this.Sd=!1}catch(f){Fw(this)}};function Hw(a){return Bv&&Lv(9)&&"number"==typeof a.timeout&&void 0!==a.ontimeout}function xa(a){return"content-type"==a.toLowerCase()}g.cf=function(){"undefined"!=typeof aa&&this.ca&&(this.dispatchEvent("timeout"),this.abort(8))};function Fw(a){a.oc=!1;a.ca&&(a.Ic=!0,a.ca.abort(),a.Ic=!1);Iw(a);Jw(a)}
+function Iw(a){a.qe||(a.qe=!0,a.dispatchEvent("complete"),a.dispatchEvent("error"))}g.abort=function(){this.ca&&this.oc&&(this.oc=!1,this.Ic=!0,this.ca.abort(),this.Ic=!1,this.dispatchEvent("complete"),this.dispatchEvent("abort"),Jw(this))};g.nd=function(){this.ca&&(this.oc&&(this.oc=!1,this.Ic=!0,this.ca.abort(),this.Ic=!1),Jw(this,!0));zw.Zd.nd.call(this)};g.Ye=function(){this.od||(this.se||this.Sd||this.Ic?Kw(this):this.If())};g.If=function(){Kw(this)};
+function Kw(a){if(a.oc&&"undefined"!=typeof aa&&(!a.be[1]||4!=Lw(a)||2!=Mw(a)))if(a.Sd&&4==Lw(a))sw(a.Ye,0,a);else if(a.dispatchEvent("readystatechange"),4==Lw(a)){a.oc=!1;try{var b=Mw(a);a:switch(b){case 200:case 201:case 202:case 204:case 206:case 304:case 1223:var c=!0;break a;default:c=!1}var d;if(!(d=c)){var e;if(e=0===b){var f=String(a.ue).match(Pa)[1]||null;if(!f&&ba.self&&ba.self.location){var h=ba.self.location.protocol;f=h.substr(0,h.length-1)}e=!Bw.test(f?f.toLowerCase():"")}d=e}d?(a.dispatchEvent("complete"),
+a.dispatchEvent("success")):Iw(a)}finally{Jw(a)}}}g.Xe=function(a,b){this.dispatchEvent(Nw(a,"progress"));this.dispatchEvent(Nw(a,b?"downloadprogress":"uploadprogress"))};function Nw(a,b){return{type:b,lengthComputable:a.lengthComputable,loaded:a.loaded,total:a.total}}function Jw(a,b){if(a.ca){Gw(a);var c=a.ca,d=a.be[0]?ea:null;a.ca=null;a.be=null;b||a.dispatchEvent("ready");try{c.onreadystatechange=d}catch(e){}}}
+function Gw(a){a.ca&&a.Be&&(a.ca.ontimeout=null);"number"==typeof a.$d&&(ba.clearTimeout(a.$d),a.$d=null)}function Lw(a){return a.ca?a.ca.readyState:0}function Mw(a){try{return 2<Lw(a)?a.ca.status:-1}catch(b){return-1}}g.getResponseHeader=function(a){if(this.ca&&4==Lw(this))return a=this.ca.getResponseHeader(a),null===a?void 0:a};g.getAllResponseHeaders=function(){return this.ca&&4==Lw(this)?this.ca.getAllResponseHeaders():""};var Ow,Pw,Qw,Rw=function Rw(a,b){if(null!=a&&null!=a.oe)return a.oe(0,b);var d=Rw[n(null==a?null:a)];if(null!=d)return d.c?d.c(a,b):d.call(null,a,b);d=Rw._;if(null!=d)return d.c?d.c(a,b):d.call(null,a,b);throw Cb("ReadPort.take!",a);},Sw=function Sw(a,b,c){if(null!=a&&null!=a.Od)return a.Od(0,b,c);var e=Sw[n(null==a?null:a)];if(null!=e)return e.l?e.l(a,b,c):e.call(null,a,b,c);e=Sw._;if(null!=e)return e.l?e.l(a,b,c):e.call(null,a,b,c);throw Cb("WritePort.put!",a);},Tw=function Tw(a){if(null!=a&&null!=
+a.ld)return a.ld();var c=Tw[n(null==a?null:a)];if(null!=c)return c.h?c.h(a):c.call(null,a);c=Tw._;if(null!=c)return c.h?c.h(a):c.call(null,a);throw Cb("Channel.close!",a);},Uw=function Uw(a){if(null!=a&&null!=a.vb)return a.vb(a);var c=Uw[n(null==a?null:a)];if(null!=c)return c.h?c.h(a):c.call(null,a);c=Uw._;if(null!=c)return c.h?c.h(a):c.call(null,a);throw Cb("Handler.active?",a);},Vw=function Vw(a){if(null!=a&&null!=a.tb)return a.tb(a);var c=Vw[n(null==a?null:a)];if(null!=c)return c.h?c.h(a):c.call(null,
+a);c=Vw._;if(null!=c)return c.h?c.h(a):c.call(null,a);throw Cb("Handler.commit",a);},Ww=function Ww(a,b){if(null!=a&&null!=a.Md)return a.Md(a,b);var d=Ww[n(null==a?null:a)];if(null!=d)return d.c?d.c(a,b):d.call(null,a,b);d=Ww._;if(null!=d)return d.c?d.c(a,b):d.call(null,a,b);throw Cb("Buffer.add!*",a);},Xw=function Xw(a){switch(arguments.length){case 1:return Xw.h(arguments[0]);case 2:return Xw.c(arguments[0],arguments[1]);default:throw Error(["Invalid arity: ",v.h(arguments.length)].join(""));}};
+Xw.h=function(a){return a};Xw.c=function(a,b){return Ww(a,b)};Xw.L=2;function Yw(a,b,c,d,e){for(var f=0;;)if(f<e)c[d+f]=a[b+f],f+=1;else break}function Zw(a,b,c,d){this.head=a;this.fa=b;this.length=c;this.o=d}Zw.prototype.pop=function(){if(0===this.length)return null;var a=this.o[this.fa];this.o[this.fa]=null;this.fa=(this.fa+1)%this.o.length;--this.length;return a};Zw.prototype.unshift=function(a){this.o[this.head]=a;this.head=(this.head+1)%this.o.length;this.length+=1;return null};function $w(a,b){a.length+1===a.o.length&&a.resize();a.unshift(b)}
+Zw.prototype.resize=function(){var a=Array(2*this.o.length);return this.fa<this.head?(Yw(this.o,this.fa,a,0,this.length),this.fa=0,this.head=this.length,this.o=a):this.fa>this.head?(Yw(this.o,this.fa,a,0,this.o.length-this.fa),Yw(this.o,0,a,this.o.length-this.fa,this.head),this.fa=0,this.head=this.length,this.o=a):this.fa===this.head?(this.head=this.fa=0,this.o=a):null};function ax(a,b){for(var c=a.length,d=0;;)if(d<c){var e=a.pop();(b.h?b.h(e):b.call(null,e))&&a.unshift(e);d+=1}else break}
+function bx(a){return new Zw(0,0,0,Array(a))}function cx(a,b){this.aa=a;this.n=b;this.m=2;this.J=0}g=cx.prototype;g.Nd=function(){return this.aa.length===this.n};g.Sc=function(){return this.aa.pop()};g.Md=function(a,b){$w(this.aa,b);return this};g.ne=function(){return null};g.W=function(){return this.aa.length};function dx(a,b){this.aa=a;this.n=b;this.m=2;this.J=0}g=dx.prototype;g.Nd=function(){return!1};g.Sc=function(){return this.aa.pop()};
+g.Md=function(a,b){this.aa.length!==this.n&&this.aa.unshift(b);return this};g.ne=function(){return null};g.W=function(){return this.aa.length};if("undefined"===typeof ex)var ex={};function fx(a){this.H=a;this.m=2;this.J=0}g=fx.prototype;g.Nd=function(){return!1};g.Sc=function(){return this.H};g.Md=function(a,b){t(ex===this.H)&&(this.H=b);return this};g.ne=function(){return t(ex===this.H)?this.H=null:null};g.W=function(){return t(ex===this.H)?0:1};var gx=bx(32),hx=!1,ix=!1;function jx(){hx=!0;ix=!1;for(var a=0;;){var b=gx.pop();if(null!=b&&(b.B?b.B():b.call(null),1024>a)){a+=1;continue}break}hx=!1;return 0<gx.length?kx.B?kx.B():kx.call(null):null}function kx(){if(ix&&hx)return null;ix=!0;!ha(ba.setImmediate)||ba.Window&&ba.Window.prototype&&!tv("Edge")&&ba.Window.prototype.setImmediate==ba.setImmediate?(uv||(uv=vv()),uv(jx)):ba.setImmediate(jx)}function lx(a){$w(gx,a);kx()}function mx(a,b){setTimeout(a,b)};var nx;
+function ox(a){"undefined"===typeof nx&&(nx=function(a,c){this.H=a;this.Af=c;this.m=425984;this.J=0},nx.prototype.T=function(a,c){return new nx(this.H,c)},nx.prototype.P=function(){return this.Af},nx.prototype.pc=function(){return this.H},nx.Wc=function(){return new R(null,2,5,T,[Km,qo],null)},nx.qc=!0,nx.Tb="cljs.core.async.impl.channels/t_cljs$core$async$impl$channels36582",nx.Ec=function(a,c){return Jc(c,"cljs.core.async.impl.channels/t_cljs$core$async$impl$channels36582")});return new nx(a,Ef)}
+function px(a,b){this.Ub=a;this.H=b}function qx(a){return Uw(a.Ub)}function rx(a,b,c,d,e,f,h){this.bd=a;this.Qd=b;this.jc=c;this.Pd=d;this.aa=e;this.closed=f;this.Ab=h}function sx(a){for(;;){var b=a.jc.pop();if(null!=b){var c=b.Ub,d=b.H;if(c.vb(null)){var e=c.tb(null);lx(function(a){return function(){return a.h?a.h(!0):a.call(null,!0)}}(e,c,d,b,a))}else continue}break}ax(a.jc,Zf(!1));a.ld()}
+rx.prototype.Od=function(a,b,c){var d=this,e=this,f=d.closed;if(f||!c.vb(null))return ox(!f);if(t(function(){var a=d.aa;return t(a)?wb(d.aa.Nd(null)):a}())){c.tb(null);var h=Hd(d.Ab.c?d.Ab.c(d.aa,b):d.Ab.call(null,d.aa,b));c=function(){for(var a=he;;)if(0<d.bd.length&&0<H(d.aa)){var b=d.bd.pop();if(b.vb(null)){var c=b.tb(null),k=d.aa.Sc(null);a=ge.c(a,function(a,b,c){return function(){return b.h?b.h(c):b.call(null,c)}}(a,c,k,b,h,f,e))}}else return a}();h&&sx(e);if(E(c)){c=E(c);a=null;for(var k=0,
+l=0;;)if(l<k){var p=a.$(null,l);lx(p);l+=1}else if(c=E(c))a=c,Ae(a)?(c=Wc(a),l=Xc(a),a=c,k=H(c),c=l):(c=y(a),lx(c),c=z(a),a=null,k=0),l=0;else break}return ox(!0)}a=function(){for(;;){var a=d.bd.pop();if(t(a)){if(t(a.vb(null)))return a}else return null}}();if(t(a))return k=Vw(a),c.tb(null),lx(function(a){return function(){return a.h?a.h(b):a.call(null,b)}}(k,a,f,e)),ox(!0);64<d.Pd?(d.Pd=0,ax(d.jc,qx)):d.Pd+=1;t(c.md(null))&&$w(d.jc,new px(c,b));return null};
+rx.prototype.oe=function(a,b){var c=this;if(b.vb(null)){if(null!=c.aa&&0<H(c.aa)){var d=b.tb(null);if(t(d)){var e=c.aa.Sc(null),f=0<c.jc.length?function(){for(var a=he;;){var b=c.jc.pop(),d=b.Ub;b=b.H;var e=d.vb(null);d=e?d.tb(null):e;a=t(d)?ge.c(a,d):a;b=t(d)?Hd(c.Ab.c?c.Ab.c(c.aa,b):c.Ab.call(null,c.aa,b)):null;if(!(wb(b)&&wb(c.aa.Nd(null))&&0<c.jc.length))return new R(null,2,5,T,[b,a],null)}}():null,h=J(f,0,null),k=J(f,1,null);t(h)&&sx(this);for(var l=E(k),p=null,m=0,u=0;;)if(u<m){var w=p.$(null,
+u);lx(function(a,b,c,d,e){return function(){return e.h?e.h(!0):e.call(null,!0)}}(l,p,m,u,w,e,f,h,k,d,d,this));u+=1}else{var x=E(l);if(x){w=x;if(Ae(w))l=Wc(w),u=Xc(w),p=l,m=H(l),l=u;else{var C=y(w);lx(function(a,b,c,d,e){return function(){return e.h?e.h(!0):e.call(null,!0)}}(l,p,m,u,C,w,x,e,f,h,k,d,d,this));l=z(w);p=null;m=0}u=0}else break}return ox(e)}return null}d=function(){for(;;){var a=c.jc.pop();if(t(a)){if(Uw(a.Ub))return a}else return null}}();if(t(d))return e=Vw(d.Ub),b.tb(null),lx(function(a){return function(){return a.h?
+a.h(!0):a.call(null,!0)}}(e,d,this)),ox(d.H);if(t(c.closed))return t(c.aa)&&(c.Ab.h?c.Ab.h(c.aa):c.Ab.call(null,c.aa)),t(function(){var a=b.vb(null);return t(a)?b.tb(null):a}())?(d=function(){var a=c.aa;return t(a)?0<H(c.aa):a}(),e=t(d)?c.aa.Sc(null):null,ox(e)):null;64<c.Qd?(c.Qd=0,ax(c.bd,Uw)):c.Qd+=1;t(b.md(null))&&$w(c.bd,b)}return null};
+rx.prototype.ld=function(){var a=this;if(!a.closed){a.closed=!0;for(t(function(){var b=a.aa;return t(b)?0===a.jc.length:b}())&&(a.Ab.h?a.Ab.h(a.aa):a.Ab.call(null,a.aa));;){var b=a.bd.pop();if(null!=b){if(b.vb(null)){var c=b.tb(null),d=t(function(){var b=a.aa;return t(b)?0<H(a.aa):b}())?a.aa.Sc(null):null;lx(function(a,b){return function(){return a.h?a.h(b):a.call(null,b)}}(c,d,b,this))}}else break}t(a.aa)&&a.aa.ne(null)}return null};function tx(a){console.log(a);return null}
+function ux(a,b){var c=t(null)?null:tx;c=c.h?c.h(b):c.call(null,b);return null==c?a:Xw.c(a,c)}
+function vx(a,b){return new rx(bx(32),0,bx(32),0,a,!1,function(){return function(a){return function(){function b(b,c){try{return a.c?a.c(b,c):a.call(null,b,c)}catch(l){return ux(b,l)}}function c(b){try{return a.h?a.h(b):a.call(null,b)}catch(k){return ux(b,k)}}var f=null;f=function(a,d){switch(arguments.length){case 1:return c.call(this,a);case 2:return b.call(this,a,d)}throw Error("Invalid arity: "+(arguments.length-1));};f.h=c;f.c=b;return f}()}(t(b)?b.h?b.h(Xw):b.call(null,Xw):Xw)}())};var wx;
+function xx(a){"undefined"===typeof wx&&(wx=function(a,c){this.Cb=a;this.Cf=c;this.m=393216;this.J=0},wx.prototype.T=function(a,c){return new wx(this.Cb,c)},wx.prototype.P=function(){return this.Cf},wx.prototype.vb=function(){return!0},wx.prototype.md=function(){return!0},wx.prototype.tb=function(){return this.Cb},wx.Wc=function(){return new R(null,2,5,T,[to,Um],null)},wx.qc=!0,wx.Tb="cljs.core.async.impl.ioc-helpers/t_cljs$core$async$impl$ioc_helpers42956",wx.Ec=function(a,c){return Jc(c,"cljs.core.async.impl.ioc-helpers/t_cljs$core$async$impl$ioc_helpers42956")});
+return new wx(a,Ef)}function yx(a){try{var b=a[0];return b.h?b.h(a):b.call(null,a)}catch(c){if(c instanceof Object)throw b=c,a[6].ld(),b;throw c;}}function zx(a,b,c){c=c.oe(0,xx(function(c){a[2]=c;a[1]=b;return yx(a)}));return t(c)?(a[2]=B(c),a[1]=b,Z):null}function Ax(a,b,c,d){c=c.Od(0,d,xx(function(c){a[2]=c;a[1]=b;return yx(a)}));return t(c)?(a[2]=B(c),a[1]=b,Z):null}function Bx(a,b){var c=a[6];null!=b&&c.Od(0,b,xx(function(){return function(){return null}}(c)));c.ld();return c}
+function Cx(a){for(;;){var b=a[4],c=ul.h(b),d=Pm.h(b),e=a[5];if(t(function(){var a=e;return t(a)?wb(b):a}()))throw e;if(t(function(){var a=e;return t(a)?(a=c,t(a)?G.c(Ik,d)||e instanceof d:a):a}())){a[1]=c;a[2]=e;a[5]=null;a[4]=K.A(b,ul,null,be([Pm,null]));break}if(t(function(){var a=e;return t(a)?wb(c)&&wb(Lk.h(b)):a}()))a[4]=Xm.h(b);else{if(t(function(){var a=e;return t(a)?(a=wb(c))?Lk.h(b):a:a}())){a[1]=Lk.h(b);a[4]=K.l(b,Lk,null);break}if(t(function(){var a=wb(e);return a?Lk.h(b):a}())){a[1]=
+Lk.h(b);a[4]=K.l(b,Lk,null);break}if(wb(e)&&wb(Lk.h(b))){a[1]=cn.h(b);a[4]=Xm.h(b);break}throw Error("No matching clause");}}};function Dx(a,b,c){this.key=a;this.H=b;this.forward=c;this.m=2155872256;this.J=0}Dx.prototype.S=function(){var a=this.key;return Tb(Tb(wd,this.H),a)};Dx.prototype.R=function(a,b,c){return Y(b,Qi,"["," ","]",c,this)};function Ex(a,b,c){c=Array(c+1);for(var d=0;;)if(d<c.length)c[d]=null,d+=1;else break;return new Dx(a,b,c)}function Fx(a,b,c,d){for(;;){if(0>c)return a;a:for(;;){var e=c<a.forward.length?a.forward[c]:null;if(t(e))if(e.key<b)a=e;else break a;else break a}null!=d&&(d[c]=a);--c}}
+function Gx(a,b){this.header=a;this.level=b;this.m=2155872256;this.J=0}Gx.prototype.put=function(a,b){var c=Array(15),d=Fx(this.header,a,this.level,c).forward[0];if(null!=d&&d.key===a)return d.H=b;a:for(d=0;;)if(.5>Math.random()&&15>d)d+=1;else break a;if(d>this.level){for(var e=this.level+1;;)if(e<=d+1)c[e]=this.header,e+=1;else break;this.level=d}for(d=Ex(a,b,Array(d));;)return 0<=this.level?(c=c[0].forward,d.forward[0]=c[0],c[0]=d):null};
+Gx.prototype.remove=function(a){var b=Array(15),c=Fx(this.header,a,this.level,b);c=0===c.forward.length?null:c.forward[0];if(null!=c&&c.key===a){for(a=0;;)if(a<=this.level){var d=b[a].forward;c===(a<d.length?d[a]:null)&&(d[a]=c.forward[a]);a+=1}else break;for(;;)if(0<this.level&&this.level<this.header.forward.length&&null==this.header.forward[this.level])--this.level;else return null}else return null};
+function Hx(a){for(var b=Ix,c=b.header,d=b.level;;){if(0>d)return c===b.header?null:c;var e;a:for(e=c;;){e=d<e.forward.length?e.forward[d]:null;if(null==e){e=null;break a}if(e.key>=a)break a}null!=e?(--d,c=e):--d}}Gx.prototype.S=function(){return function(a){return function d(c){return new kf(null,function(){return function(){return null==c?null:ae(new R(null,2,5,T,[c.key,c.H],null),d(c.forward[0]))}}(a),null,null)}}(this)(this.header.forward[0])};
+Gx.prototype.R=function(a,b,c){return Y(b,function(){return function(a){return Y(b,Qi,""," ","",c,a)}}(this),"{",", ","}",c,this)};var Ix=new Gx(Ex(null,null,0),0);function Jx(a){var b=(new Date).valueOf()+a,c=Hx(b),d=t(t(c)?c.key<b+10:c)?c.H:null;if(t(d))return d;var e=vx(null,null);Ix.put(b,e);mx(function(a,b,c){return function(){Ix.remove(c);return Tw(a)}}(e,d,b,c),a);return e};function Kx(a){return Lx(a,null)}function Mx(a,b){return Lx(a,b)}function Lx(a,b){var c=G.c(a,0)?null:a;return vx("number"===typeof c?new cx(bx(c),c):c,b)}
+var Nx=function(a){"undefined"===typeof Ow&&(Ow=function(a,c,d){this.Cb=a;this.Je=c;this.Df=d;this.m=393216;this.J=0},Ow.prototype.T=function(a,c){return new Ow(this.Cb,this.Je,c)},Ow.prototype.P=function(){return this.Df},Ow.prototype.vb=function(){return!0},Ow.prototype.md=function(){return this.Je},Ow.prototype.tb=function(){return this.Cb},Ow.Wc=function(){return new R(null,3,5,T,[to,jk,gk],null)},Ow.qc=!0,Ow.Tb="cljs.core.async/t_cljs$core$async43104",Ow.Ec=function(a,c){return Jc(c,"cljs.core.async/t_cljs$core$async43104")});
+return new Ow(a,!0,Ef)}(function(){return null});function Ox(a,b){var c=Sw(a,b,Nx);return t(c)?B(c):!0}function Px(a){for(var b=Array(a),c=0;;)if(c<a)b[c]=0,c+=1;else break;for(c=1;;){if(G.c(c,a))return b;var d=Math.floor(Math.random()*c);b[c]=b[d];b[d]=c;c+=1}}
+function Qx(){var a=dg.h(!0);"undefined"===typeof Pw&&(Pw=function(a,c){this.Hc=a;this.Ef=c;this.m=393216;this.J=0},Pw.prototype.T=function(){return function(a,c){return new Pw(this.Hc,c)}}(a),Pw.prototype.P=function(){return function(){return this.Ef}}(a),Pw.prototype.vb=function(){return function(){return B(this.Hc)}}(a),Pw.prototype.md=function(){return function(){return!0}}(a),Pw.prototype.tb=function(){return function(){fg(this.Hc,null);return!0}}(a),Pw.Wc=function(){return function(){return new R(null,
+2,5,T,[em,ek],null)}}(a),Pw.qc=!0,Pw.Tb="cljs.core.async/t_cljs$core$async43126",Pw.Ec=function(){return function(a,c){return Jc(c,"cljs.core.async/t_cljs$core$async43126")}}(a));return new Pw(a,Ef)}
+function Rx(a,b){"undefined"===typeof Qw&&(Qw=function(a,b,e){this.Hc=a;this.ed=b;this.Ff=e;this.m=393216;this.J=0},Qw.prototype.T=function(a,b){return new Qw(this.Hc,this.ed,b)},Qw.prototype.P=function(){return this.Ff},Qw.prototype.vb=function(){return Uw(this.Hc)},Qw.prototype.md=function(){return!0},Qw.prototype.tb=function(){Vw(this.Hc);return this.ed},Qw.Wc=function(){return new R(null,3,5,T,[em,Mk,hl],null)},Qw.qc=!0,Qw.Tb="cljs.core.async/t_cljs$core$async43129",Qw.Ec=function(a,b){return Jc(b,
+"cljs.core.async/t_cljs$core$async43129")});return new Qw(a,b,Ef)}
+function Sx(a,b,c){var d=Qx(),e=H(b),f=Px(e),h=Im.h(c),k=function(){for(var c=0;;)if(c<e){var k=t(h)?c:f[c],m=Vd(b,k),u=ze(m)?m.h?m.h(0):m.call(null,0):null,w=t(u)?function(){var b=m.h?m.h(1):m.call(null,1);return Sw(u,b,Rx(d,function(b,c,d,e,f){return function(b){b=new R(null,2,5,T,[b,f],null);return a.h?a.h(b):a.call(null,b)}}(c,b,k,m,u,d,e,f,h)))}():Rw(m,Rx(d,function(b,c,d){return function(b){b=new R(null,2,5,T,[b,d],null);return a.h?a.h(b):a.call(null,b)}}(c,k,m,u,d,e,f,h)));if(t(w))return ox(new R(null,
+2,5,T,[B(w),function(){var a=u;return t(a)?a:m}()],null));c+=1}else return null}();return t(k)?k:He(c,Ik)&&(k=function(){var a=Uw(d);return t(a)?Vw(d):a}(),t(k))?ox(new R(null,2,5,T,[Ik.h(c),Ik],null)):null}
+function Tx(a,b){var c=Kx(1);lx(function(c){return function(){var d=function(){return function(a){return function(){function b(b){for(;;){a:try{for(;;){var c=a(b);if(!N(c,Z)){var d=c;break a}}}catch(x){if(x instanceof Object)b[5]=x,Cx(b),d=Z;else throw x;}if(!N(d,Z))return d}}function c(){var a=[null,null,null,null,null,null,null,null];a[0]=d;a[1]=1;return a}var d=null;d=function(a){switch(arguments.length){case 0:return c.call(this);case 1:return b.call(this,a)}throw Error("Invalid arity: "+(arguments.length-
+1));};d.B=c;d.h=b;return d}()}(function(){return function(c){var d=c[1];return 7===d?(c[2]=c[2],c[1]=3,Z):1===d?(c[2]=null,c[1]=2,Z):4===d?(d=c[2],c[7]=d,c[1]=t(null==d)?5:6,Z):13===d?(c[2]=null,c[1]=14,Z):6===d?(d=c[7],Ax(c,11,b,d)):3===d?Bx(c,c[2]):12===d?(c[2]=null,c[1]=2,Z):2===d?zx(c,4,a):11===d?(c[1]=t(c[2])?12:13,Z):9===d?(c[2]=null,c[1]=10,Z):5===d?(c[1]=t(!0)?8:9,Z):14===d||10===d?(c[2]=c[2],c[1]=7,Z):8===d?(d=Tw(b),c[2]=d,c[1]=10,Z):null}}(c),c)}(),f=function(){var a=d.B?d.B():d.call(null);
+a[6]=c;return a}();return yx(f)}}(c))}function Ux(a){for(var b=[],c=arguments.length,d=0;;)if(d<c)b.push(arguments[d]),d+=1;else break;return Vx(arguments[0],arguments[1],arguments[2],3<b.length?new Jb(b.slice(3),0,null):null)}function Vx(a,b,c,d){var e=null!=d&&(d.m&64||q===d.G)?P(U,d):d;a[1]=b;b=Sx(function(){return function(b){a[2]=b;return yx(a)}}(d,e,e),c,e);return t(b)?(a[2]=B(b),Z):null};function Wx(){}var Xx=function Xx(a,b){if(null!=a&&null!=a.qb)return a.qb(a,b);var d=Xx[n(null==a?null:a)];if(null!=d)return d.c?d.c(a,b):d.call(null,a,b);d=Xx._;if(null!=d)return d.c?d.c(a,b):d.call(null,a,b);throw Cb("Update.update-player",a);};function Yx(){}var Zx=function Zx(a,b){if(null!=a&&null!=a.de)return a.de(a,b);var d=Zx[n(null==a?null:a)];if(null!=d)return d.c?d.c(a,b):d.call(null,a,b);d=Zx._;if(null!=d)return d.c?d.c(a,b):d.call(null,a,b);throw Cb("ChannelSource.get-channels",a);};
+function $x(a,b,c){this.v=a;this.j=b;this.w=c;this.m=2229667594;this.J=139264}g=$x.prototype;g.V=function(a,b){return this.I(null,b,null)};g.I=function(a,b,c){return D.l(this.j,b,c)};g.R=function(a,b,c){return Y(b,function(){return function(a){return Y(b,Qi,""," ","",c,a)}}(this),"#asciinema.player.messages.FastForward{",", ","}",c,O.c(he,this.j))};g.ba=function(){return new fh(0,this,0,he,t(this.j)?dd(this.j):Cf())};g.P=function(){return this.v};g.W=function(){return 0+H(this.j)};
+g.U=function(){var a=this,b=this.w;if(null!=b)return b;var c=function(){return function(){return function(a){return 1082393681^Dd(a)}}(b,a)(a)}();return this.w=c};g.K=function(a,b){return null!=b&&this.constructor===b.constructor&&G.c(this.j,b.j)};g.ga=function(a,b){return He(vi,b)?le.c(tc(wg.c(Ef,this),this.v),b):new $x(this.v,Bf(le.c(this.j,b)),null)};g.O=function(a,b,c){return new $x(this.v,K.l(this.j,b,c),null)};g.S=function(){return E(O.c(he,this.j))};
+g.T=function(a,b){return new $x(b,this.j,this.w)};g.X=function(a,b){return ze(b)?this.O(null,A.c(b,0),A.c(b,1)):Mb(Tb,this,b)};function ay(a,b,c){this.v=a;this.j=b;this.w=c;this.m=2229667594;this.J=139264}g=ay.prototype;g.V=function(a,b){return this.I(null,b,null)};g.I=function(a,b,c){return D.l(this.j,b,c)};g.R=function(a,b,c){return Y(b,function(){return function(a){return Y(b,Qi,""," ","",c,a)}}(this),"#asciinema.player.messages.Rewind{",", ","}",c,O.c(he,this.j))};
+g.ba=function(){return new fh(0,this,0,he,t(this.j)?dd(this.j):Cf())};g.P=function(){return this.v};g.W=function(){return 0+H(this.j)};g.U=function(){var a=this,b=this.w;if(null!=b)return b;var c=function(){return function(){return function(a){return-1020675721^Dd(a)}}(b,a)(a)}();return this.w=c};g.K=function(a,b){return null!=b&&this.constructor===b.constructor&&G.c(this.j,b.j)};g.ga=function(a,b){return He(vi,b)?le.c(tc(wg.c(Ef,this),this.v),b):new ay(this.v,Bf(le.c(this.j,b)),null)};
+g.O=function(a,b,c){return new ay(this.v,K.l(this.j,b,c),null)};g.S=function(){return E(O.c(he,this.j))};g.T=function(a,b){return new ay(b,this.j,this.w)};g.X=function(a,b){return ze(b)?this.O(null,A.c(b,0),A.c(b,1)):Mb(Tb,this,b)};function by(a,b,c,d){this.position=a;this.v=b;this.j=c;this.w=d;this.m=2229667594;this.J=139264}g=by.prototype;g.V=function(a,b){return this.I(null,b,null)};
+g.I=function(a,b,c){switch(b instanceof L?b.ea:null){case "position":return this.position;default:return D.l(this.j,b,c)}};g.R=function(a,b,c){return Y(b,function(){return function(a){return Y(b,Qi,""," ","",c,a)}}(this),"#asciinema.player.messages.Seek{",", ","}",c,O.c(new R(null,1,5,T,[new R(null,2,5,T,[nn,this.position],null)],null),this.j))};g.ba=function(){return new fh(0,this,1,new R(null,1,5,T,[nn],null),t(this.j)?dd(this.j):Cf())};g.P=function(){return this.v};g.W=function(){return 1+H(this.j)};
+g.U=function(){var a=this,b=this.w;if(null!=b)return b;var c=function(){return function(){return function(a){return-2136325183^Dd(a)}}(b,a)(a)}();return this.w=c};g.K=function(a,b){return null!=b&&this.constructor===b.constructor&&G.c(this.position,b.position)&&G.c(this.j,b.j)};g.ga=function(a,b){return He(new ti(null,new r(null,1,[nn,null],null),null),b)?le.c(tc(wg.c(Ef,this),this.v),b):new by(this.position,this.v,Bf(le.c(this.j,b)),null)};
+g.O=function(a,b,c){return t(N.c?N.c(nn,b):N.call(null,nn,b))?new by(c,this.v,this.j,null):new by(this.position,this.v,K.l(this.j,b,c),null)};g.S=function(){return E(O.c(new R(null,1,5,T,[new R(null,2,5,T,[nn,this.position],null)],null),this.j))};g.T=function(a,b){return new by(this.position,b,this.j,this.w)};g.X=function(a,b){return ze(b)?this.O(null,A.c(b,0),A.c(b,1)):Mb(Tb,this,b)};function cy(a){return new by(a,null,null,null)}
+function dy(a,b,c){this.v=a;this.j=b;this.w=c;this.m=2229667594;this.J=139264}g=dy.prototype;g.V=function(a,b){return this.I(null,b,null)};g.I=function(a,b,c){return D.l(this.j,b,c)};g.R=function(a,b,c){return Y(b,function(){return function(a){return Y(b,Qi,""," ","",c,a)}}(this),"#asciinema.player.messages.SpeedDown{",", ","}",c,O.c(he,this.j))};g.ba=function(){return new fh(0,this,0,he,t(this.j)?dd(this.j):Cf())};g.P=function(){return this.v};g.W=function(){return 0+H(this.j)};
+g.U=function(){var a=this,b=this.w;if(null!=b)return b;var c=function(){return function(){return function(a){return 1945704126^Dd(a)}}(b,a)(a)}();return this.w=c};g.K=function(a,b){return null!=b&&this.constructor===b.constructor&&G.c(this.j,b.j)};g.ga=function(a,b){return He(vi,b)?le.c(tc(wg.c(Ef,this),this.v),b):new dy(this.v,Bf(le.c(this.j,b)),null)};g.O=function(a,b,c){return new dy(this.v,K.l(this.j,b,c),null)};g.S=function(){return E(O.c(he,this.j))};
+g.T=function(a,b){return new dy(b,this.j,this.w)};g.X=function(a,b){return ze(b)?this.O(null,A.c(b,0),A.c(b,1)):Mb(Tb,this,b)};function ey(a,b,c){this.v=a;this.j=b;this.w=c;this.m=2229667594;this.J=139264}g=ey.prototype;g.V=function(a,b){return this.I(null,b,null)};g.I=function(a,b,c){return D.l(this.j,b,c)};g.R=function(a,b,c){return Y(b,function(){return function(a){return Y(b,Qi,""," ","",c,a)}}(this),"#asciinema.player.messages.SpeedUp{",", ","}",c,O.c(he,this.j))};
+g.ba=function(){return new fh(0,this,0,he,t(this.j)?dd(this.j):Cf())};g.P=function(){return this.v};g.W=function(){return 0+H(this.j)};g.U=function(){var a=this,b=this.w;if(null!=b)return b;var c=function(){return function(){return function(a){return 2001377313^Dd(a)}}(b,a)(a)}();return this.w=c};g.K=function(a,b){return null!=b&&this.constructor===b.constructor&&G.c(this.j,b.j)};g.ga=function(a,b){return He(vi,b)?le.c(tc(wg.c(Ef,this),this.v),b):new ey(this.v,Bf(le.c(this.j,b)),null)};
+g.O=function(a,b,c){return new ey(this.v,K.l(this.j,b,c),null)};g.S=function(){return E(O.c(he,this.j))};g.T=function(a,b){return new ey(b,this.j,this.w)};g.X=function(a,b){return ze(b)?this.O(null,A.c(b,0),A.c(b,1)):Mb(Tb,this,b)};function fy(a,b,c){this.v=a;this.j=b;this.w=c;this.m=2229667594;this.J=139264}g=fy.prototype;g.V=function(a,b){return this.I(null,b,null)};g.I=function(a,b,c){return D.l(this.j,b,c)};
+g.R=function(a,b,c){return Y(b,function(){return function(a){return Y(b,Qi,""," ","",c,a)}}(this),"#asciinema.player.messages.TogglePlay{",", ","}",c,O.c(he,this.j))};g.ba=function(){return new fh(0,this,0,he,t(this.j)?dd(this.j):Cf())};g.P=function(){return this.v};g.W=function(){return 0+H(this.j)};g.U=function(){var a=this,b=this.w;if(null!=b)return b;var c=function(){return function(){return function(a){return 1662385780^Dd(a)}}(b,a)(a)}();return this.w=c};
+g.K=function(a,b){return null!=b&&this.constructor===b.constructor&&G.c(this.j,b.j)};g.ga=function(a,b){return He(vi,b)?le.c(tc(wg.c(Ef,this),this.v),b):new fy(this.v,Bf(le.c(this.j,b)),null)};g.O=function(a,b,c){return new fy(this.v,K.l(this.j,b,c),null)};g.S=function(){return E(O.c(he,this.j))};g.T=function(a,b){return new fy(b,this.j,this.w)};g.X=function(a,b){return ze(b)?this.O(null,A.c(b,0),A.c(b,1)):Mb(Tb,this,b)};
+function gy(a,b,c,d){this.show=a;this.v=b;this.j=c;this.w=d;this.m=2229667594;this.J=139264}g=gy.prototype;g.V=function(a,b){return this.I(null,b,null)};g.I=function(a,b,c){switch(b instanceof L?b.ea:null){case "show":return this.show;default:return D.l(this.j,b,c)}};g.R=function(a,b,c){return Y(b,function(){return function(a){return Y(b,Qi,""," ","",c,a)}}(this),"#asciinema.player.messages.ShowCursor{",", ","}",c,O.c(new R(null,1,5,T,[new R(null,2,5,T,[so,this.show],null)],null),this.j))};
+g.ba=function(){return new fh(0,this,1,new R(null,1,5,T,[so],null),t(this.j)?dd(this.j):Cf())};g.P=function(){return this.v};g.W=function(){return 1+H(this.j)};g.U=function(){var a=this,b=this.w;if(null!=b)return b;var c=function(){return function(){return function(a){return 1380979759^Dd(a)}}(b,a)(a)}();return this.w=c};g.K=function(a,b){return null!=b&&this.constructor===b.constructor&&G.c(this.show,b.show)&&G.c(this.j,b.j)};
+g.ga=function(a,b){return He(new ti(null,new r(null,1,[so,null],null),null),b)?le.c(tc(wg.c(Ef,this),this.v),b):new gy(this.show,this.v,Bf(le.c(this.j,b)),null)};g.O=function(a,b,c){return t(N.c?N.c(so,b):N.call(null,so,b))?new gy(c,this.v,this.j,null):new gy(this.show,this.v,K.l(this.j,b,c),null)};g.S=function(){return E(O.c(new R(null,1,5,T,[new R(null,2,5,T,[so,this.show],null)],null),this.j))};g.T=function(a,b){return new gy(this.show,b,this.j,this.w)};
+g.X=function(a,b){return ze(b)?this.O(null,A.c(b,0),A.c(b,1)):Mb(Tb,this,b)};function hy(a,b,c,d){this.show=a;this.v=b;this.j=c;this.w=d;this.m=2229667594;this.J=139264}g=hy.prototype;g.V=function(a,b){return this.I(null,b,null)};g.I=function(a,b,c){switch(b instanceof L?b.ea:null){case "show":return this.show;default:return D.l(this.j,b,c)}};
+g.R=function(a,b,c){return Y(b,function(){return function(a){return Y(b,Qi,""," ","",c,a)}}(this),"#asciinema.player.messages.ShowHud{",", ","}",c,O.c(new R(null,1,5,T,[new R(null,2,5,T,[so,this.show],null)],null),this.j))};g.ba=function(){return new fh(0,this,1,new R(null,1,5,T,[so],null),t(this.j)?dd(this.j):Cf())};g.P=function(){return this.v};g.W=function(){return 1+H(this.j)};
+g.U=function(){var a=this,b=this.w;if(null!=b)return b;var c=function(){return function(){return function(a){return-1875838466^Dd(a)}}(b,a)(a)}();return this.w=c};g.K=function(a,b){return null!=b&&this.constructor===b.constructor&&G.c(this.show,b.show)&&G.c(this.j,b.j)};g.ga=function(a,b){return He(new ti(null,new r(null,1,[so,null],null),null),b)?le.c(tc(wg.c(Ef,this),this.v),b):new hy(this.show,this.v,Bf(le.c(this.j,b)),null)};
+g.O=function(a,b,c){return t(N.c?N.c(so,b):N.call(null,so,b))?new hy(c,this.v,this.j,null):new hy(this.show,this.v,K.l(this.j,b,c),null)};g.S=function(){return E(O.c(new R(null,1,5,T,[new R(null,2,5,T,[so,this.show],null)],null),this.j))};g.T=function(a,b){return new hy(this.show,b,this.j,this.w)};g.X=function(a,b){return ze(b)?this.O(null,A.c(b,0),A.c(b,1)):Mb(Tb,this,b)};function iy(a){return new hy(a,null,null,null)}
+function jy(a,b,c,d,e,f){this.width=a;this.height=b;this.duration=c;this.v=d;this.j=e;this.w=f;this.m=2229667594;this.J=139264}g=jy.prototype;g.V=function(a,b){return this.I(null,b,null)};g.I=function(a,b,c){switch(b instanceof L?b.ea:null){case "width":return this.width;case "height":return this.height;case "duration":return this.duration;default:return D.l(this.j,b,c)}};
+g.R=function(a,b,c){return Y(b,function(){return function(a){return Y(b,Qi,""," ","",c,a)}}(this),"#asciinema.player.messages.SetMetadata{",", ","}",c,O.c(new R(null,3,5,T,[new R(null,2,5,T,[fl,this.width],null),new R(null,2,5,T,[no,this.height],null),new R(null,2,5,T,[wl,this.duration],null)],null),this.j))};g.ba=function(){return new fh(0,this,3,new R(null,3,5,T,[fl,no,wl],null),t(this.j)?dd(this.j):Cf())};g.P=function(){return this.v};g.W=function(){return 3+H(this.j)};
+g.U=function(){var a=this,b=this.w;if(null!=b)return b;var c=function(){return function(){return function(a){return 2110730596^Dd(a)}}(b,a)(a)}();return this.w=c};g.K=function(a,b){return null!=b&&this.constructor===b.constructor&&G.c(this.width,b.width)&&G.c(this.height,b.height)&&G.c(this.duration,b.duration)&&G.c(this.j,b.j)};
+g.ga=function(a,b){return He(new ti(null,new r(null,3,[fl,null,wl,null,no,null],null),null),b)?le.c(tc(wg.c(Ef,this),this.v),b):new jy(this.width,this.height,this.duration,this.v,Bf(le.c(this.j,b)),null)};
+g.O=function(a,b,c){return t(N.c?N.c(fl,b):N.call(null,fl,b))?new jy(c,this.height,this.duration,this.v,this.j,null):t(N.c?N.c(no,b):N.call(null,no,b))?new jy(this.width,c,this.duration,this.v,this.j,null):t(N.c?N.c(wl,b):N.call(null,wl,b))?new jy(this.width,this.height,c,this.v,this.j,null):new jy(this.width,this.height,this.duration,this.v,K.l(this.j,b,c),null)};
+g.S=function(){return E(O.c(new R(null,3,5,T,[new R(null,2,5,T,[fl,this.width],null),new R(null,2,5,T,[no,this.height],null),new R(null,2,5,T,[wl,this.duration],null)],null),this.j))};g.T=function(a,b){return new jy(this.width,this.height,this.duration,b,this.j,this.w)};g.X=function(a,b){return ze(b)?this.O(null,A.c(b,0),A.c(b,1)):Mb(Tb,this,b)};function ky(a,b,c,d){this.tc=a;this.v=b;this.j=c;this.w=d;this.m=2229667594;this.J=139264}g=ky.prototype;g.V=function(a,b){return this.I(null,b,null)};
+g.I=function(a,b,c){switch(b instanceof L?b.ea:null){case "loading":return this.tc;default:return D.l(this.j,b,c)}};g.R=function(a,b,c){return Y(b,function(){return function(a){return Y(b,Qi,""," ","",c,a)}}(this),"#asciinema.player.messages.SetLoading{",", ","}",c,O.c(new R(null,1,5,T,[new R(null,2,5,T,[Hm,this.tc],null)],null),this.j))};g.ba=function(){return new fh(0,this,1,new R(null,1,5,T,[Hm],null),t(this.j)?dd(this.j):Cf())};g.P=function(){return this.v};g.W=function(){return 1+H(this.j)};
+g.U=function(){var a=this,b=this.w;if(null!=b)return b;var c=function(){return function(){return function(a){return 1609009220^Dd(a)}}(b,a)(a)}();return this.w=c};g.K=function(a,b){return null!=b&&this.constructor===b.constructor&&G.c(this.tc,b.tc)&&G.c(this.j,b.j)};g.ga=function(a,b){return He(new ti(null,new r(null,1,[Hm,null],null),null),b)?le.c(tc(wg.c(Ef,this),this.v),b):new ky(this.tc,this.v,Bf(le.c(this.j,b)),null)};
+g.O=function(a,b,c){return t(N.c?N.c(Hm,b):N.call(null,Hm,b))?new ky(c,this.v,this.j,null):new ky(this.tc,this.v,K.l(this.j,b,c),null)};g.S=function(){return E(O.c(new R(null,1,5,T,[new R(null,2,5,T,[Hm,this.tc],null)],null),this.j))};g.T=function(a,b){return new ky(this.tc,b,this.j,this.w)};g.X=function(a,b){return ze(b)?this.O(null,A.c(b,0),A.c(b,1)):Mb(Tb,this,b)};function ly(a){return new ky(a,null,null,null)}
+function my(a,b,c,d){this.uc=a;this.v=b;this.j=c;this.w=d;this.m=2229667594;this.J=139264}g=my.prototype;g.V=function(a,b){return this.I(null,b,null)};g.I=function(a,b,c){switch(b instanceof L?b.ea:null){case "playing":return this.uc;default:return D.l(this.j,b,c)}};g.R=function(a,b,c){return Y(b,function(){return function(a){return Y(b,Qi,""," ","",c,a)}}(this),"#asciinema.player.messages.SetPlaying{",", ","}",c,O.c(new R(null,1,5,T,[new R(null,2,5,T,[jn,this.uc],null)],null),this.j))};
+g.ba=function(){return new fh(0,this,1,new R(null,1,5,T,[jn],null),t(this.j)?dd(this.j):Cf())};g.P=function(){return this.v};g.W=function(){return 1+H(this.j)};g.U=function(){var a=this,b=this.w;if(null!=b)return b;var c=function(){return function(){return function(a){return-2119286176^Dd(a)}}(b,a)(a)}();return this.w=c};g.K=function(a,b){return null!=b&&this.constructor===b.constructor&&G.c(this.uc,b.uc)&&G.c(this.j,b.j)};
+g.ga=function(a,b){return He(new ti(null,new r(null,1,[jn,null],null),null),b)?le.c(tc(wg.c(Ef,this),this.v),b):new my(this.uc,this.v,Bf(le.c(this.j,b)),null)};g.O=function(a,b,c){return t(N.c?N.c(jn,b):N.call(null,jn,b))?new my(c,this.v,this.j,null):new my(this.uc,this.v,K.l(this.j,b,c),null)};g.S=function(){return E(O.c(new R(null,1,5,T,[new R(null,2,5,T,[jn,this.uc],null)],null),this.j))};g.T=function(a,b){return new my(this.uc,b,this.j,this.w)};
+g.X=function(a,b){return ze(b)?this.O(null,A.c(b,0),A.c(b,1)):Mb(Tb,this,b)};function ny(a){return new my(a,null,null,null)}function oy(a,b,c){this.v=a;this.j=b;this.w=c;this.m=2229667594;this.J=139264}g=oy.prototype;g.V=function(a,b){return this.I(null,b,null)};g.I=function(a,b,c){return D.l(this.j,b,c)};g.R=function(a,b,c){return Y(b,function(){return function(a){return Y(b,Qi,""," ","",c,a)}}(this),"#asciinema.player.messages.TriggerCanPlay{",", ","}",c,O.c(he,this.j))};
+g.ba=function(){return new fh(0,this,0,he,t(this.j)?dd(this.j):Cf())};g.P=function(){return this.v};g.W=function(){return 0+H(this.j)};g.U=function(){var a=this,b=this.w;if(null!=b)return b;var c=function(){return function(){return function(a){return-1080034109^Dd(a)}}(b,a)(a)}();return this.w=c};g.K=function(a,b){return null!=b&&this.constructor===b.constructor&&G.c(this.j,b.j)};g.ga=function(a,b){return He(vi,b)?le.c(tc(wg.c(Ef,this),this.v),b):new oy(this.v,Bf(le.c(this.j,b)),null)};
+g.O=function(a,b,c){return new oy(this.v,K.l(this.j,b,c),null)};g.S=function(){return E(O.c(he,this.j))};g.T=function(a,b){return new oy(b,this.j,this.w)};g.X=function(a,b){return ze(b)?this.O(null,A.c(b,0),A.c(b,1)):Mb(Tb,this,b)};function py(a,b,c,d){this.screen=a;this.v=b;this.j=c;this.w=d;this.m=2229667594;this.J=139264}g=py.prototype;g.V=function(a,b){return this.I(null,b,null)};
+g.I=function(a,b,c){switch(b instanceof L?b.ea:null){case "screen":return this.screen;default:return D.l(this.j,b,c)}};g.R=function(a,b,c){return Y(b,function(){return function(a){return Y(b,Qi,""," ","",c,a)}}(this),"#asciinema.player.messages.UpdateScreen{",", ","}",c,O.c(new R(null,1,5,T,[new R(null,2,5,T,[V,this.screen],null)],null),this.j))};g.ba=function(){return new fh(0,this,1,new R(null,1,5,T,[V],null),t(this.j)?dd(this.j):Cf())};g.P=function(){return this.v};g.W=function(){return 1+H(this.j)};
+g.U=function(){var a=this,b=this.w;if(null!=b)return b;var c=function(){return function(){return function(a){return-1861248332^Dd(a)}}(b,a)(a)}();return this.w=c};g.K=function(a,b){return null!=b&&this.constructor===b.constructor&&G.c(this.screen,b.screen)&&G.c(this.j,b.j)};g.ga=function(a,b){return He(new ti(null,new r(null,1,[V,null],null),null),b)?le.c(tc(wg.c(Ef,this),this.v),b):new py(this.screen,this.v,Bf(le.c(this.j,b)),null)};
+g.O=function(a,b,c){return t(N.c?N.c(V,b):N.call(null,V,b))?new py(c,this.v,this.j,null):new py(this.screen,this.v,K.l(this.j,b,c),null)};g.S=function(){return E(O.c(new R(null,1,5,T,[new R(null,2,5,T,[V,this.screen],null)],null),this.j))};g.T=function(a,b){return new py(this.screen,b,this.j,this.w)};g.X=function(a,b){return ze(b)?this.O(null,A.c(b,0),A.c(b,1)):Mb(Tb,this,b)};function qy(a){return new py(a,null,null,null)}
+function ry(a,b,c,d){this.time=a;this.v=b;this.j=c;this.w=d;this.m=2229667594;this.J=139264}g=ry.prototype;g.V=function(a,b){return this.I(null,b,null)};g.I=function(a,b,c){switch(b instanceof L?b.ea:null){case "time":return this.time;default:return D.l(this.j,b,c)}};g.R=function(a,b,c){return Y(b,function(){return function(a){return Y(b,Qi,""," ","",c,a)}}(this),"#asciinema.player.messages.UpdateTime{",", ","}",c,O.c(new R(null,1,5,T,[new R(null,2,5,T,[Zk,this.time],null)],null),this.j))};
+g.ba=function(){return new fh(0,this,1,new R(null,1,5,T,[Zk],null),t(this.j)?dd(this.j):Cf())};g.P=function(){return this.v};g.W=function(){return 1+H(this.j)};g.U=function(){var a=this,b=this.w;if(null!=b)return b;var c=function(){return function(){return function(a){return 463038319^Dd(a)}}(b,a)(a)}();return this.w=c};g.K=function(a,b){return null!=b&&this.constructor===b.constructor&&G.c(this.time,b.time)&&G.c(this.j,b.j)};
+g.ga=function(a,b){return He(new ti(null,new r(null,1,[Zk,null],null),null),b)?le.c(tc(wg.c(Ef,this),this.v),b):new ry(this.time,this.v,Bf(le.c(this.j,b)),null)};g.O=function(a,b,c){return t(N.c?N.c(Zk,b):N.call(null,Zk,b))?new ry(c,this.v,this.j,null):new ry(this.time,this.v,K.l(this.j,b,c),null)};g.S=function(){return E(O.c(new R(null,1,5,T,[new R(null,2,5,T,[Zk,this.time],null)],null),this.j))};g.T=function(a,b){return new ry(this.time,b,this.j,this.w)};
+g.X=function(a,b){return ze(b)?this.O(null,A.c(b,0),A.c(b,1)):Mb(Tb,this,b)};function sy(a){return new ry(a,null,null,null)};var ty=function ty(a){if(null!=a&&null!=a.Bd)return a.Bd(a);var c=ty[n(null==a?null:a)];if(null!=c)return c.h?c.h(a):c.call(null,a);c=ty._;if(null!=c)return c.h?c.h(a):c.call(null,a);throw Cb("Source.init",a);},uy=function uy(a){if(null!=a&&null!=a.Ad)return a.Ad(a);var c=uy[n(null==a?null:a)];if(null!=c)return c.h?c.h(a):c.call(null,a);c=uy._;if(null!=c)return c.h?c.h(a):c.call(null,a);throw Cb("Source.close",a);},vy=function vy(a){if(null!=a&&null!=a.ac)return a.ac(a);var c=vy[n(null==a?null:a)];
+if(null!=c)return c.h?c.h(a):c.call(null,a);c=vy._;if(null!=c)return c.h?c.h(a):c.call(null,a);throw Cb("Source.start",a);},wy=function wy(a){if(null!=a&&null!=a.wc)return a.wc(a);var c=wy[n(null==a?null:a)];if(null!=c)return c.h?c.h(a):c.call(null,a);c=wy._;if(null!=c)return c.h?c.h(a):c.call(null,a);throw Cb("Source.stop",a);},xy=function xy(a){if(null!=a&&null!=a.Dd)return a.Dd(a);var c=xy[n(null==a?null:a)];if(null!=c)return c.h?c.h(a):c.call(null,a);c=xy._;if(null!=c)return c.h?c.h(a):c.call(null,
+a);throw Cb("Source.toggle",a);},yy=function yy(a,b){if(null!=a&&null!=a.Cd)return a.Cd(a,b);var d=yy[n(null==a?null:a)];if(null!=d)return d.c?d.c(a,b):d.call(null,a,b);d=yy._;if(null!=d)return d.c?d.c(a,b):d.call(null,a,b);throw Cb("Source.seek",a);},zy=function zy(a,b){if(null!=a&&null!=a.zd)return a.zd(a,b);var d=zy[n(null==a?null:a)];if(null!=d)return d.c?d.c(a,b):d.call(null,a,b);d=zy._;if(null!=d)return d.c?d.c(a,b):d.call(null,a,b);throw Cb("Source.change-speed",a);};
+if("undefined"===typeof xj)var xj=function(){var a=dg.h(Ef),b=dg.h(Ef),c=dg.h(Ef),d=dg.h(Ef),e=D.l(Ef,Qn,jj());return new uj(td.c("asciinema.player.source","make-source"),function(){return function(a,b){var c=null!=b&&(b.m&64||q===b.G)?P(U,b):b;c=D.c(c,rl);return t(c)?c:ok}}(a,b,c,d,e),Ik,e,a,b,c,d)}();function Ay(){return ig.c(function(a){return function(b){b*=a;return new R(null,2,5,T,[b,b],null)}}(1/3),Fi(0,Number.MAX_VALUE,1))}
+function By(a){var b=Kx(null),c=Lx(new fx(ex),null),d=Kx(1);lx(function(b,c,d){return function(){var e=function(){return function(a){return function(){function b(b){for(;;){a:try{for(;;){var c=a(b);if(!N(c,Z)){var d=c;break a}}}catch(I){if(I instanceof Object)b[5]=I,Cx(b),d=Z;else throw I;}if(!N(d,Z))return d}}function c(){var a=[null,null,null,null,null,null,null,null];a[0]=d;a[1]=1;return a}var d=null;d=function(a){switch(arguments.length){case 0:return c.call(this);case 1:return b.call(this,a)}throw Error("Invalid arity: "+
+(arguments.length-1));};d.B=c;d.h=b;return d}()}(function(b,c,d){return function(e){var f=e[1];if(1===f)return zx(e,2,c);if(2===f){var h=e[2],k=function(){return function(a,b,c,d,e){return function(a){return Ox(e,a)}}(h,f,b,c,d)}();k=a.h?a.h(k):a.call(null,k);e[7]=h;return Bx(e,k)}return null}}(b,c,d),b,c,d)}(),f=function(){var a=e.B?e.B():e.call(null);a[6]=b;return a}();return yx(f)}}(d,b,c));return function(a,b){return function(c){t(c)&&Tw(a);return b}}(b,c)}
+function Cy(a,b,c,d){return By(function(e){if("string"===typeof a)return Ew(a,function(){return function(a){a=a.target;try{var f=a.ca?a.ca.responseText:""}catch(l){f=""}f=pv(f,b,c,d);return e.h?e.h(f):e.call(null,f)}}(a));var f=pv(a,b,c,d);return e.h?e.h(f):e.call(null,f)})}
+function Dy(a){var b=Kx(null),c=Kx(1);lx(function(b,c){return function(){var d=function(){return function(a){return function(){function b(b){for(;;){a:try{for(;;){var c=a(b);if(!N(c,Z)){var d=c;break a}}}catch(C){if(C instanceof Object)b[5]=C,Cx(b),d=Z;else throw C;}if(!N(d,Z))return d}}function c(){var a=[null,null,null,null,null,null,null,null,null,null,null,null,null,null];a[0]=d;a[1]=1;return a}var d=null;d=function(a){switch(arguments.length){case 0:return c.call(this);case 1:return b.call(this,
+a)}throw Error("Invalid arity: "+(arguments.length-1));};d.B=c;d.h=b;return d}()}(function(b,c){return function(b){var d=b[1];if(7===d)return d=Jx(1E3*b[7]),zx(b,10,d);if(1===d){d=Ou(1);var e=d.B?d.B():d.call(null),f=a;b[8]=d;b[9]=e;b[10]=f;b[2]=null;b[1]=2;return Z}return 4===d?(e=b[11],d=b[9],f=J(e,0,null),e=J(e,1,null),d=f-d,b[12]=e,b[7]=d,b[1]=t(0<d)?7:8,Z):15===d?(b[1]=t(b[2])?16:17,Z):13===d?(b[2]=null,b[1]=14,Z):6===d?(b[2]=b[2],b[1]=3,Z):17===d?(b[2]=null,b[1]=18,Z):3===d?Bx(b,b[2]):12===
+d?(d=b[8],f=b[10],f=vd(f),d=d.B?d.B():d.call(null),b[9]=d,b[10]=f,b[2]=null,b[1]=2,Z):2===d?(f=b[10],d=y(f),b[11]=d,b[1]=t(d)?4:5,Z):11===d?(b[1]=t(b[2])?12:13,Z):9===d?(b[2]=b[2],b[1]=6,Z):5===d?(d=Tw(c),b[2]=d,b[1]=6,Z):14===d?(b[2]=b[2],b[1]=9,Z):16===d?(d=b[9],f=b[10],f=vd(f),b[9]=d,b[10]=f,b[2]=null,b[1]=2,Z):10===d?(e=b[12],b[13]=b[2],Ax(b,11,c,e)):18===d?(b[2]=b[2],b[1]=9,Z):8===d?(e=b[12],Ax(b,15,c,e)):null}}(b,c),b,c)}(),e=function(){var a=d.B?d.B():d.call(null);a[6]=b;return a}();return yx(e)}}(c,
+b));return b}
+function Ey(a,b,c,d,e,f){var h=Kx(1);lx(function(h){return function(){var k=function(){return function(a){return function(){function b(b){for(;;){a:try{for(;;){var c=a(b);if(!N(c,Z)){var d=c;break a}}}catch(M){if(M instanceof Object)b[5]=M,Cx(b),d=Z;else throw M;}if(!N(d,Z))return d}}function c(){var a=[null,null,null,null,null,null,null,null,null,null,null,null,null,null];a[0]=d;a[1]=1;return a}var d=null;d=function(a){switch(arguments.length){case 0:return c.call(this);case 1:return b.call(this,a)}throw Error("Invalid arity: "+
+(arguments.length-1));};d.B=c;d.h=b;return d}()}(function(){return function(h){var k=h[1];if(7===k)return h[2]=h[2],h[1]=3,Z;if(1===k){k=Yu(c,d,b);var l=Dy(k);k=c;var m=Ou(d);h[7]=l;h[8]=m;h[9]=k;h[2]=null;h[1]=2;return Z}if(4===k)return l=h[7],m=h[2],k=J(m,0,null),m=J(m,1,null),l=G.c(l,m),h[11]=k,h[10]=m,h[1]=l?5:6,Z;if(15===k)return l=h[7],m=h[8],k=h[9],l=Tw(l),m=m.B?m.B():m.call(null),h[12]=l,h[2]=k+m,h[1]=17,Z;if(13===k)return h[2]=null,h[1]=14,Z;if(6===k)return k=h[10],k=G.c(f,k),h[1]=k?15:16,
+Z;if(17===k)return h[2]=h[2],h[1]=7,Z;if(3===k)return Bx(h,h[2]);if(12===k)return k=Yu(0,d,b),k=Dy(k),l=Ou(d),h[7]=k,h[8]=l,h[9]=0,h[2]=null,h[1]=2,Z;if(2===k)return l=h[7],Ux(h,4,new R(null,2,5,T,[l,f],null));if(11===k)return l=h[7],m=h[8],k=h[9],h[13]=h[2],h[7]=l,h[8]=m,h[9]=k,h[2]=null,h[1]=2,Z;if(9===k)return h[1]=t(e)?12:13,Z;if(5===k)return k=h[11],h[1]=t(k)?8:9,Z;if(14===k)return h[2]=h[2],h[1]=10,Z;if(16===k)throw k=h[10],h=["No matching clause: ",v.h(k)].join(""),Error(h);return 10===k?(h[2]=
+h[2],h[1]=7,Z):8===k?(k=h[11],Ax(h,11,a,k)):null}}(h),h)}(),p=function(){var a=k.B?k.B():k.call(null);a[6]=h;return a}();return yx(p)}}(h));return h}
+function Fy(a,b,c,d,e,f,h){var k=Kx(1);lx(function(k){return function(){var l=function(){return function(a){return function(){function b(b){for(;;){a:try{for(;;){var c=a(b);if(!N(c,Z)){var d=c;break a}}}catch(S){if(S instanceof Object)b[5]=S,Cx(b),d=Z;else throw S;}if(!N(d,Z))return d}}function c(){var a=[null,null,null,null,null,null,null,null,null,null,null,null,null];a[0]=d;a[1]=1;return a}var d=null;d=function(a){switch(arguments.length){case 0:return c.call(this);case 1:return b.call(this,a)}throw Error("Invalid arity: "+
+(arguments.length-1));};d.B=c;d.h=b;return d}()}(function(){return function(k){var l=k[1];if(7===l)return l=k[7],k[2]=l,k[1]=9,Z;if(1===l)return Ax(k,2,a,ny(!0));if(4===l){l=k[2];var m=Su(qy,b),p=Ay();p=Su(sy,p);m=Ey(a,Tu(m,p),d,e,f,h);k[8]=l;return zx(k,5,m)}return 6===l?(l=ny(!1),k[9]=k[2],Ax(k,10,a,l)):3===l?(l=k[2],m=fe($u(d,b)),m=qy(m),k[10]=l,Ax(k,4,a,m)):2===l?(l=sy(d),k[11]=k[2],Ax(k,3,a,l)):9===l?Ax(k,6,a,sy(k[2])):5===l?(l=k[2],k[7]=l,k[1]=t(l)?7:8,Z):10===l?(l=k[7],k[12]=k[2],Bx(k,l)):
+8===l?(k[2]=c,k[1]=9,Z):null}}(k),k)}(),m=function(){var a=l.B?l.B():l.call(null);a[6]=k;return a}();return yx(m)}}(k));return k}
+function Gy(a,b,c){var d=null!=a&&(a.m&64||q===a.G)?P(U,a):a,e=D.c(d,Hk),f=D.c(d,Ak),h=D.c(d,dn),k=D.c(d,Jl),l=Kx(10),p=Kx(1);lx(function(a,d,e,f,h,k,l,p){return function(){var m=function(){return function(a){return function(){function b(b){for(;;){a:try{for(;;){var c=a(b);if(!N(c,Z)){var d=c;break a}}}catch(Ba){if(Ba instanceof Object)b[5]=Ba,Cx(b),d=Z;else throw Ba;}if(!N(d,Z))return d}}function c(){var a=[null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,
+null,null,null,null,null,null,null];a[0]=d;a[1]=1;return a}var d=null;d=function(a){switch(arguments.length){case 0:return c.call(this);case 1:return b.call(this,a)}throw Error("Invalid arity: "+(arguments.length-1));};d.B=c;d.h=b;return d}()}(function(a,d,e,f,h,k,l,m){return function(a){var e=a[1];if(65===e){var f=a,p=f;p[2]=a[2];p[1]=62;return Z}if(70===e){var u=f=a;u[2]=!1;u[1]=71;return Z}if(62===e){var w=a[2],x=f=a;x[2]=w;x[1]=59;return Z}if(74===e){var C=a[7],F=a[8],I=a[2],M=D.c(I,Uk),S=D.c(I,
+wl),Q=Nu(F,S),X=sy(Q);a[7]=Q;a[9]=M;f=a;return Ax(f,75,b,X)}if(7===e){var W=a[10],Ha=a[2],Ba=J(Ha,0,null);F=J(Ha,1,null);var Ga=G.c(gl,Ba);a[8]=F;a[10]=Ba;f=a;f[1]=Ga?8:9;return Z}if(59===e){var Oa=a[2],Ja=f=a;Ja[2]=Oa;Ja[1]=51;return Z}if(20===e){var db=P(U,c),xb=f=a;xb[2]=db;xb[1]=22;return Z}if(72===e){var az=P(U,c),ps=f=a;ps[2]=az;ps[1]=74;return Z}if(58===e){W=a[10];var bz=G.c(Xj,W);f=a;f[1]=bz?60:61;return Z}if(60===e){var ac=a[11],bc=0,Dc=ac,Eb=null,bb=null;a[11]=Dc;a[12]=bb;a[13]=bc;a[14]=
+Eb;var qs=f=a;qs[2]=null;qs[1]=2;return Z}if(27===e){ac=a[11];bb=a[12];bc=a[13];Eb=a[14];var cz=bb,dz=Eb,Rd=bc;Dc=ac;var Sd=dz,Td=cz;a[11]=Dc;a[12]=Td;a[13]=Rd;a[14]=Sd;var rs=f=a;rs[2]=null;rs[1]=2;return Z}if(1===e){bc=h;ac=k;bb=Eb=null;a[11]=ac;a[12]=bb;a[13]=bc;a[14]=Eb;var ss=f=a;ss[2]=null;ss[1]=2;return Z}if(69===e){var ts=f=a;ts[2]=!0;ts[1]=71;return Z}if(24===e){W=a[10];var ez=G.c(Nl,W);f=a;f[1]=ez?30:31;return Z}if(55===e){var fz=new R(null,1,5,T,[gl],null);a[15]=a[2];f=a;return Ax(f,56,
+d,fz)}if(39===e){var gz=a[2],us=f=a;us[2]=gz;us[1]=32;return Z}if(46===e){var vs=f=a;vs[2]=null;vs[1]=47;return Z}if(4===e){Eb=a[14];var ws=a[2],Ci=J(ws,0,null),hz=J(ws,1,null),iz=G.c(hz,Eb);a[16]=Ci;f=a;f[1]=iz?5:6;return Z}if(54===e){F=a[8];bb=a[12];bc=a[13];Eb=a[14];var jz=a[2],kz=bb,lz=Eb;Rd=bc;ac=F;Sd=lz;Td=kz;a[11]=ac;a[17]=jz;a[12]=Td;a[13]=Rd;a[14]=Sd;var xs=f=a;xs[2]=null;xs[1]=2;return Z}if(15===e){var ys=f=a;ys[2]=!1;ys[1]=16;return Z}if(48===e){var mz=a[2],zs=f=a;zs[2]=mz;zs[1]=47;return Z}if(50===
+e){W=a[10];var nz=G.c(qk,W);f=a;f[1]=nz?57:58;return Z}if(75===e){C=a[7];M=a[9];var oz=a[2],pz=fe($u(C,M)),qz=qy(pz);a[18]=oz;f=a;return Ax(f,76,b,qz)}if(21===e){var As=f=a;As[2]=c;As[1]=22;return Z}if(31===e){W=a[10];var rz=G.c(Kn,W);f=a;f[1]=rz?37:38;return Z}if(32===e){var sz=a[2],Bs=f=a;Bs[2]=sz;Bs[1]=25;return Z}if(40===e){var tz=new R(null,1,5,T,[wm],null);f=a;return Ax(f,43,d,tz)}if(56===e){var uz=a[2],Cs=f=a;Cs[2]=uz;Cs[1]=54;return Z}if(33===e){var Ds=f=a;Ds[2]=wm;Ds[1]=35;return Z}if(13===
+e){var vz=a[2],Es=f=a;Es[2]=vz;Es[1]=10;return Z}if(22===e){ac=a[11];bc=a[13];var Fs=a[2],wz=D.c(Fs,Uk),xz=D.c(Fs,wl),Gs=Kx(null),yz=Fy(b,wz,xz,bc,ac,l,Gs),zz=ac;Rd=null;Dc=zz;Eb=yz;bb=Gs;a[11]=Dc;a[12]=bb;a[13]=Rd;a[14]=Eb;var Hs=f=a;Hs[2]=null;Hs[1]=2;return Z}if(36===e){ac=a[11];bb=a[12];bc=a[13];Eb=a[14];var Az=a[2],Bz=ac,Cz=bb,Dz=Eb;Rd=bc;Dc=Bz;Sd=Dz;Td=Cz;a[11]=Dc;a[19]=Az;a[12]=Td;a[13]=Rd;a[14]=Sd;var Is=f=a;Is[2]=null;Is[1]=2;return Z}if(41===e){var Js=f=a;Js[2]=null;Js[1]=42;return Z}if(43===
+e){var Ez=a[2],Ks=f=a;Ks[2]=Ez;Ks[1]=42;return Z}if(61===e){W=a[10];var Fz=G.c(go,W);f=a;f[1]=Fz?63:64;return Z}if(29===e){var Gz=ac=a[11];bc=a[2];Dc=Gz;bb=Eb=null;a[11]=Dc;a[12]=bb;a[13]=bc;a[14]=Eb;var Ls=f=a;Ls[2]=null;Ls[1]=2;return Z}if(44===e)return bb=a[12],a[20]=a[2],f=a,f[1]=t(bb)?45:46,Z;if(6===e){Ci=a[16];var Ms=f=a;Ms[2]=Ci;Ms[1]=7;return Z}if(28===e){var Hz=a[2],Ns=f=a;Ns[2]=Hz;Ns[1]=25;return Z}if(64===e){W=a[10];var Iz=["No matching clause: ",v.h(W)].join("");throw Error(Iz);}if(51===
+e){var Jz=a[2],Os=f=a;Os[2]=Jz;Os[1]=39;return Z}if(25===e){var Kz=a[2],Ps=f=a;Ps[2]=Kz;Ps[1]=10;return Z}if(34===e){var Qs=f=a;Qs[2]=gl;Qs[1]=35;return Z}if(17===e){var Rs=f=a;Rs[2]=!0;Rs[1]=19;return Z}if(3===e){var Lz=a[2];f=a;return Bx(f,Lz)}if(12===e){var Mz=wb(null==c);f=a;f[1]=Mz?14:15;return Z}if(2===e){Eb=a[14];var Nz=vg(ub,new R(null,3,5,T,[d,m,Eb],null));f=a;return Vx(f,4,Nz,be([Im,!0]))}if(66===e){var Oz=q===c.G,Pz=c.m&64||Oz;f=a;f[1]=t(Pz)?69:70;return Z}if(23===e)return bb=a[12],f=a,
+f[1]=t(bb)?26:27,Z;if(47===e){ac=a[11];bb=a[12];bc=a[13];Eb=a[14];var Qz=a[2],Rz=ac,Sz=bb,Tz=Eb;Rd=bc;Dc=Rz;Sd=Tz;Td=Sz;a[11]=Dc;a[21]=Qz;a[12]=Td;a[13]=Rd;a[14]=Sd;var Ss=f=a;Ss[2]=null;Ss[1]=2;return Z}if(35===e){var Uz=new R(null,1,5,T,[a[2]],null);f=a;return Ax(f,36,d,Uz)}if(76===e){ac=a[11];C=a[7];bb=a[12];Eb=a[14];var Vz=a[2],Wz=ac,Xz=bb,Yz=Eb;bc=C;Dc=Wz;Sd=Yz;Td=Xz;a[11]=Dc;a[22]=Vz;a[12]=Td;a[13]=bc;a[14]=Sd;var Ts=f=a;Ts[2]=null;Ts[1]=2;return Z}if(19===e){var Zz=a[2],Us=f=a;Us[2]=Zz;Us[1]=
+16;return Z}if(57===e){var Vs=f=a;Vs[2]=null;Vs[1]=59;return Z}if(68===e){var $z=a[2];f=a;f[1]=t($z)?72:73;return Z}if(11===e){ac=a[11];bb=a[12];bc=a[13];Eb=a[14];var aA=ac,bA=bb,cA=Eb;Rd=bc;Dc=aA;Sd=cA;Td=bA;a[11]=Dc;a[12]=Td;a[13]=Rd;a[14]=Sd;var Ws=f=a;Ws[2]=null;Ws[1]=2;return Z}if(9===e){W=a[10];var dA=G.c(wm,W);f=a;f[1]=dA?23:24;return Z}if(5===e){Ci=a[16];var eA=new R(null,2,5,T,[Xj,Ci],null),Xs=f=a;Xs[2]=eA;Xs[1]=7;return Z}if(14===e){var fA=q===c.G,gA=c.m&64||fA;f=a;f[1]=t(gA)?17:18;return Z}if(45===
+e){var hA=new R(null,1,5,T,[gl],null);f=a;return Ax(f,48,d,hA)}if(53===e){var Ys=f=a;Ys[2]=null;Ys[1]=54;return Z}if(26===e){bb=a[12];Eb=a[14];var iA=Tw(bb);a[23]=iA;f=a;return zx(f,29,Eb)}if(16===e){var jA=a[2];f=a;f[1]=t(jA)?20:21;return Z}if(38===e){W=a[10];var kA=G.c(lm,W);f=a;f[1]=kA?49:50;return Z}if(30===e)return bb=a[12],f=a,f[1]=t(bb)?33:34,Z;if(73===e){var Zs=f=a;Zs[2]=c;Zs[1]=74;return Z}if(10===e){var lA=a[2],$s=f=a;$s[2]=lA;$s[1]=3;return Z}if(18===e){var at=f=a;at[2]=!1;at[1]=19;return Z}if(52===
+e){var mA=new R(null,1,5,T,[wm],null);f=a;return Ax(f,55,d,mA)}if(67===e){var bt=f=a;bt[2]=!1;bt[1]=68;return Z}if(71===e){var nA=a[2],ct=f=a;ct[2]=nA;ct[1]=68;return Z}if(42===e){F=a[8];var oA=new R(null,2,5,T,[go,F],null);a[24]=a[2];f=a;return Ax(f,44,d,oA)}if(37===e)return bb=a[12],f=a,f[1]=t(bb)?40:41,Z;if(63===e){var pA=wb(null==c);f=a;f[1]=pA?66:67;return Z}return 8===e?(bb=a[12],f=a,f[1]=t(bb)?11:12,Z):49===e?(bb=a[12],f=a,f[1]=t(bb)?52:53,Z):null}}(a,d,e,f,h,k,l,p),a,d,e,f,h,k,l,p)}(),u=function(){var b=
+m.B?m.B():m.call(null);b[6]=a;return b}();return yx(u)}}(p,l,a,d,e,f,h,k));return p}function Hy(a){var b=window.requestIdleCallback;return t(b)?(a=function(a,b){return function h(c){return function(a){return function(){if(E(c)){var b=h(vd(c));return a.h?a.h(b):a.call(null,b)}return null}}(a,b)}}(b,b)(a),b.h?b.h(a):b.call(null,a)):null}
+function Iy(a,b){var c=null!=a&&(a.m&64||q===a.G)?P(U,a):a,d=D.c(c,bl),e=D.c(c,Jl),f=D.c(c,Hj),h=D.c(c,Sj),k=D.c(c,Mm),l=Kx(1);lx(function(a,c,d,e,f,h,k,l,M){return function(){var m=function(){return function(a){return function(){function b(b){for(;;){a:try{for(;;){var c=a(b);if(!N(c,Z)){var d=c;break a}}}catch(Ba){if(Ba instanceof Object)b[5]=Ba,Cx(b),d=Z;else throw Ba;}if(!N(d,Z))return d}}function c(){var a=[null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null];
+a[0]=d;a[1]=1;return a}var d=null;d=function(a){switch(arguments.length){case 0:return c.call(this);case 1:return b.call(this,a)}throw Error("Invalid arity: "+(arguments.length-1));};d.B=c;d.h=b;return d}()}(function(a,c,d,e,f,h,k,l,m){return function(a){var c=a[1];if(7===c)return c=a[7],c=G.c(k,c),a[1]=c?9:10,Z;if(1===c)return a[1]=t(l)?2:3,Z;if(4===c){c=a[2];c=f.h?f.h(c):f.call(null,c);var d=new R(null,2,5,T,[c,k],null);a[8]=c;return Ux(a,5,d)}if(15===c)return c=d=a[9],c=null!=c&&(c.m&64||q===c.G)?
+P(U,c):c,c=D.c(c,Uk),c=Ox(b,qy(fe($u(m,c)))),a[2]=c,a[1]=17,Z;if(13===c)return c=a[2],d=ly(!1),a[10]=c,Ax(a,14,b,d);if(6===c)return c=a[11],a[2]=c,a[1]=8,Z;if(17===c){d=a[9];c=a[2];var h=d;var p=null!=h&&(h.m&64||q===h.G)?P(U,h):h;h=D.c(p,fl);var u=D.c(p,no);p=D.c(p,wl);Ox(b,new jy(h,u,p,null,null,null));h=Ox(b,new oy(null,null,null));u=Gy(e,b,d);d=Uk.h(d);d=Hy(d);a[12]=c;a[13]=u;a[14]=h;return Bx(a,d)}if(3===c)return a[2]=m,a[1]=4,Z;if(12===c)return c=a[2],d=f.h?f.h(!0):f.call(null,!0),a[15]=c,zx(a,
+13,d);if(2===c)return a[2]=l,a[1]=4,Z;if(11===c)return a[2]=a[2],a[1]=8,Z;if(9===c)return Ax(a,12,b,ly(!0));if(5===c)return d=a[8],h=a[2],c=J(h,0,null),h=J(h,1,null),d=G.c(d,h),a[7]=h,a[11]=c,a[1]=d?6:7,Z;if(14===c)return c=a[10],a[16]=a[2],a[2]=c,a[1]=11,Z;if(16===c)return a[2]=null,a[1]=17,Z;if(10===c)throw c=a[7],a=["No matching clause: ",v.h(c)].join(""),Error(a);return 8===c?(d=a[2],a[9]=d,a[1]=t(m)?15:16,Z):null}}(a,c,d,e,f,h,k,l,M),a,c,d,e,f,h,k,l,M)}(),p=function(){var b=m.B?m.B():m.call(null);
+b[6]=a;return b}();return yx(p)}}(l,a,c,c,d,e,f,h,k))}function Jy(a,b,c,d,e,f,h,k,l,p,m,u){this.nb=a;this.Ea=b;this.$a=c;this.ob=d;this.speed=e;this.Y=f;this.jb=h;this.mb=k;this.lb=l;this.v=p;this.j=m;this.w=u;this.m=2229667594;this.J=139264}g=Jy.prototype;g.Bd=function(){var a=Kx(null);Iy(this,a);t(this.Y)&&this.ac(null);return a};g.Ad=function(){Ox(this.Ea,new R(null,1,5,T,[wm],null));return Ox(this.Ea,new R(null,1,5,T,[qk],null))};
+g.ac=function(){Tw(this.$a);return Ox(this.Ea,new R(null,1,5,T,[gl],null))};g.wc=function(){return Ox(this.Ea,new R(null,1,5,T,[wm],null))};g.Dd=function(){Tw(this.$a);return Ox(this.Ea,new R(null,1,5,T,[Nl],null))};g.Cd=function(a,b){Tw(this.$a);return Ox(this.Ea,new R(null,2,5,T,[Kn,b],null))};g.zd=function(a,b){return Ox(this.Ea,new R(null,2,5,T,[lm,b],null))};g.V=function(a,b){return this.I(null,b,null)};
+g.I=function(a,b,c){switch(b instanceof L?b.ea:null){case "recording-ch-fn":return this.nb;case "command-ch":return this.Ea;case "force-load-ch":return this.$a;case "start-at":return this.ob;case "speed":return this.speed;case "auto-play?":return this.Y;case "loop?":return this.jb;case "preload?":return this.mb;case "poster-time":return this.lb;default:return D.l(this.j,b,c)}};
+g.R=function(a,b,c){return Y(b,function(){return function(a){return Y(b,Qi,""," ","",c,a)}}(this),"#asciinema.player.source.Recording{",", ","}",c,O.c(new R(null,9,5,T,[new R(null,2,5,T,[bl,this.nb],null),new R(null,2,5,T,[Jl,this.Ea],null),new R(null,2,5,T,[Hj,this.$a],null),new R(null,2,5,T,[Hk,this.ob],null),new R(null,2,5,T,[Ak,this.speed],null),new R(null,2,5,T,[Jm,this.Y],null),new R(null,2,5,T,[dn,this.jb],null),new R(null,2,5,T,[Sj,this.mb],null),new R(null,2,5,T,[Mm,this.lb],null)],null),
+this.j))};g.ba=function(){return new fh(0,this,9,new R(null,9,5,T,[bl,Jl,Hj,Hk,Ak,Jm,dn,Sj,Mm],null),t(this.j)?dd(this.j):Cf())};g.P=function(){return this.v};g.W=function(){return 9+H(this.j)};g.U=function(){var a=this,b=this.w;if(null!=b)return b;var c=function(){return function(){return function(a){return 1201370539^Dd(a)}}(b,a)(a)}();return this.w=c};
+g.K=function(a,b){return null!=b&&this.constructor===b.constructor&&G.c(this.nb,b.nb)&&G.c(this.Ea,b.Ea)&&G.c(this.$a,b.$a)&&G.c(this.ob,b.ob)&&G.c(this.speed,b.speed)&&G.c(this.Y,b.Y)&&G.c(this.jb,b.jb)&&G.c(this.mb,b.mb)&&G.c(this.lb,b.lb)&&G.c(this.j,b.j)};
+g.ga=function(a,b){return He(new ti(null,new r(null,9,[Hj,null,Sj,null,Ak,null,Hk,null,bl,null,Jl,null,Jm,null,Mm,null,dn,null],null),null),b)?le.c(tc(wg.c(Ef,this),this.v),b):new Jy(this.nb,this.Ea,this.$a,this.ob,this.speed,this.Y,this.jb,this.mb,this.lb,this.v,Bf(le.c(this.j,b)),null)};
+g.O=function(a,b,c){return t(N.c?N.c(bl,b):N.call(null,bl,b))?new Jy(c,this.Ea,this.$a,this.ob,this.speed,this.Y,this.jb,this.mb,this.lb,this.v,this.j,null):t(N.c?N.c(Jl,b):N.call(null,Jl,b))?new Jy(this.nb,c,this.$a,this.ob,this.speed,this.Y,this.jb,this.mb,this.lb,this.v,this.j,null):t(N.c?N.c(Hj,b):N.call(null,Hj,b))?new Jy(this.nb,this.Ea,c,this.ob,this.speed,this.Y,this.jb,this.mb,this.lb,this.v,this.j,null):t(N.c?N.c(Hk,b):N.call(null,Hk,b))?new Jy(this.nb,this.Ea,this.$a,c,this.speed,this.Y,
+this.jb,this.mb,this.lb,this.v,this.j,null):t(N.c?N.c(Ak,b):N.call(null,Ak,b))?new Jy(this.nb,this.Ea,this.$a,this.ob,c,this.Y,this.jb,this.mb,this.lb,this.v,this.j,null):t(N.c?N.c(Jm,b):N.call(null,Jm,b))?new Jy(this.nb,this.Ea,this.$a,this.ob,this.speed,c,this.jb,this.mb,this.lb,this.v,this.j,null):t(N.c?N.c(dn,b):N.call(null,dn,b))?new Jy(this.nb,this.Ea,this.$a,this.ob,this.speed,this.Y,c,this.mb,this.lb,this.v,this.j,null):t(N.c?N.c(Sj,b):N.call(null,Sj,b))?new Jy(this.nb,this.Ea,this.$a,this.ob,
+this.speed,this.Y,this.jb,c,this.lb,this.v,this.j,null):t(N.c?N.c(Mm,b):N.call(null,Mm,b))?new Jy(this.nb,this.Ea,this.$a,this.ob,this.speed,this.Y,this.jb,this.mb,c,this.v,this.j,null):new Jy(this.nb,this.Ea,this.$a,this.ob,this.speed,this.Y,this.jb,this.mb,this.lb,this.v,K.l(this.j,b,c),null)};
+g.S=function(){return E(O.c(new R(null,9,5,T,[new R(null,2,5,T,[bl,this.nb],null),new R(null,2,5,T,[Jl,this.Ea],null),new R(null,2,5,T,[Hj,this.$a],null),new R(null,2,5,T,[Hk,this.ob],null),new R(null,2,5,T,[Ak,this.speed],null),new R(null,2,5,T,[Jm,this.Y],null),new R(null,2,5,T,[dn,this.jb],null),new R(null,2,5,T,[Sj,this.mb],null),new R(null,2,5,T,[Mm,this.lb],null)],null),this.j))};
+g.T=function(a,b){return new Jy(this.nb,this.Ea,this.$a,this.ob,this.speed,this.Y,this.jb,this.mb,this.lb,b,this.j,this.w)};g.X=function(a,b){return ze(b)?this.O(null,A.c(b,0),A.c(b,1)):Mb(Tb,this,b)};wj(ok,function(a,b){var c=null!=b&&(b.m&64||q===b.G)?P(U,b):b,d=D.c(c,fl),e=D.c(c,no),f=D.c(c,Hk),h=D.c(c,Ak),k=D.c(c,qm),l=D.c(c,Em),p=D.c(c,cm),m=D.c(c,vm);c=D.c(c,Mm);d=Cy(a,d,e,k);e=Kx(10);k=Kx(null);return new Jy(d,e,k,f,h,l,p,m,c,null,null,null)});
+function Ky(a,b,c){var d=Kx(null),e=Kx(1);lx(function(d,e){return function(){var f=function(){return function(a){return function(){function b(b){for(;;){a:try{for(;;){var c=a(b);if(!N(c,Z)){var d=c;break a}}}catch(I){if(I instanceof Object)b[5]=I,Cx(b),d=Z;else throw I;}if(!N(d,Z))return d}}function c(){var a=[null,null,null,null,null,null,null,null,null,null,null];a[0]=d;a[1]=1;return a}var d=null;d=function(a){switch(arguments.length){case 0:return c.call(this);case 1:return b.call(this,a)}throw Error("Invalid arity: "+
+(arguments.length-1));};d.B=c;d.h=b;return d}()}(function(d,e){return function(d){var f=d[1];if(1===f)return f=Ot(a,b),d[7]=f,d[2]=null,d[1]=2,Z;if(2===f)return zx(d,4,e);if(3===f)return Bx(d,d[2]);if(4===f)return f=d[2],d[8]=f,d[1]=t(f)?5:6,Z;if(5===f){var h=d[8];f=d[7];f=Ju(f,h);h=qy(f);d[9]=f;return Ax(d,8,c,h)}return 6===f?(d[2]=null,d[1]=7,Z):7===f?(d[2]=d[2],d[1]=3,Z):8===f?(f=d[9],h=d[2],d[10]=h,d[7]=f,d[2]=null,d[1]=2,Z):null}}(d,e),d,e)}(),h=function(){var a=f.B?f.B():f.call(null);a[6]=d;
+return a}();return yx(h)}}(e,d));return d}
+function Ly(a,b,c,d){var e=Kx(1);lx(function(e){return function(){var f=function(){return function(a){return function(){function b(b){for(;;){a:try{for(;;){var c=a(b);if(!N(c,Z)){var d=c;break a}}}catch(F){if(F instanceof Object)b[5]=F,Cx(b),d=Z;else throw F;}if(!N(d,Z))return d}}function c(){var a=[null,null,null,null,null,null,null,null,null,null,null];a[0]=d;a[1]=1;return a}var d=null;d=function(a){switch(arguments.length){case 0:return c.call(this);case 1:return b.call(this,a)}throw Error("Invalid arity: "+
+(arguments.length-1));};d.B=c;d.h=b;return d}()}(function(){return function(e){var f=e[1];if(7===f)return Ax(e,9,b,String.fromCharCode(Math.floor(160*Math.random())));if(1===f)return Ax(e,2,a,ny(!0));if(4===f)return f=ny(!1),e[7]=e[2],Ax(e,10,a,f);if(6===f)return e[2]=null,e[1]=8,Z;if(3===f)return f=Jx(100*Math.random()/c),Ux(e,5,new R(null,2,5,T,[d,f],null));if(2===f)return e[8]=e[2],e[2]=null,e[1]=3,Z;if(9===f)return e[9]=e[2],e[2]=null,e[1]=3,Z;if(5===f){var h=e[2];f=J(h,0,null);h=J(h,1,null);
+h=G.c(h,d);e[10]=f;e[1]=h?6:7;return Z}return 10===f?Bx(e,e[2]):8===f?(e[2]=e[2],e[1]=4,Z):null}}(e),e)}(),k=function(){var a=f.B?f.B():f.call(null);a[6]=e;return a}();return yx(k)}}(e));return e}function My(a,b,c,d,e,f,h,k,l,p){this.speed=a;this.Y=b;this.width=c;this.height=d;this.ha=e;this.pb=f;this.La=h;this.v=k;this.j=l;this.w=p;this.m=2229667594;this.J=139264}g=My.prototype;g.Bd=function(){fg(this.ha,Kx(null));fg(this.pb,Ky(this.width,this.height,B(this.ha)));t(this.Y)&&this.ac(null);return B(this.ha)};
+g.Ad=function(){return this.wc(null)};g.ac=function(){if(t(B(this.La)))return null;var a=Kx(null);fg(this.La,a);return Ly(B(this.ha),B(this.pb),this.speed,a)};g.wc=function(){return t(B(this.La))?(Tw(B(this.La)),fg(this.La,null)):null};g.Dd=function(){return t(B(this.La))?this.wc(null):this.ac(null)};g.Cd=function(){return null};g.zd=function(){return null};g.V=function(a,b){return this.I(null,b,null)};
+g.I=function(a,b,c){switch(b instanceof L?b.ea:null){case "speed":return this.speed;case "auto-play?":return this.Y;case "width":return this.width;case "height":return this.height;case "msg-ch":return this.ha;case "stdout-ch":return this.pb;case "stop-ch":return this.La;default:return D.l(this.j,b,c)}};
+g.R=function(a,b,c){return Y(b,function(){return function(a){return Y(b,Qi,""," ","",c,a)}}(this),"#asciinema.player.source.JunkPrinter{",", ","}",c,O.c(new R(null,7,5,T,[new R(null,2,5,T,[Ak,this.speed],null),new R(null,2,5,T,[Jm,this.Y],null),new R(null,2,5,T,[fl,this.width],null),new R(null,2,5,T,[no,this.height],null),new R(null,2,5,T,[Dl,this.ha],null),new R(null,2,5,T,[rn,this.pb],null),new R(null,2,5,T,[Zl,this.La],null)],null),this.j))};
+g.ba=function(){return new fh(0,this,7,new R(null,7,5,T,[Ak,Jm,fl,no,Dl,rn,Zl],null),t(this.j)?dd(this.j):Cf())};g.P=function(){return this.v};g.W=function(){return 7+H(this.j)};g.U=function(){var a=this,b=this.w;if(null!=b)return b;var c=function(){return function(){return function(a){return 1937333797^Dd(a)}}(b,a)(a)}();return this.w=c};
+g.K=function(a,b){return null!=b&&this.constructor===b.constructor&&G.c(this.speed,b.speed)&&G.c(this.Y,b.Y)&&G.c(this.width,b.width)&&G.c(this.height,b.height)&&G.c(this.ha,b.ha)&&G.c(this.pb,b.pb)&&G.c(this.La,b.La)&&G.c(this.j,b.j)};g.ga=function(a,b){return He(new ti(null,new r(null,7,[Ak,null,fl,null,Dl,null,Zl,null,Jm,null,rn,null,no,null],null),null),b)?le.c(tc(wg.c(Ef,this),this.v),b):new My(this.speed,this.Y,this.width,this.height,this.ha,this.pb,this.La,this.v,Bf(le.c(this.j,b)),null)};
+g.O=function(a,b,c){return t(N.c?N.c(Ak,b):N.call(null,Ak,b))?new My(c,this.Y,this.width,this.height,this.ha,this.pb,this.La,this.v,this.j,null):t(N.c?N.c(Jm,b):N.call(null,Jm,b))?new My(this.speed,c,this.width,this.height,this.ha,this.pb,this.La,this.v,this.j,null):t(N.c?N.c(fl,b):N.call(null,fl,b))?new My(this.speed,this.Y,c,this.height,this.ha,this.pb,this.La,this.v,this.j,null):t(N.c?N.c(no,b):N.call(null,no,b))?new My(this.speed,this.Y,this.width,c,this.ha,this.pb,this.La,this.v,this.j,null):
+t(N.c?N.c(Dl,b):N.call(null,Dl,b))?new My(this.speed,this.Y,this.width,this.height,c,this.pb,this.La,this.v,this.j,null):t(N.c?N.c(rn,b):N.call(null,rn,b))?new My(this.speed,this.Y,this.width,this.height,this.ha,c,this.La,this.v,this.j,null):t(N.c?N.c(Zl,b):N.call(null,Zl,b))?new My(this.speed,this.Y,this.width,this.height,this.ha,this.pb,c,this.v,this.j,null):new My(this.speed,this.Y,this.width,this.height,this.ha,this.pb,this.La,this.v,K.l(this.j,b,c),null)};
+g.S=function(){return E(O.c(new R(null,7,5,T,[new R(null,2,5,T,[Ak,this.speed],null),new R(null,2,5,T,[Jm,this.Y],null),new R(null,2,5,T,[fl,this.width],null),new R(null,2,5,T,[no,this.height],null),new R(null,2,5,T,[Dl,this.ha],null),new R(null,2,5,T,[rn,this.pb],null),new R(null,2,5,T,[Zl,this.La],null)],null),this.j))};g.T=function(a,b){return new My(this.speed,this.Y,this.width,this.height,this.ha,this.pb,this.La,b,this.j,this.w)};
+g.X=function(a,b){return ze(b)?this.O(null,A.c(b,0),A.c(b,1)):Mb(Tb,this,b)};wj(mn,function(a,b){var c=null!=b&&(b.m&64||q===b.G)?P(U,b):b;D.c(c,$m);var d=D.c(c,fl),e=D.c(c,no),f=D.c(c,Ak);c=D.c(c,Em);var h=dg.h(null),k=dg.h(null),l=dg.h(null);return new My(f,c,d,e,h,k,l,null,null,null)});function Ny(a){return ev(JSON.parse(a))}
+function Oy(a,b){var c=Kx(1);lx(function(c){return function(){var d=function(){return function(a){return function(){function b(b){for(;;){a:try{for(;;){var c=a(b);if(!N(c,Z)){var d=c;break a}}}catch(x){if(x instanceof Object)b[5]=x,Cx(b),d=Z;else throw x;}if(!N(d,Z))return d}}function c(){var a=[null,null,null,null,null,null,null,null,null,null,null,null,null];a[0]=d;a[1]=1;return a}var d=null;d=function(a){switch(arguments.length){case 0:return c.call(this);case 1:return b.call(this,a)}throw Error("Invalid arity: "+
+(arguments.length-1));};d.B=c;d.h=b;return d}()}(function(){return function(c){var d=c[1];if(7===d)return c[2]=!1,c[1]=8,Z;if(20===d)return c[2]=!1,c[1]=21,Z;if(27===d){d=c[7];var e=D.c(c[2],ko);return Ax(c,28,d,e)}if(1===d)return zx(c,2,a);if(24===d)return c[2]=c[2],c[1]=21,Z;if(4===d)return c[2]=!1,c[1]=5,Z;if(15===d)return d=c[2],c[8]=d,c[1]=t(d)?16:17,Z;if(21===d)return c[1]=t(c[2])?25:26,Z;if(13===d)return zx(c,15,a);if(22===d)return c[2]=!0,c[1]=24,Z;if(6===d)return c[2]=!0,c[1]=8,Z;if(28===
+d)return c[9]=c[2],c[2]=null,c[1]=13,Z;if(25===d)return d=c[8],d=P(U,d),c[2]=d,c[1]=27,Z;if(17===d)return c[2]=null,c[1]=18,Z;if(3===d)return d=c[10],e=q===d.G,c[1]=t(d.m&64||e)?6:7,Z;if(12===d)return c[11]=c[2],c[2]=null,c[1]=13,Z;if(2===d)return d=c[2],e=wb(null==d),c[10]=d,c[1]=e?3:4,Z;if(23===d)return c[2]=!1,c[1]=24,Z;if(19===d)return d=c[8],e=q===d.G,c[1]=t(d.m&64||e)?22:23,Z;if(11===d){var f=c[2];d=D.c(f,Zk);e=D.c(f,fl);var h=D.c(f,no);f=D.c(f,ko);e=Ky(e,h,b);c[7]=e;c[12]=d;return Ax(c,12,
+e,f)}return 9===d?(d=c[10],d=P(U,d),c[2]=d,c[1]=11,Z):5===d?(c[1]=t(c[2])?9:10,Z):14===d?Bx(c,c[2]):26===d?(d=c[8],c[2]=d,c[1]=27,Z):16===d?(d=c[8],c[1]=wb(null==d)?19:20,Z):10===d?(d=c[10],c[2]=d,c[1]=11,Z):18===d?(c[2]=c[2],c[1]=14,Z):8===d?(c[2]=c[2],c[1]=5,Z):null}}(c),c)}(),f=function(){var a=d.B?d.B():d.call(null);a[6]=c;return a}();return yx(f)}}(c))}
+function Py(a,b){var c=new EventSource(a),d=dg.h(null);Ox(b,ly(!0));c.onopen=function(a,c){return function(){var a=Mx(1E4,ig.h(Ny));fg(c,a);Oy(a,b);Ox(b,ny(!0));return Ox(b,ly(!1))}}(c,d);c.onerror=function(a,c){return function(){Tw(B(c));fg(c,null);return Ox(b,ly(!0))}}(c,d);return c.onmessage=function(a,b){return function(a){var c=B(b);return t(c)?Ox(c,a.data):null}}(c,d)}
+function Qy(a,b,c,d,e,f,h){this.ha=a;this.url=b;this.Y=c;this.yb=d;this.v=e;this.j=f;this.w=h;this.m=2229667594;this.J=139264}g=Qy.prototype;g.Bd=function(){fg(this.ha,Kx(null));return t(this.Y)?this.ac(null):null};g.Ad=function(){return this.wc(null)};g.ac=function(){if(t(B(this.yb)))return null;fg(this.yb,!0);return Py(this.url,B(this.ha))};g.wc=function(){return null};g.Dd=function(){return this.ac(null)};g.Cd=function(){return null};g.zd=function(){return null};
+g.V=function(a,b){return this.I(null,b,null)};g.I=function(a,b,c){switch(b instanceof L?b.ea:null){case "msg-ch":return this.ha;case "url":return this.url;case "auto-play?":return this.Y;case "started?":return this.yb;default:return D.l(this.j,b,c)}};
+g.R=function(a,b,c){return Y(b,function(){return function(a){return Y(b,Qi,""," ","",c,a)}}(this),"#asciinema.player.source.Stream{",", ","}",c,O.c(new R(null,4,5,T,[new R(null,2,5,T,[Dl,this.ha],null),new R(null,2,5,T,[$m,this.url],null),new R(null,2,5,T,[Jm,this.Y],null),new R(null,2,5,T,[rm,this.yb],null)],null),this.j))};g.ba=function(){return new fh(0,this,4,new R(null,4,5,T,[Dl,$m,Jm,rm],null),t(this.j)?dd(this.j):Cf())};g.P=function(){return this.v};g.W=function(){return 4+H(this.j)};
+g.U=function(){var a=this,b=this.w;if(null!=b)return b;var c=function(){return function(){return function(a){return 187678783^Dd(a)}}(b,a)(a)}();return this.w=c};g.K=function(a,b){return null!=b&&this.constructor===b.constructor&&G.c(this.ha,b.ha)&&G.c(this.url,b.url)&&G.c(this.Y,b.Y)&&G.c(this.yb,b.yb)&&G.c(this.j,b.j)};
+g.ga=function(a,b){return He(new ti(null,new r(null,4,[Dl,null,rm,null,Jm,null,$m,null],null),null),b)?le.c(tc(wg.c(Ef,this),this.v),b):new Qy(this.ha,this.url,this.Y,this.yb,this.v,Bf(le.c(this.j,b)),null)};
+g.O=function(a,b,c){return t(N.c?N.c(Dl,b):N.call(null,Dl,b))?new Qy(c,this.url,this.Y,this.yb,this.v,this.j,null):t(N.c?N.c($m,b):N.call(null,$m,b))?new Qy(this.ha,c,this.Y,this.yb,this.v,this.j,null):t(N.c?N.c(Jm,b):N.call(null,Jm,b))?new Qy(this.ha,this.url,c,this.yb,this.v,this.j,null):t(N.c?N.c(rm,b):N.call(null,rm,b))?new Qy(this.ha,this.url,this.Y,c,this.v,this.j,null):new Qy(this.ha,this.url,this.Y,this.yb,this.v,K.l(this.j,b,c),null)};
+g.S=function(){return E(O.c(new R(null,4,5,T,[new R(null,2,5,T,[Dl,this.ha],null),new R(null,2,5,T,[$m,this.url],null),new R(null,2,5,T,[Jm,this.Y],null),new R(null,2,5,T,[rm,this.yb],null)],null),this.j))};g.T=function(a,b){return new Qy(this.ha,this.url,this.Y,this.yb,b,this.j,this.w)};g.X=function(a,b){return ze(b)?this.O(null,A.c(b,0),A.c(b,1)):Mb(Tb,this,b)};wj(hm,function(a,b){var c=null!=b&&(b.m&64||q===b.G)?P(U,b):b;c=D.c(c,Em);var d=dg.h(null),e=dg.h(!1);return new Qy(d,a,c,e,null,null,null)});function Ry(a){var b=new R(null,5,5,T,["fullscreenElement","mozFullScreenElement","webkitFullscreenElement","webkitCurrentFullScreenElement","msFullscreenElement"],null);b=Wf($f.c(Ee,Pu),b);t(b)?(a=Wf(Pu,new R(null,5,5,T,["exitFullscreen","webkitExitFullscreen","webkitCancelFullScreen","mozCancelFullScreen","msExitFullscreen"],null)),a=t(a)?a.call(document):null):(b=new R(null,5,5,T,["requestFullscreen","webkitRequestFullscreen","webkitRequestFullScreen","mozRequestFullScreen","msRequestFullscreen"],
+null),b=Wf(ag.c(Hb,a),b),a=t(b)?b.call(a):null);return a};r.prototype.yd=function(){return il.h(this)};r.prototype.xd=function(){return pl.h(this)};function Sy(a,b){return function(c){var d=b.h?b.h(c):b.call(null,c);return t(d)?(Ox(a,d),c.stopPropagation()):null}}function Ty(a,b){return Sy(a,function(){return b})}function Uy(a,b,c){var d="number"===typeof a||G.c(a,"fg")||G.c(a,"bg");return t(d)?(a=t(t(b)?8>a:b)?a+8:a,[v.h(c),v.h(a)].join("")):null}
+function Vy(a){var b=J(a,0,null),c=J(a,1,null);a=J(a,2,null);return["rgb(",v.h(b),",",v.h(c),",",v.h(a),")"].join("")}
+var Wy=hj(function(a){a=null!=a&&(a.m&64||q===a.G)?P(U,a):a;var b=D.c(a,Nk),c=D.c(a,pl);a=K.l(a,Nk,t(c)?wb(b):b);var d=null!=a&&(a.m&64||q===a.G)?P(U,a):a,e=D.c(d,Ok),f=D.c(d,Tn);b=D.c(d,Kj);var h=D.c(d,dk);c=D.c(d,Vl);var k=D.c(d,Nk),l=D.c(d,Yn);d=D.c(d,pl);var p=t(k)?t(e)?e:"fg":f;e=Uy(t(k)?t(f)?f:"bg":e,b,"fg-");h=Uy(p,h,"bg-");c=vg(ub,new R(null,6,5,T,[e,h,t(b)?"bright":null,t(l)?"italic":null,t(c)?"underline":null,t(d)?"cursor":null],null));if(E(c))a:for(b=new cb,c=E(c);;)if(null!=c)b.append(""+
+v.h(y(c))),c=z(c),null!=c&&b.append(" ");else{b=b.toString();break a}else b=null;l=null!=a&&(a.m&64||q===a.G)?P(U,a):a;a=D.c(l,Ok);c=D.c(l,Tn);h=D.c(l,Nk);l=t(h)?c:a;a=t(h)?a:c;a=hi.A(be([t(ze.h?ze.h(l):ze.call(null,l))?new r(null,1,[ik,Vy(l)],null):null,t(ze.h?ze.h(a):ze.call(null,a))?new r(null,1,[al,Vy(a)],null):null]));return hi.A(be([t(b)?new r(null,1,[vn,b],null):null,t(a)?new r(null,1,[fm,a],null):null]))});
+function Xy(a,b){var c=J(a,0,null),d=J(a,1,null);d=Bg(d,pl,function(){return function(a){return t(a)?B(b):a}}(a,c,d));return new R(null,3,5,T,[ro,Wy.h?Wy.h(d):Wy.call(null,d),c],null)}function Yy(a,b){var c=J(a,0,null),d=J(a,1,null),e=jg(b,c);e=E(e)?new R(null,2,5,T,[Eo(e),d],null):null;var f=K.l(d,pl,!0);f=new R(null,2,5,T,[Vd(c,b),f],null);c=kg(b+1,c);d=E(c)?new R(null,2,5,T,[Eo(c),d],null):null;return vg(ub,new R(null,3,5,T,[e,f,d],null))}
+function Zy(a,b){for(var c=he,d=a,e=b;;)if(E(d)){var f=y(d),h=J(f,0,null);J(f,1,null);h=H(h);if(h<=e)c=ge.c(c,f),d=vd(d),e-=h;else return O.A(c,Yy(f,e),be([vd(d)]))}else return c}function $y(a,b,c){a=t(B(b))?Zy(B(a),B(b)):B(a);return new R(null,2,5,T,[Lm,Ii(bg(function(){return function(a,b){return pe(new R(null,3,5,T,[Xy,b,c],null),new r(null,1,[mk,a],null))}}(a),a))],null)}var qA=new ti(null,new r(null,3,["small",null,"medium",null,"big",null],null),null);
+function rA(a,b,c,d,e){var f=yp(function(){var a=B(c);return t(qA.h?qA.h(a):qA.call(null,a))?["font-",v.h(a)].join(""):null}),h=yp(function(){return function(){var d=B(a),e=B(b),f=B(c);f=t(qA.h?qA.h(f):qA.call(null,f))?null:new r(null,1,[wk,f],null);return hi.A(be([new r(null,2,[fl,[v.h(d),"ch"].join(""),no,[v.h(1.3333333333*e),"em"].join("")],null),f]))}}(f)),k=yp(function(){return function(){return Lu(B(d))}}(f,h)),l=yp(function(a,c,d){return function(){return xg(function(a,b,c){return function(d){return yp(function(a,
+b,c){return function(){return D.c(B(c),d)}}(a,b,c))}}(a,c,d),Fi(0,B(b),1))}}(f,h,k)),p=yp(function(){return function(){return Mu(B(d))}}(f,h,k,l)),m=yp(function(a,b,c,d,e){return function(){return zn.h(B(e))}}(f,h,k,l,p)),u=yp(function(a,b,c,d,e){return function(){return Aj.h(B(e))}}(f,h,k,l,p,m)),w=yp(function(a,b,c,d,e){return function(){return On.h(B(e))}}(f,h,k,l,p,m,u));return function(a,b,c,d,f,h,k,l){return function(){return new R(null,3,5,T,[Gm,new r(null,2,[vn,B(a),fm,B(b)],null),bg(function(a,
+b,c,d,f,h,k,l){return function(m,p){var u=yp(function(a,b,c,d,e,f,h,k){return function(){var a=B(k);return t(a)?(a=G.c(m,B(h)))?B(f):a:a}}(a,b,c,d,f,h,k,l));return pe(new R(null,4,5,T,[$y,p,u,e],null),new r(null,1,[mk,m],null))}}(a,b,c,d,f,h,k,l),B(d))],null)}}(f,h,k,l,p,m,u,w)}
+function sA(){return new R(null,2,5,T,[Ym,new r(null,4,[Mn,"1.1",Fl,"0 0 866.0254037844387 866.0254037844387",vn,"icon",mo,new r(null,1,[An,'\x3cdefs\x3e \x3cmask id\x3d"small-triangle-mask"\x3e \x3crect width\x3d"100%" height\x3d"100%" fill\x3d"white"/\x3e \x3cpolygon points\x3d"508.01270189221935 433.01270189221935, 208.0127018922194 259.8076211353316, 208.01270189221927 606.217782649107" fill\x3d"black"\x3e\x3c/polygon\x3e \x3c/mask\x3e \x3c/defs\x3e \x3cpolygon points\x3d"808.0127018922194 433.01270189221935, 58.01270189221947 -1.1368683772161603e-13, 58.01270189221913 866.0254037844386" mask\x3d"url(#small-triangle-mask)" fill\x3d"white"\x3e\x3c/polygon\x3e \x3cpolyline points\x3d"481.2177826491071 333.0127018922194, 134.80762113533166 533.0127018922194" stroke\x3d"white" stroke-width\x3d"90"\x3e\x3c/polyline\x3e'],null)],
+null)],null)}function tA(){return new R(null,3,5,T,[Ym,new r(null,3,[Mn,"1.1",Fl,"0 0 12 12",vn,"icon"],null),new R(null,2,5,T,[Fj,new r(null,1,[pn,"M1,0 L11,6 L1,12 Z"],null)],null)],null)}function uA(){return new R(null,4,5,T,[Ym,new r(null,3,[Mn,"1.1",Fl,"0 0 12 12",vn,"icon"],null),new R(null,2,5,T,[Fj,new r(null,1,[pn,"M1,0 L4,0 L4,12 L1,12 Z"],null)],null),new R(null,2,5,T,[Fj,new r(null,1,[pn,"M8,0 L11,0 L11,12 L8,12 Z"],null)],null)],null)}
+function vA(){return new R(null,4,5,T,[Ym,new r(null,3,[Mn,"1.1",Fl,"0 0 12 12",vn,"icon"],null),new R(null,2,5,T,[Fj,new r(null,1,[pn,"M12,0 L7,0 L9,2 L7,4 L8,5 L10,3 L12,5 Z"],null)],null),new R(null,2,5,T,[Fj,new r(null,1,[pn,"M0,12 L0,7 L2,9 L4,7 L5,8 L3,10 L5,12 Z"],null)],null)],null)}
+function wA(){return new R(null,4,5,T,[Ym,new r(null,3,[Mn,"1.1",Fl,"0 0 12 12",vn,"icon"],null),new R(null,2,5,T,[Fj,new r(null,1,[pn,"M7,5 L7,0 L9,2 L11,0 L12,1 L10,3 L12,5 Z"],null)],null),new R(null,2,5,T,[Fj,new r(null,1,[pn,"M5,7 L0,7 L2,9 L0,11 L1,12 L3,10 L5,12 Z"],null)],null)],null)}function xA(a,b){return function(b){return function(){return new R(null,3,5,T,[cl,new r(null,1,[Sl,b],null),new R(null,1,5,T,[t(B(a))?uA:tA],null)],null)}}(Ty(b,new fy(null,null,null)))}
+function yA(a){return 10>a?["0",v.h(a)].join(""):a}function zA(a){var b=Math.floor((a%60+60)%60);return[v.h(yA(Math.floor(a/60))),":",v.h(yA(b))].join("")}function AA(a,b){var c=T,d=new R(null,2,5,T,[Yk,zA(B(a))],null),e=T;var f=B(a);var h=B(b);f=["-",v.h(zA(h-f))].join("");return new R(null,3,5,c,[Ml,d,new R(null,2,5,e,[co,f],null)],null)}
+function BA(){function a(a){a.preventDefault();return Ry(a.currentTarget.parentNode.parentNode.parentNode)}return function(){return new R(null,4,5,T,[un,new r(null,1,[Sl,a],null),new R(null,1,5,T,[vA],null),new R(null,1,5,T,[wA],null)],null)}}
+function CA(a,b){var c=Sy(b,function(a){var b=a.currentTarget.offsetWidth,c=a.currentTarget.getBoundingClientRect();return cy(Nu(a.clientX-c.left,b)/b)}),d=yp(function(){return function(){return[v.h(100*B(a)),"%"].join("")}}(c));return function(a,b){return function(){return new R(null,2,5,T,[Vj,new R(null,3,5,T,[Bl,new r(null,1,[Ql,a],null),new R(null,2,5,T,[Cj,new R(null,2,5,T,[ro,new r(null,1,[fm,new r(null,1,[fl,B(b)],null)],null)],null)],null)],null)],null)}}(c,d)}
+function DA(a,b,c,d){return function(e){return function(){return new R(null,5,5,T,[Kk,new R(null,3,5,T,[xA,a,d],null),new R(null,3,5,T,[AA,b,c],null),new R(null,1,5,T,[BA],null),new R(null,3,5,T,[CA,e,d],null)],null)}}(yp(function(){return B(b)/B(c)}))}
+function EA(a){return function(a){return function(){return new R(null,3,5,T,[ol,new r(null,1,[Sl,a],null),new R(null,2,5,T,[Xk,new R(null,2,5,T,[km,new R(null,2,5,T,[ro,new R(null,1,5,T,[sA],null)],null)],null)],null)],null)}}(Ty(a,new fy(null,null,null)))}function FA(){return new R(null,2,5,T,[Ek,new R(null,1,5,T,[xn],null)],null)}function GA(a){return Wf(function(b){return a[b]},new R(null,4,5,T,["altKey","shiftKey","metaKey","ctrlKey"],null))}
+function HA(a){var b=t(GA(a))?null:function(){switch(a.key){case " ":return new fy(null,null,null);case "f":return bm;case "0":return cy(0);case "1":return cy(.1);case "2":return cy(.2);case "3":return cy(.3);case "4":return cy(.4);case "5":return cy(.5);case "6":return cy(.6);case "7":return cy(.7);case "8":return cy(.8);case "9":return cy(.9);default:return null}}();if(t(b))return b;switch(a.key){case "\x3e":return new ey(null,null,null);case "\x3c":return new dy(null,null,null);default:return null}}
+function IA(a){if(t(GA(a)))return null;switch(a.which){case 37:return new ay(null,null,null);case 39:return new $x(null,null,null);default:return null}}function JA(a){var b=HA(a);return t(b)?(a.preventDefault(),G.c(b,bm)?(Ry(a.currentTarget),null):b):null}function KA(a){var b=IA(a);return t(b)?(a.preventDefault(),b):null}
+function LA(a,b,c,d){a=t(a)?['"',v.h(a),'"'].join(""):"untitled";return new R(null,4,5,T,[dl,t(d)?new R(null,2,5,T,[jo,new r(null,1,[zl,d],null)],null):null,a,t(b)?new R(null,3,5,T,[ro," by ",t(c)?new R(null,3,5,T,[lo,new r(null,1,[ho,c],null),b],null):b],null):null],null)}
+function MA(a){var b=Mx(1,ig.h(iy)),c=Kx(1);lx(function(c){return function(){var d=function(){return function(a){return function(){function b(b){for(;;){a:try{for(;;){var c=a(b);if(!N(c,Z)){var d=c;break a}}}catch(x){if(x instanceof Object)b[5]=x,Cx(b),d=Z;else throw x;}if(!N(d,Z))return d}}function c(){var a=[null,null,null,null,null,null,null,null,null,null,null,null];a[0]=d;a[1]=1;return a}var d=null;d=function(a){switch(arguments.length){case 0:return c.call(this);case 1:return b.call(this,a)}throw Error("Invalid arity: "+
+(arguments.length-1));};d.B=c;d.h=b;return d}()}(function(){return function(c){var d=c[1];if(7===d)return c[7]=c[2],Ax(c,12,b,!1);if(1===d)return c[2]=null,c[1]=2,Z;if(4===d)return c[8]=c[2],Ax(c,5,b,!0);if(6===d)return d=Jx(3E3),Ux(c,8,new R(null,2,5,T,[a,d],null));if(3===d)return Bx(c,c[2]);if(12===d)return c[9]=c[2],c[2]=null,c[1]=2,Z;if(2===d)return zx(c,4,a);if(11===d)return c[2]=c[2],c[1]=7,Z;if(9===d)return c[2]=null,c[1]=6,Z;if(5===d)return c[10]=c[2],c[2]=null,c[1]=6,Z;if(10===d)return c[2]=
+null,c[1]=11,Z;if(8===d){var e=c[2];d=J(e,0,null);e=J(e,1,null);e=G.c(e,a);c[11]=d;c[1]=e?9:10;return Z}return null}}(c),c)}(),f=function(){var a=d.B?d.B():d.call(null);a[6]=c;return a}();return yx(f)}}(c));return b}
+function NA(a,b){var c=dg.h(b),d=Kx(1);lx(function(b,c){return function(){var d=function(){return function(a){return function(){function b(b){for(;;){a:try{for(;;){var c=a(b);if(!N(c,Z)){var d=c;break a}}}catch(F){if(F instanceof Object)b[5]=F,Cx(b),d=Z;else throw F;}if(!N(d,Z))return d}}function c(){var a=[null,null,null,null,null,null,null,null,null,null,null,null,null];a[0]=d;a[1]=1;return a}var d=null;d=function(a){switch(arguments.length){case 0:return c.call(this);case 1:return b.call(this,
+a)}throw Error("Invalid arity: "+(arguments.length-1));};d.B=c;d.h=b;return d}()}(function(b,c){return function(d){var e=d[1];if(7===e){var f=d[7],h=wb(null==f);d[8]=d[2];d[1]=h?8:9;return Z}if(20===e)return f=d[7],d[1]=t(q===f.Fe)?23:24,Z;if(27===e)return d[2]=!1,d[1]=28,Z;if(1===e)return d[2]=null,d[1]=2,Z;if(24===e)return f=d[7],d[1]=t(!f.Tc)?26:27,Z;if(4===e){f=d[7];var k=d[9];h=d[2];var l=J(h,0,null),m=J(h,1,null);d[10]=m;d[7]=l;d[9]=h;d[1]=t(null==l)?5:6;return Z}return 15===e?(d[2]=!1,d[1]=
+16,Z):21===e?(f=d[7],h=Ab(Yx,f),d[2]=h,d[1]=22,Z):31===e?(d[11]=d[2],d[2]=null,d[1]=2,Z):13===e?(d[2]=d[2],d[1]=10,Z):22===e?(d[1]=t(d[2])?29:30,Z):29===e?(f=d[7],h=B(a),h=Zx(f,h),h=gg.l(c,wo,h),d[2]=h,d[1]=31,Z):6===e?(d[2]=null,d[1]=7,Z):28===e?(d[2]=d[2],d[1]=25,Z):25===e?(d[2]=d[2],d[1]=22,Z):17===e?(m=d[10],f=d[7],k=d[9],h=gg.c(a,function(){return function(a,b){return function(a){return Xx(b,a)}}(k,f,m,m,f,k,e,b,c)}()),d[2]=h,d[1]=19,Z):3===e?Bx(d,d[2]):12===e?(f=d[7],d[1]=t(!f.Tc)?14:15,Z):
+2===e?(h=B(c),h=E(h),Ux(d,4,h)):23===e?(d[2]=!0,d[1]=25,Z):19===e?(f=d[7],h=wb(null==f),d[12]=d[2],d[1]=h?20:21,Z):11===e?(d[2]=!0,d[1]=13,Z):9===e?(f=d[7],h=Ab(Wx,f),d[2]=h,d[1]=10,Z):5===e?(m=d[10],h=gg.l(c,re,m),d[2]=h,d[1]=7,Z):14===e?(f=d[7],h=Ab(Wx,f),d[2]=h,d[1]=16,Z):26===e?(f=d[7],h=Ab(Yx,f),d[2]=h,d[1]=28,Z):16===e?(d[2]=d[2],d[1]=13,Z):30===e?(d[2]=null,d[1]=31,Z):10===e?(d[1]=t(d[2])?17:18,Z):18===e?(d[2]=null,d[1]=19,Z):8===e?(f=d[7],d[1]=t(q===f.sb)?11:12,Z):null}}(b,c),b,c)}(),e=function(){var a=
+d.B?d.B():d.call(null);a[6]=b;return a}();return yx(e)}}(d,c));return d}
+function OA(a,b,c){c=Ty(c,!0);var d=Sy(b,JA),e=Sy(b,KA),f=yp(function(){return function(){return Hm.h(B(a))}}(c,d,e)),h=yp(function(){return function(){return el.h(B(a))}}(c,d,e,f)),k=yp(function(a,b,c,d,e){return function(){var a=B(d);return t(a)?a:B(e)}}(c,d,e,f,h)),l=yp(function(b,c,d,e,f,h){return function(){var b=Gk.h(B(a));b=t(b)?b:wb(B(h));return t(b)?"hud":null}}(c,d,e,f,h,k)),p=yp(function(){return function(){return["asciinema-theme-",v.h(gm.h(B(a)))].join("")}}(c,d,e,f,h,k,l)),m=yp(function(){return function(){var b=
+fl.h(B(a));return t(b)?b:80}}(c,d,e,f,h,k,l,p)),u=yp(function(){return function(){var b=no.h(B(a));return t(b)?b:24}}(c,d,e,f,h,k,l,p,m)),w=yp(function(){return function(){return wk.h(B(a))}}(c,d,e,f,h,k,l,p,m,u)),x=yp(function(){return function(){return V.h(B(a))}}(c,d,e,f,h,k,l,p,m,u,w)),C=yp(function(){return function(){return ml.h(B(a))}}(c,d,e,f,h,k,l,p,m,u,w,x)),F=yp(function(){return function(){return jn.h(B(a))}}(c,d,e,f,h,k,l,p,m,u,w,x,C)),I=yp(function(){return function(){return Uj.h(B(a))}}(c,
+d,e,f,h,k,l,p,m,u,w,x,C,F)),M=yp(function(){return function(){return wl.h(B(a))}}(c,d,e,f,h,k,l,p,m,u,w,x,C,F,I)),S=B(a),X=null!=S&&(S.m&64||q===S.G)?P(U,S):S,Ga=D.c(X,ki),db=D.c(X,li),Q=D.c(X,mi),xb=D.c(X,ni);return function(a,c,d,e,f,h,k,l,m,p,u,w,x,C,F,I,M,S,Q,X,Ga,db){return function(){return new R(null,3,5,T,[Cn,new r(null,5,[Jj,-1,Zj,c,Rn,d,Vm,a,vn,B(k)],null),new R(null,7,5,T,[Sm,new r(null,1,[vn,B(l)],null),new R(null,6,5,T,[rA,m,p,u,w,x],null),new R(null,5,5,T,[DA,C,F,I,b],null),t(t(Q)?Q:
+X)?new R(null,5,5,T,[LA,Q,X,Ga,db],null):null,t(B(h))?null:new R(null,2,5,T,[EA,b],null),t(B(e))?new R(null,1,5,T,[FA],null):null],null)],null)}}(c,d,e,f,h,k,l,p,m,u,w,x,C,F,I,M,S,X,Ga,db,Q,xb)}
+function PA(a){var b=Kx(null),c=Kx(new dx(bx(1),1));return function(b,c){return function(){return Pp(new r(null,4,[ln,"asciinema-player",Dm,function(b,c){return function(){return OA(a,b,c)}}(b,c),$k,function(b,c){return function(){var d=ty(Gl.h(B(a))),e=MA(c);Tx(e,b);return NA(a,Je([b,d]))}}(b,c),Wm,function(){return function(){return uy(Gl.h(B(a)))}}(b,c)],null))}}(b,c)};function QA(a,b){var c=null!=b&&(b.m&64||q===b.G)?P(U,b):b,d=D.c(c,Ak),e=D.c(c,Gl);d=a.h?a.h(d):a.call(null,d);zy(e,d);return K.l(c,Ak,d)}$x.prototype.sb=q;$x.prototype.qb=function(a,b){var c=null!=b&&(b.m&64||q===b.G)?P(U,b):b,d=D.c(c,Uj),e=D.c(c,wl),f=D.c(c,Gl);t(e)&&yy(f,Nu(d+5,e));return c};ay.prototype.sb=q;ay.prototype.qb=function(a,b){var c=null!=b&&(b.m&64||q===b.G)?P(U,b):b,d=D.c(c,Uj),e=D.c(c,wl),f=D.c(c,Gl);t(e)&&yy(f,Nu(d+-5,e));return c};by.prototype.sb=q;
+by.prototype.qb=function(a,b){var c=null!=b&&(b.m&64||q===b.G)?P(U,b):b,d=D.c(c,wl),e=D.c(c,Gl);t(d)&&(d*=nn.h(this),yy(e,d));return c};dy.prototype.sb=q;dy.prototype.qb=function(a,b){return QA(function(){return function(a){return a/2}}(this),b)};ey.prototype.sb=q;ey.prototype.qb=function(a,b){return QA(function(){return function(a){return 2*a}}(this),b)};fy.prototype.sb=q;fy.prototype.qb=function(a,b){xy(Gl.h(b));return b};gy.prototype.sb=q;gy.prototype.qb=function(a,b){return K.l(b,ml,so.h(this))};
+hy.prototype.sb=q;hy.prototype.qb=function(a,b){return K.l(b,Gk,so.h(this))};jy.prototype.sb=q;jy.prototype.qb=function(a,b){var c=null!=a&&(a.m&64||q===a.G)?P(U,a):a;D.c(c,fl);D.c(c,no);D.c(c,wl);c=null!=b&&(b.m&64||q===b.G)?P(U,b):b;var d=D.c(c,fl),e=D.c(c,no),f=null!=this&&(this.m&64||q===this.G)?P(U,this):this,h=D.c(f,fl),k=D.c(f,no);f=D.c(f,wl);return K.A(c,fl,t(d)?d:h,be([no,t(e)?e:k,wl,f]))};ky.prototype.sb=q;ky.prototype.qb=function(a,b){return K.l(b,Hm,Hm.h(this))};oy.prototype.sb=q;
+oy.prototype.qb=function(a,b){var c=null!=b&&(b.m&64||q===b.G)?P(U,b):b,d=D.c(c,oi);t(d)&&(ap(bp),d.B?d.B():d.call(null));return c};ry.prototype.sb=q;ry.prototype.qb=function(a,b){return K.l(b,Uj,Zk.h(this))};function RA(){return ig.l(function(a,b){return new R(null,2,5,T,[a,new gy(b,null,null,null)],null)},rg(function(a){return a+.5},.5),og(new R(null,2,5,T,[!1,!0],null)))}function SA(a){var b=Dy(RA());return K.l(K.l(a,ml,!0),Ol,b)}
+function TA(a){a=null!=a&&(a.m&64||q===a.G)?P(U,a):a;var b=D.c(a,Ol);Tw(b);return K.l(K.l(a,ml,!0),Ol,null)}function UA(a){a=null!=a&&(a.m&64||q===a.G)?P(U,a):a;a=D.c(a,Ol);return t(a)?Je([a]):vi}my.prototype.sb=q;
+my.prototype.qb=function(a,b){var c=null!=a&&(a.m&64||q===a.G)?P(U,a):a;D.c(c,jn);var d=null!=b&&(b.m&64||q===b.G)?P(U,b):b,e=D.c(d,jn);c=D.c(d,pi);var f=D.c(d,qi),h=null!=this&&(this.m&64||q===this.G)?P(U,this):this;h=D.c(h,jn);if(G.c(e,h))return d;d=K.A(d,jn,h,be([el,!0]));if(t(h))return t(c)&&(c.B?c.B():c.call(null)),SA(d);t(f)&&(f.B?f.B():f.call(null));return TA(d)};my.prototype.Fe=q;my.prototype.de=function(a,b){return UA(b)};py.prototype.sb=q;
+py.prototype.qb=function(a,b){var c=K.l(b,V,V.h(this));c=null!=c&&(c.m&64||q===c.G)?P(U,c):c;var d=D.c(c,Ol);return t(d)?SA(TA(c)):c};py.prototype.Fe=q;py.prototype.de=function(a,b){return UA(b)};function VA(a){return t(a)?(a=ig.c(parseFloat,Fo(""+v.h(a),/:/)),a=ig.l(Ye,cf(a),rg(function(){return function(a){return 60*a}}(a),1)),P(Xe,a)):null}
+function WA(a,b,c){t(a)?"string"===typeof a?t(0===a.indexOf("data:application/json;base64,"))?(b=a.substring(29).replace(RegExp("\\s","g"),""),b=JSON.parse(atob(b)),b=fj(b),b=new r(null,1,[V,new r(null,1,[il,b],null)],null)):t(0===a.indexOf("data:text/plain,"))?(a=a.substring(16),b=Ju(Ot(t(b)?b:80,t(c)?c:24),a),b=new r(null,1,[V,b],null)):b=t(0===a.indexOf("npt:"))?new r(null,1,[Zk,VA(a.substring(4))],null):null:b=new r(null,1,[V,new r(null,1,[il,a],null)],null):b=null;return b}
+var XA=new r(null,2,[pl,new r(null,1,[On,!1],null),il,he],null);
+function YA(a,b){var c=null!=b&&(b.m&64||q===b.G)?P(U,b):b,d=D.c(c,no),e=D.l(c,wk,"small"),f=D.l(c,Ak,1),h=D.c(c,Hk),k=D.c(c,fl),l=D.c(c,rl),p=D.l(c,cm,!1),m=D.l(c,gm,"asciinema"),u=D.c(c,qm),w=D.c(c,Bm),x=D.l(c,vm,!1),C=D.l(c,Em,!1),F=function(){var a=VA(h);return t(a)?a:0}();w=WA(w,k,d);var I=null!=w&&(w.m&64||q===w.G)?P(U,w):w;w=D.c(I,V);I=D.c(I,Zk);var M=t(I)?I:wb(w)&&0<F?F:null;I=function(){var b=Pe([Ak,Hk,fl,rl,cm,qm,vm,Em,Mm,no],[f,F,k,l,p,u,x,C,M,d]);return xj.c?xj.c(a,b):xj.call(null,a,b)}();
+return hi.A(be([Pe([Uj,V,wk,Ak,Gk,el,fl,wl,Gl,Ol,gm,Hm,jn,no],[F,t(w)?w:XA,e,f,!1,!1,k,null,I,null,m,!1,!1,d]),ji(c)]))}function ZA(a,b,c){a="string"===typeof a?document.getElementById(a):a;b=tp.h(P(YA,be([b,c])));c=new R(null,2,5,T,[PA,b],null);qq?oq(c,a,null):pq.call(null,c,a);return b}
+ib=function(){function a(a){var c=null;if(0<arguments.length){c=0;for(var e=Array(arguments.length-0);c<e.length;)e[c]=arguments[c+0],++c;c=new Jb(e,0,null)}return b.call(this,c)}function b(a){return console.log.apply(console,Lb(a))}a.L=0;a.N=function(a){a=E(a);return b(a)};a.A=b;return a}();
+kb=function(){function a(a){var c=null;if(0<arguments.length){c=0;for(var e=Array(arguments.length-0);c<e.length;)e[c]=arguments[c+0],++c;c=new Jb(e,0,null)}return b.call(this,c)}function b(a){return console.error.apply(console,Lb(a))}a.L=0;a.N=function(a){a=E(a);return b(a)};a.A=b;return a}();var $A=function $A(a){switch(arguments.length){case 2:return $A.c(arguments[0],arguments[1]);case 3:return $A.l(arguments[0],arguments[1],arguments[2]);default:throw Error(["Invalid arity: ",v.h(arguments.length)].join(""));}};da("asciinema.player.js.CreatePlayer",$A);$A.c=function(a,b){return $A.l(a,b,Ef)};
+$A.l=function(a,b,c){b=fj(b);c=yo(fj(c));a=ZA(a,b,c);return cj(new r(null,5,[En,function(a,b,c){return function(){return Uj.h(B(c))}}(b,c,a),Bj,function(a,b,c){return function(a){var b=B(c);b=null!=b&&(b.m&64||q===b.G)?P(U,b):b;D.c(b,wl);b=D.c(b,Gl);return yy(b,a)}}(b,c,a),Zm,function(a,b,c){return function(){return wl.h(B(c))}}(b,c,a),Jn,function(a,b,c){return function(){var a=B(c);a=null!=a&&(a.m&64||q===a.G)?P(U,a):a;a=D.c(a,Gl);return vy(a)}}(b,c,a),sn,function(a,b,c){return function(){var a=
+B(c);a=null!=a&&(a.m&64||q===a.G)?P(U,a):a;a=D.c(a,Gl);return wy(a)}}(b,c,a)],null))};$A.L=3;da("asciinema.player.js.UnmountPlayer",function(a){a="string"===typeof a?document.getElementById(a):a;gg.l(lq,le,a);return kq().unmountComponentAtNode(a)});registerAsciinemaPlayerElement();
})();
+
+//# sourceMappingURL=asciinema-player.js.map
+
diff --git a/public_html/index.php b/public_html/index.php
index 37526d903..930737eb1 100644
--- a/public_html/index.php
+++ b/public_html/index.php
@@ -1,4 +1,40 @@
<?php
+/**
+ * CodeIgniter
+ *
+ * An open source application development framework for PHP
+ *
+ * This content is released under the MIT License (MIT)
+ *
+ * Copyright (c) 2014 - 2019, British Columbia Institute of Technology
+ *
+ * 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.
+ *
+ * @package CodeIgniter
+ * @author EllisLab Dev Team
+ * @copyright Copyright (c) 2008 - 2014, EllisLab, Inc. (https://ellislab.com/)
+ * @copyright Copyright (c) 2014 - 2019, British Columbia Institute of Technology (https://bcit.ca/)
+ * @license https://opensource.org/licenses/MIT MIT License
+ * @link https://codeigniter.com
+ * @since Version 1.0.0
+ * @filesource
+ */
/*
*---------------------------------------------------------------
@@ -16,7 +52,6 @@
* production
*
* NOTE: If you change these, also change the error_reporting() code below
- *
*/
define('ENVIRONMENT', 'development');
/*
@@ -27,75 +62,98 @@
* Different environments will require different levels of error reporting.
* By default development will show errors but testing and live will hide them.
*/
-
-if (false && defined('ENVIRONMENT'))
+if (false) {
+switch (ENVIRONMENT)
{
- switch (ENVIRONMENT)
- {
- case 'development':
- error_reporting(E_ALL);
- break;
-
- case 'testing':
- case 'production':
- error_reporting(0);
- break;
-
- default:
- exit('The application environment is not set correctly.');
- }
+ case 'development':
+ error_reporting(-1);
+ ini_set('display_errors', 1);
+ break;
+
+ case 'testing':
+ case 'production':
+ ini_set('display_errors', 0);
+ if (version_compare(PHP_VERSION, '5.3', '>='))
+ {
+ error_reporting(E_ALL & ~E_NOTICE & ~E_DEPRECATED & ~E_STRICT & ~E_USER_NOTICE & ~E_USER_DEPRECATED);
+ }
+ else
+ {
+ error_reporting(E_ALL & ~E_NOTICE & ~E_STRICT & ~E_USER_NOTICE);
+ }
+ break;
+
+ default:
+ header('HTTP/1.1 503 Service Unavailable.', TRUE, 503);
+ echo 'The application environment is not set correctly.';
+ exit(1); // EXIT_ERROR
+}
}
/*
*---------------------------------------------------------------
- * SYSTEM FOLDER NAME
+ * SYSTEM DIRECTORY NAME
*---------------------------------------------------------------
*
- * This variable must contain the name of your "system" folder.
- * Include the path if the folder is not in the same directory
- * as this file.
- *
+ * This variable must contain the name of your "system" directory.
+ * Set the path if it is not in the same directory as this file.
*/
$system_path = dirname(__FILE__).'/../system';
/*
*---------------------------------------------------------------
- * APPLICATION FOLDER NAME
+ * APPLICATION DIRECTORY NAME
*---------------------------------------------------------------
*
* If you want this front controller to use a different "application"
- * folder then the default one you can set its name here. The folder
- * can also be renamed or relocated anywhere on your server. If
- * you do, use a full server path. For more info please see the user guide:
- * http://codeigniter.com/user_guide/general/managing_apps.html
+ * directory than the default one you can set its name here. The directory
+ * can also be renamed or relocated anywhere on your server. If you do,
+ * use an absolute (full) server path.
+ * For more info please see the user guide:
*
- * NO TRAILING SLASH!
+ * https://codeigniter.com/user_guide/general/managing_apps.html
*
+ * NO TRAILING SLASH!
*/
$application_folder = dirname(__FILE__).'/../application';
/*
+ *---------------------------------------------------------------
+ * VIEW DIRECTORY NAME
+ *---------------------------------------------------------------
+ *
+ * If you want to move the view directory out of the application
+ * directory, set the path to it here. The directory can be renamed
+ * and relocated anywhere on your server. If blank, it will default
+ * to the standard location inside your application directory.
+ * If you do move this, use an absolute (full) server path.
+ *
+ * NO TRAILING SLASH!
+ */
+ $view_folder = '';
+
+
+/*
* --------------------------------------------------------------------
* DEFAULT CONTROLLER
* --------------------------------------------------------------------
*
* Normally you will set your default controller in the routes.php file.
* You can, however, force a custom routing by hard-coding a
- * specific controller class/function here. For most applications, you
+ * specific controller class/function here. For most applications, you
* WILL NOT set your routing here, but it's an option for those
* special instances where you might want to override the standard
* routing in a specific front controller that shares a common CI installation.
*
- * IMPORTANT: If you set the routing here, NO OTHER controller will be
+ * IMPORTANT: If you set the routing here, NO OTHER controller will be
* callable. In essence, this preference limits your application to ONE
- * specific controller. Leave the function name blank if you need
+ * specific controller. Leave the function name blank if you need
* to call functions dynamically via the URI.
*
* Un-comment the $routing array below to use this feature
- *
*/
- // The directory name, relative to the "controllers" folder. Leave blank
- // if your controller is not in a sub-folder within the "controllers" folder
+ // The directory name, relative to the "controllers" directory. Leave blank
+ // if your controller is not in a sub-directory within the "controllers" one
// $routing['directory'] = '';
// The controller class file name. Example: Mycontroller
@@ -118,7 +176,6 @@ if (false && defined('ENVIRONMENT'))
* config values.
*
* Un-comment the $assign_to_config array below to use this feature
- *
*/
// $assign_to_config['name_of_config_item'] = 'value of config item';
@@ -140,18 +197,26 @@ if (false && defined('ENVIRONMENT'))
chdir(dirname(__FILE__).'/..');
}
- if (realpath($system_path) !== FALSE)
+ if (($_temp = realpath($system_path)) !== FALSE)
{
- $system_path = realpath($system_path).'/';
+ $system_path = $_temp.DIRECTORY_SEPARATOR;
+ }
+ else
+ {
+ // Ensure there's a trailing slash
+ $system_path = strtr(
+ rtrim($system_path, '/\\'),
+ '/\\',
+ DIRECTORY_SEPARATOR.DIRECTORY_SEPARATOR
+ ).DIRECTORY_SEPARATOR;
}
-
- // ensure there's a trailing slash
- $system_path = rtrim($system_path, '/').'/';
// Is the system path correct?
if ( ! is_dir($system_path))
{
- exit("Your system folder path does not appear to be set correctly. Please open the following file and correct this: ".pathinfo(__FILE__, PATHINFO_BASENAME));
+ header('HTTP/1.1 503 Service Unavailable.', TRUE, 503);
+ echo 'Your system folder path does not appear to be set correctly. Please open the following file and correct this: '.pathinfo(__FILE__, PATHINFO_BASENAME);
+ exit(3); // EXIT_CONFIG
}
/*
@@ -162,35 +227,85 @@ if (false && defined('ENVIRONMENT'))
// The name of THIS file
define('SELF', pathinfo(__FILE__, PATHINFO_BASENAME));
- // The PHP file extension
- // this global constant is deprecated.
- define('EXT', '.php');
+ // Path to the system directory
+ define('BASEPATH', $system_path);
- // Path to the system folder
- define('BASEPATH', str_replace("\\", "/", $system_path));
+ // Path to the front controller (this file) directory
+ define('FCPATH', dirname(__FILE__).DIRECTORY_SEPARATOR."..".DIRECTORY_SEPARATOR);
- // Path to the front controller (this file)
- define('FCPATH', str_replace(SELF, '', __FILE__).'/../');
+ // Name of the "system" directory
+ define('SYSDIR', basename(BASEPATH));
- // Name of the "system folder"
- define('SYSDIR', trim(strrchr(trim(BASEPATH, '/'), '/'), '/'));
-
-
- // The path to the "application" folder
+ // The path to the "application" directory
if (is_dir($application_folder))
{
- define('APPPATH', $application_folder.'/');
+ if (($_temp = realpath($application_folder)) !== FALSE)
+ {
+ $application_folder = $_temp;
+ }
+ else
+ {
+ $application_folder = strtr(
+ rtrim($application_folder, '/\\'),
+ '/\\',
+ DIRECTORY_SEPARATOR.DIRECTORY_SEPARATOR
+ );
+ }
+ }
+ elseif (is_dir(BASEPATH.$application_folder.DIRECTORY_SEPARATOR))
+ {
+ $application_folder = BASEPATH.strtr(
+ trim($application_folder, '/\\'),
+ '/\\',
+ DIRECTORY_SEPARATOR.DIRECTORY_SEPARATOR
+ );
}
else
{
- if ( ! is_dir(BASEPATH.$application_folder.'/'))
+ header('HTTP/1.1 503 Service Unavailable.', TRUE, 503);
+ echo 'Your application folder path does not appear to be set correctly. Please open the following file and correct this: '.SELF;
+ exit(3); // EXIT_CONFIG
+ }
+
+ define('APPPATH', $application_folder.DIRECTORY_SEPARATOR);
+
+ // The path to the "views" directory
+ if ( ! isset($view_folder[0]) && is_dir(APPPATH.'views'.DIRECTORY_SEPARATOR))
+ {
+ $view_folder = APPPATH.'views';
+ }
+ elseif (is_dir($view_folder))
+ {
+ if (($_temp = realpath($view_folder)) !== FALSE)
{
- exit("Your application folder path does not appear to be set correctly. Please open the following file and correct this: ".SELF);
+ $view_folder = $_temp;
}
-
- define('APPPATH', BASEPATH.$application_folder.'/');
+ else
+ {
+ $view_folder = strtr(
+ rtrim($view_folder, '/\\'),
+ '/\\',
+ DIRECTORY_SEPARATOR.DIRECTORY_SEPARATOR
+ );
+ }
+ }
+ elseif (is_dir(APPPATH.$view_folder.DIRECTORY_SEPARATOR))
+ {
+ $view_folder = APPPATH.strtr(
+ trim($view_folder, '/\\'),
+ '/\\',
+ DIRECTORY_SEPARATOR.DIRECTORY_SEPARATOR
+ );
+ }
+ else
+ {
+ header('HTTP/1.1 503 Service Unavailable.', TRUE, 503);
+ echo 'Your view folder path does not appear to be set correctly. Please open the following file and correct this: '.SELF;
+ exit(3); // EXIT_CONFIG
}
+ define('VIEWPATH', $view_folder.DIRECTORY_SEPARATOR);
+
if (getenv("HOME") == "") {
putenv('HOME='.FCPATH);
}
@@ -202,11 +317,15 @@ if (false && defined('ENVIRONMENT'))
require APPPATH.'libraries/ExceptionHandler.php';
\libraries\ExceptionHandler::setup();
-// wrapper for CI so that it calls our handler rather than it's own
-function _exception_handler($severity, $message, $filepath, $line) {
+// wrapper for CI so that it calls our handlers rather than it's own
+function _error_handler($severity, $message, $filepath, $line) {
return \libraries\ExceptionHandler::error_handler($severity, $message, $filepath, $line);
}
+function _exception_handler($ex) {
+ return \libraries\ExceptionHandler::exception_handler($ex);
+}
+
// Source: http://stackoverflow.com/a/15875555
function guidv4()
{
@@ -225,7 +344,6 @@ function guidv4()
* --------------------------------------------------------------------
*
* And away we go...
- *
*/
$testname = null;
@@ -241,11 +359,11 @@ if (getenv("ENVIRONMENT") === "testsuite" && getenv("COLLECT_COVERAGE") == 1) {
if ($testname) {
include APPPATH."../vendor/autoload.php";
$filter = new \SebastianBergmann\CodeCoverage\Filter();
- $filter->addDirectoryToWhitelist(APPPATH);
- $filter->removeDirectoryFromWhitelist(APPPATH."/third_party/");
+ $filter->includeDirectory(APPPATH);
+ $filter->excludeDirectory(APPPATH."/third_party/");
// Force phpdbg for speed
//$driver = new \SebastianBergmann\CodeCoverage\Driver\PHPDBG();
- $driver = null;
+ $driver = (new \SebastianBergmann\CodeCoverage\Driver\Selector)->forLineCoverage($filter);
$coverage = new \SebastianBergmann\CodeCoverage\CodeCoverage($driver, $filter);
$coverage->start($testname);
}
@@ -260,6 +378,9 @@ try {
}
redirect("user/login?redirect_uri=".$redirect_uri);
} catch (\exceptions\PublicApiException $e) {
+ if ($e->get_http_error_code() == 500) {
+ \libraries\ExceptionHandler::log_exception($e);
+ }
show_error(nl2br(htmlspecialchars($e->__toString())), $e->get_http_error_code());
} finally {
if ($testname) {
@@ -269,5 +390,3 @@ try {
}
}
-/* End of file index.php */
-/* Location: ./index.php */
diff --git a/run-tests.sh b/run-tests.sh
index 89db85ee9..166aacd3f 100755
--- a/run-tests.sh
+++ b/run-tests.sh
@@ -6,7 +6,12 @@
export ENVIRONMENT="testsuite"
export COLLECT_COVERAGE=1
+if [[ $COLLECT_COVERAGE = 1 ]]; then
+ export XDEBUG_MODE=coverage
+fi
+
startdir="$(dirname "$0")"
+datadir="testsuite-tmp"
die() {
echo "$@" >&2
@@ -28,9 +33,13 @@ trap cleanup EXIT INT
cleanup() {
pkill -P $$
php index.php tools drop_all_tables
+ rm -rf "$datadir"
}
+rm -rf "$datadir"
+
mkdir -p test-coverage-data
+mkdir -p "$datadir"
php=(php)
if ((COLLECT_COVERAGE)); then
diff --git a/scripts/get_lexer_list.py b/scripts/get_lexer_list.py
index 1392459a8..e7636b46d 100755
--- a/scripts/get_lexer_list.py
+++ b/scripts/get_lexer_list.py
@@ -1,4 +1,4 @@
-#!/usr/bin/env python
+#!/usr/bin/env python3
import pygments.lexers
import json
diff --git a/system/.htaccess b/system/.htaccess
index 14249c50b..97c65d2df 100644
--- a/system/.htaccess
+++ b/system/.htaccess
@@ -1 +1,6 @@
-Deny from all \ No newline at end of file
+<IfModule authz_core_module>
+ Require all denied
+</IfModule>
+<IfModule !authz_core_module>
+ Deny from all
+</IfModule> \ No newline at end of file
diff --git a/system/core/Benchmark.php b/system/core/Benchmark.php
index a5c3e999b..20ac2f558 100644
--- a/system/core/Benchmark.php
+++ b/system/core/Benchmark.php
@@ -1,61 +1,83 @@
-<?php if ( ! defined('BASEPATH')) exit('No direct script access allowed');
+<?php
/**
* CodeIgniter
*
- * An open source application development framework for PHP 5.1.6 or newer
+ * An open source application development framework for PHP
*
- * @package CodeIgniter
- * @author ExpressionEngine Dev Team
- * @copyright Copyright (c) 2008 - 2014, EllisLab, Inc.
- * @license http://codeigniter.com/user_guide/license.html
- * @link http://codeigniter.com
- * @since Version 1.0
+ * This content is released under the MIT License (MIT)
+ *
+ * Copyright (c) 2019 - 2022, CodeIgniter Foundation
+ *
+ * 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.
+ *
+ * @package CodeIgniter
+ * @author EllisLab Dev Team
+ * @copyright Copyright (c) 2008 - 2014, EllisLab, Inc. (https://ellislab.com/)
+ * @copyright Copyright (c) 2014 - 2019, British Columbia Institute of Technology (https://bcit.ca/)
+ * @copyright Copyright (c) 2019 - 2022, CodeIgniter Foundation (https://codeigniter.com/)
+ * @license https://opensource.org/licenses/MIT MIT License
+ * @link https://codeigniter.com
+ * @since Version 1.0.0
* @filesource
*/
-
-// ------------------------------------------------------------------------
+defined('BASEPATH') OR exit('No direct script access allowed');
/**
- * CodeIgniter Benchmark Class
+ * Benchmark Class
*
* This class enables you to mark points and calculate the time difference
- * between them. Memory consumption can also be displayed.
+ * between them. Memory consumption can also be displayed.
*
* @package CodeIgniter
* @subpackage Libraries
* @category Libraries
- * @author ExpressionEngine Dev Team
- * @link http://codeigniter.com/user_guide/libraries/benchmark.html
+ * @author EllisLab Dev Team
+ * @link https://codeigniter.com/userguide3/libraries/benchmark.html
*/
class CI_Benchmark {
/**
- * List of all benchmark markers and when they were added
+ * List of all benchmark markers
*
- * @var array
+ * @var array
*/
- var $marker = array();
-
- // --------------------------------------------------------------------
+ public $marker = array();
/**
* Set a benchmark marker
*
* Multiple calls to this function can be made so that several
- * execution points can be timed
+ * execution points can be timed.
*
- * @access public
- * @param string $name name of the marker
+ * @param string $name Marker name
* @return void
*/
- function mark($name)
+ public function mark($name)
{
- $this->marker[$name] = microtime();
+ $this->marker[$name] = microtime(TRUE);
}
// --------------------------------------------------------------------
/**
+ * Elapsed time
+ *
* Calculates the time difference between two marked points.
*
* If the first parameter is empty this function instead returns the
@@ -63,15 +85,17 @@ class CI_Benchmark {
* execution time to be shown in a template. The output class will
* swap the real value for this variable.
*
- * @access public
- * @param string a particular marked point
- * @param string a particular marked point
- * @param integer the number of decimal places
- * @return mixed
+ * @param string $point1 A particular marked point
+ * @param string $point2 A particular marked point
+ * @param int $decimals Number of decimal places
+ *
+ * @return string Calculated elapsed time on success,
+ * an '{elapsed_string}' if $point1 is empty
+ * or an empty string if $point1 is not found.
*/
- function elapsed_time($point1 = '', $point2 = '', $decimals = 4)
+ public function elapsed_time($point1 = '', $point2 = '', $decimals = 4)
{
- if ($point1 == '')
+ if ($point1 === '')
{
return '{elapsed_time}';
}
@@ -83,13 +107,10 @@ class CI_Benchmark {
if ( ! isset($this->marker[$point2]))
{
- $this->marker[$point2] = microtime();
+ $this->marker[$point2] = microtime(TRUE);
}
- list($sm, $ss) = explode(' ', $this->marker[$point1]);
- list($em, $es) = explode(' ', $this->marker[$point2]);
-
- return number_format(($em + $es) - ($sm + $ss), $decimals);
+ return number_format($this->marker[$point2] - $this->marker[$point1], $decimals);
}
// --------------------------------------------------------------------
@@ -97,22 +118,17 @@ class CI_Benchmark {
/**
* Memory Usage
*
- * This function returns the {memory_usage} pseudo-variable.
+ * Simply returns the {memory_usage} marker.
+ *
* This permits it to be put it anywhere in a template
* without the memory being calculated until the end.
* The output class will swap the real value for this variable.
*
- * @access public
- * @return string
+ * @return string '{memory_usage}'
*/
- function memory_usage()
+ public function memory_usage()
{
return '{memory_usage}';
}
}
-
-// END CI_Benchmark class
-
-/* End of file Benchmark.php */
-/* Location: ./system/core/Benchmark.php */ \ No newline at end of file
diff --git a/system/core/CodeIgniter.php b/system/core/CodeIgniter.php
index 34078174a..87dd868f9 100644
--- a/system/core/CodeIgniter.php
+++ b/system/core/CodeIgniter.php
@@ -1,19 +1,42 @@
-<?php if ( ! defined('BASEPATH')) exit('No direct script access allowed');
+<?php
/**
* CodeIgniter
*
- * An open source application development framework for PHP 5.1.6 or newer
+ * An open source application development framework for PHP
*
- * @package CodeIgniter
- * @author ExpressionEngine Dev Team
- * @copyright Copyright (c) 2008 - 2014, EllisLab, Inc.
- * @license http://codeigniter.com/user_guide/license.html
- * @link http://codeigniter.com
- * @since Version 1.0
+ * This content is released under the MIT License (MIT)
+ *
+ * Copyright (c) 2019 - 2022, CodeIgniter Foundation
+ *
+ * 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.
+ *
+ * @package CodeIgniter
+ * @author EllisLab Dev Team
+ * @copyright Copyright (c) 2008 - 2014, EllisLab, Inc. (https://ellislab.com/)
+ * @copyright Copyright (c) 2014 - 2019, British Columbia Institute of Technology (https://bcit.ca/)
+ * @copyright Copyright (c) 2019 - 2022, CodeIgniter Foundation (https://codeigniter.com/)
+ * @license https://opensource.org/licenses/MIT MIT License
+ * @link https://codeigniter.com
+ * @since Version 1.0.0
* @filesource
*/
-
-// ------------------------------------------------------------------------
+defined('BASEPATH') OR exit('No direct script access allowed');
/**
* System Initialization File
@@ -21,60 +44,50 @@
* Loads the base classes and executes the request.
*
* @package CodeIgniter
- * @subpackage codeigniter
+ * @subpackage CodeIgniter
* @category Front-controller
- * @author ExpressionEngine Dev Team
- * @link http://codeigniter.com/user_guide/
+ * @author EllisLab Dev Team
+ * @link https://codeigniter.com/userguide3/
*/
/**
* CodeIgniter Version
*
- * @var string
- *
- */
- define('CI_VERSION', '2.2.0');
-
-/**
- * CodeIgniter Branch (Core = TRUE, Reactor = FALSE)
- *
- * @var boolean
+ * @var string
*
*/
- define('CI_CORE', FALSE);
-
-/*
- * ------------------------------------------------------
- * Load the global functions
- * ------------------------------------------------------
- */
- require(BASEPATH.'core/Common.php');
+ const CI_VERSION = '3.2.0-dev';
/*
* ------------------------------------------------------
* Load the framework constants
* ------------------------------------------------------
*/
- if (defined('ENVIRONMENT') AND file_exists(APPPATH.'config/'.ENVIRONMENT.'/constants.php'))
+ if (file_exists(APPPATH.'config/'.ENVIRONMENT.'/constants.php'))
{
- require(APPPATH.'config/'.ENVIRONMENT.'/constants.php');
+ require_once(APPPATH.'config/'.ENVIRONMENT.'/constants.php');
}
- else
+
+ if (file_exists(APPPATH.'config/constants.php'))
{
- require(APPPATH.'config/constants.php');
+ require_once(APPPATH.'config/constants.php');
}
/*
* ------------------------------------------------------
- * Define a custom error handler so we can log PHP errors
+ * Load the global functions
* ------------------------------------------------------
*/
- set_error_handler('_exception_handler');
+ require_once(BASEPATH.'core/Common.php');
- if ( ! is_php('5.3'))
- {
- @set_magic_quotes_runtime(0); // Kill magic quotes
- }
+/*
+ * ------------------------------------------------------
+ * Define a custom error handler so we can log PHP errors
+ * ------------------------------------------------------
+ */
+ set_error_handler('_error_handler');
+ set_exception_handler('_exception_handler');
+ register_shutdown_function('_shutdown_handler');
/*
* ------------------------------------------------------
@@ -85,26 +98,39 @@
* The subclass prefix allows CI to know if a core class is
* being extended via a library in the local application
* "libraries" folder. Since CI allows config items to be
- * overriden via data set in the main index. php file,
+ * overridden via data set in the main index.php file,
* before proceeding we need to know if a subclass_prefix
- * override exists. If so, we will set this value now,
+ * override exists. If so, we will set this value now,
* before any classes are loaded
* Note: Since the config file data is cached it doesn't
* hurt to load it here.
*/
- if (isset($assign_to_config['subclass_prefix']) AND $assign_to_config['subclass_prefix'] != '')
+ if ( ! empty($assign_to_config['subclass_prefix']))
{
get_config(array('subclass_prefix' => $assign_to_config['subclass_prefix']));
}
/*
* ------------------------------------------------------
- * Set a liberal script execution time limit
+ * Should we use a Composer autoloader?
* ------------------------------------------------------
*/
- if (function_exists("set_time_limit") == TRUE AND @ini_get("safe_mode") == 0)
+ if ($composer_autoload = config_item('composer_autoload'))
{
- @set_time_limit(300);
+ if ($composer_autoload === TRUE)
+ {
+ file_exists(APPPATH.'vendor/autoload.php')
+ ? require_once(APPPATH.'vendor/autoload.php')
+ : log_message('error', '$config[\'composer_autoload\'] is set to TRUE but '.APPPATH.'vendor/autoload.php was not found.');
+ }
+ elseif (file_exists($composer_autoload))
+ {
+ require_once($composer_autoload);
+ }
+ else
+ {
+ log_message('error', 'Could not find the specified $config[\'composer_autoload\'] path: '.$composer_autoload);
+ }
}
/*
@@ -118,65 +144,121 @@
/*
* ------------------------------------------------------
+ * Instantiate the config class
+ * ------------------------------------------------------
+ *
+ * Note: It is important that Config is loaded first as
+ * most other classes depend on it either directly or by
+ * depending on another class that uses it.
+ *
+ */
+ $CFG =& load_class('Config', 'core');
+
+ // Do we have any manually set config items in the index.php file?
+ if (isset($assign_to_config) && is_array($assign_to_config))
+ {
+ foreach ($assign_to_config as $key => $value)
+ {
+ $CFG->set_item($key, $value);
+ }
+ }
+
+/*
+ * ------------------------------------------------------
* Instantiate the hooks class
* ------------------------------------------------------
*/
- $EXT =& load_class('Hooks', 'core');
+ $EXT =& load_class('Hooks', 'core', $CFG);
/*
* ------------------------------------------------------
* Is there a "pre_system" hook?
* ------------------------------------------------------
*/
- $EXT->_call_hook('pre_system');
+ $EXT->call_hook('pre_system');
/*
* ------------------------------------------------------
- * Instantiate the config class
+ * Important charset-related stuff
* ------------------------------------------------------
+ *
+ * Configure mbstring and/or iconv if they are enabled
+ * and set MB_ENABLED and ICONV_ENABLED constants, so
+ * that we don't repeatedly do extension_loaded() or
+ * function_exists() calls.
+ *
+ * Note: UTF-8 class depends on this. It used to be done
+ * in it's constructor, but it's _not_ class-specific.
+ *
*/
- $CFG =& load_class('Config', 'core');
+ $charset = strtoupper(config_item('charset'));
+ ini_set('default_charset', $charset);
- // Do we have any manually set config items in the index.php file?
- if (isset($assign_to_config))
+ if (extension_loaded('mbstring'))
+ {
+ define('MB_ENABLED', TRUE);
+ // mbstring.internal_encoding is deprecated starting with PHP 5.6
+ // and it's usage triggers E_DEPRECATED messages.
+ @ini_set('mbstring.internal_encoding', $charset);
+ // This is required for mb_convert_encoding() to strip invalid characters.
+ // That's utilized by CI_Utf8, but it's also done for consistency with iconv.
+ mb_substitute_character('none');
+ }
+ else
{
- $CFG->_assign_to_config($assign_to_config);
+ define('MB_ENABLED', FALSE);
+ }
+
+ // There's an ICONV_IMPL constant, but the PHP manual says that using
+ // iconv's predefined constants is "strongly discouraged".
+ if (extension_loaded('iconv'))
+ {
+ define('ICONV_ENABLED', TRUE);
+ // iconv.internal_encoding is deprecated starting with PHP 5.6
+ // and it's usage triggers E_DEPRECATED messages.
+ @ini_set('iconv.internal_encoding', $charset);
+ }
+ else
+ {
+ define('ICONV_ENABLED', FALSE);
+ }
+
+ if (is_php('5.6'))
+ {
+ ini_set('php.internal_encoding', $charset);
}
/*
* ------------------------------------------------------
- * Instantiate the UTF-8 class
+ * Load compatibility features
* ------------------------------------------------------
- *
- * Note: Order here is rather important as the UTF-8
- * class needs to be used very early on, but it cannot
- * properly determine if UTf-8 can be supported until
- * after the Config class is instantiated.
- *
*/
- $UNI =& load_class('Utf8', 'core');
+ require_once(BASEPATH.'core/compat/mbstring.php');
+ require_once(BASEPATH.'core/compat/hash.php');
+ require_once(BASEPATH.'core/compat/password.php');
+ require_once(BASEPATH.'core/compat/standard.php');
+
+/*
+ * ------------------------------------------------------
+ * Instantiate the UTF-8 class
+ * ------------------------------------------------------
+ */
+ $UNI =& load_class('Utf8', 'core', $charset);
/*
* ------------------------------------------------------
* Instantiate the URI class
* ------------------------------------------------------
*/
- $URI =& load_class('URI', 'core');
+ $URI =& load_class('URI', 'core', $CFG);
/*
* ------------------------------------------------------
* Instantiate the routing class and set the routing
* ------------------------------------------------------
*/
- $RTR =& load_class('Router', 'core');
- $RTR->_set_routing();
-
- // Set any routing overrides that may exist in the main index file
- if (isset($routing))
- {
- $RTR->_set_overrides($routing);
- }
+ $RTR =& load_class('Router', 'core', isset($routing) ? $routing : NULL);
/*
* ------------------------------------------------------
@@ -187,15 +269,12 @@
/*
* ------------------------------------------------------
- * Is there a valid cache file? If so, we're done...
+ * Is there a valid cache file? If so, we're done...
* ------------------------------------------------------
*/
- if ($EXT->_call_hook('cache_override') === FALSE)
+ if ($EXT->call_hook('cache_override') === FALSE && $OUT->_display_cache($CFG, $URI) === TRUE)
{
- if ($OUT->_display_cache($CFG, $URI) == TRUE)
- {
- exit;
- }
+ exit;
}
/*
@@ -203,14 +282,14 @@
* Load the security class for xss and csrf support
* -----------------------------------------------------
*/
- $SEC =& load_class('Security', 'core');
+ $SEC =& load_class('Security', 'core', $charset);
/*
* ------------------------------------------------------
* Load the Input class and sanitize globals
* ------------------------------------------------------
*/
- $IN =& load_class('Input', 'core');
+ $IN =& load_class('Input', 'core', $SEC);
/*
* ------------------------------------------------------
@@ -226,76 +305,157 @@
*
*/
// Load the base controller class
- require BASEPATH.'core/Controller.php';
-
+ require_once BASEPATH.'core/Controller.php';
+
+ /**
+ * Reference to the CI_Controller method.
+ *
+ * Returns current CI instance object
+ *
+ * @return CI_Controller
+ */
function &get_instance()
{
return CI_Controller::get_instance();
}
-
if (file_exists(APPPATH.'core/'.$CFG->config['subclass_prefix'].'Controller.php'))
{
- require APPPATH.'core/'.$CFG->config['subclass_prefix'].'Controller.php';
+ require_once APPPATH.'core/'.$CFG->config['subclass_prefix'].'Controller.php';
}
- // Load the local application controller
- // Note: The Router class automatically validates the controller path using the router->_validate_request().
- // If this include fails it means that the default controller in the Routes.php file is not resolving to something valid.
- if ( ! file_exists(APPPATH.'controllers/'.$RTR->fetch_directory().$RTR->fetch_class().'.php'))
- {
- show_error('Unable to load your default controller. Please make sure the controller specified in your Routes.php file is valid.');
- }
-
- include(APPPATH.'controllers/'.$RTR->fetch_directory().$RTR->fetch_class().'.php');
-
// Set a mark point for benchmarking
$BM->mark('loading_time:_base_classes_end');
/*
* ------------------------------------------------------
- * Security check
+ * Sanity checks
* ------------------------------------------------------
*
- * None of the functions in the app controller or the
- * loader class can be called via the URI, nor can
- * controller functions that begin with an underscore
+ * The Router class has already validated the request,
+ * leaving us with 3 options here:
+ *
+ * 1) an empty class name, if we reached the default
+ * controller, but it didn't exist;
+ * 2) a query string which doesn't go through a
+ * file_exists() check
+ * 3) a regular request for a non-existing page
+ *
+ * We handle all of these as a 404 error.
+ *
+ * Furthermore, none of the methods in the app controller
+ * or the loader class can be called via the URI, nor can
+ * controller methods that begin with an underscore.
*/
- $class = $RTR->fetch_class();
- $method = $RTR->fetch_method();
- if ( ! class_exists($class)
- OR strncmp($method, '_', 1) == 0
- OR in_array(strtolower($method), array_map('strtolower', get_class_methods('CI_Controller')))
- )
+ $e404 = FALSE;
+ $class = ucfirst($RTR->class);
+ $method = $RTR->method;
+
+ if (empty($class) OR ! file_exists(APPPATH.'controllers/'.$RTR->directory.$class.'.php'))
+ {
+ $e404 = TRUE;
+ }
+ else
+ {
+ require_once(APPPATH.'controllers/'.$RTR->directory.$class.'.php');
+
+ if ( ! class_exists($class, FALSE) OR $method[0] === '_' OR method_exists('CI_Controller', $method))
+ {
+ $e404 = TRUE;
+ }
+ elseif (method_exists($class, '_remap'))
+ {
+ $params = array($method, array_slice($URI->rsegments, 2));
+ $method = '_remap';
+ }
+ elseif ( ! method_exists($class, $method))
+ {
+ $e404 = TRUE;
+ }
+ /**
+ * DO NOT CHANGE THIS, NOTHING ELSE WORKS!
+ *
+ * - method_exists() returns true for non-public methods, which passes the previous elseif
+ * - is_callable() returns false for PHP 4-style constructors, even if there's a __construct()
+ * - method_exists($class, '__construct') won't work because CI_Controller::__construct() is inherited
+ * - People will only complain if this doesn't work, even though it is documented that it shouldn't.
+ *
+ * ReflectionMethod::isConstructor() is the ONLY reliable check,
+ * knowing which method will be executed as a constructor.
+ */
+ else
+ {
+ $reflection = new ReflectionMethod($class, $method);
+ if ( ! $reflection->isPublic() OR $reflection->isConstructor())
+ {
+ $e404 = TRUE;
+ }
+ }
+ }
+
+ if ($e404)
{
if ( ! empty($RTR->routes['404_override']))
{
- $x = explode('/', $RTR->routes['404_override']);
- $class = $x[0];
- $method = (isset($x[1]) ? $x[1] : 'index');
- if ( ! class_exists($class))
+ if (sscanf($RTR->routes['404_override'], '%[^/]/%s', $error_class, $error_method) !== 2)
+ {
+ $error_method = 'index';
+ }
+
+ $error_class = ucfirst($error_class);
+
+ if ( ! class_exists($error_class, FALSE))
{
- if ( ! file_exists(APPPATH.'controllers/'.$class.'.php'))
+ if (file_exists(APPPATH.'controllers/'.$RTR->directory.$error_class.'.php'))
{
- show_404("{$class}/{$method}");
+ require_once(APPPATH.'controllers/'.$RTR->directory.$error_class.'.php');
+ $e404 = ! class_exists($error_class, FALSE);
+ }
+ // Were we in a directory? If so, check for a global override
+ elseif ( ! empty($RTR->directory) && file_exists(APPPATH.'controllers/'.$error_class.'.php'))
+ {
+ require_once(APPPATH.'controllers/'.$error_class.'.php');
+ if (($e404 = ! class_exists($error_class, FALSE)) === FALSE)
+ {
+ $RTR->directory = '';
+ }
}
-
- include_once(APPPATH.'controllers/'.$class.'.php');
}
+ else
+ {
+ $e404 = FALSE;
+ }
+ }
+
+ // Did we reset the $e404 flag? If so, set the rsegments, starting from index 1
+ if ( ! $e404)
+ {
+ $class = $error_class;
+ $method = $error_method;
+
+ $URI->rsegments = array(
+ 1 => $class,
+ 2 => $method
+ );
}
else
{
- show_404("{$class}/{$method}");
+ show_404($RTR->directory.$class.'/'.$method);
}
}
+ if ($method !== '_remap')
+ {
+ $params = array_slice($URI->rsegments, 2);
+ }
+
/*
* ------------------------------------------------------
* Is there a "pre_controller" hook?
* ------------------------------------------------------
*/
- $EXT->_call_hook('pre_controller');
+ $EXT->call_hook('pre_controller');
/*
* ------------------------------------------------------
@@ -312,53 +472,14 @@
* Is there a "post_controller_constructor" hook?
* ------------------------------------------------------
*/
- $EXT->_call_hook('post_controller_constructor');
+ $EXT->call_hook('post_controller_constructor');
/*
* ------------------------------------------------------
* Call the requested method
* ------------------------------------------------------
*/
- // Is there a "remap" function? If so, we call it instead
- if (method_exists($CI, '_remap'))
- {
- $CI->_remap($method, array_slice($URI->rsegments, 2));
- }
- else
- {
- // is_callable() returns TRUE on some versions of PHP 5 for private and protected
- // methods, so we'll use this workaround for consistent behavior
- if ( ! in_array(strtolower($method), array_map('strtolower', get_class_methods($CI))))
- {
- // Check and see if we are using a 404 override and use it.
- if ( ! empty($RTR->routes['404_override']))
- {
- $x = explode('/', $RTR->routes['404_override']);
- $class = $x[0];
- $method = (isset($x[1]) ? $x[1] : 'index');
- if ( ! class_exists($class))
- {
- if ( ! file_exists(APPPATH.'controllers/'.$class.'.php'))
- {
- show_404("{$class}/{$method}");
- }
-
- include_once(APPPATH.'controllers/'.$class.'.php');
- unset($CI);
- $CI = new $class();
- }
- }
- else
- {
- show_404("{$class}/{$method}");
- }
- }
-
- // Call the requested method.
- // Any URI segments present (besides the class/function) will be passed to the method for convenience
- call_user_func_array(array(&$CI, $method), array_slice($URI->rsegments, 2));
- }
-
+ call_user_func_array(array(&$CI, $method), $params);
// Mark a benchmark end point
$BM->mark('controller_execution_time_( '.$class.' / '.$method.' )_end');
@@ -368,14 +489,14 @@
* Is there a "post_controller" hook?
* ------------------------------------------------------
*/
- $EXT->_call_hook('post_controller');
+ $EXT->call_hook('post_controller');
/*
* ------------------------------------------------------
* Send the final rendered output to the browser
* ------------------------------------------------------
*/
- if ($EXT->_call_hook('display_override') === FALSE)
+ if ($EXT->call_hook('display_override') === FALSE)
{
$OUT->_display();
}
@@ -385,18 +506,4 @@
* Is there a "post_system" hook?
* ------------------------------------------------------
*/
- $EXT->_call_hook('post_system');
-
-/*
- * ------------------------------------------------------
- * Close the DB connection if one exists
- * ------------------------------------------------------
- */
- if (class_exists('CI_DB') AND isset($CI->db))
- {
- $CI->db->close();
- }
-
-
-/* End of file CodeIgniter.php */
-/* Location: ./system/core/CodeIgniter.php */ \ No newline at end of file
+ $EXT->call_hook('post_system');
diff --git a/system/core/Common.php b/system/core/Common.php
index 4bf8a9ef5..c7bb34549 100644
--- a/system/core/Common.php
+++ b/system/core/Common.php
@@ -1,24 +1,42 @@
-<?php if ( ! defined('BASEPATH')) exit('No direct script access allowed');
-/**
- * MODIFIED
- * config_item(): option to override returned values
- */
-
+<?php
/**
* CodeIgniter
*
- * An open source application development framework for PHP 5.1.6 or newer
+ * An open source application development framework for PHP
*
- * @package CodeIgniter
- * @author ExpressionEngine Dev Team
- * @copyright Copyright (c) 2008 - 2014, EllisLab, Inc.
- * @license http://codeigniter.com/user_guide/license.html
- * @link http://codeigniter.com
- * @since Version 1.0
+ * This content is released under the MIT License (MIT)
+ *
+ * Copyright (c) 2019 - 2022, CodeIgniter Foundation
+ *
+ * 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.
+ *
+ * @package CodeIgniter
+ * @author EllisLab Dev Team
+ * @copyright Copyright (c) 2008 - 2014, EllisLab, Inc. (https://ellislab.com/)
+ * @copyright Copyright (c) 2014 - 2019, British Columbia Institute of Technology (https://bcit.ca/)
+ * @copyright Copyright (c) 2019 - 2022, CodeIgniter Foundation (https://codeigniter.com/)
+ * @license https://opensource.org/licenses/MIT MIT License
+ * @link https://codeigniter.com
+ * @since Version 1.0.0
* @filesource
*/
-
-// ------------------------------------------------------------------------
+defined('BASEPATH') OR exit('No direct script access allowed');
/**
* Common Functions
@@ -26,34 +44,30 @@
* Loads the base classes and executes the request.
*
* @package CodeIgniter
- * @subpackage codeigniter
+ * @subpackage CodeIgniter
* @category Common Functions
- * @author ExpressionEngine Dev Team
- * @link http://codeigniter.com/user_guide/
+ * @author EllisLab Dev Team
+ * @link https://codeigniter.com/userguide3/
*/
// ------------------------------------------------------------------------
-/**
-* Determines if the current version of PHP is greater then the supplied value
-*
-* Since there are a few places where we conditionally test for PHP > 5
-* we'll set a static variable.
-*
-* @access public
-* @param string
-* @return bool TRUE if the current version is $version or higher
-*/
if ( ! function_exists('is_php'))
{
- function is_php($version = '5.0.0')
+ /**
+ * Determines if the current version of PHP is equal to or greater than the supplied value
+ *
+ * @param string
+ * @return bool TRUE if the current version is $version or higher
+ */
+ function is_php($version)
{
static $_is_php;
- $version = (string)$version;
+ $version = (string) $version;
if ( ! isset($_is_php[$version]))
{
- $_is_php[$version] = (version_compare(PHP_VERSION, $version) < 0) ? FALSE : TRUE;
+ $_is_php[$version] = version_compare(PHP_VERSION, $version, '>=');
}
return $_is_php[$version];
@@ -62,43 +76,43 @@ if ( ! function_exists('is_php'))
// ------------------------------------------------------------------------
-/**
- * Tests for file writability
- *
- * is_writable() returns TRUE on Windows servers when you really can't write to
- * the file, based on the read-only attribute. is_writable() is also unreliable
- * on Unix servers if safe_mode is on.
- *
- * @access private
- * @return void
- */
if ( ! function_exists('is_really_writable'))
{
+ /**
+ * Tests for file writability
+ *
+ * is_writable() returns TRUE on Windows servers when you really can't write to
+ * the file, based on the read-only attribute.
+ *
+ * @link https://bugs.php.net/bug.php?id=54709
+ * @param string
+ * @return bool
+ */
function is_really_writable($file)
{
- // If we're on a Unix server with safe_mode off we call is_writable
- if (DIRECTORY_SEPARATOR == '/' AND @ini_get("safe_mode") == FALSE)
+ // If we're on a UNIX-like server, just is_writable()
+ if (DIRECTORY_SEPARATOR === '/')
{
return is_writable($file);
}
- // For windows servers and safe_mode "on" installations we'll actually
- // write a file then read it. Bah...
+ /* For Windows servers and safe_mode "on" installations we'll actually
+ * write a file then read it. Bah...
+ */
if (is_dir($file))
{
- $file = rtrim($file, '/').'/'.md5(mt_rand(1,100).mt_rand(1,100));
-
- if (($fp = @fopen($file, FOPEN_WRITE_CREATE)) === FALSE)
+ $file = rtrim($file, '/').'/'.md5(mt_rand());
+ if (($fp = @fopen($file, 'ab')) === FALSE)
{
return FALSE;
}
fclose($fp);
- @chmod($file, DIR_WRITE_MODE);
+ @chmod($file, 0777);
@unlink($file);
return TRUE;
}
- elseif ( ! is_file($file) OR ($fp = @fopen($file, FOPEN_WRITE_CREATE)) === FALSE)
+ elseif ( ! is_file($file) OR ($fp = @fopen($file, 'ab')) === FALSE)
{
return FALSE;
}
@@ -110,26 +124,25 @@ if ( ! function_exists('is_really_writable'))
// ------------------------------------------------------------------------
-/**
-* Class registry
-*
-* This function acts as a singleton. If the requested class does not
-* exist it is instantiated and set to a static variable. If it has
-* previously been instantiated the variable is returned.
-*
-* @access public
-* @param string the class name being requested
-* @param string the directory where the class should be found
-* @param string the class name prefix
-* @return object
-*/
if ( ! function_exists('load_class'))
{
- function &load_class($class, $directory = 'libraries', $prefix = 'CI_')
+ /**
+ * Class registry
+ *
+ * This function acts as a singleton. If the requested class does not
+ * exist it is instantiated and set to a static variable. If it has
+ * previously been instantiated the variable is returned.
+ *
+ * @param string the class name being requested
+ * @param string the directory where the class should be found
+ * @param mixed an optional argument to pass to the class constructor
+ * @return object
+ */
+ function &load_class($class, $directory = 'libraries', $param = NULL)
{
static $_classes = array();
- // Does the class exist? If so, we're done...
+ // Does the class exist? If so, we're done...
if (isset($_classes[$class]))
{
return $_classes[$class];
@@ -143,60 +156,64 @@ if ( ! function_exists('load_class'))
{
if (file_exists($path.$directory.'/'.$class.'.php'))
{
- $name = $prefix.$class;
+ $name = 'CI_'.$class;
- if (class_exists($name) === FALSE)
+ if (class_exists($name, FALSE) === FALSE)
{
- require($path.$directory.'/'.$class.'.php');
+ require_once($path.$directory.'/'.$class.'.php');
}
break;
}
}
- // Is the request a class extension? If so we load it too
+ // Is the request a class extension? If so we load it too
if (file_exists(APPPATH.$directory.'/'.config_item('subclass_prefix').$class.'.php'))
{
$name = config_item('subclass_prefix').$class;
- if (class_exists($name) === FALSE)
+ if (class_exists($name, FALSE) === FALSE)
{
- require(APPPATH.$directory.'/'.config_item('subclass_prefix').$class.'.php');
+ require_once(APPPATH.$directory.'/'.$name.'.php');
}
}
// Did we find the class?
if ($name === FALSE)
{
- // Note: We use exit() rather then show_error() in order to avoid a
- // self-referencing loop with the Excptions class
- exit('Unable to locate the specified class: '.$class.'.php');
+ // Note: We use exit() rather than show_error() in order to avoid a
+ // self-referencing loop with the Exceptions class
+ set_status_header(503);
+ echo 'Unable to locate the specified class: '.$class.'.php';
+ exit(5); // EXIT_UNK_CLASS
}
// Keep track of what we just loaded
is_loaded($class);
- $_classes[$class] = new $name();
+ $_classes[$class] = isset($param)
+ ? new $name($param)
+ : new $name();
return $_classes[$class];
}
}
// --------------------------------------------------------------------
-/**
-* Keeps track of which libraries have been loaded. This function is
-* called by the load_class() function above
-*
-* @access public
-* @return array
-*/
if ( ! function_exists('is_loaded'))
{
+ /**
+ * Keeps track of which libraries have been loaded. This function is
+ * called by the load_class() function above
+ *
+ * @param string
+ * @return array
+ */
function &is_loaded($class = '')
{
static $_is_loaded = array();
- if ($class != '')
+ if ($class !== '')
{
$_is_loaded[strtolower($class)] = $class;
}
@@ -207,333 +224,505 @@ if ( ! function_exists('is_loaded'))
// ------------------------------------------------------------------------
-/**
-* Loads the main config.php file
-*
-* This function lets us grab the config file even if the Config class
-* hasn't been instantiated yet
-*
-* @access private
-* @return array
-*/
if ( ! function_exists('get_config'))
{
- function &get_config($replace = array())
+ /**
+ * Loads the main config.php file
+ *
+ * This function lets us grab the config file even if the Config class
+ * hasn't been instantiated yet
+ *
+ * @param array
+ * @return array
+ */
+ function &get_config(Array $replace = array())
{
- static $_config;
-
- if (isset($_config))
- {
- return $_config[0];
- }
+ static $config;
- // Is the config file in the environment folder?
- if ( ! defined('ENVIRONMENT') OR ! file_exists($file_path = APPPATH.'config/'.ENVIRONMENT.'/config.php'))
+ if (empty($config))
{
$file_path = APPPATH.'config/config.php';
+ $found = FALSE;
+ if (file_exists($file_path))
+ {
+ $found = TRUE;
+ require($file_path);
+ }
+
+ // Is the config file in the environment folder?
+ if (file_exists($file_path = APPPATH.'config/'.ENVIRONMENT.'/config.php'))
+ {
+ require($file_path);
+ }
+ elseif ( ! $found)
+ {
+ set_status_header(503);
+ echo 'The configuration file does not exist.';
+ exit(3); // EXIT_CONFIG
+ }
+
+ // Does the $config array exist in the file?
+ if ( ! isset($config) OR ! is_array($config))
+ {
+ set_status_header(503);
+ echo 'Your config file does not appear to be formatted correctly.';
+ exit(3); // EXIT_CONFIG
+ }
}
- // Fetch the config file
- if ( ! file_exists($file_path))
+ // Are any values being dynamically added or replaced?
+ foreach ($replace as $key => $val)
{
- exit('The configuration file does not exist.');
+ $config[$key] = $val;
}
- require($file_path);
+ return $config;
+ }
+}
- // Does the $config array exist in the file?
- if ( ! isset($config) OR ! is_array($config))
- {
- exit('Your config file does not appear to be formatted correctly.');
- }
+// ------------------------------------------------------------------------
+
+if ( ! function_exists('config_item'))
+{
+ /**
+ * Returns the specified config item
+ *
+ * @param string
+ * @return mixed
+ */
+ function config_item($item)
+ {
+ static $_config;
- // Are any values being dynamically replaced?
- if (count($replace) > 0)
+ if (empty($_config))
{
- foreach ($replace as $key => $val)
- {
- if (isset($config[$key]))
- {
- $config[$key] = $val;
- }
- }
+ // references cannot be directly assigned to static variables, so we use an array
+ $_config[0] =& get_config();
}
- $_config[0] =& $config;
- return $_config[0];
+ return isset($_config[0][$item]) ? $_config[0][$item] : NULL;
}
}
// ------------------------------------------------------------------------
-/**
-* Returns the specified config item
-*
-* @access public
-* @return mixed
-*/
-if ( ! function_exists('config_item'))
+if ( ! function_exists('get_mimes'))
{
- function config_item($item, $value = null)
+ /**
+ * Returns the MIME types array from config/mimes.php
+ *
+ * @return array
+ */
+ function &get_mimes()
{
- static $_config_item = array();
+ static $_mimes;
- if ( ! isset($_config_item[$item]))
+ if (empty($_mimes))
{
- $config =& get_config();
+ $_mimes = file_exists(APPPATH.'config/mimes.php')
+ ? include(APPPATH.'config/mimes.php')
+ : array();
- if ( ! isset($config[$item]))
+ if (file_exists(APPPATH.'config/'.ENVIRONMENT.'/mimes.php'))
{
- return FALSE;
+ $_mimes = array_merge($_mimes, include(APPPATH.'config/'.ENVIRONMENT.'/mimes.php'));
}
- $_config_item[$item] = $config[$item];
}
- if ($value !== null) {
- $_config_item[$item] = $value;
+ return $_mimes;
+ }
+}
+
+// ------------------------------------------------------------------------
+
+if ( ! function_exists('is_https'))
+{
+ /**
+ * Is HTTPS?
+ *
+ * Determines if the application is accessed via an encrypted
+ * (HTTPS) connection.
+ *
+ * @return bool
+ */
+ function is_https()
+ {
+ if ( ! empty($_SERVER['HTTPS']) && strtolower($_SERVER['HTTPS']) !== 'off')
+ {
+ return TRUE;
+ }
+ elseif (isset($_SERVER['HTTP_X_FORWARDED_PROTO']) && strtolower($_SERVER['HTTP_X_FORWARDED_PROTO']) === 'https')
+ {
+ return TRUE;
+ }
+ elseif ( ! empty($_SERVER['HTTP_FRONT_END_HTTPS']) && strtolower($_SERVER['HTTP_FRONT_END_HTTPS']) !== 'off')
+ {
+ return TRUE;
}
- return $_config_item[$item];
+ return FALSE;
+ }
+}
+
+// ------------------------------------------------------------------------
+
+if ( ! function_exists('is_cli'))
+{
+
+ /**
+ * Is CLI?
+ *
+ * Test to see if a request was made from the command line.
+ *
+ * @return bool
+ */
+ function is_cli()
+ {
+ return (PHP_SAPI === 'cli' OR defined('STDIN'));
}
}
// ------------------------------------------------------------------------
-/**
-* Error Handler
-*
-* This function lets us invoke the exception class and
-* display errors using the standard error template located
-* in application/errors/errors.php
-* This function will send the error page directly to the
-* browser and exit.
-*
-* @access public
-* @return void
-*/
if ( ! function_exists('show_error'))
{
+ /**
+ * Error Handler
+ *
+ * This function lets us invoke the exception class and
+ * display errors using the standard error template located
+ * in application/views/errors/error_general.php
+ * This function will send the error page directly to the
+ * browser and exit.
+ *
+ * @param string
+ * @param int
+ * @param string
+ * @return void
+ */
function show_error($message, $status_code = 500, $heading = 'An Error Was Encountered')
{
+ $status_code = abs($status_code);
+ if ($status_code < 100)
+ {
+ $exit_status = $status_code + 9; // 9 is EXIT__AUTO_MIN
+ $status_code = 500;
+ }
+ else
+ {
+ $exit_status = 1; // EXIT_ERROR
+ }
+
$_error =& load_class('Exceptions', 'core');
echo $_error->show_error($heading, $message, 'error_general', $status_code);
- exit;
+ exit($exit_status);
}
}
// ------------------------------------------------------------------------
-/**
-* 404 Page Handler
-*
-* This function is similar to the show_error() function above
-* However, instead of the standard error template it displays
-* 404 errors.
-*
-* @access public
-* @return void
-*/
if ( ! function_exists('show_404'))
{
+ /**
+ * 404 Page Handler
+ *
+ * This function is similar to the show_error() function above
+ * However, instead of the standard error template it displays
+ * 404 errors.
+ *
+ * @param string
+ * @param bool
+ * @return void
+ */
function show_404($page = '', $log_error = TRUE)
{
$_error =& load_class('Exceptions', 'core');
$_error->show_404($page, $log_error);
- exit;
+ exit(4); // EXIT_UNKNOWN_FILE
}
}
// ------------------------------------------------------------------------
-/**
-* Error Logging Interface
-*
-* We use this as a simple mechanism to access the logging
-* class and send messages to be logged.
-*
-* @access public
-* @return void
-*/
if ( ! function_exists('log_message'))
{
- function log_message($level = 'error', $message, $php_error = FALSE)
+ /**
+ * Error Logging Interface
+ *
+ * We use this as a simple mechanism to access the logging
+ * class and send messages to be logged.
+ *
+ * @param string the error level: 'error', 'debug' or 'info'
+ * @param string the error message
+ * @return void
+ */
+ function log_message($level, $message)
{
static $_log;
- if (config_item('log_threshold') == 0)
+ if ($_log === NULL)
{
- return;
+ // references cannot be directly assigned to static variables, so we use an array
+ $_log[0] =& load_class('Log', 'core');
}
- $_log =& load_class('Log');
- $_log->write_log($level, $message, $php_error);
+ $_log[0]->write_log($level, $message);
}
}
// ------------------------------------------------------------------------
-/**
- * Set HTTP Status Header
- *
- * @access public
- * @param int the status code
- * @param string
- * @return void
- */
if ( ! function_exists('set_status_header'))
{
+ /**
+ * Set HTTP Status Header
+ *
+ * @param int the status code
+ * @param string
+ * @return void
+ */
function set_status_header($code = 200, $text = '')
{
- $stati = array(
- 200 => 'OK',
- 201 => 'Created',
- 202 => 'Accepted',
- 203 => 'Non-Authoritative Information',
- 204 => 'No Content',
- 205 => 'Reset Content',
- 206 => 'Partial Content',
-
- 300 => 'Multiple Choices',
- 301 => 'Moved Permanently',
- 302 => 'Found',
- 304 => 'Not Modified',
- 305 => 'Use Proxy',
- 307 => 'Temporary Redirect',
-
- 400 => 'Bad Request',
- 401 => 'Unauthorized',
- 403 => 'Forbidden',
- 404 => 'Not Found',
- 405 => 'Method Not Allowed',
- 406 => 'Not Acceptable',
- 407 => 'Proxy Authentication Required',
- 408 => 'Request Timeout',
- 409 => 'Conflict',
- 410 => 'Gone',
- 411 => 'Length Required',
- 412 => 'Precondition Failed',
- 413 => 'Request Entity Too Large',
- 414 => 'Request-URI Too Long',
- 415 => 'Unsupported Media Type',
- 416 => 'Requested Range Not Satisfiable',
- 417 => 'Expectation Failed',
-
- 500 => 'Internal Server Error',
- 501 => 'Not Implemented',
- 502 => 'Bad Gateway',
- 503 => 'Service Unavailable',
- 504 => 'Gateway Timeout',
- 505 => 'HTTP Version Not Supported'
- );
-
- if ($code == '' OR ! is_numeric($code))
+ if (is_cli())
{
- show_error('Status codes must be numeric', 500);
+ return;
}
- if (isset($stati[$code]) AND $text == '')
+ if (empty($code) OR ! is_numeric($code))
{
- $text = $stati[$code];
+ show_error('Status codes must be numeric', 500);
}
- if ($text == '')
+ if (empty($text))
{
- show_error('No status text available. Please check your status code number or supply your own message text.', 500);
+ is_int($code) OR $code = (int) $code;
+ $stati = array(
+ 100 => 'Continue',
+ 101 => 'Switching Protocols',
+ 103 => 'Early Hints',
+
+ 200 => 'OK',
+ 201 => 'Created',
+ 202 => 'Accepted',
+ 203 => 'Non-Authoritative Information',
+ 204 => 'No Content',
+ 205 => 'Reset Content',
+ 206 => 'Partial Content',
+ 207 => 'Multi-Status',
+
+ 300 => 'Multiple Choices',
+ 301 => 'Moved Permanently',
+ 302 => 'Found',
+ 303 => 'See Other',
+ 304 => 'Not Modified',
+ 305 => 'Use Proxy',
+ 307 => 'Temporary Redirect',
+ 308 => 'Permanent Redirect',
+
+ 400 => 'Bad Request',
+ 401 => 'Unauthorized',
+ 402 => 'Payment Required',
+ 403 => 'Forbidden',
+ 404 => 'Not Found',
+ 405 => 'Method Not Allowed',
+ 406 => 'Not Acceptable',
+ 407 => 'Proxy Authentication Required',
+ 408 => 'Request Timeout',
+ 409 => 'Conflict',
+ 410 => 'Gone',
+ 411 => 'Length Required',
+ 412 => 'Precondition Failed',
+ 413 => 'Request Entity Too Large',
+ 414 => 'Request-URI Too Long',
+ 415 => 'Unsupported Media Type',
+ 416 => 'Requested Range Not Satisfiable',
+ 417 => 'Expectation Failed',
+ 421 => 'Misdirected Request',
+ 422 => 'Unprocessable Entity',
+ 426 => 'Upgrade Required',
+ 428 => 'Precondition Required',
+ 429 => 'Too Many Requests',
+ 431 => 'Request Header Fields Too Large',
+ 451 => 'Unavailable For Legal Reasons',
+
+ 500 => 'Internal Server Error',
+ 501 => 'Not Implemented',
+ 502 => 'Bad Gateway',
+ 503 => 'Service Unavailable',
+ 504 => 'Gateway Timeout',
+ 505 => 'HTTP Version Not Supported',
+ 511 => 'Network Authentication Required',
+ );
+
+ if (isset($stati[$code]))
+ {
+ $text = $stati[$code];
+ }
+ else
+ {
+ show_error('No status text available. Please check your status code number or supply your own message text.', 500);
+ }
}
- $server_protocol = (isset($_SERVER['SERVER_PROTOCOL'])) ? $_SERVER['SERVER_PROTOCOL'] : FALSE;
-
- if (substr(php_sapi_name(), 0, 3) == 'cgi')
- {
- header("Status: {$code} {$text}", TRUE);
- }
- elseif ($server_protocol == 'HTTP/1.1' OR $server_protocol == 'HTTP/1.0')
- {
- header($server_protocol." {$code} {$text}", TRUE, $code);
- }
- else
+ if (strpos(PHP_SAPI, 'cgi') === 0)
{
- header("HTTP/1.1 {$code} {$text}", TRUE, $code);
+ header('Status: '.$code.' '.$text, TRUE);
+ return;
}
+
+ $server_protocol = (isset($_SERVER['SERVER_PROTOCOL']) && in_array($_SERVER['SERVER_PROTOCOL'], array('HTTP/1.0', 'HTTP/1.1', 'HTTP/2', 'HTTP/2.0'), TRUE))
+ ? $_SERVER['SERVER_PROTOCOL'] : 'HTTP/1.1';
+ header($server_protocol.' '.$code.' '.$text, TRUE, $code);
}
}
// --------------------------------------------------------------------
-/**
-* Exception Handler
-*
-* This is the custom exception handler that is declaired at the top
-* of Codeigniter.php. The main reason we use this is to permit
-* PHP errors to be logged in our own log files since the user may
-* not have access to server logs. Since this function
-* effectively intercepts PHP errors, however, we also need
-* to display errors based on the current error_reporting level.
-* We do that with the use of a PHP error template.
-*
-* @access private
-* @return void
-*/
-if ( ! function_exists('_exception_handler'))
+if ( ! function_exists('_error_handler'))
{
- function _exception_handler($severity, $message, $filepath, $line)
+ /**
+ * Error Handler
+ *
+ * This is the custom error handler that is declared at the (relative)
+ * top of CodeIgniter.php. The main reason we use this is to permit
+ * PHP errors to be logged in our own log files since the user may
+ * not have access to server logs. Since this function effectively
+ * intercepts PHP errors, however, we also need to display errors
+ * based on the current error_reporting level.
+ * We do that with the use of a PHP error template.
+ *
+ * @param int $severity
+ * @param string $message
+ * @param string $filepath
+ * @param int $line
+ * @return void
+ */
+ function _error_handler($severity, $message, $filepath, $line)
{
- // We don't bother with "strict" notices since they tend to fill up
- // the log file with excess information that isn't normally very helpful.
- // For example, if you are running PHP 5 and you use version 4 style
- // class functions (without prefixes like "public", "private", etc.)
- // you'll get notices telling you that these have been deprecated.
- if ($severity == E_STRICT)
+ $is_error = (((E_ERROR | E_PARSE | E_COMPILE_ERROR | E_CORE_ERROR | E_USER_ERROR) & $severity) === $severity);
+
+ // When an error occurred, set the status header to '500 Internal Server Error'
+ // to indicate to the client something went wrong.
+ // This can't be done within the $_error->show_php_error method because
+ // it is only called when the display_errors flag is set (which isn't usually
+ // the case in a production environment) or when errors are ignored because
+ // they are above the error_reporting threshold.
+ if ($is_error)
+ {
+ set_status_header(500);
+ }
+
+ // Should we ignore the error? We'll get the current error_reporting
+ // level and add its bits with the severity bits to find out.
+ if (($severity & error_reporting()) !== $severity)
{
return;
}
$_error =& load_class('Exceptions', 'core');
+ $_error->log_exception($severity, $message, $filepath, $line);
- // Should we display the error? We'll get the current error_reporting
- // level and add its bits with the severity bits to find out.
- if (($severity & error_reporting()) == $severity)
+ // Should we display the error?
+ if (str_ireplace(array('off', 'none', 'no', 'false', 'null'), '', ini_get('display_errors')))
{
$_error->show_php_error($severity, $message, $filepath, $line);
}
- // Should we log the error? No? We're done...
- if (config_item('log_threshold') == 0)
+ // If the error is fatal, the execution of the script should be stopped because
+ // errors can't be recovered from. Halting the script conforms with PHP's
+ // default error handling. See https://secure.php.net/manual/en/errorfunc.constants.php
+ if ($is_error)
{
- return;
+ exit(1); // EXIT_ERROR
}
+ }
+}
- $_error->log_exception($severity, $message, $filepath, $line);
+// ------------------------------------------------------------------------
+
+if ( ! function_exists('_exception_handler'))
+{
+ /**
+ * Exception Handler
+ *
+ * Sends uncaught exceptions to the logger and displays them
+ * only if display_errors is On so that they don't show up in
+ * production environments.
+ *
+ * @param Exception $exception
+ * @return void
+ */
+ function _exception_handler($exception)
+ {
+ $_error =& load_class('Exceptions', 'core');
+ $_error->log_exception('error', 'Exception: '.$exception->getMessage(), $exception->getFile(), $exception->getLine());
+
+ is_cli() OR set_status_header(500);
+ // Should we display the error?
+ if (str_ireplace(array('off', 'none', 'no', 'false', 'null'), '', ini_get('display_errors')))
+ {
+ $_error->show_exception($exception);
+ }
+
+ exit(1); // EXIT_ERROR
+ }
+}
+
+// ------------------------------------------------------------------------
+
+if ( ! function_exists('_shutdown_handler'))
+{
+ /**
+ * Shutdown Handler
+ *
+ * This is the shutdown handler that is declared at the top
+ * of CodeIgniter.php. The main reason we use this is to simulate
+ * a complete custom exception handler.
+ *
+ * E_STRICT is purposively neglected because such events may have
+ * been caught. Duplication or none? None is preferred for now.
+ *
+ * @link http://insomanic.me.uk/post/229851073/php-trick-catching-fatal-errors-e-error-with-a
+ * @return void
+ */
+ function _shutdown_handler()
+ {
+ $last_error = error_get_last();
+ if (isset($last_error) &&
+ ($last_error['type'] & (E_ERROR | E_PARSE | E_CORE_ERROR | E_CORE_WARNING | E_COMPILE_ERROR | E_COMPILE_WARNING)))
+ {
+ _error_handler($last_error['type'], $last_error['message'], $last_error['file'], $last_error['line']);
+ }
}
}
// --------------------------------------------------------------------
-/**
- * Remove Invisible Characters
- *
- * This prevents sandwiching null characters
- * between ascii characters, like Java\0script.
- *
- * @access public
- * @param string
- * @return string
- */
if ( ! function_exists('remove_invisible_characters'))
{
+ /**
+ * Remove Invisible Characters
+ *
+ * This prevents sandwiching null characters
+ * between ascii characters, like Java\0script.
+ *
+ * @param string
+ * @param bool
+ * @return string
+ */
function remove_invisible_characters($str, $url_encoded = TRUE)
{
$non_displayables = array();
-
- // every control character except newline (dec 10)
- // carriage return (dec 13), and horizontal tab (dec 09)
-
+
+ // every control character except newline (dec 10),
+ // carriage return (dec 13) and horizontal tab (dec 09)
if ($url_encoded)
{
- $non_displayables[] = '/%0[0-8bcef]/'; // url encoded 00-08, 11, 12, 14, 15
- $non_displayables[] = '/%1[0-9a-f]/'; // url encoded 16-31
+ $non_displayables[] = '/%0[0-8bcef]/i'; // url encoded 00-08, 11, 12, 14, 15
+ $non_displayables[] = '/%1[0-9a-f]/i'; // url encoded 16-31
+ $non_displayables[] = '/%7f/i'; // url encoded 127
}
-
+
$non_displayables[] = '/[\x00-\x08\x0B\x0C\x0E-\x1F\x7F]+/S'; // 00-08, 11, 12, 14-31, 127
do
@@ -548,27 +737,117 @@ if ( ! function_exists('remove_invisible_characters'))
// ------------------------------------------------------------------------
-/**
-* Returns HTML escaped variable
-*
-* @access public
-* @param mixed
-* @return mixed
-*/
if ( ! function_exists('html_escape'))
{
- function html_escape($var)
+ /**
+ * Returns HTML escaped variable.
+ *
+ * @param mixed $var The input string or array of strings to be escaped.
+ * @param bool $double_encode $double_encode set to FALSE prevents escaping twice.
+ * @return mixed The escaped string or array of strings as a result.
+ */
+ function html_escape($var, $double_encode = TRUE)
{
+ if (empty($var))
+ {
+ return $var;
+ }
+
if (is_array($var))
{
- return array_map('html_escape', $var);
+ foreach (array_keys($var) as $key)
+ {
+ $var[$key] = html_escape($var[$key], $double_encode);
+ }
+
+ return $var;
}
- else
+
+ return htmlspecialchars($var, ENT_QUOTES, config_item('charset'), $double_encode);
+ }
+}
+
+// ------------------------------------------------------------------------
+
+if ( ! function_exists('_stringify_attributes'))
+{
+ /**
+ * Stringify attributes for use in HTML tags.
+ *
+ * Helper function used to convert a string, array, or object
+ * of attributes to a string.
+ *
+ * @param mixed string, array, object
+ * @param bool
+ * @return string
+ */
+ function _stringify_attributes($attributes, $js = FALSE)
+ {
+ if (empty($attributes))
+ {
+ return NULL;
+ }
+
+ if (is_string($attributes))
{
- return htmlspecialchars($var, ENT_QUOTES, config_item('charset'));
+ return ' '.$attributes;
}
+
+ $attributes = (array) $attributes;
+
+ $atts = '';
+ foreach ($attributes as $key => $val)
+ {
+ $atts .= ($js) ? $key.'='.$val.',' : ' '.$key.'="'.$val.'"';
+ }
+
+ return rtrim($atts, ',');
}
}
-/* End of file Common.php */
-/* Location: ./system/core/Common.php */ \ No newline at end of file
+// ------------------------------------------------------------------------
+
+if ( ! function_exists('function_usable'))
+{
+ /**
+ * Function usable
+ *
+ * Executes a function_exists() check, and if the Suhosin PHP
+ * extension is loaded - checks whether the function that is
+ * checked might be disabled in there as well.
+ *
+ * This is useful as function_exists() will return FALSE for
+ * functions disabled via the *disable_functions* php.ini
+ * setting, but not for *suhosin.executor.func.blacklist* and
+ * *suhosin.executor.disable_eval*. These settings will just
+ * terminate script execution if a disabled function is executed.
+ *
+ * The above described behavior turned out to be a bug in Suhosin,
+ * but even though a fix was committed for 0.9.34 on 2012-02-12,
+ * that version is yet to be released. This function will therefore
+ * be just temporary, but would probably be kept for a few years.
+ *
+ * @link http://www.hardened-php.net/suhosin/
+ * @param string $function_name Function to check for
+ * @return bool TRUE if the function exists and is safe to call,
+ * FALSE otherwise.
+ */
+ function function_usable($function_name)
+ {
+ static $_suhosin_func_blacklist;
+
+ if (function_exists($function_name))
+ {
+ if ( ! isset($_suhosin_func_blacklist))
+ {
+ $_suhosin_func_blacklist = extension_loaded('suhosin')
+ ? explode(',', trim(ini_get('suhosin.executor.func.blacklist')))
+ : array();
+ }
+
+ return ! in_array($function_name, $_suhosin_func_blacklist, TRUE);
+ }
+
+ return FALSE;
+ }
+}
diff --git a/system/core/Config.php b/system/core/Config.php
index caa8b945a..4efe1e1ec 100644
--- a/system/core/Config.php
+++ b/system/core/Config.php
@@ -1,78 +1,108 @@
-<?php if ( ! defined('BASEPATH')) exit('No direct script access allowed');
+<?php
/**
* CodeIgniter
*
- * An open source application development framework for PHP 5.1.6 or newer
+ * An open source application development framework for PHP
*
- * @package CodeIgniter
- * @author ExpressionEngine Dev Team
- * @copyright Copyright (c) 2008 - 2014, EllisLab, Inc.
- * @license http://codeigniter.com/user_guide/license.html
- * @link http://codeigniter.com
- * @since Version 1.0
+ * This content is released under the MIT License (MIT)
+ *
+ * Copyright (c) 2019 - 2022, CodeIgniter Foundation
+ *
+ * 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.
+ *
+ * @package CodeIgniter
+ * @author EllisLab Dev Team
+ * @copyright Copyright (c) 2008 - 2014, EllisLab, Inc. (https://ellislab.com/)
+ * @copyright Copyright (c) 2014 - 2019, British Columbia Institute of Technology (https://bcit.ca/)
+ * @copyright Copyright (c) 2019 - 2022, CodeIgniter Foundation (https://codeigniter.com/)
+ * @license https://opensource.org/licenses/MIT MIT License
+ * @link https://codeigniter.com
+ * @since Version 1.0.0
* @filesource
*/
-
-// ------------------------------------------------------------------------
+defined('BASEPATH') OR exit('No direct script access allowed');
/**
- * CodeIgniter Config Class
+ * Config Class
*
* This class contains functions that enable config files to be managed
*
* @package CodeIgniter
* @subpackage Libraries
* @category Libraries
- * @author ExpressionEngine Dev Team
- * @link http://codeigniter.com/user_guide/libraries/config.html
+ * @author EllisLab Dev Team
+ * @link https://codeigniter.com/userguide3/libraries/config.html
*/
class CI_Config {
/**
* List of all loaded config values
*
- * @var array
+ * @var array
*/
- var $config = array();
+ public $config = array();
+
/**
* List of all loaded config files
*
- * @var array
+ * @var array
*/
- var $is_loaded = array();
+ public $is_loaded = array();
+
/**
- * List of paths to search when trying to load a config file
+ * List of paths to search when trying to load a config file.
*
- * @var array
+ * @used-by CI_Loader
+ * @var array
*/
- var $_config_paths = array(APPPATH);
+ public $_config_paths = array(APPPATH);
+
+ // --------------------------------------------------------------------
/**
- * Constructor
+ * Class constructor
*
- * Sets the $config data from the primary config.php file as a class variable
+ * Sets the $config data from the primary config.php file as a class variable.
*
- * @access public
- * @param string the config file name
- * @param boolean if configuration values should be loaded into their own section
- * @param boolean true if errors should just return false, false if an error message should be displayed
- * @return boolean if the file was successfully loaded or not
+ * @return void
*/
- function __construct()
+ public function __construct()
{
$this->config =& get_config();
- log_message('debug', "Config Class Initialized");
// Set the base_url automatically if none was provided
- if ($this->config['base_url'] == '')
+ if (empty($this->config['base_url']))
{
- if (isset($_SERVER['HTTP_HOST']))
+ if (isset($_SERVER['SERVER_ADDR']))
{
- $base_url = isset($_SERVER['HTTPS']) && strtolower($_SERVER['HTTPS']) !== 'off' ? 'https' : 'http';
- $base_url .= '://'. $_SERVER['HTTP_HOST'];
- $base_url .= str_replace(basename($_SERVER['SCRIPT_NAME']), '', $_SERVER['SCRIPT_NAME']);
- }
+ if (strpos($_SERVER['SERVER_ADDR'], ':') !== FALSE)
+ {
+ $server_addr = '['.$_SERVER['SERVER_ADDR'].']';
+ }
+ else
+ {
+ $server_addr = $_SERVER['SERVER_ADDR'];
+ }
+ $base_url = (is_https() ? 'https' : 'http').'://'.$server_addr
+ .substr($_SERVER['SCRIPT_NAME'], 0, strpos($_SERVER['SCRIPT_NAME'], basename($_SERVER['SCRIPT_FILENAME'])));
+ }
else
{
$base_url = 'http://localhost/';
@@ -80,6 +110,8 @@ class CI_Config {
$this->set_item('base_url', $base_url);
}
+
+ log_message('info', 'Config Class Initialized');
}
// --------------------------------------------------------------------
@@ -87,91 +119,71 @@ class CI_Config {
/**
* Load Config File
*
- * @access public
- * @param string the config file name
- * @param boolean if configuration values should be loaded into their own section
- * @param boolean true if errors should just return false, false if an error message should be displayed
- * @return boolean if the file was loaded correctly
+ * @param string $file Configuration file name
+ * @param bool $use_sections Whether configuration values should be loaded into their own section
+ * @param bool $fail_gracefully Whether to just return FALSE or display an error message
+ * @return bool TRUE if the file was loaded correctly or FALSE on failure
*/
- function load($file = '', $use_sections = FALSE, $fail_gracefully = FALSE)
+ public function load($file = '', $use_sections = FALSE, $fail_gracefully = FALSE)
{
- $file = ($file == '') ? 'config' : str_replace('.php', '', $file);
- $found = FALSE;
+ $file = ($file === '') ? 'config' : str_replace('.php', '', $file);
$loaded = FALSE;
- $check_locations = defined('ENVIRONMENT')
- ? array(ENVIRONMENT.'/'.$file, $file)
- : array($file);
-
foreach ($this->_config_paths as $path)
{
- foreach ($check_locations as $location)
+ foreach (array($file, ENVIRONMENT.DIRECTORY_SEPARATOR.$file) as $location)
{
$file_path = $path.'config/'.$location.'.php';
-
if (in_array($file_path, $this->is_loaded, TRUE))
{
- $loaded = TRUE;
- continue 2;
+ return TRUE;
}
- if (file_exists($file_path))
+ if ( ! file_exists($file_path))
{
- $found = TRUE;
- break;
+ continue;
}
- }
- if ($found === FALSE)
- {
- continue;
- }
+ include($file_path);
- include($file_path);
-
- if ( ! isset($config) OR ! is_array($config))
- {
- if ($fail_gracefully === TRUE)
+ if ( ! isset($config) OR ! is_array($config))
{
- return FALSE;
+ if ($fail_gracefully === TRUE)
+ {
+ return FALSE;
+ }
+
+ show_error('Your '.$file_path.' file does not appear to contain a valid configuration array.');
}
- show_error('Your '.$file_path.' file does not appear to contain a valid configuration array.');
- }
- if ($use_sections === TRUE)
- {
- if (isset($this->config[$file]))
+ if ($use_sections === TRUE)
{
- $this->config[$file] = array_merge($this->config[$file], $config);
+ $this->config[$file] = isset($this->config[$file])
+ ? array_merge($this->config[$file], $config)
+ : $config;
}
else
{
- $this->config[$file] = $config;
+ $this->config = array_merge($this->config, $config);
}
- }
- else
- {
- $this->config = array_merge($this->config, $config);
- }
- $this->is_loaded[] = $file_path;
- unset($config);
-
- $loaded = TRUE;
- log_message('debug', 'Config file loaded: '.$file_path);
- break;
+ $this->is_loaded[] = $file_path;
+ $config = NULL;
+ $loaded = TRUE;
+ log_message('info', 'Config file loaded: '.$file_path);
+ }
}
- if ($loaded === FALSE)
+ if ($loaded === TRUE)
{
- if ($fail_gracefully === TRUE)
- {
- return FALSE;
- }
- show_error('The configuration file '.$file.'.php does not exist.');
+ return TRUE;
+ }
+ elseif ($fail_gracefully === TRUE)
+ {
+ return FALSE;
}
- return TRUE;
+ show_error('The configuration file '.$file.'.php does not exist.');
}
// --------------------------------------------------------------------
@@ -179,59 +191,35 @@ class CI_Config {
/**
* Fetch a config file item
*
- *
- * @access public
- * @param string the config item name
- * @param string the index name
- * @param bool
- * @return string
+ * @param string $item Config item name
+ * @param string $index Index name
+ * @return string|null The configuration item or NULL if the item doesn't exist
*/
- function item($item, $index = '')
+ public function item($item, $index = '')
{
if ($index == '')
{
- if ( ! isset($this->config[$item]))
- {
- return FALSE;
- }
-
- $pref = $this->config[$item];
- }
- else
- {
- if ( ! isset($this->config[$index]))
- {
- return FALSE;
- }
-
- if ( ! isset($this->config[$index][$item]))
- {
- return FALSE;
- }
-
- $pref = $this->config[$index][$item];
+ return isset($this->config[$item]) ? $this->config[$item] : NULL;
}
- return $pref;
+ return isset($this->config[$index], $this->config[$index][$item]) ? $this->config[$index][$item] : NULL;
}
// --------------------------------------------------------------------
/**
- * Fetch a config file item - adds slash after item (if item is not empty)
+ * Fetch a config file item with slash appended (if not empty)
*
- * @access public
- * @param string the config item name
- * @param bool
- * @return string
+ * @param string $item Config item name
+ * @return string|null The configuration item or NULL if the item doesn't exist
*/
- function slash_item($item)
+ public function slash_item($item)
{
if ( ! isset($this->config[$item]))
{
- return FALSE;
+ return NULL;
}
- if( trim($this->config[$item]) == '')
+ elseif (trim($this->config[$item]) === '')
{
return '';
}
@@ -243,94 +231,122 @@ class CI_Config {
/**
* Site URL
+ *
* Returns base_url . index_page [. uri_string]
*
- * @access public
- * @param string the URI string
+ * @uses CI_Config::_uri_string()
+ *
+ * @param string|string[] $uri URI string or an array of segments
+ * @param string $protocol
* @return string
*/
- function site_url($uri = '')
+ public function site_url($uri = '', $protocol = NULL)
{
- if ($uri == '')
+ $base_url = $this->slash_item('base_url');
+
+ if (isset($protocol))
{
- return $this->slash_item('base_url').$this->item('index_page');
+ // For protocol-relative links
+ if ($protocol === '')
+ {
+ $base_url = substr($base_url, strpos($base_url, '//'));
+ }
+ else
+ {
+ $base_url = $protocol.substr($base_url, strpos($base_url, '://'));
+ }
}
- if ($this->item('enable_query_strings') == FALSE)
+ if (empty($uri))
+ {
+ return $base_url.$this->item('index_page');
+ }
+
+ $uri = $this->_uri_string($uri);
+
+ if ($this->item('enable_query_strings') === FALSE)
{
- $suffix = ($this->item('url_suffix') == FALSE) ? '' : $this->item('url_suffix');
- return $this->slash_item('base_url').$this->slash_item('index_page').$this->_uri_string($uri).$suffix;
+ $suffix = isset($this->config['url_suffix']) ? $this->config['url_suffix'] : '';
+
+ if ($suffix !== '')
+ {
+ if (($offset = strpos($uri, '?')) !== FALSE)
+ {
+ $uri = substr($uri, 0, $offset).$suffix.substr($uri, $offset);
+ }
+ else
+ {
+ $uri .= $suffix;
+ }
+ }
+
+ return $base_url.$this->slash_item('index_page').$uri;
}
- else
+ elseif (strpos($uri, '?') === FALSE)
{
- return $this->slash_item('base_url').$this->item('index_page').'?'.$this->_uri_string($uri);
+ $uri = '?'.$uri;
}
+
+ return $base_url.$this->item('index_page').$uri;
}
// -------------------------------------------------------------
/**
* Base URL
+ *
* Returns base_url [. uri_string]
*
- * @access public
- * @param string $uri
- * @return string
- */
- function base_url($uri = '')
- {
- return $this->slash_item('base_url').ltrim($this->_uri_string($uri), '/');
- }
-
- // -------------------------------------------------------------
-
- /**
- * Build URI string for use in Config::site_url() and Config::base_url()
+ * @uses CI_Config::_uri_string()
*
- * @access protected
- * @param $uri
- * @return string
+ * @param string|string[] $uri URI string or an array of segments
+ * @param string $protocol
+ * @return string
*/
- protected function _uri_string($uri)
+ public function base_url($uri = '', $protocol = NULL)
{
- if ($this->item('enable_query_strings') == FALSE)
+ $base_url = $this->slash_item('base_url');
+
+ if (isset($protocol))
{
- if (is_array($uri))
+ // For protocol-relative links
+ if ($protocol === '')
{
- $uri = implode('/', $uri);
+ $base_url = substr($base_url, strpos($base_url, '//'));
}
- $uri = ltrim($uri, '/');
- }
- else
- {
- if (is_array($uri))
+ else
{
- $i = 0;
- $str = '';
- foreach ($uri as $key => $val)
- {
- $prefix = ($i == 0) ? '' : '&';
- $str .= $prefix.$key.'='.$val;
- $i++;
- }
- $uri = $str;
+ $base_url = $protocol.substr($base_url, strpos($base_url, '://'));
}
}
- return $uri;
+
+ return $base_url.$this->_uri_string($uri);
}
- // --------------------------------------------------------------------
+ // -------------------------------------------------------------
/**
- * System URL
+ * Build URI string
+ *
+ * @used-by CI_Config::site_url()
+ * @used-by CI_Config::base_url()
*
- * @access public
+ * @param string|string[] $uri URI string or an array of segments
* @return string
*/
- function system_url()
+ protected function _uri_string($uri)
{
- $x = explode("/", preg_replace("|/*(.+?)/*$|", "\\1", BASEPATH));
- return $this->slash_item('base_url').end($x).'/';
+ if ($this->item('enable_query_strings') === FALSE)
+ {
+ is_array($uri) && $uri = implode('/', $uri);
+ return ltrim($uri, '/');
+ }
+ elseif (is_array($uri))
+ {
+ return http_build_query($uri);
+ }
+
+ return $uri;
}
// --------------------------------------------------------------------
@@ -338,42 +354,13 @@ class CI_Config {
/**
* Set a config file item
*
- * @access public
- * @param string the config item key
- * @param string the config item value
+ * @param string $item Config item key
+ * @param string $value Config item value
* @return void
*/
- function set_item($item, $value)
+ public function set_item($item, $value)
{
$this->config[$item] = $value;
}
- // --------------------------------------------------------------------
-
- /**
- * Assign to Config
- *
- * This function is called by the front controller (CodeIgniter.php)
- * after the Config class is instantiated. It permits config items
- * to be assigned or overriden by variables contained in the index.php file
- *
- * @access private
- * @param array
- * @return void
- */
- function _assign_to_config($items = array())
- {
- if (is_array($items))
- {
- foreach ($items as $key => $val)
- {
- $this->set_item($key, $val);
- }
- }
- }
}
-
-// END CI_Config class
-
-/* End of file Config.php */
-/* Location: ./system/core/Config.php */
diff --git a/system/core/Controller.php b/system/core/Controller.php
index 6ccaf9755..ac7a0566a 100644
--- a/system/core/Controller.php
+++ b/system/core/Controller.php
@@ -1,22 +1,45 @@
-<?php if ( ! defined('BASEPATH')) exit('No direct script access allowed');
+<?php
/**
* CodeIgniter
*
- * An open source application development framework for PHP 5.1.6 or newer
+ * An open source application development framework for PHP
*
- * @package CodeIgniter
- * @author ExpressionEngine Dev Team
- * @copyright Copyright (c) 2008 - 2014, EllisLab, Inc.
- * @license http://codeigniter.com/user_guide/license.html
- * @link http://codeigniter.com
- * @since Version 1.0
+ * This content is released under the MIT License (MIT)
+ *
+ * Copyright (c) 2019 - 2022, CodeIgniter Foundation
+ *
+ * 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.
+ *
+ * @package CodeIgniter
+ * @author EllisLab Dev Team
+ * @copyright Copyright (c) 2008 - 2014, EllisLab, Inc. (https://ellislab.com/)
+ * @copyright Copyright (c) 2014 - 2019, British Columbia Institute of Technology (https://bcit.ca/)
+ * @copyright Copyright (c) 2019 - 2022, CodeIgniter Foundation (https://codeigniter.com/)
+ * @license https://opensource.org/licenses/MIT MIT License
+ * @link https://codeigniter.com
+ * @since Version 1.0.0
* @filesource
*/
-
-// ------------------------------------------------------------------------
+defined('BASEPATH') OR exit('No direct script access allowed');
/**
- * CodeIgniter Application Controller Class
+ * Application Controller Class
*
* This class object is the super class that every library in
* CodeIgniter will be assigned to.
@@ -24,15 +47,30 @@
* @package CodeIgniter
* @subpackage Libraries
* @category Libraries
- * @author ExpressionEngine Dev Team
- * @link http://codeigniter.com/user_guide/general/controllers.html
+ * @author EllisLab Dev Team
+ * @link https://codeigniter.com/userguide3/general/controllers.html
*/
+#[\AllowDynamicProperties]
class CI_Controller {
+ /**
+ * Reference to the CI singleton
+ *
+ * @var object
+ */
private static $instance;
/**
- * Constructor
+ * CI_Loader
+ *
+ * @var CI_Loader
+ */
+ public $load;
+
+ /**
+ * Class constructor
+ *
+ * @return void
*/
public function __construct()
{
@@ -47,18 +85,21 @@ class CI_Controller {
}
$this->load =& load_class('Loader', 'core');
-
$this->load->initialize();
-
- log_message('debug', "Controller Class Initialized");
+ log_message('info', 'Controller Class Initialized');
}
+ // --------------------------------------------------------------------
+
+ /**
+ * Get the CI singleton
+ *
+ * @static
+ * @return object
+ */
public static function &get_instance()
{
return self::$instance;
}
-}
-// END Controller class
-/* End of file Controller.php */
-/* Location: ./system/core/Controller.php */ \ No newline at end of file
+}
diff --git a/system/core/Exceptions.php b/system/core/Exceptions.php
index 451209689..7244f3f28 100644
--- a/system/core/Exceptions.php
+++ b/system/core/Exceptions.php
@@ -1,19 +1,42 @@
-<?php if ( ! defined('BASEPATH')) exit('No direct script access allowed');
+<?php
/**
* CodeIgniter
*
- * An open source application development framework for PHP 5.1.6 or newer
+ * An open source application development framework for PHP
*
- * @package CodeIgniter
- * @author ExpressionEngine Dev Team
- * @copyright Copyright (c) 2008 - 2014, EllisLab, Inc.
- * @license http://codeigniter.com/user_guide/license.html
- * @link http://codeigniter.com
- * @since Version 1.0
+ * This content is released under the MIT License (MIT)
+ *
+ * Copyright (c) 2019 - 2022, CodeIgniter Foundation
+ *
+ * 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.
+ *
+ * @package CodeIgniter
+ * @author EllisLab Dev Team
+ * @copyright Copyright (c) 2008 - 2014, EllisLab, Inc. (https://ellislab.com/)
+ * @copyright Copyright (c) 2014 - 2019, British Columbia Institute of Technology (https://bcit.ca/)
+ * @copyright Copyright (c) 2019 - 2022, CodeIgniter Foundation (https://codeigniter.com/)
+ * @license https://opensource.org/licenses/MIT MIT License
+ * @link https://codeigniter.com
+ * @since Version 1.0.0
* @filesource
*/
-
-// ------------------------------------------------------------------------
+defined('BASEPATH') OR exit('No direct script access allowed');
/**
* Exceptions Class
@@ -21,53 +44,47 @@
* @package CodeIgniter
* @subpackage Libraries
* @category Exceptions
- * @author ExpressionEngine Dev Team
- * @link http://codeigniter.com/user_guide/libraries/exceptions.html
+ * @author EllisLab Dev Team
+ * @link https://codeigniter.com/userguide3/libraries/exceptions.html
*/
class CI_Exceptions {
- var $action;
- var $severity;
- var $message;
- var $filename;
- var $line;
/**
* Nesting level of the output buffering mechanism
*
- * @var int
- * @access public
+ * @var int
*/
- var $ob_level;
+ public $ob_level;
/**
- * List if available error levels
+ * List of available error levels
*
- * @var array
- * @access public
+ * @var array
*/
- var $levels = array(
- E_ERROR => 'Error',
- E_WARNING => 'Warning',
- E_PARSE => 'Parsing Error',
- E_NOTICE => 'Notice',
- E_CORE_ERROR => 'Core Error',
- E_CORE_WARNING => 'Core Warning',
- E_COMPILE_ERROR => 'Compile Error',
- E_COMPILE_WARNING => 'Compile Warning',
- E_USER_ERROR => 'User Error',
- E_USER_WARNING => 'User Warning',
- E_USER_NOTICE => 'User Notice',
- E_STRICT => 'Runtime Notice'
- );
-
+ public $levels = array(
+ E_ERROR => 'Error',
+ E_WARNING => 'Warning',
+ E_PARSE => 'Parsing Error',
+ E_NOTICE => 'Notice',
+ E_CORE_ERROR => 'Core Error',
+ E_CORE_WARNING => 'Core Warning',
+ E_COMPILE_ERROR => 'Compile Error',
+ E_COMPILE_WARNING => 'Compile Warning',
+ E_USER_ERROR => 'User Error',
+ E_USER_WARNING => 'User Warning',
+ E_USER_NOTICE => 'User Notice',
+ E_STRICT => 'Runtime Notice'
+ );
/**
- * Constructor
+ * Class constructor
+ *
+ * @return void
*/
public function __construct()
{
$this->ob_level = ob_get_level();
- // Note: Do not log messages from this constructor.
+ // Note: Do not log messages from this constructor.
}
// --------------------------------------------------------------------
@@ -75,45 +92,52 @@ class CI_Exceptions {
/**
* Exception Logger
*
- * This function logs PHP generated error messages
+ * Logs PHP generated error messages
*
- * @access private
- * @param string the error severity
- * @param string the error string
- * @param string the error filepath
- * @param string the error line number
- * @return string
+ * @param int $severity Log level
+ * @param string $message Error message
+ * @param string $filepath File path
+ * @param int $line Line number
+ * @return void
*/
- function log_exception($severity, $message, $filepath, $line)
+ public function log_exception($severity, $message, $filepath, $line)
{
- $severity = ( ! isset($this->levels[$severity])) ? $severity : $this->levels[$severity];
-
- log_message('error', 'Severity: '.$severity.' --> '.$message. ' '.$filepath.' '.$line, TRUE);
+ $severity = isset($this->levels[$severity]) ? $this->levels[$severity] : $severity;
+ log_message('error', 'Severity: '.$severity.' --> '.$message.' '.$filepath.' '.$line);
}
// --------------------------------------------------------------------
/**
- * 404 Page Not Found Handler
+ * 404 Error Handler
*
- * @access private
- * @param string the page
- * @param bool log error yes/no
- * @return string
+ * @uses CI_Exceptions::show_error()
+ *
+ * @param string $page Page URI
+ * @param bool $log_error Whether to log the error
+ * @return void
*/
- function show_404($page = '', $log_error = TRUE)
+ public function show_404($page = '', $log_error = TRUE)
{
- $heading = "404 Page Not Found";
- $message = "The page you requested was not found.";
+ if (is_cli())
+ {
+ $heading = 'Not Found';
+ $message = 'The controller/method pair you requested was not found.';
+ }
+ else
+ {
+ $heading = '404 Page Not Found';
+ $message = 'The page you requested was not found.';
+ }
// By default we log this, but allow a dev to skip it
if ($log_error)
{
- log_message('error', '404 Page Not Found --> '.$page);
+ log_message('error', $heading.': '.$page);
}
echo $this->show_error($heading, $message, 'error_404', 404);
- exit;
+ exit(4); // EXIT_UNKNOWN_FILE
}
// --------------------------------------------------------------------
@@ -121,29 +145,46 @@ class CI_Exceptions {
/**
* General Error Page
*
- * This function takes an error message as input
- * (either as a string or an array) and displays
- * it using the specified template.
+ * Takes an error message as input (either as a string or an array)
+ * and displays it using the specified template.
*
- * @access private
- * @param string the heading
- * @param string the message
- * @param string the template name
- * @param int the status code
- * @return string
+ * @param string $heading Page heading
+ * @param string|string[] $message Error message
+ * @param string $template Template name
+ * @param int $status_code (default: 500)
+ *
+ * @return string Error page output
*/
- function show_error($heading, $message, $template = 'error_general', $status_code = 500)
+ public function show_error($heading, $message, $template = 'error_general', $status_code = 500)
{
- set_status_header($status_code);
+ $templates_path = config_item('error_views_path');
+ if (empty($templates_path))
+ {
+ $templates_path = VIEWPATH.'errors'.DIRECTORY_SEPARATOR;
+ }
+ else
+ {
+ $templates_path = rtrim($templates_path, '/\\').DIRECTORY_SEPARATOR;
+ }
- $message = '<p>'.implode('</p><p>', ( ! is_array($message)) ? array($message) : $message).'</p>';
+ if (is_cli())
+ {
+ $message = "\t".(is_array($message) ? implode("\n\t", $message) : $message);
+ $template = 'cli'.DIRECTORY_SEPARATOR.$template;
+ }
+ else
+ {
+ set_status_header($status_code);
+ $message = '<p>'.(is_array($message) ? implode('</p><p>', $message) : $message).'</p>';
+ $template = 'html'.DIRECTORY_SEPARATOR.$template;
+ }
if (ob_get_level() > $this->ob_level + 1)
{
ob_end_flush();
}
ob_start();
- include(APPPATH.'errors/'.$template.'.php');
+ include($templates_path.$template.'.php');
$buffer = ob_get_contents();
ob_end_clean();
return $buffer;
@@ -151,27 +192,85 @@ class CI_Exceptions {
// --------------------------------------------------------------------
+ public function show_exception($exception)
+ {
+ $templates_path = config_item('error_views_path');
+ if (empty($templates_path))
+ {
+ $templates_path = VIEWPATH.'errors'.DIRECTORY_SEPARATOR;
+ }
+ else
+ {
+ $templates_path = rtrim($templates_path, '/\\').DIRECTORY_SEPARATOR;
+ }
+
+ $message = $exception->getMessage();
+ if (empty($message))
+ {
+ $message = '(null)';
+ }
+
+ if (is_cli())
+ {
+ $templates_path .= 'cli'.DIRECTORY_SEPARATOR;
+ }
+ else
+ {
+ $templates_path .= 'html'.DIRECTORY_SEPARATOR;
+ }
+
+ if (ob_get_level() > $this->ob_level + 1)
+ {
+ ob_end_flush();
+ }
+
+ ob_start();
+ include($templates_path.'error_exception.php');
+ $buffer = ob_get_contents();
+ ob_end_clean();
+ echo $buffer;
+ }
+
+ // --------------------------------------------------------------------
+
/**
* Native PHP error handler
*
- * @access private
- * @param string the error severity
- * @param string the error string
- * @param string the error filepath
- * @param string the error line number
- * @return string
+ * @param int $severity Error level
+ * @param string $message Error message
+ * @param string $filepath File path
+ * @param int $line Line number
+ * @return void
*/
- function show_php_error($severity, $message, $filepath, $line)
+ public function show_php_error($severity, $message, $filepath, $line)
{
- $severity = ( ! isset($this->levels[$severity])) ? $severity : $this->levels[$severity];
+ $templates_path = config_item('error_views_path');
+ if (empty($templates_path))
+ {
+ $templates_path = VIEWPATH.'errors'.DIRECTORY_SEPARATOR;
+ }
+ else
+ {
+ $templates_path = rtrim($templates_path, '/\\').DIRECTORY_SEPARATOR;
+ }
- $filepath = str_replace("\\", "/", $filepath);
+ $severity = isset($this->levels[$severity]) ? $this->levels[$severity] : $severity;
- // For safety reasons we do not show the full file path
- if (FALSE !== strpos($filepath, '/'))
+ // For safety reasons we don't show the full file path in non-CLI requests
+ if ( ! is_cli())
{
- $x = explode('/', $filepath);
- $filepath = $x[count($x)-2].'/'.end($x);
+ $filepath = str_replace('\\', '/', $filepath);
+ if (FALSE !== strpos($filepath, '/'))
+ {
+ $x = explode('/', $filepath);
+ $filepath = $x[count($x)-2].'/'.end($x);
+ }
+
+ $template = 'html'.DIRECTORY_SEPARATOR.'error_php';
+ }
+ else
+ {
+ $template = 'cli'.DIRECTORY_SEPARATOR.'error_php';
}
if (ob_get_level() > $this->ob_level + 1)
@@ -179,15 +278,10 @@ class CI_Exceptions {
ob_end_flush();
}
ob_start();
- include(APPPATH.'errors/error_php.php');
+ include($templates_path.$template.'.php');
$buffer = ob_get_contents();
ob_end_clean();
echo $buffer;
}
-
}
-// END Exceptions Class
-
-/* End of file Exceptions.php */
-/* Location: ./system/core/Exceptions.php */ \ No newline at end of file
diff --git a/system/core/Hooks.php b/system/core/Hooks.php
index ee5c23076..20248aa8a 100644
--- a/system/core/Hooks.php
+++ b/system/core/Hooks.php
@@ -1,95 +1,115 @@
-<?php if ( ! defined('BASEPATH')) exit('No direct script access allowed');
+<?php
/**
* CodeIgniter
*
- * An open source application development framework for PHP 5.1.6 or newer
+ * An open source application development framework for PHP
*
- * @package CodeIgniter
- * @author ExpressionEngine Dev Team
- * @copyright Copyright (c) 2008 - 2014, EllisLab, Inc.
- * @license http://codeigniter.com/user_guide/license.html
- * @link http://codeigniter.com
- * @since Version 1.0
+ * This content is released under the MIT License (MIT)
+ *
+ * Copyright (c) 2019 - 2022, CodeIgniter Foundation
+ *
+ * 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.
+ *
+ * @package CodeIgniter
+ * @author EllisLab Dev Team
+ * @copyright Copyright (c) 2008 - 2014, EllisLab, Inc. (https://ellislab.com/)
+ * @copyright Copyright (c) 2014 - 2019, British Columbia Institute of Technology (https://bcit.ca/)
+ * @copyright Copyright (c) 2019 - 2022, CodeIgniter Foundation (https://codeigniter.com/)
+ * @license https://opensource.org/licenses/MIT MIT License
+ * @link https://codeigniter.com
+ * @since Version 1.0.0
* @filesource
*/
-
-// ------------------------------------------------------------------------
+defined('BASEPATH') OR exit('No direct script access allowed');
/**
- * CodeIgniter Hooks Class
+ * Hooks Class
*
* Provides a mechanism to extend the base system without hacking.
*
* @package CodeIgniter
* @subpackage Libraries
* @category Libraries
- * @author ExpressionEngine Dev Team
- * @link http://codeigniter.com/user_guide/libraries/encryption.html
+ * @author EllisLab Dev Team
+ * @link https://codeigniter.com/userguide3/general/hooks.html
*/
class CI_Hooks {
/**
- * Determines wether hooks are enabled
+ * Determines whether hooks are enabled
*
- * @var bool
+ * @var bool
*/
- var $enabled = FALSE;
+ public $enabled = FALSE;
+
/**
* List of all hooks set in config/hooks.php
*
- * @var array
+ * @var array
*/
- var $hooks = array();
+ public $hooks = array();
+
/**
- * Determines wether hook is in progress, used to prevent infinte loops
+ * Array with class objects to use hooks methods
*
- * @var bool
+ * @var array
*/
- var $in_progress = FALSE;
+ protected $_objects = array();
/**
- * Constructor
+ * In progress flag
*
+ * Determines whether hook is in progress, used to prevent infinte loops
+ *
+ * @var bool
*/
- function __construct()
- {
- $this->_initialize();
- log_message('debug', "Hooks Class Initialized");
- }
-
- // --------------------------------------------------------------------
+ protected $_in_progress = FALSE;
/**
- * Initialize the Hooks Preferences
+ * Class constructor
*
- * @access private
+ * @param CI_Config $config
* @return void
*/
- function _initialize()
+ public function __construct(CI_Config $config)
{
- $CFG =& load_class('Config', 'core');
+ log_message('info', 'Hooks Class Initialized');
// If hooks are not enabled in the config file
// there is nothing else to do
-
- if ($CFG->item('enable_hooks') == FALSE)
+ if ($config->item('enable_hooks') === FALSE)
{
return;
}
// Grab the "hooks" definition file.
- // If there are no hooks, we're done.
-
- if (defined('ENVIRONMENT') AND is_file(APPPATH.'config/'.ENVIRONMENT.'/hooks.php'))
- {
- include(APPPATH.'config/'.ENVIRONMENT.'/hooks.php');
- }
- elseif (is_file(APPPATH.'config/hooks.php'))
+ if (file_exists(APPPATH.'config/hooks.php'))
{
include(APPPATH.'config/hooks.php');
}
+ if (file_exists(APPPATH.'config/'.ENVIRONMENT.'/hooks.php'))
+ {
+ include(APPPATH.'config/'.ENVIRONMENT.'/hooks.php');
+ }
+ // If there are no hooks, we're done.
if ( ! isset($hook) OR ! is_array($hook))
{
return;
@@ -104,20 +124,21 @@ class CI_Hooks {
/**
* Call Hook
*
- * Calls a particular hook
+ * Calls a particular hook. Called by CodeIgniter.php.
*
- * @access private
- * @param string the hook name
- * @return mixed
+ * @uses CI_Hooks::_run_hook()
+ *
+ * @param string $which Hook name
+ * @return bool TRUE on success or FALSE on failure
*/
- function _call_hook($which = '')
+ public function call_hook($which = '')
{
if ( ! $this->enabled OR ! isset($this->hooks[$which]))
{
return FALSE;
}
- if (isset($this->hooks[$which][0]) AND is_array($this->hooks[$which][0]))
+ if (is_array($this->hooks[$which]) && ! isset($this->hooks[$which]['function']))
{
foreach ($this->hooks[$which] as $val)
{
@@ -139,13 +160,21 @@ class CI_Hooks {
*
* Runs a particular hook
*
- * @access private
- * @param array the hook details
- * @return bool
+ * @param array $data Hook details
+ * @return bool TRUE on success or FALSE on failure
*/
- function _run_hook($data)
+ protected function _run_hook($data)
{
- if ( ! is_array($data))
+ // Closures/lambda functions and array($object, 'method') callables
+ if (is_callable($data))
+ {
+ is_array($data)
+ ? $data[0]->{$data[1]}()
+ : $data();
+
+ return TRUE;
+ }
+ elseif ( ! is_array($data))
{
return FALSE;
}
@@ -156,8 +185,7 @@ class CI_Hooks {
// If the script being called happens to have the same
// hook call within it a loop can happen
-
- if ($this->in_progress == TRUE)
+ if ($this->_in_progress === TRUE)
{
return;
}
@@ -166,7 +194,7 @@ class CI_Hooks {
// Set file path
// -----------------------------------
- if ( ! isset($data['filepath']) OR ! isset($data['filename']))
+ if ( ! isset($data['filepath'], $data['filename']))
{
return FALSE;
}
@@ -178,71 +206,62 @@ class CI_Hooks {
return FALSE;
}
- // -----------------------------------
- // Set class/function name
- // -----------------------------------
-
- $class = FALSE;
- $function = FALSE;
- $params = '';
-
- if (isset($data['class']) AND $data['class'] != '')
- {
- $class = $data['class'];
- }
-
- if (isset($data['function']))
- {
- $function = $data['function'];
- }
-
- if (isset($data['params']))
- {
- $params = $data['params'];
- }
+ // Determine and class and/or function names
+ $class = empty($data['class']) ? FALSE : $data['class'];
+ $function = empty($data['function']) ? FALSE : $data['function'];
+ $params = isset($data['params']) ? $data['params'] : '';
- if ($class === FALSE AND $function === FALSE)
+ if (empty($function))
{
return FALSE;
}
- // -----------------------------------
- // Set the in_progress flag
- // -----------------------------------
-
- $this->in_progress = TRUE;
+ // Set the _in_progress flag
+ $this->_in_progress = TRUE;
- // -----------------------------------
// Call the requested class and/or function
- // -----------------------------------
-
if ($class !== FALSE)
{
- if ( ! class_exists($class))
+ // The object is stored?
+ if (isset($this->_objects[$class]))
{
- require($filepath);
+ if (method_exists($this->_objects[$class], $function))
+ {
+ $this->_objects[$class]->$function($params);
+ }
+ else
+ {
+ return $this->_in_progress = FALSE;
+ }
}
+ else
+ {
+ class_exists($class, FALSE) OR require_once($filepath);
+
+ if ( ! class_exists($class, FALSE) OR ! method_exists($class, $function))
+ {
+ return $this->_in_progress = FALSE;
+ }
- $HOOK = new $class;
- $HOOK->$function($params);
+ // Store the object and execute the method
+ $this->_objects[$class] = new $class();
+ $this->_objects[$class]->$function($params);
+ }
}
else
{
+ function_exists($function) OR require_once($filepath);
+
if ( ! function_exists($function))
{
- require($filepath);
+ return $this->_in_progress = FALSE;
}
$function($params);
}
- $this->in_progress = FALSE;
+ $this->_in_progress = FALSE;
return TRUE;
}
}
-
-// END CI_Hooks class
-
-/* End of file Hooks.php */
-/* Location: ./system/core/Hooks.php */ \ No newline at end of file
diff --git a/system/core/Input.php b/system/core/Input.php
index 7bc34231d..59ca3e112 100644
--- a/system/core/Input.php
+++ b/system/core/Input.php
@@ -1,19 +1,42 @@
-<?php if ( ! defined('BASEPATH')) exit('No direct script access allowed');
+<?php
/**
* CodeIgniter
*
- * An open source application development framework for PHP 5.1.6 or newer
+ * An open source application development framework for PHP
*
- * @package CodeIgniter
- * @author ExpressionEngine Dev Team
- * @copyright Copyright (c) 2008 - 2014, EllisLab, Inc.
- * @license http://codeigniter.com/user_guide/license.html
- * @link http://codeigniter.com
- * @since Version 1.0
+ * This content is released under the MIT License (MIT)
+ *
+ * Copyright (c) 2019 - 2022, CodeIgniter Foundation
+ *
+ * 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.
+ *
+ * @package CodeIgniter
+ * @author EllisLab Dev Team
+ * @copyright Copyright (c) 2008 - 2014, EllisLab, Inc. (https://ellislab.com/)
+ * @copyright Copyright (c) 2014 - 2019, British Columbia Institute of Technology (https://bcit.ca/)
+ * @copyright Copyright (c) 2019 - 2022, CodeIgniter Foundation (https://codeigniter.com/)
+ * @license https://opensource.org/licenses/MIT MIT License
+ * @link https://codeigniter.com
+ * @since Version 1.0.0
* @filesource
*/
-
-// ------------------------------------------------------------------------
+defined('BASEPATH') OR exit('No direct script access allowed');
/**
* Input Class
@@ -23,84 +46,68 @@
* @package CodeIgniter
* @subpackage Libraries
* @category Input
- * @author ExpressionEngine Dev Team
- * @link http://codeigniter.com/user_guide/libraries/input.html
+ * @author EllisLab Dev Team
+ * @link https://codeigniter.com/userguide3/libraries/input.html
*/
class CI_Input {
/**
* IP address of the current user
*
- * @var string
+ * @var string
*/
- var $ip_address = FALSE;
+ protected $ip_address = FALSE;
+
/**
- * user agent (web browser) being used by the current user
+ * List of all HTTP request headers
*
- * @var string
+ * @var array
*/
- var $user_agent = FALSE;
+ protected $headers = array();
+
/**
- * If FALSE, then $_GET will be set to an empty array
+ * Raw input stream data
*
- * @var bool
- */
- var $_allow_get_array = TRUE;
- /**
- * If TRUE, then newlines are standardized
+ * Holds a cache of php://input contents
*
- * @var bool
+ * @var string
*/
- var $_standardize_newlines = TRUE;
+ protected $_raw_input_stream;
+
/**
- * Determines whether the XSS filter is always active when GET, POST or COOKIE data is encountered
- * Set automatically based on config setting
+ * Parsed input stream data
*
- * @var bool
- */
- var $_enable_xss = FALSE;
- /**
- * Enables a CSRF cookie token to be set.
- * Set automatically based on config setting
+ * Parsed from php://input at runtime
*
- * @var bool
+ * @see CI_Input::input_stream()
+ * @var array
*/
- var $_enable_csrf = FALSE;
+ protected $_input_stream;
+
/**
- * List of all HTTP request headers
+ * CI_Security instance
*
- * @var array
+ * Used for the optional $xss_filter parameter that most
+ * getter methods have here.
+ *
+ * @var CI_Security
*/
- protected $headers = array();
+ protected $security;
+
+ // --------------------------------------------------------------------
/**
- * Constructor
+ * Class constructor
*
- * Sets whether to globally enable the XSS processing
- * and whether to allow the $_GET array
+ * Determines whether to globally enable the XSS processing
+ * and whether to allow the $_GET array.
*
* @return void
*/
- public function __construct()
+ public function __construct(CI_Security &$security)
{
- log_message('debug', "Input Class Initialized");
-
- $this->_allow_get_array = (config_item('allow_get_array') === TRUE);
- $this->_enable_xss = (config_item('global_xss_filtering') === TRUE);
- $this->_enable_csrf = (config_item('csrf_protection') === TRUE);
-
- global $SEC;
- $this->security =& $SEC;
-
- // Do we need the UTF-8 class?
- if (UTF8_ENABLED === TRUE)
- {
- global $UNI;
- $this->uni =& $UNI;
- }
-
- // Sanitize global arrays
- $this->_sanitize_globals();
+ $this->security = $security;
+ log_message('info', 'Input Class Initialized');
}
// --------------------------------------------------------------------
@@ -108,147 +115,201 @@ class CI_Input {
/**
* Fetch from array
*
- * This is a helper function to retrieve values from global arrays
+ * Internal method used to retrieve values from global arrays.
*
- * @access private
- * @param array
- * @param string
- * @param bool
- * @return string
+ * @param array &$array $_GET, $_POST, $_COOKIE, $_SERVER, etc.
+ * @param mixed $index Index for item to be fetched from $array
+ * @param bool $xss_clean Whether to apply XSS filtering
+ * @return mixed
*/
- function _fetch_from_array(&$array, $index = '', $xss_clean = FALSE)
+ protected function _fetch_from_array(&$array, $index = NULL, $xss_clean = FALSE)
{
- if ( ! isset($array[$index]))
+ // If $index is NULL, it means that the whole $array is requested
+ isset($index) OR $index = array_keys($array);
+
+ // allow fetching multiple keys at once
+ if (is_array($index))
{
- return FALSE;
+ $output = array();
+ foreach ($index as $key)
+ {
+ $output[$key] = $this->_fetch_from_array($array, $key, $xss_clean);
+ }
+
+ return $output;
}
- if ($xss_clean === TRUE)
+ if (isset($array[$index]))
{
- return $this->security->xss_clean($array[$index]);
+ $value = $array[$index];
}
+ elseif (($count = preg_match_all('/(?:^[^\[]+)|\[[^]]*\]/', $index, $matches)) > 1) // Does the index contain array notation
+ {
+ $value = $array;
+ for ($i = 0; $i < $count; $i++)
+ {
+ $key = trim($matches[0][$i], '[]');
+ if ($key === '') // Empty notation will return the value as array
+ {
+ break;
+ }
- return $array[$index];
+ if (isset($value[$key]))
+ {
+ $value = $value[$key];
+ }
+ else
+ {
+ return NULL;
+ }
+ }
+ }
+ else
+ {
+ return NULL;
+ }
+
+ return ($xss_clean === TRUE)
+ ? $this->security->xss_clean($value)
+ : $value;
}
// --------------------------------------------------------------------
/**
- * Fetch an item from the GET array
- *
- * @access public
- * @param string
- * @param bool
- * @return string
- */
- function get($index = NULL, $xss_clean = FALSE)
+ * Fetch an item from the GET array
+ *
+ * @param mixed $index Index for item to be fetched from $_GET
+ * @param bool $xss_clean Whether to apply XSS filtering
+ * @return mixed
+ */
+ public function get($index = NULL, $xss_clean = FALSE)
{
- // Check if a field has been provided
- if ($index === NULL AND ! empty($_GET))
- {
- $get = array();
-
- // loop through the full _GET array
- foreach (array_keys($_GET) as $key)
- {
- $get[$key] = $this->_fetch_from_array($_GET, $key, $xss_clean);
- }
- return $get;
- }
-
return $this->_fetch_from_array($_GET, $index, $xss_clean);
}
// --------------------------------------------------------------------
/**
- * Fetch an item from the POST array
- *
- * @access public
- * @param string
- * @param bool
- * @return string
- */
- function post($index = NULL, $xss_clean = FALSE)
+ * Fetch an item from the POST array
+ *
+ * @param mixed $index Index for item to be fetched from $_POST
+ * @param bool $xss_clean Whether to apply XSS filtering
+ * @return mixed
+ */
+ public function post($index = NULL, $xss_clean = FALSE)
{
- // Check if a field has been provided
- if ($index === NULL AND ! empty($_POST))
- {
- $post = array();
-
- // Loop through the full _POST array and return it
- foreach (array_keys($_POST) as $key)
- {
- $post[$key] = $this->_fetch_from_array($_POST, $key, $xss_clean);
- }
- return $post;
- }
-
return $this->_fetch_from_array($_POST, $index, $xss_clean);
}
+ // --------------------------------------------------------------------
+
+ /**
+ * Fetch an item from POST data with fallback to GET
+ *
+ * @param string $index Index for item to be fetched from $_POST or $_GET
+ * @param bool $xss_clean Whether to apply XSS filtering
+ * @return mixed
+ */
+ public function post_get($index, $xss_clean = FALSE)
+ {
+ $output = $this->post($index, $xss_clean);
+ return isset($output) ? $output : $this->get($index, $xss_clean);
+ }
// --------------------------------------------------------------------
/**
- * Fetch an item from either the GET array or the POST
- *
- * @access public
- * @param string The index key
- * @param bool XSS cleaning
- * @return string
- */
- function get_post($index = '', $xss_clean = FALSE)
+ * Fetch an item from GET data with fallback to POST
+ *
+ * @param string $index Index for item to be fetched from $_GET or $_POST
+ * @param bool $xss_clean Whether to apply XSS filtering
+ * @return mixed
+ */
+ public function get_post($index, $xss_clean = FALSE)
{
- if ( ! isset($_POST[$index]) )
- {
- return $this->get($index, $xss_clean);
- }
- else
- {
- return $this->post($index, $xss_clean);
- }
+ $output = $this->get($index, $xss_clean);
+ return isset($output) ? $output : $this->post($index, $xss_clean);
}
// --------------------------------------------------------------------
/**
- * Fetch an item from the COOKIE array
- *
- * @access public
- * @param string
- * @param bool
- * @return string
- */
- function cookie($index = '', $xss_clean = FALSE)
+ * Fetch an item from the COOKIE array
+ *
+ * @param mixed $index Index for item to be fetched from $_COOKIE
+ * @param bool $xss_clean Whether to apply XSS filtering
+ * @return mixed
+ */
+ public function cookie($index = NULL, $xss_clean = FALSE)
{
return $this->_fetch_from_array($_COOKIE, $index, $xss_clean);
}
+ // --------------------------------------------------------------------
+
+ /**
+ * Fetch an item from the SERVER array
+ *
+ * @param mixed $index Index for item to be fetched from $_SERVER
+ * @param bool $xss_clean Whether to apply XSS filtering
+ * @return mixed
+ */
+ public function server($index, $xss_clean = FALSE)
+ {
+ return $this->_fetch_from_array($_SERVER, $index, $xss_clean);
+ }
+
// ------------------------------------------------------------------------
/**
- * Set cookie
- *
- * Accepts six parameter, or you can submit an associative
- * array in the first parameter containing all the values.
- *
- * @access public
- * @param mixed
- * @param string the value of the cookie
- * @param string the number of seconds until expiration
- * @param string the cookie domain. Usually: .yourdomain.com
- * @param string the cookie path
- * @param string the cookie prefix
- * @param bool true makes the cookie secure
- * @return void
- */
- function set_cookie($name = '', $value = '', $expire = '', $domain = '', $path = '/', $prefix = '', $secure = FALSE)
+ * Fetch an item from the php://input stream
+ *
+ * Useful when you need to access PUT, DELETE or PATCH request data.
+ *
+ * @param string $index Index for item to be fetched
+ * @param bool $xss_clean Whether to apply XSS filtering
+ * @return mixed
+ */
+ public function input_stream($index = NULL, $xss_clean = FALSE)
+ {
+ // Prior to PHP 5.6, the input stream can only be read once,
+ // so we'll need to check if we have already done that first.
+ if ( ! is_array($this->_input_stream))
+ {
+ // $this->raw_input_stream will trigger __get().
+ parse_str($this->raw_input_stream, $this->_input_stream);
+ is_array($this->_input_stream) OR $this->_input_stream = array();
+ }
+
+ return $this->_fetch_from_array($this->_input_stream, $index, $xss_clean);
+ }
+
+ // ------------------------------------------------------------------------
+
+ /**
+ * Set cookie
+ *
+ * Accepts an arbitrary number of parameters (up to 7) or an associative
+ * array in the first parameter containing all the values.
+ *
+ * @param string|mixed[] $name Cookie name or an array containing parameters
+ * @param string $value Cookie value
+ * @param int $expire Cookie expiration time in seconds
+ * @param string $domain Cookie domain (e.g.: '.yourdomain.com')
+ * @param string $path Cookie path (default: '/')
+ * @param string $prefix Cookie name prefix
+ * @param bool $secure Whether to only transfer cookies via SSL
+ * @param bool $httponly Whether to only makes the cookie accessible via HTTP (no javascript)
+ * @param string $samesite SameSite attribute
+ * @return void
+ */
+ public function set_cookie($name, $value = '', $expire = 0, $domain = '', $path = '/', $prefix = '', $secure = NULL, $httponly = NULL, $samesite = NULL)
{
if (is_array($name))
{
// always leave 'name' in last place, as the loop will break otherwise, due to $$item
- foreach (array('value', 'expire', 'domain', 'path', 'prefix', 'secure', 'name') as $item)
+ foreach (array('value', 'expire', 'domain', 'path', 'prefix', 'secure', 'httponly', 'name', 'samesite') as $item)
{
if (isset($name[$item]))
{
@@ -257,57 +318,90 @@ class CI_Input {
}
}
- if ($prefix == '' AND config_item('cookie_prefix') != '')
+ if ($prefix === '' && config_item('cookie_prefix') !== '')
{
$prefix = config_item('cookie_prefix');
}
- if ($domain == '' AND config_item('cookie_domain') != '')
+
+ if ($domain == '' && config_item('cookie_domain') != '')
{
$domain = config_item('cookie_domain');
}
- if ($path == '/' AND config_item('cookie_path') != '/')
+
+ if ($path === '/' && config_item('cookie_path') !== '/')
{
$path = config_item('cookie_path');
}
- if ($secure == FALSE AND config_item('cookie_secure') != FALSE)
+
+ $secure = ($secure === NULL && config_item('cookie_secure') !== NULL)
+ ? (bool) config_item('cookie_secure')
+ : (bool) $secure;
+
+ $httponly = ($httponly === NULL && config_item('cookie_httponly') !== NULL)
+ ? (bool) config_item('cookie_httponly')
+ : (bool) $httponly;
+
+ if ( ! is_numeric($expire) OR $expire < 0)
{
- $secure = config_item('cookie_secure');
+ $expire = 1;
+ }
+ else
+ {
+ $expire = ($expire > 0) ? time() + $expire : 0;
}
- if ( ! is_numeric($expire))
+ isset($samesite) OR $samesite = config_item('cookie_samesite');
+ if (isset($samesite))
{
- $expire = time() - 86500;
+ $samesite = ucfirst(strtolower($samesite));
+ in_array($samesite, array('Lax', 'Strict', 'None'), TRUE) OR $samesite = 'Lax';
}
else
{
- $expire = ($expire > 0) ? time() + $expire : 0;
+ $samesite = 'Lax';
}
- setcookie($prefix.$name, $value, $expire, $path, $domain, $secure);
- }
+ if ($samesite === 'None' && ! $secure)
+ {
+ log_message('error', $name.' cookie sent with SameSite=None, but without Secure attribute.');
+ }
- // --------------------------------------------------------------------
+ if ( ! is_php('7.3'))
+ {
+ $maxage = $expire - time();
+ if ($maxage < 1)
+ {
+ $maxage = 0;
+ }
- /**
- * Fetch an item from the SERVER array
- *
- * @access public
- * @param string
- * @param bool
- * @return string
- */
- function server($index = '', $xss_clean = FALSE)
- {
- return $this->_fetch_from_array($_SERVER, $index, $xss_clean);
+ $cookie_header = 'Set-Cookie: '.$prefix.$name.'='.rawurlencode($value);
+ $cookie_header .= ($expire === 0 ? '' : '; Expires='.gmdate('D, d-M-Y H:i:s T', $expire)).'; Max-Age='.$maxage;
+ $cookie_header .= '; Path='.$path.($domain !== '' ? '; Domain='.$domain : '');
+ $cookie_header .= ($secure ? '; Secure' : '').($httponly ? '; HttpOnly' : '').'; SameSite='.$samesite;
+ header($cookie_header);
+ return;
+ }
+
+ $setcookie_options = array(
+ 'expires' => $expire,
+ 'path' => $path,
+ 'domain' => $domain,
+ 'secure' => $secure,
+ 'httponly' => $httponly,
+ 'samesite' => $samesite,
+ );
+ setcookie($prefix.$name, $value, $setcookie_options);
}
// --------------------------------------------------------------------
/**
- * Fetch the IP Address
- *
- * @return string
- */
+ * Fetch the IP Address
+ *
+ * Determines and validates the visitor's IP address.
+ *
+ * @return string IP address
+ */
public function ip_address()
{
if ($this->ip_address !== FALSE)
@@ -316,25 +410,27 @@ class CI_Input {
}
$proxy_ips = config_item('proxy_ips');
- if ( ! empty($proxy_ips))
+ if ( ! empty($proxy_ips) && ! is_array($proxy_ips))
{
$proxy_ips = explode(',', str_replace(' ', '', $proxy_ips));
+ }
+
+ $this->ip_address = $this->server('REMOTE_ADDR');
+
+ if ($proxy_ips)
+ {
foreach (array('HTTP_X_FORWARDED_FOR', 'HTTP_CLIENT_IP', 'HTTP_X_CLIENT_IP', 'HTTP_X_CLUSTER_CLIENT_IP') as $header)
{
- if (($spoof = $this->server($header)) !== FALSE)
+ if (($spoof = $this->server($header)) !== NULL)
{
// Some proxies typically list the whole chain of IP
// addresses through which the client has reached us.
// e.g. client_ip, proxy_ip1, proxy_ip2, etc.
- if (strpos($spoof, ',') !== FALSE)
- {
- $spoof = explode(',', $spoof, 2);
- $spoof = $spoof[0];
- }
+ sscanf($spoof, '%[^,]', $spoof);
if ( ! $this->valid_ip($spoof))
{
- $spoof = FALSE;
+ $spoof = NULL;
}
else
{
@@ -343,401 +439,134 @@ class CI_Input {
}
}
- $this->ip_address = ($spoof !== FALSE && in_array($_SERVER['REMOTE_ADDR'], $proxy_ips, TRUE))
- ? $spoof : $_SERVER['REMOTE_ADDR'];
- }
- else
- {
- $this->ip_address = $_SERVER['REMOTE_ADDR'];
- }
-
- if ( ! $this->valid_ip($this->ip_address))
- {
- $this->ip_address = '0.0.0.0';
- }
-
- return $this->ip_address;
- }
-
- // --------------------------------------------------------------------
-
- /**
- * Validate IP Address
- *
- * @access public
- * @param string
- * @param string ipv4 or ipv6
- * @return bool
- */
- public function valid_ip($ip, $which = '')
- {
- $which = strtolower($which);
-
- // First check if filter_var is available
- if (is_callable('filter_var'))
- {
- switch ($which) {
- case 'ipv4':
- $flag = FILTER_FLAG_IPV4;
- break;
- case 'ipv6':
- $flag = FILTER_FLAG_IPV6;
- break;
- default:
- $flag = '';
- break;
- }
-
- return (bool) filter_var($ip, FILTER_VALIDATE_IP, $flag);
- }
-
- if ($which !== 'ipv6' && $which !== 'ipv4')
- {
- if (strpos($ip, ':') !== FALSE)
- {
- $which = 'ipv6';
- }
- elseif (strpos($ip, '.') !== FALSE)
- {
- $which = 'ipv4';
- }
- else
- {
- return FALSE;
- }
- }
-
- $func = '_valid_'.$which;
- return $this->$func($ip);
- }
-
- // --------------------------------------------------------------------
-
- /**
- * Validate IPv4 Address
- *
- * Updated version suggested by Geert De Deckere
- *
- * @access protected
- * @param string
- * @return bool
- */
- protected function _valid_ipv4($ip)
- {
- $ip_segments = explode('.', $ip);
-
- // Always 4 segments needed
- if (count($ip_segments) !== 4)
- {
- return FALSE;
- }
- // IP can not start with 0
- if ($ip_segments[0][0] == '0')
- {
- return FALSE;
- }
-
- // Check each segment
- foreach ($ip_segments as $segment)
- {
- // IP segments must be digits and can not be
- // longer than 3 digits or greater then 255
- if ($segment == '' OR preg_match("/[^0-9]/", $segment) OR $segment > 255 OR strlen($segment) > 3)
- {
- return FALSE;
- }
- }
-
- return TRUE;
- }
-
- // --------------------------------------------------------------------
-
- /**
- * Validate IPv6 Address
- *
- * @access protected
- * @param string
- * @return bool
- */
- protected function _valid_ipv6($str)
- {
- // 8 groups, separated by :
- // 0-ffff per group
- // one set of consecutive 0 groups can be collapsed to ::
-
- $groups = 8;
- $collapsed = FALSE;
-
- $chunks = array_filter(
- preg_split('/(:{1,2})/', $str, NULL, PREG_SPLIT_DELIM_CAPTURE)
- );
-
- // Rule out easy nonsense
- if (current($chunks) == ':' OR end($chunks) == ':')
- {
- return FALSE;
- }
-
- // PHP supports IPv4-mapped IPv6 addresses, so we'll expect those as well
- if (strpos(end($chunks), '.') !== FALSE)
- {
- $ipv4 = array_pop($chunks);
-
- if ( ! $this->_valid_ipv4($ipv4))
- {
- return FALSE;
- }
-
- $groups--;
- }
-
- while ($seg = array_pop($chunks))
- {
- if ($seg[0] == ':')
+ if ($spoof)
{
- if (--$groups == 0)
+ for ($i = 0, $c = count($proxy_ips); $i < $c; $i++)
{
- return FALSE; // too many groups
- }
-
- if (strlen($seg) > 2)
- {
- return FALSE; // long separator
- }
-
- if ($seg == '::')
- {
- if ($collapsed)
+ // Check if we have an IP address or a subnet
+ if (strpos($proxy_ips[$i], '/') === FALSE)
{
- return FALSE; // multiple collapsed
+ // An IP address (and not a subnet) is specified.
+ // We can compare right away.
+ if ($proxy_ips[$i] === $this->ip_address)
+ {
+ $this->ip_address = $spoof;
+ break;
+ }
+
+ continue;
}
- $collapsed = TRUE;
- }
- }
- elseif (preg_match("/[^0-9a-f]/i", $seg) OR strlen($seg) > 4)
- {
- return FALSE; // invalid segment
- }
- }
+ // We have a subnet ... now the heavy lifting begins
+ isset($separator) OR $separator = $this->valid_ip($this->ip_address, 'ipv6') ? ':' : '.';
- return $collapsed OR $groups == 1;
- }
-
- // --------------------------------------------------------------------
-
- /**
- * User Agent
- *
- * @access public
- * @return string
- */
- function user_agent()
- {
- if ($this->user_agent !== FALSE)
- {
- return $this->user_agent;
- }
-
- $this->user_agent = ( ! isset($_SERVER['HTTP_USER_AGENT'])) ? FALSE : $_SERVER['HTTP_USER_AGENT'];
+ // If the proxy entry doesn't match the IP protocol - skip it
+ if (strpos($proxy_ips[$i], $separator) === FALSE)
+ {
+ continue;
+ }
- return $this->user_agent;
- }
+ // Convert the REMOTE_ADDR IP address to binary, if needed
+ if ( ! isset($ip, $sprintf))
+ {
+ if ($separator === ':')
+ {
+ // Make sure we're have the "full" IPv6 format
+ $ip = explode(':',
+ str_replace('::',
+ str_repeat(':', 9 - substr_count($this->ip_address, ':')),
+ $this->ip_address
+ )
+ );
+
+ for ($j = 0; $j < 8; $j++)
+ {
+ $ip[$j] = intval($ip[$j], 16);
+ }
+
+ $sprintf = '%016b%016b%016b%016b%016b%016b%016b%016b';
+ }
+ else
+ {
+ $ip = explode('.', $this->ip_address);
+ $sprintf = '%08b%08b%08b%08b';
+ }
+
+ $ip = vsprintf($sprintf, $ip);
+ }
- // --------------------------------------------------------------------
+ // Split the netmask length off the network address
+ sscanf($proxy_ips[$i], '%[^/]/%d', $netaddr, $masklen);
- /**
- * Sanitize Globals
- *
- * This function does the following:
- *
- * Unsets $_GET data (if query strings are not enabled)
- *
- * Unsets all globals if register_globals is enabled
- *
- * Standardizes newline characters to \n
- *
- * @access private
- * @return void
- */
- function _sanitize_globals()
- {
- // It would be "wrong" to unset any of these GLOBALS.
- $protected = array('_SERVER', '_GET', '_POST', '_FILES', '_REQUEST',
- '_SESSION', '_ENV', 'GLOBALS', 'HTTP_RAW_POST_DATA',
- 'system_folder', 'application_folder', 'BM', 'EXT',
- 'CFG', 'URI', 'RTR', 'OUT', 'IN');
-
- // Unset globals for securiy.
- // This is effectively the same as register_globals = off
- foreach (array($_GET, $_POST, $_COOKIE) as $global)
- {
- if ( ! is_array($global))
- {
- if ( ! in_array($global, $protected))
- {
- global $$global;
- $$global = NULL;
- }
- }
- else
- {
- foreach ($global as $key => $val)
- {
- if ( ! in_array($key, $protected))
+ // Again, an IPv6 address is most likely in a compressed form
+ if ($separator === ':')
{
- global $$key;
- $$key = NULL;
+ $netaddr = explode(':', str_replace('::', str_repeat(':', 9 - substr_count($netaddr, ':')), $netaddr));
+ for ($j = 0; $j < 8; $j++)
+ {
+ $netaddr[$j] = intval($netaddr[$j], 16);
+ }
+ }
+ else
+ {
+ $netaddr = explode('.', $netaddr);
}
- }
- }
- }
- // Is $_GET data allowed? If not we'll set the $_GET to an empty array
- if ($this->_allow_get_array == FALSE)
- {
- $_GET = array();
- }
- else
- {
- if (is_array($_GET) AND count($_GET) > 0)
- {
- foreach ($_GET as $key => $val)
- {
- $_GET[$this->_clean_input_keys($key)] = $this->_clean_input_data($val);
+ // Convert to binary and finally compare
+ if (strncmp($ip, vsprintf($sprintf, $netaddr), $masklen) === 0)
+ {
+ $this->ip_address = $spoof;
+ break;
+ }
}
}
}
- // Clean $_POST Data
- if (is_array($_POST) AND count($_POST) > 0)
- {
- foreach ($_POST as $key => $val)
- {
- $_POST[$this->_clean_input_keys($key)] = $this->_clean_input_data($val);
- }
- }
-
- // Clean $_COOKIE Data
- if (is_array($_COOKIE) AND count($_COOKIE) > 0)
- {
- // Also get rid of specially treated cookies that might be set by a server
- // or silly application, that are of no use to a CI application anyway
- // but that when present will trip our 'Disallowed Key Characters' alarm
- // http://www.ietf.org/rfc/rfc2109.txt
- // note that the key names below are single quoted strings, and are not PHP variables
- unset($_COOKIE['$Version']);
- unset($_COOKIE['$Path']);
- unset($_COOKIE['$Domain']);
-
- foreach ($_COOKIE as $key => $val)
- {
- $_COOKIE[$this->_clean_input_keys($key)] = $this->_clean_input_data($val);
- }
- }
-
- // Sanitize PHP_SELF
- $_SERVER['PHP_SELF'] = strip_tags($_SERVER['PHP_SELF']);
-
-
- // CSRF Protection check on HTTP requests
- if ($this->_enable_csrf == TRUE && ! $this->is_cli_request())
+ if ( ! $this->valid_ip($this->ip_address))
{
- $this->security->csrf_verify();
+ return $this->ip_address = '0.0.0.0';
}
- log_message('debug', "Global POST and COOKIE data sanitized");
+ return $this->ip_address;
}
// --------------------------------------------------------------------
/**
- * Clean Input Data
- *
- * This is a helper function. It escapes data and
- * standardizes newline characters to \n
- *
- * @access private
- * @param string
- * @return string
- */
- function _clean_input_data($str)
+ * Validate IP Address
+ *
+ * @param string $ip IP address
+ * @param string $which IP protocol: 'ipv4' or 'ipv6'
+ * @return bool
+ */
+ public function valid_ip($ip, $which = '')
{
- if (is_array($str))
+ switch (strtolower($which))
{
- $new_array = array();
- foreach ($str as $key => $val)
- {
- $new_array[$this->_clean_input_keys($key)] = $this->_clean_input_data($val);
- }
- return $new_array;
+ case 'ipv4':
+ $which = FILTER_FLAG_IPV4;
+ break;
+ case 'ipv6':
+ $which = FILTER_FLAG_IPV6;
+ break;
+ default:
+ $which = 0;
+ break;
}
- /* We strip slashes if magic quotes is on to keep things consistent
-
- NOTE: In PHP 5.4 get_magic_quotes_gpc() will always return 0 and
- it will probably not exist in future versions at all.
- */
- if ( ! is_php('5.4') && get_magic_quotes_gpc())
- {
- $str = stripslashes($str);
- }
-
- // Clean UTF-8 if supported
- if (UTF8_ENABLED === TRUE)
- {
- $str = $this->uni->clean_string($str);
- }
-
- // Remove control characters
- $str = remove_invisible_characters($str, false);
-
- // Should we filter the input data?
- if ($this->_enable_xss === TRUE)
- {
- $str = $this->security->xss_clean($str);
- }
-
- // Standardize newlines if needed
- if ($this->_standardize_newlines == TRUE)
- {
- if (strpos($str, "\r") !== FALSE)
- {
- $str = str_replace(array("\r\n", "\r", "\r\n\n"), PHP_EOL, $str);
- }
- }
-
- return $str;
+ return (bool) filter_var($ip, FILTER_VALIDATE_IP, $which);
}
// --------------------------------------------------------------------
/**
- * Clean Keys
- *
- * This is a helper function. To prevent malicious users
- * from trying to exploit keys we make sure that keys are
- * only named with alpha-numeric text and a few other items.
- *
- * @access private
- * @param string
- * @return string
- */
- function _clean_input_keys($str)
+ * Fetch User Agent string
+ *
+ * @return string|null User Agent string or NULL if it doesn't exist
+ */
+ public function user_agent($xss_clean = FALSE)
{
- if ( ! preg_match("/^[a-z0-9:_\/-]+$/i", $str))
- {
- exit('Disallowed Key Characters.');
- }
-
- // Clean UTF-8 if supported
- if (UTF8_ENABLED === TRUE)
- {
- $str = $this->uni->clean_string($str);
- }
-
- return $str;
+ return $this->_fetch_from_array($_SERVER, 'HTTP_USER_AGENT', $xss_clean);
}
// --------------------------------------------------------------------
@@ -745,43 +574,40 @@ class CI_Input {
/**
* Request Headers
*
- * In Apache, you can simply call apache_request_headers(), however for
- * people running other webservers the function is undefined.
- *
- * @param bool XSS cleaning
- *
- * @return array
+ * @param bool $xss_clean Whether to apply XSS filtering
+ * @return array
*/
public function request_headers($xss_clean = FALSE)
{
- // Look at Apache go!
+ // If header is already defined, return it immediately
+ if ( ! empty($this->headers))
+ {
+ return $this->_fetch_from_array($this->headers, NULL, $xss_clean);
+ }
+
+ // In Apache, you can simply call apache_request_headers()
if (function_exists('apache_request_headers'))
{
- $headers = apache_request_headers();
+ $this->headers = apache_request_headers();
}
else
{
- $headers['Content-Type'] = (isset($_SERVER['CONTENT_TYPE'])) ? $_SERVER['CONTENT_TYPE'] : @getenv('CONTENT_TYPE');
+ isset($_SERVER['CONTENT_TYPE']) && $this->headers['Content-Type'] = $_SERVER['CONTENT_TYPE'];
foreach ($_SERVER as $key => $val)
{
- if (strncmp($key, 'HTTP_', 5) === 0)
+ if (sscanf($key, 'HTTP_%s', $header) === 1)
{
- $headers[substr($key, 5)] = $this->_fetch_from_array($_SERVER, $key, $xss_clean);
+ // take SOME_HEADER and turn it into Some-Header
+ $header = str_replace('_', ' ', strtolower($header));
+ $header = str_replace(' ', '-', ucwords($header));
+
+ $this->headers[$header] = $_SERVER[$key];
}
}
}
- // take SOME_HEADER and turn it into Some-Header
- foreach ($headers as $key => $val)
- {
- $key = str_replace('_', ' ', strtolower($key));
- $key = str_replace(' ', '-', ucwords($key));
-
- $this->headers[$key] = $val;
- }
-
- return $this->headers;
+ return $this->_fetch_from_array($this->headers, NULL, $xss_clean);
}
// --------------------------------------------------------------------
@@ -791,59 +617,88 @@ class CI_Input {
*
* Returns the value of a single member of the headers class member
*
- * @param string array key for $this->headers
- * @param boolean XSS Clean or not
- * @return mixed FALSE on failure, string on success
+ * @param string $index Header name
+ * @param bool $xss_clean Whether to apply XSS filtering
+ * @return string|null The requested header on success or NULL on failure
*/
public function get_request_header($index, $xss_clean = FALSE)
{
- if (empty($this->headers))
- {
- $this->request_headers();
- }
+ static $headers;
- if ( ! isset($this->headers[$index]))
+ if ( ! isset($headers))
{
- return FALSE;
+ empty($this->headers) && $this->request_headers();
+ foreach ($this->headers as $key => $value)
+ {
+ $headers[strtolower($key)] = $value;
+ }
}
- if ($xss_clean === TRUE)
+ $index = strtolower($index);
+
+ if ( ! isset($headers[$index]))
{
- return $this->security->xss_clean($this->headers[$index]);
+ return NULL;
}
- return $this->headers[$index];
+ return ($xss_clean === TRUE)
+ ? $this->security->xss_clean($headers[$index])
+ : $headers[$index];
}
// --------------------------------------------------------------------
/**
- * Is ajax Request?
+ * Is AJAX request?
*
- * Test to see if a request contains the HTTP_X_REQUESTED_WITH header
+ * Test to see if a request contains the HTTP_X_REQUESTED_WITH header.
*
- * @return boolean
+ * @return bool
*/
public function is_ajax_request()
{
- return ($this->server('HTTP_X_REQUESTED_WITH') === 'XMLHttpRequest');
+ return ( ! empty($_SERVER['HTTP_X_REQUESTED_WITH']) && strtolower($_SERVER['HTTP_X_REQUESTED_WITH']) === 'xmlhttprequest');
}
// --------------------------------------------------------------------
/**
- * Is cli Request?
+ * Get Request Method
*
- * Test to see if a request was made from the command line
+ * Return the request method
*
- * @return bool
+ * @param bool $upper Whether to return in upper or lower case
+ * (default: FALSE)
+ * @return string
*/
- public function is_cli_request()
+ public function method($upper = FALSE)
{
- return (php_sapi_name() === 'cli' OR defined('STDIN'));
+ return ($upper)
+ ? strtoupper($this->server('REQUEST_METHOD'))
+ : strtolower($this->server('REQUEST_METHOD'));
}
-}
+ // ------------------------------------------------------------------------
+
+ /**
+ * Magic __get()
+ *
+ * Allows read access to protected properties
+ *
+ * @param string $name
+ * @return mixed
+ */
+ public function __get($name)
+ {
+ if ($name === 'raw_input_stream')
+ {
+ isset($this->_raw_input_stream) OR $this->_raw_input_stream = file_get_contents('php://input');
+ return $this->_raw_input_stream;
+ }
+ elseif ($name === 'ip_address')
+ {
+ return $this->ip_address;
+ }
+ }
-/* End of file Input.php */
-/* Location: ./system/core/Input.php */ \ No newline at end of file
+}
diff --git a/system/core/Lang.php b/system/core/Lang.php
index ef5d1080c..18299060c 100644
--- a/system/core/Lang.php
+++ b/system/core/Lang.php
@@ -1,19 +1,42 @@
-<?php if ( ! defined('BASEPATH')) exit('No direct script access allowed');
+<?php
/**
* CodeIgniter
*
- * An open source application development framework for PHP 5.1.6 or newer
+ * An open source application development framework for PHP
*
- * @package CodeIgniter
- * @author ExpressionEngine Dev Team
- * @copyright Copyright (c) 2008 - 2014, EllisLab, Inc.
- * @license http://codeigniter.com/user_guide/license.html
- * @link http://codeigniter.com
- * @since Version 1.0
+ * This content is released under the MIT License (MIT)
+ *
+ * Copyright (c) 2019 - 2022, CodeIgniter Foundation
+ *
+ * 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.
+ *
+ * @package CodeIgniter
+ * @author EllisLab Dev Team
+ * @copyright Copyright (c) 2008 - 2014, EllisLab, Inc. (https://ellislab.com/)
+ * @copyright Copyright (c) 2014 - 2019, British Columbia Institute of Technology (https://bcit.ca/)
+ * @copyright Copyright (c) 2019 - 2022, CodeIgniter Foundation (https://codeigniter.com/)
+ * @license https://opensource.org/licenses/MIT MIT License
+ * @link https://codeigniter.com
+ * @since Version 1.0.0
* @filesource
*/
-
-// ------------------------------------------------------------------------
+defined('BASEPATH') OR exit('No direct script access allowed');
/**
* Language Class
@@ -21,32 +44,33 @@
* @package CodeIgniter
* @subpackage Libraries
* @category Language
- * @author ExpressionEngine Dev Team
- * @link http://codeigniter.com/user_guide/libraries/language.html
+ * @author EllisLab Dev Team
+ * @link https://codeigniter.com/userguide3/libraries/language.html
*/
class CI_Lang {
/**
* List of translations
*
- * @var array
+ * @var array
*/
- var $language = array();
+ public $language = array();
+
/**
* List of loaded language files
*
- * @var array
+ * @var array
*/
- var $is_loaded = array();
+ public $is_loaded = array();
/**
- * Constructor
+ * Class constructor
*
- * @access public
+ * @return void
*/
- function __construct()
+ public function __construct()
{
- log_message('debug', "Language Class Initialized");
+ log_message('info', 'Language Class Initialized');
}
// --------------------------------------------------------------------
@@ -54,98 +78,122 @@ class CI_Lang {
/**
* Load a language file
*
- * @access public
- * @param mixed the name of the language file to be loaded. Can be an array
- * @param string the language (english, etc.)
- * @param bool return loaded array of translations
- * @param bool add suffix to $langfile
- * @param string alternative path to look for language file
- * @return mixed
+ * @param mixed $langfile Language file name
+ * @param string $idiom Language name (english, etc.)
+ * @param bool $return Whether to return the loaded array of translations
+ * @param bool $add_suffix Whether to add suffix to $langfile
+ * @param string $alt_path Alternative path to look for the language file
+ *
+ * @return void|string[] Array containing translations, if $return is set to TRUE
*/
- function load($langfile = '', $idiom = '', $return = FALSE, $add_suffix = TRUE, $alt_path = '')
+ public function load($langfile, $idiom = '', $return = FALSE, $add_suffix = TRUE, $alt_path = '')
{
+ if (is_array($langfile))
+ {
+ foreach ($langfile as $value)
+ {
+ $this->load($value, $idiom, $return, $add_suffix, $alt_path);
+ }
+
+ return;
+ }
+
$langfile = str_replace('.php', '', $langfile);
- if ($add_suffix == TRUE)
+ if ($add_suffix === TRUE)
{
- $langfile = str_replace('_lang.', '', $langfile).'_lang';
+ $langfile = preg_replace('/_lang$/', '', $langfile).'_lang';
}
$langfile .= '.php';
- if (in_array($langfile, $this->is_loaded, TRUE))
+ if (empty($idiom) OR ! preg_match('/^[a-z_-]+$/i', $idiom))
{
- return;
+ $config =& get_config();
+ $idiom = empty($config['language']) ? 'english' : $config['language'];
}
- $config =& get_config();
+ if ($return === FALSE && isset($this->is_loaded[$langfile]) && $this->is_loaded[$langfile] === $idiom)
+ {
+ return;
+ }
- if ($idiom == '')
+ // Load the base file, so any others found can override it
+ $basepath = BASEPATH.'language/'.$idiom.'/'.$langfile;
+ if (($found = file_exists($basepath)) === TRUE)
{
- $deft_lang = ( ! isset($config['language'])) ? 'english' : $config['language'];
- $idiom = ($deft_lang == '') ? 'english' : $deft_lang;
+ include($basepath);
}
- // Determine where the language file is and load it
- if ($alt_path != '' && file_exists($alt_path.'language/'.$idiom.'/'.$langfile))
+ // Do we have an alternative path to look in?
+ if ($alt_path !== '')
{
- include($alt_path.'language/'.$idiom.'/'.$langfile);
+ $alt_path .= 'language/'.$idiom.'/'.$langfile;
+ if (file_exists($alt_path))
+ {
+ include($alt_path);
+ $found = TRUE;
+ }
}
else
{
- $found = FALSE;
-
foreach (get_instance()->load->get_package_paths(TRUE) as $package_path)
{
- if (file_exists($package_path.'language/'.$idiom.'/'.$langfile))
+ $package_path .= 'language/'.$idiom.'/'.$langfile;
+ if ($basepath !== $package_path && file_exists($package_path))
{
- include($package_path.'language/'.$idiom.'/'.$langfile);
+ include($package_path);
$found = TRUE;
break;
}
}
-
- if ($found !== TRUE)
- {
- show_error('Unable to load the requested language file: language/'.$idiom.'/'.$langfile);
- }
}
+ if ($found !== TRUE)
+ {
+ show_error('Unable to load the requested language file: language/'.$idiom.'/'.$langfile);
+ }
- if ( ! isset($lang))
+ if ( ! isset($lang) OR ! is_array($lang))
{
log_message('error', 'Language file contains no data: language/'.$idiom.'/'.$langfile);
+
+ if ($return === TRUE)
+ {
+ return array();
+ }
return;
}
- if ($return == TRUE)
+ if ($return === TRUE)
{
return $lang;
}
- $this->is_loaded[] = $langfile;
+ $this->is_loaded[$langfile] = $idiom;
$this->language = array_merge($this->language, $lang);
- unset($lang);
- log_message('debug', 'Language file loaded: language/'.$idiom.'/'.$langfile);
+ log_message('info', 'Language file loaded: language/'.$idiom.'/'.$langfile);
return TRUE;
}
// --------------------------------------------------------------------
/**
- * Fetch a single line of text from the language array
+ * Language line
*
- * @access public
- * @param string $line the language line
- * @return string
+ * Fetches a single line of text from the language array
+ *
+ * @param string $line Language line key
+ * @param bool $log_errors Whether to log an error message if the line is not found
+ * @return string Translation
*/
- function line($line = '')
+ public function line($line, $log_errors = TRUE)
{
- $value = ($line == '' OR ! isset($this->language[$line])) ? FALSE : $this->language[$line];
+ $value = isset($this->language[$line]) ? $this->language[$line] : FALSE;
// Because killer robots like unicorns!
- if ($value === FALSE)
+ if ($value === FALSE && $log_errors === TRUE)
{
log_message('error', 'Could not find the language line "'.$line.'"');
}
@@ -154,7 +202,3 @@ class CI_Lang {
}
}
-// END Language Class
-
-/* End of file Lang.php */
-/* Location: ./system/core/Lang.php */
diff --git a/system/core/Loader.php b/system/core/Loader.php
index b5b0634e6..b09f9e688 100644
--- a/system/core/Loader.php
+++ b/system/core/Loader.php
@@ -1,157 +1,169 @@
-<?php if ( ! defined('BASEPATH')) exit('No direct script access allowed');
+<?php
/**
* CodeIgniter
*
- * An open source application development framework for PHP 5.1.6 or newer
+ * An open source application development framework for PHP
*
- * @package CodeIgniter
- * @author ExpressionEngine Dev Team
- * @copyright Copyright (c) 2008 - 2014, EllisLab, Inc.
- * @license http://codeigniter.com/user_guide/license.html
- * @link http://codeigniter.com
- * @since Version 1.0
+ * This content is released under the MIT License (MIT)
+ *
+ * Copyright (c) 2019 - 2022, CodeIgniter Foundation
+ *
+ * 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.
+ *
+ * @package CodeIgniter
+ * @author EllisLab Dev Team
+ * @copyright Copyright (c) 2008 - 2014, EllisLab, Inc. (https://ellislab.com/)
+ * @copyright Copyright (c) 2014 - 2019, British Columbia Institute of Technology (https://bcit.ca/)
+ * @copyright Copyright (c) 2019 - 2022, CodeIgniter Foundation (https://codeigniter.com/)
+ * @license https://opensource.org/licenses/MIT MIT License
+ * @link https://codeigniter.com
+ * @since Version 1.0.0
* @filesource
*/
-
-// ------------------------------------------------------------------------
+defined('BASEPATH') OR exit('No direct script access allowed');
/**
* Loader Class
*
- * Loads views and files
+ * Loads framework components.
*
* @package CodeIgniter
* @subpackage Libraries
- * @author ExpressionEngine Dev Team
* @category Loader
- * @link http://codeigniter.com/user_guide/libraries/loader.html
+ * @author EllisLab Dev Team
+ * @link https://codeigniter.com/userguide3/libraries/loader.html
*/
+#[\AllowDynamicProperties]
class CI_Loader {
// All these are set automatically. Don't mess with them.
/**
* Nesting level of the output buffering mechanism
*
- * @var int
- * @access protected
+ * @var int
*/
protected $_ci_ob_level;
+
/**
* List of paths to load views from
*
- * @var array
- * @access protected
+ * @var array
*/
- protected $_ci_view_paths = array();
+ protected $_ci_view_paths = array(VIEWPATH => TRUE);
+
/**
* List of paths to load libraries from
*
- * @var array
- * @access protected
+ * @var array
*/
- protected $_ci_library_paths = array();
+ protected $_ci_library_paths = array(APPPATH, BASEPATH);
+
/**
* List of paths to load models from
*
- * @var array
- * @access protected
+ * @var array
*/
- protected $_ci_model_paths = array();
+ protected $_ci_model_paths = array(APPPATH);
+
/**
* List of paths to load helpers from
*
- * @var array
- * @access protected
+ * @var array
*/
- protected $_ci_helper_paths = array();
- /**
- * List of loaded base classes
- * Set by the controller class
- *
- * @var array
- * @access protected
- */
- protected $_base_classes = array(); // Set by the controller class
+ protected $_ci_helper_paths = array(APPPATH, BASEPATH);
+
/**
* List of cached variables
*
- * @var array
- * @access protected
+ * @var array
*/
- protected $_ci_cached_vars = array();
+ protected $_ci_cached_vars = array();
+
/**
- * List of loaded classes
+ * Stack of variable arrays to provide nested _ci_load calls with all variables from parent calls
*
- * @var array
- * @access protected
+ * @var array
*/
- protected $_ci_classes = array();
+ protected $_ci_load_vars_stack = array();
+
/**
- * List of loaded files
+ * List of loaded classes
*
- * @var array
- * @access protected
+ * @var array
*/
- protected $_ci_loaded_files = array();
+ protected $_ci_classes = array();
+
/**
* List of loaded models
*
- * @var array
- * @access protected
+ * @var array
*/
- protected $_ci_models = array();
+ protected $_ci_models = array();
+
/**
* List of loaded helpers
*
- * @var array
- * @access protected
+ * @var array
*/
- protected $_ci_helpers = array();
+ protected $_ci_helpers = array();
+
/**
* List of class name mappings
*
- * @var array
- * @access protected
+ * @var array
*/
- protected $_ci_varmap = array('unit_test' => 'unit',
- 'user_agent' => 'agent');
+ protected $_ci_varmap = array(
+ 'unit_test' => 'unit',
+ 'user_agent' => 'agent'
+ );
+
+ // --------------------------------------------------------------------
/**
- * Constructor
+ * Class constructor
+ *
+ * Sets component load paths, gets the initial output buffering level.
*
- * Sets the path to the view files and gets the initial output buffering level
+ * @return void
*/
public function __construct()
{
- $this->_ci_ob_level = ob_get_level();
- $this->_ci_library_paths = array(APPPATH, BASEPATH);
- $this->_ci_helper_paths = array(APPPATH, BASEPATH);
- $this->_ci_model_paths = array(APPPATH);
- $this->_ci_view_paths = array(APPPATH.'views/' => TRUE);
+ $this->_ci_ob_level = ob_get_level();
+ $this->_ci_classes =& is_loaded();
- log_message('debug', "Loader Class Initialized");
+ log_message('info', 'Loader Class Initialized');
}
// --------------------------------------------------------------------
/**
- * Initialize the Loader
+ * Initializer
*
- * This method is called once in CI_Controller.
- *
- * @param array
- * @return object
+ * @todo Figure out a way to move this to the constructor
+ * without breaking *package_path*() methods.
+ * @uses CI_Loader::_ci_autoloader()
+ * @used-by CI_Controller::__construct()
+ * @return void
*/
public function initialize()
{
- $this->_ci_classes = array();
- $this->_ci_loaded_files = array();
- $this->_ci_models = array();
- $this->_base_classes =& is_loaded();
-
$this->_ci_autoloader();
-
- return $this;
}
// --------------------------------------------------------------------
@@ -159,61 +171,61 @@ class CI_Loader {
/**
* Is Loaded
*
- * A utility function to test if a class is in the self::$_ci_classes array.
- * This function returns the object name if the class tested for is loaded,
- * and returns FALSE if it isn't.
+ * A utility method to test if a class is in the self::$_ci_classes array.
*
- * It is mainly used in the form_helper -> _get_validation_object()
+ * @used-by Mainly used by Form Helper function _get_validation_object().
*
- * @param string class being checked for
- * @return mixed class object name on the CI SuperObject or FALSE
+ * @param string $class Class name to check for
+ * @return string|bool Class object name if loaded or FALSE
*/
public function is_loaded($class)
{
- if (isset($this->_ci_classes[$class]))
- {
- return $this->_ci_classes[$class];
- }
-
- return FALSE;
+ return array_search(ucfirst($class), $this->_ci_classes, TRUE);
}
// --------------------------------------------------------------------
/**
- * Class Loader
+ * Library Loader
*
- * This function lets users load and instantiate classes.
- * It is designed to be called from a user's app controllers.
+ * Loads and instantiates libraries.
+ * Designed to be called from application controllers.
*
- * @param string the name of the class
- * @param mixed the optional parameters
- * @param string an optional object name
- * @return void
+ * @param mixed $library Library name
+ * @param array $params Optional parameters to pass to the library class constructor
+ * @param string $object_name An optional object name to assign to
+ * @return object
*/
- public function library($library = '', $params = NULL, $object_name = NULL)
+ public function library($library, $params = NULL, $object_name = NULL)
{
- if (is_array($library))
+ if (empty($library))
+ {
+ return $this;
+ }
+ elseif (is_array($library))
{
- foreach ($library as $class)
+ foreach ($library as $key => $value)
{
- $this->library($class, $params);
+ if (is_int($key))
+ {
+ $this->library($value, $params);
+ }
+ else
+ {
+ $this->library($key, $params, $value);
+ }
}
- return;
+ return $this;
}
- if ($library == '' OR isset($this->_base_classes[$library]))
- {
- return FALSE;
- }
-
- if ( ! is_null($params) && ! is_array($params))
+ if ($params !== NULL && ! is_array($params))
{
$params = NULL;
}
- $this->_ci_load_class($library, $params, $object_name);
+ $this->_ci_load_library($library, $params, $object_name);
+ return $this;
}
// --------------------------------------------------------------------
@@ -221,27 +233,27 @@ class CI_Loader {
/**
* Model Loader
*
- * This function lets users load and instantiate models.
+ * Loads and instantiates models.
*
- * @param string the name of the class
- * @param string name for the model
- * @param bool database connection
- * @return void
+ * @param mixed $model Model name
+ * @param string $name An optional object name to assign to
+ * @param bool $db_conn An optional database connection configuration to initialize
+ * @return object
*/
public function model($model, $name = '', $db_conn = FALSE)
{
- if (is_array($model))
+ if (empty($model))
+ {
+ return $this;
+ }
+ elseif (is_array($model))
{
- foreach ($model as $babe)
+ foreach ($model as $key => $value)
{
- $this->model($babe);
+ is_int($key) ? $this->model($value, '', $db_conn) : $this->model($key, $value, $db_conn);
}
- return;
- }
- if ($model == '')
- {
- return;
+ return $this;
}
$path = '';
@@ -250,64 +262,112 @@ class CI_Loader {
if (($last_slash = strrpos($model, '/')) !== FALSE)
{
// The path is in front of the last slash
- $path = substr($model, 0, $last_slash + 1);
+ $path = substr($model, 0, ++$last_slash);
// And the model name behind it
- $model = substr($model, $last_slash + 1);
+ $model = substr($model, $last_slash);
}
- if ($name == '')
+ if (empty($name))
{
$name = $model;
}
if (in_array($name, $this->_ci_models, TRUE))
{
- return;
+ return $this;
}
$CI =& get_instance();
if (isset($CI->$name))
{
- show_error('The model name you are loading is the name of a resource that is already being used: '.$name);
+ throw new RuntimeException('The model name you are loading is the name of a resource that is already being used: '.$name);
}
- $model = strtolower($model);
-
- foreach ($this->_ci_model_paths as $mod_path)
+ if ($db_conn !== FALSE && ! class_exists('CI_DB', FALSE))
{
- if ( ! file_exists($mod_path.'models/'.$path.$model.'.php'))
+ if ($db_conn === TRUE)
{
- continue;
+ $db_conn = '';
}
- if ($db_conn !== FALSE AND ! class_exists('CI_DB'))
+ $this->database($db_conn, FALSE, TRUE);
+ }
+
+ // Note: All of the code under this condition used to be just:
+ //
+ // load_class('Model', 'core');
+ //
+ // However, load_class() instantiates classes
+ // to cache them for later use and that prevents
+ // MY_Model from being an abstract class and is
+ // sub-optimal otherwise anyway.
+ if ( ! class_exists('CI_Model', FALSE))
+ {
+ $app_path = APPPATH.'core'.DIRECTORY_SEPARATOR;
+ if (file_exists($app_path.'Model.php'))
{
- if ($db_conn === TRUE)
+ require_once($app_path.'Model.php');
+ if ( ! class_exists('CI_Model', FALSE))
{
- $db_conn = '';
+ throw new RuntimeException($app_path."Model.php exists, but doesn't declare class CI_Model");
}
- $CI->load->database($db_conn, FALSE, TRUE);
+ log_message('info', 'CI_Model class loaded');
+ }
+ elseif ( ! class_exists('CI_Model', FALSE))
+ {
+ require_once(BASEPATH.'core'.DIRECTORY_SEPARATOR.'Model.php');
}
- if ( ! class_exists('CI_Model'))
+ $class = config_item('subclass_prefix').'Model';
+ if (file_exists($app_path.$class.'.php'))
{
- load_class('Model', 'core');
+ require_once($app_path.$class.'.php');
+ if ( ! class_exists($class, FALSE))
+ {
+ throw new RuntimeException($app_path.$class.".php exists, but doesn't declare class ".$class);
+ }
+
+ log_message('info', config_item('subclass_prefix').'Model class loaded');
}
+ }
- require_once($mod_path.'models/'.$path.$model.'.php');
+ $model = ucfirst($model);
+ if ( ! class_exists($model, FALSE))
+ {
+ foreach ($this->_ci_model_paths as $mod_path)
+ {
+ if ( ! file_exists($mod_path.'models/'.$path.$model.'.php'))
+ {
+ continue;
+ }
- $model = ucfirst($model);
+ require_once($mod_path.'models/'.$path.$model.'.php');
+ if ( ! class_exists($model, FALSE))
+ {
+ throw new RuntimeException($mod_path."models/".$path.$model.".php exists, but doesn't declare class ".$model);
+ }
- $CI->$name = new $model();
+ break;
+ }
- $this->_ci_models[] = $name;
- return;
+ if ( ! class_exists($model, FALSE))
+ {
+ throw new RuntimeException('Unable to locate the model you have specified: '.$model);
+ }
}
- // couldn't find the model
- show_error('Unable to locate the model you have specified: '.$model);
+ if ( ! is_subclass_of($model, 'CI_Model'))
+ {
+ throw new RuntimeException("Class ".$model." doesn't extend CI_Model");
+ }
+
+ $this->_ci_models[] = $name;
+ $model = new $model();
+ $CI->$name = $model;
+ log_message('info', 'Model "'.get_class($model).'" initialized');
+ return $this;
}
// --------------------------------------------------------------------
@@ -315,18 +375,18 @@ class CI_Loader {
/**
* Database Loader
*
- * @param string the DB credentials
- * @param bool whether to return the DB object
- * @param bool whether to enable active record (this allows us to override the config setting)
- * @return object
+ * @param mixed $params Database configuration options
+ * @param bool $return Whether to return the database object
+ * @return object|bool Database object if $return is set to TRUE,
+ * FALSE on failure, CI_Loader instance in any other case
*/
- public function database($params = '', $return = FALSE, $active_record = NULL)
+ public function database($params = '', $return = FALSE)
{
// Grab the super object
$CI =& get_instance();
// Do we even need to load the database class?
- if (class_exists('CI_DB') AND $return == FALSE AND $active_record == NULL AND isset($CI->db) AND is_object($CI->db))
+ if ($return === FALSE && isset($CI->db) && is_object($CI->db) && ! empty($CI->db->conn_id))
{
return FALSE;
}
@@ -335,42 +395,48 @@ class CI_Loader {
if ($return === TRUE)
{
- return DB($params, $active_record);
+ return DB($params);
}
- // Initialize the db variable. Needed to prevent
+ // Initialize the db variable. Needed to prevent
// reference errors with some configurations
$CI->db = '';
// Load the DB class
- $CI->db =& DB($params, $active_record);
+ $CI->db =& DB($params);
+ return $this;
}
// --------------------------------------------------------------------
/**
- * Load the Utilities Class
+ * Load the Database Utilities Class
*
- * @return string
+ * @param object $db Database object
+ * @param bool $return Whether to return the DB Utilities class object or not
+ * @return object
*/
- public function dbutil()
+ public function dbutil($db = NULL, $return = FALSE)
{
- if ( ! class_exists('CI_DB'))
- {
- $this->database();
- }
-
$CI =& get_instance();
- // for backwards compatibility, load dbforge so we can extend dbutils off it
- // this use is deprecated and strongly discouraged
- $CI->load->dbforge();
+ if ( ! is_object($db) OR ! ($db instanceof CI_DB))
+ {
+ class_exists('CI_DB', FALSE) OR $this->database();
+ $db =& $CI->db;
+ }
require_once(BASEPATH.'database/DB_utility.php');
- require_once(BASEPATH.'database/drivers/'.$CI->db->dbdriver.'/'.$CI->db->dbdriver.'_utility.php');
- $class = 'CI_DB_'.$CI->db->dbdriver.'_utility';
+ require_once(BASEPATH.'database/drivers/'.$db->dbdriver.'/'.$db->dbdriver.'_utility.php');
+ $class = 'CI_DB_'.$db->dbdriver.'_utility';
+
+ if ($return === TRUE)
+ {
+ return new $class($db);
+ }
- $CI->dbutil = new $class();
+ $CI->dbutil = new $class($db);
+ return $this;
}
// --------------------------------------------------------------------
@@ -378,57 +444,72 @@ class CI_Loader {
/**
* Load the Database Forge Class
*
- * @return string
+ * @param object $db Database object
+ * @param bool $return Whether to return the DB Forge class object or not
+ * @return object
*/
- public function dbforge()
+ public function dbforge($db = NULL, $return = FALSE)
{
- if ( ! class_exists('CI_DB'))
+ $CI =& get_instance();
+ if ( ! is_object($db) OR ! ($db instanceof CI_DB))
{
- $this->database();
+ class_exists('CI_DB', FALSE) OR $this->database();
+ $db =& $CI->db;
}
- $CI =& get_instance();
-
require_once(BASEPATH.'database/DB_forge.php');
- require_once(BASEPATH.'database/drivers/'.$CI->db->dbdriver.'/'.$CI->db->dbdriver.'_forge.php');
- $class = 'CI_DB_'.$CI->db->dbdriver.'_forge';
+ require_once(BASEPATH.'database/drivers/'.$db->dbdriver.'/'.$db->dbdriver.'_forge.php');
- $CI->dbforge = new $class();
+ if ( ! empty($db->subdriver))
+ {
+ $driver_path = BASEPATH.'database/drivers/'.$db->dbdriver.'/subdrivers/'.$db->dbdriver.'_'.$db->subdriver.'_forge.php';
+ if (file_exists($driver_path))
+ {
+ require_once($driver_path);
+ $class = 'CI_DB_'.$db->dbdriver.'_'.$db->subdriver.'_forge';
+ }
+ }
+ else
+ {
+ $class = 'CI_DB_'.$db->dbdriver.'_forge';
+ }
+
+ if ($return === TRUE)
+ {
+ return new $class($db);
+ }
+
+ $CI->dbforge = new $class($db);
+ return $this;
}
// --------------------------------------------------------------------
/**
- * Load View
+ * View Loader
*
- * This function is used to load a "view" file. It has three parameters:
+ * Loads "view" files.
*
- * 1. The name of the "view" file to be included.
- * 2. An associative array of data to be extracted for use in the view.
- * 3. TRUE/FALSE - whether to return the data or load it. In
- * some cases it's advantageous to be able to return data so that
- * a developer can process it in some way.
- *
- * @param string
- * @param array
- * @param bool
- * @return void
+ * @param string $view View name
+ * @param array $vars An associative array of data
+ * to be extracted for use in the view
+ * @param bool $return Whether to return the view output
+ * or leave it to the Output class
+ * @return object|string
*/
public function view($view, $vars = array(), $return = FALSE)
{
- return $this->_ci_load(array('_ci_view' => $view, '_ci_vars' => $this->_ci_object_to_array($vars), '_ci_return' => $return));
+ return $this->_ci_load(array('_ci_view' => $view, '_ci_vars' => $this->_ci_prepare_view_vars($vars), '_ci_return' => $return));
}
// --------------------------------------------------------------------
/**
- * Load File
- *
- * This is a generic file loader
+ * Generic File Loader
*
- * @param string
- * @param bool
- * @return string
+ * @param string $path File path
+ * @param bool $return Whether to return the file output
+ * @return object|string
*/
public function file($path, $return = FALSE)
{
@@ -443,26 +524,39 @@ class CI_Loader {
* Once variables are set they become available within
* the controller class and its "view" files.
*
- * @param array
- * @param string
- * @return void
+ * @param array|object|string $vars
+ * An associative array or object containing values
+ * to be set, or a value's name if string
+ * @param string $val Value to set, only used if $vars is a string
+ * @return object
*/
- public function vars($vars = array(), $val = '')
+ public function vars($vars, $val = '')
{
- if ($val != '' AND is_string($vars))
+ $vars = is_string($vars)
+ ? array($vars => $val)
+ : $this->_ci_prepare_view_vars($vars);
+
+ foreach ($vars as $key => $val)
{
- $vars = array($vars => $val);
+ $this->_ci_cached_vars[$key] = $val;
}
- $vars = $this->_ci_object_to_array($vars);
+ return $this;
+ }
- if (is_array($vars) AND count($vars) > 0)
- {
- foreach ($vars as $key => $val)
- {
- $this->_ci_cached_vars[$key] = $val;
- }
- }
+ // --------------------------------------------------------------------
+
+ /**
+ * Clear Cached Variables
+ *
+ * Clears the cached variables.
+ *
+ * @return CI_Loader
+ */
+ public function clear_vars()
+ {
+ $this->_ci_cached_vars = array();
+ return $this;
}
// --------------------------------------------------------------------
@@ -472,8 +566,8 @@ class CI_Loader {
*
* Check if a variable is set and retrieve it.
*
- * @param array
- * @return void
+ * @param string $key Variable name
+ * @return mixed The variable or NULL if not found
*/
public function get_var($key)
{
@@ -483,43 +577,68 @@ class CI_Loader {
// --------------------------------------------------------------------
/**
- * Load Helper
+ * Get Variables
*
- * This function loads the specified helper file.
+ * Retrieves all loaded variables.
*
- * @param mixed
- * @return void
+ * @return array
+ */
+ public function get_vars()
+ {
+ return $this->_ci_cached_vars;
+ }
+
+ // --------------------------------------------------------------------
+
+ /**
+ * Helper Loader
+ *
+ * @param string|string[] $helpers Helper name(s)
+ * @return object
*/
public function helper($helpers = array())
{
- foreach ($this->_ci_prep_filename($helpers, '_helper') as $helper)
+ is_array($helpers) OR $helpers = array($helpers);
+ foreach ($helpers as &$helper)
{
+ $filename = basename($helper);
+ $filepath = ($filename === $helper) ? '' : substr($helper, 0, strlen($helper) - strlen($filename));
+ $filename = strtolower(preg_replace('#(_helper)?(\.php)?$#i', '', $filename)).'_helper';
+ $helper = $filepath.$filename;
+
if (isset($this->_ci_helpers[$helper]))
{
continue;
}
- $ext_helper = APPPATH.'helpers/'.config_item('subclass_prefix').$helper.'.php';
-
// Is this a helper extension request?
- if (file_exists($ext_helper))
+ $ext_helper = config_item('subclass_prefix').$filename;
+ $ext_loaded = FALSE;
+ foreach ($this->_ci_helper_paths as $path)
{
- $base_helper = BASEPATH.'helpers/'.$helper.'.php';
+ if (file_exists($path.'helpers/'.$ext_helper.'.php'))
+ {
+ include_once($path.'helpers/'.$ext_helper.'.php');
+ $ext_loaded = TRUE;
+ }
+ }
+ // If we have loaded extensions - check if the base one is here
+ if ($ext_loaded === TRUE)
+ {
+ $base_helper = BASEPATH.'helpers/'.$helper.'.php';
if ( ! file_exists($base_helper))
{
show_error('Unable to load the requested file: helpers/'.$helper.'.php');
}
- include_once($ext_helper);
include_once($base_helper);
-
$this->_ci_helpers[$helper] = TRUE;
- log_message('debug', 'Helper loaded: '.$helper);
+ log_message('info', 'Helper loaded: '.$helper);
continue;
}
- // Try to load the helper
+ // No extensions found ... try loading regular helpers and/or overrides
foreach ($this->_ci_helper_paths as $path)
{
if (file_exists($path.'helpers/'.$helper.'.php'))
@@ -527,7 +646,7 @@ class CI_Loader {
include_once($path.'helpers/'.$helper.'.php');
$this->_ci_helpers[$helper] = TRUE;
- log_message('debug', 'Helper loaded: '.$helper);
+ log_message('info', 'Helper loaded: '.$helper);
break;
}
}
@@ -538,6 +657,8 @@ class CI_Loader {
show_error('Unable to load the requested file: helpers/'.$helper.'.php');
}
}
+
+ return $this;
}
// --------------------------------------------------------------------
@@ -545,82 +666,96 @@ class CI_Loader {
/**
* Load Helpers
*
- * This is simply an alias to the above function in case the
- * user has written the plural form of this function.
+ * An alias for the helper() method in case the developer has
+ * written the plural form of it.
*
- * @param array
- * @return void
+ * @uses CI_Loader::helper()
+ * @param string|string[] $helpers Helper name(s)
+ * @return object
*/
public function helpers($helpers = array())
{
- $this->helper($helpers);
+ return $this->helper($helpers);
}
// --------------------------------------------------------------------
/**
- * Loads a language file
+ * Language Loader
*
- * @param array
- * @param string
- * @return void
+ * Loads language files.
+ *
+ * @param string|string[] $files List of language file names to load
+ * @param string Language name
+ * @return object
*/
- public function language($file = array(), $lang = '')
+ public function language($files, $lang = '')
{
- $CI =& get_instance();
-
- if ( ! is_array($file))
- {
- $file = array($file);
- }
-
- foreach ($file as $langfile)
- {
- $CI->lang->load($langfile, $lang);
- }
+ get_instance()->lang->load($files, $lang);
+ return $this;
}
// --------------------------------------------------------------------
/**
- * Loads a config file
+ * Config Loader
*
- * @param string
- * @param bool
- * @param bool
- * @return void
+ * Loads a config file (an alias for CI_Config::load()).
+ *
+ * @uses CI_Config::load()
+ * @param string $file Configuration file name
+ * @param bool $use_sections Whether configuration values should be loaded into their own section
+ * @param bool $fail_gracefully Whether to just return FALSE or display an error message
+ * @return bool TRUE if the file was loaded correctly or FALSE on failure
*/
- public function config($file = '', $use_sections = FALSE, $fail_gracefully = FALSE)
+ public function config($file, $use_sections = FALSE, $fail_gracefully = FALSE)
{
- $CI =& get_instance();
- $CI->config->load($file, $use_sections, $fail_gracefully);
+ return get_instance()->config->load($file, $use_sections, $fail_gracefully);
}
// --------------------------------------------------------------------
/**
- * Driver
+ * Driver Loader
*
- * Loads a driver library
+ * Loads a driver library.
*
- * @param string the name of the class
- * @param mixed the optional parameters
- * @param string an optional object name
- * @return void
+ * @param string|string[] $library Driver name(s)
+ * @param array $params Optional parameters to pass to the driver
+ * @param string $object_name An optional object name to assign to
+ *
+ * @return object|bool Object or FALSE on failure if $library is a string
+ * and $object_name is set. CI_Loader instance otherwise.
*/
- public function driver($library = '', $params = NULL, $object_name = NULL)
+ public function driver($library, $params = NULL, $object_name = NULL)
{
- if ( ! class_exists('CI_Driver_Library'))
+ if (is_array($library))
{
- // we aren't instantiating an object here, that'll be done by the Library itself
- require BASEPATH.'libraries/Driver.php';
- }
+ foreach ($library as $key => $value)
+ {
+ if (is_int($key))
+ {
+ $this->driver($value, $params);
+ }
+ else
+ {
+ $this->driver($key, $params, $value);
+ }
+ }
- if ($library == '')
+ return $this;
+ }
+ elseif (empty($library))
{
return FALSE;
}
+ if ( ! class_exists('CI_Driver_Library', FALSE))
+ {
+ // We aren't instantiating an object here, just making the base class available
+ require BASEPATH.'libraries/Driver.php';
+ }
+
// We can save the loader some time since Drivers will *always* be in a subfolder,
// and typically identically named to the library
if ( ! strpos($library, '/'))
@@ -636,13 +771,19 @@ class CI_Loader {
/**
* Add Package Path
*
- * Prepends a parent path to the library, model, helper, and config path arrays
+ * Prepends a parent path to the library, model, helper and config
+ * path arrays.
*
- * @param string
- * @param boolean
- * @return void
+ * @see CI_Loader::$_ci_library_paths
+ * @see CI_Loader::$_ci_model_paths
+ * @see CI_Loader::$_ci_helper_paths
+ * @see CI_Config::$_config_paths
+ *
+ * @param string $path Path to add
+ * @param bool $view_cascade (default: TRUE)
+ * @return object
*/
- public function add_package_path($path, $view_cascade=TRUE)
+ public function add_package_path($path, $view_cascade = TRUE)
{
$path = rtrim($path, '/').'/';
@@ -654,7 +795,9 @@ class CI_Loader {
// Add config file path
$config =& $this->_ci_get_component('config');
- array_unshift($config->_config_paths, $path);
+ $config->_config_paths[] = $path;
+
+ return $this;
}
// --------------------------------------------------------------------
@@ -662,14 +805,14 @@ class CI_Loader {
/**
* Get Package Paths
*
- * Return a list of all package paths, by default it will ignore BASEPATH.
+ * Return a list of all package paths.
*
- * @param string
- * @return void
+ * @param bool $include_base Whether to include BASEPATH (default: FALSE)
+ * @return array
*/
public function get_package_paths($include_base = FALSE)
{
- return $include_base === TRUE ? $this->_ci_library_paths : $this->_ci_model_paths;
+ return ($include_base === TRUE) ? $this->_ci_library_paths : $this->_ci_model_paths;
}
// --------------------------------------------------------------------
@@ -677,24 +820,24 @@ class CI_Loader {
/**
* Remove Package Path
*
- * Remove a path from the library, model, and helper path arrays if it exists
- * If no path is provided, the most recently added path is removed.
+ * Remove a path from the library, model, helper and/or config
+ * path arrays if it exists. If no path is provided, the most recently
+ * added path will be removed removed.
*
- * @param type
- * @param bool
- * @return type
+ * @param string $path Path to remove
+ * @return object
*/
- public function remove_package_path($path = '', $remove_config_path = TRUE)
+ public function remove_package_path($path = '')
{
$config =& $this->_ci_get_component('config');
- if ($path == '')
+ if ($path === '')
{
- $void = array_shift($this->_ci_library_paths);
- $void = array_shift($this->_ci_model_paths);
- $void = array_shift($this->_ci_helper_paths);
- $void = array_shift($this->_ci_view_paths);
- $void = array_shift($config->_config_paths);
+ array_shift($this->_ci_library_paths);
+ array_shift($this->_ci_model_paths);
+ array_shift($this->_ci_helper_paths);
+ array_shift($this->_ci_view_paths);
+ array_pop($config->_config_paths);
}
else
{
@@ -724,32 +867,37 @@ class CI_Loader {
$this->_ci_model_paths = array_unique(array_merge($this->_ci_model_paths, array(APPPATH)));
$this->_ci_view_paths = array_merge($this->_ci_view_paths, array(APPPATH.'views/' => TRUE));
$config->_config_paths = array_unique(array_merge($config->_config_paths, array(APPPATH)));
+
+ return $this;
}
// --------------------------------------------------------------------
/**
- * Loader
+ * Internal CI Data Loader
+ *
+ * Used to load views and files.
*
- * This function is used to load views and files.
* Variables are prefixed with _ci_ to avoid symbol collision with
- * variables made available to view files
+ * variables made available to view files.
*
- * @param array
- * @return void
+ * @used-by CI_Loader::view()
+ * @used-by CI_Loader::file()
+ * @param array $_ci_data Data to load
+ * @return object
*/
protected function _ci_load($_ci_data)
{
// Set the default data variables
foreach (array('_ci_view', '_ci_vars', '_ci_path', '_ci_return') as $_ci_val)
{
- $$_ci_val = ( ! isset($_ci_data[$_ci_val])) ? FALSE : $_ci_data[$_ci_val];
+ $$_ci_val = isset($_ci_data[$_ci_val]) ? $_ci_data[$_ci_val] : FALSE;
}
$file_exists = FALSE;
// Set the path to the requested file
- if ($_ci_path != '')
+ if (is_string($_ci_path) && $_ci_path !== '')
{
$_ci_x = explode('/', $_ci_path);
$_ci_file = end($_ci_x);
@@ -757,13 +905,13 @@ class CI_Loader {
else
{
$_ci_ext = pathinfo($_ci_view, PATHINFO_EXTENSION);
- $_ci_file = ($_ci_ext == '') ? $_ci_view.'.php' : $_ci_view;
+ $_ci_file = ($_ci_ext === '') ? $_ci_view.'.php' : $_ci_view;
- foreach ($this->_ci_view_paths as $view_file => $cascade)
+ foreach ($this->_ci_view_paths as $_ci_view_file => $cascade)
{
- if (file_exists($view_file.$_ci_file))
+ if (file_exists($_ci_view_file.$_ci_file))
{
- $_ci_path = $view_file.$_ci_file;
+ $_ci_path = $_ci_view_file.$_ci_file;
$file_exists = TRUE;
break;
}
@@ -782,7 +930,6 @@ class CI_Loader {
// This allows anything loaded using $this->load (views, files, etc.)
// to become accessible from within the Controller and Model functions.
-
$_ci_CI =& get_instance();
foreach (get_object_vars($_ci_CI) as $_ci_key => $_ci_var)
{
@@ -793,47 +940,55 @@ class CI_Loader {
}
/*
- * Extract and cache variables
+ * Extract and stack variables
*
- * You can either set variables using the dedicated $this->load_vars()
+ * You can either set variables using the dedicated $this->load->vars()
* function or via the second parameter of this function. We'll merge
- * the two types and cache them so that views that are embedded within
- * other views can have access to these variables.
+ * the two types so that loaded views and files have access to these
+ * variables.
+ * Additionally we want all subsequent nested _ci_load() calls embedded
+ * within the current file to 'inherit' all variables that are
+ * accessible to the current file. For this purpose we push the current
+ * variable configuration (_ci_vars) to the stack and remove it again
+ * after the file or view is completely loaded. Nested _ci_load() calls
+ * within the current file extend the stack with their variable
+ * configuration.
*/
- if (is_array($_ci_vars))
+
+ is_array($_ci_vars) OR $_ci_vars = array();
+
+ // Include the global cached vars into the current _ci_vars if needed
+ empty($this->_ci_cached_vars) OR $_ci_vars = array_merge($this->_ci_cached_vars, $_ci_vars);
+
+ // Merge the last variable configuration from a parent _ci_load()
+ // call into the current _ci_vars
+ if ( ! empty($this->_ci_load_vars_stack))
{
- $this->_ci_cached_vars = array_merge($this->_ci_cached_vars, $_ci_vars);
+ $previous_variable_configuration = end($this->_ci_load_vars_stack);
+ $_ci_vars = array_merge($previous_variable_configuration, $_ci_vars);
}
- extract($this->_ci_cached_vars);
- /*
+ array_push($this->_ci_load_vars_stack, $_ci_vars);
+ extract($_ci_vars);
+
+ /**
* Buffer the output
*
* We buffer the output for two reasons:
* 1. Speed. You get a significant speed boost.
- * 2. So that the final rendered template can be
- * post-processed by the output class. Why do we
- * need post processing? For one thing, in order to
- * show the elapsed page load time. Unless we
- * can intercept the content right before it's sent to
- * the browser and then stop the timer it won't be accurate.
+ * 2. So that the final rendered template can be post-processed by
+ * the output class. Why do we need post processing? For one thing,
+ * in order to show the elapsed page load time. Unless we can
+ * intercept the content right before it's sent to the browser and
+ * then stop the timer it won't be accurate.
*/
ob_start();
- // If the PHP installation does not support short tags we'll
- // do a little string replacement, changing the short tags
- // to standard PHP echo statements.
-
- if ((bool) @ini_get('short_open_tag') === FALSE AND config_item('rewrite_short_tags') == TRUE)
- {
- echo eval('?>'.preg_replace("/;*\s*\?>/", "; ?>", str_replace('<?=', '<?php echo ', file_get_contents($_ci_path))));
- }
- else
- {
- include($_ci_path); // include() vs include_once() allows for multiple views with the same name
- }
+ include($_ci_path); // include() vs include_once() allows for multiple views with the same name
+ log_message('info', 'File loaded: '.$_ci_path);
- log_message('debug', 'File loaded: '.$_ci_path);
+ // Remove current _ci_vars from stack
+ array_pop($this->_ci_load_vars_stack);
// Return the file data if requested
if ($_ci_return === TRUE)
@@ -851,7 +1006,6 @@ class CI_Loader {
* we are beyond the first level of output buffering so that
* it can be seen and included properly by the first included
* template and any subsequent ones. Oy!
- *
*/
if (ob_get_level() > $this->_ci_ob_level + 1)
{
@@ -862,21 +1016,24 @@ class CI_Loader {
$_ci_CI->output->append_output(ob_get_contents());
@ob_end_clean();
}
+
+ return $this;
}
// --------------------------------------------------------------------
/**
- * Load class
+ * Internal CI Library Loader
*
- * This function loads the requested class.
+ * @used-by CI_Loader::library()
+ * @uses CI_Loader::_ci_init_library()
*
- * @param string the item that is being loaded
- * @param mixed any additional parameters
- * @param string an optional object name
+ * @param string $class Class name to load
+ * @param mixed $params Optional parameters to pass to the class constructor
+ * @param string $object_name Optional object name to assign to
* @return void
*/
- protected function _ci_load_class($class, $params = NULL, $object_name = NULL)
+ protected function _ci_load_library($class, $params = NULL, $object_name = NULL)
{
// Get the class name, and while we're at it trim any slashes.
// The directory path can be included as part of the class name,
@@ -885,128 +1042,182 @@ class CI_Loader {
// Was the path included with the class name?
// We look for a slash to determine this
- $subdir = '';
if (($last_slash = strrpos($class, '/')) !== FALSE)
{
// Extract the path
- $subdir = substr($class, 0, $last_slash + 1);
+ $subdir = substr($class, 0, ++$last_slash);
// Get the filename from the path
- $class = substr($class, $last_slash + 1);
+ $class = substr($class, $last_slash);
+ }
+ else
+ {
+ $subdir = '';
}
- // We'll test for both lowercase and capitalized versions of the file name
- foreach (array(ucfirst($class), strtolower($class)) as $class)
+ $class = ucfirst($class);
+
+ // Is this a stock library? There are a few special conditions if so ...
+ if (file_exists(BASEPATH.'libraries/'.$subdir.$class.'.php'))
{
- $subclass = APPPATH.'libraries/'.$subdir.config_item('subclass_prefix').$class.'.php';
+ return $this->_ci_load_stock_library($class, $subdir, $params, $object_name);
+ }
- // Is this a class extension request?
- if (file_exists($subclass))
+ // Safety: Was the class already loaded by a previous call?
+ if (class_exists($class, FALSE))
+ {
+ $property = $object_name;
+ if (empty($property))
{
- $baseclass = BASEPATH.'libraries/'.ucfirst($class).'.php';
+ $property = strtolower($class);
+ isset($this->_ci_varmap[$property]) && $property = $this->_ci_varmap[$property];
+ }
- if ( ! file_exists($baseclass))
- {
- log_message('error', "Unable to load the requested class: ".$class);
- show_error("Unable to load the requested class: ".$class);
- }
+ $CI =& get_instance();
+ if (isset($CI->$property))
+ {
+ log_message('debug', $class.' class already loaded. Second attempt ignored.');
+ return;
+ }
- // Safety: Was the class already loaded by a previous call?
- if (in_array($subclass, $this->_ci_loaded_files))
- {
- // Before we deem this to be a duplicate request, let's see
- // if a custom object name is being supplied. If so, we'll
- // return a new instance of the object
- if ( ! is_null($object_name))
- {
- $CI =& get_instance();
- if ( ! isset($CI->$object_name))
- {
- return $this->_ci_init_class($class, config_item('subclass_prefix'), $params, $object_name);
- }
- }
+ return $this->_ci_init_library($class, '', $params, $object_name);
+ }
- $is_duplicate = TRUE;
- log_message('debug', $class." class already loaded. Second attempt ignored.");
- return;
- }
+ // Let's search for the requested library file and load it.
+ foreach ($this->_ci_library_paths as $path)
+ {
+ // BASEPATH has already been checked for
+ if ($path === BASEPATH)
+ {
+ continue;
+ }
+
+ $filepath = $path.'libraries/'.$subdir.$class.'.php';
+ // Does the file exist? No? Bummer...
+ if ( ! file_exists($filepath))
+ {
+ continue;
+ }
- include_once($baseclass);
- include_once($subclass);
- $this->_ci_loaded_files[] = $subclass;
+ include_once($filepath);
+ return $this->_ci_init_library($class, '', $params, $object_name);
+ }
- return $this->_ci_init_class($class, config_item('subclass_prefix'), $params, $object_name);
+ // One last attempt. Maybe the library is in a subdirectory, but it wasn't specified?
+ if ($subdir === '')
+ {
+ return $this->_ci_load_library($class.'/'.$class, $params, $object_name);
+ }
+
+ // If we got this far we were unable to find the requested class.
+ log_message('error', 'Unable to load the requested class: '.$class);
+ show_error('Unable to load the requested class: '.$class);
+ }
+
+ // --------------------------------------------------------------------
+
+ /**
+ * Internal CI Stock Library Loader
+ *
+ * @used-by CI_Loader::_ci_load_library()
+ * @uses CI_Loader::_ci_init_library()
+ *
+ * @param string $library_name Library name to load
+ * @param string $file_path Path to the library filename, relative to libraries/
+ * @param mixed $params Optional parameters to pass to the class constructor
+ * @param string $object_name Optional object name to assign to
+ * @return void
+ */
+ protected function _ci_load_stock_library($library_name, $file_path, $params, $object_name)
+ {
+ $prefix = 'CI_';
+
+ if (class_exists($prefix.$library_name, FALSE))
+ {
+ if (class_exists(config_item('subclass_prefix').$library_name, FALSE))
+ {
+ $prefix = config_item('subclass_prefix');
}
- // Lets search for the requested library file and load it.
- $is_duplicate = FALSE;
- foreach ($this->_ci_library_paths as $path)
+ $property = $object_name;
+ if (empty($property))
{
- $filepath = $path.'libraries/'.$subdir.$class.'.php';
+ $property = strtolower($library_name);
+ isset($this->_ci_varmap[$property]) && $property = $this->_ci_varmap[$property];
+ }
- // Does the file exist? No? Bummer...
- if ( ! file_exists($filepath))
- {
- continue;
- }
+ $CI =& get_instance();
+ if ( ! isset($CI->$property))
+ {
+ return $this->_ci_init_library($library_name, $prefix, $params, $object_name);
+ }
- // Safety: Was the class already loaded by a previous call?
- if (in_array($filepath, $this->_ci_loaded_files))
- {
- // Before we deem this to be a duplicate request, let's see
- // if a custom object name is being supplied. If so, we'll
- // return a new instance of the object
- if ( ! is_null($object_name))
- {
- $CI =& get_instance();
- if ( ! isset($CI->$object_name))
- {
- return $this->_ci_init_class($class, '', $params, $object_name);
- }
- }
+ log_message('debug', $library_name.' class already loaded. Second attempt ignored.');
+ return;
+ }
+
+ $paths = $this->_ci_library_paths;
+ array_pop($paths); // BASEPATH
+ array_pop($paths); // APPPATH (needs to be the first path checked)
+ array_unshift($paths, APPPATH);
- $is_duplicate = TRUE;
- log_message('debug', $class." class already loaded. Second attempt ignored.");
- return;
+ foreach ($paths as $path)
+ {
+ if (file_exists($path = $path.'libraries/'.$file_path.$library_name.'.php'))
+ {
+ // Override
+ include_once($path);
+ if (class_exists($prefix.$library_name, FALSE))
+ {
+ return $this->_ci_init_library($library_name, $prefix, $params, $object_name);
}
- include_once($filepath);
- $this->_ci_loaded_files[] = $filepath;
- return $this->_ci_init_class($class, '', $params, $object_name);
+ log_message('debug', $path.' exists, but does not declare '.$prefix.$library_name);
}
+ }
- } // END FOREACH
+ include_once(BASEPATH.'libraries/'.$file_path.$library_name.'.php');
- // One last attempt. Maybe the library is in a subdirectory, but it wasn't specified?
- if ($subdir == '')
+ // Check for extensions
+ $subclass = config_item('subclass_prefix').$library_name;
+ foreach ($paths as $path)
{
- $path = strtolower($class).'/'.$class;
- return $this->_ci_load_class($path, $params);
- }
+ if (file_exists($path = $path.'libraries/'.$file_path.$subclass.'.php'))
+ {
+ include_once($path);
+ if (class_exists($subclass, FALSE))
+ {
+ $prefix = config_item('subclass_prefix');
+ break;
+ }
- // If we got this far we were unable to find the requested class.
- // We do not issue errors if the load call failed due to a duplicate request
- if ($is_duplicate == FALSE)
- {
- log_message('error', "Unable to load the requested class: ".$class);
- show_error("Unable to load the requested class: ".$class);
+ log_message('debug', $path.' exists, but does not declare '.$subclass);
+ }
}
+
+ return $this->_ci_init_library($library_name, $prefix, $params, $object_name);
}
// --------------------------------------------------------------------
/**
- * Instantiates a class
+ * Internal CI Library Instantiator
+ *
+ * @used-by CI_Loader::_ci_load_stock_library()
+ * @used-by CI_Loader::_ci_load_library()
*
- * @param string
- * @param string
- * @param bool
- * @param string an optional object name
- * @return null
+ * @param string $class Class name
+ * @param string $prefix Class name prefix
+ * @param array|null|bool $config Optional configuration to pass to the class constructor:
+ * FALSE to skip;
+ * NULL to search in config paths;
+ * array containing configuration data
+ * @param string $object_name Optional object name to assign to
+ * @return void
*/
- protected function _ci_init_class($class, $prefix = '', $config = FALSE, $object_name = NULL)
+ protected function _ci_init_library($class, $prefix, $config = FALSE, $object_name = NULL)
{
- // Is there an associated config file for this class? Note: these should always be lowercase
+ // Is there an associated config file for this class? Note: these should always be lowercase
if ($config === NULL)
{
// Fetch the config paths containing any package paths
@@ -1014,117 +1225,111 @@ class CI_Loader {
if (is_array($config_component->_config_paths))
{
- // Break on the first found file, thus package files
- // are not overridden by default paths
+ $found = FALSE;
foreach ($config_component->_config_paths as $path)
{
// We test for both uppercase and lowercase, for servers that
- // are case-sensitive with regard to file names. Check for environment
- // first, global next
- if (defined('ENVIRONMENT') AND file_exists($path .'config/'.ENVIRONMENT.'/'.strtolower($class).'.php'))
+ // are case-sensitive with regard to file names. Load global first,
+ // override with environment next
+ if (file_exists($path.'config/'.strtolower($class).'.php'))
{
- include($path .'config/'.ENVIRONMENT.'/'.strtolower($class).'.php');
- break;
+ include($path.'config/'.strtolower($class).'.php');
+ $found = TRUE;
}
- elseif (defined('ENVIRONMENT') AND file_exists($path .'config/'.ENVIRONMENT.'/'.ucfirst(strtolower($class)).'.php'))
+ elseif (file_exists($path.'config/'.ucfirst(strtolower($class)).'.php'))
{
- include($path .'config/'.ENVIRONMENT.'/'.ucfirst(strtolower($class)).'.php');
- break;
+ include($path.'config/'.ucfirst(strtolower($class)).'.php');
+ $found = TRUE;
}
- elseif (file_exists($path .'config/'.strtolower($class).'.php'))
+
+ if (file_exists($path.'config/'.ENVIRONMENT.'/'.strtolower($class).'.php'))
{
- include($path .'config/'.strtolower($class).'.php');
- break;
+ include($path.'config/'.ENVIRONMENT.'/'.strtolower($class).'.php');
+ $found = TRUE;
}
- elseif (file_exists($path .'config/'.ucfirst(strtolower($class)).'.php'))
+ elseif (file_exists($path.'config/'.ENVIRONMENT.'/'.ucfirst(strtolower($class)).'.php'))
+ {
+ include($path.'config/'.ENVIRONMENT.'/'.ucfirst(strtolower($class)).'.php');
+ $found = TRUE;
+ }
+
+ // Break on the first found configuration, thus package
+ // files are not overridden by default paths
+ if ($found === TRUE)
{
- include($path .'config/'.ucfirst(strtolower($class)).'.php');
break;
}
}
}
}
- if ($prefix == '')
- {
- if (class_exists('CI_'.$class))
- {
- $name = 'CI_'.$class;
- }
- elseif (class_exists(config_item('subclass_prefix').$class))
- {
- $name = config_item('subclass_prefix').$class;
- }
- else
- {
- $name = $class;
- }
- }
- else
- {
- $name = $prefix.$class;
- }
+ $class_name = $prefix.$class;
// Is the class name valid?
- if ( ! class_exists($name))
+ if ( ! class_exists($class_name, FALSE))
{
- log_message('error', "Non-existent class: ".$name);
- show_error("Non-existent class: ".$class);
+ log_message('error', 'Non-existent class: '.$class_name);
+ show_error('Non-existent class: '.$class_name);
}
// Set the variable name we will assign the class to
- // Was a custom class name supplied? If so we'll use it
- $class = strtolower($class);
-
- if (is_null($object_name))
+ // Was a custom class name supplied? If so we'll use it
+ if (empty($object_name))
{
- $classvar = ( ! isset($this->_ci_varmap[$class])) ? $class : $this->_ci_varmap[$class];
+ $object_name = strtolower($class);
+ if (isset($this->_ci_varmap[$object_name]))
+ {
+ $object_name = $this->_ci_varmap[$object_name];
+ }
}
- else
+
+ // Don't overwrite existing properties
+ $CI =& get_instance();
+ if (isset($CI->$object_name))
{
- $classvar = $object_name;
+ if ($CI->$object_name instanceof $class_name)
+ {
+ log_message('debug', $class_name." has already been instantiated as '".$object_name."'. Second attempt aborted.");
+ return;
+ }
+
+ show_error("Resource '".$object_name."' already exists and is not a ".$class_name." instance.");
}
// Save the class name and object name
- $this->_ci_classes[$class] = $classvar;
+ $this->_ci_classes[$object_name] = $class;
// Instantiate the class
- $CI =& get_instance();
- if ($config !== NULL)
- {
- $CI->$classvar = new $name($config);
- }
- else
- {
- $CI->$classvar = new $name;
- }
+ $CI->$object_name = isset($config)
+ ? new $class_name($config)
+ : new $class_name();
}
// --------------------------------------------------------------------
/**
- * Autoloader
+ * CI Autoloader
*
- * The config/autoload.php file contains an array that permits sub-systems,
- * libraries, and helpers to be loaded automatically.
+ * Loads component listed in the config/autoload.php file.
*
- * @param array
+ * @used-by CI_Loader::initialize()
* @return void
*/
- private function _ci_autoloader()
+ protected function _ci_autoloader()
{
- if (defined('ENVIRONMENT') AND file_exists(APPPATH.'config/'.ENVIRONMENT.'/autoload.php'))
+ if (file_exists(APPPATH.'config/autoload.php'))
{
- include(APPPATH.'config/'.ENVIRONMENT.'/autoload.php');
+ include(APPPATH.'config/autoload.php');
}
- else
+
+ if (file_exists(APPPATH.'config/'.ENVIRONMENT.'/autoload.php'))
{
- include(APPPATH.'config/autoload.php');
+ include(APPPATH.'config/'.ENVIRONMENT.'/autoload.php');
}
if ( ! isset($autoload))
{
- return FALSE;
+ return;
}
// Autoload packages
@@ -1139,31 +1344,29 @@ class CI_Loader {
// Load any custom config file
if (count($autoload['config']) > 0)
{
- $CI =& get_instance();
- foreach ($autoload['config'] as $key => $val)
+ foreach ($autoload['config'] as $val)
{
- $CI->config->load($val);
+ $this->config($val);
}
}
// Autoload helpers and languages
foreach (array('helper', 'language') as $type)
{
- if (isset($autoload[$type]) AND count($autoload[$type]) > 0)
+ if (isset($autoload[$type]) && count($autoload[$type]) > 0)
{
$this->$type($autoload[$type]);
}
}
- // A little tweak to remain backward compatible
- // The $autoload['core'] item was deprecated
- if ( ! isset($autoload['libraries']) AND isset($autoload['core']))
+ // Autoload drivers
+ if (isset($autoload['drivers']))
{
- $autoload['libraries'] = $autoload['core'];
+ $this->driver($autoload['drivers']);
}
// Load libraries
- if (isset($autoload['libraries']) AND count($autoload['libraries']) > 0)
+ if (isset($autoload['libraries']) && count($autoload['libraries']) > 0)
{
// Load the database driver.
if (in_array('database', $autoload['libraries']))
@@ -1173,10 +1376,7 @@ class CI_Loader {
}
// Load all other libraries
- foreach ($autoload['libraries'] as $item)
- {
- $this->library($item);
- }
+ $this->library($autoload['libraries']);
}
// Autoload models
@@ -1189,24 +1389,42 @@ class CI_Loader {
// --------------------------------------------------------------------
/**
- * Object to Array
+ * Prepare variables for _ci_vars, to be later extract()-ed inside views
*
- * Takes an object as input and converts the class variables to array key/vals
+ * Converts objects to associative arrays and filters-out internal
+ * variable names (i.e. keys prefixed with '_ci_').
*
- * @param object
+ * @param mixed $vars
* @return array
*/
- protected function _ci_object_to_array($object)
+ protected function _ci_prepare_view_vars($vars)
{
- return (is_object($object)) ? get_object_vars($object) : $object;
+ if ( ! is_array($vars))
+ {
+ $vars = is_object($vars)
+ ? get_object_vars($vars)
+ : array();
+ }
+
+ foreach (array_keys($vars) as $key)
+ {
+ if (strncmp($key, '_ci_', 4) === 0)
+ {
+ unset($vars[$key]);
+ }
+ }
+
+ return $vars;
}
// --------------------------------------------------------------------
/**
- * Get a reference to a specific library or model
+ * CI Component getter
+ *
+ * Get a reference to a specific library or model.
*
- * @param string
+ * @param string $component Component name
* @return bool
*/
protected function &_ci_get_component($component)
@@ -1214,35 +1432,4 @@ class CI_Loader {
$CI =& get_instance();
return $CI->$component;
}
-
- // --------------------------------------------------------------------
-
- /**
- * Prep filename
- *
- * This function preps the name of various items to make loading them more reliable.
- *
- * @param mixed
- * @param string
- * @return array
- */
- protected function _ci_prep_filename($filename, $extension)
- {
- if ( ! is_array($filename))
- {
- return array(strtolower(str_replace('.php', '', str_replace($extension, '', $filename)).$extension));
- }
- else
- {
- foreach ($filename as $key => $val)
- {
- $filename[$key] = strtolower(str_replace('.php', '', str_replace($extension, '', $val)).$extension);
- }
-
- return $filename;
- }
- }
}
-
-/* End of file Loader.php */
-/* Location: ./system/core/Loader.php */ \ No newline at end of file
diff --git a/system/core/Log.php b/system/core/Log.php
new file mode 100644
index 000000000..99642e0c6
--- /dev/null
+++ b/system/core/Log.php
@@ -0,0 +1,296 @@
+<?php
+/**
+ * CodeIgniter
+ *
+ * An open source application development framework for PHP
+ *
+ * This content is released under the MIT License (MIT)
+ *
+ * Copyright (c) 2019 - 2022, CodeIgniter Foundation
+ *
+ * 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.
+ *
+ * @package CodeIgniter
+ * @author EllisLab Dev Team
+ * @copyright Copyright (c) 2008 - 2014, EllisLab, Inc. (https://ellislab.com/)
+ * @copyright Copyright (c) 2014 - 2019, British Columbia Institute of Technology (https://bcit.ca/)
+ * @copyright Copyright (c) 2019 - 2022, CodeIgniter Foundation (https://codeigniter.com/)
+ * @license https://opensource.org/licenses/MIT MIT License
+ * @link https://codeigniter.com
+ * @since Version 1.0.0
+ * @filesource
+ */
+defined('BASEPATH') OR exit('No direct script access allowed');
+
+/**
+ * Logging Class
+ *
+ * @package CodeIgniter
+ * @subpackage Libraries
+ * @category Logging
+ * @author EllisLab Dev Team
+ * @link https://codeigniter.com/userguide3/general/errors.html
+ */
+class CI_Log {
+
+ /**
+ * Path to save log files
+ *
+ * @var string
+ */
+ protected $_log_path;
+
+ /**
+ * Log filename
+ *
+ * @var string
+ */
+ protected $_log_filename;
+
+ /**
+ * File permissions
+ *
+ * @var int
+ */
+ protected $_file_permissions = 0644;
+
+ /**
+ * Level of logging
+ *
+ * @var int
+ */
+ protected $_threshold = 1;
+
+ /**
+ * Array of threshold levels to log
+ *
+ * @var array
+ */
+ protected $_threshold_array = array();
+
+ /**
+ * Format of timestamp for log files
+ *
+ * @var string
+ */
+ protected $_date_fmt = 'Y-m-d H:i:s';
+
+ /**
+ * Whether or not the logger can write to the log files
+ *
+ * @var bool
+ */
+ protected $_enabled = TRUE;
+
+ /**
+ * Predefined logging levels
+ *
+ * @var array
+ */
+ protected $_levels = array('ERROR' => 1, 'DEBUG' => 2, 'INFO' => 3, 'ALL' => 4);
+
+ /**
+ * mbstring.func_overload flag
+ *
+ * @var bool
+ */
+ protected static $func_overload;
+
+ // --------------------------------------------------------------------
+
+ /**
+ * Class constructor
+ *
+ * @return void
+ */
+ public function __construct()
+ {
+ $config =& get_config();
+
+ isset(self::$func_overload) OR self::$func_overload = ( ! is_php('8.0') && extension_loaded('mbstring') && @ini_get('mbstring.func_overload'));
+
+ $this->_log_path = ($config['log_path'] !== '')
+ ? rtrim($config['log_path'], '/\\').DIRECTORY_SEPARATOR : APPPATH.'logs'.DIRECTORY_SEPARATOR;
+
+ $this->_log_filename = (isset($config['log_filename']) && $config['log_filename'] !== '')
+ ? $config['log_filename'] : 'log-'.date('Y-m-d').'.php';
+
+ file_exists($this->_log_path) OR mkdir($this->_log_path, 0755, TRUE);
+
+ if ( ! is_dir($this->_log_path) OR ! is_really_writable($this->_log_path))
+ {
+ $this->_enabled = FALSE;
+ }
+
+ if (is_numeric($config['log_threshold']))
+ {
+ $this->_threshold = (int) $config['log_threshold'];
+ }
+ elseif (is_array($config['log_threshold']))
+ {
+ $this->_threshold = 0;
+ $this->_threshold_array = array_flip($config['log_threshold']);
+ }
+
+ if ( ! empty($config['log_date_format']))
+ {
+ $this->_date_fmt = $config['log_date_format'];
+ }
+
+ if ( ! empty($config['log_file_permissions']) && is_int($config['log_file_permissions']))
+ {
+ $this->_file_permissions = $config['log_file_permissions'];
+ }
+ }
+
+ // --------------------------------------------------------------------
+
+ /**
+ * Write Log File
+ *
+ * Generally this function will be called using the global log_message() function
+ *
+ * @param string $level The error level: 'error', 'debug' or 'info'
+ * @param string $msg The error message
+ * @return bool
+ */
+ public function write_log($level, $msg)
+ {
+ if ($this->_enabled === FALSE)
+ {
+ return FALSE;
+ }
+
+ $level = strtoupper($level);
+
+ if (( ! isset($this->_levels[$level]) OR ($this->_levels[$level] > $this->_threshold))
+ && ! isset($this->_threshold_array[$this->_levels[$level]]))
+ {
+ return FALSE;
+ }
+
+ $filepath = $this->_log_path.$this->_log_filename;
+ $message = '';
+
+ if ( ! file_exists($filepath))
+ {
+ $newfile = TRUE;
+ // Only add protection to php files
+ if (substr($this->_log_filename, -3, 3) === 'php')
+ {
+ $message .= "<?php defined('BASEPATH') OR exit('No direct script access allowed'); ?>\n\n";
+ }
+ }
+
+ if ( ! $fp = @fopen($filepath, 'ab'))
+ {
+ return FALSE;
+ }
+
+ flock($fp, LOCK_EX);
+
+ // Instantiating DateTime with microseconds appended to initial date is needed for proper support of this format
+ if (strpos($this->_date_fmt, 'u') !== FALSE)
+ {
+ $microtime_full = microtime(TRUE);
+ $microtime_short = sprintf("%06d", ($microtime_full - floor($microtime_full)) * 1000000);
+ $date = new DateTime(date('Y-m-d H:i:s.'.$microtime_short, $microtime_full));
+ $date = $date->format($this->_date_fmt);
+ }
+ else
+ {
+ $date = date($this->_date_fmt);
+ }
+
+ $message .= $this->_format_line($level, $date, $msg);
+
+ for ($written = 0, $length = self::strlen($message); $written < $length; $written += $result)
+ {
+ if (($result = fwrite($fp, self::substr($message, $written))) === FALSE)
+ {
+ break;
+ }
+ }
+
+ flock($fp, LOCK_UN);
+ fclose($fp);
+
+ if (isset($newfile) && $newfile === TRUE)
+ {
+ chmod($filepath, $this->_file_permissions);
+ }
+
+ return is_int($result);
+ }
+
+ // --------------------------------------------------------------------
+
+ /**
+ * Format the log line.
+ *
+ * This is for extensibility of log formatting
+ * If you want to change the log format, extend the CI_Log class and override this method
+ *
+ * @param string $level The error level
+ * @param string $date Formatted date string
+ * @param string $message The log message
+ * @return string Formatted log line with a new line character at the end
+ */
+ protected function _format_line($level, $date, $message)
+ {
+ return $level.' - '.$date.' --> '.$message.PHP_EOL;
+ }
+
+ // --------------------------------------------------------------------
+
+ /**
+ * Byte-safe strlen()
+ *
+ * @param string $str
+ * @return int
+ */
+ protected static function strlen($str)
+ {
+ return (self::$func_overload)
+ ? mb_strlen($str, '8bit')
+ : strlen($str);
+ }
+
+ // --------------------------------------------------------------------
+
+ /**
+ * Byte-safe substr()
+ *
+ * @param string $str
+ * @param int $start
+ * @param int $length
+ * @return string
+ */
+ protected static function substr($str, $start, $length = NULL)
+ {
+ if (self::$func_overload)
+ {
+ return mb_substr($str, $start, $length, '8bit');
+ }
+
+ return isset($length)
+ ? substr($str, $start, $length)
+ : substr($str, $start);
+ }
+}
diff --git a/system/core/Model.php b/system/core/Model.php
index 1f142509e..1ba10fbb7 100644
--- a/system/core/Model.php
+++ b/system/core/Model.php
@@ -1,57 +1,69 @@
-<?php if ( ! defined('BASEPATH')) exit('No direct script access allowed');
+<?php
/**
* CodeIgniter
*
- * An open source application development framework for PHP 5.1.6 or newer
+ * An open source application development framework for PHP
*
- * @package CodeIgniter
- * @author ExpressionEngine Dev Team
- * @copyright Copyright (c) 2008 - 2014, EllisLab, Inc.
- * @license http://codeigniter.com/user_guide/license.html
- * @link http://codeigniter.com
- * @since Version 1.0
+ * This content is released under the MIT License (MIT)
+ *
+ * Copyright (c) 2019 - 2022, CodeIgniter Foundation
+ *
+ * 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.
+ *
+ * @package CodeIgniter
+ * @author EllisLab Dev Team
+ * @copyright Copyright (c) 2008 - 2014, EllisLab, Inc. (https://ellislab.com/)
+ * @copyright Copyright (c) 2014 - 2019, British Columbia Institute of Technology (https://bcit.ca/)
+ * @copyright Copyright (c) 2019 - 2022, CodeIgniter Foundation (https://codeigniter.com/)
+ * @license https://opensource.org/licenses/MIT MIT License
+ * @link https://codeigniter.com
+ * @since Version 1.0.0
* @filesource
*/
-
-// ------------------------------------------------------------------------
+defined('BASEPATH') OR exit('No direct script access allowed');
/**
- * CodeIgniter Model Class
+ * Model Class
*
* @package CodeIgniter
* @subpackage Libraries
* @category Libraries
- * @author ExpressionEngine Dev Team
- * @link http://codeigniter.com/user_guide/libraries/config.html
+ * @author EllisLab Dev Team
+ * @link https://codeigniter.com/userguide3/libraries/config.html
*/
class CI_Model {
/**
- * Constructor
- *
- * @access public
- */
- function __construct()
- {
- log_message('debug', "Model Class Initialized");
- }
-
- /**
- * __get
+ * __get magic
*
* Allows models to access CI's loaded classes using the same
* syntax as controllers.
*
- * @param string
- * @access private
+ * @param string $key
*/
- function __get($key)
+ public function __get($key)
{
- $CI =& get_instance();
- return $CI->$key;
+ // Debugging note:
+ // If you're here because you're getting an error message
+ // saying 'Undefined Property: system/core/Model.php', it's
+ // most likely a typo in your model code.
+ return get_instance()->$key;
}
-}
-// END Model Class
-/* End of file Model.php */
-/* Location: ./system/core/Model.php */ \ No newline at end of file
+}
diff --git a/system/core/Output.php b/system/core/Output.php
index 7959befb7..02f3933f5 100644
--- a/system/core/Output.php
+++ b/system/core/Output.php
@@ -1,112 +1,157 @@
-<?php if ( ! defined('BASEPATH')) exit('No direct script access allowed');
+<?php
/**
* CodeIgniter
*
- * An open source application development framework for PHP 5.1.6 or newer
+ * An open source application development framework for PHP
*
- * @package CodeIgniter
- * @author ExpressionEngine Dev Team
- * @copyright Copyright (c) 2008 - 2014, EllisLab, Inc.
- * @license http://codeigniter.com/user_guide/license.html
- * @link http://codeigniter.com
- * @since Version 1.0
+ * This content is released under the MIT License (MIT)
+ *
+ * Copyright (c) 2019 - 2022, CodeIgniter Foundation
+ *
+ * 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.
+ *
+ * @package CodeIgniter
+ * @author EllisLab Dev Team
+ * @copyright Copyright (c) 2008 - 2014, EllisLab, Inc. (https://ellislab.com/)
+ * @copyright Copyright (c) 2014 - 2019, British Columbia Institute of Technology (https://bcit.ca/)
+ * @copyright Copyright (c) 2019 - 2022, CodeIgniter Foundation (https://codeigniter.com/)
+ * @license https://opensource.org/licenses/MIT MIT License
+ * @link https://codeigniter.com
+ * @since Version 1.0.0
* @filesource
*/
-
-// ------------------------------------------------------------------------
+defined('BASEPATH') OR exit('No direct script access allowed');
/**
* Output Class
*
- * Responsible for sending final output to browser
+ * Responsible for sending final output to the browser.
*
* @package CodeIgniter
* @subpackage Libraries
* @category Output
- * @author ExpressionEngine Dev Team
- * @link http://codeigniter.com/user_guide/libraries/output.html
+ * @author EllisLab Dev Team
+ * @link https://codeigniter.com/userguide3/libraries/output.html
*/
class CI_Output {
/**
- * Current output string
+ * Final output string
*
- * @var string
- * @access protected
+ * @var string
*/
- protected $final_output;
+ public $final_output = '';
+
/**
* Cache expiration time
*
- * @var int
- * @access protected
+ * @var int
*/
- protected $cache_expiration = 0;
+ public $cache_expiration = 0;
+
/**
* List of server headers
*
- * @var array
- * @access protected
+ * @var array
*/
- protected $headers = array();
+ public $headers = array();
+
/**
* List of mime types
*
- * @var array
- * @access protected
+ * @var array
+ */
+ public $mimes = array();
+
+ /**
+ * Mime-type for the current page
+ *
+ * @var string
+ */
+ protected $mime_type = 'text/html';
+
+ /**
+ * Enable Profiler flag
+ *
+ * @var bool
*/
- protected $mime_types = array();
+ public $enable_profiler = FALSE;
+
/**
- * Determines wether profiler is enabled
+ * php.ini zlib.output_compression flag
*
- * @var book
- * @access protected
+ * @var bool
*/
- protected $enable_profiler = FALSE;
+ protected $_zlib_oc = FALSE;
+
/**
- * Determines if output compression is enabled
+ * CI output compression flag
*
- * @var bool
- * @access protected
+ * @var bool
*/
- protected $_zlib_oc = FALSE;
+ protected $_compress_output = FALSE;
+
/**
* List of profiler sections
*
- * @var array
- * @access protected
+ * @var array
*/
- protected $_profiler_sections = array();
+ protected $_profiler_sections = array();
+
/**
- * Whether or not to parse variables like {elapsed_time} and {memory_usage}
+ * Parse markers flag
+ *
+ * Whether or not to parse variables like {elapsed_time} and {memory_usage}.
*
- * @var bool
- * @access protected
+ * @var bool
*/
- protected $parse_exec_vars = TRUE;
+ public $parse_exec_vars = TRUE;
/**
- * Constructor
+ * mbstring.func_overload flag
*
+ * @var bool
*/
- function __construct()
- {
- $this->_zlib_oc = @ini_get('zlib.output_compression');
+ protected static $func_overload;
- // Get mime types for later
- if (defined('ENVIRONMENT') AND file_exists(APPPATH.'config/'.ENVIRONMENT.'/mimes.php'))
- {
- include APPPATH.'config/'.ENVIRONMENT.'/mimes.php';
- }
- else
- {
- include APPPATH.'config/mimes.php';
- }
+ /**
+ * Class constructor
+ *
+ * Determines whether zLib output compression will be used.
+ *
+ * @return void
+ */
+ public function __construct()
+ {
+ $this->_zlib_oc = (bool) ini_get('zlib.output_compression');
+ $this->_compress_output = (
+ $this->_zlib_oc === FALSE
+ && config_item('compress_output') === TRUE
+ && extension_loaded('zlib')
+ );
+ isset(self::$func_overload) OR self::$func_overload = ( ! is_php('8.0') && extension_loaded('mbstring') && @ini_get('mbstring.func_overload'));
- $this->mime_types = $mimes;
+ // Get mime types for later
+ $this->mimes =& get_mimes();
- log_message('debug', "Output Class Initialized");
+ log_message('info', 'Output Class Initialized');
}
// --------------------------------------------------------------------
@@ -114,12 +159,11 @@ class CI_Output {
/**
* Get Output
*
- * Returns the current output string
+ * Returns the current output string.
*
- * @access public
* @return string
*/
- function get_output()
+ public function get_output()
{
return $this->final_output;
}
@@ -129,16 +173,14 @@ class CI_Output {
/**
* Set Output
*
- * Sets the output string
+ * Sets the output string.
*
- * @access public
- * @param string
- * @return void
+ * @param string $output Output data
+ * @return CI_Output
*/
- function set_output($output)
+ public function set_output($output)
{
$this->final_output = $output;
-
return $this;
}
@@ -147,23 +189,14 @@ class CI_Output {
/**
* Append Output
*
- * Appends data onto the output string
+ * Appends data onto the output string.
*
- * @access public
- * @param string
- * @return void
+ * @param string $output Data to append
+ * @return CI_Output
*/
- function append_output($output)
+ public function append_output($output)
{
- if ($this->final_output == '')
- {
- $this->final_output = $output;
- }
- else
- {
- $this->final_output .= $output;
- }
-
+ $this->final_output .= $output;
return $this;
}
@@ -172,52 +205,49 @@ class CI_Output {
/**
* Set Header
*
- * Lets you set a server header which will be outputted with the final display.
+ * Lets you set a server header which will be sent with the final output.
*
- * Note: If a file is cached, headers will not be sent. We need to figure out
- * how to permit header data to be saved with the cache data...
+ * Note: If a file is cached, headers will not be sent.
+ * @todo We need to figure out how to permit headers to be cached.
*
- * @access public
- * @param string
- * @param bool
- * @return void
+ * @param string $header Header
+ * @param bool $replace Whether to replace the old header value, if already set
+ * @return CI_Output
*/
- function set_header($header, $replace = TRUE)
+ public function set_header($header, $replace = TRUE)
{
// If zlib.output_compression is enabled it will compress the output,
// but it will not modify the content-length header to compensate for
// the reduction, causing the browser to hang waiting for more data.
// We'll just skip content-length in those cases.
-
- if ($this->_zlib_oc && strncasecmp($header, 'content-length', 14) == 0)
+ if ($this->_zlib_oc && strncasecmp($header, 'content-length', 14) === 0)
{
- return;
+ return $this;
}
$this->headers[] = array($header, $replace);
-
return $this;
}
// --------------------------------------------------------------------
/**
- * Set Content Type Header
+ * Set Content-Type Header
*
- * @access public
- * @param string extension of the file we're outputting
- * @return void
+ * @param string $mime_type Extension of the file we're outputting
+ * @param string $charset Character set (default: NULL)
+ * @return CI_Output
*/
- function set_content_type($mime_type)
+ public function set_content_type($mime_type, $charset = NULL)
{
if (strpos($mime_type, '/') === FALSE)
{
$extension = ltrim($mime_type, '.');
// Is this extension supported?
- if (isset($this->mime_types[$extension]))
+ if (isset($this->mimes[$extension]))
{
- $mime_type =& $this->mime_types[$extension];
+ $mime_type =& $this->mimes[$extension];
if (is_array($mime_type))
{
@@ -226,28 +256,93 @@ class CI_Output {
}
}
- $header = 'Content-Type: '.$mime_type;
+ $this->mime_type = $mime_type;
- $this->headers[] = array($header, TRUE);
+ if (empty($charset))
+ {
+ $charset = config_item('charset');
+ }
+ $header = 'Content-Type: '.$mime_type
+ .(empty($charset) ? '' : '; charset='.$charset);
+
+ $this->headers[] = array($header, TRUE);
return $this;
}
// --------------------------------------------------------------------
/**
+ * Get Current Content-Type Header
+ *
+ * @return string 'text/html', if not already set
+ */
+ public function get_content_type()
+ {
+ for ($i = 0, $c = count($this->headers); $i < $c; $i++)
+ {
+ if (sscanf($this->headers[$i][0], 'Content-Type: %[^;]', $content_type) === 1)
+ {
+ return $content_type;
+ }
+ }
+
+ return 'text/html';
+ }
+
+ // --------------------------------------------------------------------
+
+ /**
+ * Get Header
+ *
+ * @param string $header
+ * @return string
+ */
+ public function get_header($header)
+ {
+ // We only need [x][0] from our multi-dimensional array
+ $header_lines = array_map(function ($headers)
+ {
+ return array_shift($headers);
+ }, $this->headers);
+
+ $headers = array_merge(
+ $header_lines,
+ headers_list()
+ );
+
+ if (empty($headers) OR empty($header))
+ {
+ return NULL;
+ }
+
+ // Count backwards, in order to get the last matching header
+ for ($c = count($headers) - 1; $c > -1; $c--)
+ {
+ if (strncasecmp($header, $headers[$c], $l = self::strlen($header)) === 0)
+ {
+ return trim(self::substr($headers[$c], $l+1));
+ }
+ }
+
+ return NULL;
+ }
+
+ // --------------------------------------------------------------------
+
+ /**
* Set HTTP Status Header
- * moved to Common procedural functions in 1.7.2
*
- * @access public
- * @param int the status code
- * @param string
- * @return void
+ * As of version 1.7.2, this is an alias for common function
+ * set_status_header().
+ *
+ * @param int $code Status code (default: 200)
+ * @param string $text Optional message
+ * @return CI_Output
*/
- function set_status_header($code = 200, $text = '')
+ public function set_status_header($code = 200, $text = '')
{
set_status_header($code, $text);
-
return $this;
}
@@ -256,14 +351,12 @@ class CI_Output {
/**
* Enable/disable Profiler
*
- * @access public
- * @param bool
- * @return void
+ * @param bool $val TRUE to enable or FALSE to disable
+ * @return CI_Output
*/
- function enable_profiler($val = TRUE)
+ public function enable_profiler($val = TRUE)
{
- $this->enable_profiler = (is_bool($val)) ? $val : TRUE;
-
+ $this->enable_profiler = is_bool($val) ? $val : TRUE;
return $this;
}
@@ -272,17 +365,23 @@ class CI_Output {
/**
* Set Profiler Sections
*
- * Allows override of default / config settings for Profiler section display
+ * Allows override of default/config settings for
+ * Profiler section display.
*
- * @access public
- * @param array
- * @return void
+ * @param array $sections Profiler sections
+ * @return CI_Output
*/
- function set_profiler_sections($sections)
+ public function set_profiler_sections($sections)
{
+ if (isset($sections['query_toggle_count']))
+ {
+ $this->_profiler_sections['query_toggle_count'] = (int) $sections['query_toggle_count'];
+ unset($sections['query_toggle_count']);
+ }
+
foreach ($sections as $section => $enable)
{
- $this->_profiler_sections[$section] = ($enable !== FALSE) ? TRUE : FALSE;
+ $this->_profiler_sections[$section] = ($enable !== FALSE);
}
return $this;
@@ -293,14 +392,12 @@ class CI_Output {
/**
* Set Cache
*
- * @access public
- * @param integer
- * @return void
+ * @param int $time Cache expiration time in minutes
+ * @return CI_Output
*/
- function cache($time)
+ public function cache($time)
{
- $this->cache_expiration = ( ! is_numeric($time)) ? 0 : $time;
-
+ $this->cache_expiration = is_numeric($time) ? $time : 0;
return $this;
}
@@ -309,27 +406,27 @@ class CI_Output {
/**
* Display Output
*
- * All "view" data is automatically put into this variable by the controller class:
+ * Processes and sends finalized output data to the browser along
+ * with any server headers and profile data. It also stops benchmark
+ * timers so the page rendering speed and memory usage can be shown.
*
- * $this->final_output
+ * Note: All "view" data is automatically put into $this->final_output
+ * by controller class.
*
- * This function sends the finalized output data to the browser along
- * with any server headers and profile data. It also stops the
- * benchmark timer so the page rendering speed and memory usage can be shown.
- *
- * @access public
- * @param string
- * @return mixed
+ * @uses CI_Output::$final_output
+ * @param string $output Output data override
+ * @return void
*/
- function _display($output = '')
+ public function _display($output = NULL)
{
- // Note: We use globals because we can't use $CI =& get_instance()
+ // Note: We use load_class() because we can't use $CI =& get_instance()
// since this function is sometimes called by the caching mechanism,
// which happens before the CI super object is available.
- global $BM, $CFG;
+ $BM =& load_class('Benchmark', 'core');
+ $CFG =& load_class('Config', 'core');
// Grab the super object if we can.
- if (class_exists('CI_Controller'))
+ if (class_exists('CI_Controller', FALSE))
{
$CI =& get_instance();
}
@@ -337,14 +434,14 @@ class CI_Output {
// --------------------------------------------------------------------
// Set the output data
- if ($output == '')
+ if ($output === NULL)
{
$output =& $this->final_output;
}
// --------------------------------------------------------------------
- // Do we need to write a cache file? Only if the controller does not have its
+ // Do we need to write a cache file? Only if the controller does not have its
// own _output() method and we are not dealing with a cache file, which we
// can determine by the existence of the $CI object above
if ($this->cache_expiration > 0 && isset($CI) && ! method_exists($CI, '_output'))
@@ -361,24 +458,18 @@ class CI_Output {
if ($this->parse_exec_vars === TRUE)
{
- $memory = ( ! function_exists('memory_get_usage')) ? '0' : round(memory_get_usage()/1024/1024, 2).'MB';
-
- $output = str_replace('{elapsed_time}', $elapsed, $output);
- $output = str_replace('{memory_usage}', $memory, $output);
+ $memory = round(memory_get_usage() / 1024 / 1024, 2).'MB';
+ $output = str_replace(array('{elapsed_time}', '{memory_usage}'), array($elapsed, $memory), $output);
}
// --------------------------------------------------------------------
// Is compression requested?
- if ($CFG->item('compress_output') === TRUE && $this->_zlib_oc == FALSE)
+ if (isset($CI) // This means that we're not serving a cache file, if we were, it would already be compressed
+ && $this->_compress_output === TRUE
+ && isset($_SERVER['HTTP_ACCEPT_ENCODING']) && strpos($_SERVER['HTTP_ACCEPT_ENCODING'], 'gzip') !== FALSE)
{
- if (extension_loaded('zlib'))
- {
- if (isset($_SERVER['HTTP_ACCEPT_ENCODING']) AND strpos($_SERVER['HTTP_ACCEPT_ENCODING'], 'gzip') !== FALSE)
- {
- ob_start('ob_gzhandler');
- }
- }
+ ob_start('ob_gzhandler');
}
// --------------------------------------------------------------------
@@ -399,20 +490,34 @@ class CI_Output {
// simply echo out the data and exit.
if ( ! isset($CI))
{
+ if ($this->_compress_output === TRUE)
+ {
+ if (isset($_SERVER['HTTP_ACCEPT_ENCODING']) && strpos($_SERVER['HTTP_ACCEPT_ENCODING'], 'gzip') !== FALSE)
+ {
+ header('Content-Encoding: gzip');
+ header('Content-Length: '.self::strlen($output));
+ }
+ else
+ {
+ // User agent doesn't support gzip compression,
+ // so we'll have to decompress our cache
+ $output = gzinflate(self::substr($output, 10, -8));
+ }
+ }
+
echo $output;
- log_message('debug', "Final output sent to browser");
- log_message('debug', "Total execution time: ".$elapsed);
- return TRUE;
+ log_message('info', 'Final output sent to browser');
+ log_message('info', 'Total execution time: '.$elapsed);
+ return;
}
// --------------------------------------------------------------------
// Do we need to generate profile data?
// If so, load the Profile class and run it.
- if ($this->enable_profiler == TRUE)
+ if ($this->enable_profiler === TRUE)
{
$CI->load->library('profiler');
-
if ( ! empty($this->_profiler_sections))
{
$CI->profiler->set_sections($this->_profiler_sections);
@@ -420,20 +525,13 @@ class CI_Output {
// If the output data contains closing </body> and </html> tags
// we will remove them and add them back after we insert the profile data
- if (preg_match("|</body>.*?</html>|is", $output))
+ $output = preg_replace('|</body>.*?</html>|is', '', $output, -1, $count).$CI->profiler->run();
+ if ($count > 0)
{
- $output = preg_replace("|</body>.*?</html>|is", '', $output);
- $output .= $CI->profiler->run();
$output .= '</body></html>';
}
- else
- {
- $output .= $CI->profiler->run();
- }
}
- // --------------------------------------------------------------------
-
// Does the controller contain a function named _output()?
// If so send the output there. Otherwise, echo it.
if (method_exists($CI, '_output'))
@@ -442,133 +540,305 @@ class CI_Output {
}
else
{
- echo $output; // Send it to the browser!
+ echo $output; // Send it to the browser!
}
- log_message('debug', "Final output sent to browser");
- log_message('debug', "Total execution time: ".$elapsed);
+ log_message('info', 'Final output sent to browser');
+ log_message('info', 'Total execution time: '.$elapsed);
}
// --------------------------------------------------------------------
/**
- * Write a Cache File
+ * Write Cache
*
- * @access public
- * @param string
+ * @param string $output Output data to cache
* @return void
*/
- function _write_cache($output)
+ public function _write_cache($output)
{
$CI =& get_instance();
$path = $CI->config->item('cache_path');
-
- $cache_path = ($path == '') ? APPPATH.'cache/' : $path;
+ $cache_path = ($path === '') ? APPPATH.'cache'.DIRECTORY_SEPARATOR : rtrim($path, '/\\').DIRECTORY_SEPARATOR;
if ( ! is_dir($cache_path) OR ! is_really_writable($cache_path))
{
- log_message('error', "Unable to write cache file: ".$cache_path);
+ log_message('error', 'Unable to write cache file: '.$cache_path);
return;
}
- $uri = $CI->config->item('base_url').
- $CI->config->item('index_page').
- $CI->uri->uri_string();
+ $uri = $CI->config->item('base_url')
+ .$CI->config->slash_item('index_page')
+ .$CI->uri->uri_string();
+
+ if (($cache_query_string = $CI->config->item('cache_query_string')) && ! empty($_SERVER['QUERY_STRING']))
+ {
+ if (is_array($cache_query_string))
+ {
+ $uri .= '?'.http_build_query(array_intersect_key($_GET, array_flip($cache_query_string)));
+ }
+ else
+ {
+ $uri .= '?'.$_SERVER['QUERY_STRING'];
+ }
+ }
$cache_path .= md5($uri);
- if ( ! $fp = @fopen($cache_path, FOPEN_WRITE_CREATE_DESTRUCTIVE))
+ if ( ! $fp = @fopen($cache_path, 'w+b'))
{
- log_message('error', "Unable to write cache file: ".$cache_path);
+ log_message('error', 'Unable to write cache file: '.$cache_path);
return;
}
+ if ( ! flock($fp, LOCK_EX))
+ {
+ log_message('error', 'Unable to secure a file lock for file at: '.$cache_path);
+ fclose($fp);
+ return;
+ }
+
+ // If output compression is enabled, compress the cache
+ // itself, so that we don't have to do that each time
+ // we're serving it
+ if ($this->_compress_output === TRUE)
+ {
+ $output = gzencode($output);
+
+ if ($this->get_header('content-type') === NULL)
+ {
+ $this->set_content_type($this->mime_type);
+ }
+ }
+
$expire = time() + ($this->cache_expiration * 60);
- if (flock($fp, LOCK_EX))
+ // Put together our serialized info.
+ $cache_info = serialize(array(
+ 'expire' => $expire,
+ 'headers' => $this->headers
+ ));
+
+ $output = $cache_info.'ENDCI--->'.$output;
+
+ for ($written = 0, $length = self::strlen($output); $written < $length; $written += $result)
{
- fwrite($fp, $expire.'TS--->'.$output);
- flock($fp, LOCK_UN);
+ if (($result = fwrite($fp, self::substr($output, $written))) === FALSE)
+ {
+ break;
+ }
}
- else
+
+ flock($fp, LOCK_UN);
+ fclose($fp);
+
+ if ( ! is_int($result))
{
- log_message('error', "Unable to secure a file lock for file at: ".$cache_path);
+ @unlink($cache_path);
+ log_message('error', 'Unable to write the complete cache content at: '.$cache_path);
return;
}
- fclose($fp);
- @chmod($cache_path, FILE_WRITE_MODE);
- log_message('debug', "Cache file written: ".$cache_path);
+ chmod($cache_path, 0640);
+ log_message('debug', 'Cache file written: '.$cache_path);
+
+ // Send HTTP cache-control headers to browser to match file cache settings.
+ $this->set_cache_header($_SERVER['REQUEST_TIME'], $expire);
}
// --------------------------------------------------------------------
/**
- * Update/serve a cached file
+ * Update/serve cached output
*
- * @access public
- * @param object config class
- * @param object uri class
- * @return void
+ * @uses CI_Config
+ * @uses CI_URI
+ *
+ * @param object &$CFG CI_Config class instance
+ * @param object &$URI CI_URI class instance
+ * @return bool TRUE on success or FALSE on failure
*/
- function _display_cache(&$CFG, &$URI)
+ public function _display_cache(&$CFG, &$URI)
{
- $cache_path = ($CFG->item('cache_path') == '') ? APPPATH.'cache/' : $CFG->item('cache_path');
+ $cache_path = ($CFG->item('cache_path') === '') ? APPPATH.'cache/' : $CFG->item('cache_path');
+
+ // Build the file path. The file name is an MD5 hash of the full URI
+ $uri = $CFG->item('base_url').$CFG->slash_item('index_page').$URI->uri_string;
- // Build the file path. The file name is an MD5 hash of the full URI
- $uri = $CFG->item('base_url').
- $CFG->item('index_page').
- $URI->uri_string;
+ if (($cache_query_string = $CFG->item('cache_query_string')) && ! empty($_SERVER['QUERY_STRING']))
+ {
+ if (is_array($cache_query_string))
+ {
+ $uri .= '?'.http_build_query(array_intersect_key($_GET, array_flip($cache_query_string)));
+ }
+ else
+ {
+ $uri .= '?'.$_SERVER['QUERY_STRING'];
+ }
+ }
$filepath = $cache_path.md5($uri);
- if ( ! @file_exists($filepath))
+ if ( ! file_exists($filepath) OR ! $fp = @fopen($filepath, 'rb'))
{
return FALSE;
}
- if ( ! $fp = @fopen($filepath, FOPEN_READ))
+ flock($fp, LOCK_SH);
+
+ $cache = (filesize($filepath) > 0) ? fread($fp, filesize($filepath)) : '';
+
+ flock($fp, LOCK_UN);
+ fclose($fp);
+
+ // Look for embedded serialized file info.
+ if ( ! preg_match('/^(.*)ENDCI--->/', $cache, $match))
{
return FALSE;
}
- flock($fp, LOCK_SH);
+ $cache_info = unserialize($match[1]);
+ $expire = $cache_info['expire'];
- $cache = '';
- if (filesize($filepath) > 0)
+ $last_modified = filemtime($filepath);
+
+ // Has the file expired?
+ if ($_SERVER['REQUEST_TIME'] >= $expire && is_really_writable($cache_path))
{
- $cache = fread($fp, filesize($filepath));
+ // If so we'll delete it.
+ @unlink($filepath);
+ log_message('debug', 'Cache file has expired. File deleted.');
+ return FALSE;
}
- flock($fp, LOCK_UN);
- fclose($fp);
+ // Send the HTTP cache control headers
+ $this->set_cache_header($last_modified, $expire);
+
+ // Add headers from cache file.
+ foreach ($cache_info['headers'] as $header)
+ {
+ $this->set_header($header[0], $header[1]);
+ }
- // Strip out the embedded timestamp
- if ( ! preg_match("/(\d+TS--->)/", $cache, $match))
+ // Display the cache
+ $this->_display(self::substr($cache, self::strlen($match[0])));
+ log_message('debug', 'Cache file is current. Sending it to browser.');
+ return TRUE;
+ }
+
+ // --------------------------------------------------------------------
+
+ /**
+ * Delete cache
+ *
+ * @param string $uri URI string
+ * @return bool
+ */
+ public function delete_cache($uri = '')
+ {
+ $CI =& get_instance();
+ $cache_path = $CI->config->item('cache_path');
+ if ($cache_path === '')
{
+ $cache_path = APPPATH.'cache/';
+ }
+
+ if ( ! is_dir($cache_path))
+ {
+ log_message('error', 'Unable to find cache path: '.$cache_path);
return FALSE;
}
- // Has the file expired? If so we'll delete it.
- if (time() >= trim(str_replace('TS--->', '', $match['1'])))
+ if (empty($uri))
{
- if (is_really_writable($cache_path))
+ $uri = $CI->uri->uri_string();
+
+ if (($cache_query_string = $CI->config->item('cache_query_string')) && ! empty($_SERVER['QUERY_STRING']))
{
- @unlink($filepath);
- log_message('debug', "Cache file has expired. File deleted");
- return FALSE;
+ if (is_array($cache_query_string))
+ {
+ $uri .= '?'.http_build_query(array_intersect_key($_GET, array_flip($cache_query_string)));
+ }
+ else
+ {
+ $uri .= '?'.$_SERVER['QUERY_STRING'];
+ }
}
}
- // Display the cache
- $this->_display(str_replace($match['0'], '', $cache));
- log_message('debug', "Cache file is current. Sending it to browser.");
+ $cache_path .= md5($CI->config->item('base_url').$CI->config->slash_item('index_page').ltrim($uri, '/'));
+
+ if ( ! @unlink($cache_path))
+ {
+ log_message('error', 'Unable to delete cache file for '.$uri);
+ return FALSE;
+ }
+
return TRUE;
}
+ // --------------------------------------------------------------------
-}
-// END Output Class
+ /**
+ * Set Cache Header
+ *
+ * Set the HTTP headers to match the server-side file cache settings
+ * in order to reduce bandwidth.
+ *
+ * @param int $last_modified Timestamp of when the page was last modified
+ * @param int $expiration Timestamp of when should the requested page expire from cache
+ * @return void
+ */
+ public function set_cache_header($last_modified, $expiration)
+ {
+ $max_age = $expiration - $_SERVER['REQUEST_TIME'];
-/* End of file Output.php */
-/* Location: ./system/core/Output.php */ \ No newline at end of file
+ if (isset($_SERVER['HTTP_IF_MODIFIED_SINCE']) && $last_modified <= strtotime($_SERVER['HTTP_IF_MODIFIED_SINCE']))
+ {
+ $this->set_status_header(304);
+ exit;
+ }
+
+ header('Pragma: public');
+ header('Cache-Control: max-age='.$max_age.', public');
+ header('Expires: '.gmdate('D, d M Y H:i:s', $expiration).' GMT');
+ header('Last-modified: '.gmdate('D, d M Y H:i:s', $last_modified).' GMT');
+ }
+
+ // --------------------------------------------------------------------
+
+ /**
+ * Byte-safe strlen()
+ *
+ * @param string $str
+ * @return int
+ */
+ protected static function strlen($str)
+ {
+ return (self::$func_overload)
+ ? mb_strlen($str, '8bit')
+ : strlen($str);
+ }
+
+ // --------------------------------------------------------------------
+
+ /**
+ * Byte-safe substr()
+ *
+ * @param string $str
+ * @param int $start
+ * @param int $length
+ * @return string
+ */
+ protected static function substr($str, $start, $length = NULL)
+ {
+ if (self::$func_overload)
+ {
+ return mb_substr($str, $start, $length, '8bit');
+ }
+
+ return isset($length)
+ ? substr($str, $start, $length)
+ : substr($str, $start);
+ }
+}
diff --git a/system/core/Router.php b/system/core/Router.php
index b48a34562..f15a596f4 100644
--- a/system/core/Router.php
+++ b/system/core/Router.php
@@ -1,19 +1,42 @@
-<?php if ( ! defined('BASEPATH')) exit('No direct script access allowed');
+<?php
/**
* CodeIgniter
*
- * An open source application development framework for PHP 5.1.6 or newer
+ * An open source application development framework for PHP
*
- * @package CodeIgniter
- * @author ExpressionEngine Dev Team
- * @copyright Copyright (c) 2008 - 2014, EllisLab, Inc.
- * @license http://codeigniter.com/user_guide/license.html
- * @link http://codeigniter.com
- * @since Version 1.0
+ * This content is released under the MIT License (MIT)
+ *
+ * Copyright (c) 2019 - 2022, CodeIgniter Foundation
+ *
+ * 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.
+ *
+ * @package CodeIgniter
+ * @author EllisLab Dev Team
+ * @copyright Copyright (c) 2008 - 2014, EllisLab, Inc. (https://ellislab.com/)
+ * @copyright Copyright (c) 2014 - 2019, British Columbia Institute of Technology (https://bcit.ca/)
+ * @copyright Copyright (c) 2019 - 2022, CodeIgniter Foundation (https://codeigniter.com/)
+ * @license https://opensource.org/licenses/MIT MIT License
+ * @link https://codeigniter.com
+ * @since Version 1.0.0
* @filesource
*/
-
-// ------------------------------------------------------------------------
+defined('BASEPATH') OR exit('No direct script access allowed');
/**
* Router Class
@@ -22,389 +45,397 @@
*
* @package CodeIgniter
* @subpackage Libraries
- * @author ExpressionEngine Dev Team
* @category Libraries
- * @link http://codeigniter.com/user_guide/general/routing.html
+ * @author EllisLab Dev Team
+ * @link https://codeigniter.com/userguide3/general/routing.html
*/
class CI_Router {
/**
- * Config class
+ * CI_Config class object
*
- * @var object
- * @access public
+ * @var object
*/
- var $config;
+ public $config;
+
/**
* List of routes
*
- * @var array
- * @access public
- */
- var $routes = array();
- /**
- * List of error routes
- *
- * @var array
- * @access public
+ * @var array
*/
- var $error_routes = array();
+ public $routes = array();
+
/**
* Current class name
*
- * @var string
- * @access public
+ * @var string
*/
- var $class = '';
+ public $class = '';
+
/**
* Current method name
*
- * @var string
- * @access public
+ * @var string
*/
- var $method = 'index';
+ public $method = 'index';
+
/**
* Sub-directory that contains the requested controller class
*
- * @var string
- * @access public
+ * @var string
*/
- var $directory = '';
+ public $directory = '';
+
/**
* Default controller (and method if specific)
*
- * @var string
- * @access public
+ * @var string
+ */
+ public $default_controller;
+
+ /**
+ * Translate URI dashes
+ *
+ * Determines whether dashes in controller & method segments
+ * should be automatically replaced by underscores.
+ *
+ * @var bool
+ */
+ public $translate_uri_dashes = FALSE;
+
+ /**
+ * Enable query strings flag
+ *
+ * Determines whether to use GET parameters or segment URIs
+ *
+ * @var bool
*/
- var $default_controller;
+ public $enable_query_strings = FALSE;
+
+ // --------------------------------------------------------------------
+
+
+ public $uri;
/**
- * Constructor
+ * Class constructor
*
* Runs the route mapping function.
+ *
+ * @param array $routing
+ * @return void
*/
- function __construct()
+ public function __construct($routing = NULL)
{
$this->config =& load_class('Config', 'core');
$this->uri =& load_class('URI', 'core');
- log_message('debug', "Router Class Initialized");
+
+ $this->enable_query_strings = ( ! is_cli() && $this->config->item('enable_query_strings') === TRUE);
+
+ // If a directory override is configured, it has to be set before any dynamic routing logic
+ is_array($routing) && isset($routing['directory']) && $this->set_directory($routing['directory']);
+ $this->_set_routing();
+
+ // Set any routing overrides that may exist in the main index file
+ if (is_array($routing))
+ {
+ empty($routing['controller']) OR $this->set_class($routing['controller']);
+ empty($routing['function']) OR $this->set_method($routing['function']);
+ }
+
+ log_message('info', 'Router Class Initialized');
}
// --------------------------------------------------------------------
/**
- * Set the route mapping
+ * Set route mapping
*
- * This function determines what should be served based on the URI request,
+ * Determines what should be served based on the URI request,
* as well as any "routes" that have been set in the routing config file.
*
- * @access private
* @return void
*/
- function _set_routing()
+ protected function _set_routing()
{
- // Are query strings enabled in the config file? Normally CI doesn't utilize query strings
- // since URI segments are more search-engine friendly, but they can optionally be used.
- // If this feature is enabled, we will gather the directory/class/method a little differently
- $segments = array();
- if ($this->config->item('enable_query_strings') === TRUE AND isset($_GET[$this->config->item('controller_trigger')]))
+ // Load the routes.php file. It would be great if we could
+ // skip this for enable_query_strings = TRUE, but then
+ // default_controller would be empty ...
+ if (file_exists(APPPATH.'config/routes.php'))
{
- if (isset($_GET[$this->config->item('directory_trigger')]))
- {
- $this->set_directory(trim($this->uri->_filter_uri($_GET[$this->config->item('directory_trigger')])));
- $segments[] = $this->fetch_directory();
- }
-
- if (isset($_GET[$this->config->item('controller_trigger')]))
- {
- $this->set_class(trim($this->uri->_filter_uri($_GET[$this->config->item('controller_trigger')])));
- $segments[] = $this->fetch_class();
- }
-
- if (isset($_GET[$this->config->item('function_trigger')]))
- {
- $this->set_method(trim($this->uri->_filter_uri($_GET[$this->config->item('function_trigger')])));
- $segments[] = $this->fetch_method();
- }
+ include(APPPATH.'config/routes.php');
}
- // Load the routes.php file.
- if (defined('ENVIRONMENT') AND is_file(APPPATH.'config/'.ENVIRONMENT.'/routes.php'))
+ if (file_exists(APPPATH.'config/'.ENVIRONMENT.'/routes.php'))
{
include(APPPATH.'config/'.ENVIRONMENT.'/routes.php');
}
- elseif (is_file(APPPATH.'config/routes.php'))
- {
- include(APPPATH.'config/routes.php');
- }
-
- $this->routes = ( ! isset($route) OR ! is_array($route)) ? array() : $route;
- unset($route);
- // Set the default controller so we can display it in the event
- // the URI doesn't correlated to a valid controller.
- $this->default_controller = ( ! isset($this->routes['default_controller']) OR $this->routes['default_controller'] == '') ? FALSE : strtolower($this->routes['default_controller']);
-
- // Were there any query string segments? If so, we'll validate them and bail out since we're done.
- if (count($segments) > 0)
+ // Validate & get reserved routes
+ if (isset($route) && is_array($route))
{
- return $this->_validate_request($segments);
+ isset($route['default_controller']) && $this->default_controller = $route['default_controller'];
+ isset($route['translate_uri_dashes']) && $this->translate_uri_dashes = $route['translate_uri_dashes'];
+ unset($route['default_controller'], $route['translate_uri_dashes']);
+ $this->routes = $route;
}
- // Fetch the complete URI string
- $this->uri->_fetch_uri_string();
-
- // Is there a URI string? If not, the default controller specified in the "routes" file will be shown.
- if ($this->uri->uri_string == '')
+ // Are query strings enabled in the config file? Normally CI doesn't utilize query strings
+ // since URI segments are more search-engine friendly, but they can optionally be used.
+ // If this feature is enabled, we will gather the directory/class/method a little differently
+ if ($this->enable_query_strings)
{
- return $this->_set_default_controller();
- }
-
- // Do we need to remove the URL suffix?
- $this->uri->_remove_url_suffix();
+ // If the directory is set at this time, it means an override exists, so skip the checks
+ if ( ! isset($this->directory))
+ {
+ $_d = $this->config->item('directory_trigger');
+ $_d = isset($_GET[$_d]) ? trim($_GET[$_d], " \t\n\r\0\x0B/") : '';
- // Compile the segments into an array
- $this->uri->_explode_segments();
+ if ($_d !== '')
+ {
+ $this->uri->filter_uri($_d);
+ $this->set_directory($_d);
+ }
+ }
- // Parse any custom routing that may exist
- $this->_parse_routes();
+ $_c = trim($this->config->item('controller_trigger'));
+ if ( ! empty($_GET[$_c]))
+ {
+ $this->uri->filter_uri($_GET[$_c]);
+ $this->set_class($_GET[$_c]);
- // Re-index the segment array so that it starts with 1 rather than 0
- $this->uri->_reindex_segments();
- }
+ $_f = trim($this->config->item('function_trigger'));
+ if ( ! empty($_GET[$_f]))
+ {
+ $this->uri->filter_uri($_GET[$_f]);
+ $this->set_method($_GET[$_f]);
+ }
- // --------------------------------------------------------------------
+ $this->uri->rsegments = array(
+ 1 => $this->class,
+ 2 => $this->method
+ );
+ }
+ else
+ {
+ $this->_set_default_controller();
+ }
- /**
- * Set the default controller
- *
- * @access private
- * @return void
- */
- function _set_default_controller()
- {
- if ($this->default_controller === FALSE)
- {
- show_error("Unable to determine what should be displayed. A default route has not been specified in the routing file.");
+ // Routing rules don't apply to query strings and we don't need to detect
+ // directories, so we're done here
+ return;
}
- // Is the method being specified?
- if (strpos($this->default_controller, '/') !== FALSE)
- {
- $x = explode('/', $this->default_controller);
- $this->set_class($x[0]);
- $this->set_method($x[1]);
- $this->_set_request($x);
+ // Is there anything to parse?
+ if ($this->uri->uri_string !== '')
+ {
+ $this->_parse_routes();
}
else
{
- $this->set_class($this->default_controller);
- $this->set_method('index');
- $this->_set_request(array($this->default_controller, 'index'));
+ $this->_set_default_controller();
}
-
- // re-index the routed segments array so it starts with 1 rather than 0
- $this->uri->_reindex_segments();
-
- log_message('debug', "No URI present. Default controller set.");
}
// --------------------------------------------------------------------
/**
- * Set the Route
+ * Set request route
*
- * This function takes an array of URI segments as
- * input, and sets the current class/method
+ * Takes an array of URI segments as input and sets the class/method
+ * to be called.
*
- * @access private
- * @param array
- * @param bool
+ * @used-by CI_Router::_parse_routes()
+ * @param array $segments URI segments
* @return void
*/
- function _set_request($segments = array())
+ protected function _set_request($segments = array())
{
$segments = $this->_validate_request($segments);
+ // If we don't have any segments left - try the default controller;
+ // WARNING: Directories get shifted out of the segments array!
+ if (empty($segments))
+ {
+ $this->_set_default_controller();
+ return;
+ }
- if (count($segments) == 0)
+ if ($this->translate_uri_dashes === TRUE)
{
- return $this->_set_default_controller();
+ $segments[0] = str_replace('-', '_', $segments[0]);
+ if (isset($segments[1]))
+ {
+ $segments[1] = str_replace('-', '_', $segments[1]);
+ }
}
$this->set_class($segments[0]);
-
if (isset($segments[1]))
{
- // A standard method request
$this->set_method($segments[1]);
}
else
{
- // This lets the "routed" segment array identify that the default
- // index method is being used.
$segments[1] = 'index';
}
- // Update our "routed" segment array to contain the segments.
- // Note: If there is no custom routing, this array will be
- // identical to $this->uri->segments
+ array_unshift($segments, NULL);
+ unset($segments[0]);
$this->uri->rsegments = $segments;
}
// --------------------------------------------------------------------
/**
- * Validates the supplied segments. Attempts to determine the path to
- * the controller.
+ * Set default controller
*
- * @access private
- * @param array
- * @return array
+ * @return void
*/
- function _validate_request($segments)
+ protected function _set_default_controller()
{
- if (count($segments) == 0)
+ if (empty($this->default_controller))
{
- return $segments;
+ show_error('Unable to determine what should be displayed. A default route has not been specified in the routing file.');
}
- // Does the requested controller exist in the root folder?
- if (file_exists(APPPATH.'controllers/'.$segments[0].'.php'))
+ // Is the method being specified?
+ if (sscanf($this->default_controller, '%[^/]/%s', $class, $method) !== 2)
{
- return $segments;
+ $method = 'index';
}
- // Is the controller in a sub-folder?
- if (is_dir(APPPATH.'controllers/'.$segments[0]))
+ if ( ! file_exists(APPPATH.'controllers/'.$this->directory.ucfirst($class).'.php'))
{
- // Set the directory and remove it from the segment array
- $this->set_directory($segments[0]);
- $segments = array_slice($segments, 1);
+ // This will trigger 404 later
+ return;
+ }
- if (count($segments) > 0)
- {
- // Does the requested controller exist in the sub-folder?
- if ( ! file_exists(APPPATH.'controllers/'.$this->fetch_directory().$segments[0].'.php'))
- {
- if ( ! empty($this->routes['404_override']))
- {
- $x = explode('/', $this->routes['404_override']);
-
- $this->set_directory('');
- $this->set_class($x[0]);
- $this->set_method(isset($x[1]) ? $x[1] : 'index');
-
- return $x;
- }
- else
- {
- show_404($this->fetch_directory().$segments[0]);
- }
- }
- }
- else
- {
- // Is the method being specified in the route?
- if (strpos($this->default_controller, '/') !== FALSE)
- {
- $x = explode('/', $this->default_controller);
+ $this->set_class($class);
+ $this->set_method($method);
- $this->set_class($x[0]);
- $this->set_method($x[1]);
- }
- else
- {
- $this->set_class($this->default_controller);
- $this->set_method('index');
- }
-
- // Does the default controller exist in the sub-folder?
- if ( ! file_exists(APPPATH.'controllers/'.$this->fetch_directory().$this->default_controller.'.php'))
- {
- $this->directory = '';
- return array();
- }
+ // Assign routed segments, index starting from 1
+ $this->uri->rsegments = array(
+ 1 => $class,
+ 2 => $method
+ );
- }
+ log_message('debug', 'No URI present. Default controller set.');
+ }
- return $segments;
- }
+ // --------------------------------------------------------------------
+ /**
+ * Validate request
+ *
+ * Attempts validate the URI request and determine the controller path.
+ *
+ * @used-by CI_Router::_set_request()
+ * @param array $segments URI segments
+ * @return mixed URI segments
+ */
+ protected function _validate_request($segments)
+ {
+ $c = count($segments);
+ $directory_override = $this->directory !== '';
- // If we've gotten this far it means that the URI does not correlate to a valid
- // controller class. We will now see if there is an override
- if ( ! empty($this->routes['404_override']))
+ // Loop through our segments and return as soon as a controller
+ // is found or when such a directory doesn't exist
+ while ($c-- > 0)
{
- $x = explode('/', $this->routes['404_override']);
+ $test = $this->directory
+ .ucfirst($this->translate_uri_dashes === TRUE ? str_replace('-', '_', $segments[0]) : $segments[0]);
- $this->set_class($x[0]);
- $this->set_method(isset($x[1]) ? $x[1] : 'index');
+ if ( ! file_exists(APPPATH.'controllers/'.$test.'.php')
+ && $directory_override === FALSE
+ && is_dir(APPPATH.'controllers/'.$this->directory.$segments[0])
+ )
+ {
+ $this->set_directory(array_shift($segments), TRUE);
+ continue;
+ }
- return $x;
+ return $segments;
}
-
- // Nothing else to do at this point but show a 404
- show_404($segments[0]);
+ // This means that all segments were actually directories
+ return $segments;
}
// --------------------------------------------------------------------
/**
- * Parse Routes
+ * Parse Routes
*
- * This function matches any routes that may exist in
- * the config/routes.php file against the URI to
- * determine if the class/method need to be remapped.
+ * Matches any routes that may exist in the config/routes.php file
+ * against the URI to determine if the class/method need to be remapped.
*
- * @access private
* @return void
*/
- function _parse_routes()
+ protected function _parse_routes()
{
// Turn the segment array into a URI string
$uri = implode('/', $this->uri->segments);
- // Is there a literal match? If so we're done
- if (isset($this->routes[$uri]))
- {
- return $this->_set_request(explode('/', $this->routes[$uri]));
- }
+ // Get HTTP verb
+ $http_verb = isset($_SERVER['REQUEST_METHOD']) ? strtolower($_SERVER['REQUEST_METHOD']) : 'cli';
- // Loop through the route array looking for wild-cards
+ // Loop through the route array looking for wildcards
foreach ($this->routes as $key => $val)
{
- // Convert wild-cards to RegEx
- $key = str_replace(':any', '.+', str_replace(':num', '[0-9]+', $key));
+ // Check if route format is using HTTP verbs
+ if (is_array($val))
+ {
+ $val = array_change_key_case($val, CASE_LOWER);
+ if (isset($val[$http_verb]))
+ {
+ $val = $val[$http_verb];
+ }
+ else
+ {
+ continue;
+ }
+ }
+
+ // Convert wildcards to RegEx
+ $key = str_replace(array(':any', ':num'), array('[^/]+', '[0-9]+'), $key);
// Does the RegEx match?
- if (preg_match('#^'.$key.'$#', $uri))
+ if (preg_match('#^'.$key.'$#', $uri, $matches))
{
- // Do we have a back-reference?
- if (strpos($val, '$') !== FALSE AND strpos($key, '(') !== FALSE)
+ // Are we using callbacks to process back-references?
+ if ( ! is_string($val) && is_callable($val))
+ {
+ // Remove the original string from the matches array.
+ array_shift($matches);
+
+ // Execute the callback using the values in matches as its parameters.
+ $val = call_user_func_array($val, $matches);
+ }
+ // Are we using the default routing method for back-references?
+ elseif (strpos($val, '$') !== FALSE && strpos($key, '(') !== FALSE)
{
$val = preg_replace('#^'.$key.'$#', $val, $uri);
}
- return $this->_set_request(explode('/', $val));
+ $this->_set_request(explode('/', $val));
+ return;
}
}
// If we got this far it means we didn't encounter a
// matching route so we'll set the site default route
- $this->_set_request($this->uri->segments);
+ $this->_set_request(array_values($this->uri->segments));
}
// --------------------------------------------------------------------
/**
- * Set the class name
+ * Set class name
*
- * @access public
- * @param string
+ * @param string $class Class name
* @return void
*/
- function set_class($class)
+ public function set_class($class)
{
$this->class = str_replace(array('/', '.'), '', $class);
}
@@ -412,26 +443,12 @@ class CI_Router {
// --------------------------------------------------------------------
/**
- * Fetch the current class
+ * Set method name
*
- * @access public
- * @return string
- */
- function fetch_class()
- {
- return $this->class;
- }
-
- // --------------------------------------------------------------------
-
- /**
- * Set the method name
- *
- * @access public
- * @param string
+ * @param string $method Method name
* @return void
*/
- function set_method($method)
+ public function set_method($method)
{
$this->method = $method;
}
@@ -439,84 +456,21 @@ class CI_Router {
// --------------------------------------------------------------------
/**
- * Fetch the current method
+ * Set directory name
*
- * @access public
- * @return string
- */
- function fetch_method()
- {
- if ($this->method == $this->fetch_class())
- {
- return 'index';
- }
-
- return $this->method;
- }
-
- // --------------------------------------------------------------------
-
- /**
- * Set the directory name
- *
- * @access public
- * @param string
+ * @param string $dir Directory name
+ * @param bool $append Whether we're appending rather than setting the full value
* @return void
*/
- function set_directory($dir)
+ public function set_directory($dir, $append = FALSE)
{
- $this->directory = str_replace(array('/', '.'), '', $dir).'/';
- }
-
- // --------------------------------------------------------------------
-
- /**
- * Fetch the sub-directory (if any) that contains the requested controller class
- *
- * @access public
- * @return string
- */
- function fetch_directory()
- {
- return $this->directory;
- }
-
- // --------------------------------------------------------------------
-
- /**
- * Set the controller overrides
- *
- * @access public
- * @param array
- * @return null
- */
- function _set_overrides($routing)
- {
- if ( ! is_array($routing))
- {
- return;
- }
-
- if (isset($routing['directory']))
- {
- $this->set_directory($routing['directory']);
- }
-
- if (isset($routing['controller']) AND $routing['controller'] != '')
+ if ($append !== TRUE OR empty($this->directory))
{
- $this->set_class($routing['controller']);
+ $this->directory = str_replace('.', '', trim($dir, '/')).'/';
}
-
- if (isset($routing['function']))
+ else
{
- $routing['function'] = ($routing['function'] == '') ? 'index' : $routing['function'];
- $this->set_method($routing['function']);
+ $this->directory .= str_replace('.', '', trim($dir, '/')).'/';
}
}
-
-
}
-// END Router Class
-
-/* End of file Router.php */
-/* Location: ./system/core/Router.php */ \ No newline at end of file
diff --git a/system/core/Security.php b/system/core/Security.php
index efa2df922..d3c8b976e 100644
--- a/system/core/Security.php
+++ b/system/core/Security.php
@@ -1,19 +1,42 @@
-<?php if ( ! defined('BASEPATH')) exit('No direct script access allowed');
+<?php
/**
* CodeIgniter
*
- * An open source application development framework for PHP 5.1.6 or newer
+ * An open source application development framework for PHP
*
- * @package CodeIgniter
- * @author ExpressionEngine Dev Team
- * @copyright Copyright (c) 2008 - 2014, EllisLab, Inc.
- * @license http://codeigniter.com/user_guide/license.html
- * @link http://codeigniter.com
- * @since Version 1.0
+ * This content is released under the MIT License (MIT)
+ *
+ * Copyright (c) 2019 - 2022, CodeIgniter Foundation
+ *
+ * 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.
+ *
+ * @package CodeIgniter
+ * @author EllisLab Dev Team
+ * @copyright Copyright (c) 2008 - 2014, EllisLab, Inc. (https://ellislab.com/)
+ * @copyright Copyright (c) 2014 - 2019, British Columbia Institute of Technology (https://bcit.ca/)
+ * @copyright Copyright (c) 2019 - 2022, CodeIgniter Foundation (https://codeigniter.com/)
+ * @license https://opensource.org/licenses/MIT MIT License
+ * @link https://codeigniter.com
+ * @since Version 1.0.0
* @filesource
*/
-
-// ------------------------------------------------------------------------
+defined('BASEPATH') OR exit('No direct script access allowed');
/**
* Security Class
@@ -21,119 +44,168 @@
* @package CodeIgniter
* @subpackage Libraries
* @category Security
- * @author ExpressionEngine Dev Team
- * @link http://codeigniter.com/user_guide/libraries/security.html
+ * @author EllisLab Dev Team
+ * @link https://codeigniter.com/userguide3/libraries/security.html
*/
class CI_Security {
/**
- * Random Hash for protecting URLs
+ * List of sanitize filename strings
+ *
+ * @var array
+ */
+ public $filename_bad_chars = array(
+ '../', '<!--', '-->', '<', '>',
+ "'", '"', '&', '$', '#',
+ '{', '}', '[', ']', '=',
+ ';', '?', '%20', '%22',
+ '%3c', // <
+ '%253c', // <
+ '%3e', // >
+ '%0e', // >
+ '%28', // (
+ '%29', // )
+ '%2528', // (
+ '%26', // &
+ '%24', // $
+ '%3f', // ?
+ '%3b', // ;
+ '%3d' // =
+ );
+
+ /**
+ * Character set
+ *
+ * Will be overridden by the constructor.
+ *
+ * @var string
+ */
+ public $charset = 'UTF-8';
+
+ /**
+ * XSS Hash
*
- * @var string
- * @access protected
+ * Random Hash for protecting URLs.
+ *
+ * @var string
*/
- protected $_xss_hash = '';
+ protected $_xss_hash;
+
/**
- * Random Hash for Cross Site Request Forgery Protection Cookie
+ * CSRF Hash
*
- * @var string
- * @access protected
+ * Random hash for Cross Site Request Forgery protection cookie
+ *
+ * @var string
*/
- protected $_csrf_hash = '';
+ protected $_csrf_hash;
+
/**
- * Expiration time for Cross Site Request Forgery Protection Cookie
- * Defaults to two hours (in seconds)
+ * CSRF Expire time
*
- * @var int
- * @access protected
+ * Expiration time for Cross Site Request Forgery protection cookie.
+ * Defaults to two hours (in seconds).
+ *
+ * @var int
*/
- protected $_csrf_expire = 7200;
+ protected $_csrf_expire = 7200;
+
/**
- * Token name for Cross Site Request Forgery Protection Cookie
+ * CSRF Token name
*
- * @var string
- * @access protected
+ * Token name for Cross Site Request Forgery protection cookie.
+ *
+ * @var string
*/
- protected $_csrf_token_name = 'ci_csrf_token';
+ protected $_csrf_token_name = 'ci_csrf_token';
+
/**
- * Cookie name for Cross Site Request Forgery Protection Cookie
+ * CSRF Cookie name
*
- * @var string
- * @access protected
+ * Cookie name for Cross Site Request Forgery protection cookie.
+ *
+ * @var string
*/
- protected $_csrf_cookie_name = 'ci_csrf_token';
+ protected $_csrf_cookie_name = 'ci_csrf_token';
+
/**
* List of never allowed strings
*
- * @var array
- * @access protected
+ * @var array
*/
- protected $_never_allowed_str = array(
- 'document.cookie' => '[removed]',
- 'document.write' => '[removed]',
- '.parentNode' => '[removed]',
- '.innerHTML' => '[removed]',
- 'window.location' => '[removed]',
- '-moz-binding' => '[removed]',
- '<!--' => '&lt;!--',
- '-->' => '--&gt;',
- '<![CDATA[' => '&lt;![CDATA[',
- '<comment>' => '&lt;comment&gt;'
+ protected $_never_allowed_str = array(
+ 'document.cookie' => '[removed]',
+ '(document).cookie' => '[removed]',
+ 'document.write' => '[removed]',
+ '(document).write' => '[removed]',
+ '.parentNode' => '[removed]',
+ '.innerHTML' => '[removed]',
+ '-moz-binding' => '[removed]',
+ '<!--' => '&lt;!--',
+ '-->' => '--&gt;',
+ '<![CDATA[' => '&lt;![CDATA[',
+ '<comment>' => '&lt;comment&gt;',
+ '<%' => '&lt;&#37;'
);
- /* never allowed, regex replacement */
/**
- * List of never allowed regex replacement
+ * List of never allowed regex replacements
*
- * @var array
- * @access protected
+ * @var array
*/
protected $_never_allowed_regex = array(
'javascript\s*:',
+ '(\(?document\)?|\(?window\)?(\.document)?)\.(location|on\w*)',
'expression\s*(\(|&\#40;)', // CSS and IE
'vbscript\s*:', // IE, surprise!
- 'Redirect\s+302',
+ 'wscript\s*:', // IE
+ 'jscript\s*:', // IE
+ 'vbs\s*:', // IE
+ 'Redirect\s+30\d',
"([\"'])?data\s*:[^\\1]*?base64[^\\1]*?,[^\\1]*?\\1?"
);
/**
- * Constructor
+ * Class constructor
*
* @return void
*/
- public function __construct()
+ public function __construct($charset)
{
+ $this->charset = $charset;
+
// Is CSRF protection enabled?
- if (config_item('csrf_protection') === TRUE)
+ if (config_item('csrf_protection') && ! is_cli())
{
// CSRF config
foreach (array('csrf_expire', 'csrf_token_name', 'csrf_cookie_name') as $key)
{
- if (FALSE !== ($val = config_item($key)))
+ if (NULL !== ($val = config_item($key)))
{
$this->{'_'.$key} = $val;
}
}
// Append application specific cookie prefix
- if (config_item('cookie_prefix'))
+ if ($cookie_prefix = config_item('cookie_prefix'))
{
- $this->_csrf_cookie_name = config_item('cookie_prefix').$this->_csrf_cookie_name;
+ $this->_csrf_cookie_name = $cookie_prefix.$this->_csrf_cookie_name;
}
// Set the CSRF hash
$this->_csrf_set_hash();
+ $this->csrf_verify();
}
- log_message('debug', "Security Class Initialized");
+ log_message('info', 'Security Class Initialized');
}
// --------------------------------------------------------------------
/**
- * Verify Cross Site Request Forgery Protection
+ * CSRF Verify
*
- * @return object
+ * @return CI_Security
*/
public function csrf_verify()
{
@@ -143,52 +215,95 @@ class CI_Security {
return $this->csrf_set_cookie();
}
- // Do the tokens exist in both the _POST and _COOKIE arrays?
- if ( ! isset($_POST[$this->_csrf_token_name], $_COOKIE[$this->_csrf_cookie_name]))
+ // Check if URI has been whitelisted from CSRF checks
+ if ($exclude_uris = config_item('csrf_exclude_uris'))
{
- $this->csrf_show_error();
+ $uri = load_class('URI', 'core');
+ foreach ($exclude_uris as $excluded)
+ {
+ if (preg_match('#^'.$excluded.'$#i'.(UTF8_ENABLED ? 'u' : ''), $uri->uri_string()))
+ {
+ return $this;
+ }
+ }
}
- // Do the tokens match?
- if ($_POST[$this->_csrf_token_name] != $_COOKIE[$this->_csrf_cookie_name])
- {
- $this->csrf_show_error();
- }
+ // Check CSRF token validity, but don't error on mismatch just yet - we'll want to regenerate
+ $valid = isset($_POST[$this->_csrf_token_name], $_COOKIE[$this->_csrf_cookie_name])
+ && is_string($_POST[$this->_csrf_token_name]) && is_string($_COOKIE[$this->_csrf_cookie_name])
+ && hash_equals($_POST[$this->_csrf_token_name], $_COOKIE[$this->_csrf_cookie_name]);
- // We kill this since we're done and we don't want to
- // polute the _POST array
+ // We kill this since we're done and we don't want to pollute the _POST array
unset($_POST[$this->_csrf_token_name]);
- // Nothing should last forever
- unset($_COOKIE[$this->_csrf_cookie_name]);
+ // Regenerate on every submission?
+ if (config_item('csrf_regenerate'))
+ {
+ // Nothing should last forever
+ unset($_COOKIE[$this->_csrf_cookie_name]);
+ $this->_csrf_hash = NULL;
+ }
+
$this->_csrf_set_hash();
$this->csrf_set_cookie();
- log_message('debug', 'CSRF token verified');
+ if ($valid !== TRUE)
+ {
+ $this->csrf_show_error();
+ }
+ log_message('info', 'CSRF token verified');
return $this;
}
// --------------------------------------------------------------------
/**
- * Set Cross Site Request Forgery Protection Cookie
+ * CSRF Set Cookie
*
- * @return object
+ * @codeCoverageIgnore
+ * @return CI_Security
*/
public function csrf_set_cookie()
{
$expire = time() + $this->_csrf_expire;
- $secure_cookie = (config_item('cookie_secure') === TRUE) ? 1 : 0;
+ $secure_cookie = (bool) config_item('cookie_secure');
- if ($secure_cookie && (empty($_SERVER['HTTPS']) OR strtolower($_SERVER['HTTPS']) === 'off'))
+ if ($secure_cookie && ! is_https())
{
return FALSE;
}
- setcookie($this->_csrf_cookie_name, $this->_csrf_hash, $expire, config_item('cookie_path'), config_item('cookie_domain'), $secure_cookie);
+ if (is_php('7.3'))
+ {
+ setcookie(
+ $this->_csrf_cookie_name,
+ $this->_csrf_hash,
+ array(
+ 'expires' => $expire,
+ 'path' => config_item('cookie_path'),
+ 'domain' => config_item('cookie_domain'),
+ 'secure' => $secure_cookie,
+ 'httponly' => config_item('cookie_httponly'),
+ 'samesite' => 'Strict'
+ )
+ );
+ }
+ else
+ {
+ $domain = trim(config_item('cookie_domain'));
+ header('Set-Cookie: '.$this->_csrf_cookie_name.'='.$this->_csrf_hash
+ .'; Expires='.gmdate('D, d-M-Y H:i:s T', $expire)
+ .'; Max-Age='.$this->_csrf_expire
+ .'; Path='.rawurlencode(config_item('cookie_path'))
+ .($domain === '' ? '' : '; Domain='.$domain)
+ .($secure_cookie ? '; Secure' : '')
+ .(config_item('cookie_httponly') ? '; HttpOnly' : '')
+ .'; SameSite=Strict'
+ );
+ }
- log_message('debug', "CRSF cookie Set");
+ log_message('info', 'CSRF cookie sent');
return $this;
}
@@ -202,7 +317,7 @@ class CI_Security {
*/
public function csrf_show_error()
{
- show_error('The action you have requested is not allowed.');
+ show_error('The action you have requested is not allowed.', 403);
}
// --------------------------------------------------------------------
@@ -210,9 +325,8 @@ class CI_Security {
/**
* Get CSRF Hash
*
- * Getter Method
- *
- * @return string self::_csrf_hash
+ * @see CI_Security::$_csrf_hash
+ * @return string CSRF hash
*/
public function get_csrf_hash()
{
@@ -224,9 +338,8 @@ class CI_Security {
/**
* Get CSRF Token Name
*
- * Getter Method
- *
- * @return string self::csrf_token_name
+ * @see CI_Security::$_csrf_token_name
+ * @return string CSRF token name
*/
public function get_csrf_token_name()
{
@@ -239,52 +352,44 @@ class CI_Security {
* XSS Clean
*
* Sanitizes data so that Cross Site Scripting Hacks can be
- * prevented. This function does a fair amount of work but
+ * prevented. This method does a fair amount of work but
* it is extremely thorough, designed to prevent even the
* most obscure XSS attempts. Nothing is ever 100% foolproof,
* of course, but I haven't been able to get anything passed
* the filter.
*
- * Note: This function should only be used to deal with data
- * upon submission. It's not something that should
- * be used for general runtime processing.
+ * Note: Should only be used to deal with data upon submission.
+ * It's not something that should be used for general
+ * runtime processing.
*
- * This function was based in part on some code and ideas I
- * got from Bitflux: http://channel.bitflux.ch/wiki/XSS_Prevention
+ * @link http://channel.bitflux.ch/wiki/XSS_Prevention
+ * Based in part on some code and ideas from Bitflux.
*
- * To help develop this script I used this great list of
- * vulnerabilities along with a few other hacks I've
- * harvested from examining vulnerabilities in other programs:
- * http://ha.ckers.org/xss.html
+ * @link http://ha.ckers.org/xss.html
+ * To help develop this script I used this great list of
+ * vulnerabilities along with a few other hacks I've
+ * harvested from examining vulnerabilities in other programs.
*
- * @param mixed string or array
- * @param bool
+ * @param string|string[] $str Input data
+ * @param bool $is_image Whether the input is an image
* @return string
*/
public function xss_clean($str, $is_image = FALSE)
{
- /*
- * Is the string an array?
- *
- */
+ // Is the string an array?
if (is_array($str))
{
- while (list($key) = each($str))
+ foreach ($str as $key => &$value)
{
- $str[$key] = $this->xss_clean($str[$key]);
+ $str[$key] = $this->xss_clean($value);
}
return $str;
}
- /*
- * Remove Invisible Characters
- */
+ // Remove Invisible Characters
$str = remove_invisible_characters($str);
- // Validate Entities in URLs
- $str = $this->_validate_entities($str);
-
/*
* URL Decode
*
@@ -293,9 +398,18 @@ class CI_Security {
* <a href="http://%77%77%77%2E%67%6F%6F%67%6C%65%2E%63%6F%6D">Google</a>
*
* Note: Use rawurldecode() so it does not remove plus signs
- *
*/
- $str = rawurldecode($str);
+ if (stripos($str, '%') !== false)
+ {
+ do
+ {
+ $oldstr = $str;
+ $str = rawurldecode($str);
+ $str = preg_replace_callback('#%(?:\s*[0-9a-f]){2,}#i', array($this, '_urldecodespaces'), $str);
+ }
+ while ($oldstr !== $str);
+ unset($oldstr);
+ }
/*
* Convert character entities to ASCII
@@ -303,16 +417,11 @@ class CI_Security {
* This permits our tests below to work reliably.
* We only convert entities that are within tags since
* these are the ones that will pose security problems.
- *
*/
+ $str = preg_replace_callback("/[^a-z0-9>]+[a-z0-9]+=([\'\"]).*?\\1/si", array($this, '_convert_attribute'), $str);
+ $str = preg_replace_callback('/<\w+.*/si', array($this, '_decode_entity'), $str);
- $str = preg_replace_callback("/[a-z]+=([\'\"]).*?\\1/si", array($this, '_convert_attribute'), $str);
-
- $str = preg_replace_callback("/<\w+.*?(?=>|<|$)/si", array($this, '_decode_entity'), $str);
-
- /*
- * Remove Invisible Characters Again!
- */
+ // Remove Invisible Characters Again!
$str = remove_invisible_characters($str);
/*
@@ -323,15 +432,9 @@ class CI_Security {
* NOTE: preg_replace was found to be amazingly slow here on
* large blocks of data, so we use str_replace.
*/
+ $str = str_replace("\t", ' ', $str);
- if (strpos($str, "\t") !== FALSE)
- {
- $str = str_replace("\t", ' ', $str);
- }
-
- /*
- * Capture converted string for later comparison
- */
+ // Capture converted string for later comparison
$converted_string = $str;
// Remove Strings that are never allowed
@@ -351,11 +454,11 @@ class CI_Security {
// Images have a tendency to have the PHP short opening and
// closing tags every so often so we skip those and only
// do the long opening tags.
- $str = preg_replace('/<\?(php)/i', "&lt;?\\1", $str);
+ $str = preg_replace('/<\?(php)/i', '&lt;?\\1', $str);
}
else
{
- $str = str_replace(array('<?', '?'.'>'), array('&lt;?', '?&gt;'), $str);
+ $str = str_replace(array('<?', '?'.'>'), array('&lt;?', '?&gt;'), $str);
}
/*
@@ -365,56 +468,54 @@ class CI_Security {
* These words are compacted back to their correct state.
*/
$words = array(
- 'javascript', 'expression', 'vbscript', 'script', 'base64',
- 'applet', 'alert', 'document', 'write', 'cookie', 'window'
+ 'javascript', 'expression', 'vbscript', 'jscript', 'wscript',
+ 'vbs', 'script', 'base64', 'applet', 'alert', 'document',
+ 'write', 'cookie', 'window', 'confirm', 'prompt', 'eval'
);
foreach ($words as $word)
{
- $temp = '';
-
- for ($i = 0, $wordlen = strlen($word); $i < $wordlen; $i++)
- {
- $temp .= substr($word, $i, 1)."\s*";
- }
+ $word = implode('\s*', str_split($word)).'\s*';
// We only want to do this when it is followed by a non-word character
// That way valid stuff like "dealer to" does not become "dealerto"
- $str = preg_replace_callback('#('.substr($temp, 0, -3).')(\W)#is', array($this, '_compact_exploded_words'), $str);
+ $str = preg_replace_callback('#('.substr($word, 0, -3).')(\W)#is', array($this, '_compact_exploded_words'), $str);
}
/*
* Remove disallowed Javascript in links or img tags
- * We used to do some version comparisons and use of stripos for PHP5,
+ * We used to do some version comparisons and use of stripos(),
* but it is dog slow compared to these simplified non-capturing
* preg_match(), especially if the pattern exists in the string
+ *
+ * Note: It was reported that not only space characters, but all in
+ * the following pattern can be parsed as separators between a tag name
+ * and its attributes: [\d\s"\'`;,\/\=\(\x00\x0B\x09\x0C]
+ * ... however, remove_invisible_characters() above already strips the
+ * hex-encoded ones, so we'll skip them below.
*/
do
{
$original = $str;
- if (preg_match("/<a/i", $str))
+ if (preg_match('/<a/i', $str))
{
- $str = preg_replace_callback("#<a\s+([^>]*?)(>|$)#si", array($this, '_js_link_removal'), $str);
+ $str = preg_replace_callback('#<a(?:rea)?[^a-z0-9>]+([^>]*?)(?:>|$)#si', array($this, '_js_link_removal'), $str);
}
- if (preg_match("/<img/i", $str))
+ if (preg_match('/<img/i', $str))
{
- $str = preg_replace_callback("#<img\s+([^>]*?)(\s?/?>|$)#si", array($this, '_js_img_removal'), $str);
+ $str = preg_replace_callback('#<img[^a-z0-9]+([^>]*?)(?:\s?/?>|$)#si', array($this, '_js_img_removal'), $str);
}
- if (preg_match("/script/i", $str) OR preg_match("/xss/i", $str))
+ if (preg_match('/script|xss/i', $str))
{
- $str = preg_replace("#<(/*)(script|xss)(.*?)\>#si", '[removed]', $str);
+ $str = preg_replace('#</*(?:script|xss).*?>#si', '[removed]', $str);
}
}
- while($original != $str);
-
+ while ($original !== $str);
unset($original);
- // Remove evil attributes such as style, onclick and xmlns
- $str = $this->_remove_evil_attributes($str, $is_image);
-
/*
* Sanitize naughty HTML elements
*
@@ -424,23 +525,55 @@ class CI_Security {
* So this: <blink>
* Becomes: &lt;blink&gt;
*/
- $naughty = 'alert|applet|audio|basefont|base|behavior|bgsound|blink|body|embed|expression|form|frameset|frame|head|html|ilayer|iframe|input|isindex|layer|link|meta|object|plaintext|style|script|textarea|title|video|xml|xss';
- $str = preg_replace_callback('#<(/*\s*)('.$naughty.')([^><]*)([><]*)#is', array($this, '_sanitize_naughty_html'), $str);
+ $pattern = '#'
+ .'<((?<slash>/*\s*)((?<tagName>[a-z0-9]+)(?=[^a-z0-9]|$)|.+)' // tag start and name, followed by a non-tag character
+ .'[^\s\042\047a-z0-9>/=]*' // a valid attribute character immediately after the tag would count as a separator
+ // optional attributes
+ .'(?<attributes>(?:[\s\042\047/=]*' // non-attribute characters, excluding > (tag close) for obvious reasons
+ .'[^\s\042\047>/=]+' // attribute characters
+ // optional attribute-value
+ .'(?:\s*=' // attribute-value separator
+ .'(?:[^\s\042\047=><`]+|\s*\042[^\042]*\042|\s*\047[^\047]*\047|\s*(?U:[^\s\042\047=><`]*))' // single, double or non-quoted value
+ .')?' // end optional attribute-value group
+ .')*)' // end optional attributes group
+ .'[^>]*)(?<closeTag>\>)?#isS';
+
+ // Note: It would be nice to optimize this for speed, BUT
+ // only matching the naughty elements here results in
+ // false positives and in turn - vulnerabilities!
+ do
+ {
+ $old_str = $str;
+ $str = preg_replace_callback($pattern, array($this, '_sanitize_naughty_html'), $str);
+ }
+ while ($old_str !== $str);
+ unset($old_str);
/*
* Sanitize naughty scripting elements
*
* Similar to above, only instead of looking for
* tags it looks for PHP and JavaScript commands
- * that are disallowed. Rather than removing the
+ * that are disallowed. Rather than removing the
* code, it simply converts the parenthesis to entities
* rendering the code un-executable.
*
* For example: eval('some code')
- * Becomes: eval&#40;'some code'&#41;
+ * Becomes: eval&#40;'some code'&#41;
*/
- $str = preg_replace('#(alert|cmd|passthru|eval|exec|expression|system|fopen|fsockopen|file|file_get_contents|readfile|unlink)(\s*)\((.*?)\)#si', "\\1\\2&#40;\\3&#41;", $str);
+ $str = preg_replace(
+ '#(alert|prompt|confirm|cmd|passthru|eval|exec|expression|system|fopen|fsockopen|file|file_get_contents|readfile|unlink)(\s*)\((.*?)\)#si',
+ '\\1\\2&#40;\\3&#41;',
+ $str
+ );
+ // Same thing, but for "tag functions" (e.g. eval`some code`)
+ // See https://github.com/bcit-ci/CodeIgniter/issues/5420
+ $str = preg_replace(
+ '#(alert|prompt|confirm|cmd|passthru|eval|exec|expression|system|fopen|fsockopen|file|file_get_contents|readfile|unlink)(\s*)`(.*?)`#si',
+ '\\1\\2&#96;\\3&#96;',
+ $str
+ );
// Final clean up
// This adds a bit of extra precaution in case
@@ -456,29 +589,32 @@ class CI_Security {
* string post-removal of XSS, then it fails, as there was unwanted XSS
* code found and removed/changed during processing.
*/
-
if ($is_image === TRUE)
{
- return ($str == $converted_string) ? TRUE: FALSE;
+ return ($str === $converted_string);
}
- log_message('debug', "XSS Filtering completed");
return $str;
}
// --------------------------------------------------------------------
/**
- * Random Hash for protecting URLs
+ * XSS Hash
*
- * @return string
+ * Generates the XSS hash if needed and returns it.
+ *
+ * @see CI_Security::$_xss_hash
+ * @return string XSS hash
*/
public function xss_hash()
{
- if ($this->_xss_hash == '')
+ if ($this->_xss_hash === NULL)
{
- mt_srand();
- $this->_xss_hash = md5(time() + mt_rand(0, 1999999999));
+ $rand = $this->get_random_bytes(16);
+ $this->_xss_hash = ($rand === FALSE)
+ ? md5(uniqid(mt_rand(), TRUE))
+ : bin2hex($rand);
}
return $this->_xss_hash;
@@ -487,76 +623,134 @@ class CI_Security {
// --------------------------------------------------------------------
/**
+ * Get random bytes
+ *
+ * @param int $length Output length
+ * @return string
+ */
+ public function get_random_bytes($length)
+ {
+ if (empty($length) OR ! ctype_digit((string) $length))
+ {
+ return FALSE;
+ }
+
+ if (function_exists('random_bytes'))
+ {
+ try
+ {
+ // The cast is required to avoid TypeError
+ return random_bytes((int) $length);
+ }
+ catch (Exception $e)
+ {
+ // If random_bytes() can't do the job, we can't either ...
+ // There's no point in using fallbacks.
+ log_message('error', $e->getMessage());
+ return FALSE;
+ }
+ }
+
+ // Unfortunately, none of the following PRNGs is guaranteed to exist ...
+ if (defined('MCRYPT_DEV_URANDOM') && ($output = mcrypt_create_iv($length, MCRYPT_DEV_URANDOM)) !== FALSE)
+ {
+ return $output;
+ }
+
+ if (is_readable('/dev/urandom') && ($fp = fopen('/dev/urandom', 'rb')) !== FALSE)
+ {
+ // Try not to waste entropy ...
+ stream_set_chunk_size($fp, $length);
+ $output = fread($fp, $length);
+ fclose($fp);
+ if ($output !== FALSE)
+ {
+ return $output;
+ }
+ }
+
+ if (function_exists('openssl_random_pseudo_bytes'))
+ {
+ return openssl_random_pseudo_bytes($length);
+ }
+
+ return FALSE;
+ }
+
+ // --------------------------------------------------------------------
+
+ /**
* HTML Entities Decode
*
- * This function is a replacement for html_entity_decode()
+ * A replacement for html_entity_decode()
*
* The reason we are not using html_entity_decode() by itself is because
* while it is not technically correct to leave out the semicolon
* at the end of an entity most browsers will still interpret the entity
- * correctly. html_entity_decode() does not convert entities without
+ * correctly. html_entity_decode() does not convert entities without
* semicolons, so we are left with our own little solution here. Bummer.
*
- * @param string
- * @param string
+ * @link https://secure.php.net/html-entity-decode
+ *
+ * @param string $str Input
+ * @param string $charset Character set
* @return string
*/
- public function entity_decode($str, $charset='UTF-8')
+ public function entity_decode($str, $charset = NULL)
{
- if (stristr($str, '&') === FALSE)
+ if (strpos($str, '&') === FALSE)
{
return $str;
}
- $str = html_entity_decode($str, ENT_COMPAT, $charset);
- $str = preg_replace('~&#x(0*[0-9a-f]{2,5})~ei', 'chr(hexdec("\\1"))', $str);
- return preg_replace('~&#([0-9]{2,4})~e', 'chr(\\1)', $str);
+ static $_entities;
+
+ isset($charset) OR $charset = $this->charset;
+ isset($_entities) OR $_entities = array_map('strtolower', get_html_translation_table(HTML_ENTITIES, ENT_COMPAT | ENT_HTML5, $charset));
+
+ do
+ {
+ $str_compare = $str;
+
+ // Decode standard entities, avoiding false positives
+ if (preg_match_all('/&[a-z]{2,}(?![a-z;])/i', $str, $matches))
+ {
+ $replace = array();
+ $matches = array_unique(array_map('strtolower', $matches[0]));
+ foreach ($matches as &$match)
+ {
+ if (($char = array_search($match.';', $_entities, TRUE)) !== FALSE)
+ {
+ $replace[$match] = $char;
+ }
+ }
+
+ $str = str_replace(array_keys($replace), array_values($replace), $str);
+ }
+
+ // Decode numeric & UTF16 two byte entities
+ $str = html_entity_decode(
+ preg_replace('/(&#(?:x0*[0-9a-f]{2,5}(?![0-9a-f;])|(?:0*\d{2,4}(?![0-9;]))))/iS', '$1;', $str),
+ ENT_COMPAT | ENT_HTML5,
+ $charset
+ );
+ }
+ while ($str_compare !== $str);
+ return $str;
}
// --------------------------------------------------------------------
/**
- * Filename Security
+ * Sanitize Filename
*
- * @param string
- * @param bool
+ * @param string $str Input file name
+ * @param bool $relative_path Whether to preserve paths
* @return string
*/
public function sanitize_filename($str, $relative_path = FALSE)
{
- $bad = array(
- "../",
- "<!--",
- "-->",
- "<",
- ">",
- "'",
- '"',
- '&',
- '$',
- '#',
- '{',
- '}',
- '[',
- ']',
- '=',
- ';',
- '?',
- "%20",
- "%22",
- "%3c", // <
- "%253c", // <
- "%3e", // >
- "%0e", // >
- "%28", // (
- "%29", // )
- "%2528", // (
- "%26", // &
- "%24", // $
- "%3f", // ?
- "%3b", // ;
- "%3d" // =
- );
+ $bad = $this->filename_bad_chars;
if ( ! $relative_path)
{
@@ -565,7 +759,53 @@ class CI_Security {
}
$str = remove_invisible_characters($str, FALSE);
- return stripslashes(str_replace($bad, '', $str));
+
+ do
+ {
+ $old = $str;
+ $str = str_replace($bad, '', $str);
+ }
+ while ($old !== $str);
+
+ return stripslashes($str);
+ }
+
+ // ----------------------------------------------------------------
+
+ /**
+ * Strip Image Tags
+ *
+ * @param string $str
+ * @return string
+ */
+ public function strip_image_tags($str)
+ {
+ return preg_replace(
+ array(
+ '#<img[\s/]+.*?src\s*=\s*(["\'])([^\\1]+?)\\1.*?\>#i',
+ '#<img[\s/]+.*?src\s*=\s*?(([^\s"\'=<>`]+)).*?\>#i'
+ ),
+ '\\2',
+ $str
+ );
+ }
+
+ // ----------------------------------------------------------------
+
+ /**
+ * URL-decode taking spaces into account
+ *
+ * @see https://github.com/bcit-ci/CodeIgniter/issues/4877
+ * @param array $matches
+ * @return string
+ */
+ protected function _urldecodespaces($matches)
+ {
+ $input = $matches[0];
+ $nospaces = preg_replace('#\s+#', '', $input);
+ return ($nospaces === $input)
+ ? $input
+ : rawurldecode($nospaces);
}
// ----------------------------------------------------------------
@@ -573,11 +813,12 @@ class CI_Security {
/**
* Compact Exploded Words
*
- * Callback function for xss_clean() to remove whitespace from
- * things like j a v a s c r i p t
+ * Callback method for xss_clean() to remove whitespace from
+ * things like 'j a v a s c r i p t'.
*
- * @param type
- * @return type
+ * @used-by CI_Security::xss_clean()
+ * @param array $matches
+ * @return string
*/
protected function _compact_exploded_words($matches)
{
@@ -586,86 +827,93 @@ class CI_Security {
// --------------------------------------------------------------------
- /*
- * Remove Evil HTML Attributes (like evenhandlers and style)
+ /**
+ * Sanitize Naughty HTML
*
- * It removes the evil attribute and either:
- * - Everything up until a space
- * For example, everything between the pipes:
- * <a |style=document.write('hello');alert('world');| class=link>
- * - Everything inside the quotes
- * For example, everything between the pipes:
- * <a |style="document.write('hello'); alert('world');"| class="link">
+ * Callback method for xss_clean() to remove naughty HTML elements.
*
- * @param string $str The string to check
- * @param boolean $is_image TRUE if this is an image
- * @return string The string with the evil attributes removed
+ * @used-by CI_Security::xss_clean()
+ * @param array $matches
+ * @return string
*/
- protected function _remove_evil_attributes($str, $is_image)
+ protected function _sanitize_naughty_html($matches)
{
- // All javascript event handlers (e.g. onload, onclick, onmouseover), style, and xmlns
- $evil_attributes = array('on\w*', 'style', 'xmlns', 'formaction');
+ static $naughty_tags = array(
+ 'alert', 'area', 'prompt', 'confirm', 'applet', 'audio', 'basefont', 'base', 'behavior', 'bgsound',
+ 'blink', 'body', 'embed', 'expression', 'form', 'frameset', 'frame', 'head', 'html', 'ilayer',
+ 'iframe', 'input', 'button', 'select', 'isindex', 'layer', 'link', 'meta', 'keygen', 'object',
+ 'plaintext', 'style', 'script', 'textarea', 'title', 'math', 'video', 'svg', 'xml', 'xss'
+ );
- if ($is_image === TRUE)
+ static $evil_attributes = array(
+ 'on\w+', 'style', 'xmlns', 'formaction', 'form', 'xlink:href', 'FSCommand', 'seekSegmentTime'
+ );
+
+ // First, escape unclosed tags
+ if (empty($matches['closeTag']))
+ {
+ return '&lt;'.$matches[1];
+ }
+ // Is the element that we caught naughty? If so, escape it
+ elseif (in_array(strtolower($matches['tagName']), $naughty_tags, TRUE))
{
- /*
- * Adobe Photoshop puts XML metadata into JFIF images,
- * including namespacing, so we have to allow this for images.
- */
- unset($evil_attributes[array_search('xmlns', $evil_attributes)]);
+ return '&lt;'.$matches[1].'&gt;';
}
+ // For other tags, see if their attributes are "evil" and strip those
+ elseif (isset($matches['attributes']))
+ {
+ // We'll store the already filtered attributes here
+ $attributes = array();
- do {
- $count = 0;
- $attribs = array();
+ // Attribute-catching pattern
+ $attributes_pattern = '#'
+ .'(?<name>[^\s\042\047>/=]+)' // attribute characters
+ // optional attribute-value
+ .'(?:\s*=(?<value>[^\s\042\047=><`]+|\s*\042[^\042]*\042|\s*\047[^\047]*\047|\s*(?U:[^\s\042\047=><`]*)))' // attribute-value separator
+ .'#i';
- // find occurrences of illegal attribute strings with quotes (042 and 047 are octal quotes)
- preg_match_all('/('.implode('|', $evil_attributes).')\s*=\s*(\042|\047)([^\\2]*?)(\\2)/is', $str, $matches, PREG_SET_ORDER);
+ // Blacklist pattern for evil attribute names
+ $is_evil_pattern = '#^('.implode('|', $evil_attributes).')$#i';
- foreach ($matches as $attr)
+ // Each iteration filters a single attribute
+ do
{
- $attribs[] = preg_quote($attr[0], '/');
- }
+ // Strip any non-alpha characters that may precede an attribute.
+ // Browsers often parse these incorrectly and that has been a
+ // of numerous XSS issues we've had.
+ $matches['attributes'] = preg_replace('#^[^a-z]+#i', '', $matches['attributes']);
- // find occurrences of illegal attribute strings without quotes
- preg_match_all('/('.implode('|', $evil_attributes).')\s*=\s*([^\s>]*)/is', $str, $matches, PREG_SET_ORDER);
+ if ( ! preg_match($attributes_pattern, $matches['attributes'], $attribute, PREG_OFFSET_CAPTURE))
+ {
+ // No (valid) attribute found? Discard everything else inside the tag
+ break;
+ }
- foreach ($matches as $attr)
- {
- $attribs[] = preg_quote($attr[0], '/');
- }
+ if (
+ // Is it indeed an "evil" attribute?
+ preg_match($is_evil_pattern, $attribute['name'][0])
+ // Or does it have an equals sign, but no value and not quoted? Strip that too!
+ OR (trim($attribute['value'][0]) === '')
+ )
+ {
+ $attributes[] = 'xss=removed';
+ }
+ else
+ {
+ $attributes[] = $attribute[0][0];
+ }
- // replace illegal attribute strings that are inside an html tag
- if (count($attribs) > 0)
- {
- $str = preg_replace('/(<?)(\/?[^><]+?)([^A-Za-z<>\-])(.*?)('.implode('|', $attribs).')(.*?)([\s><]?)([><]*)/i', '$1$2 $4$6$7$8', $str, -1, $count);
+ $matches['attributes'] = substr($matches['attributes'], $attribute[0][1] + strlen($attribute[0][0]));
}
+ while ($matches['attributes'] !== '');
- } while ($count);
-
- return $str;
- }
-
- // --------------------------------------------------------------------
-
- /**
- * Sanitize Naughty HTML
- *
- * Callback function for xss_clean() to remove naughty HTML elements
- *
- * @param array
- * @return string
- */
- protected function _sanitize_naughty_html($matches)
- {
- // encode opening brace
- $str = '&lt;'.$matches[1].$matches[2].$matches[3];
-
- // encode captured opening or closing brace to prevent recursive vectors
- $str .= str_replace(array('>', '<'), array('&gt;', '&lt;'),
- $matches[4]);
+ $attributes = empty($attributes)
+ ? ''
+ : ' '.implode(' ', $attributes);
+ return '<'.$matches['slash'].$matches['tagName'].$attributes.'>';
+ }
- return $str;
+ return $matches[0];
}
// --------------------------------------------------------------------
@@ -673,12 +921,14 @@ class CI_Security {
/**
* JS Link Removal
*
- * Callback function for xss_clean() to sanitize links
+ * Callback method for xss_clean() to sanitize links.
+ *
* This limits the PCRE backtracks, making it more performance friendly
* and prevents PREG_BACKTRACK_LIMIT_ERROR from being triggered in
- * PHP 5.2+ on link-heavy strings
+ * PHP 5.2+ on link-heavy strings.
*
- * @param array
+ * @used-by CI_Security::xss_clean()
+ * @param array $match
* @return string
*/
protected function _js_link_removal($match)
@@ -686,9 +936,9 @@ class CI_Security {
return str_replace(
$match[1],
preg_replace(
- '#href=.*?(alert\(|alert&\#40;|javascript\:|livescript\:|mocha\:|charset\=|window\.|document\.|\.cookie|<script|<xss|data\s*:)#si',
+ '#href=.*?(?:(?:alert|prompt|confirm)(?:\(|&\#40;|`|&\#96;)|javascript:|livescript:|mocha:|charset=|window\.|\(?document\)?\.|\.cookie|<script|<xss|d\s*a\s*t\s*a\s*:)#si',
'',
- $this->_filter_attributes(str_replace(array('<', '>'), '', $match[1]))
+ $this->_filter_attributes($match[1])
),
$match[0]
);
@@ -699,12 +949,14 @@ class CI_Security {
/**
* JS Image Removal
*
- * Callback function for xss_clean() to sanitize image tags
+ * Callback method for xss_clean() to sanitize image tags.
+ *
* This limits the PCRE backtracks, making it more performance friendly
* and prevents PREG_BACKTRACK_LIMIT_ERROR from being triggered in
- * PHP 5.2+ on image tag heavy strings
+ * PHP 5.2+ on image tag heavy strings.
*
- * @param array
+ * @used-by CI_Security::xss_clean()
+ * @param array $match
* @return string
*/
protected function _js_img_removal($match)
@@ -712,9 +964,9 @@ class CI_Security {
return str_replace(
$match[1],
preg_replace(
- '#src=.*?(alert\(|alert&\#40;|javascript\:|livescript\:|mocha\:|charset\=|window\.|document\.|\.cookie|<script|<xss|base64\s*,)#si',
+ '#src=.*?(?:(?:alert|prompt|confirm|eval)(?:\(|&\#40;|`|&\#96;)|javascript:|livescript:|mocha:|charset=|window\.|\(?document\)?\.|\.cookie|<script|<xss|base64\s*,)#si',
'',
- $this->_filter_attributes(str_replace(array('<', '>'), '', $match[1]))
+ $this->_filter_attributes($match[1])
),
$match[0]
);
@@ -725,9 +977,8 @@ class CI_Security {
/**
* Attribute Conversion
*
- * Used as a callback for XSS Clean
- *
- * @param array
+ * @used-by CI_Security::xss_clean()
+ * @param array $match
* @return string
*/
protected function _convert_attribute($match)
@@ -740,20 +991,21 @@ class CI_Security {
/**
* Filter Attributes
*
- * Filters tag attributes for consistency and safety
+ * Filters tag attributes for consistency and safety.
*
- * @param string
+ * @used-by CI_Security::_js_img_removal()
+ * @used-by CI_Security::_js_link_removal()
+ * @param string $str
* @return string
*/
protected function _filter_attributes($str)
{
$out = '';
-
if (preg_match_all('#\s*[a-z\-]+\s*=\s*(\042|\047)([^\\1]*?)\\1#is', $str, $matches))
{
foreach ($matches[0] as $match)
{
- $out .= preg_replace("#/\*.*?\*/#s", '', $match);
+ $out .= preg_replace('#/\*.*?\*/#s', '', $match);
}
}
@@ -765,68 +1017,30 @@ class CI_Security {
/**
* HTML Entity Decode Callback
*
- * Used as a callback for XSS Clean
- *
- * @param array
+ * @used-by CI_Security::xss_clean()
+ * @param array $match
* @return string
*/
protected function _decode_entity($match)
{
- return $this->entity_decode($match[0], strtoupper(config_item('charset')));
- }
-
- // --------------------------------------------------------------------
+ // Protect GET variables in URLs
+ // 901119URL5918AMP18930PROTECT8198
+ $match = preg_replace('|\&([a-z\_0-9\-]+)\=([a-z\_0-9\-/]+)|i', $this->xss_hash().'\\1=\\2', $match[0]);
- /**
- * Validate URL entities
- *
- * Called by xss_clean()
- *
- * @param string
- * @return string
- */
- protected function _validate_entities($str)
- {
- /*
- * Protect GET variables in URLs
- */
-
- // 901119URL5918AMP18930PROTECT8198
-
- $str = preg_replace('|\&([a-z\_0-9\-]+)\=([a-z\_0-9\-]+)|i', $this->xss_hash()."\\1=\\2", $str);
-
- /*
- * Validate standard character entities
- *
- * Add a semicolon if missing. We do this to enable
- * the conversion of entities to ASCII later.
- *
- */
- $str = preg_replace('#(&\#?[0-9a-z]{2,})([\x00-\x20])*;?#i', "\\1;\\2", $str);
-
- /*
- * Validate UTF16 two byte encoding (x00)
- *
- * Just as above, adds a semicolon if missing.
- *
- */
- $str = preg_replace('#(&\#x?)([0-9A-F]+);?#i',"\\1\\2;",$str);
-
- /*
- * Un-Protect GET variables in URLs
- */
- $str = str_replace($this->xss_hash(), '&', $str);
-
- return $str;
+ // Decode, then un-protect URL GET vars
+ return str_replace(
+ $this->xss_hash(),
+ '&',
+ $this->entity_decode($match, $this->charset)
+ );
}
- // ----------------------------------------------------------------------
+ // --------------------------------------------------------------------
/**
* Do Never Allowed
*
- * A utility function for xss_clean()
- *
+ * @used-by CI_Security::xss_clean()
* @param string
* @return string
*/
@@ -845,31 +1059,30 @@ class CI_Security {
// --------------------------------------------------------------------
/**
- * Set Cross Site Request Forgery Protection Cookie
+ * Set CSRF Hash and Cookie
*
* @return string
*/
protected function _csrf_set_hash()
{
- if ($this->_csrf_hash == '')
+ if ($this->_csrf_hash === NULL)
{
- // If the cookie exists we will use it's value.
+ // If the cookie exists we will use its value.
// We don't necessarily want to regenerate it with
// each page load since a page could contain embedded
// sub-pages causing this feature to fail
- if (isset($_COOKIE[$this->_csrf_cookie_name]) &&
- preg_match('#^[0-9a-f]{32}$#iS', $_COOKIE[$this->_csrf_cookie_name]) === 1)
+ if (isset($_COOKIE[$this->_csrf_cookie_name]) && is_string($_COOKIE[$this->_csrf_cookie_name])
+ && preg_match('#^[0-9a-f]{32}$#iS', $_COOKIE[$this->_csrf_cookie_name]) === 1)
{
return $this->_csrf_hash = $_COOKIE[$this->_csrf_cookie_name];
}
- return $this->_csrf_hash = md5(uniqid(rand(), TRUE));
+ $rand = $this->get_random_bytes(16);
+ $this->_csrf_hash = ($rand === FALSE)
+ ? md5(uniqid(mt_rand(), TRUE))
+ : bin2hex($rand);
}
return $this->_csrf_hash;
}
-
}
-
-/* End of file Security.php */
-/* Location: ./system/libraries/Security.php */
diff --git a/system/core/URI.php b/system/core/URI.php
index 3dacd6743..eff969d75 100644
--- a/system/core/URI.php
+++ b/system/core/URI.php
@@ -1,24 +1,42 @@
-<?php if ( ! defined('BASEPATH')) exit('No direct script access allowed');
-/**
- * MODIFIED:
- * _detect_uri(): ltrim instead of trim at the end to preserve tailing slashes
- */
-
+<?php
/**
* CodeIgniter
*
- * An open source application development framework for PHP 5.1.6 or newer
+ * An open source application development framework for PHP
*
- * @package CodeIgniter
- * @author ExpressionEngine Dev Team
- * @copyright Copyright (c) 2008 - 2014, EllisLab, Inc.
- * @license http://codeigniter.com/user_guide/license.html
- * @link http://codeigniter.com
- * @since Version 1.0
+ * This content is released under the MIT License (MIT)
+ *
+ * Copyright (c) 2019 - 2022, CodeIgniter Foundation
+ *
+ * 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.
+ *
+ * @package CodeIgniter
+ * @author EllisLab Dev Team
+ * @copyright Copyright (c) 2008 - 2014, EllisLab, Inc. (https://ellislab.com/)
+ * @copyright Copyright (c) 2014 - 2019, British Columbia Institute of Technology (https://bcit.ca/)
+ * @copyright Copyright (c) 2019 - 2022, CodeIgniter Foundation (https://codeigniter.com/)
+ * @license https://opensource.org/licenses/MIT MIT License
+ * @link https://codeigniter.com
+ * @since Version 1.0.0
* @filesource
*/
-
-// ------------------------------------------------------------------------
+defined('BASEPATH') OR exit('No direct script access allowed');
/**
* URI Class
@@ -28,382 +46,388 @@
* @package CodeIgniter
* @subpackage Libraries
* @category URI
- * @author ExpressionEngine Dev Team
- * @link http://codeigniter.com/user_guide/libraries/uri.html
+ * @author EllisLab Dev Team
+ * @link https://codeigniter.com/userguide3/libraries/uri.html
*/
class CI_URI {
/**
- * List of cached uri segments
+ * List of cached URI segments
*
- * @var array
- * @access public
+ * @var array
*/
- var $keyval = array();
+ public $keyval = array();
+
/**
- * Current uri string
+ * Current URI string
*
- * @var string
- * @access public
+ * @var string
*/
- var $uri_string;
+ public $uri_string = '';
+
/**
- * List of uri segments
+ * List of URI segments
*
- * @var array
- * @access public
+ * Starts at 1 instead of 0.
+ *
+ * @var array
*/
- var $segments = array();
+ public $segments = array();
+
/**
- * Re-indexed list of uri segments
- * Starts at 1 instead of 0
+ * List of routed URI segments
*
- * @var array
- * @access public
+ * Starts at 1 instead of 0.
+ *
+ * @var array
*/
- var $rsegments = array();
+ public $rsegments = array();
/**
- * Constructor
+ * Permitted URI chars
*
- * Simply globalizes the $RTR object. The front
- * loads the Router class early on so it's not available
- * normally as other classes are.
+ * PCRE character group allowed in URI segments
*
- * @access public
+ * @var string
*/
- function __construct()
- {
- $this->config =& load_class('Config', 'core');
- log_message('debug', "URI Class Initialized");
- }
-
+ protected $_permitted_uri_chars;
- // --------------------------------------------------------------------
+ public $config;
/**
- * Get the URI String
+ * Class constructor
*
- * @access private
- * @return string
+ * @return void
*/
- function _fetch_uri_string()
+ public function __construct(CI_Config $config)
{
- if (strtoupper($this->config->item('uri_protocol')) == 'AUTO')
+ $this->config = $config;
+
+ // If it's a CLI request, ignore the configuration
+ if (is_cli())
{
- // Is the request coming from the command line?
- if (php_sapi_name() == 'cli' or defined('STDIN'))
- {
- $this->_set_uri_string($this->_parse_cli_args());
- return;
- }
+ $this->_set_uri_string($this->_parse_argv(), TRUE);
+ }
+ // If query strings are enabled, we don't need to parse any segments.
+ elseif ($this->config->item('enable_query_strings') !== TRUE)
+ {
+ $this->_permitted_uri_chars = $this->config->item('permitted_uri_chars');
+ $protocol = $this->config->item('uri_protocol');
+ empty($protocol) && $protocol = 'REQUEST_URI';
- // Let's try the REQUEST_URI first, this will work in most situations
- if ($uri = $this->_detect_uri())
+ switch ($protocol)
{
- $this->_set_uri_string($uri);
- return;
+ case 'AUTO': // For BC purposes only
+ case 'REQUEST_URI':
+ $uri = $this->_parse_request_uri();
+ break;
+ case 'QUERY_STRING':
+ $uri = $this->_parse_query_string();
+ break;
+ case 'PATH_INFO':
+ default:
+ $uri = isset($_SERVER[$protocol])
+ ? $_SERVER[$protocol]
+ : $this->_parse_request_uri();
+ break;
}
- // Is there a PATH_INFO variable?
- // Note: some servers seem to have trouble with getenv() so we'll test it two ways
- $path = (isset($_SERVER['PATH_INFO'])) ? $_SERVER['PATH_INFO'] : @getenv('PATH_INFO');
- if (trim($path, '/') != '' && $path != "/".SELF)
- {
- $this->_set_uri_string($path);
- return;
- }
+ $this->_set_uri_string($uri, FALSE);
+ }
+
+ log_message('info', 'URI Class Initialized');
+ }
+
+ // --------------------------------------------------------------------
- // No PATH_INFO?... What about QUERY_STRING?
- $path = (isset($_SERVER['QUERY_STRING'])) ? $_SERVER['QUERY_STRING'] : @getenv('QUERY_STRING');
- if (trim($path, '/') != '')
+ /**
+ * Set URI String
+ *
+ * @param string $str Input URI string
+ * @param bool $is_cli Whether the input comes from CLI
+ * @return void
+ */
+ protected function _set_uri_string($str, $is_cli = FALSE)
+ {
+ // CLI requests have a bit simpler logic
+ if ($is_cli)
+ {
+ if (($this->uri_string = trim($str, '/')) === '')
{
- $this->_set_uri_string($path);
return;
}
- // As a last ditch effort lets try using the $_GET array
- if (is_array($_GET) && count($_GET) == 1 && trim(key($_GET), '/') != '')
+ $this->segments[0] = NULL;
+ foreach (explode('/', $this->uri_string) as $segment)
{
- $this->_set_uri_string(key($_GET));
- return;
+ if (($segment = trim($segment)) !== '')
+ {
+ $this->segments[] = $segment;
+ }
}
- // We've exhausted all our options...
- $this->uri_string = '';
+ unset($this->segments[0]);
return;
}
- $uri = strtoupper($this->config->item('uri_protocol'));
+ // Filter out control characters and trim slashes
+ $this->uri_string = trim(remove_invisible_characters($str, FALSE), '/');
- if ($uri == 'REQUEST_URI')
+ if ($this->uri_string === '')
{
- $this->_set_uri_string($this->_detect_uri());
return;
}
- elseif ($uri == 'CLI')
+
+ // Remove the URL suffix, if present
+ if (($suffix = (string) $this->config->item('url_suffix')) !== '')
{
- $this->_set_uri_string($this->_parse_cli_args());
- return;
- }
+ $slen = strlen($suffix);
- $path = (isset($_SERVER[$uri])) ? $_SERVER[$uri] : @getenv($uri);
- $this->_set_uri_string($path);
- }
+ if (substr($this->uri_string, -$slen) === $suffix)
+ {
+ $this->uri_string = substr($this->uri_string, 0, -$slen);
+ }
+ }
- // --------------------------------------------------------------------
+ $this->segments[0] = NULL;
+ foreach (explode('/', trim($this->uri_string, '/')) as $segment)
+ {
+ $segment = trim($segment);
+ // Filter segments for security
+ $this->filter_uri($segment);
- /**
- * Set the URI String
- *
- * @access public
- * @param string
- * @return string
- */
- function _set_uri_string($str)
- {
- // Filter out control characters
- $str = remove_invisible_characters($str, FALSE);
+ if ($segment !== '')
+ {
+ $this->segments[] = $segment;
+ }
+ }
- // If the URI contains only a slash we'll kill it
- $this->uri_string = ($str == '/') ? '' : $str;
+ unset($this->segments[0]);
}
// --------------------------------------------------------------------
/**
- * Detects the URI
+ * Parse REQUEST_URI
*
- * This function will detect the URI automatically and fix the query string
- * if necessary.
+ * Will parse REQUEST_URI and automatically detect the URI from it,
+ * while fixing the query string if necessary.
*
- * @access private
* @return string
*/
- private function _detect_uri()
+ protected function _parse_request_uri()
{
- if ( ! isset($_SERVER['REQUEST_URI']) OR ! isset($_SERVER['SCRIPT_NAME']))
+ if ( ! isset($_SERVER['REQUEST_URI'], $_SERVER['SCRIPT_NAME']))
{
return '';
}
- $uri = $_SERVER['REQUEST_URI'];
- if (strpos($uri, $_SERVER['SCRIPT_NAME']) === 0)
- {
- $uri = substr($uri, strlen($_SERVER['SCRIPT_NAME']));
- }
- elseif (strpos($uri, dirname($_SERVER['SCRIPT_NAME'])) === 0)
+ // parse_url() returns false if no host is present, but the path or query string
+ // contains a colon followed by a number
+ $uri = parse_url('http://dummy'.$_SERVER['REQUEST_URI']);
+ $query = isset($uri['query']) ? $uri['query'] : '';
+ $uri = isset($uri['path']) ? $uri['path'] : '';
+
+ if (isset($_SERVER['SCRIPT_NAME'][0]))
{
- $uri = substr($uri, strlen(dirname($_SERVER['SCRIPT_NAME'])));
+ if (strpos($uri, $_SERVER['SCRIPT_NAME']) === 0)
+ {
+ $uri = (string) substr($uri, strlen($_SERVER['SCRIPT_NAME']));
+ }
+ elseif (strpos($uri, dirname($_SERVER['SCRIPT_NAME'])) === 0)
+ {
+ $uri = (string) substr($uri, strlen(dirname($_SERVER['SCRIPT_NAME'])));
+ }
}
// This section ensures that even on servers that require the URI to be in the query string (Nginx) a correct
// URI is found, and also fixes the QUERY_STRING server var and $_GET array.
- if (strncmp($uri, '?/', 2) === 0)
+ if (trim($uri, '/') === '' && strncmp($query, '/', 1) === 0)
{
- $uri = substr($uri, 2);
- }
- $parts = preg_split('#\?#i', $uri, 2);
- $uri = $parts[0];
- if (isset($parts[1]))
- {
- $_SERVER['QUERY_STRING'] = $parts[1];
- parse_str($_SERVER['QUERY_STRING'], $_GET);
+ $query = explode('?', $query, 2);
+ $uri = $query[0];
+ $_SERVER['QUERY_STRING'] = isset($query[1]) ? $query[1] : '';
}
else
{
- $_SERVER['QUERY_STRING'] = '';
- $_GET = array();
+ $_SERVER['QUERY_STRING'] = $query;
}
- if ($uri == '/' || empty($uri))
+ parse_str($_SERVER['QUERY_STRING'], $_GET);
+
+ if ($uri === '/' OR $uri === '')
{
return '/';
}
- $uri = parse_url($uri, PHP_URL_PATH);
-
// Do some final cleaning of the URI and return it
- return str_replace(array('//', '../'), '/', ltrim($uri, '/'));
+ return $this->_remove_relative_directory($uri);
}
// --------------------------------------------------------------------
/**
- * Parse cli arguments
+ * Parse QUERY_STRING
*
- * Take each command line argument and assume it is a URI segment.
+ * Will parse QUERY_STRING and automatically detect the URI from it.
*
- * @access private
* @return string
*/
- private function _parse_cli_args()
+ protected function _parse_query_string()
{
- $args = array_slice($_SERVER['argv'], 1);
-
- return $args ? '/' . implode('/', $args) : '';
- }
-
- // --------------------------------------------------------------------
+ $uri = isset($_SERVER['QUERY_STRING']) ? $_SERVER['QUERY_STRING'] : @getenv('QUERY_STRING');
- /**
- * Filter segments for malicious characters
- *
- * @access private
- * @param string
- * @return string
- */
- function _filter_uri($str)
- {
- if ($str != '' && $this->config->item('permitted_uri_chars') != '' && $this->config->item('enable_query_strings') == FALSE)
+ if (trim($uri, '/') === '')
{
- // preg_quote() in PHP 5.3 escapes -, so the str_replace() and addition of - to preg_quote() is to maintain backwards
- // compatibility as many are unaware of how characters in the permitted_uri_chars will be parsed as a regex pattern
- if ( ! preg_match("|^[".str_replace(array('\\-', '\-'), '-', preg_quote($this->config->item('permitted_uri_chars'), '-'))."]+$|i", $str))
- {
- show_error('The URI you submitted has disallowed characters.', 400);
- }
+ return '';
+ }
+ elseif (strncmp($uri, '/', 1) === 0)
+ {
+ $uri = explode('?', $uri, 2);
+ $_SERVER['QUERY_STRING'] = isset($uri[1]) ? $uri[1] : '';
+ $uri = $uri[0];
}
- // Convert programatic characters to entities
- $bad = array('$', '(', ')', '%28', '%29');
- $good = array('&#36;', '&#40;', '&#41;', '&#40;', '&#41;');
+ parse_str($_SERVER['QUERY_STRING'], $_GET);
- return str_replace($bad, $good, $str);
+ return $this->_remove_relative_directory($uri);
}
// --------------------------------------------------------------------
/**
- * Remove the suffix from the URL if needed
+ * Parse CLI arguments
*
- * @access private
- * @return void
+ * Take each command line argument and assume it is a URI segment.
+ *
+ * @return string
*/
- function _remove_url_suffix()
+ protected function _parse_argv()
{
- if ($this->config->item('url_suffix') != "")
- {
- $this->uri_string = preg_replace("|".preg_quote($this->config->item('url_suffix'))."$|", "", $this->uri_string);
- }
+ $args = array_slice($_SERVER['argv'], 1);
+ return $args ? implode('/', $args) : '';
}
// --------------------------------------------------------------------
/**
- * Explode the URI Segments. The individual segments will
- * be stored in the $this->segments array.
+ * Remove relative directory (../) and multi slashes (///)
*
- * @access private
- * @return void
+ * Do some final cleaning of the URI and return it, currently only used in self::_parse_request_uri()
+ *
+ * @param string $uri
+ * @return string
*/
- function _explode_segments()
+ protected function _remove_relative_directory($uri)
{
- foreach (explode("/", preg_replace("|/*(.+?)/*$|", "\\1", $this->uri_string)) as $val)
+ $uris = array();
+ $tok = strtok($uri, '/');
+ while ($tok !== FALSE)
{
- // Filter segments for security
- $val = trim($this->_filter_uri($val));
-
- if ($val != '')
+ if (( ! empty($tok) OR $tok === '0') && $tok !== '..')
{
- $this->segments[] = $val;
+ $uris[] = $tok;
}
+ $tok = strtok('/');
}
+
+ return implode('/', $uris);
}
// --------------------------------------------------------------------
+
/**
- * Re-index Segments
+ * Filter URI
*
- * This function re-indexes the $this->segment array so that it
- * starts at 1 rather than 0. Doing so makes it simpler to
- * use functions like $this->uri->segment(n) since there is
- * a 1:1 relationship between the segment array and the actual segments.
+ * Filters segments for malicious characters.
*
- * @access private
+ * @param string $str
* @return void
*/
- function _reindex_segments()
+ public function filter_uri(&$str)
{
- array_unshift($this->segments, NULL);
- array_unshift($this->rsegments, NULL);
- unset($this->segments[0]);
- unset($this->rsegments[0]);
+ if ( ! empty($str) && ! empty($this->_permitted_uri_chars) && ! preg_match('/^['.$this->_permitted_uri_chars.']+$/i'.(UTF8_ENABLED ? 'u' : ''), $str))
+ {
+ show_error('The URI you submitted has disallowed characters.', 400);
+ }
}
// --------------------------------------------------------------------
/**
- * Fetch a URI Segment
- *
- * This function returns the URI segment based on the number provided.
+ * Fetch URI Segment
*
- * @access public
- * @param integer
- * @param bool
- * @return string
+ * @see CI_URI::$segments
+ * @param int $n Index
+ * @param mixed $no_result What to return if the segment index is not found
+ * @return mixed
*/
- function segment($n, $no_result = FALSE)
+ public function segment($n, $no_result = NULL)
{
- return ( ! isset($this->segments[$n])) ? $no_result : $this->segments[$n];
+ return isset($this->segments[$n]) ? $this->segments[$n] : $no_result;
}
// --------------------------------------------------------------------
/**
- * Fetch a URI "routed" Segment
+ * Fetch URI "routed" Segment
*
- * This function returns the re-routed URI segment (assuming routing rules are used)
- * based on the number provided. If there is no routing this function returns the
- * same result as $this->segment()
+ * Returns the re-routed URI segment (assuming routing rules are used)
+ * based on the index provided. If there is no routing, will return
+ * the same result as CI_URI::segment().
*
- * @access public
- * @param integer
- * @param bool
- * @return string
+ * @see CI_URI::$rsegments
+ * @see CI_URI::segment()
+ * @param int $n Index
+ * @param mixed $no_result What to return if the segment index is not found
+ * @return mixed
*/
- function rsegment($n, $no_result = FALSE)
+ public function rsegment($n, $no_result = NULL)
{
- return ( ! isset($this->rsegments[$n])) ? $no_result : $this->rsegments[$n];
+ return isset($this->rsegments[$n]) ? $this->rsegments[$n] : $no_result;
}
// --------------------------------------------------------------------
/**
- * Generate a key value pair from the URI string
+ * URI to assoc
*
- * This function generates and associative array of URI data starting
- * at the supplied segment. For example, if this is your URI:
+ * Generates an associative array of URI data starting at the supplied
+ * segment index. For example, if this is your URI:
*
* example.com/user/search/name/joe/location/UK/gender/male
*
- * You can use this function to generate an array with this prototype:
+ * You can use this method to generate an array with this prototype:
*
- * array (
- * name => joe
- * location => UK
- * gender => male
- * )
+ * array (
+ * name => joe
+ * location => UK
+ * gender => male
+ * )
*
- * @access public
- * @param integer the starting segment number
- * @param array an array of default values
+ * @param int $n Index (default: 3)
+ * @param array $default Default values
* @return array
*/
- function uri_to_assoc($n = 3, $default = array())
+ public function uri_to_assoc($n = 3, $default = array())
{
return $this->_uri_to_assoc($n, $default, 'segment');
}
+
+ // --------------------------------------------------------------------
+
/**
- * Identical to above only it uses the re-routed segment array
+ * Routed URI to assoc
*
- * @access public
- * @param integer the starting segment number
- * @param array an array of default values
- * @return array
+ * Identical to CI_URI::uri_to_assoc(), only it uses the re-routed
+ * segment array.
*
+ * @see CI_URI::uri_to_assoc()
+ * @param int $n Index (default: 3)
+ * @param array $default Default values
+ * @return array
*/
- function ruri_to_assoc($n = 3, $default = array())
+ public function ruri_to_assoc($n = 3, $default = array())
{
return $this->_uri_to_assoc($n, $default, 'rsegment');
}
@@ -411,57 +435,43 @@ class CI_URI {
// --------------------------------------------------------------------
/**
- * Generate a key value pair from the URI string or Re-routed URI string
+ * Internal URI-to-assoc
+ *
+ * Generates a key/value pair from the URI string or re-routed URI string.
*
- * @access private
- * @param integer the starting segment number
- * @param array an array of default values
- * @param string which array we should use
+ * @used-by CI_URI::uri_to_assoc()
+ * @used-by CI_URI::ruri_to_assoc()
+ * @param int $n Index (default: 3)
+ * @param array $default Default values
+ * @param string $which Array name ('segment' or 'rsegment')
* @return array
*/
- function _uri_to_assoc($n = 3, $default = array(), $which = 'segment')
+ protected function _uri_to_assoc($n = 3, $default = array(), $which = 'segment')
{
- if ($which == 'segment')
- {
- $total_segments = 'total_segments';
- $segment_array = 'segment_array';
- }
- else
- {
- $total_segments = 'total_rsegments';
- $segment_array = 'rsegment_array';
- }
-
if ( ! is_numeric($n))
{
return $default;
}
- if (isset($this->keyval[$n]))
+ if (isset($this->keyval[$which], $this->keyval[$which][$n]))
{
- return $this->keyval[$n];
+ return $this->keyval[$which][$n];
}
+ $total_segments = "total_{$which}s";
+ $segment_array = "{$which}_array";
+
if ($this->$total_segments() < $n)
{
- if (count($default) == 0)
- {
- return array();
- }
-
- $retval = array();
- foreach ($default as $val)
- {
- $retval[$val] = FALSE;
- }
- return $retval;
+ return (count($default) === 0)
+ ? array()
+ : array_fill_keys($default, NULL);
}
$segments = array_slice($this->$segment_array(), ($n - 1));
-
$i = 0;
$lastval = '';
- $retval = array();
+ $retval = array();
foreach ($segments as $seg)
{
if ($i % 2)
@@ -470,7 +480,7 @@ class CI_URI {
}
else
{
- $retval[$seg] = FALSE;
+ $retval[$seg] = NULL;
$lastval = $seg;
}
@@ -483,30 +493,31 @@ class CI_URI {
{
if ( ! array_key_exists($val, $retval))
{
- $retval[$val] = FALSE;
+ $retval[$val] = NULL;
}
}
}
// Cache the array for reuse
- $this->keyval[$n] = $retval;
+ isset($this->keyval[$which]) OR $this->keyval[$which] = array();
+ $this->keyval[$which][$n] = $retval;
return $retval;
}
// --------------------------------------------------------------------
/**
- * Generate a URI string from an associative array
+ * Assoc to URI
*
+ * Generates a URI string from an associative array.
*
- * @access public
- * @param array an associative array of key/values
- * @return array
+ * @param array $array Input array of key/value pairs
+ * @return string URI string
*/
- function assoc_to_uri($array)
+ public function assoc_to_uri($array)
{
$temp = array();
- foreach ((array)$array as $key => $val)
+ foreach ((array) $array as $key => $val)
{
$temp[] = $key;
$temp[] = $val;
@@ -518,14 +529,15 @@ class CI_URI {
// --------------------------------------------------------------------
/**
- * Fetch a URI Segment and add a trailing slash
+ * Slash segment
+ *
+ * Fetches an URI segment with a slash.
*
- * @access public
- * @param integer
- * @param string
+ * @param int $n Index
+ * @param string $where Where to add the slash ('trailing' or 'leading')
* @return string
*/
- function slash_segment($n, $where = 'trailing')
+ public function slash_segment($n, $where = 'trailing')
{
return $this->_slash_segment($n, $where, 'segment');
}
@@ -533,14 +545,15 @@ class CI_URI {
// --------------------------------------------------------------------
/**
- * Fetch a URI Segment and add a trailing slash
+ * Slash routed segment
*
- * @access public
- * @param integer
- * @param string
+ * Fetches an URI routed segment with a slash.
+ *
+ * @param int $n Index
+ * @param string $where Where to add the slash ('trailing' or 'leading')
* @return string
*/
- function slash_rsegment($n, $where = 'trailing')
+ public function slash_rsegment($n, $where = 'trailing')
{
return $this->_slash_segment($n, $where, 'rsegment');
}
@@ -548,24 +561,27 @@ class CI_URI {
// --------------------------------------------------------------------
/**
- * Fetch a URI Segment and add a trailing slash - helper function
+ * Internal Slash segment
+ *
+ * Fetches an URI Segment and adds a slash to it.
*
- * @access private
- * @param integer
- * @param string
- * @param string
+ * @used-by CI_URI::slash_segment()
+ * @used-by CI_URI::slash_rsegment()
+ *
+ * @param int $n Index
+ * @param string $where Where to add the slash ('trailing' or 'leading')
+ * @param string $which Array name ('segment' or 'rsegment')
* @return string
*/
- function _slash_segment($n, $where = 'trailing', $which = 'segment')
+ protected function _slash_segment($n, $where = 'trailing', $which = 'segment')
{
- $leading = '/';
- $trailing = '/';
+ $leading = $trailing = '/';
- if ($where == 'trailing')
+ if ($where === 'trailing')
{
$leading = '';
}
- elseif ($where == 'leading')
+ elseif ($where === 'leading')
{
$trailing = '';
}
@@ -578,10 +594,9 @@ class CI_URI {
/**
* Segment Array
*
- * @access public
- * @return array
+ * @return array CI_URI::$segments
*/
- function segment_array()
+ public function segment_array()
{
return $this->segments;
}
@@ -591,10 +606,9 @@ class CI_URI {
/**
* Routed Segment Array
*
- * @access public
- * @return array
+ * @return array CI_URI::$rsegments
*/
- function rsegment_array()
+ public function rsegment_array()
{
return $this->rsegments;
}
@@ -604,10 +618,9 @@ class CI_URI {
/**
* Total number of segments
*
- * @access public
- * @return integer
+ * @return int
*/
- function total_segments()
+ public function total_segments()
{
return count($this->segments);
}
@@ -617,10 +630,9 @@ class CI_URI {
/**
* Total number of routed segments
*
- * @access public
- * @return integer
+ * @return int
*/
- function total_rsegments()
+ public function total_rsegments()
{
return count($this->rsegments);
}
@@ -628,32 +640,25 @@ class CI_URI {
// --------------------------------------------------------------------
/**
- * Fetch the entire URI string
+ * Fetch URI string
*
- * @access public
- * @return string
+ * @return string CI_URI::$uri_string
*/
- function uri_string()
+ public function uri_string()
{
return $this->uri_string;
}
-
// --------------------------------------------------------------------
/**
- * Fetch the entire Re-routed URI string
+ * Fetch Re-routed URI string
*
- * @access public
* @return string
*/
- function ruri_string()
+ public function ruri_string()
{
- return '/'.implode('/', $this->rsegment_array());
+ return ltrim(load_class('Router', 'core')->directory, '/').implode('/', $this->rsegments);
}
}
-// END URI Class
-
-/* End of file URI.php */
-/* Location: ./system/core/URI.php */
diff --git a/system/core/Utf8.php b/system/core/Utf8.php
index 1a5bee198..58cbb1334 100644
--- a/system/core/Utf8.php
+++ b/system/core/Utf8.php
@@ -1,19 +1,42 @@
-<?php if ( ! defined('BASEPATH')) exit('No direct script access allowed');
+<?php
/**
* CodeIgniter
*
- * An open source application development framework for PHP 5.1.6 or newer
+ * An open source application development framework for PHP
*
- * @package CodeIgniter
- * @author ExpressionEngine Dev Team
- * @copyright Copyright (c) 2008 - 2014, EllisLab, Inc.
- * @license http://codeigniter.com/user_guide/license.html
- * @link http://codeigniter.com
- * @since Version 2.0
+ * This content is released under the MIT License (MIT)
+ *
+ * Copyright (c) 2019 - 2022, CodeIgniter Foundation
+ *
+ * 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.
+ *
+ * @package CodeIgniter
+ * @author EllisLab Dev Team
+ * @copyright Copyright (c) 2008 - 2014, EllisLab, Inc. (https://ellislab.com/)
+ * @copyright Copyright (c) 2014 - 2019, British Columbia Institute of Technology (https://bcit.ca/)
+ * @copyright Copyright (c) 2019 - 2022, CodeIgniter Foundation (https://codeigniter.com/)
+ * @license https://opensource.org/licenses/MIT MIT License
+ * @link https://codeigniter.com
+ * @since Version 2.0.0
* @filesource
*/
-
-// ------------------------------------------------------------------------
+defined('BASEPATH') OR exit('No direct script access allowed');
/**
* Utf8 Class
@@ -23,52 +46,36 @@
* @package CodeIgniter
* @subpackage Libraries
* @category UTF-8
- * @author ExpressionEngine Dev Team
- * @link http://codeigniter.com/user_guide/libraries/utf8.html
+ * @author EllisLab Dev Team
+ * @link https://codeigniter.com/userguide3/libraries/utf8.html
*/
class CI_Utf8 {
/**
- * Constructor
+ * Class constructor
*
- * Determines if UTF-8 support is to be enabled
+ * Determines if UTF-8 support is to be enabled.
*
+ * @return void
*/
- function __construct()
+ public function __construct($charset)
{
- log_message('debug', "Utf8 Class Initialized");
-
- global $CFG;
-
if (
- preg_match('/./u', 'é') === 1 // PCRE must support UTF-8
- AND function_exists('iconv') // iconv must be installed
- AND ini_get('mbstring.func_overload') != 1 // Multibyte string function overloading cannot be enabled
- AND $CFG->item('charset') == 'UTF-8' // Application charset must be UTF-8
- )
+ defined('PREG_BAD_UTF8_ERROR') // PCRE must support UTF-8
+ && (ICONV_ENABLED === TRUE OR MB_ENABLED === TRUE) // iconv or mbstring must be installed
+ && $charset === 'UTF-8' // Application charset must be UTF-8
+ )
{
- log_message('debug', "UTF-8 Support Enabled");
-
define('UTF8_ENABLED', TRUE);
-
- // set internal encoding for multibyte string functions if necessary
- // and set a flag so we don't have to repeatedly use extension_loaded()
- // or function_exists()
- if (extension_loaded('mbstring'))
- {
- define('MB_ENABLED', TRUE);
- mb_internal_encoding('UTF-8');
- }
- else
- {
- define('MB_ENABLED', FALSE);
- }
+ log_message('info', 'UTF-8 Support Enabled');
}
else
{
- log_message('debug', "UTF-8 Support Disabled");
define('UTF8_ENABLED', FALSE);
+ log_message('info', 'UTF-8 Support Disabled');
}
+
+ log_message('info', 'Utf8 Class Initialized');
}
// --------------------------------------------------------------------
@@ -76,17 +83,23 @@ class CI_Utf8 {
/**
* Clean UTF-8 strings
*
- * Ensures strings are UTF-8
+ * Ensures strings contain only valid UTF-8 characters.
*
- * @access public
- * @param string
+ * @param string $str String to clean
* @return string
*/
- function clean_string($str)
+ public function clean_string($str)
{
- if ($this->_is_ascii($str) === FALSE)
+ if ($this->is_ascii($str) === FALSE)
{
- $str = @iconv('UTF-8', 'UTF-8//IGNORE', $str);
+ if (MB_ENABLED)
+ {
+ $str = mb_convert_encoding($str, 'UTF-8', 'UTF-8');
+ }
+ elseif (ICONV_ENABLED)
+ {
+ $str = @iconv('UTF-8', 'UTF-8//IGNORE', $str);
+ }
}
return $str;
@@ -99,13 +112,12 @@ class CI_Utf8 {
*
* Removes all ASCII control characters except horizontal tabs,
* line feeds, and carriage returns, as all others can cause
- * problems in XML
+ * problems in XML.
*
- * @access public
- * @param string
+ * @param string $str String to clean
* @return string
*/
- function safe_ascii_for_xml($str)
+ public function safe_ascii_for_xml($str)
{
return remove_invisible_characters($str, FALSE);
}
@@ -115,29 +127,24 @@ class CI_Utf8 {
/**
* Convert to UTF-8
*
- * Attempts to convert a string to UTF-8
+ * Attempts to convert a string to UTF-8.
*
- * @access public
- * @param string
- * @param string - input encoding
- * @return string
+ * @param string $str Input string
+ * @param string $encoding Input encoding
+ * @return string $str encoded in UTF-8 or FALSE on failure
*/
- function convert_to_utf8($str, $encoding)
+ public function convert_to_utf8($str, $encoding)
{
- if (function_exists('iconv'))
+ if (MB_ENABLED)
{
- $str = @iconv($encoding, 'UTF-8', $str);
+ return mb_convert_encoding($str, 'UTF-8', $encoding);
}
- elseif (function_exists('mb_convert_encoding'))
+ elseif (ICONV_ENABLED)
{
- $str = @mb_convert_encoding($str, 'UTF-8', $encoding);
- }
- else
- {
- return FALSE;
+ return @iconv($encoding, 'UTF-8', $str);
}
- return $str;
+ return FALSE;
}
// --------------------------------------------------------------------
@@ -145,21 +152,14 @@ class CI_Utf8 {
/**
* Is ASCII?
*
- * Tests if a string is standard 7-bit ASCII or not
+ * Tests if a string is standard 7-bit ASCII or not.
*
- * @access public
- * @param string
+ * @param string $str String to check
* @return bool
*/
- function _is_ascii($str)
+ public function is_ascii($str)
{
- return (preg_match('/[^\x00-\x7F]/S', $str) == 0);
+ return (preg_match('/[^\x00-\x7F]/S', $str) === 0);
}
- // --------------------------------------------------------------------
-
}
-// End Utf8 Class
-
-/* End of file Utf8.php */
-/* Location: ./system/core/Utf8.php */ \ No newline at end of file
diff --git a/system/core/compat/hash.php b/system/core/compat/hash.php
new file mode 100644
index 000000000..8d03fa922
--- /dev/null
+++ b/system/core/compat/hash.php
@@ -0,0 +1,253 @@
+<?php
+/**
+ * CodeIgniter
+ *
+ * An open source application development framework for PHP
+ *
+ * This content is released under the MIT License (MIT)
+ *
+ * Copyright (c) 2019 - 2022, CodeIgniter Foundation
+ *
+ * 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.
+ *
+ * @package CodeIgniter
+ * @author EllisLab Dev Team
+ * @copyright Copyright (c) 2008 - 2014, EllisLab, Inc. (https://ellislab.com/)
+ * @copyright Copyright (c) 2014 - 2019, British Columbia Institute of Technology (https://bcit.ca/)
+ * @copyright Copyright (c) 2019 - 2022, CodeIgniter Foundation (https://codeigniter.com/)
+ * @license https://opensource.org/licenses/MIT MIT License
+ * @link https://codeigniter.com
+ * @since Version 3.0.0
+ * @filesource
+ */
+defined('BASEPATH') OR exit('No direct script access allowed');
+
+/**
+ * PHP ext/hash compatibility package
+ *
+ * @package CodeIgniter
+ * @subpackage CodeIgniter
+ * @category Compatibility
+ * @author Andrey Andreev
+ * @link https://codeigniter.com/userguide3/
+ * @link https://secure.php.net/hash
+ */
+
+// ------------------------------------------------------------------------
+
+if (is_php('5.6'))
+{
+ return;
+}
+
+// ------------------------------------------------------------------------
+
+if ( ! function_exists('hash_equals'))
+{
+ /**
+ * hash_equals()
+ *
+ * @link https://secure.php.net/hash_equals
+ * @param string $known_string
+ * @param string $user_string
+ * @return bool
+ */
+ function hash_equals($known_string, $user_string)
+ {
+ if ( ! is_string($known_string))
+ {
+ trigger_error('hash_equals(): Expected known_string to be a string, '.strtolower(gettype($known_string)).' given', E_USER_WARNING);
+ return FALSE;
+ }
+ elseif ( ! is_string($user_string))
+ {
+ trigger_error('hash_equals(): Expected user_string to be a string, '.strtolower(gettype($user_string)).' given', E_USER_WARNING);
+ return FALSE;
+ }
+ elseif (($length = strlen($known_string)) !== strlen($user_string))
+ {
+ return FALSE;
+ }
+
+ $diff = 0;
+ for ($i = 0; $i < $length; $i++)
+ {
+ $diff |= ord($known_string[$i]) ^ ord($user_string[$i]);
+ }
+
+ return ($diff === 0);
+ }
+}
+
+// ------------------------------------------------------------------------
+
+if (is_php('5.5'))
+{
+ return;
+}
+
+// ------------------------------------------------------------------------
+
+if ( ! function_exists('hash_pbkdf2'))
+{
+ /**
+ * hash_pbkdf2()
+ *
+ * @link https://secure.php.net/hash_pbkdf2
+ * @param string $algo
+ * @param string $password
+ * @param string $salt
+ * @param int $iterations
+ * @param int $length
+ * @param bool $raw_output
+ * @return string
+ */
+ function hash_pbkdf2($algo, $password, $salt, $iterations, $length = 0, $raw_output = FALSE)
+ {
+ if ( ! in_array(strtolower($algo), hash_algos(), TRUE))
+ {
+ trigger_error('hash_pbkdf2(): Unknown hashing algorithm: '.$algo, E_USER_WARNING);
+ return FALSE;
+ }
+
+ if (($type = gettype($iterations)) !== 'integer')
+ {
+ if ($type === 'object' && method_exists($iterations, '__toString'))
+ {
+ $iterations = (string) $iterations;
+ }
+
+ if (is_string($iterations) && is_numeric($iterations))
+ {
+ $iterations = (int) $iterations;
+ }
+ else
+ {
+ trigger_error('hash_pbkdf2() expects parameter 4 to be long, '.$type.' given', E_USER_WARNING);
+ return NULL;
+ }
+ }
+
+ if ($iterations < 1)
+ {
+ trigger_error('hash_pbkdf2(): Iterations must be a positive integer: '.$iterations, E_USER_WARNING);
+ return FALSE;
+ }
+
+ if (($type = gettype($length)) !== 'integer')
+ {
+ if ($type === 'object' && method_exists($length, '__toString'))
+ {
+ $length = (string) $length;
+ }
+
+ if (is_string($length) && is_numeric($length))
+ {
+ $length = (int) $length;
+ }
+ else
+ {
+ trigger_error('hash_pbkdf2() expects parameter 5 to be long, '.$type.' given', E_USER_WARNING);
+ return NULL;
+ }
+ }
+
+ if ($length < 0)
+ {
+ trigger_error('hash_pbkdf2(): Length must be greater than or equal to 0: '.$length, E_USER_WARNING);
+ return FALSE;
+ }
+
+ $hash_length = defined('MB_OVERLOAD_STRING')
+ ? mb_strlen(hash($algo, NULL, TRUE), '8bit')
+ : strlen(hash($algo, NULL, TRUE));
+ empty($length) && $length = $hash_length;
+
+ // Pre-hash password inputs longer than the algorithm's block size
+ // (i.e. prepare HMAC key) to mitigate potential DoS attacks.
+ static $block_sizes;
+ empty($block_sizes) && $block_sizes = array(
+ 'gost' => 32,
+ 'haval128,3' => 128,
+ 'haval160,3' => 128,
+ 'haval192,3' => 128,
+ 'haval224,3' => 128,
+ 'haval256,3' => 128,
+ 'haval128,4' => 128,
+ 'haval160,4' => 128,
+ 'haval192,4' => 128,
+ 'haval224,4' => 128,
+ 'haval256,4' => 128,
+ 'haval128,5' => 128,
+ 'haval160,5' => 128,
+ 'haval192,5' => 128,
+ 'haval224,5' => 128,
+ 'haval256,5' => 128,
+ 'md2' => 16,
+ 'md4' => 64,
+ 'md5' => 64,
+ 'ripemd128' => 64,
+ 'ripemd160' => 64,
+ 'ripemd256' => 64,
+ 'ripemd320' => 64,
+ 'sha1' => 64,
+ 'sha224' => 64,
+ 'sha256' => 64,
+ 'sha384' => 128,
+ 'sha512' => 128,
+ 'snefru' => 32,
+ 'snefru256' => 32,
+ 'tiger128,3' => 64,
+ 'tiger160,3' => 64,
+ 'tiger192,3' => 64,
+ 'tiger128,4' => 64,
+ 'tiger160,4' => 64,
+ 'tiger192,4' => 64,
+ 'whirlpool' => 64
+ );
+
+ if (isset($block_sizes[$algo], $password[$block_sizes[$algo]]))
+ {
+ $password = hash($algo, $password, TRUE);
+ }
+
+ $hash = '';
+ // Note: Blocks are NOT 0-indexed
+ for ($bc = (int) ceil($length / $hash_length), $bi = 1; $bi <= $bc; $bi++)
+ {
+ $key = $derived_key = hash_hmac($algo, $salt.pack('N', $bi), $password, TRUE);
+ for ($i = 1; $i < $iterations; $i++)
+ {
+ $derived_key ^= $key = hash_hmac($algo, $key, $password, TRUE);
+ }
+
+ $hash .= $derived_key;
+ }
+
+ // This is not RFC-compatible, but we're aiming for natural PHP compatibility
+ if ( ! $raw_output)
+ {
+ $hash = bin2hex($hash);
+ }
+
+ return defined('MB_OVERLOAD_STRING')
+ ? mb_substr($hash, 0, $length, '8bit')
+ : substr($hash, 0, $length);
+ }
+}
diff --git a/system/core/compat/index.html b/system/core/compat/index.html
new file mode 100644
index 000000000..bcb7cae34
--- /dev/null
+++ b/system/core/compat/index.html
@@ -0,0 +1,11 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+ <title>403 Forbidden</title>
+</head>
+<body>
+
+<p>Directory access is forbidden.</p>
+
+</body>
+</html>
diff --git a/system/core/compat/mbstring.php b/system/core/compat/mbstring.php
new file mode 100644
index 000000000..7ec598d68
--- /dev/null
+++ b/system/core/compat/mbstring.php
@@ -0,0 +1,150 @@
+<?php
+/**
+ * CodeIgniter
+ *
+ * An open source application development framework for PHP
+ *
+ * This content is released under the MIT License (MIT)
+ *
+ * Copyright (c) 2019 - 2022, CodeIgniter Foundation
+ *
+ * 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.
+ *
+ * @package CodeIgniter
+ * @author EllisLab Dev Team
+ * @copyright Copyright (c) 2008 - 2014, EllisLab, Inc. (https://ellislab.com/)
+ * @copyright Copyright (c) 2014 - 2019, British Columbia Institute of Technology (https://bcit.ca/)
+ * @copyright Copyright (c) 2019 - 2022, CodeIgniter Foundation (https://codeigniter.com/)
+ * @license https://opensource.org/licenses/MIT MIT License
+ * @link https://codeigniter.com
+ * @since Version 3.0.0
+ * @filesource
+ */
+defined('BASEPATH') OR exit('No direct script access allowed');
+
+/**
+ * PHP ext/mbstring compatibility package
+ *
+ * @package CodeIgniter
+ * @subpackage CodeIgniter
+ * @category Compatibility
+ * @author Andrey Andreev
+ * @link https://codeigniter.com/userguide3/
+ * @link https://secure.php.net/mbstring
+ */
+
+// ------------------------------------------------------------------------
+
+if (MB_ENABLED === TRUE)
+{
+ return;
+}
+
+// ------------------------------------------------------------------------
+
+if ( ! function_exists('mb_strlen'))
+{
+ /**
+ * mb_strlen()
+ *
+ * WARNING: This function WILL fall-back to strlen()
+ * if iconv is not available!
+ *
+ * @link https://secure.php.net/mb_strlen
+ * @param string $str
+ * @param string $encoding
+ * @return int
+ */
+ function mb_strlen($str, $encoding = NULL)
+ {
+ if (ICONV_ENABLED === TRUE)
+ {
+ return iconv_strlen($str, isset($encoding) ? $encoding : config_item('charset'));
+ }
+
+ log_message('debug', 'Compatibility (mbstring): iconv_strlen() is not available, falling back to strlen().');
+ return strlen($str);
+ }
+}
+
+// ------------------------------------------------------------------------
+
+if ( ! function_exists('mb_strpos'))
+{
+ /**
+ * mb_strpos()
+ *
+ * WARNING: This function WILL fall-back to strpos()
+ * if iconv is not available!
+ *
+ * @link https://secure.php.net/mb_strpos
+ * @param string $haystack
+ * @param string $needle
+ * @param int $offset
+ * @param string $encoding
+ * @return mixed
+ */
+ function mb_strpos($haystack, $needle, $offset = 0, $encoding = NULL)
+ {
+ if (ICONV_ENABLED === TRUE)
+ {
+ return iconv_strpos($haystack, $needle, $offset, isset($encoding) ? $encoding : config_item('charset'));
+ }
+
+ log_message('debug', 'Compatibility (mbstring): iconv_strpos() is not available, falling back to strpos().');
+ return strpos($haystack, $needle, $offset);
+ }
+}
+
+// ------------------------------------------------------------------------
+
+if ( ! function_exists('mb_substr'))
+{
+ /**
+ * mb_substr()
+ *
+ * WARNING: This function WILL fall-back to substr()
+ * if iconv is not available.
+ *
+ * @link https://secure.php.net/mb_substr
+ * @param string $str
+ * @param int $start
+ * @param int $length
+ * @param string $encoding
+ * @return string
+ */
+ function mb_substr($str, $start, $length = NULL, $encoding = NULL)
+ {
+ if (ICONV_ENABLED === TRUE)
+ {
+ isset($encoding) OR $encoding = config_item('charset');
+ return iconv_substr(
+ $str,
+ $start,
+ isset($length) ? $length : iconv_strlen($str, $encoding), // NULL doesn't work
+ $encoding
+ );
+ }
+
+ log_message('debug', 'Compatibility (mbstring): iconv_substr() is not available, falling back to substr().');
+ return isset($length)
+ ? substr($str, $start, $length)
+ : substr($str, $start);
+ }
+}
diff --git a/system/core/compat/password.php b/system/core/compat/password.php
new file mode 100644
index 000000000..3aae48576
--- /dev/null
+++ b/system/core/compat/password.php
@@ -0,0 +1,252 @@
+<?php
+/**
+ * CodeIgniter
+ *
+ * An open source application development framework for PHP
+ *
+ * This content is released under the MIT License (MIT)
+ *
+ * Copyright (c) 2019 - 2022, CodeIgniter Foundation
+ *
+ * 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.
+ *
+ * @package CodeIgniter
+ * @author EllisLab Dev Team
+ * @copyright Copyright (c) 2008 - 2014, EllisLab, Inc. (https://ellislab.com/)
+ * @copyright Copyright (c) 2014 - 2019, British Columbia Institute of Technology (https://bcit.ca/)
+ * @copyright Copyright (c) 2019 - 2022, CodeIgniter Foundation (https://codeigniter.com/)
+ * @license https://opensource.org/licenses/MIT MIT License
+ * @link https://codeigniter.com
+ * @since Version 3.0.0
+ * @filesource
+ */
+defined('BASEPATH') OR exit('No direct script access allowed');
+
+/**
+ * PHP ext/standard/password compatibility package
+ *
+ * @package CodeIgniter
+ * @subpackage CodeIgniter
+ * @category Compatibility
+ * @author Andrey Andreev
+ * @link https://codeigniter.com/userguide3/
+ * @link https://secure.php.net/password
+ */
+
+// ------------------------------------------------------------------------
+
+if (is_php('5.5') OR ! defined('CRYPT_BLOWFISH') OR CRYPT_BLOWFISH !== 1 OR defined('HHVM_VERSION'))
+{
+ return;
+}
+
+// ------------------------------------------------------------------------
+
+defined('PASSWORD_BCRYPT') OR define('PASSWORD_BCRYPT', 1);
+defined('PASSWORD_DEFAULT') OR define('PASSWORD_DEFAULT', PASSWORD_BCRYPT);
+
+// ------------------------------------------------------------------------
+
+if ( ! function_exists('password_get_info'))
+{
+ /**
+ * password_get_info()
+ *
+ * @link https://secure.php.net/password_get_info
+ * @param string $hash
+ * @return array
+ */
+ function password_get_info($hash)
+ {
+ return (strlen($hash) < 60 OR sscanf($hash, '$2y$%d', $hash) !== 1)
+ ? array('algo' => 0, 'algoName' => 'unknown', 'options' => array())
+ : array('algo' => 1, 'algoName' => 'bcrypt', 'options' => array('cost' => $hash));
+ }
+}
+
+// ------------------------------------------------------------------------
+
+if ( ! function_exists('password_hash'))
+{
+ /**
+ * password_hash()
+ *
+ * @link https://secure.php.net/password_hash
+ * @param string $password
+ * @param int $algo
+ * @param array $options
+ * @return mixed
+ */
+ function password_hash($password, $algo, array $options = array())
+ {
+ static $func_overload;
+ isset($func_overload) OR $func_overload = (extension_loaded('mbstring') && ini_get('mbstring.func_overload'));
+
+ if ($algo !== 1)
+ {
+ trigger_error('password_hash(): Unknown hashing algorithm: '.(int) $algo, E_USER_WARNING);
+ return NULL;
+ }
+
+ if (isset($options['cost']) && ($options['cost'] < 4 OR $options['cost'] > 31))
+ {
+ trigger_error('password_hash(): Invalid bcrypt cost parameter specified: '.(int) $options['cost'], E_USER_WARNING);
+ return NULL;
+ }
+
+ if (isset($options['salt']) && ($saltlen = ($func_overload ? mb_strlen($options['salt'], '8bit') : strlen($options['salt']))) < 22)
+ {
+ trigger_error('password_hash(): Provided salt is too short: '.$saltlen.' expecting 22', E_USER_WARNING);
+ return NULL;
+ }
+ elseif ( ! isset($options['salt']))
+ {
+ if (function_exists('random_bytes'))
+ {
+ try
+ {
+ $options['salt'] = random_bytes(16);
+ }
+ catch (Exception $e)
+ {
+ log_message('error', 'compat/password: Error while trying to use random_bytes(): '.$e->getMessage());
+ return FALSE;
+ }
+ }
+ elseif (defined('MCRYPT_DEV_URANDOM'))
+ {
+ $options['salt'] = mcrypt_create_iv(16, MCRYPT_DEV_URANDOM);
+ }
+ elseif (DIRECTORY_SEPARATOR === '/' && (is_readable($dev = '/dev/arandom') OR is_readable($dev = '/dev/urandom')))
+ {
+ if (($fp = fopen($dev, 'rb')) === FALSE)
+ {
+ log_message('error', 'compat/password: Unable to open '.$dev.' for reading.');
+ return FALSE;
+ }
+
+ // Try not to waste entropy ...
+ stream_set_chunk_size($fp, 16);
+
+ $options['salt'] = '';
+ for ($read = 0; $read < 16; $read = ($func_overload) ? mb_strlen($options['salt'], '8bit') : strlen($options['salt']))
+ {
+ if (($read = fread($fp, 16 - $read)) === FALSE)
+ {
+ log_message('error', 'compat/password: Error while reading from '.$dev.'.');
+ return FALSE;
+ }
+ $options['salt'] .= $read;
+ }
+
+ fclose($fp);
+ }
+ elseif (function_exists('openssl_random_pseudo_bytes'))
+ {
+ $is_secure = NULL;
+ $options['salt'] = openssl_random_pseudo_bytes(16, $is_secure);
+ if ($is_secure !== TRUE)
+ {
+ log_message('error', 'compat/password: openssl_random_pseudo_bytes() set the $cryto_strong flag to FALSE');
+ return FALSE;
+ }
+ }
+ else
+ {
+ log_message('error', 'compat/password: No CSPRNG available.');
+ return FALSE;
+ }
+
+ $options['salt'] = str_replace('+', '.', rtrim(base64_encode($options['salt']), '='));
+ }
+ elseif ( ! preg_match('#^[a-zA-Z0-9./]+$#D', $options['salt']))
+ {
+ $options['salt'] = str_replace('+', '.', rtrim(base64_encode($options['salt']), '='));
+ }
+
+ isset($options['cost']) OR $options['cost'] = 10;
+
+ return (strlen($password = crypt($password, sprintf('$2y$%02d$%s', $options['cost'], $options['salt']))) === 60)
+ ? $password
+ : FALSE;
+ }
+}
+
+// ------------------------------------------------------------------------
+
+if ( ! function_exists('password_needs_rehash'))
+{
+ /**
+ * password_needs_rehash()
+ *
+ * @link https://secure.php.net/password_needs_rehash
+ * @param string $hash
+ * @param int $algo
+ * @param array $options
+ * @return bool
+ */
+ function password_needs_rehash($hash, $algo, array $options = array())
+ {
+ $info = password_get_info($hash);
+
+ if ($algo !== $info['algo'])
+ {
+ return TRUE;
+ }
+ elseif ($algo === 1)
+ {
+ $options['cost'] = isset($options['cost']) ? (int) $options['cost'] : 10;
+ return ($info['options']['cost'] !== $options['cost']);
+ }
+
+ // Odd at first glance, but according to a comment in PHP's own unit tests,
+ // because it is an unknown algorithm - it's valid and therefore doesn't
+ // need rehashing.
+ return FALSE;
+ }
+}
+
+// ------------------------------------------------------------------------
+
+if ( ! function_exists('password_verify'))
+{
+ /**
+ * password_verify()
+ *
+ * @link https://secure.php.net/password_verify
+ * @param string $password
+ * @param string $hash
+ * @return bool
+ */
+ function password_verify($password, $hash)
+ {
+ if (strlen($hash) !== 60 OR strlen($password = crypt($password, $hash)) !== 60)
+ {
+ return FALSE;
+ }
+
+ $compare = 0;
+ for ($i = 0; $i < 60; $i++)
+ {
+ $compare |= (ord($password[$i]) ^ ord($hash[$i]));
+ }
+
+ return ($compare === 0);
+ }
+}
diff --git a/system/core/compat/standard.php b/system/core/compat/standard.php
new file mode 100644
index 000000000..a6a1a9cee
--- /dev/null
+++ b/system/core/compat/standard.php
@@ -0,0 +1,135 @@
+<?php
+/**
+ * CodeIgniter
+ *
+ * An open source application development framework for PHP
+ *
+ * This content is released under the MIT License (MIT)
+ *
+ * Copyright (c) 2019 - 2022, CodeIgniter Foundation
+ *
+ * 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.
+ *
+ * @package CodeIgniter
+ * @author EllisLab Dev Team
+ * @copyright Copyright (c) 2008 - 2014, EllisLab, Inc. (https://ellislab.com/)
+ * @copyright Copyright (c) 2014 - 2019, British Columbia Institute of Technology (https://bcit.ca/)
+ * @copyright Copyright (c) 2019 - 2022, CodeIgniter Foundation (https://codeigniter.com/)
+ * @license https://opensource.org/licenses/MIT MIT License
+ * @link https://codeigniter.com
+ * @since Version 3.0.0
+ * @filesource
+ */
+defined('BASEPATH') OR exit('No direct script access allowed');
+
+/**
+ * PHP ext/standard compatibility package
+ *
+ * @package CodeIgniter
+ * @subpackage CodeIgniter
+ * @category Compatibility
+ * @author Andrey Andreev
+ * @link https://codeigniter.com/userguide3/
+ */
+
+// ------------------------------------------------------------------------
+
+if (is_php('5.5'))
+{
+ return;
+}
+
+// ------------------------------------------------------------------------
+
+if ( ! function_exists('array_column'))
+{
+ /**
+ * array_column()
+ *
+ * @link https://secure.php.net/array_column
+ * @param array $array
+ * @param mixed $column_key
+ * @param mixed $index_key
+ * @return array
+ */
+ function array_column(array $array, $column_key, $index_key = NULL)
+ {
+ if ( ! in_array($type = gettype($column_key), array('integer', 'string', 'NULL'), TRUE))
+ {
+ if ($type === 'double')
+ {
+ $column_key = (int) $column_key;
+ }
+ elseif ($type === 'object' && method_exists($column_key, '__toString'))
+ {
+ $column_key = (string) $column_key;
+ }
+ else
+ {
+ trigger_error('array_column(): The column key should be either a string or an integer', E_USER_WARNING);
+ return FALSE;
+ }
+ }
+
+ if ( ! in_array($type = gettype($index_key), array('integer', 'string', 'NULL'), TRUE))
+ {
+ if ($type === 'double')
+ {
+ $index_key = (int) $index_key;
+ }
+ elseif ($type === 'object' && method_exists($index_key, '__toString'))
+ {
+ $index_key = (string) $index_key;
+ }
+ else
+ {
+ trigger_error('array_column(): The index key should be either a string or an integer', E_USER_WARNING);
+ return FALSE;
+ }
+ }
+
+ $result = array();
+ foreach ($array as &$a)
+ {
+ if ($column_key === NULL)
+ {
+ $value = $a;
+ }
+ elseif (is_array($a) && array_key_exists($column_key, $a))
+ {
+ $value = $a[$column_key];
+ }
+ else
+ {
+ continue;
+ }
+
+ if ($index_key === NULL OR ! array_key_exists($index_key, $a))
+ {
+ $result[] = $value;
+ }
+ else
+ {
+ $result[$a[$index_key]] = $value;
+ }
+ }
+
+ return $result;
+ }
+}
diff --git a/system/core/index.html b/system/core/index.html
index c942a79ce..bcb7cae34 100644
--- a/system/core/index.html
+++ b/system/core/index.html
@@ -1,4 +1,5 @@
-<html>
+<!DOCTYPE html>
+<html lang="en">
<head>
<title>403 Forbidden</title>
</head>
@@ -7,4 +8,4 @@
<p>Directory access is forbidden.</p>
</body>
-</html> \ No newline at end of file
+</html>
diff --git a/system/database/DB.php b/system/database/DB.php
index d74738a03..d029054b6 100644
--- a/system/database/DB.php
+++ b/system/database/DB.php
@@ -1,100 +1,140 @@
-<?php if ( ! defined('BASEPATH')) exit('No direct script access allowed');
+<?php
/**
* CodeIgniter
*
- * An open source application development framework for PHP 5.1.6 or newer
+ * An open source application development framework for PHP
*
- * @package CodeIgniter
- * @author ExpressionEngine Dev Team
- * @copyright Copyright (c) 2008 - 2014, EllisLab, Inc.
- * @license http://codeigniter.com/user_guide/license.html
- * @link http://codeigniter.com
- * @since Version 1.0
+ * This content is released under the MIT License (MIT)
+ *
+ * Copyright (c) 2019 - 2022, CodeIgniter Foundation
+ *
+ * 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.
+ *
+ * @package CodeIgniter
+ * @author EllisLab Dev Team
+ * @copyright Copyright (c) 2008 - 2014, EllisLab, Inc. (https://ellislab.com/)
+ * @copyright Copyright (c) 2014 - 2019, British Columbia Institute of Technology (https://bcit.ca/)
+ * @copyright Copyright (c) 2019 - 2022, CodeIgniter Foundation (https://codeigniter.com/)
+ * @license https://opensource.org/licenses/MIT MIT License
+ * @link https://codeigniter.com
+ * @since Version 1.0.0
* @filesource
*/
-
-// ------------------------------------------------------------------------
+defined('BASEPATH') OR exit('No direct script access allowed');
/**
* Initialize the database
*
* @category Database
- * @author ExpressionEngine Dev Team
- * @link http://codeigniter.com/user_guide/database/
- * @param string
- * @param bool Determines if active record should be used or not
+ * @author EllisLab Dev Team
+ * @link https://codeigniter.com/userguide3/database/
+ *
+ * @param string|string[] $params
*/
-function &DB($params = '', $active_record_override = NULL)
+function &DB($params = '')
{
// Load the DB config file if a DSN string wasn't passed
- if (is_string($params) AND strpos($params, '://') === FALSE)
+ if (is_string($params) && strpos($params, '://') === FALSE)
{
// Is the config file in the environment folder?
- if ( ! defined('ENVIRONMENT') OR ! file_exists($file_path = APPPATH.'config/'.ENVIRONMENT.'/database.php'))
+ if ( ! file_exists($file_path = APPPATH.'config/'.ENVIRONMENT.'/database.php')
+ && ! file_exists($file_path = APPPATH.'config/database.php'))
{
- if ( ! file_exists($file_path = APPPATH.'config/database.php'))
- {
- show_error('The configuration file database.php does not exist.');
- }
+ show_error('The configuration file database.php does not exist.');
}
include($file_path);
- if ( ! isset($db) OR count($db) == 0)
+ // Make packages contain database config files,
+ // given that the controller instance already exists
+ if (class_exists('CI_Controller', FALSE))
+ {
+ foreach (get_instance()->load->get_package_paths() as $path)
+ {
+ if ($path !== APPPATH)
+ {
+ if (file_exists($file_path = $path.'config/'.ENVIRONMENT.'/database.php'))
+ {
+ include($file_path);
+ }
+ elseif (file_exists($file_path = $path.'config/database.php'))
+ {
+ include($file_path);
+ }
+ }
+ }
+ }
+
+ if (empty($db))
{
show_error('No database connection settings were found in the database config file.');
}
- if ($params != '')
+ if ($params !== '')
{
$active_group = $params;
}
- if ( ! isset($active_group) OR ! isset($db[$active_group]))
+ if ( ! isset($active_group))
{
- show_error('You have specified an invalid database connection group.');
+ show_error('You have not specified a database connection group via $active_group in your config/database.php file.');
+ }
+ elseif ( ! isset($db[$active_group]))
+ {
+ show_error('You have specified an invalid database connection group ('.$active_group.') in your config/database.php file.');
}
$params = $db[$active_group];
}
elseif (is_string($params))
{
-
- /* parse the URL from the DSN string
- * Database settings can be passed as discreet
- * parameters or as a data source name in the first
- * parameter. DSNs must have this prototype:
- * $dsn = 'driver://username:password@hostname/database';
+ /**
+ * Parse the URL from the DSN string
+ * Database settings can be passed as discreet
+ * parameters or as a data source name in the first
+ * parameter. DSNs must have this prototype:
+ * $dsn = 'driver://username:password@hostname/database';
*/
-
- if (($dns = @parse_url($params)) === FALSE)
+ if (($dsn = @parse_url($params)) === FALSE)
{
show_error('Invalid DB Connection String');
}
$params = array(
- 'dbdriver' => $dns['scheme'],
- 'hostname' => (isset($dns['host'])) ? rawurldecode($dns['host']) : '',
- 'username' => (isset($dns['user'])) ? rawurldecode($dns['user']) : '',
- 'password' => (isset($dns['pass'])) ? rawurldecode($dns['pass']) : '',
- 'database' => (isset($dns['path'])) ? rawurldecode(substr($dns['path'], 1)) : ''
- );
-
- // were additional config items set?
- if (isset($dns['query']))
+ 'dbdriver' => $dsn['scheme'],
+ 'hostname' => isset($dsn['host']) ? rawurldecode($dsn['host']) : '',
+ 'port' => isset($dsn['port']) ? rawurldecode($dsn['port']) : '',
+ 'username' => isset($dsn['user']) ? rawurldecode($dsn['user']) : '',
+ 'password' => isset($dsn['pass']) ? rawurldecode($dsn['pass']) : '',
+ 'database' => isset($dsn['path']) ? rawurldecode(substr($dsn['path'], 1)) : ''
+ );
+
+ // Were additional config items set?
+ if (isset($dsn['query']))
{
- parse_str($dns['query'], $extra);
+ parse_str($dsn['query'], $extra);
foreach ($extra as $key => $val)
{
- // booleans please
- if (strtoupper($val) == "TRUE")
- {
- $val = TRUE;
- }
- elseif (strtoupper($val) == "FALSE")
+ if (is_string($val) && in_array(strtoupper($val), array('TRUE', 'FALSE', 'NULL')))
{
- $val = FALSE;
+ $val = var_export($val, TRUE);
}
$params[$key] = $val;
@@ -102,61 +142,53 @@ function &DB($params = '', $active_record_override = NULL)
}
}
- // No DB specified yet? Beat them senseless...
- if ( ! isset($params['dbdriver']) OR $params['dbdriver'] == '')
+ // No DB specified yet? Beat them senseless...
+ if (empty($params['dbdriver']))
{
show_error('You have not selected a database type to connect to.');
}
- // Load the DB classes. Note: Since the active record class is optional
- // we need to dynamically create a class that extends proper parent class
- // based on whether we're using the active record class or not.
- // Kudos to Paul for discovering this clever use of eval()
-
- if ($active_record_override !== NULL)
- {
- $active_record = $active_record_override;
- }
-
require_once(BASEPATH.'database/DB_driver.php');
-
- if ( ! isset($active_record) OR $active_record == TRUE)
+ require_once(BASEPATH.'database/DB_query_builder.php');
+ if ( ! class_exists('CI_DB', FALSE))
{
- require_once(BASEPATH.'database/DB_active_rec.php');
-
- if ( ! class_exists('CI_DB'))
- {
- eval('class CI_DB extends CI_DB_active_record { }');
- }
- }
- else
- {
- if ( ! class_exists('CI_DB'))
- {
- eval('class CI_DB extends CI_DB_driver { }');
- }
+ /**
+ * CI_DB
+ *
+ * Acts as an alias for both CI_DB_driver and CI_DB_query_builder.
+ *
+ * @see CI_DB_query_builder
+ * @see CI_DB_driver
+ */
+ class CI_DB extends CI_DB_query_builder {}
}
- require_once(BASEPATH.'database/drivers/'.$params['dbdriver'].'/'.$params['dbdriver'].'_driver.php');
+ // Load the DB driver
+ $driver_file = BASEPATH.'database/drivers/'.$params['dbdriver'].'/'.$params['dbdriver'].'_driver.php';
+ file_exists($driver_file) OR show_error('Invalid DB driver');
+ require_once($driver_file);
+
+ // Load the result classes as well
+ require_once(BASEPATH.'database/DB_result.php');
+ require_once(BASEPATH.'database/drivers/'.$params['dbdriver'].'/'.$params['dbdriver'].'_result.php');
// Instantiate the DB adapter
$driver = 'CI_DB_'.$params['dbdriver'].'_driver';
$DB = new $driver($params);
- if ($DB->autoinit == TRUE)
+ // Check for a subdriver
+ if ( ! empty($DB->subdriver))
{
- $DB->initialize();
- }
+ $driver_file = BASEPATH.'database/drivers/'.$DB->dbdriver.'/subdrivers/'.$DB->dbdriver.'_'.$DB->subdriver.'_driver.php';
- if (isset($params['stricton']) && $params['stricton'] == TRUE)
- {
- $DB->query('SET SESSION sql_mode="STRICT_ALL_TABLES"');
+ if (file_exists($driver_file))
+ {
+ require_once($driver_file);
+ $driver = 'CI_DB_'.$DB->dbdriver.'_'.$DB->subdriver.'_driver';
+ $DB = new $driver($params);
+ }
}
+ $DB->initialize();
return $DB;
}
-
-
-
-/* End of file DB.php */
-/* Location: ./system/database/DB.php */ \ No newline at end of file
diff --git a/system/database/DB_active_rec.php b/system/database/DB_active_rec.php
deleted file mode 100644
index 811df97fb..000000000
--- a/system/database/DB_active_rec.php
+++ /dev/null
@@ -1,2045 +0,0 @@
-<?php if ( ! defined('BASEPATH')) exit('No direct script access allowed');
-/**
- * CodeIgniter
- *
- * An open source application development framework for PHP 5.1.6 or newer
- *
- * @package CodeIgniter
- * @author ExpressionEngine Dev Team
- * @copyright Copyright (c) 2008 - 2014, EllisLab, Inc.
- * @license http://codeigniter.com/user_guide/license.html
- * @link http://codeigniter.com
- * @since Version 1.0
- * @filesource
- */
-
-// ------------------------------------------------------------------------
-
-/**
- * Active Record Class
- *
- * This is the platform-independent base Active Record implementation class.
- *
- * @package CodeIgniter
- * @subpackage Drivers
- * @category Database
- * @author ExpressionEngine Dev Team
- * @link http://codeigniter.com/user_guide/database/
- */
-class CI_DB_active_record extends CI_DB_driver {
-
- var $ar_select = array();
- var $ar_distinct = FALSE;
- var $ar_from = array();
- var $ar_join = array();
- var $ar_where = array();
- var $ar_like = array();
- var $ar_groupby = array();
- var $ar_having = array();
- var $ar_keys = array();
- var $ar_limit = FALSE;
- var $ar_offset = FALSE;
- var $ar_order = FALSE;
- var $ar_orderby = array();
- var $ar_set = array();
- var $ar_wherein = array();
- var $ar_aliased_tables = array();
- var $ar_store_array = array();
-
- // Active Record Caching variables
- var $ar_caching = FALSE;
- var $ar_cache_exists = array();
- var $ar_cache_select = array();
- var $ar_cache_from = array();
- var $ar_cache_join = array();
- var $ar_cache_where = array();
- var $ar_cache_like = array();
- var $ar_cache_groupby = array();
- var $ar_cache_having = array();
- var $ar_cache_orderby = array();
- var $ar_cache_set = array();
-
- var $ar_no_escape = array();
- var $ar_cache_no_escape = array();
-
- // --------------------------------------------------------------------
-
- /**
- * Select
- *
- * Generates the SELECT portion of the query
- *
- * @param string
- * @return object
- */
- public function select($select = '*', $escape = NULL)
- {
- if (is_string($select))
- {
- $select = explode(',', $select);
- }
-
- foreach ($select as $val)
- {
- $val = trim($val);
-
- if ($val != '')
- {
- $this->ar_select[] = $val;
- $this->ar_no_escape[] = $escape;
-
- if ($this->ar_caching === TRUE)
- {
- $this->ar_cache_select[] = $val;
- $this->ar_cache_exists[] = 'select';
- $this->ar_cache_no_escape[] = $escape;
- }
- }
- }
- return $this;
- }
-
- // --------------------------------------------------------------------
-
- /**
- * Select Max
- *
- * Generates a SELECT MAX(field) portion of a query
- *
- * @param string the field
- * @param string an alias
- * @return object
- */
- public function select_max($select = '', $alias = '')
- {
- return $this->_max_min_avg_sum($select, $alias, 'MAX');
- }
-
- // --------------------------------------------------------------------
-
- /**
- * Select Min
- *
- * Generates a SELECT MIN(field) portion of a query
- *
- * @param string the field
- * @param string an alias
- * @return object
- */
- public function select_min($select = '', $alias = '')
- {
- return $this->_max_min_avg_sum($select, $alias, 'MIN');
- }
-
- // --------------------------------------------------------------------
-
- /**
- * Select Average
- *
- * Generates a SELECT AVG(field) portion of a query
- *
- * @param string the field
- * @param string an alias
- * @return object
- */
- public function select_avg($select = '', $alias = '')
- {
- return $this->_max_min_avg_sum($select, $alias, 'AVG');
- }
-
- // --------------------------------------------------------------------
-
- /**
- * Select Sum
- *
- * Generates a SELECT SUM(field) portion of a query
- *
- * @param string the field
- * @param string an alias
- * @return object
- */
- public function select_sum($select = '', $alias = '')
- {
- return $this->_max_min_avg_sum($select, $alias, 'SUM');
- }
-
- // --------------------------------------------------------------------
-
- /**
- * Processing Function for the four functions above:
- *
- * select_max()
- * select_min()
- * select_avg()
- * select_sum()
- *
- * @param string the field
- * @param string an alias
- * @return object
- */
- protected function _max_min_avg_sum($select = '', $alias = '', $type = 'MAX')
- {
- if ( ! is_string($select) OR $select == '')
- {
- $this->display_error('db_invalid_query');
- }
-
- $type = strtoupper($type);
-
- if ( ! in_array($type, array('MAX', 'MIN', 'AVG', 'SUM')))
- {
- show_error('Invalid function type: '.$type);
- }
-
- if ($alias == '')
- {
- $alias = $this->_create_alias_from_table(trim($select));
- }
-
- $sql = $type.'('.$this->_protect_identifiers(trim($select)).') AS '.$alias;
-
- $this->ar_select[] = $sql;
-
- if ($this->ar_caching === TRUE)
- {
- $this->ar_cache_select[] = $sql;
- $this->ar_cache_exists[] = 'select';
- }
-
- return $this;
- }
-
- // --------------------------------------------------------------------
-
- /**
- * Determines the alias name based on the table
- *
- * @param string
- * @return string
- */
- protected function _create_alias_from_table($item)
- {
- if (strpos($item, '.') !== FALSE)
- {
- return end(explode('.', $item));
- }
-
- return $item;
- }
-
- // --------------------------------------------------------------------
-
- /**
- * DISTINCT
- *
- * Sets a flag which tells the query string compiler to add DISTINCT
- *
- * @param bool
- * @return object
- */
- public function distinct($val = TRUE)
- {
- $this->ar_distinct = (is_bool($val)) ? $val : TRUE;
- return $this;
- }
-
- // --------------------------------------------------------------------
-
- /**
- * From
- *
- * Generates the FROM portion of the query
- *
- * @param mixed can be a string or array
- * @return object
- */
- public function from($from)
- {
- foreach ((array)$from as $val)
- {
- if (strpos($val, ',') !== FALSE)
- {
- foreach (explode(',', $val) as $v)
- {
- $v = trim($v);
- $this->_track_aliases($v);
-
- $this->ar_from[] = $this->_protect_identifiers($v, TRUE, NULL, FALSE);
-
- if ($this->ar_caching === TRUE)
- {
- $this->ar_cache_from[] = $this->_protect_identifiers($v, TRUE, NULL, FALSE);
- $this->ar_cache_exists[] = 'from';
- }
- }
-
- }
- else
- {
- $val = trim($val);
-
- // Extract any aliases that might exist. We use this information
- // in the _protect_identifiers to know whether to add a table prefix
- $this->_track_aliases($val);
-
- $this->ar_from[] = $this->_protect_identifiers($val, TRUE, NULL, FALSE);
-
- if ($this->ar_caching === TRUE)
- {
- $this->ar_cache_from[] = $this->_protect_identifiers($val, TRUE, NULL, FALSE);
- $this->ar_cache_exists[] = 'from';
- }
- }
- }
-
- return $this;
- }
-
- // --------------------------------------------------------------------
-
- /**
- * Join
- *
- * Generates the JOIN portion of the query
- *
- * @param string
- * @param string the join condition
- * @param string the type of join
- * @return object
- */
- public function join($table, $cond, $type = '')
- {
- if ($type != '')
- {
- $type = strtoupper(trim($type));
-
- if ( ! in_array($type, array('LEFT', 'RIGHT', 'OUTER', 'INNER', 'LEFT OUTER', 'RIGHT OUTER')))
- {
- $type = '';
- }
- else
- {
- $type .= ' ';
- }
- }
-
- // Extract any aliases that might exist. We use this information
- // in the _protect_identifiers to know whether to add a table prefix
- $this->_track_aliases($table);
-
- // Strip apart the condition and protect the identifiers
- if (preg_match('/([\w\.]+)([\W\s]+)(.+)/', $cond, $match))
- {
- $match[1] = $this->_protect_identifiers($match[1]);
- $match[3] = $this->_protect_identifiers($match[3]);
-
- $cond = $match[1].$match[2].$match[3];
- }
-
- // Assemble the JOIN statement
- $join = $type.'JOIN '.$this->_protect_identifiers($table, TRUE, NULL, FALSE).' ON '.$cond;
-
- $this->ar_join[] = $join;
- if ($this->ar_caching === TRUE)
- {
- $this->ar_cache_join[] = $join;
- $this->ar_cache_exists[] = 'join';
- }
-
- return $this;
- }
-
- // --------------------------------------------------------------------
-
- /**
- * Where
- *
- * Generates the WHERE portion of the query. Separates
- * multiple calls with AND
- *
- * @param mixed
- * @param mixed
- * @return object
- */
- public function where($key, $value = NULL, $escape = TRUE)
- {
- return $this->_where($key, $value, 'AND ', $escape);
- }
-
- // --------------------------------------------------------------------
-
- /**
- * OR Where
- *
- * Generates the WHERE portion of the query. Separates
- * multiple calls with OR
- *
- * @param mixed
- * @param mixed
- * @return object
- */
- public function or_where($key, $value = NULL, $escape = TRUE)
- {
- return $this->_where($key, $value, 'OR ', $escape);
- }
-
- // --------------------------------------------------------------------
-
- /**
- * Where
- *
- * Called by where() or or_where()
- *
- * @param mixed
- * @param mixed
- * @param string
- * @return object
- */
- protected function _where($key, $value = NULL, $type = 'AND ', $escape = NULL)
- {
- if ( ! is_array($key))
- {
- $key = array($key => $value);
- }
-
- // If the escape value was not set will will base it on the global setting
- if ( ! is_bool($escape))
- {
- $escape = $this->_protect_identifiers;
- }
-
- foreach ($key as $k => $v)
- {
- $prefix = (count($this->ar_where) == 0 AND count($this->ar_cache_where) == 0) ? '' : $type;
-
- if (is_null($v) && ! $this->_has_operator($k))
- {
- // value appears not to have been set, assign the test to IS NULL
- $k .= ' IS NULL';
- }
-
- if ( ! is_null($v))
- {
- if ($escape === TRUE)
- {
- $k = $this->_protect_identifiers($k, FALSE, $escape);
-
- $v = ' '.$this->escape($v);
- }
-
- if ( ! $this->_has_operator($k))
- {
- $k .= ' = ';
- }
- }
- else
- {
- $k = $this->_protect_identifiers($k, FALSE, $escape);
- }
-
- $this->ar_where[] = $prefix.$k.$v;
-
- if ($this->ar_caching === TRUE)
- {
- $this->ar_cache_where[] = $prefix.$k.$v;
- $this->ar_cache_exists[] = 'where';
- }
-
- }
-
- return $this;
- }
-
- // --------------------------------------------------------------------
-
- /**
- * Where_in
- *
- * Generates a WHERE field IN ('item', 'item') SQL query joined with
- * AND if appropriate
- *
- * @param string The field to search
- * @param array The values searched on
- * @return object
- */
- public function where_in($key = NULL, $values = NULL)
- {
- return $this->_where_in($key, $values);
- }
-
- // --------------------------------------------------------------------
-
- /**
- * Where_in_or
- *
- * Generates a WHERE field IN ('item', 'item') SQL query joined with
- * OR if appropriate
- *
- * @param string The field to search
- * @param array The values searched on
- * @return object
- */
- public function or_where_in($key = NULL, $values = NULL)
- {
- return $this->_where_in($key, $values, FALSE, 'OR ');
- }
-
- // --------------------------------------------------------------------
-
- /**
- * Where_not_in
- *
- * Generates a WHERE field NOT IN ('item', 'item') SQL query joined
- * with AND if appropriate
- *
- * @param string The field to search
- * @param array The values searched on
- * @return object
- */
- public function where_not_in($key = NULL, $values = NULL)
- {
- return $this->_where_in($key, $values, TRUE);
- }
-
- // --------------------------------------------------------------------
-
- /**
- * Where_not_in_or
- *
- * Generates a WHERE field NOT IN ('item', 'item') SQL query joined
- * with OR if appropriate
- *
- * @param string The field to search
- * @param array The values searched on
- * @return object
- */
- public function or_where_not_in($key = NULL, $values = NULL)
- {
- return $this->_where_in($key, $values, TRUE, 'OR ');
- }
-
- // --------------------------------------------------------------------
-
- /**
- * Where_in
- *
- * Called by where_in, where_in_or, where_not_in, where_not_in_or
- *
- * @param string The field to search
- * @param array The values searched on
- * @param boolean If the statement would be IN or NOT IN
- * @param string
- * @return object
- */
- protected function _where_in($key = NULL, $values = NULL, $not = FALSE, $type = 'AND ')
- {
- if ($key === NULL OR $values === NULL)
- {
- return;
- }
-
- if ( ! is_array($values))
- {
- $values = array($values);
- }
-
- $not = ($not) ? ' NOT' : '';
-
- foreach ($values as $value)
- {
- $this->ar_wherein[] = $this->escape($value);
- }
-
- $prefix = (count($this->ar_where) == 0) ? '' : $type;
-
- $where_in = $prefix . $this->_protect_identifiers($key) . $not . " IN (" . implode(", ", $this->ar_wherein) . ") ";
-
- $this->ar_where[] = $where_in;
- if ($this->ar_caching === TRUE)
- {
- $this->ar_cache_where[] = $where_in;
- $this->ar_cache_exists[] = 'where';
- }
-
- // reset the array for multiple calls
- $this->ar_wherein = array();
- return $this;
- }
-
- // --------------------------------------------------------------------
-
- /**
- * Like
- *
- * Generates a %LIKE% portion of the query. Separates
- * multiple calls with AND
- *
- * @param mixed
- * @param mixed
- * @return object
- */
- public function like($field, $match = '', $side = 'both')
- {
- return $this->_like($field, $match, 'AND ', $side);
- }
-
- // --------------------------------------------------------------------
-
- /**
- * Not Like
- *
- * Generates a NOT LIKE portion of the query. Separates
- * multiple calls with AND
- *
- * @param mixed
- * @param mixed
- * @return object
- */
- public function not_like($field, $match = '', $side = 'both')
- {
- return $this->_like($field, $match, 'AND ', $side, 'NOT');
- }
-
- // --------------------------------------------------------------------
-
- /**
- * OR Like
- *
- * Generates a %LIKE% portion of the query. Separates
- * multiple calls with OR
- *
- * @param mixed
- * @param mixed
- * @return object
- */
- public function or_like($field, $match = '', $side = 'both')
- {
- return $this->_like($field, $match, 'OR ', $side);
- }
-
- // --------------------------------------------------------------------
-
- /**
- * OR Not Like
- *
- * Generates a NOT LIKE portion of the query. Separates
- * multiple calls with OR
- *
- * @param mixed
- * @param mixed
- * @return object
- */
- public function or_not_like($field, $match = '', $side = 'both')
- {
- return $this->_like($field, $match, 'OR ', $side, 'NOT');
- }
-
- // --------------------------------------------------------------------
-
- /**
- * Like
- *
- * Called by like() or orlike()
- *
- * @param mixed
- * @param mixed
- * @param string
- * @return object
- */
- protected function _like($field, $match = '', $type = 'AND ', $side = 'both', $not = '')
- {
- if ( ! is_array($field))
- {
- $field = array($field => $match);
- }
-
- foreach ($field as $k => $v)
- {
- $k = $this->_protect_identifiers($k);
-
- $prefix = (count($this->ar_like) == 0) ? '' : $type;
-
- $v = $this->escape_like_str($v);
-
- if ($side == 'none')
- {
- $like_statement = $prefix." $k $not LIKE '{$v}'";
- }
- elseif ($side == 'before')
- {
- $like_statement = $prefix." $k $not LIKE '%{$v}'";
- }
- elseif ($side == 'after')
- {
- $like_statement = $prefix." $k $not LIKE '{$v}%'";
- }
- else
- {
- $like_statement = $prefix." $k $not LIKE '%{$v}%'";
- }
-
- // some platforms require an escape sequence definition for LIKE wildcards
- if ($this->_like_escape_str != '')
- {
- $like_statement = $like_statement.sprintf($this->_like_escape_str, $this->_like_escape_chr);
- }
-
- $this->ar_like[] = $like_statement;
- if ($this->ar_caching === TRUE)
- {
- $this->ar_cache_like[] = $like_statement;
- $this->ar_cache_exists[] = 'like';
- }
-
- }
- return $this;
- }
-
- // --------------------------------------------------------------------
-
- /**
- * GROUP BY
- *
- * @param string
- * @return object
- */
- public function group_by($by)
- {
- if (is_string($by))
- {
- $by = explode(',', $by);
- }
-
- foreach ($by as $val)
- {
- $val = trim($val);
-
- if ($val != '')
- {
- $this->ar_groupby[] = $this->_protect_identifiers($val);
-
- if ($this->ar_caching === TRUE)
- {
- $this->ar_cache_groupby[] = $this->_protect_identifiers($val);
- $this->ar_cache_exists[] = 'groupby';
- }
- }
- }
- return $this;
- }
-
- // --------------------------------------------------------------------
-
- /**
- * Sets the HAVING value
- *
- * Separates multiple calls with AND
- *
- * @param string
- * @param string
- * @return object
- */
- public function having($key, $value = '', $escape = TRUE)
- {
- return $this->_having($key, $value, 'AND ', $escape);
- }
-
- // --------------------------------------------------------------------
-
- /**
- * Sets the OR HAVING value
- *
- * Separates multiple calls with OR
- *
- * @param string
- * @param string
- * @return object
- */
- public function or_having($key, $value = '', $escape = TRUE)
- {
- return $this->_having($key, $value, 'OR ', $escape);
- }
-
- // --------------------------------------------------------------------
-
- /**
- * Sets the HAVING values
- *
- * Called by having() or or_having()
- *
- * @param string
- * @param string
- * @return object
- */
- protected function _having($key, $value = '', $type = 'AND ', $escape = TRUE)
- {
- if ( ! is_array($key))
- {
- $key = array($key => $value);
- }
-
- foreach ($key as $k => $v)
- {
- $prefix = (count($this->ar_having) == 0) ? '' : $type;
-
- if ($escape === TRUE)
- {
- $k = $this->_protect_identifiers($k);
- }
-
- if ( ! $this->_has_operator($k))
- {
- $k .= ' = ';
- }
-
- if ($v != '')
- {
- $v = ' '.$this->escape($v);
- }
-
- $this->ar_having[] = $prefix.$k.$v;
- if ($this->ar_caching === TRUE)
- {
- $this->ar_cache_having[] = $prefix.$k.$v;
- $this->ar_cache_exists[] = 'having';
- }
- }
-
- return $this;
- }
-
- // --------------------------------------------------------------------
-
- /**
- * Sets the ORDER BY value
- *
- * @param string
- * @param string direction: asc or desc
- * @return object
- */
- public function order_by($orderby, $direction = '')
- {
- if (strtolower($direction) == 'random')
- {
- $orderby = ''; // Random results want or don't need a field name
- $direction = $this->_random_keyword;
- }
- elseif (trim($direction) != '')
- {
- $direction = (in_array(strtoupper(trim($direction)), array('ASC', 'DESC'), TRUE)) ? ' '.$direction : ' ASC';
- }
-
-
- if (strpos($orderby, ',') !== FALSE)
- {
- $temp = array();
- foreach (explode(',', $orderby) as $part)
- {
- $part = trim($part);
- if ( ! in_array($part, $this->ar_aliased_tables))
- {
- $part = $this->_protect_identifiers(trim($part));
- }
-
- $temp[] = $part;
- }
-
- $orderby = implode(', ', $temp);
- }
- else if ($direction != $this->_random_keyword)
- {
- $orderby = $this->_protect_identifiers($orderby);
- }
-
- $orderby_statement = $orderby.$direction;
-
- $this->ar_orderby[] = $orderby_statement;
- if ($this->ar_caching === TRUE)
- {
- $this->ar_cache_orderby[] = $orderby_statement;
- $this->ar_cache_exists[] = 'orderby';
- }
-
- return $this;
- }
-
- // --------------------------------------------------------------------
-
- /**
- * Sets the LIMIT value
- *
- * @param integer the limit value
- * @param integer the offset value
- * @return object
- */
- public function limit($value, $offset = '')
- {
- $this->ar_limit = (int) $value;
-
- if ($offset != '')
- {
- $this->ar_offset = (int) $offset;
- }
-
- return $this;
- }
-
- // --------------------------------------------------------------------
-
- /**
- * Sets the OFFSET value
- *
- * @param integer the offset value
- * @return object
- */
- public function offset($offset)
- {
- $this->ar_offset = $offset;
- return $this;
- }
-
- // --------------------------------------------------------------------
-
- /**
- * The "set" function. Allows key/value pairs to be set for inserting or updating
- *
- * @param mixed
- * @param string
- * @param boolean
- * @return object
- */
- public function set($key, $value = '', $escape = TRUE)
- {
- $key = $this->_object_to_array($key);
-
- if ( ! is_array($key))
- {
- $key = array($key => $value);
- }
-
- foreach ($key as $k => $v)
- {
- if ($escape === FALSE)
- {
- $this->ar_set[$this->_protect_identifiers($k)] = $v;
- }
- else
- {
- $this->ar_set[$this->_protect_identifiers($k, FALSE, TRUE)] = $this->escape($v);
- }
- }
-
- return $this;
- }
-
- // --------------------------------------------------------------------
-
- /**
- * Get
- *
- * Compiles the select statement based on the other functions called
- * and runs the query
- *
- * @param string the table
- * @param string the limit clause
- * @param string the offset clause
- * @return object
- */
- public function get($table = '', $limit = null, $offset = null)
- {
- if ($table != '')
- {
- $this->_track_aliases($table);
- $this->from($table);
- }
-
- if ( ! is_null($limit))
- {
- $this->limit($limit, $offset);
- }
-
- $sql = $this->_compile_select();
-
- $result = $this->query($sql);
- $this->_reset_select();
- return $result;
- }
-
- /**
- * "Count All Results" query
- *
- * Generates a platform-specific query string that counts all records
- * returned by an Active Record query.
- *
- * @param string
- * @return string
- */
- public function count_all_results($table = '')
- {
- if ($table != '')
- {
- $this->_track_aliases($table);
- $this->from($table);
- }
-
- $sql = $this->_compile_select($this->_count_string . $this->_protect_identifiers('numrows'));
-
- $query = $this->query($sql);
- $this->_reset_select();
-
- if ($query->num_rows() == 0)
- {
- return 0;
- }
-
- $row = $query->row();
- return (int) $row->numrows;
- }
-
- // --------------------------------------------------------------------
-
- /**
- * Get_Where
- *
- * Allows the where clause, limit and offset to be added directly
- *
- * @param string the where clause
- * @param string the limit clause
- * @param string the offset clause
- * @return object
- */
- public function get_where($table = '', $where = null, $limit = null, $offset = null)
- {
- if ($table != '')
- {
- $this->from($table);
- }
-
- if ( ! is_null($where))
- {
- $this->where($where);
- }
-
- if ( ! is_null($limit))
- {
- $this->limit($limit, $offset);
- }
-
- $sql = $this->_compile_select();
-
- $result = $this->query($sql);
- $this->_reset_select();
- return $result;
- }
-
- // --------------------------------------------------------------------
-
- /**
- * Insert_Batch
- *
- * Compiles batch insert strings and runs the queries
- *
- * @param string the table to retrieve the results from
- * @param array an associative array of insert values
- * @return object
- */
- public function insert_batch($table = '', $set = NULL)
- {
- if ( ! is_null($set))
- {
- $this->set_insert_batch($set);
- }
-
- if (count($this->ar_set) == 0)
- {
- if ($this->db_debug)
- {
- //No valid data array. Folds in cases where keys and values did not match up
- return $this->display_error('db_must_use_set');
- }
- return FALSE;
- }
-
- if ($table == '')
- {
- if ( ! isset($this->ar_from[0]))
- {
- if ($this->db_debug)
- {
- return $this->display_error('db_must_set_table');
- }
- return FALSE;
- }
-
- $table = $this->ar_from[0];
- }
-
- // Batch this baby
- for ($i = 0, $total = count($this->ar_set); $i < $total; $i = $i + 100)
- {
-
- $sql = $this->_insert_batch($this->_protect_identifiers($table, TRUE, NULL, FALSE), $this->ar_keys, array_slice($this->ar_set, $i, 100));
-
- //echo $sql;
-
- $this->query($sql);
- }
-
- $this->_reset_write();
-
-
- return TRUE;
- }
-
- // --------------------------------------------------------------------
-
- /**
- * The "set_insert_batch" function. Allows key/value pairs to be set for batch inserts
- *
- * @param mixed
- * @param string
- * @param boolean
- * @return object
- */
- public function set_insert_batch($key, $value = '', $escape = TRUE)
- {
- $key = $this->_object_to_array_batch($key);
-
- if ( ! is_array($key))
- {
- $key = array($key => $value);
- }
-
- $keys = array_keys(current($key));
- sort($keys);
-
- foreach ($key as $row)
- {
- if (count(array_diff($keys, array_keys($row))) > 0 OR count(array_diff(array_keys($row), $keys)) > 0)
- {
- // batch function above returns an error on an empty array
- $this->ar_set[] = array();
- return;
- }
-
- ksort($row); // puts $row in the same order as our keys
-
- if ($escape === FALSE)
- {
- $this->ar_set[] = '('.implode(',', $row).')';
- }
- else
- {
- $clean = array();
-
- foreach ($row as $value)
- {
- $clean[] = $this->escape($value);
- }
-
- $this->ar_set[] = '('.implode(',', $clean).')';
- }
- }
-
- foreach ($keys as $k)
- {
- $this->ar_keys[] = $this->_protect_identifiers($k);
- }
-
- return $this;
- }
-
- // --------------------------------------------------------------------
-
- /**
- * Insert
- *
- * Compiles an insert string and runs the query
- *
- * @param string the table to insert data into
- * @param array an associative array of insert values
- * @return object
- */
- function insert($table = '', $set = NULL)
- {
- if ( ! is_null($set))
- {
- $this->set($set);
- }
-
- if (count($this->ar_set) == 0)
- {
- if ($this->db_debug)
- {
- return $this->display_error('db_must_use_set');
- }
- return FALSE;
- }
-
- if ($table == '')
- {
- if ( ! isset($this->ar_from[0]))
- {
- if ($this->db_debug)
- {
- return $this->display_error('db_must_set_table');
- }
- return FALSE;
- }
-
- $table = $this->ar_from[0];
- }
-
- $sql = $this->_insert($this->_protect_identifiers($table, TRUE, NULL, FALSE), array_keys($this->ar_set), array_values($this->ar_set));
-
- $this->_reset_write();
- return $this->query($sql);
- }
-
- // --------------------------------------------------------------------
-
- /**
- * Replace
- *
- * Compiles an replace into string and runs the query
- *
- * @param string the table to replace data into
- * @param array an associative array of insert values
- * @return object
- */
- public function replace($table = '', $set = NULL)
- {
- if ( ! is_null($set))
- {
- $this->set($set);
- }
-
- if (count($this->ar_set) == 0)
- {
- if ($this->db_debug)
- {
- return $this->display_error('db_must_use_set');
- }
- return FALSE;
- }
-
- if ($table == '')
- {
- if ( ! isset($this->ar_from[0]))
- {
- if ($this->db_debug)
- {
- return $this->display_error('db_must_set_table');
- }
- return FALSE;
- }
-
- $table = $this->ar_from[0];
- }
-
- $sql = $this->_replace($this->_protect_identifiers($table, TRUE, NULL, FALSE), array_keys($this->ar_set), array_values($this->ar_set));
-
- $this->_reset_write();
- return $this->query($sql);
- }
-
- // --------------------------------------------------------------------
-
- /**
- * Update
- *
- * Compiles an update string and runs the query
- *
- * @param string the table to retrieve the results from
- * @param array an associative array of update values
- * @param mixed the where clause
- * @return object
- */
- public function update($table = '', $set = NULL, $where = NULL, $limit = NULL)
- {
- // Combine any cached components with the current statements
- $this->_merge_cache();
-
- if ( ! is_null($set))
- {
- $this->set($set);
- }
-
- if (count($this->ar_set) == 0)
- {
- if ($this->db_debug)
- {
- return $this->display_error('db_must_use_set');
- }
- return FALSE;
- }
-
- if ($table == '')
- {
- if ( ! isset($this->ar_from[0]))
- {
- if ($this->db_debug)
- {
- return $this->display_error('db_must_set_table');
- }
- return FALSE;
- }
-
- $table = $this->ar_from[0];
- }
-
- if ($where != NULL)
- {
- $this->where($where);
- }
-
- if ($limit != NULL)
- {
- $this->limit($limit);
- }
-
- $sql = $this->_update($this->_protect_identifiers($table, TRUE, NULL, FALSE), $this->ar_set, $this->ar_where, $this->ar_orderby, $this->ar_limit);
-
- $this->_reset_write();
- return $this->query($sql);
- }
-
-
- // --------------------------------------------------------------------
-
- /**
- * Update_Batch
- *
- * Compiles an update string and runs the query
- *
- * @param string the table to retrieve the results from
- * @param array an associative array of update values
- * @param string the where key
- * @return object
- */
- public function update_batch($table = '', $set = NULL, $index = NULL)
- {
- // Combine any cached components with the current statements
- $this->_merge_cache();
-
- if (is_null($index))
- {
- if ($this->db_debug)
- {
- return $this->display_error('db_must_use_index');
- }
-
- return FALSE;
- }
-
- if ( ! is_null($set))
- {
- $this->set_update_batch($set, $index);
- }
-
- if (count($this->ar_set) == 0)
- {
- if ($this->db_debug)
- {
- return $this->display_error('db_must_use_set');
- }
-
- return FALSE;
- }
-
- if ($table == '')
- {
- if ( ! isset($this->ar_from[0]))
- {
- if ($this->db_debug)
- {
- return $this->display_error('db_must_set_table');
- }
- return FALSE;
- }
-
- $table = $this->ar_from[0];
- }
-
- // Batch this baby
- for ($i = 0, $total = count($this->ar_set); $i < $total; $i = $i + 100)
- {
- $sql = $this->_update_batch($this->_protect_identifiers($table, TRUE, NULL, FALSE), array_slice($this->ar_set, $i, 100), $this->_protect_identifiers($index), $this->ar_where);
-
- $this->query($sql);
- }
-
- $this->_reset_write();
- }
-
- // --------------------------------------------------------------------
-
- /**
- * The "set_update_batch" function. Allows key/value pairs to be set for batch updating
- *
- * @param array
- * @param string
- * @param boolean
- * @return object
- */
- public function set_update_batch($key, $index = '', $escape = TRUE)
- {
- $key = $this->_object_to_array_batch($key);
-
- if ( ! is_array($key))
- {
- // @todo error
- }
-
- foreach ($key as $k => $v)
- {
- $index_set = FALSE;
- $clean = array();
-
- foreach ($v as $k2 => $v2)
- {
- if ($k2 == $index)
- {
- $index_set = TRUE;
- }
- else
- {
- $not[] = $k2.'-'.$v2;
- }
-
- if ($escape === FALSE)
- {
- $clean[$this->_protect_identifiers($k2)] = $v2;
- }
- else
- {
- $clean[$this->_protect_identifiers($k2)] = $this->escape($v2);
- }
- }
-
- if ($index_set == FALSE)
- {
- return $this->display_error('db_batch_missing_index');
- }
-
- $this->ar_set[] = $clean;
- }
-
- return $this;
- }
-
- // --------------------------------------------------------------------
-
- /**
- * Empty Table
- *
- * Compiles a delete string and runs "DELETE FROM table"
- *
- * @param string the table to empty
- * @return object
- */
- public function empty_table($table = '')
- {
- if ($table == '')
- {
- if ( ! isset($this->ar_from[0]))
- {
- if ($this->db_debug)
- {
- return $this->display_error('db_must_set_table');
- }
- return FALSE;
- }
-
- $table = $this->ar_from[0];
- }
- else
- {
- $table = $this->_protect_identifiers($table, TRUE, NULL, FALSE);
- }
-
- $sql = $this->_delete($table);
-
- $this->_reset_write();
-
- return $this->query($sql);
- }
-
- // --------------------------------------------------------------------
-
- /**
- * Truncate
- *
- * Compiles a truncate string and runs the query
- * If the database does not support the truncate() command
- * This function maps to "DELETE FROM table"
- *
- * @param string the table to truncate
- * @return object
- */
- public function truncate($table = '')
- {
- if ($table == '')
- {
- if ( ! isset($this->ar_from[0]))
- {
- if ($this->db_debug)
- {
- return $this->display_error('db_must_set_table');
- }
- return FALSE;
- }
-
- $table = $this->ar_from[0];
- }
- else
- {
- $table = $this->_protect_identifiers($table, TRUE, NULL, FALSE);
- }
-
- $sql = $this->_truncate($table);
-
- $this->_reset_write();
-
- return $this->query($sql);
- }
-
- // --------------------------------------------------------------------
-
- /**
- * Delete
- *
- * Compiles a delete string and runs the query
- *
- * @param mixed the table(s) to delete from. String or array
- * @param mixed the where clause
- * @param mixed the limit clause
- * @param boolean
- * @return object
- */
- public function delete($table = '', $where = '', $limit = NULL, $reset_data = TRUE)
- {
- // Combine any cached components with the current statements
- $this->_merge_cache();
-
- if ($table == '')
- {
- if ( ! isset($this->ar_from[0]))
- {
- if ($this->db_debug)
- {
- return $this->display_error('db_must_set_table');
- }
- return FALSE;
- }
-
- $table = $this->ar_from[0];
- }
- elseif (is_array($table))
- {
- foreach ($table as $single_table)
- {
- $this->delete($single_table, $where, $limit, FALSE);
- }
-
- $this->_reset_write();
- return;
- }
- else
- {
- $table = $this->_protect_identifiers($table, TRUE, NULL, FALSE);
- }
-
- if ($where != '')
- {
- $this->where($where);
- }
-
- if ($limit != NULL)
- {
- $this->limit($limit);
- }
-
- if (count($this->ar_where) == 0 && count($this->ar_wherein) == 0 && count($this->ar_like) == 0)
- {
- if ($this->db_debug)
- {
- return $this->display_error('db_del_must_use_where');
- }
-
- return FALSE;
- }
-
- $sql = $this->_delete($table, $this->ar_where, $this->ar_like, $this->ar_limit);
-
- if ($reset_data)
- {
- $this->_reset_write();
- }
-
- return $this->query($sql);
- }
-
- // --------------------------------------------------------------------
-
- /**
- * DB Prefix
- *
- * Prepends a database prefix if one exists in configuration
- *
- * @param string the table
- * @return string
- */
- public function dbprefix($table = '')
- {
- if ($table == '')
- {
- $this->display_error('db_table_name_required');
- }
-
- return $this->dbprefix.$table;
- }
-
- // --------------------------------------------------------------------
-
- /**
- * Set DB Prefix
- *
- * Set's the DB Prefix to something new without needing to reconnect
- *
- * @param string the prefix
- * @return string
- */
- public function set_dbprefix($prefix = '')
- {
- return $this->dbprefix = $prefix;
- }
-
- // --------------------------------------------------------------------
-
- /**
- * Track Aliases
- *
- * Used to track SQL statements written with aliased tables.
- *
- * @param string The table to inspect
- * @return string
- */
- protected function _track_aliases($table)
- {
- if (is_array($table))
- {
- foreach ($table as $t)
- {
- $this->_track_aliases($t);
- }
- return;
- }
-
- // Does the string contain a comma? If so, we need to separate
- // the string into discreet statements
- if (strpos($table, ',') !== FALSE)
- {
- return $this->_track_aliases(explode(',', $table));
- }
-
- // if a table alias is used we can recognize it by a space
- if (strpos($table, " ") !== FALSE)
- {
- // if the alias is written with the AS keyword, remove it
- $table = preg_replace('/ AS /i', ' ', $table);
-
- // Grab the alias
- $table = trim(strrchr($table, " "));
-
- // Store the alias, if it doesn't already exist
- if ( ! in_array($table, $this->ar_aliased_tables))
- {
- $this->ar_aliased_tables[] = $table;
- }
- }
- }
-
- // --------------------------------------------------------------------
-
- /**
- * Compile the SELECT statement
- *
- * Generates a query string based on which functions were used.
- * Should not be called directly. The get() function calls it.
- *
- * @return string
- */
- protected function _compile_select($select_override = FALSE)
- {
- // Combine any cached components with the current statements
- $this->_merge_cache();
-
- // ----------------------------------------------------------------
-
- // Write the "select" portion of the query
-
- if ($select_override !== FALSE)
- {
- $sql = $select_override;
- }
- else
- {
- $sql = ( ! $this->ar_distinct) ? 'SELECT ' : 'SELECT DISTINCT ';
-
- if (count($this->ar_select) == 0)
- {
- $sql .= '*';
- }
- else
- {
- // Cycle through the "select" portion of the query and prep each column name.
- // The reason we protect identifiers here rather then in the select() function
- // is because until the user calls the from() function we don't know if there are aliases
- foreach ($this->ar_select as $key => $val)
- {
- $no_escape = isset($this->ar_no_escape[$key]) ? $this->ar_no_escape[$key] : NULL;
- $this->ar_select[$key] = $this->_protect_identifiers($val, FALSE, $no_escape);
- }
-
- $sql .= implode(', ', $this->ar_select);
- }
- }
-
- // ----------------------------------------------------------------
-
- // Write the "FROM" portion of the query
-
- if (count($this->ar_from) > 0)
- {
- $sql .= "\nFROM ";
-
- $sql .= $this->_from_tables($this->ar_from);
- }
-
- // ----------------------------------------------------------------
-
- // Write the "JOIN" portion of the query
-
- if (count($this->ar_join) > 0)
- {
- $sql .= "\n";
-
- $sql .= implode("\n", $this->ar_join);
- }
-
- // ----------------------------------------------------------------
-
- // Write the "WHERE" portion of the query
-
- if (count($this->ar_where) > 0 OR count($this->ar_like) > 0)
- {
- $sql .= "\nWHERE ";
- }
-
- $sql .= implode("\n", $this->ar_where);
-
- // ----------------------------------------------------------------
-
- // Write the "LIKE" portion of the query
-
- if (count($this->ar_like) > 0)
- {
- if (count($this->ar_where) > 0)
- {
- $sql .= "\nAND ";
- }
-
- $sql .= implode("\n", $this->ar_like);
- }
-
- // ----------------------------------------------------------------
-
- // Write the "GROUP BY" portion of the query
-
- if (count($this->ar_groupby) > 0)
- {
- $sql .= "\nGROUP BY ";
-
- $sql .= implode(', ', $this->ar_groupby);
- }
-
- // ----------------------------------------------------------------
-
- // Write the "HAVING" portion of the query
-
- if (count($this->ar_having) > 0)
- {
- $sql .= "\nHAVING ";
- $sql .= implode("\n", $this->ar_having);
- }
-
- // ----------------------------------------------------------------
-
- // Write the "ORDER BY" portion of the query
-
- if (count($this->ar_orderby) > 0)
- {
- $sql .= "\nORDER BY ";
- $sql .= implode(', ', $this->ar_orderby);
-
- if ($this->ar_order !== FALSE)
- {
- $sql .= ($this->ar_order == 'desc') ? ' DESC' : ' ASC';
- }
- }
-
- // ----------------------------------------------------------------
-
- // Write the "LIMIT" portion of the query
-
- if (is_numeric($this->ar_limit))
- {
- $sql .= "\n";
- $sql = $this->_limit($sql, $this->ar_limit, $this->ar_offset);
- }
-
- return $sql;
- }
-
- // --------------------------------------------------------------------
-
- /**
- * Object to Array
- *
- * Takes an object as input and converts the class variables to array key/vals
- *
- * @param object
- * @return array
- */
- public function _object_to_array($object)
- {
- if ( ! is_object($object))
- {
- return $object;
- }
-
- $array = array();
- foreach (get_object_vars($object) as $key => $val)
- {
- // There are some built in keys we need to ignore for this conversion
- if ( ! is_object($val) && ! is_array($val) && $key != '_parent_name')
- {
- $array[$key] = $val;
- }
- }
-
- return $array;
- }
-
- // --------------------------------------------------------------------
-
- /**
- * Object to Array
- *
- * Takes an object as input and converts the class variables to array key/vals
- *
- * @param object
- * @return array
- */
- public function _object_to_array_batch($object)
- {
- if ( ! is_object($object))
- {
- return $object;
- }
-
- $array = array();
- $out = get_object_vars($object);
- $fields = array_keys($out);
-
- foreach ($fields as $val)
- {
- // There are some built in keys we need to ignore for this conversion
- if ($val != '_parent_name')
- {
-
- $i = 0;
- foreach ($out[$val] as $data)
- {
- $array[$i][$val] = $data;
- $i++;
- }
- }
- }
-
- return $array;
- }
-
- // --------------------------------------------------------------------
-
- /**
- * Start Cache
- *
- * Starts AR caching
- *
- * @return void
- */
- public function start_cache()
- {
- $this->ar_caching = TRUE;
- }
-
- // --------------------------------------------------------------------
-
- /**
- * Stop Cache
- *
- * Stops AR caching
- *
- * @return void
- */
- public function stop_cache()
- {
- $this->ar_caching = FALSE;
- }
-
- // --------------------------------------------------------------------
-
- /**
- * Flush Cache
- *
- * Empties the AR cache
- *
- * @access public
- * @return void
- */
- public function flush_cache()
- {
- $this->_reset_run(array(
- 'ar_cache_select' => array(),
- 'ar_cache_from' => array(),
- 'ar_cache_join' => array(),
- 'ar_cache_where' => array(),
- 'ar_cache_like' => array(),
- 'ar_cache_groupby' => array(),
- 'ar_cache_having' => array(),
- 'ar_cache_orderby' => array(),
- 'ar_cache_set' => array(),
- 'ar_cache_exists' => array(),
- 'ar_cache_no_escape' => array()
- ));
- }
-
- // --------------------------------------------------------------------
-
- /**
- * Merge Cache
- *
- * When called, this function merges any cached AR arrays with
- * locally called ones.
- *
- * @return void
- */
- protected function _merge_cache()
- {
- if (count($this->ar_cache_exists) == 0)
- {
- return;
- }
-
- foreach ($this->ar_cache_exists as $val)
- {
- $ar_variable = 'ar_'.$val;
- $ar_cache_var = 'ar_cache_'.$val;
-
- if (count($this->$ar_cache_var) == 0)
- {
- continue;
- }
-
- $this->$ar_variable = array_unique(array_merge($this->$ar_cache_var, $this->$ar_variable));
- }
-
- // If we are "protecting identifiers" we need to examine the "from"
- // portion of the query to determine if there are any aliases
- if ($this->_protect_identifiers === TRUE AND count($this->ar_cache_from) > 0)
- {
- $this->_track_aliases($this->ar_from);
- }
-
- $this->ar_no_escape = $this->ar_cache_no_escape;
- }
-
- // --------------------------------------------------------------------
-
- /**
- * Resets the active record values. Called by the get() function
- *
- * @param array An array of fields to reset
- * @return void
- */
- protected function _reset_run($ar_reset_items)
- {
- foreach ($ar_reset_items as $item => $default_value)
- {
- if ( ! in_array($item, $this->ar_store_array))
- {
- $this->$item = $default_value;
- }
- }
- }
-
- // --------------------------------------------------------------------
-
- /**
- * Resets the active record values. Called by the get() function
- *
- * @return void
- */
- protected function _reset_select()
- {
- $ar_reset_items = array(
- 'ar_select' => array(),
- 'ar_from' => array(),
- 'ar_join' => array(),
- 'ar_where' => array(),
- 'ar_like' => array(),
- 'ar_groupby' => array(),
- 'ar_having' => array(),
- 'ar_orderby' => array(),
- 'ar_wherein' => array(),
- 'ar_aliased_tables' => array(),
- 'ar_no_escape' => array(),
- 'ar_distinct' => FALSE,
- 'ar_limit' => FALSE,
- 'ar_offset' => FALSE,
- 'ar_order' => FALSE,
- );
-
- $this->_reset_run($ar_reset_items);
- }
-
- // --------------------------------------------------------------------
-
- /**
- * Resets the active record "write" values.
- *
- * Called by the insert() update() insert_batch() update_batch() and delete() functions
- *
- * @return void
- */
- protected function _reset_write()
- {
- $ar_reset_items = array(
- 'ar_set' => array(),
- 'ar_from' => array(),
- 'ar_where' => array(),
- 'ar_like' => array(),
- 'ar_orderby' => array(),
- 'ar_keys' => array(),
- 'ar_limit' => FALSE,
- 'ar_order' => FALSE
- );
-
- $this->_reset_run($ar_reset_items);
- }
-}
-
-/* End of file DB_active_rec.php */
-/* Location: ./system/database/DB_active_rec.php */ \ No newline at end of file
diff --git a/system/database/DB_cache.php b/system/database/DB_cache.php
index e6945950b..d05ebb219 100644
--- a/system/database/DB_cache.php
+++ b/system/database/DB_cache.php
@@ -1,45 +1,85 @@
-<?php if ( ! defined('BASEPATH')) exit('No direct script access allowed');
+<?php
/**
* CodeIgniter
*
- * An open source application development framework for PHP 5.1.6 or newer
+ * An open source application development framework for PHP
*
- * @package CodeIgniter
- * @author ExpressionEngine Dev Team
- * @copyright Copyright (c) 2008 - 2014, EllisLab, Inc.
- * @license http://codeigniter.com/user_guide/license.html
- * @link http://codeigniter.com
- * @since Version 1.0
+ * This content is released under the MIT License (MIT)
+ *
+ * Copyright (c) 2019 - 2022, CodeIgniter Foundation
+ *
+ * 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.
+ *
+ * @package CodeIgniter
+ * @author EllisLab Dev Team
+ * @copyright Copyright (c) 2008 - 2014, EllisLab, Inc. (https://ellislab.com/)
+ * @copyright Copyright (c) 2014 - 2019, British Columbia Institute of Technology (https://bcit.ca/)
+ * @copyright Copyright (c) 2019 - 2022, CodeIgniter Foundation (https://codeigniter.com/)
+ * @license https://opensource.org/licenses/MIT MIT License
+ * @link https://codeigniter.com
+ * @since Version 1.0.0
* @filesource
*/
-
-// ------------------------------------------------------------------------
+defined('BASEPATH') OR exit('No direct script access allowed');
/**
* Database Cache Class
*
* @category Database
- * @author ExpressionEngine Dev Team
- * @link http://codeigniter.com/user_guide/database/
+ * @author EllisLab Dev Team
+ * @link https://codeigniter.com/userguide3/database/
*/
class CI_DB_Cache {
- var $CI;
- var $db; // allows passing of db object so that multiple database connections and returned db objects can be supported
+ /**
+ * CI Singleton
+ *
+ * @var object
+ */
+ public $CI;
/**
- * Constructor
+ * Database object
*
- * Grabs the CI super object instance so we can access it.
+ * Allows passing of DB object so that multiple database connections
+ * and returned DB objects can be supported.
*
+ * @var object
*/
- function __construct(&$db)
+ public $db;
+
+ // --------------------------------------------------------------------
+
+ /**
+ * Constructor
+ *
+ * @param object &$db
+ * @return void
+ */
+ public function __construct(&$db)
{
- // Assign the main CI object to $this->CI
- // and load the file helper since we use it a lot
+ // Assign the main CI object to $this->CI and load the file helper since we use it a lot
$this->CI =& get_instance();
$this->db =& $db;
$this->CI->load->helper('file');
+
+ $this->check_path();
}
// --------------------------------------------------------------------
@@ -47,15 +87,14 @@ class CI_DB_Cache {
/**
* Set Cache Directory Path
*
- * @access public
- * @param string the path to the cache directory
+ * @param string $path Path to the cache directory
* @return bool
*/
- function check_path($path = '')
+ public function check_path($path = '')
{
- if ($path == '')
+ if ($path === '')
{
- if ($this->db->cachedir == '')
+ if ($this->db->cachedir === '')
{
return $this->db->cache_off();
}
@@ -64,14 +103,26 @@ class CI_DB_Cache {
}
// Add a trailing slash to the path if needed
- $path = preg_replace("/(.+?)\/*$/", "\\1/", $path);
+ $path = realpath($path)
+ ? rtrim(realpath($path), DIRECTORY_SEPARATOR).DIRECTORY_SEPARATOR
+ : rtrim($path, '/').'/';
- if ( ! is_dir($path) OR ! is_really_writable($path))
+ if ( ! is_dir($path))
{
+ log_message('debug', 'DB cache path error: '.$path);
+
// If the path is wrong we'll turn off caching
return $this->db->cache_off();
}
+ if ( ! is_really_writable($path))
+ {
+ log_message('debug', 'DB cache dir not writable: '.$path);
+
+ // If the path is not really writable we'll turn off caching
+ return $this->db->cache_off();
+ }
+
$this->db->cachedir = $path;
return TRUE;
}
@@ -82,25 +133,18 @@ class CI_DB_Cache {
* Retrieve a cached query
*
* The URI being requested will become the name of the cache sub-folder.
- * An MD5 hash of the SQL statement will become the cache file name
+ * An MD5 hash of the SQL statement will become the cache file name.
*
- * @access public
+ * @param string $sql
* @return string
*/
- function read($sql)
+ public function read($sql)
{
- if ( ! $this->check_path())
- {
- return $this->db->cache_off();
- }
-
$segment_one = ($this->CI->uri->segment(1) == FALSE) ? 'default' : $this->CI->uri->segment(1);
-
$segment_two = ($this->CI->uri->segment(2) == FALSE) ? 'index' : $this->CI->uri->segment(2);
-
$filepath = $this->db->cachedir.$segment_one.'+'.$segment_two.'/'.md5($sql);
- if (FALSE === ($cachedata = read_file($filepath)))
+ if ( ! is_file($filepath) OR FALSE === ($cachedata = file_get_contents($filepath)))
{
return FALSE;
}
@@ -113,32 +157,20 @@ class CI_DB_Cache {
/**
* Write a query to a cache file
*
- * @access public
+ * @param string $sql
+ * @param object $object
* @return bool
*/
- function write($sql, $object)
+ public function write($sql, $object)
{
- if ( ! $this->check_path())
- {
- return $this->db->cache_off();
- }
-
$segment_one = ($this->CI->uri->segment(1) == FALSE) ? 'default' : $this->CI->uri->segment(1);
-
$segment_two = ($this->CI->uri->segment(2) == FALSE) ? 'index' : $this->CI->uri->segment(2);
-
$dir_path = $this->db->cachedir.$segment_one.'+'.$segment_two.'/';
-
$filename = md5($sql);
- if ( ! @is_dir($dir_path))
+ if ( ! is_dir($dir_path) && ! @mkdir($dir_path, 0750))
{
- if ( ! @mkdir($dir_path, DIR_WRITE_MODE))
- {
- return FALSE;
- }
-
- @chmod($dir_path, DIR_WRITE_MODE);
+ return FALSE;
}
if (write_file($dir_path.$filename, serialize($object)) === FALSE)
@@ -146,7 +178,7 @@ class CI_DB_Cache {
return FALSE;
}
- @chmod($dir_path.$filename, FILE_WRITE_MODE);
+ chmod($dir_path.$filename, 0640);
return TRUE;
}
@@ -155,23 +187,23 @@ class CI_DB_Cache {
/**
* Delete cache files within a particular directory
*
- * @access public
- * @return bool
+ * @param string $segment_one
+ * @param string $segment_two
+ * @return void
*/
- function delete($segment_one = '', $segment_two = '')
+ public function delete($segment_one = '', $segment_two = '')
{
- if ($segment_one == '')
+ if ($segment_one === '')
{
$segment_one = ($this->CI->uri->segment(1) == FALSE) ? 'default' : $this->CI->uri->segment(1);
}
- if ($segment_two == '')
+ if ($segment_two === '')
{
$segment_two = ($this->CI->uri->segment(2) == FALSE) ? 'index' : $this->CI->uri->segment(2);
}
$dir_path = $this->db->cachedir.$segment_one.'+'.$segment_two.'/';
-
delete_files($dir_path, TRUE);
}
@@ -180,16 +212,11 @@ class CI_DB_Cache {
/**
* Delete all existing cache files
*
- * @access public
- * @return bool
+ * @return void
*/
- function delete_all()
+ public function delete_all()
{
- delete_files($this->db->cachedir, TRUE);
+ delete_files($this->db->cachedir, TRUE, TRUE);
}
}
-
-
-/* End of file DB_cache.php */
-/* Location: ./system/database/DB_cache.php */ \ No newline at end of file
diff --git a/system/database/DB_driver.php b/system/database/DB_driver.php
index c342aacbd..f7ff92197 100644
--- a/system/database/DB_driver.php
+++ b/system/database/DB_driver.php
@@ -1,19 +1,42 @@
-<?php if ( ! defined('BASEPATH')) exit('No direct script access allowed');
+<?php
/**
* CodeIgniter
*
- * An open source application development framework for PHP 5.1.6 or newer
+ * An open source application development framework for PHP
*
- * @package CodeIgniter
- * @author ExpressionEngine Dev Team
- * @copyright Copyright (c) 2008 - 2014, EllisLab, Inc.
- * @license http://codeigniter.com/user_guide/license.html
- * @link http://codeigniter.com
- * @since Version 1.0
+ * This content is released under the MIT License (MIT)
+ *
+ * Copyright (c) 2019 - 2022, CodeIgniter Foundation
+ *
+ * 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.
+ *
+ * @package CodeIgniter
+ * @author EllisLab Dev Team
+ * @copyright Copyright (c) 2008 - 2014, EllisLab, Inc. (https://ellislab.com/)
+ * @copyright Copyright (c) 2014 - 2019, British Columbia Institute of Technology (https://bcit.ca/)
+ * @copyright Copyright (c) 2019 - 2022, CodeIgniter Foundation (https://codeigniter.com/)
+ * @license https://opensource.org/licenses/MIT MIT License
+ * @link https://codeigniter.com
+ * @since Version 1.0.0
* @filesource
*/
-
-// ------------------------------------------------------------------------
+defined('BASEPATH') OR exit('No direct script access allowed');
/**
* Database Driver Class
@@ -25,60 +48,323 @@
* @package CodeIgniter
* @subpackage Drivers
* @category Database
- * @author ExpressionEngine Dev Team
- * @link http://codeigniter.com/user_guide/database/
+ * @author EllisLab Dev Team
+ * @link https://codeigniter.com/userguide3/database/
*/
-class CI_DB_driver {
-
- var $username;
- var $password;
- var $hostname;
- var $database;
- var $dbdriver = 'mysql';
- var $dbprefix = '';
- var $char_set = 'utf8';
- var $dbcollat = 'utf8_general_ci';
- var $autoinit = TRUE; // Whether to automatically initialize the DB
- var $swap_pre = '';
- var $port = '';
- var $pconnect = FALSE;
- var $conn_id = FALSE;
- var $result_id = FALSE;
- var $db_debug = FALSE;
- var $benchmark = 0;
- var $query_count = 0;
- var $bind_marker = '?';
- var $save_queries = TRUE;
- var $queries = array();
- var $query_times = array();
- var $data_cache = array();
- var $trans_enabled = TRUE;
- var $trans_strict = TRUE;
- var $_trans_depth = 0;
- var $_trans_status = TRUE; // Used with transactions to determine if a rollback should occur
- var $cache_on = FALSE;
- var $cachedir = '';
- var $cache_autodel = FALSE;
- var $CACHE; // The cache class object
-
- // Private variables
- var $_protect_identifiers = TRUE;
- var $_reserved_identifiers = array('*'); // Identifiers that should NOT be escaped
-
- // These are use with Oracle
- var $stmt_id;
- var $curs_id;
- var $limit_used;
-
-
-
- /**
- * Constructor. Accepts one parameter containing the database
- * connection settings.
- *
- * @param array
- */
- function __construct($params)
+#[\AllowDynamicProperties]
+abstract class CI_DB_driver {
+
+ /**
+ * Data Source Name / Connect string
+ *
+ * @var string
+ */
+ public $dsn;
+
+ /**
+ * Username
+ *
+ * @var string
+ */
+ public $username;
+
+ /**
+ * Password
+ *
+ * @var string
+ */
+ public $password;
+
+ /**
+ * Hostname
+ *
+ * @var string
+ */
+ public $hostname;
+
+ /**
+ * Database name
+ *
+ * @var string
+ */
+ public $database;
+
+ /**
+ * Database driver
+ *
+ * @var string
+ */
+ public $dbdriver = 'mysqli';
+
+ /**
+ * Sub-driver
+ *
+ * @used-by CI_DB_pdo_driver
+ * @var string
+ */
+ public $subdriver;
+
+ /**
+ * Table prefix
+ *
+ * @var string
+ */
+ public $dbprefix = '';
+
+ /**
+ * Character set
+ *
+ * @var string
+ */
+ public $char_set = 'utf8';
+
+ /**
+ * Collation
+ *
+ * @var string
+ */
+ public $dbcollat = 'utf8_general_ci';
+
+ /**
+ * Encryption flag/data
+ *
+ * @var mixed
+ */
+ public $encrypt = FALSE;
+
+ /**
+ * Swap Prefix
+ *
+ * @var string
+ */
+ public $swap_pre = '';
+
+ /**
+ * Database port
+ *
+ * @var int
+ */
+ public $port = NULL;
+
+ /**
+ * Persistent connection flag
+ *
+ * @var bool
+ */
+ public $pconnect = FALSE;
+
+ /**
+ * Connection ID
+ *
+ * @var object|resource
+ */
+ public $conn_id = FALSE;
+
+ /**
+ * Result ID
+ *
+ * @var object|resource
+ */
+ public $result_id = FALSE;
+
+ /**
+ * Debug flag
+ *
+ * Whether to display error messages.
+ *
+ * @var bool
+ */
+ public $db_debug = FALSE;
+
+ /**
+ * Benchmark time
+ *
+ * @var int
+ */
+ public $benchmark = 0;
+
+ /**
+ * Executed queries count
+ *
+ * @var int
+ */
+ public $query_count = 0;
+
+ /**
+ * Bind marker
+ *
+ * Character used to identify values in a prepared statement.
+ *
+ * @var string
+ */
+ public $bind_marker = '?';
+
+ /**
+ * Save queries flag
+ *
+ * Whether to keep an in-memory history of queries for debugging purposes.
+ *
+ * @var bool
+ */
+ public $save_queries = TRUE;
+
+ /**
+ * Queries list
+ *
+ * @see CI_DB_driver::$save_queries
+ * @var string[]
+ */
+ public $queries = array();
+
+ /**
+ * Query times
+ *
+ * A list of times that queries took to execute.
+ *
+ * @var array
+ */
+ public $query_times = array();
+
+ /**
+ * Data cache
+ *
+ * An internal generic value cache.
+ *
+ * @var array
+ */
+ public $data_cache = array();
+
+ /**
+ * Transaction enabled flag
+ *
+ * @var bool
+ */
+ public $trans_enabled = TRUE;
+
+ /**
+ * Strict transaction mode flag
+ *
+ * @var bool
+ */
+ public $trans_strict = TRUE;
+
+ /**
+ * Transaction depth level
+ *
+ * @var int
+ */
+ protected $_trans_depth = 0;
+
+ /**
+ * Transaction status flag
+ *
+ * Used with transactions to determine if a rollback should occur.
+ *
+ * @var bool
+ */
+ protected $_trans_status = TRUE;
+
+ /**
+ * Transaction failure flag
+ *
+ * Used with transactions to determine if a transaction has failed.
+ *
+ * @var bool
+ */
+ protected $_trans_failure = FALSE;
+
+ /**
+ * Cache On flag
+ *
+ * @var bool
+ */
+ public $cache_on = FALSE;
+
+ /**
+ * Cache directory path
+ *
+ * @var bool
+ */
+ public $cachedir = '';
+
+ /**
+ * Cache auto-delete flag
+ *
+ * @var bool
+ */
+ public $cache_autodel = FALSE;
+
+ /**
+ * DB Cache object
+ *
+ * @see CI_DB_cache
+ * @var object
+ */
+ public $CACHE;
+
+ /**
+ * Protect identifiers flag
+ *
+ * @var bool
+ */
+ protected $_protect_identifiers = TRUE;
+
+ /**
+ * List of reserved identifiers
+ *
+ * Identifiers that must NOT be escaped.
+ *
+ * @var string[]
+ */
+ protected $_reserved_identifiers = array('*');
+
+ /**
+ * Identifier escape character
+ *
+ * @var string
+ */
+ protected $_escape_char = '"';
+
+ /**
+ * ESCAPE statement string
+ *
+ * @var string
+ */
+ protected $_like_escape_str = " ESCAPE '%s' ";
+
+ /**
+ * ESCAPE character
+ *
+ * @var string
+ */
+ protected $_like_escape_chr = '!';
+
+ /**
+ * ORDER BY random keyword
+ *
+ * @var array
+ */
+ protected $_random_keyword = array('RAND()', 'RAND(%d)');
+
+ /**
+ * COUNT string
+ *
+ * @used-by CI_DB_driver::count_all()
+ * @used-by CI_DB_query_builder::count_all_results()
+ *
+ * @var string
+ */
+ protected $_count_string = 'SELECT COUNT(*) AS ';
+
+ // --------------------------------------------------------------------
+
+ /**
+ * Class constructor
+ *
+ * @param array $params
+ * @return void
+ */
+ public function __construct($params)
{
if (is_array($params))
{
@@ -88,7 +374,7 @@ class CI_DB_driver {
}
}
- log_message('debug', 'Database Driver Class Initialized');
+ log_message('info', 'Database Driver Class Initialized');
}
// --------------------------------------------------------------------
@@ -96,102 +382,139 @@ class CI_DB_driver {
/**
* Initialize Database Settings
*
- * @access private Called by the constructor
- * @param mixed
* @return void
+ * @throws RuntimeException In case of failure
*/
- function initialize()
+ public function initialize()
{
- // If an existing connection resource is available
- // there is no need to connect and select the database
- if (is_resource($this->conn_id) OR is_object($this->conn_id))
+ /* If an established connection is available, then there's
+ * no need to connect and select the database.
+ *
+ * Depending on the database driver, conn_id can be either
+ * boolean TRUE, a resource or an object.
+ */
+ if ($this->conn_id)
{
- return TRUE;
+ return;
}
// ----------------------------------------------------------------
// Connect to the database and set the connection ID
- $this->conn_id = ($this->pconnect == FALSE) ? $this->db_connect() : $this->db_pconnect();
+ $this->conn_id = $this->db_connect($this->pconnect);
- // No connection resource? Throw an error
+ // No connection resource? Check if there is a failover else throw an error
if ( ! $this->conn_id)
{
- log_message('error', 'Unable to connect to the database');
-
- if ($this->db_debug)
+ // Check if there is a failover set
+ if ( ! empty($this->failover) && is_array($this->failover))
{
- $this->display_error('db_unable_to_connect');
- }
- return FALSE;
- }
-
- // ----------------------------------------------------------------
+ // Go over all the failovers
+ foreach ($this->failover as $failover)
+ {
+ // Replace the current settings with those of the failover
+ foreach ($failover as $key => $val)
+ {
+ $this->$key = $val;
+ }
- // Select the DB... assuming a database name is specified in the config file
- if ($this->database != '')
- {
- if ( ! $this->db_select())
- {
- log_message('error', 'Unable to select database: '.$this->database);
+ // Try to connect
+ $this->conn_id = $this->db_connect($this->pconnect);
- if ($this->db_debug)
- {
- $this->display_error('db_unable_to_select', $this->database);
+ // If a connection is made break the foreach loop
+ if ($this->conn_id)
+ {
+ break;
+ }
}
- return FALSE;
}
- else
- {
- // We've selected the DB. Now we set the character set
- if ( ! $this->db_set_charset($this->char_set, $this->dbcollat))
- {
- return FALSE;
- }
- return TRUE;
+ // We still don't have a connection?
+ if ( ! $this->conn_id)
+ {
+ throw new RuntimeException('Unable to connect to the database.');
}
}
+ }
+
+ // --------------------------------------------------------------------
+ /**
+ * DB connect
+ *
+ * This is just a dummy method that all drivers will override.
+ *
+ * @return mixed
+ */
+ public function db_connect()
+ {
return TRUE;
}
// --------------------------------------------------------------------
/**
- * Set client character set
+ * Persistent database connection
*
- * @access public
- * @param string
- * @param string
- * @return resource
+ * @return mixed
*/
- function db_set_charset($charset, $collation)
+ public function db_pconnect()
{
- if ( ! $this->_db_set_charset($this->char_set, $this->dbcollat))
- {
- log_message('error', 'Unable to set database connection charset: '.$this->char_set);
+ return $this->db_connect(TRUE);
+ }
- if ($this->db_debug)
- {
- $this->display_error('db_unable_to_set_charset', $this->char_set);
- }
+ // --------------------------------------------------------------------
- return FALSE;
- }
+ /**
+ * Reconnect
+ *
+ * Keep / reestablish the db connection if no queries have been
+ * sent for a length of time exceeding the server's idle timeout.
+ *
+ * This is just a dummy method to allow drivers without such
+ * functionality to not declare it, while others will override it.
+ *
+ * @return void
+ */
+ public function reconnect()
+ {
+ }
+ // --------------------------------------------------------------------
+
+ /**
+ * Select database
+ *
+ * This is just a dummy method to allow drivers without such
+ * functionality to not declare it, while others will override it.
+ *
+ * @return bool
+ */
+ public function db_select()
+ {
return TRUE;
}
// --------------------------------------------------------------------
/**
+ * Last error
+ *
+ * @return array
+ */
+ public function error()
+ {
+ return array('code' => NULL, 'message' => NULL);
+ }
+
+ // --------------------------------------------------------------------
+
+ /**
* The name of the platform in use (mysql, mssql, etc...)
*
- * @access public
* @return string
*/
- function platform()
+ public function platform()
{
return $this->dbdriver;
}
@@ -199,36 +522,39 @@ class CI_DB_driver {
// --------------------------------------------------------------------
/**
- * Database Version Number. Returns a string containing the
- * version of the database being used
+ * Database version number
+ *
+ * Returns a string containing the version of the database being used.
+ * Most drivers will override this method.
*
- * @access public
* @return string
*/
- function version()
+ public function version()
{
- if (FALSE === ($sql = $this->_version()))
+ if (isset($this->data_cache['version']))
{
- if ($this->db_debug)
- {
- return $this->display_error('db_unsupported_function');
- }
- return FALSE;
+ return $this->data_cache['version'];
}
- // Some DBs have functions that return the version, and don't run special
- // SQL queries per se. In these instances, just return the result.
- $driver_version_exceptions = array('oci8', 'sqlite', 'cubrid');
-
- if (in_array($this->dbdriver, $driver_version_exceptions))
- {
- return $sql;
- }
- else
+ if (FALSE === ($sql = $this->_version()))
{
- $query = $this->query($sql);
- return $query->row('ver');
+ return ($this->db_debug) ? $this->display_error('db_unsupported_function') : FALSE;
}
+
+ $query = $this->query($sql)->row();
+ return $this->data_cache['version'] = $query->ver;
+ }
+
+ // --------------------------------------------------------------------
+
+ /**
+ * Version number query string
+ *
+ * @return string
+ */
+ protected function _version()
+ {
+ return 'SELECT VERSION() AS ver';
}
// --------------------------------------------------------------------
@@ -237,32 +563,32 @@ class CI_DB_driver {
* Execute the query
*
* Accepts an SQL string as input and returns a result object upon
- * successful execution of a "read" type query. Returns boolean TRUE
+ * successful execution of a "read" type query. Returns boolean TRUE
* upon successful execution of a "write" type query. Returns boolean
* FALSE upon failure, and if the $db_debug variable is set to TRUE
* will raise an error.
*
- * @access public
- * @param string An SQL query string
- * @param array An array of binding data
+ * @param string $sql
+ * @param array $binds = FALSE An array of binding data
+ * @param bool $return_object = NULL
* @return mixed
*/
- function query($sql, $binds = FALSE, $return_object = TRUE)
+ public function query($sql, $binds = FALSE, $return_object = NULL)
{
- if ($sql == '')
+ if ($sql === '')
{
- if ($this->db_debug)
- {
- log_message('error', 'Invalid query: '.$sql);
- return $this->display_error('db_invalid_query');
- }
- return FALSE;
+ log_message('error', 'Invalid query: '.$sql);
+ return ($this->db_debug) ? $this->display_error('db_invalid_query') : FALSE;
+ }
+ elseif ( ! is_bool($return_object))
+ {
+ $return_object = ! $this->is_write_type($sql);
}
// Verify table prefix and replace if necessary
- if ( ($this->dbprefix != '' AND $this->swap_pre != '') AND ($this->dbprefix != $this->swap_pre) )
+ if ($this->dbprefix !== '' && $this->swap_pre !== '' && $this->dbprefix !== $this->swap_pre)
{
- $sql = preg_replace("/(\W)".$this->swap_pre."(\S+?)/", "\\1".$this->dbprefix."\\2", $sql);
+ $sql = preg_replace('/(\W)'.$this->swap_pre.'(\S+?)/', '\\1'.$this->dbprefix.'\\2', $sql);
}
// Compile binds if needed
@@ -271,87 +597,87 @@ class CI_DB_driver {
$sql = $this->compile_binds($sql, $binds);
}
- // Is query caching enabled? If the query is a "read type"
+ // Is query caching enabled? If the query is a "read type"
// we will load the caching class and return the previously
// cached query if it exists
- if ($this->cache_on == TRUE AND stristr($sql, 'SELECT'))
+ if ($this->cache_on === TRUE && $return_object === TRUE && $this->_cache_init())
{
- if ($this->_cache_init())
+ if (FALSE !== ($cache = $this->CACHE->read($sql)))
{
- $this->load_rdriver();
- if (FALSE !== ($cache = $this->CACHE->read($sql)))
- {
- return $cache;
- }
+ return $cache;
}
}
- // Save the query for debugging
- if ($this->save_queries == TRUE)
+ // Save the query for debugging
+ if ($this->save_queries === TRUE)
{
$this->queries[] = $sql;
}
// Start the Query Timer
- $time_start = list($sm, $ss) = explode(' ', microtime());
+ $time_start = microtime(TRUE);
// Run the Query
if (FALSE === ($this->result_id = $this->simple_query($sql)))
{
- if ($this->save_queries == TRUE)
+ if ($this->save_queries === TRUE)
{
$this->query_times[] = 0;
}
// This will trigger a rollback if transactions are being used
- $this->_trans_status = FALSE;
+ if ($this->_trans_depth !== 0)
+ {
+ $this->_trans_status = FALSE;
+ }
+
+ // Grab the error now, as we might run some additional queries before displaying the error
+ $error = $this->error();
+
+ // Log errors
+ log_message('error', 'Query error: '.$error['message'].' - Invalid query: '.$sql);
if ($this->db_debug)
{
- // grab the error number and message now, as we might run some
- // additional queries before displaying the error
- $error_no = $this->_error_number();
- $error_msg = $this->_error_message();
-
// We call this function in order to roll-back queries
- // if transactions are enabled. If we don't call this here
+ // if transactions are enabled. If we don't call this here
// the error message will trigger an exit, causing the
// transactions to remain in limbo.
- $this->trans_complete();
-
- // Log and display errors
- log_message('error', 'Query error: '.$error_msg);
- return $this->display_error(
- array(
- 'Error Number: '.$error_no,
- $error_msg,
- $sql
- )
- );
+ while ($this->_trans_depth !== 0)
+ {
+ $trans_depth = $this->_trans_depth;
+ $this->trans_complete();
+ if ($trans_depth === $this->_trans_depth)
+ {
+ log_message('error', 'Database: Failure during an automated transaction commit/rollback!');
+ break;
+ }
+ }
+
+ // Display errors
+ return $this->display_error(array('Error Number: '.$error['code'], $error['message'], $sql));
}
return FALSE;
}
// Stop and aggregate the query time results
- $time_end = list($em, $es) = explode(' ', microtime());
- $this->benchmark += ($em + $es) - ($sm + $ss);
+ $time_end = microtime(TRUE);
+ $this->benchmark += $time_end - $time_start;
- if ($this->save_queries == TRUE)
+ if ($this->save_queries === TRUE)
{
- $this->query_times[] = ($em + $es) - ($sm + $ss);
+ $this->query_times[] = $time_end - $time_start;
}
// Increment the query counter
$this->query_count++;
- // Was the query a "write" type?
- // If so we'll simply return true
- if ($this->is_write_type($sql) === TRUE)
+ // Will we have a result object instantiated? If not - we'll simply return TRUE
+ if ($return_object !== TRUE)
{
- // If caching is enabled we'll auto-cleanup any
- // existing files related to this particular URI
- if ($this->cache_on == TRUE AND $this->cache_autodel == TRUE AND $this->_cache_init())
+ // If caching is enabled we'll auto-cleanup any existing files related to this particular URI
+ if ($this->cache_on === TRUE && $this->cache_autodel === TRUE && $this->_cache_init())
{
$this->CACHE->delete();
}
@@ -359,35 +685,13 @@ class CI_DB_driver {
return TRUE;
}
- // Return TRUE if we don't need to create a result object
- // Currently only the Oracle driver uses this when stored
- // procedures are used
- if ($return_object !== TRUE)
- {
- return TRUE;
- }
-
- // Load and instantiate the result driver
+ // Instantiate the driver-specific result class
+ $driver = 'CI_DB_'.$this->dbdriver.'_result';
+ $RES = new $driver($this);
- $driver = $this->load_rdriver();
- $RES = new $driver();
- $RES->conn_id = $this->conn_id;
- $RES->result_id = $this->result_id;
-
- if ($this->dbdriver == 'oci8')
- {
- $RES->stmt_id = $this->stmt_id;
- $RES->curs_id = NULL;
- $RES->limit_used = $this->limit_used;
- $this->stmt_id = FALSE;
- }
-
- // oci8 vars must be set before calling this
- $RES->num_rows = $RES->num_rows();
-
- // Is query caching enabled? If so, we'll serialize the
+ // Is query caching enabled? If so, we'll serialize the
// result object and save it to a cache file.
- if ($this->cache_on == TRUE AND $this->_cache_init())
+ if ($this->cache_on === TRUE && $this->_cache_init())
{
// We'll create a new instance of the result object
// only without the platform specific driver since
@@ -395,10 +699,10 @@ class CI_DB_driver {
// resource ID won't be any good once we've cached the
// result object, so we'll have to compile the data
// and save it)
- $CR = new CI_DB_result();
- $CR->num_rows = $RES->num_rows();
+ $CR = new CI_DB_result($this);
$CR->result_object = $RES->result_object();
$CR->result_array = $RES->result_array();
+ $CR->num_rows = $RES->num_rows();
// Reset these since cached objects can not utilize resource IDs.
$CR->conn_id = NULL;
@@ -413,43 +717,17 @@ class CI_DB_driver {
// --------------------------------------------------------------------
/**
- * Load the result drivers
- *
- * @access public
- * @return string the name of the result class
- */
- function load_rdriver()
- {
- $driver = 'CI_DB_'.$this->dbdriver.'_result';
-
- if ( ! class_exists($driver))
- {
- include_once(BASEPATH.'database/DB_result.php');
- include_once(BASEPATH.'database/drivers/'.$this->dbdriver.'/'.$this->dbdriver.'_result.php');
- }
-
- return $driver;
- }
-
- // --------------------------------------------------------------------
-
- /**
* Simple Query
- * This is a simplified version of the query() function. Internally
+ * This is a simplified version of the query() function. Internally
* we only use it when running transaction commands since they do
* not require all the features of the main query() function.
*
- * @access public
* @param string the sql query
* @return mixed
*/
- function simple_query($sql)
+ public function simple_query($sql)
{
- if ( ! $this->conn_id)
- {
- $this->initialize();
- }
-
+ empty($this->conn_id) && $this->initialize();
return $this->_execute($sql);
}
@@ -459,10 +737,9 @@ class CI_DB_driver {
* Disable Transactions
* This permits transactions to be disabled at run-time.
*
- * @access public
* @return void
*/
- function trans_off()
+ public function trans_off()
{
$this->trans_enabled = FALSE;
}
@@ -471,15 +748,18 @@ class CI_DB_driver {
/**
* Enable/disable Transaction Strict Mode
+ *
* When strict mode is enabled, if you are running multiple groups of
- * transactions, if one group fails all groups will be rolled back.
- * If strict mode is disabled, each group is treated autonomously, meaning
- * a failure of one group will not affect any others
+ * transactions, if one group fails all subsequent groups will be
+ * rolled back.
*
- * @access public
+ * If strict mode is disabled, each group is treated autonomously,
+ * meaning a failure of one group will not affect any others
+ *
+ * @param bool $mode = TRUE
* @return void
*/
- function trans_strict($mode = TRUE)
+ public function trans_strict($mode = TRUE)
{
$this->trans_strict = is_bool($mode) ? $mode : TRUE;
}
@@ -489,24 +769,17 @@ class CI_DB_driver {
/**
* Start Transaction
*
- * @access public
- * @return void
+ * @param bool $test_mode = FALSE
+ * @return bool
*/
- function trans_start($test_mode = FALSE)
+ public function trans_start($test_mode = FALSE)
{
if ( ! $this->trans_enabled)
{
return FALSE;
}
- // When transactions are nested we only begin/commit/rollback the outermost ones
- if ($this->_trans_depth > 0)
- {
- $this->_trans_depth += 1;
- return;
- }
-
- $this->trans_begin($test_mode);
+ return $this->trans_begin($test_mode);
}
// --------------------------------------------------------------------
@@ -514,31 +787,23 @@ class CI_DB_driver {
/**
* Complete Transaction
*
- * @access public
* @return bool
*/
- function trans_complete()
+ public function trans_complete()
{
if ( ! $this->trans_enabled)
{
return FALSE;
}
- // When transactions are nested we only begin/commit/rollback the outermost ones
- if ($this->_trans_depth > 1)
- {
- $this->_trans_depth -= 1;
- return TRUE;
- }
-
// The query() function will set this flag to FALSE in the event that a query failed
- if ($this->_trans_status === FALSE)
+ if ($this->_trans_status === FALSE OR $this->_trans_failure === TRUE)
{
$this->trans_rollback();
// If we are NOT running in strict mode, we will reset
- // the _trans_status flag so that subsequent groups of transactions
- // will be permitted.
+ // the _trans_status flag so that subsequent groups of
+ // transactions will be permitted.
if ($this->trans_strict === FALSE)
{
$this->_trans_status = TRUE;
@@ -548,8 +813,7 @@ class CI_DB_driver {
return FALSE;
}
- $this->trans_commit();
- return TRUE;
+ return $this->trans_commit();
}
// --------------------------------------------------------------------
@@ -557,10 +821,9 @@ class CI_DB_driver {
/**
* Lets you retrieve the transaction flag to determine if it has failed
*
- * @access public
* @return bool
*/
- function trans_status()
+ public function trans_status()
{
return $this->_trans_status;
}
@@ -568,44 +831,160 @@ class CI_DB_driver {
// --------------------------------------------------------------------
/**
+ * Returns TRUE if a transaction is currently active
+ *
+ * @return bool
+ */
+ public function trans_active()
+ {
+ return (bool) $this->_trans_depth;
+ }
+
+ // --------------------------------------------------------------------
+
+ /**
+ * Begin Transaction
+ *
+ * @param bool $test_mode
+ * @return bool
+ */
+ public function trans_begin($test_mode = FALSE)
+ {
+ if ( ! $this->trans_enabled)
+ {
+ return FALSE;
+ }
+ // When transactions are nested we only begin/commit/rollback the outermost ones
+ elseif ($this->_trans_depth > 0)
+ {
+ $this->_trans_depth++;
+ return TRUE;
+ }
+
+ // Reset the transaction failure flag.
+ // If the $test_mode flag is set to TRUE transactions will be rolled back
+ // even if the queries produce a successful result.
+ $this->_trans_failure = ($test_mode === TRUE);
+
+ if ($this->_trans_begin())
+ {
+ $this->_trans_status = TRUE;
+ $this->_trans_depth++;
+ return TRUE;
+ }
+
+ return FALSE;
+ }
+
+ // --------------------------------------------------------------------
+
+ /**
+ * Commit Transaction
+ *
+ * @return bool
+ */
+ public function trans_commit()
+ {
+ if ( ! $this->trans_enabled OR $this->_trans_depth === 0)
+ {
+ return FALSE;
+ }
+ // When transactions are nested we only begin/commit/rollback the outermost ones
+ elseif ($this->_trans_depth > 1 OR $this->_trans_commit())
+ {
+ $this->_trans_depth--;
+ return TRUE;
+ }
+
+ return FALSE;
+ }
+
+ // --------------------------------------------------------------------
+
+ /**
+ * Rollback Transaction
+ *
+ * @return bool
+ */
+ public function trans_rollback()
+ {
+ if ( ! $this->trans_enabled OR $this->_trans_depth === 0)
+ {
+ return FALSE;
+ }
+ // When transactions are nested we only begin/commit/rollback the outermost ones
+ elseif ($this->_trans_depth > 1 OR $this->_trans_rollback())
+ {
+ $this->_trans_depth--;
+ return TRUE;
+ }
+
+ return FALSE;
+ }
+
+ // --------------------------------------------------------------------
+
+ /**
* Compile Bindings
*
- * @access public
* @param string the sql statement
* @param array an array of bind data
* @return string
*/
- function compile_binds($sql, $binds)
+ public function compile_binds($sql, $binds)
{
- if (strpos($sql, $this->bind_marker) === FALSE)
+ if (empty($this->bind_marker) OR strpos($sql, $this->bind_marker) === FALSE)
{
return $sql;
}
-
- if ( ! is_array($binds))
+ elseif ( ! is_array($binds))
{
$binds = array($binds);
+ $bind_count = 1;
+ }
+ else
+ {
+ // Make sure we're using numeric keys
+ $binds = array_values($binds);
+ $bind_count = count($binds);
}
- // Get the sql segments around the bind markers
- $segments = explode($this->bind_marker, $sql);
+ // We'll need the marker length later
+ $ml = strlen($this->bind_marker);
- // The count of bind should be 1 less then the count of segments
- // If there are more bind arguments trim it down
- if (count($binds) >= count($segments)) {
- $binds = array_slice($binds, 0, count($segments)-1);
+ // Make sure not to replace a chunk inside a string that happens to match the bind marker
+ if ($c = preg_match_all("/'[^']*'|\"[^\"]*\"/i", $sql, $matches))
+ {
+ $c = preg_match_all('/'.preg_quote($this->bind_marker, '/').'/i',
+ str_replace($matches[0],
+ str_replace($this->bind_marker, str_repeat(' ', $ml), $matches[0]),
+ $sql, $c),
+ $matches, PREG_OFFSET_CAPTURE);
+
+ // Bind values' count must match the count of markers in the query
+ if ($bind_count !== $c)
+ {
+ return $sql;
+ }
+ }
+ elseif (($c = preg_match_all('/'.preg_quote($this->bind_marker, '/').'/i', $sql, $matches, PREG_OFFSET_CAPTURE)) !== $bind_count)
+ {
+ return $sql;
}
- // Construct the binded query
- $result = $segments[0];
- $i = 0;
- foreach ($binds as $bind)
+ do
{
- $result .= $this->escape($bind);
- $result .= $segments[++$i];
+ $c--;
+ $escaped_value = $this->escape($binds[$c]);
+ if (is_array($escaped_value))
+ {
+ $escaped_value = '('.implode(',', $escaped_value).')';
+ }
+ $sql = substr_replace($sql, $escaped_value, $matches[0][$c][1], $ml);
}
+ while ($c !== 0);
- return $result;
+ return $sql;
}
// --------------------------------------------------------------------
@@ -613,17 +992,12 @@ class CI_DB_driver {
/**
* Determines if a query is a "write" type.
*
- * @access public
* @param string An SQL query string
- * @return boolean
+ * @return bool
*/
- function is_write_type($sql)
+ public function is_write_type($sql)
{
- if ( ! preg_match('/^\s*"?(SET|INSERT|UPDATE|DELETE|REPLACE|CREATE|DROP|TRUNCATE|LOAD DATA|COPY|ALTER|GRANT|REVOKE|LOCK|UNLOCK)\s+/i', $sql))
- {
- return FALSE;
- }
- return TRUE;
+ return (bool) preg_match('/^\s*"?(SET|INSERT|UPDATE|DELETE|REPLACE|CREATE|DROP|TRUNCATE|LOAD|COPY|ALTER|RENAME|GRANT|REVOKE|LOCK|UNLOCK|REINDEX|MERGE)\s/i', $sql);
}
// --------------------------------------------------------------------
@@ -631,11 +1005,10 @@ class CI_DB_driver {
/**
* Calculate the aggregate query elapsed time
*
- * @access public
- * @param integer The number of decimal places
- * @return integer
+ * @param int The number of decimal places
+ * @return string
*/
- function elapsed_time($decimals = 6)
+ public function elapsed_time($decimals = 6)
{
return number_format($this->benchmark, $decimals);
}
@@ -645,10 +1018,9 @@ class CI_DB_driver {
/**
* Returns the total number of queries
*
- * @access public
- * @return integer
+ * @return int
*/
- function total_queries()
+ public function total_queries()
{
return $this->query_count;
}
@@ -658,10 +1030,9 @@ class CI_DB_driver {
/**
* Returns the last query that was executed
*
- * @access public
- * @return void
+ * @return string
*/
- function last_query()
+ public function last_query()
{
return end($this->queries);
}
@@ -674,23 +1045,63 @@ class CI_DB_driver {
* Escapes data based on type
* Sets boolean and null types
*
- * @access public
* @param string
* @return mixed
*/
- function escape($str)
+ public function escape($str)
{
- if (is_string($str))
+ if (is_array($str))
+ {
+ $str = array_map(array(&$this, 'escape'), $str);
+ return $str;
+ }
+ elseif (is_string($str) OR (is_object($str) && method_exists($str, '__toString')))
{
- $str = "'".$this->escape_str($str)."'";
+ return "'".$this->escape_str($str)."'";
}
elseif (is_bool($str))
{
- $str = ($str === FALSE) ? 0 : 1;
+ return ($str === FALSE) ? 0 : 1;
}
- elseif (is_null($str))
+ elseif ($str === NULL)
{
- $str = 'NULL';
+ return 'NULL';
+ }
+
+ return $str;
+ }
+
+ // --------------------------------------------------------------------
+
+ /**
+ * Escape String
+ *
+ * @param string|string[] $str Input string
+ * @param bool $like Whether or not the string will be used in a LIKE condition
+ * @return string
+ */
+ public function escape_str($str, $like = FALSE)
+ {
+ if (is_array($str))
+ {
+ foreach ($str as $key => $val)
+ {
+ $str[$key] = $this->escape_str($val, $like);
+ }
+
+ return $str;
+ }
+
+ $str = $this->_escape_str($str);
+
+ // escape LIKE condition wildcards
+ if ($like === TRUE)
+ {
+ return str_replace(
+ array($this->_like_escape_chr, '%', '_'),
+ array($this->_like_escape_chr.$this->_like_escape_chr, $this->_like_escape_chr.'%', $this->_like_escape_chr.'_'),
+ $str
+ );
}
return $str;
@@ -704,11 +1115,10 @@ class CI_DB_driver {
* Calls the individual driver for platform
* specific escaping for LIKE conditions
*
- * @access public
- * @param string
+ * @param string|string[]
* @return mixed
*/
- function escape_like_str($str)
+ public function escape_like_str($str)
{
return $this->escape_str($str, TRUE);
}
@@ -716,25 +1126,60 @@ class CI_DB_driver {
// --------------------------------------------------------------------
/**
+ * Platform-dependent string escape
+ *
+ * @param string
+ * @return string
+ */
+ protected function _escape_str($str)
+ {
+ return str_replace("'", "''", remove_invisible_characters($str, FALSE));
+ }
+
+ // --------------------------------------------------------------------
+
+ /**
* Primary
*
- * Retrieves the primary key. It assumes that the row in the first
+ * Retrieves the primary key. It assumes that the row in the first
* position is the primary key
*
- * @access public
- * @param string the table name
+ * @param string $table Table name
* @return string
*/
- function primary($table = '')
+ public function primary($table)
{
$fields = $this->list_fields($table);
+ return is_array($fields) ? current($fields) : FALSE;
+ }
+
+ // --------------------------------------------------------------------
- if ( ! is_array($fields))
+ /**
+ * "Count All" query
+ *
+ * Generates a platform-specific query string that counts all records in
+ * the specified database
+ *
+ * @param string
+ * @return int
+ */
+ public function count_all($table = '')
+ {
+ if ($table === '')
{
- return FALSE;
+ return 0;
+ }
+
+ $query = $this->query($this->_count_string.$this->escape_identifiers('numrows').' FROM '.$this->protect_identifiers($table, TRUE, NULL, FALSE));
+ if ($query->num_rows() === 0)
+ {
+ return 0;
}
- return current($fields);
+ $query = $query->row();
+ $this->_reset_select();
+ return (int) $query->numrows;
}
// --------------------------------------------------------------------
@@ -742,10 +1187,10 @@ class CI_DB_driver {
/**
* Returns an array of table names
*
- * @access public
+ * @param string $constrain_by_prefix = FALSE
* @return array
*/
- function list_tables($constrain_by_prefix = FALSE)
+ public function list_tables($constrain_by_prefix = FALSE)
{
// Is there a cached result?
if (isset($this->data_cache['table_names']))
@@ -755,32 +1200,40 @@ class CI_DB_driver {
if (FALSE === ($sql = $this->_list_tables($constrain_by_prefix)))
{
- if ($this->db_debug)
- {
- return $this->display_error('db_unsupported_function');
- }
- return FALSE;
+ return ($this->db_debug) ? $this->display_error('db_unsupported_function') : FALSE;
}
- $retval = array();
+ $this->data_cache['table_names'] = array();
$query = $this->query($sql);
- if ($query->num_rows() > 0)
+ foreach ($query->result_array() as $row)
{
- foreach ($query->result_array() as $row)
+ // Do we know from which column to get the table name?
+ if ( ! isset($key))
{
- if (isset($row['TABLE_NAME']))
+ if (isset($row['table_name']))
{
- $retval[] = $row['TABLE_NAME'];
+ $key = 'table_name';
+ }
+ elseif (isset($row['TABLE_NAME']))
+ {
+ $key = 'TABLE_NAME';
}
else
{
- $retval[] = array_shift($row);
+ /* We have no other choice but to just get the first element's key.
+ * Due to array_shift() accepting its argument by reference, if
+ * E_STRICT is on, this would trigger a warning. So we'll have to
+ * assign it first.
+ */
+ $key = array_keys($row);
+ $key = array_shift($key);
}
}
+
+ $this->data_cache['table_names'][] = $row[$key];
}
- $this->data_cache['table_names'] = $retval;
return $this->data_cache['table_names'];
}
@@ -788,80 +1241,71 @@ class CI_DB_driver {
/**
* Determine if a particular table exists
- * @access public
- * @return boolean
+ *
+ * @param string $table_name
+ * @return bool
*/
- function table_exists($table_name)
+ public function table_exists($table_name)
{
- return ( ! in_array($this->_protect_identifiers($table_name, TRUE, FALSE, FALSE), $this->list_tables())) ? FALSE : TRUE;
+ return in_array($this->protect_identifiers($table_name, TRUE, FALSE, FALSE), $this->list_tables());
}
// --------------------------------------------------------------------
/**
- * Fetch MySQL Field Names
+ * Fetch Field Names
*
- * @access public
- * @param string the table name
+ * @param string $table Table name
* @return array
*/
- function list_fields($table = '')
+ public function list_fields($table)
{
- // Is there a cached result?
- if (isset($this->data_cache['field_names'][$table]))
- {
- return $this->data_cache['field_names'][$table];
- }
-
- if ($table == '')
- {
- if ($this->db_debug)
- {
- return $this->display_error('db_field_param_missing');
- }
- return FALSE;
- }
-
if (FALSE === ($sql = $this->_list_columns($table)))
{
- if ($this->db_debug)
- {
- return $this->display_error('db_unsupported_function');
- }
- return FALSE;
+ return ($this->db_debug) ? $this->display_error('db_unsupported_function') : FALSE;
}
$query = $this->query($sql);
+ $fields = array();
- $retval = array();
foreach ($query->result_array() as $row)
{
- if (isset($row['COLUMN_NAME']))
+ // Do we know from where to get the column's name?
+ if ( ! isset($key))
{
- $retval[] = $row['COLUMN_NAME'];
- }
- else
- {
- $retval[] = current($row);
+ if (isset($row['column_name']))
+ {
+ $key = 'column_name';
+ }
+ elseif (isset($row['COLUMN_NAME']))
+ {
+ $key = 'COLUMN_NAME';
+ }
+ else
+ {
+ // We have no other choice but to just get the first element's key.
+ $key = key($row);
+ }
}
+
+ $fields[] = $row[$key];
}
- $this->data_cache['field_names'][$table] = $retval;
- return $this->data_cache['field_names'][$table];
+ return $fields;
}
// --------------------------------------------------------------------
/**
* Determine if a particular field exists
- * @access public
+ *
* @param string
* @param string
- * @return boolean
+ * @return bool
*/
- function field_exists($field_name, $table_name)
+ public function field_exists($field_name, $table_name)
{
- return ( ! in_array($field_name, $this->list_fields($table_name))) ? FALSE : TRUE;
+ return in_array($field_name, $this->list_fields($table_name));
}
// --------------------------------------------------------------------
@@ -869,24 +1313,78 @@ class CI_DB_driver {
/**
* Returns an object with field data
*
- * @access public
- * @param string the table name
- * @return object
+ * @param string $table the table name
+ * @return array
+ */
+ public function field_data($table)
+ {
+ $query = $this->query($this->_field_data($this->protect_identifiers($table, TRUE, NULL, FALSE)));
+ return ($query) ? $query->field_data() : FALSE;
+ }
+
+ // --------------------------------------------------------------------
+
+ /**
+ * Escape the SQL Identifiers
+ *
+ * This function escapes column and table names
+ *
+ * @param mixed $item Identifier to escape
+ * @param bool $split Whether to split identifiers when a dot is encountered
+ * @return mixed
*/
- function field_data($table = '')
+ public function escape_identifiers($item, $split = TRUE)
{
- if ($table == '')
+ if ($this->_escape_char === '' OR empty($item) OR in_array($item, $this->_reserved_identifiers))
{
- if ($this->db_debug)
+ return $item;
+ }
+ elseif (is_array($item))
+ {
+ foreach ($item as $key => $value)
{
- return $this->display_error('db_field_param_missing');
+ $item[$key] = $this->escape_identifiers($value);
}
- return FALSE;
+
+ return $item;
+ }
+ // Avoid breaking functions and literal values inside queries
+ elseif (ctype_digit($item) OR $item[0] === "'" OR ($this->_escape_char !== '"' && $item[0] === '"') OR strpos($item, '(') !== FALSE)
+ {
+ return $item;
}
- $query = $this->query($this->_field_data($this->_protect_identifiers($table, TRUE, NULL, FALSE)));
+ static $preg_ec;
- return $query->field_data();
+ if (empty($preg_ec))
+ {
+ if (is_array($this->_escape_char))
+ {
+ $preg_ec = array(
+ preg_quote($this->_escape_char[0]),
+ preg_quote($this->_escape_char[1]),
+ $this->_escape_char[0],
+ $this->_escape_char[1]
+ );
+ }
+ else
+ {
+ $preg_ec[0] = $preg_ec[1] = preg_quote($this->_escape_char);
+ $preg_ec[2] = $preg_ec[3] = $this->_escape_char;
+ }
+ }
+
+ foreach ($this->_reserved_identifiers as $id)
+ {
+ if (strpos($item, '.'.$id) !== FALSE)
+ {
+ return preg_replace('#'.$preg_ec[0].'?([^'.$preg_ec[1].'\.]+)'.$preg_ec[1].'?\.#i', $preg_ec[2].'$1'.$preg_ec[3].'.', $item);
+ }
+ }
+
+ $dot = ($split !== FALSE) ? '\.' : '';
+
+ return preg_replace('#'.$preg_ec[0].'?([^'.$preg_ec[1].$dot.']+)'.$preg_ec[1].'?(\.)?#i', $preg_ec[2].'$1'.$preg_ec[3].'$2', $item);
}
// --------------------------------------------------------------------
@@ -894,23 +1392,38 @@ class CI_DB_driver {
/**
* Generate an insert string
*
- * @access public
* @param string the table upon which the query will be performed
* @param array an associative array data of key/values
* @return string
*/
- function insert_string($table, $data)
+ public function insert_string($table, $data)
{
- $fields = array();
- $values = array();
+ $fields = $values = array();
foreach ($data as $key => $val)
{
- $fields[] = $this->_escape_identifiers($key);
+ $fields[] = $this->escape_identifiers($key);
$values[] = $this->escape($val);
}
- return $this->_insert($this->_protect_identifiers($table, TRUE, NULL, FALSE), $fields, $values);
+ return $this->_insert($this->protect_identifiers($table, TRUE, NULL, FALSE), $fields, $values);
+ }
+
+ // --------------------------------------------------------------------
+
+ /**
+ * Insert statement
+ *
+ * Generates a platform-specific insert string from the supplied data
+ *
+ * @param string the table name
+ * @param array the insert keys
+ * @param array the insert values
+ * @return string
+ */
+ protected function _insert($table, $keys, $values)
+ {
+ return 'INSERT INTO '.$table.' ('.implode(', ', $keys).') VALUES ('.implode(', ', $values).')';
}
// --------------------------------------------------------------------
@@ -918,51 +1431,53 @@ class CI_DB_driver {
/**
* Generate an update string
*
- * @access public
* @param string the table upon which the query will be performed
* @param array an associative array data of key/values
* @param mixed the "where" statement
* @return string
*/
- function update_string($table, $data, $where)
+ public function update_string($table, $data, $where)
{
- if ($where == '')
+ if (empty($where))
{
- return false;
+ return FALSE;
}
+ $this->where($where);
+
$fields = array();
foreach ($data as $key => $val)
{
- $fields[$this->_protect_identifiers($key)] = $this->escape($val);
+ $fields[$this->protect_identifiers($key)] = $this->escape($val);
}
- if ( ! is_array($where))
- {
- $dest = array($where);
- }
- else
- {
- $dest = array();
- foreach ($where as $key => $val)
- {
- $prefix = (count($dest) == 0) ? '' : ' AND ';
-
- if ($val !== '')
- {
- if ( ! $this->_has_operator($key))
- {
- $key .= ' =';
- }
+ $sql = $this->_update($this->protect_identifiers($table, TRUE, NULL, FALSE), $fields);
+ $this->_reset_write();
+ return $sql;
+ }
- $val = ' '.$this->escape($val);
- }
+ // --------------------------------------------------------------------
- $dest[] = $prefix.$key.$val;
- }
+ /**
+ * Update statement
+ *
+ * Generates a platform-specific update string from the supplied data
+ *
+ * @param string the table name
+ * @param array the update data
+ * @return string
+ */
+ protected function _update($table, $values)
+ {
+ foreach ($values as $key => $val)
+ {
+ $valstr[] = $key.' = '.$val;
}
- return $this->_update($this->_protect_identifiers($table, TRUE, NULL, FALSE), $fields, $dest);
+ return 'UPDATE '.$table.' SET '.implode(', ', $valstr)
+ .$this->_compile_wh('qb_where')
+ .$this->_compile_order_by()
+ .($this->qb_limit !== FALSE ? ' LIMIT '.$this->qb_limit : '');
}
// --------------------------------------------------------------------
@@ -970,19 +1485,51 @@ class CI_DB_driver {
/**
* Tests whether the string has an SQL operator
*
- * @access private
* @param string
* @return bool
*/
- function _has_operator($str)
+ protected function _has_operator($str)
{
- $str = trim($str);
- if ( ! preg_match("/(\s|<|>|!|=|is null|is not null)/i", $str))
- {
- return FALSE;
- }
+ return (bool) preg_match('/(<|>|!|=|\sIS NULL|\sIS NOT NULL|\sEXISTS|\sBETWEEN|\sLIKE|\sIN\s*\(|\s)/i', trim($str));
+ }
- return TRUE;
+ // --------------------------------------------------------------------
+
+ /**
+ * Returns the SQL string operator
+ *
+ * @param string
+ * @return string
+ */
+ protected function _get_operator($str)
+ {
+ static $_operators;
+
+ if (empty($_operators))
+ {
+ $_les = ($this->_like_escape_str !== '')
+ ? '\s+'.preg_quote(trim(sprintf($this->_like_escape_str, $this->_like_escape_chr)), '/')
+ : '';
+ $_operators = array(
+ '\s*(?:<|>|!)?=\s*', // =, <=, >=, !=
+ '\s*<>?\s*', // <, <>
+ '\s*>\s*', // >
+ '\s+IS NULL', // IS NULL
+ '\s+IS NOT NULL', // IS NOT NULL
+ '\s+EXISTS\s*\(.*\)', // EXISTS(sql)
+ '\s+NOT EXISTS\s*\(.*\)', // NOT EXISTS(sql)
+ '\s+BETWEEN\s+', // BETWEEN value AND value
+ '\s+NOT BETWEEN\s+', // NOT BETWEEN value AND value
+ '\s+IN\s*\(.*\)', // IN(list)
+ '\s+NOT IN\s*\(.*\)', // NOT IN (list)
+ '\s+LIKE\s+\S.*('.$_les.')?', // LIKE 'expr'[ ESCAPE '%s']
+ '\s+NOT LIKE\s+\S.*('.$_les.')?' // NOT LIKE 'expr'[ ESCAPE '%s']
+ );
+
+ }
+
+ return preg_match('/'.implode('|', $_operators).'/i', $str, $match)
+ ? $match[0] : FALSE;
}
// --------------------------------------------------------------------
@@ -990,14 +1537,12 @@ class CI_DB_driver {
/**
* Enables a native PHP function to be run, using a platform agnostic wrapper.
*
- * @access public
- * @param string the function name
- * @param mixed any parameters needed by the function
+ * @param string $function Function name
* @return mixed
*/
- function call_function($function)
+ public function call_function($function)
{
- $driver = ($this->dbdriver == 'postgre') ? 'pg_' : $this->dbdriver.'_';
+ $driver = ($this->dbdriver === 'postgre') ? 'pg_' : $this->dbdriver.'_';
if (FALSE === strpos($driver, $function))
{
@@ -1006,24 +1551,12 @@ class CI_DB_driver {
if ( ! function_exists($function))
{
- if ($this->db_debug)
- {
- return $this->display_error('db_unsupported_function');
- }
- return FALSE;
- }
- else
- {
- $args = (func_num_args() > 1) ? array_splice(func_get_args(), 1) : null;
- if (is_null($args))
- {
- return call_user_func($function);
- }
- else
- {
- return call_user_func_array($function, $args);
- }
+ return ($this->db_debug) ? $this->display_error('db_unsupported_function') : FALSE;
}
+
+ return (func_num_args() > 1)
+ ? call_user_func_array($function, array_slice(func_get_args(), 1))
+ : call_user_func($function);
}
// --------------------------------------------------------------------
@@ -1031,11 +1564,10 @@ class CI_DB_driver {
/**
* Set Cache Directory Path
*
- * @access public
* @param string the path to the cache directory
* @return void
*/
- function cache_set_path($path = '')
+ public function cache_set_path($path = '')
{
$this->cachedir = $path;
}
@@ -1045,13 +1577,11 @@ class CI_DB_driver {
/**
* Enable Query Caching
*
- * @access public
- * @return void
+ * @return bool cache_on value
*/
- function cache_on()
+ public function cache_on()
{
- $this->cache_on = TRUE;
- return TRUE;
+ return $this->cache_on = TRUE;
}
// --------------------------------------------------------------------
@@ -1059,31 +1589,27 @@ class CI_DB_driver {
/**
* Disable Query Caching
*
- * @access public
- * @return void
+ * @return bool cache_on value
*/
- function cache_off()
+ public function cache_off()
{
- $this->cache_on = FALSE;
- return FALSE;
+ return $this->cache_on = FALSE;
}
-
// --------------------------------------------------------------------
/**
* Delete the cache files associated with a particular URI
*
- * @access public
- * @return void
+ * @param string $segment_one = ''
+ * @param string $segment_two = ''
+ * @return bool
*/
- function cache_delete($segment_one = '', $segment_two = '')
+ public function cache_delete($segment_one = '', $segment_two = '')
{
- if ( ! $this->_cache_init())
- {
- return FALSE;
- }
- return $this->CACHE->delete($segment_one, $segment_two);
+ return $this->_cache_init()
+ ? $this->CACHE->delete($segment_one, $segment_two)
+ : FALSE;
}
// --------------------------------------------------------------------
@@ -1091,17 +1617,13 @@ class CI_DB_driver {
/**
* Delete All cache files
*
- * @access public
- * @return void
+ * @return bool
*/
- function cache_delete_all()
+ public function cache_delete_all()
{
- if ( ! $this->_cache_init())
- {
- return FALSE;
- }
-
- return $this->CACHE->delete_all();
+ return $this->_cache_init()
+ ? $this->CACHE->delete_all()
+ : FALSE;
}
// --------------------------------------------------------------------
@@ -1109,22 +1631,17 @@ class CI_DB_driver {
/**
* Initialize the Cache Class
*
- * @access private
- * @return void
+ * @return bool
*/
- function _cache_init()
+ protected function _cache_init()
{
- if (is_object($this->CACHE) AND class_exists('CI_DB_Cache'))
+ if ( ! class_exists('CI_DB_Cache', FALSE))
{
- return TRUE;
+ require_once(BASEPATH.'database/DB_cache.php');
}
-
- if ( ! class_exists('CI_DB_Cache'))
+ elseif (is_object($this->CACHE))
{
- if ( ! @include(BASEPATH.'database/DB_cache.php'))
- {
- return $this->cache_off();
- }
+ return TRUE;
}
$this->CACHE = new CI_DB_Cache($this); // pass db object to support multiple db connections and returned db objects
@@ -1136,15 +1653,28 @@ class CI_DB_driver {
/**
* Close DB Connection
*
- * @access public
* @return void
*/
- function close()
+ public function close()
{
- if (is_resource($this->conn_id) OR is_object($this->conn_id))
+ if ($this->conn_id)
{
- $this->_close($this->conn_id);
+ $this->_close();
+ $this->conn_id = FALSE;
}
+ }
+
+ // --------------------------------------------------------------------
+
+ /**
+ * Close DB Connection
+ *
+ * This method would be overridden by most of the drivers.
+ *
+ * @return void
+ */
+ protected function _close()
+ {
$this->conn_id = FALSE;
}
@@ -1153,49 +1683,54 @@ class CI_DB_driver {
/**
* Display an error message
*
- * @access public
* @param string the error message
* @param string any "swap" values
- * @param boolean whether to localize the message
- * @return string sends the application/error_db.php template
+ * @param bool whether to localize the message
+ * @return string sends the application/views/errors/error_db.php template
*/
- function display_error($error = '', $swap = '', $native = FALSE)
+ public function display_error($error = '', $swap = '', $native = FALSE)
{
$LANG =& load_class('Lang', 'core');
$LANG->load('db');
$heading = $LANG->line('db_error_heading');
- if ($native == TRUE)
+ if ($native === TRUE)
{
- $message = $error;
+ $message = (array) $error;
}
else
{
- $message = ( ! is_array($error)) ? array(str_replace('%s', $swap, $LANG->line($error))) : $error;
+ $message = is_array($error) ? $error : array(str_replace('%s', $swap, $LANG->line($error)));
}
// Find the most likely culprit of the error by going through
// the backtrace until the source file is no longer in the
// database folder.
-
$trace = debug_backtrace();
-
foreach ($trace as $call)
{
- if (isset($call['file']) && strpos($call['file'], BASEPATH.'database') === FALSE)
+ if (isset($call['file'], $call['class']))
{
- // Found it - use a relative path for safety
- $message[] = 'Filename: '.str_replace(array(BASEPATH, APPPATH), '', $call['file']);
- $message[] = 'Line Number: '.$call['line'];
+ // We'll need this on Windows, as APPPATH and BASEPATH will always use forward slashes
+ if (DIRECTORY_SEPARATOR !== '/')
+ {
+ $call['file'] = str_replace('\\', '/', $call['file']);
+ }
- break;
+ if (strpos($call['file'], BASEPATH.'database') === FALSE && strpos($call['class'], 'Loader') === FALSE)
+ {
+ // Found it - use a relative path for safety
+ $message[] = 'Filename: '.str_replace(array(APPPATH, BASEPATH), '', $call['file']);
+ $message[] = 'Line Number: '.$call['line'];
+ break;
+ }
}
}
$error =& load_class('Exceptions', 'core');
echo $error->show_error($heading, $message, 'error_db');
- exit;
+ exit(8); // EXIT_DATABASE
}
// --------------------------------------------------------------------
@@ -1203,29 +1738,13 @@ class CI_DB_driver {
/**
* Protect Identifiers
*
- * This function adds backticks if appropriate based on db type
- *
- * @access private
- * @param mixed the item to escape
- * @return mixed the item with backticks
- */
- function protect_identifiers($item, $prefix_single = FALSE)
- {
- return $this->_protect_identifiers($item, $prefix_single);
- }
-
- // --------------------------------------------------------------------
-
- /**
- * Protect Identifiers
- *
- * This function is used extensively by the Active Record class, and by
+ * This function is used extensively by the Query Builder class, and by
* a couple functions in this class.
* It takes a column or table name (optionally with an alias) and inserts
- * the table prefix onto it. Some logic is necessary in order to deal with
- * column names that include the path. Consider a query like this:
+ * the table prefix onto it. Some logic is necessary in order to deal with
+ * column names that include the path. Consider a query like this:
*
- * SELECT * FROM hostname.database.table.column AS c FROM hostname.database.table
+ * SELECT hostname.database.table.column AS c FROM hostname.database.table
*
* Or a query with aliasing:
*
@@ -1236,14 +1755,13 @@ class CI_DB_driver {
* insert the table prefix (if it exists) in the proper position, and escape only
* the correct identifiers.
*
- * @access private
* @param string
* @param bool
* @param mixed
* @param bool
* @return string
*/
- function _protect_identifiers($item, $prefix_single = FALSE, $protect_identifiers = NULL, $field_exists = TRUE)
+ public function protect_identifiers($item, $prefix_single = FALSE, $protect_identifiers = NULL, $field_exists = TRUE)
{
if ( ! is_bool($protect_identifiers))
{
@@ -1253,37 +1771,48 @@ class CI_DB_driver {
if (is_array($item))
{
$escaped_array = array();
-
foreach ($item as $k => $v)
{
- $escaped_array[$this->_protect_identifiers($k)] = $this->_protect_identifiers($v);
+ $escaped_array[$this->protect_identifiers($k)] = $this->protect_identifiers($v, $prefix_single, $protect_identifiers, $field_exists);
}
return $escaped_array;
}
+ // This is basically a bug fix for queries that use MAX, MIN, etc.
+ // If a parenthesis is found we know that we do not need to
+ // escape the data or add a prefix. There's probably a more graceful
+ // way to deal with this, but I'm not thinking of it -- Rick
+ //
+ // Added exception for single quotes as well, we don't want to alter
+ // literal strings. -- Narf
+ if (strcspn($item, "()'") !== strlen($item))
+ {
+ return $item;
+ }
+
// Convert tabs or multiple spaces into single spaces
- $item = preg_replace('/[\t ]+/', ' ', $item);
+ $item = preg_replace('/\s+/', ' ', trim($item));
// If the item has an alias declaration we remove it and set it aside.
- // Basically we remove everything to the right of the first space
- if (strpos($item, ' ') !== FALSE)
+ // Note: strripos() is used in order to support spaces in table names
+ if ($offset = strripos($item, ' AS '))
{
- $alias = strstr($item, ' ');
- $item = substr($item, 0, - strlen($alias));
+ $alias = ($protect_identifiers)
+ ? substr($item, $offset, 4).$this->escape_identifiers(substr($item, $offset + 4), FALSE)
+ : substr($item, $offset);
+ $item = substr($item, 0, $offset);
}
- else
+ elseif ($offset = strrpos($item, ' '))
{
- $alias = '';
+ $alias = ($protect_identifiers)
+ ? ' '.$this->escape_identifiers(substr($item, $offset + 1), FALSE)
+ : substr($item, $offset);
+ $item = substr($item, 0, $offset);
}
-
- // This is basically a bug fix for queries that use MAX, MIN, etc.
- // If a parenthesis is found we know that we do not need to
- // escape the data or add a prefix. There's probably a more graceful
- // way to deal with this, but I'm not thinking of it -- Rick
- if (strpos($item, '(') !== FALSE)
+ else
{
- return $item.$alias;
+ $alias = '';
}
// Break the string apart if it contains periods, then insert the table prefix
@@ -1291,12 +1820,15 @@ class CI_DB_driver {
// with an alias. While we're at it, we will escape the components
if (strpos($item, '.') !== FALSE)
{
- $parts = explode('.', $item);
+ $parts = explode('.', $item);
// Does the first segment of the exploded item match
- // one of the aliases previously identified? If so,
+ // one of the aliases previously identified? If so,
// we have nothing more to do other than escape the item
- if (in_array($parts[0], $this->ar_aliased_tables))
+ //
+ // NOTE: The ! empty() condition prevents this method
+ // from breaking when QB isn't enabled.
+ if ( ! empty($this->qb_aliased_tables) && in_array($parts[0], $this->qb_aliased_tables))
{
if ($protect_identifiers === TRUE)
{
@@ -1304,17 +1836,18 @@ class CI_DB_driver {
{
if ( ! in_array($val, $this->_reserved_identifiers))
{
- $parts[$key] = $this->_escape_identifiers($val);
+ $parts[$key] = $this->escape_identifiers($val);
}
}
$item = implode('.', $parts);
}
+
return $item.$alias;
}
- // Is there a table prefix defined in the config file? If not, no need to do anything
- if ($this->dbprefix != '')
+ // Is there a table prefix defined in the config file? If not, no need to do anything
+ if ($this->dbprefix !== '')
{
// We now add the table prefix based on some logic.
// Do we have 4 segments (hostname.database.table.column)?
@@ -1338,21 +1871,24 @@ class CI_DB_driver {
// This flag is set when the supplied $item does not contain a field name.
// This can happen when this function is being called from a JOIN.
- if ($field_exists == FALSE)
+ if ($field_exists === FALSE)
{
$i++;
}
+ // dbprefix may've already been applied, with or without the identifier escaped
+ $ec = '(?<ec>'.preg_quote(is_array($this->_escape_char) ? $this->_escape_char[0] : $this->_escape_char).')?';
+ isset($ec[0]) && $ec .= '?'; // Just in case someone has disabled escaping by forcing an empty escape character
+
// Verify table prefix and replace if necessary
- if ($this->swap_pre != '' && strncmp($parts[$i], $this->swap_pre, strlen($this->swap_pre)) === 0)
+ if ($this->swap_pre !== '' && preg_match('#^'.$ec.preg_quote($this->swap_pre).'#', $parts[$i]))
{
- $parts[$i] = preg_replace("/^".$this->swap_pre."(\S+?)/", $this->dbprefix."\\1", $parts[$i]);
+ $parts[$i] = preg_replace('#^'.$ec.preg_quote($this->swap_pre).'(\S+?)#', '\\1'.$this->dbprefix.'\\2', $parts[$i]);
}
-
// We only add the table prefix if it does not already exist
- if (substr($parts[$i], 0, strlen($this->dbprefix)) != $this->dbprefix)
+ else
{
- $parts[$i] = $this->dbprefix.$parts[$i];
+ preg_match('#^'.$ec.preg_quote($this->dbprefix).'#', $parts[$i]) OR $parts[$i] = $this->dbprefix.$parts[$i];
}
// Put the parts back together
@@ -1361,31 +1897,30 @@ class CI_DB_driver {
if ($protect_identifiers === TRUE)
{
- $item = $this->_escape_identifiers($item);
+ $item = $this->escape_identifiers($item);
}
return $item.$alias;
}
- // Is there a table prefix? If not, no need to insert it
- if ($this->dbprefix != '')
+ // Is there a table prefix? If not, no need to insert it
+ if ($this->dbprefix !== '')
{
// Verify table prefix and replace if necessary
- if ($this->swap_pre != '' && strncmp($item, $this->swap_pre, strlen($this->swap_pre)) === 0)
+ if ($this->swap_pre !== '' && strpos($item, $this->swap_pre) === 0)
{
- $item = preg_replace("/^".$this->swap_pre."(\S+?)/", $this->dbprefix."\\1", $item);
+ $item = preg_replace('/^'.$this->swap_pre.'(\S+?)/', $this->dbprefix.'\\1', $item);
}
-
// Do we prefix an item with no segments?
- if ($prefix_single == TRUE AND substr($item, 0, strlen($this->dbprefix)) != $this->dbprefix)
+ elseif ($prefix_single === TRUE && strpos($item, $this->dbprefix) !== 0)
{
$item = $this->dbprefix.$item;
}
}
- if ($protect_identifiers === TRUE AND ! in_array($item, $this->_reserved_identifiers))
+ if ($protect_identifiers === TRUE && ! in_array($item, $this->_reserved_identifiers))
{
- $item = $this->_escape_identifiers($item);
+ $item = $this->escape_identifiers($item);
}
return $item.$alias;
@@ -1394,9 +1929,8 @@ class CI_DB_driver {
// --------------------------------------------------------------------
/**
- * Dummy method that allows Active Record class to be disabled
- *
- * This function is used extensively by every db driver.
+ * Dummy method that allows Query Builder class to be disabled
+ * and keep count_all() working.
*
* @return void
*/
@@ -1405,6 +1939,3 @@ class CI_DB_driver {
}
}
-
-/* End of file DB_driver.php */
-/* Location: ./system/database/DB_driver.php */ \ No newline at end of file
diff --git a/system/database/DB_forge.php b/system/database/DB_forge.php
index b92069bbc..36679a464 100644
--- a/system/database/DB_forge.php
+++ b/system/database/DB_forge.php
@@ -1,46 +1,174 @@
-<?php if ( ! defined('BASEPATH')) exit('No direct script access allowed');
+<?php
/**
- * Code Igniter
+ * CodeIgniter
*
- * An open source application development framework for PHP 5.1.6 or newer
+ * An open source application development framework for PHP
*
- * @package CodeIgniter
- * @author ExpressionEngine Dev Team
- * @copyright Copyright (c) 2008 - 2014, EllisLab, Inc.
- * @license http://codeigniter.com/user_guide/license.html
- * @link http://codeigniter.com
- * @since Version 1.0
+ * This content is released under the MIT License (MIT)
+ *
+ * Copyright (c) 2019 - 2022, CodeIgniter Foundation
+ *
+ * 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.
+ *
+ * @package CodeIgniter
+ * @author EllisLab Dev Team
+ * @copyright Copyright (c) 2008 - 2014, EllisLab, Inc. (https://ellislab.com/)
+ * @copyright Copyright (c) 2014 - 2019, British Columbia Institute of Technology (https://bcit.ca/)
+ * @copyright Copyright (c) 2019 - 2022, CodeIgniter Foundation (https://codeigniter.com/)
+ * @license https://opensource.org/licenses/MIT MIT License
+ * @link https://codeigniter.com
+ * @since Version 1.0.0
* @filesource
*/
-
-// ------------------------------------------------------------------------
+defined('BASEPATH') OR exit('No direct script access allowed');
/**
- * Database Utility Class
+ * Database Forge Class
*
* @category Database
- * @author ExpressionEngine Dev Team
- * @link http://codeigniter.com/user_guide/database/
+ * @author EllisLab Dev Team
+ * @link https://codeigniter.com/userguide3/database/
*/
-class CI_DB_forge {
+abstract class CI_DB_forge {
+
+ /**
+ * Database object
+ *
+ * @var object
+ */
+ protected $db;
+
+ /**
+ * Fields data
+ *
+ * @var array
+ */
+ public $fields = array();
+
+ /**
+ * Keys data
+ *
+ * @var array
+ */
+ public $keys = array();
+
+ /**
+ * Primary Keys data
+ *
+ * @var array
+ */
+ public $primary_keys = array();
+
+ /**
+ * Database character set
+ *
+ * @var string
+ */
+ public $db_char_set = '';
- var $fields = array();
- var $keys = array();
- var $primary_keys = array();
- var $db_char_set = '';
+ // --------------------------------------------------------------------
+
+ /**
+ * CREATE DATABASE statement
+ *
+ * @var string
+ */
+ protected $_create_database = 'CREATE DATABASE %s';
+
+ /**
+ * DROP DATABASE statement
+ *
+ * @var string
+ */
+ protected $_drop_database = 'DROP DATABASE %s';
+
+ /**
+ * CREATE TABLE statement
+ *
+ * @var string
+ */
+ protected $_create_table = "%s %s (%s\n)";
/**
- * Constructor
+ * CREATE TABLE IF statement
*
- * Grabs the CI super object instance so we can access it.
+ * @var string
+ */
+ protected $_create_table_if = 'CREATE TABLE IF NOT EXISTS';
+
+ /**
+ * CREATE TABLE keys flag
+ *
+ * Whether table keys are created from within the
+ * CREATE TABLE statement.
*
+ * @var bool
*/
- function __construct()
+ protected $_create_table_keys = FALSE;
+
+ /**
+ * DROP TABLE IF EXISTS statement
+ *
+ * @var string
+ */
+ protected $_drop_table_if = 'DROP TABLE IF EXISTS';
+
+ /**
+ * RENAME TABLE statement
+ *
+ * @var string
+ */
+ protected $_rename_table = 'ALTER TABLE %s RENAME TO %s;';
+
+ /**
+ * UNSIGNED support
+ *
+ * @var bool|array
+ */
+ protected $_unsigned = TRUE;
+
+ /**
+ * NULL value representation in CREATE/ALTER TABLE statements
+ *
+ * @var string
+ */
+ protected $_null = '';
+
+ /**
+ * DEFAULT value representation in CREATE/ALTER TABLE statements
+ *
+ * @var string
+ */
+ protected $_default = ' DEFAULT ';
+
+ // --------------------------------------------------------------------
+
+ /**
+ * Class constructor
+ *
+ * @param object &$db Database object
+ * @return void
+ */
+ public function __construct(&$db)
{
- // Assign the main database object to $this->db
- $CI =& get_instance();
- $this->db =& $CI->db;
- log_message('debug', "Database Forge Class Initialized");
+ $this->db =& $db;
+ log_message('info', 'Database Forge Class Initialized');
}
// --------------------------------------------------------------------
@@ -48,20 +176,26 @@ class CI_DB_forge {
/**
* Create database
*
- * @access public
- * @param string the database name
+ * @param string $db_name
* @return bool
*/
- function create_database($db_name)
+ public function create_database($db_name)
{
- $sql = $this->_create_database($db_name);
+ if ($this->_create_database === FALSE)
+ {
+ return ($this->db->db_debug) ? $this->db->display_error('db_unsupported_feature') : FALSE;
+ }
+ elseif ( ! $this->db->query(sprintf($this->_create_database, $this->db->escape_identifiers($db_name), $this->db->char_set, $this->db->dbcollat)))
+ {
+ return ($this->db->db_debug) ? $this->db->display_error('db_unable_to_drop') : FALSE;
+ }
- if (is_bool($sql))
+ if ( ! empty($this->db->data_cache['db_names']))
{
- return $sql;
+ $this->db->data_cache['db_names'][] = $db_name;
}
- return $this->db->query($sql);
+ return TRUE;
}
// --------------------------------------------------------------------
@@ -69,20 +203,30 @@ class CI_DB_forge {
/**
* Drop database
*
- * @access public
- * @param string the database name
+ * @param string $db_name
* @return bool
*/
- function drop_database($db_name)
+ public function drop_database($db_name)
{
- $sql = $this->_drop_database($db_name);
+ if ($this->_drop_database === FALSE)
+ {
+ return ($this->db->db_debug) ? $this->db->display_error('db_unsupported_feature') : FALSE;
+ }
+ elseif ( ! $this->db->query(sprintf($this->_drop_database, $this->db->escape_identifiers($db_name))))
+ {
+ return ($this->db->db_debug) ? $this->db->display_error('db_unable_to_drop') : FALSE;
+ }
- if (is_bool($sql))
+ if ( ! empty($this->db->data_cache['db_names']))
{
- return $sql;
+ $key = array_search(strtolower($db_name), array_map('strtolower', $this->db->data_cache['db_names']), TRUE);
+ if ($key !== FALSE)
+ {
+ unset($this->db->data_cache['db_names'][$key]);
+ }
}
- return $this->db->query($sql);
+ return TRUE;
}
// --------------------------------------------------------------------
@@ -90,26 +234,26 @@ class CI_DB_forge {
/**
* Add Key
*
- * @access public
- * @param string key
- * @param string type
- * @return void
+ * @param string $key
+ * @param bool $primary
+ * @return CI_DB_forge
*/
- function add_key($key = '', $primary = FALSE)
+ public function add_key($key, $primary = FALSE)
{
- if (is_array($key))
+ // DO NOT change this! This condition is only applicable
+ // for PRIMARY keys because you can only have one such,
+ // and therefore all fields you add to it will be included
+ // in the same, composite PRIMARY KEY.
+ //
+ // It's not the same for regular indexes.
+ if ($primary === TRUE && is_array($key))
{
foreach ($key as $one)
{
$this->add_key($one, $primary);
}
- return;
- }
-
- if ($key == '')
- {
- show_error('Key information is required for that operation.');
+ return $this;
}
if ($primary === TRUE)
@@ -120,6 +264,8 @@ class CI_DB_forge {
{
$this->keys[] = $key;
}
+
+ return $this;
}
// --------------------------------------------------------------------
@@ -127,28 +273,22 @@ class CI_DB_forge {
/**
* Add Field
*
- * @access public
- * @param string collation
- * @return void
+ * @param array $field
+ * @return CI_DB_forge
*/
- function add_field($field = '')
+ public function add_field($field)
{
- if ($field == '')
- {
- show_error('Field information is required.');
- }
-
if (is_string($field))
{
- if ($field == 'id')
+ if ($field === 'id')
{
$this->add_field(array(
- 'id' => array(
- 'type' => 'INT',
- 'constraint' => 9,
- 'auto_increment' => TRUE
- )
- ));
+ 'id' => array(
+ 'type' => 'INT',
+ 'constraint' => 9,
+ 'auto_increment' => TRUE
+ )
+ ));
$this->add_key('id', TRUE);
}
else
@@ -167,6 +307,7 @@ class CI_DB_forge {
$this->fields = array_merge($this->fields, $field);
}
+ return $this;
}
// --------------------------------------------------------------------
@@ -174,26 +315,134 @@ class CI_DB_forge {
/**
* Create Table
*
- * @access public
- * @param string the table name
+ * @param string $table Table name
+ * @param bool $if_not_exists Whether to add IF NOT EXISTS condition
+ * @param array $attributes Associative array of table attributes
* @return bool
*/
- function create_table($table = '', $if_not_exists = FALSE)
+ public function create_table($table, $if_not_exists = FALSE, array $attributes = array())
{
- if ($table == '')
+ if ($table === '')
{
show_error('A table name is required for that operation.');
}
+ else
+ {
+ $table = $this->db->dbprefix.$table;
+ }
- if (count($this->fields) == 0)
+ if (count($this->fields) === 0)
{
show_error('Field information is required.');
}
- $sql = $this->_create_table($this->db->dbprefix.$table, $this->fields, $this->primary_keys, $this->keys, $if_not_exists);
+ $sql = $this->_create_table($table, $if_not_exists, $attributes);
+
+ if (is_bool($sql))
+ {
+ $this->_reset();
+ if ($sql === FALSE)
+ {
+ return ($this->db->db_debug) ? $this->db->display_error('db_unsupported_feature') : FALSE;
+ }
+ }
+
+ if (($result = $this->db->query($sql)) !== FALSE)
+ {
+ if (isset($this->db->data_cache['table_names']))
+ {
+ $this->db->data_cache['table_names'][] = $table;
+ }
+
+ // Most databases don't support creating indexes from within the CREATE TABLE statement
+ if ( ! empty($this->keys))
+ {
+ for ($i = 0, $sqls = $this->_process_indexes($table), $c = count($sqls); $i < $c; $i++)
+ {
+ $this->db->query($sqls[$i]);
+ }
+ }
+ }
$this->_reset();
- return $this->db->query($sql);
+ return $result;
+ }
+
+ // --------------------------------------------------------------------
+
+ /**
+ * Create Table
+ *
+ * @param string $table Table name
+ * @param bool $if_not_exists Whether to add 'IF NOT EXISTS' condition
+ * @param array $attributes Associative array of table attributes
+ * @return mixed
+ */
+ protected function _create_table($table, $if_not_exists, $attributes)
+ {
+ if ($if_not_exists === TRUE && $this->_create_table_if === FALSE)
+ {
+ if ($this->db->table_exists($table))
+ {
+ return TRUE;
+ }
+
+ $if_not_exists = FALSE;
+ }
+
+ $sql = ($if_not_exists)
+ ? sprintf($this->_create_table_if, $this->db->escape_identifiers($table))
+ : 'CREATE TABLE';
+
+ $columns = $this->_process_fields(TRUE);
+ for ($i = 0, $c = count($columns); $i < $c; $i++)
+ {
+ $columns[$i] = ($columns[$i]['_literal'] !== FALSE)
+ ? "\n\t".$columns[$i]['_literal']
+ : "\n\t".$this->_process_column($columns[$i]);
+ }
+
+ $columns = implode(',', $columns)
+ .$this->_process_primary_keys($table);
+
+ // Are indexes created from within the CREATE TABLE statement? (e.g. in MySQL)
+ if ($this->_create_table_keys === TRUE)
+ {
+ $columns .= $this->_process_indexes($table);
+ }
+
+ // _create_table will usually have the following format: "%s %s (%s\n)"
+ $sql = sprintf($this->_create_table.'%s',
+ $sql,
+ $this->db->escape_identifiers($table),
+ $columns,
+ $this->_create_table_attr($attributes)
+ );
+
+ return $sql;
+ }
+
+ // --------------------------------------------------------------------
+
+ /**
+ * CREATE TABLE attributes
+ *
+ * @param array $attributes Associative array of table attributes
+ * @return string
+ */
+ protected function _create_table_attr($attributes)
+ {
+ $sql = '';
+
+ foreach (array_keys($attributes) as $key)
+ {
+ if (is_string($key))
+ {
+ $sql .= ' '.strtoupper($key).' '.$attributes[$key];
+ }
+ }
+
+ return $sql;
}
// --------------------------------------------------------------------
@@ -201,20 +450,68 @@ class CI_DB_forge {
/**
* Drop Table
*
- * @access public
- * @param string the table name
+ * @param string $table_name Table name
+ * @param bool $if_exists Whether to add an IF EXISTS condition
* @return bool
*/
- function drop_table($table_name)
+ public function drop_table($table_name, $if_exists = FALSE)
{
- $sql = $this->_drop_table($this->db->dbprefix.$table_name);
+ if ($table_name === '')
+ {
+ return ($this->db->db_debug) ? $this->db->display_error('db_table_name_required') : FALSE;
+ }
- if (is_bool($sql))
+ if (($query = $this->_drop_table($this->db->dbprefix.$table_name, $if_exists)) === TRUE)
{
- return $sql;
+ return TRUE;
}
- return $this->db->query($sql);
+ $query = $this->db->query($query);
+
+ // Update table list cache
+ if ($query && ! empty($this->db->data_cache['table_names']))
+ {
+ $key = array_search(strtolower($this->db->dbprefix.$table_name), array_map('strtolower', $this->db->data_cache['table_names']), TRUE);
+ if ($key !== FALSE)
+ {
+ unset($this->db->data_cache['table_names'][$key]);
+ }
+ }
+
+ return $query;
+ }
+
+ // --------------------------------------------------------------------
+
+ /**
+ * Drop Table
+ *
+ * Generates a platform-specific DROP TABLE string
+ *
+ * @param string $table Table name
+ * @param bool $if_exists Whether to add an IF EXISTS condition
+ * @return mixed (Returns a platform-specific DROP table string, or TRUE to indicate there's nothing to do)
+ */
+ protected function _drop_table($table, $if_exists)
+ {
+ $sql = 'DROP TABLE';
+
+ if ($if_exists)
+ {
+ if ($this->_drop_table_if === FALSE)
+ {
+ if ( ! $this->db->table_exists($table))
+ {
+ return TRUE;
+ }
+ }
+ else
+ {
+ $sql = sprintf($this->_drop_table_if, $this->db->escape_identifiers($table));
+ }
+ }
+
+ return $sql.' '.$this->db->escape_identifiers($table);
}
// --------------------------------------------------------------------
@@ -222,20 +519,37 @@ class CI_DB_forge {
/**
* Rename Table
*
- * @access public
- * @param string the old table name
- * @param string the new table name
+ * @param string $table_name Old table name
+ * @param string $new_table_name New table name
* @return bool
*/
- function rename_table($table_name, $new_table_name)
+ public function rename_table($table_name, $new_table_name)
{
- if ($table_name == '' OR $new_table_name == '')
+ if ($table_name === '' OR $new_table_name === '')
{
show_error('A table name is required for that operation.');
+ return FALSE;
+ }
+ elseif ($this->_rename_table === FALSE)
+ {
+ return ($this->db->db_debug) ? $this->db->display_error('db_unsupported_feature') : FALSE;
}
- $sql = $this->_rename_table($this->db->dbprefix.$table_name, $this->db->dbprefix.$new_table_name);
- return $this->db->query($sql);
+ $result = $this->db->query(sprintf($this->_rename_table,
+ $this->db->escape_identifiers($this->db->dbprefix.$table_name),
+ $this->db->escape_identifiers($this->db->dbprefix.$new_table_name))
+ );
+
+ if ($result && ! empty($this->db->data_cache['table_names']))
+ {
+ $key = array_search(strtolower($this->db->dbprefix.$table_name), array_map('strtolower', $this->db->data_cache['table_names']), TRUE);
+ if ($key !== FALSE)
+ {
+ $this->db->data_cache['table_names'][$key] = $this->db->dbprefix.$new_table_name;
+ }
+ }
+
+ return $result;
}
// --------------------------------------------------------------------
@@ -243,120 +557,468 @@ class CI_DB_forge {
/**
* Column Add
*
- * @access public
- * @param string the table name
- * @param string the column name
- * @param string the column definition
+ * @param string $table Table name
+ * @param array $field Column definition
* @return bool
*/
- function add_column($table = '', $field = array(), $after_field = '')
+ public function add_column($table, $field)
{
- if ($table == '')
+ // Work-around for literal column definitions
+ is_array($field) OR $field = array($field);
+
+ foreach (array_keys($field) as $k)
{
- show_error('A table name is required for that operation.');
+ $this->add_field(array($k => $field[$k]));
}
- // add field info into field array, but we can only do one at a time
- // so we cycle through
-
- foreach ($field as $k => $v)
+ $sqls = $this->_alter_table('ADD', $this->db->dbprefix.$table, $this->_process_fields());
+ $this->_reset();
+ if ($sqls === FALSE)
{
- $this->add_field(array($k => $field[$k]));
+ return ($this->db->db_debug) ? $this->db->display_error('db_unsupported_feature') : FALSE;
+ }
- if (count($this->fields) == 0)
+ for ($i = 0, $c = count($sqls); $i < $c; $i++)
+ {
+ if ($this->db->query($sqls[$i]) === FALSE)
{
- show_error('Field information is required.');
+ return FALSE;
}
+ }
- $sql = $this->_alter_table('ADD', $this->db->dbprefix.$table, $this->fields, $after_field);
+ return TRUE;
+ }
- $this->_reset();
+ // --------------------------------------------------------------------
+
+ /**
+ * Column Drop
+ *
+ * @param string $table Table name
+ * @param string $column_name Column name
+ * @return bool
+ */
+ public function drop_column($table, $column_name)
+ {
+ $sql = $this->_alter_table('DROP', $this->db->dbprefix.$table, $column_name);
+ if ($sql === FALSE)
+ {
+ return ($this->db->db_debug) ? $this->db->display_error('db_unsupported_feature') : FALSE;
+ }
+
+ return $this->db->query($sql);
+ }
+
+ // --------------------------------------------------------------------
+
+ /**
+ * Column Modify
+ *
+ * @param string $table Table name
+ * @param string $field Column definition
+ * @return bool
+ */
+ public function modify_column($table, $field)
+ {
+ // Work-around for literal column definitions
+ is_array($field) OR $field = array($field);
- if ($this->db->query($sql) === FALSE)
+ foreach (array_keys($field) as $k)
+ {
+ $this->add_field(array($k => $field[$k]));
+ }
+
+ if (count($this->fields) === 0)
+ {
+ show_error('Field information is required.');
+ }
+
+ $sqls = $this->_alter_table('CHANGE', $this->db->dbprefix.$table, $this->_process_fields());
+ $this->_reset();
+ if ($sqls === FALSE)
+ {
+ return ($this->db->db_debug) ? $this->db->display_error('db_unsupported_feature') : FALSE;
+ }
+
+ for ($i = 0, $c = count($sqls); $i < $c; $i++)
+ {
+ if ($this->db->query($sqls[$i]) === FALSE)
{
return FALSE;
}
}
return TRUE;
-
}
// --------------------------------------------------------------------
/**
- * Column Drop
+ * ALTER TABLE
*
- * @access public
- * @param string the table name
- * @param string the column name
- * @return bool
+ * @param string $alter_type ALTER type
+ * @param string $table Table name
+ * @param mixed $field Column definition
+ * @return string|string[]
*/
- function drop_column($table = '', $column_name = '')
+ protected function _alter_table($alter_type, $table, $field)
{
+ $sql = 'ALTER TABLE '.$this->db->escape_identifiers($table).' ';
- if ($table == '')
+ // DROP has everything it needs now.
+ if ($alter_type === 'DROP')
{
- show_error('A table name is required for that operation.');
+ return $sql.'DROP COLUMN '.$this->db->escape_identifiers($field);
}
- if ($column_name == '')
+ $sql .= ($alter_type === 'ADD')
+ ? 'ADD '
+ : $alter_type.' COLUMN ';
+
+ $sqls = array();
+ for ($i = 0, $c = count($field); $i < $c; $i++)
{
- show_error('A column name is required for that operation.');
+ $sqls[] = $sql
+ .($field[$i]['_literal'] !== FALSE ? $field[$i]['_literal'] : $this->_process_column($field[$i]));
}
- $sql = $this->_alter_table('DROP', $this->db->dbprefix.$table, $column_name);
+ return $sqls;
+ }
- return $this->db->query($sql);
+ // --------------------------------------------------------------------
+
+ /**
+ * Process fields
+ *
+ * @param bool $create_table
+ * @return array
+ */
+ protected function _process_fields($create_table = FALSE)
+ {
+ $fields = array();
+
+ foreach ($this->fields as $key => $attributes)
+ {
+ if (is_int($key) && ! is_array($attributes))
+ {
+ $fields[] = array('_literal' => $attributes);
+ continue;
+ }
+
+ $attributes = array_change_key_case($attributes, CASE_UPPER);
+
+ if ($create_table === TRUE && empty($attributes['TYPE']))
+ {
+ continue;
+ }
+
+ isset($attributes['TYPE']) && $this->_attr_type($attributes);
+
+ $field = array(
+ 'name' => $key,
+ 'new_name' => isset($attributes['NAME']) ? $attributes['NAME'] : NULL,
+ 'type' => isset($attributes['TYPE']) ? $attributes['TYPE'] : NULL,
+ 'length' => '',
+ 'unsigned' => '',
+ 'null' => NULL,
+ 'unique' => '',
+ 'default' => '',
+ 'auto_increment' => '',
+ '_literal' => FALSE
+ );
+
+ isset($attributes['TYPE']) && $this->_attr_unsigned($attributes, $field);
+
+ if ($create_table === FALSE)
+ {
+ if (isset($attributes['AFTER']))
+ {
+ $field['after'] = $attributes['AFTER'];
+ }
+ elseif (isset($attributes['FIRST']))
+ {
+ $field['first'] = (bool) $attributes['FIRST'];
+ }
+ }
+
+ $this->_attr_default($attributes, $field);
+
+ if (isset($attributes['NULL']))
+ {
+ if ($attributes['NULL'] === TRUE)
+ {
+ $field['null'] = empty($this->_null) ? '' : ' '.$this->_null;
+ }
+ else
+ {
+ $field['null'] = ' NOT NULL';
+ }
+ }
+ elseif ($create_table === TRUE)
+ {
+ $field['null'] = ' NOT NULL';
+ }
+
+ $this->_attr_auto_increment($attributes, $field);
+ $this->_attr_unique($attributes, $field);
+
+ if (isset($attributes['COMMENT']))
+ {
+ $field['comment'] = $this->db->escape($attributes['COMMENT']);
+ }
+
+ if (isset($attributes['TYPE']) && ! empty($attributes['CONSTRAINT']))
+ {
+ switch (strtoupper($attributes['TYPE']))
+ {
+ case 'ENUM':
+ case 'SET':
+ $attributes['CONSTRAINT'] = $this->db->escape($attributes['CONSTRAINT']);
+ default:
+ $field['length'] = is_array($attributes['CONSTRAINT'])
+ ? '('.implode(',', $attributes['CONSTRAINT']).')'
+ : '('.$attributes['CONSTRAINT'].')';
+ break;
+ }
+ }
+
+ $fields[] = $field;
+ }
+
+ return $fields;
}
// --------------------------------------------------------------------
/**
- * Column Modify
+ * Process column
*
- * @access public
- * @param string the table name
- * @param string the column name
- * @param string the column definition
- * @return bool
+ * @param array $field
+ * @return string
*/
- function modify_column($table = '', $field = array())
+ protected function _process_column($field)
{
- if ($table == '')
+ return $this->db->escape_identifiers($field['name'])
+ .' '.$field['type'].$field['length']
+ .$field['unsigned']
+ .$field['default']
+ .$field['null']
+ .$field['auto_increment']
+ .$field['unique'];
+ }
+
+ // --------------------------------------------------------------------
+
+ /**
+ * Field attribute TYPE
+ *
+ * Performs a data type mapping between different databases.
+ *
+ * @param array &$attributes
+ * @return void
+ */
+ protected function _attr_type(&$attributes)
+ {
+ // Usually overridden by drivers
+ }
+
+ // --------------------------------------------------------------------
+
+ /**
+ * Field attribute UNSIGNED
+ *
+ * Depending on the _unsigned property value:
+ *
+ * - TRUE will always set $field['unsigned'] to 'UNSIGNED'
+ * - FALSE will always set $field['unsigned'] to ''
+ * - array(TYPE) will set $field['unsigned'] to 'UNSIGNED',
+ * if $attributes['TYPE'] is found in the array
+ * - array(TYPE => UTYPE) will change $field['type'],
+ * from TYPE to UTYPE in case of a match
+ *
+ * @param array &$attributes
+ * @param array &$field
+ * @return void
+ */
+ protected function _attr_unsigned(&$attributes, &$field)
+ {
+ if (empty($attributes['UNSIGNED']) OR $attributes['UNSIGNED'] !== TRUE)
{
- show_error('A table name is required for that operation.');
+ return;
}
- // add field info into field array, but we can only do one at a time
- // so we cycle through
+ // Reset the attribute in order to avoid issues if we do type conversion
+ $attributes['UNSIGNED'] = FALSE;
- foreach ($field as $k => $v)
+ if (is_array($this->_unsigned))
{
- // If no name provided, use the current name
- if ( ! isset($field[$k]['name']))
+ foreach (array_keys($this->_unsigned) as $key)
{
- $field[$k]['name'] = $k;
+ if (is_int($key) && strcasecmp($attributes['TYPE'], $this->_unsigned[$key]) === 0)
+ {
+ $field['unsigned'] = ' UNSIGNED';
+ return;
+ }
+ elseif (is_string($key) && strcasecmp($attributes['TYPE'], $key) === 0)
+ {
+ $field['type'] = $key;
+ return;
+ }
}
- $this->add_field(array($k => $field[$k]));
+ return;
+ }
+
+ $field['unsigned'] = ($this->_unsigned === TRUE) ? ' UNSIGNED' : '';
+ }
+
+ // --------------------------------------------------------------------
+
+ /**
+ * Field attribute DEFAULT
+ *
+ * @param array &$attributes
+ * @param array &$field
+ * @return void
+ */
+ protected function _attr_default(&$attributes, &$field)
+ {
+ if ($this->_default === FALSE)
+ {
+ return;
+ }
+
+ if ( ! array_key_exists('DEFAULT', $attributes))
+ {
+ return;
+ }
+
+ if ($attributes['DEFAULT'] === NULL)
+ {
+ $field['default'] = empty($this->_null) ? '' : $this->_default.$this->_null;
+
+ // Override the NULL attribute if that's our default
+ $attributes['NULL'] = TRUE;
+ $field['null'] = empty($this->_null) ? '' : ' '.$this->_null;
+ return;
+ }
+
+ // White-list CURRENT_TIMESTAMP & similar (e.g. Oracle has stuff like SYSTIMESTAMP) defaults for date/time fields
+ if (
+ isset($attributes['TYPE'])
+ && (stripos($attributes['TYPE'], 'time') !== FALSE OR stripos($attributes['TYPE'], 'date') !== FALSE)
+ && (stripos($attributes['DEFAULT'], 'time') !== FALSE OR stripos($attributes['DEFAULT'], 'date') !== FALSE)
+ )
+ {
+ $field['default'] = $this->_default.$attributes['DEFAULT'];
+ return;
+ }
+
+ $field['default'] = $this->_default.$this->db->escape($attributes['DEFAULT']);
+ }
+
+ // --------------------------------------------------------------------
+
+ /**
+ * Field attribute UNIQUE
+ *
+ * @param array &$attributes
+ * @param array &$field
+ * @return void
+ */
+ protected function _attr_unique(&$attributes, &$field)
+ {
+ if ( ! empty($attributes['UNIQUE']) && $attributes['UNIQUE'] === TRUE)
+ {
+ $field['unique'] = ' UNIQUE';
+ }
+ }
+
+ // --------------------------------------------------------------------
+
+ /**
+ * Field attribute AUTO_INCREMENT
+ *
+ * @param array &$attributes
+ * @param array &$field
+ * @return void
+ */
+ protected function _attr_auto_increment(&$attributes, &$field)
+ {
+ if ( ! empty($attributes['AUTO_INCREMENT']) && $attributes['AUTO_INCREMENT'] === TRUE && stripos($field['type'], 'int') !== FALSE)
+ {
+ $field['auto_increment'] = ' AUTO_INCREMENT';
+ }
+ }
+
+ // --------------------------------------------------------------------
- if (count($this->fields) == 0)
+ /**
+ * Process primary keys
+ *
+ * @param string $table Table name
+ * @return string
+ */
+ protected function _process_primary_keys($table)
+ {
+ $sql = '';
+
+ for ($i = 0, $c = count($this->primary_keys); $i < $c; $i++)
+ {
+ if ( ! isset($this->fields[$this->primary_keys[$i]]))
{
- show_error('Field information is required.');
+ unset($this->primary_keys[$i]);
}
+ }
- $sql = $this->_alter_table('CHANGE', $this->db->dbprefix.$table, $this->fields);
+ if (count($this->primary_keys) > 0)
+ {
+ $sql .= ",\n\tCONSTRAINT ".$this->db->escape_identifiers('pk_'.$table)
+ .' PRIMARY KEY('.implode(', ', $this->db->escape_identifiers($this->primary_keys)).')';
+ }
- $this->_reset();
+ return $sql;
+ }
+
+ // --------------------------------------------------------------------
- if ($this->db->query($sql) === FALSE)
+ /**
+ * Process indexes
+ *
+ * @param string $table Table name
+ * @return string[] list of SQL statements
+ */
+ protected function _process_indexes($table)
+ {
+ $sqls = array();
+
+ for ($i = 0, $c = count($this->keys); $i < $c; $i++)
+ {
+ if (is_array($this->keys[$i]))
{
- return FALSE;
+ for ($i2 = 0, $c2 = count($this->keys[$i]); $i2 < $c2; $i2++)
+ {
+ if ( ! isset($this->fields[$this->keys[$i][$i2]]))
+ {
+ unset($this->keys[$i][$i2]);
+ continue;
+ }
+ }
}
+ elseif ( ! isset($this->fields[$this->keys[$i]]))
+ {
+ unset($this->keys[$i]);
+ continue;
+ }
+
+ is_array($this->keys[$i]) OR $this->keys[$i] = array($this->keys[$i]);
+
+ $sqls[] = 'CREATE INDEX '.$this->db->escape_identifiers($table.'_'.implode('_', $this->keys[$i]))
+ .' ON '.$this->db->escape_identifiers($table)
+ .' ('.implode(', ', $this->db->escape_identifiers($this->keys[$i])).');';
}
- return TRUE;
+ return $sqls;
}
// --------------------------------------------------------------------
@@ -366,17 +1028,11 @@ class CI_DB_forge {
*
* Resets table creation vars
*
- * @access private
* @return void
*/
- function _reset()
+ protected function _reset()
{
- $this->fields = array();
- $this->keys = array();
- $this->primary_keys = array();
+ $this->fields = $this->keys = $this->primary_keys = array();
}
}
-
-/* End of file DB_forge.php */
-/* Location: ./system/database/DB_forge.php */ \ No newline at end of file
diff --git a/system/database/DB_query_builder.php b/system/database/DB_query_builder.php
new file mode 100644
index 000000000..de6aa04fc
--- /dev/null
+++ b/system/database/DB_query_builder.php
@@ -0,0 +1,2881 @@
+<?php
+/**
+ * CodeIgniter
+ *
+ * An open source application development framework for PHP
+ *
+ * This content is released under the MIT License (MIT)
+ *
+ * Copyright (c) 2019 - 2022, CodeIgniter Foundation
+ *
+ * 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.
+ *
+ * @package CodeIgniter
+ * @author EllisLab Dev Team
+ * @copyright Copyright (c) 2008 - 2014, EllisLab, Inc. (https://ellislab.com/)
+ * @copyright Copyright (c) 2014 - 2019, British Columbia Institute of Technology (https://bcit.ca/)
+ * @copyright Copyright (c) 2019 - 2022, CodeIgniter Foundation (https://codeigniter.com/)
+ * @license https://opensource.org/licenses/MIT MIT License
+ * @link https://codeigniter.com
+ * @since Version 1.0.0
+ * @filesource
+ */
+defined('BASEPATH') OR exit('No direct script access allowed');
+
+/**
+ * Query Builder Class
+ *
+ * This is the platform-independent base Query Builder implementation class.
+ *
+ * @package CodeIgniter
+ * @subpackage Drivers
+ * @category Database
+ * @author EllisLab Dev Team
+ * @link https://codeigniter.com/userguide3/database/
+ */
+
+abstract class CI_DB_query_builder extends CI_DB_driver {
+
+ /**
+ * Return DELETE SQL flag
+ *
+ * @var bool
+ */
+ protected $return_delete_sql = FALSE;
+
+ /**
+ * Reset DELETE data flag
+ *
+ * @var bool
+ */
+ protected $reset_delete_data = FALSE;
+
+ /**
+ * QB SELECT data
+ *
+ * @var array
+ */
+ protected $qb_select = array();
+
+ /**
+ * QB DISTINCT flag
+ *
+ * @var bool
+ */
+ protected $qb_distinct = FALSE;
+
+ /**
+ * QB FROM data
+ *
+ * @var array
+ */
+ protected $qb_from = array();
+
+ /**
+ * QB JOIN data
+ *
+ * @var array
+ */
+ protected $qb_join = array();
+
+ /**
+ * QB WHERE data
+ *
+ * @var array
+ */
+ protected $qb_where = array();
+
+ /**
+ * QB GROUP BY data
+ *
+ * @var array
+ */
+ protected $qb_groupby = array();
+
+ /**
+ * QB HAVING data
+ *
+ * @var array
+ */
+ protected $qb_having = array();
+
+ /**
+ * QB keys
+ *
+ * @var array
+ */
+ protected $qb_keys = array();
+
+ /**
+ * QB LIMIT data
+ *
+ * @var int
+ */
+ protected $qb_limit = FALSE;
+
+ /**
+ * QB OFFSET data
+ *
+ * @var int
+ */
+ protected $qb_offset = FALSE;
+
+ /**
+ * QB ORDER BY data
+ *
+ * @var array
+ */
+ protected $qb_orderby = array();
+
+ /**
+ * QB data sets
+ *
+ * @var array
+ */
+ protected $qb_set = array();
+
+ /**
+ * QB data set for update_batch()
+ *
+ * @var array
+ */
+ protected $qb_set_ub = array();
+
+ /**
+ * QB aliased tables list
+ *
+ * @var array
+ */
+ protected $qb_aliased_tables = array();
+
+ /**
+ * QB WHERE group started flag
+ *
+ * @var bool
+ */
+ protected $qb_where_group_started = FALSE;
+
+ /**
+ * QB WHERE group count
+ *
+ * @var int
+ */
+ protected $qb_where_group_count = 0;
+
+ // Query Builder Caching variables
+
+ /**
+ * QB Caching flag
+ *
+ * @var bool
+ */
+ protected $qb_caching = FALSE;
+
+ /**
+ * QB Cache exists list
+ *
+ * @var array
+ */
+ protected $qb_cache_exists = array();
+
+ /**
+ * QB Cache SELECT data
+ *
+ * @var array
+ */
+ protected $qb_cache_select = array();
+
+ /**
+ * QB Cache FROM data
+ *
+ * @var array
+ */
+ protected $qb_cache_from = array();
+
+ /**
+ * QB Cache JOIN data
+ *
+ * @var array
+ */
+ protected $qb_cache_join = array();
+
+ /**
+ * QB Cache aliased tables list
+ *
+ * @var array
+ */
+ protected $qb_cache_aliased_tables = array();
+
+ /**
+ * QB Cache WHERE data
+ *
+ * @var array
+ */
+ protected $qb_cache_where = array();
+
+ /**
+ * QB Cache GROUP BY data
+ *
+ * @var array
+ */
+ protected $qb_cache_groupby = array();
+
+ /**
+ * QB Cache HAVING data
+ *
+ * @var array
+ */
+ protected $qb_cache_having = array();
+
+ /**
+ * QB Cache ORDER BY data
+ *
+ * @var array
+ */
+ protected $qb_cache_orderby = array();
+
+ /**
+ * QB Cache data sets
+ *
+ * @var array
+ */
+ protected $qb_cache_set = array();
+
+ /**
+ * QB No Escape data
+ *
+ * @var array
+ */
+ protected $qb_no_escape = array();
+
+ /**
+ * QB Cache No Escape data
+ *
+ * @var array
+ */
+ protected $qb_cache_no_escape = array();
+
+ // --------------------------------------------------------------------
+
+ /**
+ * Select
+ *
+ * Generates the SELECT portion of the query
+ *
+ * @param string
+ * @param mixed
+ * @return CI_DB_query_builder
+ */
+ public function select($select = '*', $escape = NULL)
+ {
+ if (is_string($select))
+ {
+ $select = explode(',', $select);
+ }
+
+ // If the escape value was not set, we will base it on the global setting
+ is_bool($escape) OR $escape = $this->_protect_identifiers;
+
+ foreach ($select as $val)
+ {
+ $val = trim($val);
+
+ if ($val !== '')
+ {
+ $this->qb_select[] = $val;
+ $this->qb_no_escape[] = $escape;
+
+ if ($this->qb_caching === TRUE)
+ {
+ $this->qb_cache_select[] = $val;
+ $this->qb_cache_exists[] = 'select';
+ $this->qb_cache_no_escape[] = $escape;
+ }
+ }
+ }
+
+ return $this;
+ }
+
+ // --------------------------------------------------------------------
+
+ /**
+ * Select Max
+ *
+ * Generates a SELECT MAX(field) portion of a query
+ *
+ * @param string the field
+ * @param string an alias
+ * @return CI_DB_query_builder
+ */
+ public function select_max($select = '', $alias = '')
+ {
+ return $this->_max_min_avg_sum($select, $alias, 'MAX');
+ }
+
+ // --------------------------------------------------------------------
+
+ /**
+ * Select Min
+ *
+ * Generates a SELECT MIN(field) portion of a query
+ *
+ * @param string the field
+ * @param string an alias
+ * @return CI_DB_query_builder
+ */
+ public function select_min($select = '', $alias = '')
+ {
+ return $this->_max_min_avg_sum($select, $alias, 'MIN');
+ }
+
+ // --------------------------------------------------------------------
+
+ /**
+ * Select Average
+ *
+ * Generates a SELECT AVG(field) portion of a query
+ *
+ * @param string the field
+ * @param string an alias
+ * @return CI_DB_query_builder
+ */
+ public function select_avg($select = '', $alias = '')
+ {
+ return $this->_max_min_avg_sum($select, $alias, 'AVG');
+ }
+
+ // --------------------------------------------------------------------
+
+ /**
+ * Select Sum
+ *
+ * Generates a SELECT SUM(field) portion of a query
+ *
+ * @param string the field
+ * @param string an alias
+ * @return CI_DB_query_builder
+ */
+ public function select_sum($select = '', $alias = '')
+ {
+ return $this->_max_min_avg_sum($select, $alias, 'SUM');
+ }
+
+ // --------------------------------------------------------------------
+
+ /**
+ * SELECT [MAX|MIN|AVG|SUM]()
+ *
+ * @used-by select_max()
+ * @used-by select_min()
+ * @used-by select_avg()
+ * @used-by select_sum()
+ *
+ * @param string $select Field name
+ * @param string $alias
+ * @param string $type
+ * @return CI_DB_query_builder
+ */
+ protected function _max_min_avg_sum($select = '', $alias = '', $type = 'MAX')
+ {
+ if ( ! is_string($select) OR $select === '')
+ {
+ $this->display_error('db_invalid_query');
+ }
+
+ $type = strtoupper($type);
+
+ if ( ! in_array($type, array('MAX', 'MIN', 'AVG', 'SUM')))
+ {
+ show_error('Invalid function type: '.$type);
+ }
+
+ if ($alias === '')
+ {
+ $alias = $this->_create_alias_from_table(trim($select));
+ }
+
+ $sql = $type.'('.$this->protect_identifiers(trim($select)).') AS '.$this->escape_identifiers(trim($alias));
+
+ $this->qb_select[] = $sql;
+ $this->qb_no_escape[] = NULL;
+
+ if ($this->qb_caching === TRUE)
+ {
+ $this->qb_cache_select[] = $sql;
+ $this->qb_cache_exists[] = 'select';
+ }
+
+ return $this;
+ }
+
+ // --------------------------------------------------------------------
+
+ /**
+ * Determines the alias name based on the table
+ *
+ * @param string $item
+ * @return string
+ */
+ protected function _create_alias_from_table($item)
+ {
+ if (strpos($item, '.') !== FALSE)
+ {
+ $item = explode('.', $item);
+ return end($item);
+ }
+
+ return $item;
+ }
+
+ // --------------------------------------------------------------------
+
+ /**
+ * DISTINCT
+ *
+ * Sets a flag which tells the query string compiler to add DISTINCT
+ *
+ * @param bool $val
+ * @return CI_DB_query_builder
+ */
+ public function distinct($val = TRUE)
+ {
+ $this->qb_distinct = is_bool($val) ? $val : TRUE;
+ return $this;
+ }
+
+ // --------------------------------------------------------------------
+
+ /**
+ * From
+ *
+ * Generates the FROM portion of the query
+ *
+ * @param mixed $from can be a string or array
+ * @return CI_DB_query_builder
+ */
+ public function from($from)
+ {
+ foreach ((array) $from as $val)
+ {
+ if (strpos($val, ',') !== FALSE)
+ {
+ foreach (explode(',', $val) as $v)
+ {
+ $v = trim($v);
+ $this->_track_aliases($v);
+
+ $this->qb_from[] = $v = $this->protect_identifiers($v, TRUE, NULL, FALSE);
+
+ if ($this->qb_caching === TRUE)
+ {
+ $this->qb_cache_from[] = $v;
+ $this->qb_cache_exists[] = 'from';
+ }
+ }
+ }
+ else
+ {
+ $val = trim($val);
+
+ // Extract any aliases that might exist. We use this information
+ // in the protect_identifiers to know whether to add a table prefix
+ $this->_track_aliases($val);
+
+ $this->qb_from[] = $val = $this->protect_identifiers($val, TRUE, NULL, FALSE);
+
+ if ($this->qb_caching === TRUE)
+ {
+ $this->qb_cache_from[] = $val;
+ $this->qb_cache_exists[] = 'from';
+ }
+ }
+ }
+
+ return $this;
+ }
+
+ // --------------------------------------------------------------------
+
+ /**
+ * JOIN
+ *
+ * Generates the JOIN portion of the query
+ *
+ * @param string
+ * @param string the join condition
+ * @param string the type of join
+ * @param string whether not to try to escape identifiers
+ * @return CI_DB_query_builder
+ */
+ public function join($table, $cond, $type = '', $escape = NULL)
+ {
+ $type = trim(strtoupper($type).' JOIN');
+ preg_match('#^(NATURAL\s+)?((LEFT|RIGHT|FULL)\s+)?((INNER|OUTER)\s+)?JOIN$#', $type) OR $type = 'JOIN';
+
+ // Extract any aliases that might exist. We use this information
+ // in the protect_identifiers to know whether to add a table prefix
+ $this->_track_aliases($table);
+
+ is_bool($escape) OR $escape = $this->_protect_identifiers;
+
+ if (strpos($type, 'NATURAL') === 0)
+ {
+ $cond = '';
+ }
+ elseif ( ! $this->_has_operator($cond))
+ {
+ $cond = ' USING ('.($escape ? $this->escape_identifiers($cond) : $cond).')';
+ }
+ elseif ($escape === FALSE)
+ {
+ $cond = ' ON '.$cond;
+ }
+ else
+ {
+ // Split multiple conditions
+ if (preg_match_all('/\sAND\s|\sOR\s/i', $cond, $joints, PREG_OFFSET_CAPTURE))
+ {
+ $conditions = array();
+ $joints = $joints[0];
+ array_unshift($joints, array('', 0));
+
+ for ($i = count($joints) - 1, $pos = strlen($cond); $i >= 0; $i--)
+ {
+ $joints[$i][1] += strlen($joints[$i][0]); // offset
+ $conditions[$i] = substr($cond, $joints[$i][1], $pos - $joints[$i][1]);
+ $pos = $joints[$i][1] - strlen($joints[$i][0]);
+ $joints[$i] = $joints[$i][0];
+ }
+ }
+ else
+ {
+ $conditions = array($cond);
+ $joints = array('');
+ }
+
+ $cond = ' ON ';
+ for ($i = 0, $c = count($conditions); $i < $c; $i++)
+ {
+ $operator = $this->_get_operator($conditions[$i]);
+ $cond .= $joints[$i];
+ $cond .= preg_match("/(\(*)?([\[\]\w\.'-]+)".preg_quote($operator)."(.*)/i", $conditions[$i], $match)
+ ? $match[1].$this->protect_identifiers($match[2]).$operator.$this->protect_identifiers($match[3])
+ : $conditions[$i];
+ }
+ }
+
+ // Do we want to escape the table name?
+ if ($escape === TRUE)
+ {
+ $table = $this->protect_identifiers($table, TRUE, NULL, FALSE);
+ }
+
+ // Assemble the JOIN statement
+ $this->qb_join[] = $join = $type.' '.$table.$cond;
+
+ if ($this->qb_caching === TRUE)
+ {
+ $this->qb_cache_join[] = $join;
+ $this->qb_cache_exists[] = 'join';
+ }
+
+ return $this;
+ }
+
+ // --------------------------------------------------------------------
+
+ /**
+ * WHERE
+ *
+ * Generates the WHERE portion of the query.
+ * Separates multiple calls with 'AND'.
+ *
+ * @param mixed
+ * @param mixed
+ * @param bool
+ * @return CI_DB_query_builder
+ */
+ public function where($key, $value = NULL, $escape = NULL)
+ {
+ return $this->_wh('qb_where', $key, $value, 'AND ', $escape);
+ }
+
+ // --------------------------------------------------------------------
+
+ /**
+ * OR WHERE
+ *
+ * Generates the WHERE portion of the query.
+ * Separates multiple calls with 'OR'.
+ *
+ * @param mixed
+ * @param mixed
+ * @param bool
+ * @return CI_DB_query_builder
+ */
+ public function or_where($key, $value = NULL, $escape = NULL)
+ {
+ return $this->_wh('qb_where', $key, $value, 'OR ', $escape);
+ }
+
+ // --------------------------------------------------------------------
+
+ /**
+ * WHERE, HAVING
+ *
+ * @used-by where()
+ * @used-by or_where()
+ * @used-by having()
+ * @used-by or_having()
+ *
+ * @param string $qb_key 'qb_where' or 'qb_having'
+ * @param mixed $key
+ * @param mixed $value
+ * @param string $type
+ * @param bool $escape
+ * @return CI_DB_query_builder
+ */
+ protected function _wh($qb_key, $key, $value = NULL, $type = 'AND ', $escape = NULL)
+ {
+ $qb_cache_key = ($qb_key === 'qb_having') ? 'qb_cache_having' : 'qb_cache_where';
+
+ if ( ! is_array($key))
+ {
+ $key = array($key => $value);
+ }
+
+ // If the escape value was not set will base it on the global setting
+ is_bool($escape) OR $escape = $this->_protect_identifiers;
+
+ foreach ($key as $k => $v)
+ {
+ $prefix = (count($this->$qb_key) === 0 && count($this->$qb_cache_key) === 0)
+ ? $this->_group_get_type('')
+ : $this->_group_get_type($type);
+
+ if ($v !== NULL)
+ {
+ if ($escape === TRUE)
+ {
+ $v = $this->escape($v);
+ }
+
+ if ( ! $this->_has_operator($k))
+ {
+ $k .= ' = ';
+ }
+ }
+ elseif ( ! $this->_has_operator($k))
+ {
+ // value appears not to have been set, assign the test to IS NULL
+ $k .= ' IS NULL';
+ }
+ elseif (preg_match('/\s*(!?=|<>|\sIS(?:\s+NOT)?\s)\s*$/i', $k, $match, PREG_OFFSET_CAPTURE))
+ {
+ $k = substr($k, 0, $match[0][1]).($match[1][0] === '=' ? ' IS NULL' : ' IS NOT NULL');
+ }
+
+ $$qb_key = array('condition' => $prefix.$k, 'value' => $v, 'escape' => $escape);
+ $this->{$qb_key}[] = $$qb_key;
+ if ($this->qb_caching === TRUE)
+ {
+ $this->{$qb_cache_key}[] = $$qb_key;
+ $this->qb_cache_exists[] = substr($qb_key, 3);
+ }
+
+ }
+
+ return $this;
+ }
+
+ // --------------------------------------------------------------------
+
+ /**
+ * WHERE IN
+ *
+ * Generates a WHERE field IN('item', 'item') SQL query,
+ * joined with 'AND' if appropriate.
+ *
+ * @param string $key The field to search
+ * @param array $values The values searched on
+ * @param bool $escape
+ * @return CI_DB_query_builder
+ */
+ public function where_in($key, array $values, $escape = NULL)
+ {
+ return $this->_wh_in('qb_where', $key, $values, FALSE, 'AND ', $escape);
+ }
+
+ // --------------------------------------------------------------------
+
+ /**
+ * OR WHERE IN
+ *
+ * Generates a WHERE field IN('item', 'item') SQL query,
+ * joined with 'OR' if appropriate.
+ *
+ * @param string $key The field to search
+ * @param array $values The values searched on
+ * @param bool $escape
+ * @return CI_DB_query_builder
+ */
+ public function or_where_in($key, array $values, $escape = NULL)
+ {
+ return $this->_wh_in('qb_where', $key, $values, FALSE, 'OR ', $escape);
+ }
+
+ // --------------------------------------------------------------------
+
+ /**
+ * WHERE NOT IN
+ *
+ * Generates a WHERE field NOT IN('item', 'item') SQL query,
+ * joined with 'AND' if appropriate.
+ *
+ * @param string $key The field to search
+ * @param array $values The values searched on
+ * @param bool $escape
+ * @return CI_DB_query_builder
+ */
+ public function where_not_in($key, array $values, $escape = NULL)
+ {
+ return $this->_wh_in('qb_where', $key, $values, TRUE, 'AND ', $escape);
+ }
+
+ // --------------------------------------------------------------------
+
+ /**
+ * OR WHERE NOT IN
+ *
+ * Generates a WHERE field NOT IN('item', 'item') SQL query,
+ * joined with 'OR' if appropriate.
+ *
+ * @param string $key The field to search
+ * @param array $values The values searched on
+ * @param bool $escape
+ * @return CI_DB_query_builder
+ */
+ public function or_where_not_in($key, array $values, $escape = NULL)
+ {
+ return $this->_wh_in('qb_where', $key, $values, TRUE, 'OR ', $escape);
+ }
+
+ // --------------------------------------------------------------------
+
+ /**
+ * HAVING IN
+ *
+ * Generates a HAVING field IN('item', 'item') SQL query,
+ * joined with 'AND' if appropriate.
+ *
+ * @param string $key The field to search
+ * @param array $values The values searched on
+ * @param bool $escape
+ * @return CI_DB_query_builder
+ */
+ public function having_in($key, array $values, $escape = NULL)
+ {
+ return $this->_wh_in('qb_having', $key, $values, FALSE, 'AND ', $escape);
+ }
+
+ // --------------------------------------------------------------------
+
+ /**
+ * OR HAVING IN
+ *
+ * Generates a HAVING field IN('item', 'item') SQL query,
+ * joined with 'OR' if appropriate.
+ *
+ * @param string $key The field to search
+ * @param array $values The values searched on
+ * @param bool $escape
+ * @return CI_DB_query_builder
+ */
+ public function or_having_in($key, array $values, $escape = NULL)
+ {
+ return $this->_wh_in('qb_having', $key, $values, FALSE, 'OR ', $escape);
+ }
+
+ // --------------------------------------------------------------------
+
+ /**
+ * HAVING NOT IN
+ *
+ * Generates a HAVING field NOT IN('item', 'item') SQL query,
+ * joined with 'AND' if appropriate.
+ *
+ * @param string $key The field to search
+ * @param array $values The values searched on
+ * @param bool $escape
+ * @return CI_DB_query_builder
+ */
+ public function having_not_in($key, array $values, $escape = NULL)
+ {
+ return $this->_wh_in('qb_having', $key, $values, TRUE, 'AND ', $escape);
+ }
+
+ // --------------------------------------------------------------------
+
+ /**
+ * OR HAVING NOT IN
+ *
+ * Generates a HAVING field NOT IN('item', 'item') SQL query,
+ * joined with 'OR' if appropriate.
+ *
+ * @param string $key The field to search
+ * @param array $values The values searched on
+ * @param bool $escape
+ * @return CI_DB_query_builder
+ */
+ public function or_having_not_in($key, array $values, $escape = NULL)
+ {
+ return $this->_wh_in('qb_having', $key, $values, TRUE, 'OR ', $escape);
+ }
+
+ // --------------------------------------------------------------------
+
+ /**
+ * Internal WHERE/HAVING IN
+ *
+ * @used-by where_in()
+ * @used-by or_where_in()
+ * @used-by where_not_in()
+ * @used-by or_where_not_in()
+ * @used-by having_in()
+ * @used-by or_having_in()
+ * @used-by having_not_in()
+ * @used-by or_having_not_in()
+ *
+ * @param string $qb_key 'qb_where' or 'qb_having'
+ * @param string $key The field to search
+ * @param array $values The values searched on
+ * @param bool $not If the statement would be IN or NOT IN
+ * @param string $type
+ * @param bool $escape
+ * @return CI_DB_query_builder
+ */
+ protected function _wh_in($qb_key, $key, array $values, $not = FALSE, $type = 'AND ', $escape = NULL)
+ {
+ $qb_cache_key = ($qb_key === 'qb_having') ? 'qb_cache_having' : 'qb_cache_where';
+
+ if (empty($key) OR ! is_string($key))
+ {
+ throw new InvalidArgumentException(sprintf('%s() expects $key to be a non-empty string', debug_backtrace(0, 2)[1]['function']));
+ }
+
+ if (empty($values))
+ {
+ throw new InvalidArgumentException(sprintf('%s() expects $values to be a non-empty array', debug_backtrace(0, 2)[1]['function']));
+ }
+
+ is_bool($escape) OR $escape = $this->_protect_identifiers;
+
+ $not = ($not) ? ' NOT' : '';
+
+ if ($escape === TRUE)
+ {
+ $wh_in = array();
+ foreach ($values as $value)
+ {
+ $wh_in[] = $this->escape($value);
+ }
+ }
+ else
+ {
+ $wh_in = array_values($values);
+ }
+
+ $prefix = (count($this->$qb_key) === 0 && count($this->$qb_cache_key) === 0)
+ ? $this->_group_get_type('')
+ : $this->_group_get_type($type);
+
+ $wh_in = array(
+ 'condition' => $prefix.$key.$not.' IN('.implode(', ', $wh_in).')',
+ 'value' => NULL,
+ 'escape' => $escape
+ );
+
+ $this->{$qb_key}[] = $wh_in;
+ if ($this->qb_caching === TRUE)
+ {
+ $this->{$qb_cache_key}[] = $wh_in;
+ $this->qb_cache_exists[] = substr($qb_key, 3);
+ }
+
+ return $this;
+ }
+
+ // --------------------------------------------------------------------
+
+ /**
+ * LIKE
+ *
+ * Generates a %LIKE% portion of the query.
+ * Separates multiple calls with 'AND'.
+ *
+ * @param mixed $field
+ * @param string $match
+ * @param string $side
+ * @param bool $escape
+ * @return CI_DB_query_builder
+ */
+ public function like($field, $match = '', $side = 'both', $escape = NULL)
+ {
+ return $this->_like($field, $match, 'AND ', $side, '', $escape);
+ }
+
+ // --------------------------------------------------------------------
+
+ /**
+ * NOT LIKE
+ *
+ * Generates a NOT LIKE portion of the query.
+ * Separates multiple calls with 'AND'.
+ *
+ * @param mixed $field
+ * @param string $match
+ * @param string $side
+ * @param bool $escape
+ * @return CI_DB_query_builder
+ */
+ public function not_like($field, $match = '', $side = 'both', $escape = NULL)
+ {
+ return $this->_like($field, $match, 'AND ', $side, 'NOT', $escape);
+ }
+
+ // --------------------------------------------------------------------
+
+ /**
+ * OR LIKE
+ *
+ * Generates a %LIKE% portion of the query.
+ * Separates multiple calls with 'OR'.
+ *
+ * @param mixed $field
+ * @param string $match
+ * @param string $side
+ * @param bool $escape
+ * @return CI_DB_query_builder
+ */
+ public function or_like($field, $match = '', $side = 'both', $escape = NULL)
+ {
+ return $this->_like($field, $match, 'OR ', $side, '', $escape);
+ }
+
+ // --------------------------------------------------------------------
+
+ /**
+ * OR NOT LIKE
+ *
+ * Generates a NOT LIKE portion of the query.
+ * Separates multiple calls with 'OR'.
+ *
+ * @param mixed $field
+ * @param string $match
+ * @param string $side
+ * @param bool $escape
+ * @return CI_DB_query_builder
+ */
+ public function or_not_like($field, $match = '', $side = 'both', $escape = NULL)
+ {
+ return $this->_like($field, $match, 'OR ', $side, 'NOT', $escape);
+ }
+
+ // --------------------------------------------------------------------
+
+ /**
+ * Internal LIKE
+ *
+ * @used-by like()
+ * @used-by or_like()
+ * @used-by not_like()
+ * @used-by or_not_like()
+ *
+ * @param mixed $field
+ * @param string $match
+ * @param string $type
+ * @param string $side
+ * @param string $not
+ * @param bool $escape
+ * @return CI_DB_query_builder
+ */
+ protected function _like($field, $match = '', $type = 'AND ', $side = 'both', $not = '', $escape = NULL)
+ {
+ if ( ! is_array($field))
+ {
+ $field = array($field => $match);
+ }
+
+ is_bool($escape) OR $escape = $this->_protect_identifiers;
+ // lowercase $side in case somebody writes e.g. 'BEFORE' instead of 'before' (doh)
+ $side = strtolower($side);
+
+ foreach ($field as $k => $v)
+ {
+ $prefix = (count($this->qb_where) === 0 && count($this->qb_cache_where) === 0)
+ ? $this->_group_get_type('') : $this->_group_get_type($type);
+
+ if ($escape === TRUE)
+ {
+ $v = $this->escape_like_str($v);
+ }
+
+ switch ($side)
+ {
+ case 'none':
+ $v = "'{$v}'";
+ break;
+ case 'before':
+ $v = "'%{$v}'";
+ break;
+ case 'after':
+ $v = "'{$v}%'";
+ break;
+ case 'both':
+ default:
+ $v = "'%{$v}%'";
+ break;
+ }
+
+ // some platforms require an escape sequence definition for LIKE wildcards
+ if ($escape === TRUE && $this->_like_escape_str !== '')
+ {
+ $v .= sprintf($this->_like_escape_str, $this->_like_escape_chr);
+ }
+
+ $qb_where = array('condition' => "{$prefix} {$k} {$not} LIKE {$v}", 'value' => NULL, 'escape' => $escape);
+ $this->qb_where[] = $qb_where;
+ if ($this->qb_caching === TRUE)
+ {
+ $this->qb_cache_where[] = $qb_where;
+ $this->qb_cache_exists[] = 'where';
+ }
+ }
+
+ return $this;
+ }
+
+ // --------------------------------------------------------------------
+
+ /**
+ * Starts a query group.
+ *
+ * @param string $not (Internal use only)
+ * @param string $type (Internal use only)
+ * @return CI_DB_query_builder
+ */
+ public function group_start($not = '', $type = 'AND ')
+ {
+ $type = $this->_group_get_type($type);
+
+ $this->qb_where_group_started = TRUE;
+ $prefix = (count($this->qb_where) === 0 && count($this->qb_cache_where) === 0) ? '' : $type;
+ $where = array(
+ 'condition' => $prefix.$not.str_repeat(' ', ++$this->qb_where_group_count).' (',
+ 'value' => NULL,
+ 'escape' => FALSE
+ );
+
+ $this->qb_where[] = $where;
+ if ($this->qb_caching)
+ {
+ $this->qb_cache_where[] = $where;
+ }
+
+ return $this;
+ }
+
+ // --------------------------------------------------------------------
+
+ /**
+ * Starts a query group, but ORs the group
+ *
+ * @return CI_DB_query_builder
+ */
+ public function or_group_start()
+ {
+ return $this->group_start('', 'OR ');
+ }
+
+ // --------------------------------------------------------------------
+
+ /**
+ * Starts a query group, but NOTs the group
+ *
+ * @return CI_DB_query_builder
+ */
+ public function not_group_start()
+ {
+ return $this->group_start('NOT ', 'AND ');
+ }
+
+ // --------------------------------------------------------------------
+
+ /**
+ * Starts a query group, but OR NOTs the group
+ *
+ * @return CI_DB_query_builder
+ */
+ public function or_not_group_start()
+ {
+ return $this->group_start('NOT ', 'OR ');
+ }
+
+ // --------------------------------------------------------------------
+
+ /**
+ * Ends a query group
+ *
+ * @return CI_DB_query_builder
+ */
+ public function group_end()
+ {
+ $this->qb_where_group_started = FALSE;
+ $where = array(
+ 'condition' => str_repeat(' ', $this->qb_where_group_count--).')',
+ 'value' => NULL,
+ 'escape' => FALSE
+ );
+
+ $this->qb_where[] = $where;
+ if ($this->qb_caching)
+ {
+ $this->qb_cache_where[] = $where;
+ }
+
+ return $this;
+ }
+
+ // --------------------------------------------------------------------
+
+ /**
+ * Group_get_type
+ *
+ * @used-by group_start()
+ * @used-by _like()
+ * @used-by _wh()
+ * @used-by _where_in()
+ *
+ * @param string $type
+ * @return string
+ */
+ protected function _group_get_type($type)
+ {
+ if ($this->qb_where_group_started)
+ {
+ $type = '';
+ $this->qb_where_group_started = FALSE;
+ }
+
+ return $type;
+ }
+
+ // --------------------------------------------------------------------
+
+ /**
+ * GROUP BY
+ *
+ * @param mixed $by
+ * @param bool $escape
+ * @return CI_DB_query_builder
+ */
+ public function group_by($by, $escape = NULL)
+ {
+ is_bool($escape) OR $escape = $this->_protect_identifiers;
+
+ if (is_string($by))
+ {
+ $by = ($escape === TRUE)
+ ? explode(',', $by)
+ : array($by);
+ }
+
+ foreach ($by as $val)
+ {
+ $val = trim($val);
+
+ if ($val !== '')
+ {
+ $val = array('field' => $val, 'escape' => $escape);
+
+ $this->qb_groupby[] = $val;
+ if ($this->qb_caching === TRUE)
+ {
+ $this->qb_cache_groupby[] = $val;
+ $this->qb_cache_exists[] = 'groupby';
+ }
+ }
+ }
+
+ return $this;
+ }
+
+ // --------------------------------------------------------------------
+
+ /**
+ * HAVING
+ *
+ * Separates multiple calls with 'AND'.
+ *
+ * @param string $key
+ * @param string $value
+ * @param bool $escape
+ * @return CI_DB_query_builder
+ */
+ public function having($key, $value = NULL, $escape = NULL)
+ {
+ return $this->_wh('qb_having', $key, $value, 'AND ', $escape);
+ }
+
+ // --------------------------------------------------------------------
+
+ /**
+ * OR HAVING
+ *
+ * Separates multiple calls with 'OR'.
+ *
+ * @param string $key
+ * @param string $value
+ * @param bool $escape
+ * @return CI_DB_query_builder
+ */
+ public function or_having($key, $value = NULL, $escape = NULL)
+ {
+ return $this->_wh('qb_having', $key, $value, 'OR ', $escape);
+ }
+
+ // --------------------------------------------------------------------
+
+ /**
+ * ORDER BY
+ *
+ * @param string $orderby
+ * @param string $direction ASC, DESC or RANDOM
+ * @param bool $escape
+ * @return CI_DB_query_builder
+ */
+ public function order_by($orderby, $direction = '', $escape = NULL)
+ {
+ $direction = strtoupper(trim($direction));
+
+ if ($direction === 'RANDOM')
+ {
+ $direction = '';
+
+ // Do we have a seed value?
+ $orderby = ctype_digit((string) $orderby)
+ ? sprintf($this->_random_keyword[1], $orderby)
+ : $this->_random_keyword[0];
+ }
+ elseif (empty($orderby))
+ {
+ return $this;
+ }
+ elseif ($direction !== '')
+ {
+ $direction = in_array($direction, array('ASC', 'DESC'), TRUE) ? ' '.$direction : '';
+ }
+
+ is_bool($escape) OR $escape = $this->_protect_identifiers;
+
+ if ($escape === FALSE)
+ {
+ $qb_orderby[] = array('field' => $orderby, 'direction' => $direction, 'escape' => FALSE);
+ }
+ else
+ {
+ $qb_orderby = array();
+ foreach (explode(',', $orderby) as $field)
+ {
+ $qb_orderby[] = ($direction === '' && preg_match('/\s+(ASC|DESC)$/i', rtrim($field), $match, PREG_OFFSET_CAPTURE))
+ ? array('field' => ltrim(substr($field, 0, $match[0][1])), 'direction' => ' '.$match[1][0], 'escape' => TRUE)
+ : array('field' => trim($field), 'direction' => $direction, 'escape' => TRUE);
+ }
+ }
+
+ $this->qb_orderby = array_merge($this->qb_orderby, $qb_orderby);
+ if ($this->qb_caching === TRUE)
+ {
+ $this->qb_cache_orderby = array_merge($this->qb_cache_orderby, $qb_orderby);
+ $this->qb_cache_exists[] = 'orderby';
+ }
+
+ return $this;
+ }
+
+ // --------------------------------------------------------------------
+
+ /**
+ * LIMIT
+ *
+ * @param int $value LIMIT value
+ * @param int $offset OFFSET value
+ * @return CI_DB_query_builder
+ */
+ public function limit($value, $offset = 0)
+ {
+ is_null($value) OR $this->qb_limit = (int) $value;
+ empty($offset) OR $this->qb_offset = (int) $offset;
+
+ return $this;
+ }
+
+ // --------------------------------------------------------------------
+
+ /**
+ * Sets the OFFSET value
+ *
+ * @param int $offset OFFSET value
+ * @return CI_DB_query_builder
+ */
+ public function offset($offset)
+ {
+ empty($offset) OR $this->qb_offset = (int) $offset;
+ return $this;
+ }
+
+ // --------------------------------------------------------------------
+
+ /**
+ * LIMIT string
+ *
+ * Generates a platform-specific LIMIT clause.
+ *
+ * @param string $sql SQL Query
+ * @return string
+ */
+ protected function _limit($sql)
+ {
+ return $sql.' LIMIT '.($this->qb_offset ? $this->qb_offset.', ' : '').(int) $this->qb_limit;
+ }
+
+ // --------------------------------------------------------------------
+
+ /**
+ * The "set" function.
+ *
+ * Allows key/value pairs to be set for inserting or updating
+ *
+ * @param mixed
+ * @param string
+ * @param bool
+ * @return CI_DB_query_builder
+ */
+ public function set($key, $value = '', $escape = NULL)
+ {
+ $key = $this->_object_to_array($key);
+
+ if ( ! is_array($key))
+ {
+ $key = array($key => $value);
+ }
+
+ is_bool($escape) OR $escape = $this->_protect_identifiers;
+
+ foreach ($key as $k => $v)
+ {
+ $this->qb_set[$this->protect_identifiers($k, FALSE, $escape)] = ($escape)
+ ? $this->escape($v) : $v;
+ }
+
+ return $this;
+ }
+
+ // --------------------------------------------------------------------
+
+ /**
+ * Get SELECT query string
+ *
+ * Compiles a SELECT query string and returns the sql.
+ *
+ * @param string the table name to select from (optional)
+ * @param bool TRUE: resets QB values; FALSE: leave QB values alone
+ * @return string
+ */
+ public function get_compiled_select($table = '', $reset = TRUE)
+ {
+ if ($table !== '')
+ {
+ $this->_track_aliases($table);
+ $this->from($table);
+ }
+
+ $select = $this->_compile_select();
+
+ if ($reset === TRUE)
+ {
+ $this->_reset_select();
+ }
+
+ return $select;
+ }
+
+ // --------------------------------------------------------------------
+
+ /**
+ * Get
+ *
+ * Compiles the select statement based on the other functions called
+ * and runs the query
+ *
+ * @param string the table
+ * @param string the limit clause
+ * @param string the offset clause
+ * @return CI_DB_result
+ */
+ public function get($table = '', $limit = NULL, $offset = NULL)
+ {
+ if ($table !== '')
+ {
+ $this->_track_aliases($table);
+ $this->from($table);
+ }
+
+ if ( ! empty($limit))
+ {
+ $this->limit($limit, $offset);
+ }
+
+ $result = $this->query($this->_compile_select());
+ $this->_reset_select();
+ return $result;
+ }
+
+ // --------------------------------------------------------------------
+
+ /**
+ * "Count All Results" query
+ *
+ * Generates a platform-specific query string that counts all records
+ * returned by an Query Builder query.
+ *
+ * @param string
+ * @param bool the reset clause
+ * @return int
+ */
+ public function count_all_results($table = '', $reset = TRUE)
+ {
+ if ($table !== '')
+ {
+ $this->_track_aliases($table);
+ $this->from($table);
+ }
+
+ // ORDER BY usage is often problematic here (most notably
+ // on Microsoft SQL Server) and ultimately unnecessary
+ // for selecting COUNT(*) ...
+ $qb_orderby = $this->qb_orderby;
+ $qb_cache_orderby = $this->qb_cache_orderby;
+ $this->qb_orderby = $this->qb_cache_orderby = array();
+
+ $result = ($this->qb_distinct === TRUE OR ! empty($this->qb_groupby) OR ! empty($this->qb_cache_groupby) OR ! empty($this->qb_having) OR $this->qb_limit OR $this->qb_offset)
+ ? $this->query($this->_count_string.$this->protect_identifiers('numrows')."\nFROM (\n".$this->_compile_select()."\n) CI_count_all_results")
+ : $this->query($this->_compile_select($this->_count_string.$this->protect_identifiers('numrows')));
+
+ if ($reset === TRUE)
+ {
+ $this->_reset_select();
+ }
+ else
+ {
+ $this->qb_orderby = $qb_orderby;
+ $this->qb_cache_orderby = $qb_cache_orderby;
+ }
+
+ if ($result->num_rows() === 0)
+ {
+ return 0;
+ }
+
+ $row = $result->row();
+ return (int) $row->numrows;
+ }
+
+ // --------------------------------------------------------------------
+
+ /**
+ * get_where()
+ *
+ * Allows the where clause, limit and offset to be added directly
+ *
+ * @param string $table
+ * @param string $where
+ * @param int $limit
+ * @param int $offset
+ * @return CI_DB_result
+ */
+ public function get_where($table = '', $where = NULL, $limit = NULL, $offset = NULL)
+ {
+ if ($table !== '')
+ {
+ $this->from($table);
+ }
+
+ if ($where !== NULL)
+ {
+ $this->where($where);
+ }
+
+ if ( ! empty($limit))
+ {
+ $this->limit($limit, $offset);
+ }
+
+ $result = $this->query($this->_compile_select());
+ $this->_reset_select();
+ return $result;
+ }
+
+ // --------------------------------------------------------------------
+
+ /**
+ * Insert_Batch
+ *
+ * Compiles batch insert strings and runs the queries
+ *
+ * @param string $table Table to insert into
+ * @param array $set An associative array of insert values
+ * @param bool $escape Whether to escape values and identifiers
+ * @return int Number of rows inserted or FALSE on failure
+ */
+ public function insert_batch($table, $set = NULL, $escape = NULL, $batch_size = 100)
+ {
+ if ($set === NULL)
+ {
+ if (empty($this->qb_set))
+ {
+ return ($this->db_debug) ? $this->display_error('db_must_use_set') : FALSE;
+ }
+ }
+ else
+ {
+ if (empty($set))
+ {
+ return ($this->db_debug) ? $this->display_error('insert_batch() called with no data') : FALSE;
+ }
+
+ $this->set_insert_batch($set, '', $escape);
+ }
+
+ if (strlen($table) === 0)
+ {
+ if ( ! isset($this->qb_from[0]))
+ {
+ return ($this->db_debug) ? $this->display_error('db_must_set_table') : FALSE;
+ }
+
+ $table = $this->qb_from[0];
+ }
+
+ // Batch this baby
+ $affected_rows = 0;
+ for ($i = 0, $total = count($this->qb_set); $i < $total; $i += $batch_size)
+ {
+ if ($this->query($this->_insert_batch($this->protect_identifiers($table, TRUE, $escape, FALSE), $this->qb_keys, array_slice($this->qb_set, $i, $batch_size))))
+ {
+ $affected_rows += $this->affected_rows();
+ }
+ }
+
+ $this->_reset_write();
+ return $affected_rows;
+ }
+
+ // --------------------------------------------------------------------
+
+ /**
+ * Insert batch statement
+ *
+ * Generates a platform-specific insert string from the supplied data.
+ *
+ * @param string $table Table name
+ * @param array $keys INSERT keys
+ * @param array $values INSERT values
+ * @return string
+ */
+ protected function _insert_batch($table, $keys, $values)
+ {
+ return 'INSERT INTO '.$table.' ('.implode(', ', $keys).') VALUES '.implode(', ', $values);
+ }
+
+ // --------------------------------------------------------------------
+
+ /**
+ * The "set_insert_batch" function. Allows key/value pairs to be set for batch inserts
+ *
+ * @param mixed
+ * @param string
+ * @param bool
+ * @return CI_DB_query_builder
+ */
+ public function set_insert_batch($key, $value = '', $escape = NULL)
+ {
+ $key = $this->_object_to_array_batch($key);
+
+ if ( ! is_array($key))
+ {
+ $key = array($key => $value);
+ }
+
+ is_bool($escape) OR $escape = $this->_protect_identifiers;
+
+ $keys = array_keys($this->_object_to_array(reset($key)));
+ sort($keys);
+
+ foreach ($key as $row)
+ {
+ $row = $this->_object_to_array($row);
+ if (count(array_diff($keys, array_keys($row))) > 0 OR count(array_diff(array_keys($row), $keys)) > 0)
+ {
+ // batch function above returns an error on an empty array
+ $this->qb_set[] = array();
+ return;
+ }
+
+ ksort($row); // puts $row in the same order as our keys
+
+ if ($escape !== FALSE)
+ {
+ $clean = array();
+ foreach ($row as $value)
+ {
+ $clean[] = $this->escape($value);
+ }
+
+ $row = $clean;
+ }
+
+ $this->qb_set[] = '('.implode(',', $row).')';
+ }
+
+ foreach ($keys as $k)
+ {
+ $this->qb_keys[] = $this->protect_identifiers($k, FALSE, $escape);
+ }
+
+ return $this;
+ }
+
+ // --------------------------------------------------------------------
+
+ /**
+ * Get INSERT query string
+ *
+ * Compiles an insert query and returns the sql
+ *
+ * @param string the table to insert into
+ * @param bool TRUE: reset QB values; FALSE: leave QB values alone
+ * @return string
+ */
+ public function get_compiled_insert($table = '', $reset = TRUE)
+ {
+ if ($this->_validate_insert($table) === FALSE)
+ {
+ return FALSE;
+ }
+
+ $sql = $this->_insert(
+ $this->protect_identifiers(
+ $this->qb_from[0], TRUE, NULL, FALSE
+ ),
+ array_keys($this->qb_set),
+ array_values($this->qb_set)
+ );
+
+ if ($reset === TRUE)
+ {
+ $this->_reset_write();
+ }
+
+ return $sql;
+ }
+
+ // --------------------------------------------------------------------
+
+ /**
+ * Insert
+ *
+ * Compiles an insert string and runs the query
+ *
+ * @param string the table to insert data into
+ * @param array an associative array of insert values
+ * @param bool $escape Whether to escape values and identifiers
+ * @return bool TRUE on success, FALSE on failure
+ */
+ public function insert($table = '', $set = NULL, $escape = NULL)
+ {
+ if ($set !== NULL)
+ {
+ $this->set($set, '', $escape);
+ }
+
+ if ($this->_validate_insert($table) === FALSE)
+ {
+ return FALSE;
+ }
+
+ $sql = $this->_insert(
+ $this->protect_identifiers(
+ $this->qb_from[0], TRUE, $escape, FALSE
+ ),
+ array_keys($this->qb_set),
+ array_values($this->qb_set)
+ );
+
+ $this->_reset_write();
+ return $this->query($sql);
+ }
+
+ // --------------------------------------------------------------------
+
+ /**
+ * Validate Insert
+ *
+ * This method is used by both insert() and get_compiled_insert() to
+ * validate that the there data is actually being set and that table
+ * has been chosen to be inserted into.
+ *
+ * @param string the table to insert data into
+ * @return string
+ */
+ protected function _validate_insert($table = '')
+ {
+ if (count($this->qb_set) === 0)
+ {
+ return ($this->db_debug) ? $this->display_error('db_must_use_set') : FALSE;
+ }
+
+ if ($table !== '')
+ {
+ $this->qb_from[0] = $table;
+ }
+ elseif ( ! isset($this->qb_from[0]))
+ {
+ return ($this->db_debug) ? $this->display_error('db_must_set_table') : FALSE;
+ }
+
+ return TRUE;
+ }
+
+ // --------------------------------------------------------------------
+
+ /**
+ * Replace
+ *
+ * Compiles an replace into string and runs the query
+ *
+ * @param string the table to replace data into
+ * @param array an associative array of insert values
+ * @return bool TRUE on success, FALSE on failure
+ */
+ public function replace($table = '', $set = NULL)
+ {
+ if ($set !== NULL)
+ {
+ $this->set($set);
+ }
+
+ if (count($this->qb_set) === 0)
+ {
+ return ($this->db_debug) ? $this->display_error('db_must_use_set') : FALSE;
+ }
+
+ if ($table === '')
+ {
+ if ( ! isset($this->qb_from[0]))
+ {
+ return ($this->db_debug) ? $this->display_error('db_must_set_table') : FALSE;
+ }
+
+ $table = $this->qb_from[0];
+ }
+
+ $sql = $this->_replace($this->protect_identifiers($table, TRUE, NULL, FALSE), array_keys($this->qb_set), array_values($this->qb_set));
+
+ $this->_reset_write();
+ return $this->query($sql);
+ }
+
+ // --------------------------------------------------------------------
+
+ /**
+ * Replace statement
+ *
+ * Generates a platform-specific replace string from the supplied data
+ *
+ * @param string the table name
+ * @param array the insert keys
+ * @param array the insert values
+ * @return string
+ */
+ protected function _replace($table, $keys, $values)
+ {
+ return 'REPLACE INTO '.$table.' ('.implode(', ', $keys).') VALUES ('.implode(', ', $values).')';
+ }
+
+ // --------------------------------------------------------------------
+
+ /**
+ * FROM tables
+ *
+ * Groups tables in FROM clauses if needed, so there is no confusion
+ * about operator precedence.
+ *
+ * Note: This is only used (and overridden) by MySQL and CUBRID.
+ *
+ * @return string
+ */
+ protected function _from_tables()
+ {
+ return implode(', ', $this->qb_from);
+ }
+
+ // --------------------------------------------------------------------
+
+ /**
+ * Get UPDATE query string
+ *
+ * Compiles an update query and returns the sql
+ *
+ * @param string the table to update
+ * @param bool TRUE: reset QB values; FALSE: leave QB values alone
+ * @return string
+ */
+ public function get_compiled_update($table = '', $reset = TRUE)
+ {
+ // Combine any cached components with the current statements
+ $this->_merge_cache();
+
+ if ($this->_validate_update($table) === FALSE)
+ {
+ return FALSE;
+ }
+
+ $sql = $this->_update($this->qb_from[0], $this->qb_set);
+
+ if ($reset === TRUE)
+ {
+ $this->_reset_write();
+ }
+
+ return $sql;
+ }
+
+ // --------------------------------------------------------------------
+
+ /**
+ * UPDATE
+ *
+ * Compiles an update string and runs the query.
+ *
+ * @param string $table
+ * @param array $set An associative array of update values
+ * @param mixed $where
+ * @param int $limit
+ * @return bool TRUE on success, FALSE on failure
+ */
+ public function update($table = '', $set = NULL, $where = NULL, $limit = NULL)
+ {
+ // Combine any cached components with the current statements
+ $this->_merge_cache();
+
+ if ($set !== NULL)
+ {
+ $this->set($set);
+ }
+
+ if ($this->_validate_update($table) === FALSE)
+ {
+ return FALSE;
+ }
+
+ if ($where !== NULL)
+ {
+ $this->where($where);
+ }
+
+ if ( ! empty($limit))
+ {
+ $this->limit($limit);
+ }
+
+ $sql = $this->_update($this->qb_from[0], $this->qb_set);
+ $this->_reset_write();
+ return $this->query($sql);
+ }
+
+ // --------------------------------------------------------------------
+
+ /**
+ * Validate Update
+ *
+ * This method is used by both update() and get_compiled_update() to
+ * validate that data is actually being set and that a table has been
+ * chosen to be update.
+ *
+ * @param string the table to update data on
+ * @return bool
+ */
+ protected function _validate_update($table)
+ {
+ if (count($this->qb_set) === 0)
+ {
+ return ($this->db_debug) ? $this->display_error('db_must_use_set') : FALSE;
+ }
+
+ if ($table !== '')
+ {
+ $this->qb_from = array($this->protect_identifiers($table, TRUE, NULL, FALSE));
+ }
+ elseif ( ! isset($this->qb_from[0]))
+ {
+ return ($this->db_debug) ? $this->display_error('db_must_set_table') : FALSE;
+ }
+
+ return TRUE;
+ }
+
+ // --------------------------------------------------------------------
+
+ /**
+ * Update_Batch
+ *
+ * Compiles an update string and runs the query
+ *
+ * @param string the table to retrieve the results from
+ * @param array an associative array of update values
+ * @param string the where key
+ * @return int number of rows affected or FALSE on failure
+ */
+ public function update_batch($table, $set = NULL, $index = NULL, $batch_size = 100)
+ {
+ // Combine any cached components with the current statements
+ $this->_merge_cache();
+
+ if ($index === NULL)
+ {
+ return ($this->db_debug) ? $this->display_error('db_must_use_index') : FALSE;
+ }
+
+ if ($set === NULL)
+ {
+ if (empty($this->qb_set_ub))
+ {
+ return ($this->db_debug) ? $this->display_error('db_must_use_set') : FALSE;
+ }
+ }
+ else
+ {
+ if (empty($set))
+ {
+ return ($this->db_debug) ? $this->display_error('update_batch() called with no data') : FALSE;
+ }
+
+ $this->set_update_batch($set, $index);
+ }
+
+ if (strlen($table) === 0)
+ {
+ if ( ! isset($this->qb_from[0]))
+ {
+ return ($this->db_debug) ? $this->display_error('db_must_set_table') : FALSE;
+ }
+
+ $table = $this->qb_from[0];
+ }
+
+ // Batch this baby
+ $affected_rows = 0;
+ for ($i = 0, $total = count($this->qb_set_ub); $i < $total; $i += $batch_size)
+ {
+ if ($this->query($this->_update_batch($this->protect_identifiers($table, TRUE, NULL, FALSE), array_slice($this->qb_set_ub, $i, $batch_size), $index)))
+ {
+ $affected_rows += $this->affected_rows();
+ }
+
+ $this->qb_where = array();
+ }
+
+ $this->_reset_write();
+ return $affected_rows;
+ }
+
+ // --------------------------------------------------------------------
+
+ /**
+ * Update_Batch statement
+ *
+ * Generates a platform-specific batch update string from the supplied data
+ *
+ * @param string $table Table name
+ * @param array $values Update data
+ * @param string $index WHERE key
+ * @return string
+ */
+ protected function _update_batch($table, $values, $index)
+ {
+ $ids = array();
+ foreach ($values as $key => $val)
+ {
+ $ids[] = $val[$index]['value'];
+
+ foreach (array_keys($val) as $field)
+ {
+ if ($field !== $index)
+ {
+ $final[$val[$field]['field']][] = 'WHEN '.$val[$index]['field'].' = '.$val[$index]['value'].' THEN '.$val[$field]['value'];
+ }
+ }
+ }
+
+ $cases = '';
+ foreach ($final as $k => $v)
+ {
+ $cases .= $k." = CASE \n"
+ .implode("\n", $v)."\n"
+ .'ELSE '.$k.' END, ';
+ }
+
+ $this->where($val[$index]['field'].' IN('.implode(',', $ids).')', NULL, FALSE);
+
+ return 'UPDATE '.$table.' SET '.substr($cases, 0, -2).$this->_compile_wh('qb_where');
+ }
+
+ // --------------------------------------------------------------------
+
+ /**
+ * The "set_update_batch" function. Allows key/value pairs to be set for batch updating
+ *
+ * @param array
+ * @param string
+ * @param bool
+ * @return CI_DB_query_builder
+ */
+ public function set_update_batch($key, $index = '', $escape = NULL)
+ {
+ $key = $this->_object_to_array_batch($key);
+
+ if ( ! is_array($key))
+ {
+ // @todo error
+ }
+
+ is_bool($escape) OR $escape = $this->_protect_identifiers;
+
+ foreach ($key as $k => $v)
+ {
+ $index_set = FALSE;
+ $clean = array();
+ foreach ($v as $k2 => $v2)
+ {
+ if ($k2 === $index)
+ {
+ $index_set = TRUE;
+ }
+
+ $clean[$k2] = array(
+ 'field' => $this->protect_identifiers($k2, FALSE, $escape),
+ 'value' => ($escape === FALSE ? $v2 : $this->escape($v2))
+ );
+ }
+
+ if ($index_set === FALSE)
+ {
+ return $this->display_error('db_batch_missing_index');
+ }
+
+ $this->qb_set_ub[] = $clean;
+ }
+
+ return $this;
+ }
+
+ // --------------------------------------------------------------------
+
+ /**
+ * Empty Table
+ *
+ * Compiles a delete string and runs "DELETE FROM table"
+ *
+ * @param string the table to empty
+ * @return bool TRUE on success, FALSE on failure
+ */
+ public function empty_table($table = '')
+ {
+ if ($table === '')
+ {
+ if ( ! isset($this->qb_from[0]))
+ {
+ return ($this->db_debug) ? $this->display_error('db_must_set_table') : FALSE;
+ }
+
+ $table = $this->qb_from[0];
+ }
+ else
+ {
+ $table = $this->protect_identifiers($table, TRUE, NULL, FALSE);
+ }
+
+ $sql = $this->_delete($table);
+ $this->_reset_write();
+ return $this->query($sql);
+ }
+
+ // --------------------------------------------------------------------
+
+ /**
+ * Truncate
+ *
+ * Compiles a truncate string and runs the query
+ * If the database does not support the truncate() command
+ * This function maps to "DELETE FROM table"
+ *
+ * @param string the table to truncate
+ * @return bool TRUE on success, FALSE on failure
+ */
+ public function truncate($table = '')
+ {
+ if ($table === '')
+ {
+ if ( ! isset($this->qb_from[0]))
+ {
+ return ($this->db_debug) ? $this->display_error('db_must_set_table') : FALSE;
+ }
+
+ $table = $this->qb_from[0];
+ }
+ else
+ {
+ $table = $this->protect_identifiers($table, TRUE, NULL, FALSE);
+ }
+
+ $sql = $this->_truncate($table);
+ $this->_reset_write();
+ return $this->query($sql);
+ }
+
+ // --------------------------------------------------------------------
+
+ /**
+ * Truncate statement
+ *
+ * Generates a platform-specific truncate string from the supplied data
+ *
+ * If the database does not support the truncate() command,
+ * then this method maps to 'DELETE FROM table'
+ *
+ * @param string the table name
+ * @return string
+ */
+ protected function _truncate($table)
+ {
+ return 'TRUNCATE '.$table;
+ }
+
+ // --------------------------------------------------------------------
+
+ /**
+ * Get DELETE query string
+ *
+ * Compiles a delete query string and returns the sql
+ *
+ * @param string the table to delete from
+ * @param bool TRUE: reset QB values; FALSE: leave QB values alone
+ * @return string
+ */
+ public function get_compiled_delete($table = '', $reset = TRUE)
+ {
+ $this->return_delete_sql = TRUE;
+ $sql = $this->delete($table, '', NULL, $reset);
+ $this->return_delete_sql = FALSE;
+ return $sql;
+ }
+
+ // --------------------------------------------------------------------
+
+ /**
+ * Delete
+ *
+ * Compiles a delete string and runs the query
+ *
+ * @param mixed the table(s) to delete from. String or array
+ * @param mixed the where clause
+ * @param mixed the limit clause
+ * @param bool
+ * @return mixed
+ */
+ public function delete($table = '', $where = '', $limit = NULL, $reset_data = TRUE)
+ {
+ // Combine any cached components with the current statements
+ $this->_merge_cache();
+
+ if ($table === '')
+ {
+ if ( ! isset($this->qb_from[0]))
+ {
+ return ($this->db_debug) ? $this->display_error('db_must_set_table') : FALSE;
+ }
+
+ $table = $this->qb_from[0];
+ }
+ elseif (is_array($table))
+ {
+ empty($where) && $reset_data = FALSE;
+
+ foreach ($table as $single_table)
+ {
+ $this->delete($single_table, $where, $limit, $reset_data);
+ }
+
+ return;
+ }
+ else
+ {
+ $table = $this->protect_identifiers($table, TRUE, NULL, FALSE);
+ }
+
+ if ($where !== '')
+ {
+ $this->where($where);
+ }
+
+ if ( ! empty($limit))
+ {
+ $this->limit($limit);
+ }
+
+ if (count($this->qb_where) === 0)
+ {
+ return ($this->db_debug) ? $this->display_error('db_del_must_use_where') : FALSE;
+ }
+
+ $sql = $this->_delete($table);
+ if ($reset_data)
+ {
+ $this->_reset_write();
+ }
+
+ return ($this->return_delete_sql === TRUE) ? $sql : $this->query($sql);
+ }
+
+ // --------------------------------------------------------------------
+
+ /**
+ * Delete statement
+ *
+ * Generates a platform-specific delete string from the supplied data
+ *
+ * @param string the table name
+ * @return string
+ */
+ protected function _delete($table)
+ {
+ return 'DELETE FROM '.$table.$this->_compile_wh('qb_where')
+ .($this->qb_limit !== FALSE ? ' LIMIT '.$this->qb_limit : '');
+ }
+
+ // --------------------------------------------------------------------
+
+ /**
+ * DB Prefix
+ *
+ * Prepends a database prefix if one exists in configuration
+ *
+ * @param string the table
+ * @return string
+ */
+ public function dbprefix($table = '')
+ {
+ if ($table === '')
+ {
+ $this->display_error('db_table_name_required');
+ }
+
+ return $this->dbprefix.$table;
+ }
+
+ // --------------------------------------------------------------------
+
+ /**
+ * Set DB Prefix
+ *
+ * Set's the DB Prefix to something new without needing to reconnect
+ *
+ * @param string the prefix
+ * @return string
+ */
+ public function set_dbprefix($prefix = '')
+ {
+ return $this->dbprefix = $prefix;
+ }
+
+ // --------------------------------------------------------------------
+
+ /**
+ * Track Aliases
+ *
+ * Used to track SQL statements written with aliased tables.
+ *
+ * @param string The table to inspect
+ * @return string
+ */
+ protected function _track_aliases($table)
+ {
+ if (is_array($table))
+ {
+ foreach ($table as $t)
+ {
+ $this->_track_aliases($t);
+ }
+ return;
+ }
+
+ // Does the string contain a comma? If so, we need to separate
+ // the string into discreet statements
+ if (strpos($table, ',') !== FALSE)
+ {
+ return $this->_track_aliases(explode(',', $table));
+ }
+
+ // if a table alias is used we can recognize it by a space
+ if (strpos($table, ' ') !== FALSE)
+ {
+ // if the alias is written with the AS keyword, remove it
+ $table = preg_replace('/\s+AS\s+/i', ' ', $table);
+
+ // Grab the alias
+ $table = trim(strrchr($table, ' '));
+
+ // Store the alias, if it doesn't already exist
+ if ( ! in_array($table, $this->qb_aliased_tables, TRUE))
+ {
+ $this->qb_aliased_tables[] = $table;
+ if ($this->qb_caching === TRUE && ! in_array($table, $this->qb_cache_aliased_tables, TRUE))
+ {
+ $this->qb_cache_aliased_tables[] = $table;
+ $this->qb_cache_exists[] = 'aliased_tables';
+ }
+ }
+ }
+ }
+
+ // --------------------------------------------------------------------
+
+ /**
+ * Compile the SELECT statement
+ *
+ * Generates a query string based on which functions were used.
+ * Should not be called directly.
+ *
+ * @param bool $select_override
+ * @return string
+ */
+ protected function _compile_select($select_override = FALSE)
+ {
+ // Combine any cached components with the current statements
+ $this->_merge_cache();
+
+ // Write the "select" portion of the query
+ if ($select_override !== FALSE)
+ {
+ $sql = $select_override;
+ }
+ else
+ {
+ $sql = ( ! $this->qb_distinct) ? 'SELECT ' : 'SELECT DISTINCT ';
+
+ if (count($this->qb_select) === 0)
+ {
+ $sql .= '*';
+ }
+ else
+ {
+ // Cycle through the "select" portion of the query and prep each column name.
+ // The reason we protect identifiers here rather than in the select() function
+ // is because until the user calls the from() function we don't know if there are aliases
+ foreach ($this->qb_select as $key => $val)
+ {
+ $no_escape = isset($this->qb_no_escape[$key]) ? $this->qb_no_escape[$key] : NULL;
+ $this->qb_select[$key] = $this->protect_identifiers($val, FALSE, $no_escape);
+ }
+
+ $sql .= implode(', ', $this->qb_select);
+ }
+ }
+
+ // Write the "FROM" portion of the query
+ if (count($this->qb_from) > 0)
+ {
+ $sql .= "\nFROM ".$this->_from_tables();
+ }
+
+ // Write the "JOIN" portion of the query
+ if (count($this->qb_join) > 0)
+ {
+ $sql .= "\n".implode("\n", $this->qb_join);
+ }
+
+ $sql .= $this->_compile_wh('qb_where')
+ .$this->_compile_group_by()
+ .$this->_compile_wh('qb_having')
+ .$this->_compile_order_by(); // ORDER BY
+
+ // LIMIT
+ if ($this->qb_limit !== FALSE OR $this->qb_offset)
+ {
+ return $this->_limit($sql."\n");
+ }
+
+ return $sql;
+ }
+
+ // --------------------------------------------------------------------
+
+ /**
+ * Compile WHERE, HAVING statements
+ *
+ * Escapes identifiers in WHERE and HAVING statements at execution time.
+ *
+ * Required so that aliases are tracked properly, regardless of whether
+ * where(), or_where(), having(), or_having are called prior to from(),
+ * join() and dbprefix is added only if needed.
+ *
+ * @param string $qb_key 'qb_where' or 'qb_having'
+ * @return string SQL statement
+ */
+ protected function _compile_wh($qb_key)
+ {
+ if (count($this->$qb_key) > 0)
+ {
+ for ($i = 0, $c = count($this->$qb_key); $i < $c; $i++)
+ {
+ // Is this condition already compiled?
+ if (is_string($this->{$qb_key}[$i]))
+ {
+ continue;
+ }
+ elseif ($this->{$qb_key}[$i]['escape'] === FALSE)
+ {
+ $this->{$qb_key}[$i] = $this->{$qb_key}[$i]['condition'].(isset($this->{$qb_key}[$i]['value']) ? ' '.$this->{$qb_key}[$i]['value'] : '');
+ continue;
+ }
+
+ // Split multiple conditions
+ $conditions = preg_split(
+ '/((?:^|\s+)AND\s+|(?:^|\s+)OR\s+)/i',
+ $this->{$qb_key}[$i]['condition'],
+ -1,
+ PREG_SPLIT_DELIM_CAPTURE | PREG_SPLIT_NO_EMPTY
+ );
+
+ for ($ci = 0, $cc = count($conditions); $ci < $cc; $ci++)
+ {
+ if (($op = $this->_get_operator($conditions[$ci])) === FALSE
+ OR ! preg_match('/^(\(?)(.*)('.preg_quote($op, '/').')\s*(.*(?<!\)))?(\)?)$/i', $conditions[$ci], $matches))
+ {
+ continue;
+ }
+
+ // $matches = array(
+ // 0 => '(test <= foo)', /* the whole thing */
+ // 1 => '(', /* optional */
+ // 2 => 'test', /* the field name */
+ // 3 => ' <= ', /* $op */
+ // 4 => 'foo', /* optional, if $op is e.g. 'IS NULL' */
+ // 5 => ')' /* optional */
+ // );
+
+ if ( ! empty($matches[4]))
+ {
+ $this->_is_literal($matches[4]) OR $matches[4] = $this->protect_identifiers(trim($matches[4]));
+ $matches[4] = ' '.$matches[4];
+ }
+
+ $conditions[$ci] = $matches[1].$this->protect_identifiers(trim($matches[2]))
+ .' '.trim($matches[3]).$matches[4].$matches[5];
+ }
+
+ $this->{$qb_key}[$i] = implode('', $conditions).(isset($this->{$qb_key}[$i]['value']) ? ' '.$this->{$qb_key}[$i]['value'] : '');
+ }
+
+ return ($qb_key === 'qb_having' ? "\nHAVING " : "\nWHERE ")
+ .implode("\n", $this->$qb_key);
+ }
+
+ return '';
+ }
+
+ // --------------------------------------------------------------------
+
+ /**
+ * Compile GROUP BY
+ *
+ * Escapes identifiers in GROUP BY statements at execution time.
+ *
+ * Required so that aliases are tracked properly, regardless of whether
+ * group_by() is called prior to from(), join() and dbprefix is added
+ * only if needed.
+ *
+ * @return string SQL statement
+ */
+ protected function _compile_group_by()
+ {
+ if (count($this->qb_groupby) > 0)
+ {
+ for ($i = 0, $c = count($this->qb_groupby); $i < $c; $i++)
+ {
+ // Is it already compiled?
+ if (is_string($this->qb_groupby[$i]))
+ {
+ continue;
+ }
+
+ $this->qb_groupby[$i] = ($this->qb_groupby[$i]['escape'] === FALSE OR $this->_is_literal($this->qb_groupby[$i]['field']))
+ ? $this->qb_groupby[$i]['field']
+ : $this->protect_identifiers($this->qb_groupby[$i]['field']);
+ }
+
+ return "\nGROUP BY ".implode(', ', $this->qb_groupby);
+ }
+
+ return '';
+ }
+
+ // --------------------------------------------------------------------
+
+ /**
+ * Compile ORDER BY
+ *
+ * Escapes identifiers in ORDER BY statements at execution time.
+ *
+ * Required so that aliases are tracked properly, regardless of whether
+ * order_by() is called prior to from(), join() and dbprefix is added
+ * only if needed.
+ *
+ * @return string SQL statement
+ */
+ protected function _compile_order_by()
+ {
+ if (empty($this->qb_orderby))
+ {
+ return '';
+ }
+
+ for ($i = 0, $c = count($this->qb_orderby); $i < $c; $i++)
+ {
+ if (is_string($this->qb_orderby[$i]))
+ {
+ continue;
+ }
+
+ if ($this->qb_orderby[$i]['escape'] !== FALSE && ! $this->_is_literal($this->qb_orderby[$i]['field']))
+ {
+ $this->qb_orderby[$i]['field'] = $this->protect_identifiers($this->qb_orderby[$i]['field']);
+ }
+
+ $this->qb_orderby[$i] = $this->qb_orderby[$i]['field'].$this->qb_orderby[$i]['direction'];
+ }
+
+ return "\nORDER BY ".implode(', ', $this->qb_orderby);
+ }
+
+ // --------------------------------------------------------------------
+
+ /**
+ * Object to Array
+ *
+ * Takes an object as input and converts the class variables to array key/vals
+ *
+ * @param object
+ * @return array
+ */
+ protected function _object_to_array($object)
+ {
+ if ( ! is_object($object))
+ {
+ return $object;
+ }
+
+ $array = array();
+ foreach (get_object_vars($object) as $key => $val)
+ {
+ // There are some built in keys we need to ignore for this conversion
+ if ( ! is_object($val) && ! is_array($val) && $key !== '_parent_name')
+ {
+ $array[$key] = $val;
+ }
+ }
+
+ return $array;
+ }
+
+ // --------------------------------------------------------------------
+
+ /**
+ * Object to Array
+ *
+ * Takes an object as input and converts the class variables to array key/vals
+ *
+ * @param object
+ * @return array
+ */
+ protected function _object_to_array_batch($object)
+ {
+ if ( ! is_object($object))
+ {
+ return $object;
+ }
+
+ $array = array();
+ $out = get_object_vars($object);
+ $fields = array_keys($out);
+
+ foreach ($fields as $val)
+ {
+ // There are some built in keys we need to ignore for this conversion
+ if ($val !== '_parent_name')
+ {
+ $i = 0;
+ foreach ($out[$val] as $data)
+ {
+ $array[$i++][$val] = $data;
+ }
+ }
+ }
+
+ return $array;
+ }
+
+ // --------------------------------------------------------------------
+
+ /**
+ * Start Cache
+ *
+ * Starts QB caching
+ *
+ * @return CI_DB_query_builder
+ */
+ public function start_cache()
+ {
+ $this->qb_caching = TRUE;
+ return $this;
+ }
+
+ // --------------------------------------------------------------------
+
+ /**
+ * Stop Cache
+ *
+ * Stops QB caching
+ *
+ * @return CI_DB_query_builder
+ */
+ public function stop_cache()
+ {
+ $this->qb_caching = FALSE;
+ return $this;
+ }
+
+ // --------------------------------------------------------------------
+
+ /**
+ * Flush Cache
+ *
+ * Empties the QB cache
+ *
+ * @return CI_DB_query_builder
+ */
+ public function flush_cache()
+ {
+ $this->_reset_run(array(
+ 'qb_cache_select' => array(),
+ 'qb_cache_from' => array(),
+ 'qb_cache_join' => array(),
+ 'qb_cache_where' => array(),
+ 'qb_cache_groupby' => array(),
+ 'qb_cache_having' => array(),
+ 'qb_cache_orderby' => array(),
+ 'qb_cache_set' => array(),
+ 'qb_cache_exists' => array(),
+ 'qb_cache_no_escape' => array(),
+ 'qb_cache_aliased_tables' => array()
+ ));
+
+ return $this;
+ }
+
+ // --------------------------------------------------------------------
+
+ /**
+ * Merge Cache
+ *
+ * When called, this function merges any cached QB arrays with
+ * locally called ones.
+ *
+ * @return void
+ */
+ protected function _merge_cache()
+ {
+ if (count($this->qb_cache_exists) === 0)
+ {
+ return;
+ }
+ elseif (in_array('select', $this->qb_cache_exists, TRUE))
+ {
+ $qb_no_escape = $this->qb_cache_no_escape;
+ }
+
+ foreach (array_unique($this->qb_cache_exists) as $val) // select, from, etc.
+ {
+ $qb_variable = 'qb_'.$val;
+ $qb_cache_var = 'qb_cache_'.$val;
+ $qb_new = $this->$qb_cache_var;
+
+ for ($i = 0, $c = count($this->$qb_variable); $i < $c; $i++)
+ {
+ if ( ! in_array($this->{$qb_variable}[$i], $qb_new, TRUE))
+ {
+ $qb_new[] = $this->{$qb_variable}[$i];
+ if ($val === 'select')
+ {
+ $qb_no_escape[] = $this->qb_no_escape[$i];
+ }
+ }
+ }
+
+ $this->$qb_variable = $qb_new;
+ if ($val === 'select')
+ {
+ $this->qb_no_escape = $qb_no_escape;
+ }
+ }
+ }
+
+ // --------------------------------------------------------------------
+
+ /**
+ * Is literal
+ *
+ * Determines if a string represents a literal value or a field name
+ *
+ * @param string $str
+ * @return bool
+ */
+ protected function _is_literal($str)
+ {
+ $str = trim($str);
+
+ if (empty($str) OR ctype_digit($str) OR (string) (float) $str === $str OR in_array(strtoupper($str), array('TRUE', 'FALSE'), TRUE))
+ {
+ return TRUE;
+ }
+
+ static $_str;
+
+ if (empty($_str))
+ {
+ $_str = ($this->_escape_char !== '"')
+ ? array('"', "'") : array("'");
+ }
+
+ return in_array($str[0], $_str, TRUE);
+ }
+
+ // --------------------------------------------------------------------
+
+ /**
+ * Reset Query Builder values.
+ *
+ * Publicly-visible method to reset the QB values.
+ *
+ * @return CI_DB_query_builder
+ */
+ public function reset_query()
+ {
+ $this->_reset_select();
+ $this->_reset_write();
+ return $this;
+ }
+
+ // --------------------------------------------------------------------
+
+ /**
+ * Resets the query builder values. Called by the get() function
+ *
+ * @param array An array of fields to reset
+ * @return void
+ */
+ protected function _reset_run($qb_reset_items)
+ {
+ foreach ($qb_reset_items as $item => $default_value)
+ {
+ $this->$item = $default_value;
+ }
+ }
+
+ // --------------------------------------------------------------------
+
+ /**
+ * Resets the query builder values. Called by the get() function
+ *
+ * @return void
+ */
+ protected function _reset_select()
+ {
+ $this->_reset_run(array(
+ 'qb_select' => array(),
+ 'qb_from' => array(),
+ 'qb_join' => array(),
+ 'qb_where' => array(),
+ 'qb_groupby' => array(),
+ 'qb_having' => array(),
+ 'qb_orderby' => array(),
+ 'qb_aliased_tables' => array(),
+ 'qb_no_escape' => array(),
+ 'qb_distinct' => FALSE,
+ 'qb_limit' => FALSE,
+ 'qb_offset' => FALSE
+ ));
+ }
+
+ // --------------------------------------------------------------------
+
+ /**
+ * Resets the query builder "write" values.
+ *
+ * Called by the insert() update() insert_batch() update_batch() and delete() functions
+ *
+ * @return void
+ */
+ protected function _reset_write()
+ {
+ $this->_reset_run(array(
+ 'qb_set' => array(),
+ 'qb_set_ub' => array(),
+ 'qb_from' => array(),
+ 'qb_join' => array(),
+ 'qb_where' => array(),
+ 'qb_orderby' => array(),
+ 'qb_keys' => array(),
+ 'qb_limit' => FALSE
+ ));
+ }
+
+}
diff --git a/system/database/DB_result.php b/system/database/DB_result.php
index 5b4f60e4b..94da294db 100644
--- a/system/database/DB_result.php
+++ b/system/database/DB_result.php
@@ -1,19 +1,42 @@
-<?php if ( ! defined('BASEPATH')) exit('No direct script access allowed');
+<?php
/**
* CodeIgniter
*
- * An open source application development framework for PHP 5.1.6 or newer
+ * An open source application development framework for PHP
*
- * @package CodeIgniter
- * @author ExpressionEngine Dev Team
- * @copyright Copyright (c) 2008 - 2014, EllisLab, Inc.
- * @license http://codeigniter.com/user_guide/license.html
- * @link http://codeigniter.com
- * @since Version 1.0
+ * This content is released under the MIT License (MIT)
+ *
+ * Copyright (c) 2019 - 2022, CodeIgniter Foundation
+ *
+ * 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.
+ *
+ * @package CodeIgniter
+ * @author EllisLab Dev Team
+ * @copyright Copyright (c) 2008 - 2014, EllisLab, Inc. (https://ellislab.com/)
+ * @copyright Copyright (c) 2014 - 2019, British Columbia Institute of Technology (https://bcit.ca/)
+ * @copyright Copyright (c) 2019 - 2022, CodeIgniter Foundation (https://codeigniter.com/)
+ * @license https://opensource.org/licenses/MIT MIT License
+ * @link https://codeigniter.com
+ * @since Version 1.0.0
* @filesource
*/
-
-// ------------------------------------------------------------------------
+defined('BASEPATH') OR exit('No direct script access allowed');
/**
* Database Result Class
@@ -23,33 +46,126 @@
* class for the specific database will extend and instantiate it.
*
* @category Database
- * @author ExpressionEngine Dev Team
- * @link http://codeigniter.com/user_guide/database/
+ * @author EllisLab Dev Team
+ * @link https://codeigniter.com/userguide3/database/
*/
class CI_DB_result {
- var $conn_id = NULL;
- var $result_id = NULL;
- var $result_array = array();
- var $result_object = array();
- var $custom_result_object = array();
- var $current_row = 0;
- var $num_rows = 0;
- var $row_data = NULL;
+ /**
+ * Connection ID
+ *
+ * @var resource|object
+ */
+ public $conn_id;
+
+ /**
+ * Result ID
+ *
+ * @var resource|object
+ */
+ public $result_id;
+
+ /**
+ * Result Array
+ *
+ * @var array[]
+ */
+ public $result_array = array();
+
+ /**
+ * Result Object
+ *
+ * @var object[]
+ */
+ public $result_object = array();
+
+ /**
+ * Custom Result Object
+ *
+ * @var object[]
+ */
+ public $custom_result_object = array();
+
+ /**
+ * Current Row index
+ *
+ * @var int
+ */
+ public $current_row = 0;
+
+ /**
+ * Number of rows
+ *
+ * @var int
+ */
+ public $num_rows;
+
+ /**
+ * Row data
+ *
+ * @var array
+ */
+ public $row_data;
+
+ // --------------------------------------------------------------------
+
+ /**
+ * Constructor
+ *
+ * @param object $driver_object
+ * @return void
+ */
+ public function __construct(&$driver_object)
+ {
+ $this->conn_id = $driver_object->conn_id;
+ $this->result_id = $driver_object->result_id;
+ }
+
+ // --------------------------------------------------------------------
+
+ /**
+ * Number of rows in the result set
+ *
+ * @return int
+ */
+ public function num_rows()
+ {
+ if (is_int($this->num_rows))
+ {
+ return $this->num_rows;
+ }
+ elseif (count($this->result_array) > 0)
+ {
+ return $this->num_rows = count($this->result_array);
+ }
+ elseif (count($this->result_object) > 0)
+ {
+ return $this->num_rows = count($this->result_object);
+ }
+
+ return $this->num_rows = count($this->result_array());
+ }
+ // --------------------------------------------------------------------
/**
- * Query result. Acts as a wrapper function for the following functions.
+ * Query result. Acts as a wrapper function for the following functions.
*
- * @access public
- * @param string can be "object" or "array"
- * @return mixed either a result object or array
+ * @param string $type 'object', 'array' or a custom class name
+ * @return array
*/
public function result($type = 'object')
{
- if ($type == 'array') return $this->result_array();
- else if ($type == 'object') return $this->result_object();
- else return $this->custom_result_object($type);
+ if ($type === 'array')
+ {
+ return $this->result_array();
+ }
+ elseif ($type === 'object')
+ {
+ return $this->result_object();
+ }
+
+ return $this->custom_result_object($type);
}
// --------------------------------------------------------------------
@@ -57,48 +173,63 @@ class CI_DB_result {
/**
* Custom query result.
*
- * @param class_name A string that represents the type of object you want back
- * @return array of objects
+ * @param string $class_name
+ * @return array
*/
public function custom_result_object($class_name)
{
- if (array_key_exists($class_name, $this->custom_result_object))
+ if (isset($this->custom_result_object[$class_name]))
{
return $this->custom_result_object[$class_name];
}
-
- if ($this->result_id === FALSE OR $this->num_rows() == 0)
+ elseif ( ! $this->result_id OR $this->num_rows === 0)
{
return array();
}
- // add the data to the object
- $this->_data_seek(0);
- $result_object = array();
-
- while ($row = $this->_fetch_object())
+ // Don't fetch the result set again if we already have it
+ $_data = NULL;
+ if (($c = count($this->result_array)) > 0)
{
- $object = new $class_name();
+ $_data = 'result_array';
+ }
+ elseif (($c = count($this->result_object)) > 0)
+ {
+ $_data = 'result_object';
+ }
- foreach ($row as $key => $value)
+ if ($_data !== NULL)
+ {
+ for ($i = 0; $i < $c; $i++)
{
- $object->$key = $value;
+ $this->custom_result_object[$class_name][$i] = new $class_name();
+
+ foreach ($this->{$_data}[$i] as $key => $value)
+ {
+ $this->custom_result_object[$class_name][$i]->$key = $value;
+ }
}
- $result_object[] = $object;
+ return $this->custom_result_object[$class_name];
}
- // return the array
- return $this->custom_result_object[$class_name] = $result_object;
+ is_null($this->row_data) OR $this->data_seek(0);
+ $this->custom_result_object[$class_name] = array();
+
+ while ($row = $this->_fetch_object($class_name))
+ {
+ $this->custom_result_object[$class_name][] = $row;
+ }
+
+ return $this->custom_result_object[$class_name];
}
// --------------------------------------------------------------------
/**
- * Query result. "object" version.
+ * Query result. "object" version.
*
- * @access public
- * @return object
+ * @return array
*/
public function result_object()
{
@@ -107,15 +238,25 @@ class CI_DB_result {
return $this->result_object;
}
- // In the event that query caching is on the result_id variable
- // will return FALSE since there isn't a valid SQL resource so
- // we'll simply return an empty array.
- if ($this->result_id === FALSE OR $this->num_rows() == 0)
+ // In the event that query caching is on, the result_id variable
+ // will not be a valid resource so we'll simply return an empty
+ // array.
+ if ( ! $this->result_id OR $this->num_rows === 0)
{
return array();
}
- $this->_data_seek(0);
+ if (($c = count($this->result_array)) > 0)
+ {
+ for ($i = 0; $i < $c; $i++)
+ {
+ $this->result_object[$i] = (object) $this->result_array[$i];
+ }
+
+ return $this->result_object;
+ }
+
+ is_null($this->row_data) OR $this->data_seek(0);
while ($row = $this->_fetch_object())
{
$this->result_object[] = $row;
@@ -127,9 +268,8 @@ class CI_DB_result {
// --------------------------------------------------------------------
/**
- * Query result. "array" version.
+ * Query result. "array" version.
*
- * @access public
* @return array
*/
public function result_array()
@@ -139,15 +279,25 @@ class CI_DB_result {
return $this->result_array;
}
- // In the event that query caching is on the result_id variable
- // will return FALSE since there isn't a valid SQL resource so
- // we'll simply return an empty array.
- if ($this->result_id === FALSE OR $this->num_rows() == 0)
+ // In the event that query caching is on, the result_id variable
+ // will not be a valid resource so we'll simply return an empty
+ // array.
+ if ( ! $this->result_id OR $this->num_rows === 0)
{
return array();
}
- $this->_data_seek(0);
+ if (($c = count($this->result_object)) > 0)
+ {
+ for ($i = 0; $i < $c; $i++)
+ {
+ $this->result_array[$i] = (array) $this->result_object[$i];
+ }
+
+ return $this->result_array;
+ }
+
+ is_null($this->row_data) OR $this->data_seek(0);
while ($row = $this->_fetch_assoc())
{
$this->result_array[] = $row;
@@ -159,35 +309,34 @@ class CI_DB_result {
// --------------------------------------------------------------------
/**
- * Query result. Acts as a wrapper function for the following functions.
+ * Row
*
- * @access public
- * @param string
- * @param string can be "object" or "array"
- * @return mixed either a result object or array
+ * A wrapper method.
+ *
+ * @param mixed $n
+ * @param string $type 'object' or 'array'
+ * @return mixed
*/
public function row($n = 0, $type = 'object')
{
if ( ! is_numeric($n))
{
// We cache the row data for subsequent uses
- if ( ! is_array($this->row_data))
- {
- $this->row_data = $this->row_array(0);
- }
+ is_array($this->row_data) OR $this->row_data = $this->row_array(0);
- // array_key_exists() instead of isset() to allow for MySQL NULL values
- if (array_key_exists($n, $this->row_data))
+ // array_key_exists() instead of isset() to allow for NULL values
+ if (empty($this->row_data) OR ! array_key_exists($n, $this->row_data))
{
- return $this->row_data[$n];
+ return NULL;
}
- // reset the $n variable if the result was not achieved
- $n = 0;
+
+ return $this->row_data[$n];
}
- if ($type == 'object') return $this->row_object($n);
- else if ($type == 'array') return $this->row_array($n);
- else return $this->custom_row_object($n, $type);
+ if ($type === 'object') return $this->row_object($n);
+ elseif ($type === 'array') return $this->row_array($n);
+
+ return $this->custom_row_object($n, $type);
}
// --------------------------------------------------------------------
@@ -195,8 +344,9 @@ class CI_DB_result {
/**
* Assigns an item into a particular column slot
*
- * @access public
- * @return object
+ * @param mixed $key
+ * @param mixed $value
+ * @return void
*/
public function set_row($key, $value = NULL)
{
@@ -212,11 +362,10 @@ class CI_DB_result {
{
$this->row_data[$k] = $v;
}
-
return;
}
- if ($key != '' AND ! is_null($value))
+ if ($key !== '' && $value !== NULL)
{
$this->row_data[$key] = $value;
}
@@ -227,42 +376,44 @@ class CI_DB_result {
/**
* Returns a single result row - custom object version
*
- * @access public
+ * @param int $n
+ * @param string $type
* @return object
*/
public function custom_row_object($n, $type)
{
- $result = $this->custom_result_object($type);
+ isset($this->custom_result_object[$type]) OR $this->custom_result_object[$type] = $this->custom_result_object($type);
- if (count($result) == 0)
+ if (count($this->custom_result_object[$type]) === 0)
{
- return $result;
+ return NULL;
}
- if ($n != $this->current_row AND isset($result[$n]))
+ if ($n !== $this->current_row && isset($this->custom_result_object[$type][$n]))
{
$this->current_row = $n;
}
- return $result[$this->current_row];
+ return $this->custom_result_object[$type][$this->current_row];
}
+ // --------------------------------------------------------------------
+
/**
* Returns a single result row - object version
*
- * @access public
+ * @param int $n
* @return object
*/
public function row_object($n = 0)
{
$result = $this->result_object();
-
- if (count($result) == 0)
+ if (count($result) === 0)
{
- return $result;
+ return NULL;
}
- if ($n != $this->current_row AND isset($result[$n]))
+ if ($n !== $this->current_row && isset($result[$n]))
{
$this->current_row = $n;
}
@@ -275,19 +426,18 @@ class CI_DB_result {
/**
* Returns a single result row - array version
*
- * @access public
+ * @param int $n
* @return array
*/
public function row_array($n = 0)
{
$result = $this->result_array();
-
- if (count($result) == 0)
+ if (count($result) === 0)
{
- return $result;
+ return NULL;
}
- if ($n != $this->current_row AND isset($result[$n]))
+ if ($n !== $this->current_row && isset($result[$n]))
{
$this->current_row = $n;
}
@@ -295,24 +445,18 @@ class CI_DB_result {
return $result[$this->current_row];
}
-
// --------------------------------------------------------------------
/**
* Returns the "first" row
*
- * @access public
- * @return object
+ * @param string $type
+ * @return mixed
*/
public function first_row($type = 'object')
{
$result = $this->result($type);
-
- if (count($result) == 0)
- {
- return $result;
- }
- return $result[0];
+ return (count($result) === 0) ? NULL : $result[0];
}
// --------------------------------------------------------------------
@@ -320,18 +464,13 @@ class CI_DB_result {
/**
* Returns the "last" row
*
- * @access public
- * @return object
+ * @param string $type
+ * @return mixed
*/
public function last_row($type = 'object')
{
$result = $this->result($type);
-
- if (count($result) == 0)
- {
- return $result;
- }
- return $result[count($result) -1];
+ return (count($result) === 0) ? NULL : $result[count($result) - 1];
}
// --------------------------------------------------------------------
@@ -339,24 +478,20 @@ class CI_DB_result {
/**
* Returns the "next" row
*
- * @access public
- * @return object
+ * @param string $type
+ * @return mixed
*/
public function next_row($type = 'object')
{
$result = $this->result($type);
-
- if (count($result) == 0)
+ if (count($result) === 0)
{
- return $result;
+ return NULL;
}
- if (isset($result[$this->current_row + 1]))
- {
- ++$this->current_row;
- }
-
- return $result[$this->current_row];
+ return isset($result[$this->current_row + 1])
+ ? $result[++$this->current_row]
+ : NULL;
}
// --------------------------------------------------------------------
@@ -364,16 +499,15 @@ class CI_DB_result {
/**
* Returns the "previous" row
*
- * @access public
- * @return object
+ * @param string $type
+ * @return mixed
*/
public function previous_row($type = 'object')
{
$result = $this->result($type);
-
- if (count($result) == 0)
+ if (count($result) === 0)
{
- return $result;
+ return NULL;
}
if (isset($result[$this->current_row - 1]))
@@ -386,25 +520,147 @@ class CI_DB_result {
// --------------------------------------------------------------------
/**
- * The following functions are normally overloaded by the identically named
+ * Returns an unbuffered row and move pointer to next row
+ *
+ * @param string $type 'array', 'object' or a custom class name
+ * @return mixed
+ */
+ public function unbuffered_row($type = 'object')
+ {
+ if ($type === 'array')
+ {
+ return $this->_fetch_assoc();
+ }
+ elseif ($type === 'object')
+ {
+ return $this->_fetch_object();
+ }
+
+ return $this->_fetch_object($type);
+ }
+
+ // --------------------------------------------------------------------
+
+ /**
+ * The following methods are normally overloaded by the identically named
* methods in the platform-specific driver -- except when query caching
- * is used. When caching is enabled we do not load the other driver.
+ * is used. When caching is enabled we do not load the other driver.
* These functions are primarily here to prevent undefined function errors
- * when a cached result object is in use. They are not otherwise fully
+ * when a cached result object is in use. They are not otherwise fully
* operational due to the unavailability of the database resource IDs with
* cached results.
*/
- public function num_rows() { return $this->num_rows; }
- public function num_fields() { return 0; }
- public function list_fields() { return array(); }
- public function field_data() { return array(); }
- public function free_result() { return TRUE; }
- protected function _data_seek() { return TRUE; }
- protected function _fetch_assoc() { return array(); }
- protected function _fetch_object() { return array(); }
-}
-// END DB_result class
+ // --------------------------------------------------------------------
+
+ /**
+ * Number of fields in the result set
+ *
+ * Overridden by driver result classes.
+ *
+ * @return int
+ */
+ public function num_fields()
+ {
+ return 0;
+ }
+
+ // --------------------------------------------------------------------
+
+ /**
+ * Fetch Field Names
+ *
+ * Generates an array of column names.
+ *
+ * Overridden by driver result classes.
+ *
+ * @return array
+ */
+ public function list_fields()
+ {
+ return array();
+ }
-/* End of file DB_result.php */
-/* Location: ./system/database/DB_result.php */
+ // --------------------------------------------------------------------
+
+ /**
+ * Field data
+ *
+ * Generates an array of objects containing field meta-data.
+ *
+ * Overridden by driver result classes.
+ *
+ * @return array
+ */
+ public function field_data()
+ {
+ return array();
+ }
+
+ // --------------------------------------------------------------------
+
+ /**
+ * Free the result
+ *
+ * Overridden by driver result classes.
+ *
+ * @return void
+ */
+ public function free_result()
+ {
+ $this->result_id = FALSE;
+ }
+
+ // --------------------------------------------------------------------
+
+ /**
+ * Data Seek
+ *
+ * Moves the internal pointer to the desired offset. We call
+ * this internally before fetching results to make sure the
+ * result set starts at zero.
+ *
+ * Overridden by driver result classes.
+ *
+ * @param int $n
+ * @return bool
+ */
+ public function data_seek($n = 0)
+ {
+ return FALSE;
+ }
+
+ // --------------------------------------------------------------------
+
+ /**
+ * Result - associative array
+ *
+ * Returns the result set as an array.
+ *
+ * Overridden by driver result classes.
+ *
+ * @return array
+ */
+ protected function _fetch_assoc()
+ {
+ return array();
+ }
+
+ // --------------------------------------------------------------------
+
+ /**
+ * Result - object
+ *
+ * Returns the result set as an object.
+ *
+ * Overridden by driver result classes.
+ *
+ * @param string $class_name
+ * @return object
+ */
+ protected function _fetch_object($class_name = 'stdClass')
+ {
+ return new $class_name();
+ }
+
+}
diff --git a/system/database/DB_utility.php b/system/database/DB_utility.php
index 6a9d8cc59..317e1bc8d 100644
--- a/system/database/DB_utility.php
+++ b/system/database/DB_utility.php
@@ -1,45 +1,94 @@
-<?php if ( ! defined('BASEPATH')) exit('No direct script access allowed');
+<?php
/**
* CodeIgniter
*
- * An open source application development framework for PHP 5.1.6 or newer
+ * An open source application development framework for PHP
*
- * @package CodeIgniter
- * @author ExpressionEngine Dev Team
- * @copyright Copyright (c) 2008 - 2014, EllisLab, Inc.
- * @license http://codeigniter.com/user_guide/license.html
- * @link http://codeigniter.com
- * @since Version 1.0
+ * This content is released under the MIT License (MIT)
+ *
+ * Copyright (c) 2019 - 2022, CodeIgniter Foundation
+ *
+ * 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.
+ *
+ * @package CodeIgniter
+ * @author EllisLab Dev Team
+ * @copyright Copyright (c) 2008 - 2014, EllisLab, Inc. (https://ellislab.com/)
+ * @copyright Copyright (c) 2014 - 2019, British Columbia Institute of Technology (https://bcit.ca/)
+ * @copyright Copyright (c) 2019 - 2022, CodeIgniter Foundation (https://codeigniter.com/)
+ * @license https://opensource.org/licenses/MIT MIT License
+ * @link https://codeigniter.com
+ * @since Version 1.0.0
* @filesource
*/
-
-// ------------------------------------------------------------------------
+defined('BASEPATH') OR exit('No direct script access allowed');
/**
* Database Utility Class
*
* @category Database
- * @author ExpressionEngine Dev Team
- * @link http://codeigniter.com/user_guide/database/
+ * @author EllisLab Dev Team
+ * @link https://codeigniter.com/userguide3/database/
*/
-class CI_DB_utility extends CI_DB_forge {
+abstract class CI_DB_utility {
- var $db;
- var $data_cache = array();
+ /**
+ * Database object
+ *
+ * @var object
+ */
+ protected $db;
+
+ // --------------------------------------------------------------------
/**
- * Constructor
+ * List databases statement
*
- * Grabs the CI super object instance so we can access it.
+ * @var string
+ */
+ protected $_list_databases = FALSE;
+
+ /**
+ * OPTIMIZE TABLE statement
*
+ * @var string
*/
- function __construct()
- {
- // Assign the main database object to $this->db
- $CI =& get_instance();
- $this->db =& $CI->db;
+ protected $_optimize_table = FALSE;
+
+ /**
+ * REPAIR TABLE statement
+ *
+ * @var string
+ */
+ protected $_repair_table = FALSE;
- log_message('debug', "Database Utility Class Initialized");
+ // --------------------------------------------------------------------
+
+ /**
+ * Class constructor
+ *
+ * @param object &$db Database object
+ * @return void
+ */
+ public function __construct(&$db)
+ {
+ $this->db =& $db;
+ log_message('info', 'Database Utility Class Initialized');
}
// --------------------------------------------------------------------
@@ -47,29 +96,34 @@ class CI_DB_utility extends CI_DB_forge {
/**
* List databases
*
- * @access public
- * @return bool
+ * @return array
*/
- function list_databases()
+ public function list_databases()
{
// Is there a cached result?
- if (isset($this->data_cache['db_names']))
+ if (isset($this->db->data_cache['db_names']))
{
- return $this->data_cache['db_names'];
+ return $this->db->data_cache['db_names'];
}
+ elseif ($this->_list_databases === FALSE)
+ {
+ return ($this->db->db_debug) ? $this->db->display_error('db_unsupported_feature') : FALSE;
+ }
+
+ $this->db->data_cache['db_names'] = array();
- $query = $this->db->query($this->_list_databases());
- $dbs = array();
- if ($query->num_rows() > 0)
+ $query = $this->db->query($this->_list_databases);
+ if ($query === FALSE)
{
- foreach ($query->result_array() as $row)
- {
- $dbs[] = current($row);
- }
+ return $this->db->data_cache['db_names'];
+ }
+
+ for ($i = 0, $query = $query->result_array(), $c = count($query); $i < $c; $i++)
+ {
+ $this->db->data_cache['db_names'][] = current($query[$i]);
}
- $this->data_cache['db_names'] = $dbs;
- return $this->data_cache['db_names'];
+ return $this->db->data_cache['db_names'];
}
// --------------------------------------------------------------------
@@ -77,50 +131,37 @@ class CI_DB_utility extends CI_DB_forge {
/**
* Determine if a particular database exists
*
- * @access public
- * @param string
- * @return boolean
+ * @param string $database_name
+ * @return bool
*/
- function database_exists($database_name)
+ public function database_exists($database_name)
{
- // Some databases won't have access to the list_databases() function, so
- // this is intended to allow them to override with their own functions as
- // defined in $driver_utility.php
- if (method_exists($this, '_database_exists'))
- {
- return $this->_database_exists($database_name);
- }
- else
- {
- return ( ! in_array($database_name, $this->list_databases())) ? FALSE : TRUE;
- }
+ return in_array($database_name, $this->list_databases());
}
-
// --------------------------------------------------------------------
/**
* Optimize Table
*
- * @access public
- * @param string the table name
- * @return bool
+ * @param string $table_name
+ * @return mixed
*/
- function optimize_table($table_name)
+ public function optimize_table($table_name)
{
- $sql = $this->_optimize_table($table_name);
-
- if (is_bool($sql))
+ if ($this->_optimize_table === FALSE)
{
- show_error('db_must_use_set');
+ return ($this->db->db_debug) ? $this->db->display_error('db_unsupported_feature') : FALSE;
}
- $query = $this->db->query($sql);
- $res = $query->result_array();
+ $query = $this->db->query(sprintf($this->_optimize_table, $this->db->escape_identifiers($table_name)));
+ if ($query !== FALSE)
+ {
+ $query = $query->result_array();
+ return current($query);
+ }
- // Note: Due to a bug in current() that affects some versions
- // of PHP we can not pass function call directly into it
- return current($res);
+ return FALSE;
}
// --------------------------------------------------------------------
@@ -128,27 +169,26 @@ class CI_DB_utility extends CI_DB_forge {
/**
* Optimize Database
*
- * @access public
- * @return array
+ * @return mixed
*/
- function optimize_database()
+ public function optimize_database()
{
+ if ($this->_optimize_table === FALSE)
+ {
+ return ($this->db->db_debug) ? $this->db->display_error('db_unsupported_feature') : FALSE;
+ }
+
$result = array();
foreach ($this->db->list_tables() as $table_name)
{
- $sql = $this->_optimize_table($table_name);
-
- if (is_bool($sql))
+ $res = $this->db->query(sprintf($this->_optimize_table, $this->db->escape_identifiers($table_name)));
+ if (is_bool($res))
{
- return $sql;
+ return $res;
}
- $query = $this->db->query($sql);
-
// Build the result array...
- // Note: Due to a bug in current() that affects some versions
- // of PHP we can not pass function call directly into it
- $res = $query->result_array();
+ $res = $res->result_array();
$res = current($res);
$key = str_replace($this->db->database.'.', '', current($res));
$keys = array_keys($res);
@@ -165,25 +205,24 @@ class CI_DB_utility extends CI_DB_forge {
/**
* Repair Table
*
- * @access public
- * @param string the table name
- * @return bool
+ * @param string $table_name
+ * @return mixed
*/
- function repair_table($table_name)
+ public function repair_table($table_name)
{
- $sql = $this->_repair_table($table_name);
-
- if (is_bool($sql))
+ if ($this->_repair_table === FALSE)
{
- return $sql;
+ return ($this->db->db_debug) ? $this->db->display_error('db_unsupported_feature') : FALSE;
}
- $query = $this->db->query($sql);
+ $query = $this->db->query(sprintf($this->_repair_table, $this->db->escape_identifiers($table_name)));
+ if (is_bool($query))
+ {
+ return $query;
+ }
- // Note: Due to a bug in current() that affects some versions
- // of PHP we can not pass function call directly into it
- $res = $query->result_array();
- return current($res);
+ $query = $query->result_array();
+ return current($query);
}
// --------------------------------------------------------------------
@@ -191,40 +230,32 @@ class CI_DB_utility extends CI_DB_forge {
/**
* Generate CSV from a query result object
*
- * @access public
- * @param object The query result object
- * @param string The delimiter - comma by default
- * @param string The newline character - \n by default
- * @param string The enclosure - double quote by default
+ * @param object $query Query result object
+ * @param string $delim Delimiter (default: ,)
+ * @param string $newline Newline character (default: \n)
+ * @param string $enclosure Enclosure (default: ")
* @return string
*/
- function csv_from_result($query, $delim = ",", $newline = "\n", $enclosure = '"')
+ public function csv_from_result(CI_DB_result $query, $delim = ',', $newline = "\n", $enclosure = '"')
{
- if ( ! is_object($query) OR ! method_exists($query, 'list_fields'))
- {
- show_error('You must submit a valid result object');
- }
-
$out = '';
-
// First generate the headings from the table column names
foreach ($query->list_fields() as $name)
{
$out .= $enclosure.str_replace($enclosure, $enclosure.$enclosure, $name).$enclosure.$delim;
}
- $out = rtrim($out);
- $out .= $newline;
+ $out = substr($out, 0, -strlen($delim)).$newline;
// Next blast through the result array and build out the rows
- foreach ($query->result_array() as $row)
+ while ($row = $query->unbuffered_row('array'))
{
+ $line = array();
foreach ($row as $item)
{
- $out .= $enclosure.str_replace($enclosure, $enclosure.$enclosure, $item).$enclosure.$delim;
+ $line[] = $enclosure.str_replace($enclosure, $enclosure.$enclosure, (string) $item).$enclosure;
}
- $out = rtrim($out);
- $out .= $newline;
+ $out .= implode($delim, $line).$newline;
}
return $out;
@@ -235,18 +266,12 @@ class CI_DB_utility extends CI_DB_forge {
/**
* Generate XML data from a query result object
*
- * @access public
- * @param object The query result object
- * @param array Any preferences
+ * @param object $query Query result object
+ * @param array $params Any preferences
* @return string
*/
- function xml_from_result($query, $params = array())
+ public function xml_from_result(CI_DB_result $query, $params = array())
{
- if ( ! is_object($query) OR ! method_exists($query, 'list_fields'))
- {
- show_error('You must submit a valid result object');
- }
-
// Set our default values
foreach (array('root' => 'root', 'element' => 'element', 'newline' => "\n", 'tab' => "\t") as $key => $val)
{
@@ -260,24 +285,21 @@ class CI_DB_utility extends CI_DB_forge {
extract($params);
// Load the xml helper
- $CI =& get_instance();
- $CI->load->helper('xml');
+ get_instance()->load->helper('xml');
// Generate the result
- $xml = "<{$root}>".$newline;
- foreach ($query->result_array() as $row)
+ $xml = '<'.$root.'>'.$newline;
+ while ($row = $query->unbuffered_row())
{
- $xml .= $tab."<{$element}>".$newline;
-
+ $xml .= $tab.'<'.$element.'>'.$newline;
foreach ($row as $key => $val)
{
- $xml .= $tab.$tab."<{$key}>".xml_convert($val)."</{$key}>".$newline;
+ $xml .= $tab.$tab.'<'.$key.'>'.xml_convert($val).'</'.$key.'>'.$newline;
}
- $xml .= $tab."</{$element}>".$newline;
+ $xml .= $tab.'</'.$element.'>'.$newline;
}
- $xml .= "</$root>".$newline;
- return $xml;
+ return $xml.'</'.$root.'>'.$newline;
}
// --------------------------------------------------------------------
@@ -285,10 +307,10 @@ class CI_DB_utility extends CI_DB_forge {
/**
* Database Backup
*
- * @access public
- * @return void
+ * @param array $params
+ * @return string
*/
- function backup($params = array())
+ public function backup($params = array())
{
// If the parameters have not been submitted as an
// array then we know that it is simply the table
@@ -298,18 +320,17 @@ class CI_DB_utility extends CI_DB_forge {
$params = array('tables' => $params);
}
- // ------------------------------------------------------
-
// Set up our default preferences
$prefs = array(
- 'tables' => array(),
- 'ignore' => array(),
- 'filename' => '',
- 'format' => 'gzip', // gzip, zip, txt
- 'add_drop' => TRUE,
- 'add_insert' => TRUE,
- 'newline' => "\n"
- );
+ 'tables' => array(),
+ 'ignore' => array(),
+ 'filename' => '',
+ 'format' => 'gzip', // gzip, zip, txt
+ 'add_drop' => TRUE,
+ 'add_insert' => TRUE,
+ 'newline' => "\n",
+ 'foreign_key_checks' => TRUE
+ );
// Did the user submit any preferences? If so set them....
if (count($params) > 0)
@@ -323,92 +344,72 @@ class CI_DB_utility extends CI_DB_forge {
}
}
- // ------------------------------------------------------
-
// Are we backing up a complete database or individual tables?
// If no table names were submitted we'll fetch the entire table list
- if (count($prefs['tables']) == 0)
+ if (count($prefs['tables']) === 0)
{
$prefs['tables'] = $this->db->list_tables();
}
- // ------------------------------------------------------
-
// Validate the format
if ( ! in_array($prefs['format'], array('gzip', 'zip', 'txt'), TRUE))
{
$prefs['format'] = 'txt';
}
- // ------------------------------------------------------
-
- // Is the encoder supported? If not, we'll either issue an
+ // Is the encoder supported? If not, we'll either issue an
// error or use plain text depending on the debug settings
- if (($prefs['format'] == 'gzip' AND ! @function_exists('gzencode'))
- OR ($prefs['format'] == 'zip' AND ! @function_exists('gzcompress')))
+ if (($prefs['format'] === 'gzip' && ! function_exists('gzencode'))
+ OR ($prefs['format'] === 'zip' && ! function_exists('gzcompress')))
{
if ($this->db->db_debug)
{
- return $this->db->display_error('db_unsuported_compression');
+ return $this->db->display_error('db_unsupported_compression');
}
$prefs['format'] = 'txt';
}
- // ------------------------------------------------------
-
- // Set the filename if not provided - Only needed with Zip files
- if ($prefs['filename'] == '' AND $prefs['format'] == 'zip')
- {
- $prefs['filename'] = (count($prefs['tables']) == 1) ? $prefs['tables'] : $this->db->database;
- $prefs['filename'] .= '_'.date('Y-m-d_H-i', time());
- }
-
- // ------------------------------------------------------
-
- // Was a Gzip file requested?
- if ($prefs['format'] == 'gzip')
- {
- return gzencode($this->_backup($prefs));
- }
-
- // ------------------------------------------------------
-
- // Was a text file requested?
- if ($prefs['format'] == 'txt')
- {
- return $this->_backup($prefs);
- }
-
- // ------------------------------------------------------
-
// Was a Zip file requested?
- if ($prefs['format'] == 'zip')
+ if ($prefs['format'] === 'zip')
{
- // If they included the .zip file extension we'll remove it
- if (preg_match("|.+?\.zip$|", $prefs['filename']))
+ // Set the filename if not provided (only needed with Zip files)
+ if ($prefs['filename'] === '')
{
- $prefs['filename'] = str_replace('.zip', '', $prefs['filename']);
+ $prefs['filename'] = (count($prefs['tables']) === 1 ? $prefs['tables'] : $this->db->database)
+ .date('Y-m-d_H-i', time()).'.sql';
}
-
- // Tack on the ".sql" file extension if needed
- if ( ! preg_match("|.+?\.sql$|", $prefs['filename']))
+ else
{
- $prefs['filename'] .= '.sql';
+ // If they included the .zip file extension we'll remove it
+ if (preg_match('|.+?\.zip$|', $prefs['filename']))
+ {
+ $prefs['filename'] = str_replace('.zip', '', $prefs['filename']);
+ }
+
+ // Tack on the ".sql" file extension if needed
+ if ( ! preg_match('|.+?\.sql$|', $prefs['filename']))
+ {
+ $prefs['filename'] .= '.sql';
+ }
}
// Load the Zip class and output it
-
$CI =& get_instance();
$CI->load->library('zip');
$CI->zip->add_data($prefs['filename'], $this->_backup($prefs));
return $CI->zip->get_zip();
}
+ elseif ($prefs['format'] === 'txt') // Was a text file requested?
+ {
+ return $this->_backup($prefs);
+ }
+ elseif ($prefs['format'] === 'gzip') // Was a Gzip file requested?
+ {
+ return gzencode($this->_backup($prefs));
+ }
+ return;
}
}
-
-
-/* End of file DB_utility.php */
-/* Location: ./system/database/DB_utility.php */ \ No newline at end of file
diff --git a/system/database/drivers/cubrid/cubrid_driver.php b/system/database/drivers/cubrid/cubrid_driver.php
index 13bafa3ed..bd01be6a0 100644
--- a/system/database/drivers/cubrid/cubrid_driver.php
+++ b/system/database/drivers/cubrid/cubrid_driver.php
@@ -1,108 +1,136 @@
-<?php if ( ! defined('BASEPATH')) exit('No direct script access allowed');
+<?php
/**
* CodeIgniter
*
- * An open source application development framework for PHP 5.1.6 or newer
+ * An open source application development framework for PHP
*
- * @package CodeIgniter
- * @author Esen Sagynov
- * @copyright Copyright (c) 2008 - 2014, EllisLab, Inc.
- * @license http://codeigniter.com/user_guide/license.html
- * @link http://codeigniter.com
- * @since Version 2.0.2
+ * This content is released under the MIT License (MIT)
+ *
+ * Copyright (c) 2019 - 2022, CodeIgniter Foundation
+ *
+ * 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.
+ *
+ * @package CodeIgniter
+ * @author EllisLab Dev Team
+ * @copyright Copyright (c) 2008 - 2014, EllisLab, Inc. (https://ellislab.com/)
+ * @copyright Copyright (c) 2014 - 2019, British Columbia Institute of Technology (https://bcit.ca/)
+ * @copyright Copyright (c) 2019 - 2022, CodeIgniter Foundation (https://codeigniter.com/)
+ * @license https://opensource.org/licenses/MIT MIT License
+ * @link https://codeigniter.com
+ * @since Version 2.1.0
* @filesource
*/
-
-// ------------------------------------------------------------------------
+defined('BASEPATH') OR exit('No direct script access allowed');
/**
* CUBRID Database Adapter Class
*
* Note: _DB is an extender class that the app controller
- * creates dynamically based on whether the active record
+ * creates dynamically based on whether the query builder
* class is being used or not.
*
* @package CodeIgniter
* @subpackage Drivers
* @category Database
* @author Esen Sagynov
- * @link http://codeigniter.com/user_guide/database/
+ * @link https://codeigniter.com/userguide3/database/
*/
class CI_DB_cubrid_driver extends CI_DB {
- // Default CUBRID Broker port. Will be used unless user
- // explicitly specifies another one.
- const DEFAULT_PORT = 33000;
+ /**
+ * Database driver
+ *
+ * @var string
+ */
+ public $dbdriver = 'cubrid';
- var $dbdriver = 'cubrid';
+ /**
+ * Auto-commit flag
+ *
+ * @var bool
+ */
+ public $auto_commit = TRUE;
- // The character used for escaping - no need in CUBRID
- var $_escape_char = '';
+ // --------------------------------------------------------------------
- // clause and character used for LIKE escape sequences - not used in CUBRID
- var $_like_escape_str = '';
- var $_like_escape_chr = '';
+ /**
+ * Identifier escape character
+ *
+ * @var string
+ */
+ protected $_escape_char = '`';
/**
- * The syntax to count rows is slightly different across different
- * database engines, so this string appears in each driver and is
- * used for the count_all() and count_all_results() functions.
+ * ORDER BY random keyword
+ *
+ * @var array
*/
- var $_count_string = 'SELECT COUNT(*) AS ';
- var $_random_keyword = ' RAND()'; // database specific random keyword
+ protected $_random_keyword = array('RANDOM()', 'RANDOM(%d)');
+
+ // --------------------------------------------------------------------
/**
- * Non-persistent database connection
+ * Class constructor
*
- * @access private called by the base class
- * @return resource
+ * @param array $params
+ * @return void
*/
- function db_connect()
+ public function __construct($params)
{
- // If no port is defined by the user, use the default value
- if ($this->port == '')
- {
- $this->port = self::DEFAULT_PORT;
- }
+ parent::__construct($params);
- $conn = cubrid_connect($this->hostname, $this->port, $this->database, $this->username, $this->password);
-
- if ($conn)
+ if (preg_match('/^CUBRID:[^:]+(:[0-9][1-9]{0,4})?:[^:]+:[^:]*:[^:]*:(\?.+)?$/', $this->dsn, $matches))
{
- // Check if a user wants to run queries in dry, i.e. run the
- // queries but not commit them.
- if (isset($this->auto_commit) && ! $this->auto_commit)
- {
- cubrid_set_autocommit($conn, CUBRID_AUTOCOMMIT_FALSE);
- }
- else
+ if (stripos($matches[2], 'autocommit=off') !== FALSE)
{
- cubrid_set_autocommit($conn, CUBRID_AUTOCOMMIT_TRUE);
- $this->auto_commit = TRUE;
+ $this->auto_commit = FALSE;
}
}
-
- return $conn;
+ else
+ {
+ // If no port is defined by the user, use the default value
+ empty($this->port) OR $this->port = 33000;
+ }
}
// --------------------------------------------------------------------
/**
- * Persistent database connection
- * In CUBRID persistent DB connection is supported natively in CUBRID
- * engine which can be configured in the CUBRID Broker configuration
- * file by setting the CCI_PCONNECT parameter to ON. In that case, all
- * connections established between the client application and the
- * server will become persistent. This is calling the same
- * @cubrid_connect function will establish persisten connection
- * considering that the CCI_PCONNECT is ON.
+ * Non-persistent database connection
*
- * @access private called by the base class
+ * @param bool $persistent
* @return resource
*/
- function db_pconnect()
+ public function db_connect($persistent = FALSE)
{
- return $this->db_connect();
+ if (preg_match('/^CUBRID:[^:]+(:[0-9][1-9]{0,4})?:[^:]+:([^:]*):([^:]*):(\?.+)?$/', $this->dsn, $matches))
+ {
+ $func = ($persistent !== TRUE) ? 'cubrid_connect_with_url' : 'cubrid_pconnect_with_url';
+ return ($matches[2] === '' && $matches[3] === '' && $this->username !== '' && $this->password !== '')
+ ? $func($this->dsn, $this->username, $this->password)
+ : $func($this->dsn);
+ }
+
+ $func = ($persistent !== TRUE) ? 'cubrid_connect' : 'cubrid_pconnect';
+ return ($this->username !== '')
+ ? $func($this->hostname, $this->port, $this->database, $this->username, $this->password)
+ : $func($this->hostname, $this->port, $this->database);
}
// --------------------------------------------------------------------
@@ -113,10 +141,9 @@ class CI_DB_cubrid_driver extends CI_DB {
* Keep / reestablish the db connection if no queries have been
* sent for a length of time exceeding the server's idle timeout
*
- * @access public
* @return void
*/
- function reconnect()
+ public function reconnect()
{
if (cubrid_ping($this->conn_id) === FALSE)
{
@@ -127,54 +154,20 @@ class CI_DB_cubrid_driver extends CI_DB {
// --------------------------------------------------------------------
/**
- * Select the database
- *
- * @access private called by the base class
- * @return resource
- */
- function db_select()
- {
- // In CUBRID there is no need to select a database as the database
- // is chosen at the connection time.
- // So, to determine if the database is "selected", all we have to
- // do is ping the server and return that value.
- return cubrid_ping($this->conn_id);
- }
-
- // --------------------------------------------------------------------
-
- /**
- * Set client character set
- *
- * @access public
- * @param string
- * @param string
- * @return resource
- */
- function db_set_charset($charset, $collation)
- {
- // In CUBRID, there is no need to set charset or collation.
- // This is why returning true will allow the application continue
- // its normal process.
- return TRUE;
- }
-
- // --------------------------------------------------------------------
-
- /**
- * Version number query string
+ * Database version number
*
- * @access public
* @return string
*/
- function _version()
+ public function version()
{
- // To obtain the CUBRID Server version, no need to run the SQL query.
- // CUBRID PHP API provides a function to determin this value.
- // This is why we also need to add 'cubrid' value to the list of
- // $driver_version_exceptions array in DB_driver class in
- // version() function.
- return cubrid_get_server_info($this->conn_id);
+ if (isset($this->data_cache['version']))
+ {
+ return $this->data_cache['version'];
+ }
+
+ return ( ! $this->conn_id OR ($version = cubrid_get_server_info($this->conn_id)) === FALSE)
+ ? FALSE
+ : $this->data_cache['version'] = $version;
}
// --------------------------------------------------------------------
@@ -182,31 +175,12 @@ class CI_DB_cubrid_driver extends CI_DB {
/**
* Execute the query
*
- * @access private called by the base class
- * @param string an SQL query
+ * @param string $sql an SQL query
* @return resource
*/
- function _execute($sql)
+ protected function _execute($sql)
{
- $sql = $this->_prep_query($sql);
- return @cubrid_query($sql, $this->conn_id);
- }
-
- // --------------------------------------------------------------------
-
- /**
- * Prep the query
- *
- * If needed, each database adapter can prep the query string
- *
- * @access private called by execute()
- * @param string an SQL query
- * @return string
- */
- function _prep_query($sql)
- {
- // No need to prepare
- return $sql;
+ return cubrid_query($sql, $this->conn_id);
}
// --------------------------------------------------------------------
@@ -214,30 +188,17 @@ class CI_DB_cubrid_driver extends CI_DB {
/**
* Begin Transaction
*
- * @access public
* @return bool
*/
- function trans_begin($test_mode = FALSE)
+ protected function _trans_begin()
{
- if ( ! $this->trans_enabled)
+ if (($autocommit = cubrid_get_autocommit($this->conn_id)) === NULL)
{
- return TRUE;
+ return FALSE;
}
-
- // When transactions are nested we only begin/commit/rollback the outermost ones
- if ($this->_trans_depth > 0)
- {
- return TRUE;
- }
-
- // Reset the transaction failure flag.
- // If the $test_mode flag is set to TRUE transactions will be rolled back
- // even if the queries produce a successful result.
- $this->_trans_failure = ($test_mode === TRUE) ? TRUE : FALSE;
-
- if (cubrid_get_autocommit($this->conn_id))
+ elseif ($autocommit === TRUE)
{
- cubrid_set_autocommit($this->conn_id, CUBRID_AUTOCOMMIT_FALSE);
+ return cubrid_set_autocommit($this->conn_id, CUBRID_AUTOCOMMIT_FALSE);
}
return TRUE;
@@ -248,27 +209,18 @@ class CI_DB_cubrid_driver extends CI_DB {
/**
* Commit Transaction
*
- * @access public
* @return bool
*/
- function trans_commit()
+ protected function _trans_commit()
{
- if ( ! $this->trans_enabled)
+ if ( ! cubrid_commit($this->conn_id))
{
- return TRUE;
+ return FALSE;
}
- // When transactions are nested we only begin/commit/rollback the outermost ones
- if ($this->_trans_depth > 0)
- {
- return TRUE;
- }
-
- cubrid_commit($this->conn_id);
-
if ($this->auto_commit && ! cubrid_get_autocommit($this->conn_id))
{
- cubrid_set_autocommit($this->conn_id, CUBRID_AUTOCOMMIT_TRUE);
+ return cubrid_set_autocommit($this->conn_id, CUBRID_AUTOCOMMIT_TRUE);
}
return TRUE;
@@ -279,24 +231,15 @@ class CI_DB_cubrid_driver extends CI_DB {
/**
* Rollback Transaction
*
- * @access public
* @return bool
*/
- function trans_rollback()
+ protected function _trans_rollback()
{
- if ( ! $this->trans_enabled)
- {
- return TRUE;
- }
-
- // When transactions are nested we only begin/commit/rollback the outermost ones
- if ($this->_trans_depth > 0)
+ if ( ! cubrid_rollback($this->conn_id))
{
- return TRUE;
+ return FALSE;
}
- cubrid_rollback($this->conn_id);
-
if ($this->auto_commit && ! cubrid_get_autocommit($this->conn_id))
{
cubrid_set_autocommit($this->conn_id, CUBRID_AUTOCOMMIT_TRUE);
@@ -308,41 +251,14 @@ class CI_DB_cubrid_driver extends CI_DB {
// --------------------------------------------------------------------
/**
- * Escape String
+ * Platform-dependent string escape
*
- * @access public
* @param string
- * @param bool whether or not the string will be used in a LIKE condition
* @return string
*/
- function escape_str($str, $like = FALSE)
+ protected function _escape_str($str)
{
- if (is_array($str))
- {
- foreach ($str as $key => $val)
- {
- $str[$key] = $this->escape_str($val, $like);
- }
-
- return $str;
- }
-
- if (function_exists('cubrid_real_escape_string') AND is_resource($this->conn_id))
- {
- $str = cubrid_real_escape_string($str, $this->conn_id);
- }
- else
- {
- $str = addslashes($str);
- }
-
- // escape LIKE condition wildcards
- if ($like === TRUE)
- {
- $str = str_replace(array('%', '_'), array('\\%', '\\_'), $str);
- }
-
- return $str;
+ return cubrid_real_escape_string($str, $this->conn_id);
}
// --------------------------------------------------------------------
@@ -350,12 +266,11 @@ class CI_DB_cubrid_driver extends CI_DB {
/**
* Affected Rows
*
- * @access public
- * @return integer
+ * @return int
*/
- function affected_rows()
+ public function affected_rows()
{
- return @cubrid_affected_rows($this->conn_id);
+ return cubrid_affected_rows();
}
// --------------------------------------------------------------------
@@ -363,43 +278,11 @@ class CI_DB_cubrid_driver extends CI_DB {
/**
* Insert ID
*
- * @access public
- * @return integer
+ * @return int
*/
- function insert_id()
+ public function insert_id()
{
- return @cubrid_insert_id($this->conn_id);
- }
-
- // --------------------------------------------------------------------
-
- /**
- * "Count All" query
- *
- * Generates a platform-specific query string that counts all records in
- * the specified table
- *
- * @access public
- * @param string
- * @return string
- */
- function count_all($table = '')
- {
- if ($table == '')
- {
- return 0;
- }
-
- $query = $this->query($this->_count_string . $this->_protect_identifiers('numrows') . " FROM " . $this->_protect_identifiers($table, TRUE, NULL, FALSE));
-
- if ($query->num_rows() == 0)
- {
- return 0;
- }
-
- $row = $query->row();
- $this->_reset_select();
- return (int) $row->numrows;
+ return cubrid_insert_id($this->conn_id);
}
// --------------------------------------------------------------------
@@ -409,17 +292,16 @@ class CI_DB_cubrid_driver extends CI_DB {
*
* Generates a platform-specific query string so that the table names can be fetched
*
- * @access private
- * @param boolean
+ * @param bool $prefix_limit
* @return string
*/
- function _list_tables($prefix_limit = FALSE)
+ protected function _list_tables($prefix_limit = FALSE)
{
- $sql = "SHOW TABLES";
+ $sql = 'SHOW TABLES';
- if ($prefix_limit !== FALSE AND $this->dbprefix != '')
+ if ($prefix_limit !== FALSE && $this->dbprefix !== '')
{
- $sql .= " LIKE '".$this->escape_like_str($this->dbprefix)."%'";
+ return $sql." LIKE '".$this->escape_like_str($this->dbprefix)."%'";
}
return $sql;
@@ -432,343 +314,81 @@ class CI_DB_cubrid_driver extends CI_DB {
*
* Generates a platform-specific query string so that the column names can be fetched
*
- * @access public
- * @param string the table name
- * @return string
- */
- function _list_columns($table = '')
- {
- return "SHOW COLUMNS FROM ".$this->_protect_identifiers($table, TRUE, NULL, FALSE);
- }
-
- // --------------------------------------------------------------------
-
- /**
- * Field data query
- *
- * Generates a platform-specific query so that the column data can be retrieved
- *
- * @access public
- * @param string the table name
- * @return object
- */
- function _field_data($table)
- {
- return "SELECT * FROM ".$table." LIMIT 1";
- }
-
- // --------------------------------------------------------------------
-
- /**
- * The error message string
- *
- * @access private
+ * @param string $table
* @return string
*/
- function _error_message()
+ protected function _list_columns($table = '')
{
- return cubrid_error($this->conn_id);
+ return 'SHOW COLUMNS FROM '.$this->protect_identifiers($table, TRUE, NULL, FALSE);
}
// --------------------------------------------------------------------
/**
- * The error message number
+ * Returns an object with field data
*
- * @access private
- * @return integer
+ * @param string $table
+ * @return array
*/
- function _error_number()
+ public function field_data($table)
{
- return cubrid_errno($this->conn_id);
- }
-
- // --------------------------------------------------------------------
-
- /**
- * Escape the SQL Identifiers
- *
- * This function escapes column and table names
- *
- * @access private
- * @param string
- * @return string
- */
- function _escape_identifiers($item)
- {
- if ($this->_escape_char == '')
- {
- return $item;
- }
-
- foreach ($this->_reserved_identifiers as $id)
+ if (($query = $this->query('SHOW COLUMNS FROM '.$this->protect_identifiers($table, TRUE, NULL, FALSE))) === FALSE)
{
- if (strpos($item, '.'.$id) !== FALSE)
- {
- $str = $this->_escape_char. str_replace('.', $this->_escape_char.'.', $item);
-
- // remove duplicates if the user already included the escape
- return preg_replace('/['.$this->_escape_char.']+/', $this->_escape_char, $str);
- }
+ return FALSE;
}
+ $query = $query->result_object();
- if (strpos($item, '.') !== FALSE)
+ $retval = array();
+ for ($i = 0, $c = count($query); $i < $c; $i++)
{
- $str = $this->_escape_char.str_replace('.', $this->_escape_char.'.'.$this->_escape_char, $item).$this->_escape_char;
- }
- else
- {
- $str = $this->_escape_char.$item.$this->_escape_char;
- }
+ $retval[$i] = new stdClass();
+ $retval[$i]->name = $query[$i]->Field;
- // remove duplicates if the user already included the escape
- return preg_replace('/['.$this->_escape_char.']+/', $this->_escape_char, $str);
- }
-
- // --------------------------------------------------------------------
+ sscanf($query[$i]->Type, '%[a-z](%d)',
+ $retval[$i]->type,
+ $retval[$i]->max_length
+ );
- /**
- * From Tables
- *
- * This function implicitly groups FROM tables so there is no confusion
- * about operator precedence in harmony with SQL standards
- *
- * @access public
- * @param type
- * @return type
- */
- function _from_tables($tables)
- {
- if ( ! is_array($tables))
- {
- $tables = array($tables);
+ $retval[$i]->default = $query[$i]->Default;
+ $retval[$i]->primary_key = (int) ($query[$i]->Key === 'PRI');
}
- return '('.implode(', ', $tables).')';
- }
-
- // --------------------------------------------------------------------
-
- /**
- * Insert statement
- *
- * Generates a platform-specific insert string from the supplied data
- *
- * @access public
- * @param string the table name
- * @param array the insert keys
- * @param array the insert values
- * @return string
- */
- function _insert($table, $keys, $values)
- {
- return "INSERT INTO ".$table." (\"".implode('", "', $keys)."\") VALUES (".implode(', ', $values).")";
- }
-
- // --------------------------------------------------------------------
-
-
- /**
- * Replace statement
- *
- * Generates a platform-specific replace string from the supplied data
- *
- * @access public
- * @param string the table name
- * @param array the insert keys
- * @param array the insert values
- * @return string
- */
- function _replace($table, $keys, $values)
- {
- return "REPLACE INTO ".$table." (\"".implode('", "', $keys)."\") VALUES (".implode(', ', $values).")";
+ return $retval;
}
// --------------------------------------------------------------------
/**
- * Insert_batch statement
+ * Error
*
- * Generates a platform-specific insert string from the supplied data
+ * Returns an array containing code and message of the last
+ * database error that has occurred.
*
- * @access public
- * @param string the table name
- * @param array the insert keys
- * @param array the insert values
- * @return string
+ * @return array
*/
- function _insert_batch($table, $keys, $values)
+ public function error()
{
- return "INSERT INTO ".$table." (\"".implode('", "', $keys)."\") VALUES ".implode(', ', $values);
+ return array('code' => cubrid_errno($this->conn_id), 'message' => cubrid_error($this->conn_id));
}
// --------------------------------------------------------------------
-
/**
- * Update statement
+ * FROM tables
*
- * Generates a platform-specific update string from the supplied data
+ * Groups tables in FROM clauses if needed, so there is no confusion
+ * about operator precedence.
*
- * @access public
- * @param string the table name
- * @param array the update data
- * @param array the where clause
- * @param array the orderby clause
- * @param array the limit clause
* @return string
*/
- function _update($table, $values, $where, $orderby = array(), $limit = FALSE)
+ protected function _from_tables()
{
- foreach ($values as $key => $val)
+ if ( ! empty($this->qb_join) && count($this->qb_from) > 1)
{
- $valstr[] = sprintf('"%s" = %s', $key, $val);
+ return '('.implode(', ', $this->qb_from).')';
}
- $limit = ( ! $limit) ? '' : ' LIMIT '.$limit;
-
- $orderby = (count($orderby) >= 1)?' ORDER BY '.implode(", ", $orderby):'';
-
- $sql = "UPDATE ".$table." SET ".implode(', ', $valstr);
-
- $sql .= ($where != '' AND count($where) >=1) ? " WHERE ".implode(" ", $where) : '';
-
- $sql .= $orderby.$limit;
-
- return $sql;
- }
-
- // --------------------------------------------------------------------
-
-
- /**
- * Update_Batch statement
- *
- * Generates a platform-specific batch update string from the supplied data
- *
- * @access public
- * @param string the table name
- * @param array the update data
- * @param array the where clause
- * @return string
- */
- function _update_batch($table, $values, $index, $where = NULL)
- {
- $ids = array();
- $where = ($where != '' AND count($where) >=1) ? implode(" ", $where).' AND ' : '';
-
- foreach ($values as $key => $val)
- {
- $ids[] = $val[$index];
-
- foreach (array_keys($val) as $field)
- {
- if ($field != $index)
- {
- $final[$field][] = 'WHEN '.$index.' = '.$val[$index].' THEN '.$val[$field];
- }
- }
- }
-
- $sql = "UPDATE ".$table." SET ";
- $cases = '';
-
- foreach ($final as $k => $v)
- {
- $cases .= $k.' = CASE '."\n";
- foreach ($v as $row)
- {
- $cases .= $row."\n";
- }
-
- $cases .= 'ELSE '.$k.' END, ';
- }
-
- $sql .= substr($cases, 0, -2);
-
- $sql .= ' WHERE '.$where.$index.' IN ('.implode(',', $ids).')';
-
- return $sql;
- }
-
- // --------------------------------------------------------------------
-
-
- /**
- * Truncate statement
- *
- * Generates a platform-specific truncate string from the supplied data
- * If the database does not support the truncate() command
- * This function maps to "DELETE FROM table"
- *
- * @access public
- * @param string the table name
- * @return string
- */
- function _truncate($table)
- {
- return "TRUNCATE ".$table;
- }
-
- // --------------------------------------------------------------------
-
- /**
- * Delete statement
- *
- * Generates a platform-specific delete string from the supplied data
- *
- * @access public
- * @param string the table name
- * @param array the where clause
- * @param string the limit clause
- * @return string
- */
- function _delete($table, $where = array(), $like = array(), $limit = FALSE)
- {
- $conditions = '';
-
- if (count($where) > 0 OR count($like) > 0)
- {
- $conditions = "\nWHERE ";
- $conditions .= implode("\n", $this->ar_where);
-
- if (count($where) > 0 && count($like) > 0)
- {
- $conditions .= " AND ";
- }
- $conditions .= implode("\n", $like);
- }
-
- $limit = ( ! $limit) ? '' : ' LIMIT '.$limit;
-
- return "DELETE FROM ".$table.$conditions.$limit;
- }
-
- // --------------------------------------------------------------------
-
- /**
- * Limit string
- *
- * Generates a platform-specific LIMIT clause
- *
- * @access public
- * @param string the sql query string
- * @param integer the number of rows to limit the query to
- * @param integer the offset value
- * @return string
- */
- function _limit($sql, $limit, $offset)
- {
- if ($offset == 0)
- {
- $offset = '';
- }
- else
- {
- $offset .= ", ";
- }
-
- return $sql."LIMIT ".$offset.$limit;
+ return implode(', ', $this->qb_from);
}
// --------------------------------------------------------------------
@@ -776,17 +396,11 @@ class CI_DB_cubrid_driver extends CI_DB {
/**
* Close DB Connection
*
- * @access public
- * @param resource
* @return void
*/
- function _close($conn_id)
+ protected function _close()
{
- @cubrid_close($conn_id);
+ cubrid_close($this->conn_id);
}
}
-
-
-/* End of file cubrid_driver.php */
-/* Location: ./system/database/drivers/cubrid/cubrid_driver.php */ \ No newline at end of file
diff --git a/system/database/drivers/cubrid/cubrid_forge.php b/system/database/drivers/cubrid/cubrid_forge.php
index 0c0b08a62..e8e201f98 100644
--- a/system/database/drivers/cubrid/cubrid_forge.php
+++ b/system/database/drivers/cubrid/cubrid_forge.php
@@ -1,288 +1,231 @@
-<?php if ( ! defined('BASEPATH')) exit('No direct script access allowed');
+<?php
/**
* CodeIgniter
*
- * An open source application development framework for PHP 5.1.6 or newer
+ * An open source application development framework for PHP
*
- * @package CodeIgniter
- * @author Esen Sagynov
- * @copyright Copyright (c) 2008 - 2014, EllisLab, Inc.
- * @license http://codeigniter.com/user_guide/license.html
- * @link http://codeigniter.com
- * @since Version 1.0
+ * This content is released under the MIT License (MIT)
+ *
+ * Copyright (c) 2019 - 2022, CodeIgniter Foundation
+ *
+ * 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.
+ *
+ * @package CodeIgniter
+ * @author EllisLab Dev Team
+ * @copyright Copyright (c) 2008 - 2014, EllisLab, Inc. (https://ellislab.com/)
+ * @copyright Copyright (c) 2014 - 2019, British Columbia Institute of Technology (https://bcit.ca/)
+ * @copyright Copyright (c) 2019 - 2022, CodeIgniter Foundation (https://codeigniter.com/)
+ * @license https://opensource.org/licenses/MIT MIT License
+ * @link https://codeigniter.com
+ * @since Version 2.1.0
* @filesource
*/
-
-// ------------------------------------------------------------------------
+defined('BASEPATH') OR exit('No direct script access allowed');
/**
* CUBRID Forge Class
*
* @category Database
* @author Esen Sagynov
- * @link http://codeigniter.com/user_guide/database/
+ * @link https://codeigniter.com/userguide3/database/
*/
class CI_DB_cubrid_forge extends CI_DB_forge {
/**
- * Create database
+ * CREATE DATABASE statement
*
- * @access private
- * @param string the database name
- * @return bool
+ * @var string
*/
- function _create_database($name)
- {
- // CUBRID does not allow to create a database in SQL. The GUI tools
- // have to be used for this purpose.
- return FALSE;
- }
+ protected $_create_database = FALSE;
- // --------------------------------------------------------------------
+ /**
+ * CREATE TABLE keys flag
+ *
+ * Whether table keys are created from within the
+ * CREATE TABLE statement.
+ *
+ * @var bool
+ */
+ protected $_create_table_keys = TRUE;
/**
- * Drop database
+ * DROP DATABASE statement
*
- * @access private
- * @param string the database name
- * @return bool
+ * @var string
*/
- function _drop_database($name)
- {
- // CUBRID does not allow to drop a database in SQL. The GUI tools
- // have to be used for this purpose.
- return FALSE;
- }
+ protected $_drop_database = FALSE;
+
+ /**
+ * CREATE TABLE IF statement
+ *
+ * @var string
+ */
+ protected $_create_table_if = FALSE;
+
+ /**
+ * UNSIGNED support
+ *
+ * @var array
+ */
+ protected $_unsigned = array(
+ 'SHORT' => 'INTEGER',
+ 'SMALLINT' => 'INTEGER',
+ 'INT' => 'BIGINT',
+ 'INTEGER' => 'BIGINT',
+ 'BIGINT' => 'NUMERIC',
+ 'FLOAT' => 'DOUBLE',
+ 'REAL' => 'DOUBLE'
+ );
// --------------------------------------------------------------------
/**
- * Process Fields
+ * ALTER TABLE
*
- * @access private
- * @param mixed the fields
- * @return string
+ * @param string $alter_type ALTER type
+ * @param string $table Table name
+ * @param mixed $field Column definition
+ * @return string|string[]
*/
- function _process_fields($fields)
+ protected function _alter_table($alter_type, $table, $field)
{
- $current_field_count = 0;
- $sql = '';
+ if (in_array($alter_type, array('DROP', 'ADD'), TRUE))
+ {
+ return parent::_alter_table($alter_type, $table, $field);
+ }
- foreach ($fields as $field=>$attributes)
+ $sql = 'ALTER TABLE '.$this->db->escape_identifiers($table);
+ $sqls = array();
+ for ($i = 0, $c = count($field); $i < $c; $i++)
{
- // Numeric field names aren't allowed in databases, so if the key is
- // numeric, we know it was assigned by PHP and the developer manually
- // entered the field information, so we'll simply add it to the list
- if (is_numeric($field))
+ if ($field[$i]['_literal'] !== FALSE)
{
- $sql .= "\n\t$attributes";
+ $sqls[] = $sql.' CHANGE '.$field[$i]['_literal'];
}
else
{
- $attributes = array_change_key_case($attributes, CASE_UPPER);
-
- $sql .= "\n\t\"" . $this->db->_protect_identifiers($field) . "\"";
-
- if (array_key_exists('NAME', $attributes))
- {
- $sql .= ' '.$this->db->_protect_identifiers($attributes['NAME']).' ';
- }
-
- if (array_key_exists('TYPE', $attributes))
- {
- $sql .= ' '.$attributes['TYPE'];
-
- if (array_key_exists('CONSTRAINT', $attributes))
- {
- switch ($attributes['TYPE'])
- {
- case 'decimal':
- case 'float':
- case 'numeric':
- $sql .= '('.implode(',', $attributes['CONSTRAINT']).')';
- break;
- case 'enum': // As of version 8.4.0 CUBRID does not support
- // enum data type.
- break;
- case 'set':
- $sql .= '("'.implode('","', $attributes['CONSTRAINT']).'")';
- break;
- default:
- $sql .= '('.$attributes['CONSTRAINT'].')';
- }
- }
- }
-
- if (array_key_exists('UNSIGNED', $attributes) && $attributes['UNSIGNED'] === TRUE)
- {
- //$sql .= ' UNSIGNED';
- // As of version 8.4.0 CUBRID does not support UNSIGNED INTEGER data type.
- // Will be supported in the next release as a part of MySQL Compatibility.
- }
-
- if (array_key_exists('DEFAULT', $attributes))
- {
- $sql .= ' DEFAULT \''.$attributes['DEFAULT'].'\'';
- }
-
- if (array_key_exists('NULL', $attributes) && $attributes['NULL'] === TRUE)
- {
- $sql .= ' NULL';
- }
- else
- {
- $sql .= ' NOT NULL';
- }
-
- if (array_key_exists('AUTO_INCREMENT', $attributes) && $attributes['AUTO_INCREMENT'] === TRUE)
- {
- $sql .= ' AUTO_INCREMENT';
- }
-
- if (array_key_exists('UNIQUE', $attributes) && $attributes['UNIQUE'] === TRUE)
- {
- $sql .= ' UNIQUE';
- }
- }
-
- // don't add a comma on the end of the last field
- if (++$current_field_count < count($fields))
- {
- $sql .= ',';
+ $alter_type = empty($field[$i]['new_name']) ? ' MODIFY ' : ' CHANGE ';
+ $sqls[] = $sql.$alter_type.$this->_process_column($field[$i]);
}
}
- return $sql;
+ return $sqls;
}
// --------------------------------------------------------------------
/**
- * Create Table
+ * Process column
*
- * @access private
- * @param string the table name
- * @param mixed the fields
- * @param mixed primary key(s)
- * @param mixed key(s)
- * @param boolean should 'IF NOT EXISTS' be added to the SQL
- * @return bool
+ * @param array $field
+ * @return string
*/
- function _create_table($table, $fields, $primary_keys, $keys, $if_not_exists)
+ protected function _process_column($field)
{
- $sql = 'CREATE TABLE ';
-
- if ($if_not_exists === TRUE)
- {
- //$sql .= 'IF NOT EXISTS ';
- // As of version 8.4.0 CUBRID does not support this SQL syntax.
- }
-
- $sql .= $this->db->_escape_identifiers($table)." (";
-
- $sql .= $this->_process_fields($fields);
+ $extra_clause = isset($field['after'])
+ ? ' AFTER '.$this->db->escape_identifiers($field['after']) : '';
- // If there is a PK defined
- if (count($primary_keys) > 0)
+ if (empty($extra_clause) && isset($field['first']) && $field['first'] === TRUE)
{
- $key_name = "pk_" . $table . "_" .
- $this->db->_protect_identifiers(implode('_', $primary_keys));
-
- $primary_keys = $this->db->_protect_identifiers($primary_keys);
- $sql .= ",\n\tCONSTRAINT " . $key_name . " PRIMARY KEY(" . implode(', ', $primary_keys) . ")";
+ $extra_clause = ' FIRST';
}
- if (is_array($keys) && count($keys) > 0)
- {
- foreach ($keys as $key)
- {
- if (is_array($key))
- {
- $key_name = $this->db->_protect_identifiers(implode('_', $key));
- $key = $this->db->_protect_identifiers($key);
- }
- else
- {
- $key_name = $this->db->_protect_identifiers($key);
- $key = array($key_name);
- }
-
- $sql .= ",\n\tKEY \"{$key_name}\" (" . implode(', ', $key) . ")";
- }
- }
-
- $sql .= "\n);";
-
- return $sql;
+ return $this->db->escape_identifiers($field['name'])
+ .(empty($field['new_name']) ? '' : ' '.$this->db->escape_identifiers($field['new_name']))
+ .' '.$field['type'].$field['length']
+ .$field['unsigned']
+ .$field['null']
+ .$field['default']
+ .$field['auto_increment']
+ .$field['unique']
+ .$extra_clause;
}
// --------------------------------------------------------------------
/**
- * Drop Table
+ * Field attribute TYPE
*
- * @access private
- * @return string
+ * Performs a data type mapping between different databases.
+ *
+ * @param array &$attributes
+ * @return void
*/
- function _drop_table($table)
+ protected function _attr_type(&$attributes)
{
- return "DROP TABLE IF EXISTS ".$this->db->_escape_identifiers($table);
+ switch (strtoupper($attributes['TYPE']))
+ {
+ case 'TINYINT':
+ $attributes['TYPE'] = 'SMALLINT';
+ $attributes['UNSIGNED'] = FALSE;
+ return;
+ case 'MEDIUMINT':
+ $attributes['TYPE'] = 'INTEGER';
+ $attributes['UNSIGNED'] = FALSE;
+ return;
+ case 'LONGTEXT':
+ $attributes['TYPE'] = 'STRING';
+ return;
+ default: return;
+ }
}
// --------------------------------------------------------------------
/**
- * Alter table query
+ * Process indexes
*
- * Generates a platform-specific query so that a table can be altered
- * Called by add_column(), drop_column(), and column_alter(),
- *
- * @access private
- * @param string the ALTER type (ADD, DROP, CHANGE)
- * @param string the column name
- * @param array fields
- * @param string the field after which we should add the new field
- * @return object
+ * @param string $table (ignored)
+ * @return string
*/
- function _alter_table($alter_type, $table, $fields, $after_field = '')
+ protected function _process_indexes($table)
{
- $sql = 'ALTER TABLE '.$this->db->_protect_identifiers($table)." $alter_type ";
+ $sql = '';
- // DROP has everything it needs now.
- if ($alter_type == 'DROP')
+ for ($i = 0, $c = count($this->keys); $i < $c; $i++)
{
- return $sql.$this->db->_protect_identifiers($fields);
- }
+ if (is_array($this->keys[$i]))
+ {
+ for ($i2 = 0, $c2 = count($this->keys[$i]); $i2 < $c2; $i2++)
+ {
+ if ( ! isset($this->fields[$this->keys[$i][$i2]]))
+ {
+ unset($this->keys[$i][$i2]);
+ continue;
+ }
+ }
+ }
+ elseif ( ! isset($this->fields[$this->keys[$i]]))
+ {
+ unset($this->keys[$i]);
+ continue;
+ }
- $sql .= $this->_process_fields($fields);
+ is_array($this->keys[$i]) OR $this->keys[$i] = array($this->keys[$i]);
- if ($after_field != '')
- {
- $sql .= ' AFTER ' . $this->db->_protect_identifiers($after_field);
+ $sql .= ",\n\tKEY ".$this->db->escape_identifiers(implode('_', $this->keys[$i]))
+ .' ('.implode(', ', $this->db->escape_identifiers($this->keys[$i])).')';
}
- return $sql;
- }
+ $this->keys = array();
- // --------------------------------------------------------------------
-
- /**
- * Rename a table
- *
- * Generates a platform-specific query so that a table can be renamed
- *
- * @access private
- * @param string the old table name
- * @param string the new table name
- * @return string
- */
- function _rename_table($table_name, $new_table_name)
- {
- $sql = 'RENAME TABLE '.$this->db->_protect_identifiers($table_name)." AS ".$this->db->_protect_identifiers($new_table_name);
return $sql;
}
}
-
-/* End of file cubrid_forge.php */
-/* Location: ./system/database/drivers/cubrid/cubrid_forge.php */ \ No newline at end of file
diff --git a/system/database/drivers/cubrid/cubrid_result.php b/system/database/drivers/cubrid/cubrid_result.php
index 3f4dca935..274b0c963 100644
--- a/system/database/drivers/cubrid/cubrid_result.php
+++ b/system/database/drivers/cubrid/cubrid_result.php
@@ -1,19 +1,42 @@
-<?php if ( ! defined('BASEPATH')) exit('No direct script access allowed');
+<?php
/**
* CodeIgniter
*
- * An open source application development framework for PHP 5.1.6 or newer
+ * An open source application development framework for PHP
*
- * @package CodeIgniter
- * @author Esen Sagynov
- * @copyright Copyright (c) 2008 - 2014, EllisLab, Inc.
- * @license http://codeigniter.com/user_guide/license.html
- * @link http://codeigniter.com
- * @since Version 2.0.2
+ * This content is released under the MIT License (MIT)
+ *
+ * Copyright (c) 2019 - 2022, CodeIgniter Foundation
+ *
+ * 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.
+ *
+ * @package CodeIgniter
+ * @author EllisLab Dev Team
+ * @copyright Copyright (c) 2008 - 2014, EllisLab, Inc. (https://ellislab.com/)
+ * @copyright Copyright (c) 2014 - 2019, British Columbia Institute of Technology (https://bcit.ca/)
+ * @copyright Copyright (c) 2019 - 2022, CodeIgniter Foundation (https://codeigniter.com/)
+ * @license https://opensource.org/licenses/MIT MIT License
+ * @link https://codeigniter.com
+ * @since Version 2.1.0
* @filesource
*/
-
-// --------------------------------------------------------------------
+defined('BASEPATH') OR exit('No direct script access allowed');
/**
* CUBRID Result Class
@@ -22,19 +45,20 @@
*
* @category Database
* @author Esen Sagynov
- * @link http://codeigniter.com/user_guide/database/
+ * @link https://codeigniter.com/userguide3/database/
*/
class CI_DB_cubrid_result extends CI_DB_result {
/**
* Number of rows in the result set
*
- * @access public
- * @return integer
+ * @return int
*/
- function num_rows()
+ public function num_rows()
{
- return @cubrid_num_rows($this->result_id);
+ return is_int($this->num_rows)
+ ? $this->num_rows
+ : $this->num_rows = cubrid_num_rows($this->result_id);
}
// --------------------------------------------------------------------
@@ -42,12 +66,11 @@ class CI_DB_cubrid_result extends CI_DB_result {
/**
* Number of fields in the result set
*
- * @access public
- * @return integer
+ * @return int
*/
- function num_fields()
+ public function num_fields()
{
- return @cubrid_num_fields($this->result_id);
+ return cubrid_num_fields($this->result_id);
}
// --------------------------------------------------------------------
@@ -57,10 +80,9 @@ class CI_DB_cubrid_result extends CI_DB_result {
*
* Generates an array of column names
*
- * @access public
* @return array
*/
- function list_fields()
+ public function list_fields()
{
return cubrid_column_names($this->result_id);
}
@@ -72,59 +94,19 @@ class CI_DB_cubrid_result extends CI_DB_result {
*
* Generates an array of objects containing field meta-data
*
- * @access public
* @return array
*/
- function field_data()
+ public function field_data()
{
$retval = array();
- $tablePrimaryKeys = array();
-
- while ($field = cubrid_fetch_field($this->result_id))
+ for ($i = 0, $c = $this->num_fields(); $i < $c; $i++)
{
- $F = new stdClass();
- $F->name = $field->name;
- $F->type = $field->type;
- $F->default = $field->def;
- $F->max_length = $field->max_length;
-
- // At this moment primary_key property is not returned when
- // cubrid_fetch_field is called. The following code will
- // provide a patch for it. primary_key property will be added
- // in the next release.
-
- // TODO: later version of CUBRID will provide primary_key
- // property.
- // When PK is defined in CUBRID, an index is automatically
- // created in the db_index system table in the form of
- // pk_tblname_fieldname. So the following will count how many
- // columns are there which satisfy this format.
- // The query will search for exact single columns, thus
- // compound PK is not supported.
- $res = cubrid_query($this->conn_id,
- "SELECT COUNT(*) FROM db_index WHERE class_name = '" . $field->table .
- "' AND is_primary_key = 'YES' AND index_name = 'pk_" .
- $field->table . "_" . $field->name . "'"
- );
-
- if ($res)
- {
- $row = cubrid_fetch_array($res, CUBRID_NUM);
- $F->primary_key = ($row[0] > 0 ? 1 : null);
- }
- else
- {
- $F->primary_key = null;
- }
-
- if (is_resource($res))
- {
- cubrid_close_request($res);
- $this->result_id = FALSE;
- }
-
- $retval[] = $F;
+ $retval[$i] = new stdClass();
+ $retval[$i]->name = cubrid_field_name($this->result_id, $i);
+ $retval[$i]->type = cubrid_field_type($this->result_id, $i);
+ $retval[$i]->max_length = cubrid_field_len($this->result_id, $i);
+ $retval[$i]->primary_key = (int) (strpos(cubrid_field_flags($this->result_id, $i), 'primary_key') !== FALSE);
}
return $retval;
@@ -135,13 +117,12 @@ class CI_DB_cubrid_result extends CI_DB_result {
/**
* Free the result
*
- * @return null
+ * @return void
*/
- function free_result()
+ public function free_result()
{
- if(is_resource($this->result_id) ||
- get_resource_type($this->result_id) == "Unknown" &&
- preg_match('/Resource id #/', strval($this->result_id)))
+ if (is_resource($this->result_id) OR
+ (get_resource_type($this->result_id) === 'Unknown' && preg_match('/Resource id #/', strval($this->result_id))))
{
cubrid_close_request($this->result_id);
$this->result_id = FALSE;
@@ -155,12 +136,12 @@ class CI_DB_cubrid_result extends CI_DB_result {
*
* Moves the internal pointer to the desired offset. We call
* this internally before fetching results to make sure the
- * result set starts at zero
+ * result set starts at zero.
*
- * @access private
- * @return array
+ * @param int $n
+ * @return bool
*/
- function _data_seek($n = 0)
+ public function data_seek($n = 0)
{
return cubrid_data_seek($this->result_id, $n);
}
@@ -172,10 +153,9 @@ class CI_DB_cubrid_result extends CI_DB_result {
*
* Returns the result set as an array
*
- * @access private
* @return array
*/
- function _fetch_assoc()
+ protected function _fetch_assoc()
{
return cubrid_fetch_assoc($this->result_id);
}
@@ -187,16 +167,12 @@ class CI_DB_cubrid_result extends CI_DB_result {
*
* Returns the result set as an object
*
- * @access private
+ * @param string $class_name
* @return object
*/
- function _fetch_object()
+ protected function _fetch_object($class_name = 'stdClass')
{
- return cubrid_fetch_object($this->result_id);
+ return cubrid_fetch_object($this->result_id, $class_name);
}
}
-
-
-/* End of file cubrid_result.php */
-/* Location: ./system/database/drivers/cubrid/cubrid_result.php */ \ No newline at end of file
diff --git a/system/database/drivers/cubrid/cubrid_utility.php b/system/database/drivers/cubrid/cubrid_utility.php
index 3336a4914..ca81568c3 100644
--- a/system/database/drivers/cubrid/cubrid_utility.php
+++ b/system/database/drivers/cubrid/cubrid_utility.php
@@ -1,108 +1,80 @@
-<?php if ( ! defined('BASEPATH')) exit('No direct script access allowed');
+<?php
/**
* CodeIgniter
*
- * An open source application development framework for PHP 5.1.6 or newer
+ * An open source application development framework for PHP
*
- * @package CodeIgniter
- * @author Esen Sagynov
- * @copyright Copyright (c) 2008 - 2014, EllisLab, Inc.
- * @license http://codeigniter.com/user_guide/license.html
- * @link http://codeigniter.com
- * @since Version 1.0
+ * This content is released under the MIT License (MIT)
+ *
+ * Copyright (c) 2019 - 2022, CodeIgniter Foundation
+ *
+ * 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.
+ *
+ * @package CodeIgniter
+ * @author EllisLab Dev Team
+ * @copyright Copyright (c) 2008 - 2014, EllisLab, Inc. (https://ellislab.com/)
+ * @copyright Copyright (c) 2014 - 2019, British Columbia Institute of Technology (https://bcit.ca/)
+ * @copyright Copyright (c) 2019 - 2022, CodeIgniter Foundation (https://codeigniter.com/)
+ * @license https://opensource.org/licenses/MIT MIT License
+ * @link https://codeigniter.com
+ * @since Version 2.1.0
* @filesource
*/
-
-// ------------------------------------------------------------------------
+defined('BASEPATH') OR exit('No direct script access allowed');
/**
* CUBRID Utility Class
*
* @category Database
* @author Esen Sagynov
- * @link http://codeigniter.com/user_guide/database/
+ * @link https://codeigniter.com/userguide3/database/
*/
class CI_DB_cubrid_utility extends CI_DB_utility {
/**
* List databases
*
- * @access private
* @return array
*/
- function _list_databases()
+ public function list_databases()
{
- // CUBRID does not allow to see the list of all databases on the
- // server. It is the way its architecture is designed. Every
- // database is independent and isolated.
- // For this reason we can return only the name of the currect
- // connected database.
- if ($this->conn_id)
+ if (isset($this->db->data_cache['db_names']))
{
- return "SELECT '" . $this->database . "'";
+ return $this->db->data_cache['db_names'];
}
- else
- {
- return FALSE;
- }
- }
- // --------------------------------------------------------------------
-
- /**
- * Optimize table query
- *
- * Generates a platform-specific query so that a table can be optimized
- *
- * @access private
- * @param string the table name
- * @return object
- * @link http://www.cubrid.org/manual/840/en/Optimize%20Database
- */
- function _optimize_table($table)
- {
- // No SQL based support in CUBRID as of version 8.4.0. Database or
- // table optimization can be performed using CUBRID Manager
- // database administration tool. See the link above for more info.
- return FALSE;
+ return $this->db->data_cache['db_names'] = cubrid_list_dbs($this->db->conn_id);
}
// --------------------------------------------------------------------
/**
- * Repair table query
- *
- * Generates a platform-specific query so that a table can be repaired
- *
- * @access private
- * @param string the table name
- * @return object
- * @link http://www.cubrid.org/manual/840/en/Checking%20Database%20Consistency
- */
- function _repair_table($table)
- {
- // Not supported in CUBRID as of version 8.4.0. Database or
- // table consistency can be checked using CUBRID Manager
- // database administration tool. See the link above for more info.
- return FALSE;
- }
-
- // --------------------------------------------------------------------
- /**
* CUBRID Export
*
- * @access private
* @param array Preferences
* @return mixed
*/
- function _backup($params = array())
+ protected function _backup($params = array())
{
// No SQL based support in CUBRID as of version 8.4.0. Database or
// table backup can be performed using CUBRID Manager
// database administration tool.
- return $this->db->display_error('db_unsuported_feature');
+ return $this->db->display_error('db_unsupported_feature');
}
}
-
-/* End of file cubrid_utility.php */
-/* Location: ./system/database/drivers/cubrid/cubrid_utility.php */ \ No newline at end of file
diff --git a/system/database/drivers/cubrid/index.html b/system/database/drivers/cubrid/index.html
index c942a79ce..bcb7cae34 100644
--- a/system/database/drivers/cubrid/index.html
+++ b/system/database/drivers/cubrid/index.html
@@ -1,4 +1,5 @@
-<html>
+<!DOCTYPE html>
+<html lang="en">
<head>
<title>403 Forbidden</title>
</head>
@@ -7,4 +8,4 @@
<p>Directory access is forbidden.</p>
</body>
-</html> \ No newline at end of file
+</html>
diff --git a/system/database/drivers/ibase/ibase_driver.php b/system/database/drivers/ibase/ibase_driver.php
new file mode 100644
index 000000000..433139fa4
--- /dev/null
+++ b/system/database/drivers/ibase/ibase_driver.php
@@ -0,0 +1,414 @@
+<?php
+/**
+ * CodeIgniter
+ *
+ * An open source application development framework for PHP
+ *
+ * This content is released under the MIT License (MIT)
+ *
+ * Copyright (c) 2019 - 2022, CodeIgniter Foundation
+ *
+ * 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.
+ *
+ * @package CodeIgniter
+ * @author EllisLab Dev Team
+ * @copyright Copyright (c) 2008 - 2014, EllisLab, Inc. (https://ellislab.com/)
+ * @copyright Copyright (c) 2014 - 2019, British Columbia Institute of Technology (https://bcit.ca/)
+ * @copyright Copyright (c) 2019 - 2022, CodeIgniter Foundation (https://codeigniter.com/)
+ * @license https://opensource.org/licenses/MIT MIT License
+ * @link https://codeigniter.com
+ * @since Version 3.0.0
+ * @filesource
+ */
+defined('BASEPATH') OR exit('No direct script access allowed');
+
+/**
+ * Firebird/Interbase Database Adapter Class
+ *
+ * Note: _DB is an extender class that the app controller
+ * creates dynamically based on whether the query builder
+ * class is being used or not.
+ *
+ * @package CodeIgniter
+ * @subpackage Drivers
+ * @category Database
+ * @author EllisLab Dev Team
+ * @link https://codeigniter.com/userguide3/database/
+ */
+class CI_DB_ibase_driver extends CI_DB {
+
+ /**
+ * Database driver
+ *
+ * @var string
+ */
+ public $dbdriver = 'ibase';
+
+ // --------------------------------------------------------------------
+
+ /**
+ * ORDER BY random keyword
+ *
+ * @var array
+ */
+ protected $_random_keyword = array('RAND()', 'RAND()');
+
+ /**
+ * IBase Transaction status flag
+ *
+ * @var resource
+ */
+ protected $_ibase_trans;
+
+ // --------------------------------------------------------------------
+
+ /**
+ * Non-persistent database connection
+ *
+ * @param bool $persistent
+ * @return resource
+ */
+ public function db_connect($persistent = FALSE)
+ {
+ return ($persistent === TRUE)
+ ? ibase_pconnect($this->hostname.':'.$this->database, $this->username, $this->password, $this->char_set)
+ : ibase_connect($this->hostname.':'.$this->database, $this->username, $this->password, $this->char_set);
+ }
+
+ // --------------------------------------------------------------------
+
+ /**
+ * Database version number
+ *
+ * @return string
+ */
+ public function version()
+ {
+ if (isset($this->data_cache['version']))
+ {
+ return $this->data_cache['version'];
+ }
+
+ if (($service = ibase_service_attach($this->hostname, $this->username, $this->password)))
+ {
+ $this->data_cache['version'] = ibase_server_info($service, IBASE_SVC_SERVER_VERSION);
+
+ // Don't keep the service open
+ ibase_service_detach($service);
+ return $this->data_cache['version'];
+ }
+
+ return FALSE;
+ }
+
+ // --------------------------------------------------------------------
+
+ /**
+ * Execute the query
+ *
+ * @param string $sql an SQL query
+ * @return resource
+ */
+ protected function _execute($sql)
+ {
+ return ibase_query(isset($this->_ibase_trans) ? $this->_ibase_trans : $this->conn_id, $sql);
+ }
+
+ // --------------------------------------------------------------------
+
+ /**
+ * Begin Transaction
+ *
+ * @return bool
+ */
+ protected function _trans_begin()
+ {
+ if (($trans_handle = ibase_trans($this->conn_id)) === FALSE)
+ {
+ return FALSE;
+ }
+
+ $this->_ibase_trans = $trans_handle;
+ return TRUE;
+ }
+
+ // --------------------------------------------------------------------
+
+ /**
+ * Commit Transaction
+ *
+ * @return bool
+ */
+ protected function _trans_commit()
+ {
+ if (ibase_commit($this->_ibase_trans))
+ {
+ $this->_ibase_trans = NULL;
+ return TRUE;
+ }
+
+ return FALSE;
+ }
+
+ // --------------------------------------------------------------------
+
+ /**
+ * Rollback Transaction
+ *
+ * @return bool
+ */
+ protected function _trans_rollback()
+ {
+ if (ibase_rollback($this->_ibase_trans))
+ {
+ $this->_ibase_trans = NULL;
+ return TRUE;
+ }
+
+ return FALSE;
+ }
+
+ // --------------------------------------------------------------------
+
+ /**
+ * Affected Rows
+ *
+ * @return int
+ */
+ public function affected_rows()
+ {
+ return ibase_affected_rows($this->conn_id);
+ }
+
+ // --------------------------------------------------------------------
+
+ /**
+ * Insert ID
+ *
+ * @param string $generator_name
+ * @param int $inc_by
+ * @return int
+ */
+ public function insert_id($generator_name, $inc_by = 0)
+ {
+ //If a generator hasn't been used before it will return 0
+ return ibase_gen_id('"'.$generator_name.'"', $inc_by);
+ }
+
+ // --------------------------------------------------------------------
+
+ /**
+ * List table query
+ *
+ * Generates a platform-specific query string so that the table names can be fetched
+ *
+ * @param bool $prefix_limit
+ * @return string
+ */
+ protected function _list_tables($prefix_limit = FALSE)
+ {
+ $sql = 'SELECT TRIM("RDB$RELATION_NAME") AS TABLE_NAME FROM "RDB$RELATIONS" WHERE "RDB$RELATION_NAME" NOT LIKE \'RDB$%\' AND "RDB$RELATION_NAME" NOT LIKE \'MON$%\'';
+
+ if ($prefix_limit !== FALSE && $this->dbprefix !== '')
+ {
+ return $sql.' AND TRIM("RDB$RELATION_NAME") AS TABLE_NAME LIKE \''.$this->escape_like_str($this->dbprefix)."%' "
+ .sprintf($this->_like_escape_str, $this->_like_escape_chr);
+ }
+
+ return $sql;
+ }
+
+ // --------------------------------------------------------------------
+
+ /**
+ * Show column query
+ *
+ * Generates a platform-specific query string so that the column names can be fetched
+ *
+ * @param string $table
+ * @return string
+ */
+ protected function _list_columns($table = '')
+ {
+ return 'SELECT TRIM("RDB$FIELD_NAME") AS COLUMN_NAME FROM "RDB$RELATION_FIELDS" WHERE "RDB$RELATION_NAME" = '.$this->escape($table);
+ }
+
+ // --------------------------------------------------------------------
+
+ /**
+ * Returns an object with field data
+ *
+ * @param string $table
+ * @return array
+ */
+ public function field_data($table)
+ {
+ $sql = 'SELECT "rfields"."RDB$FIELD_NAME" AS "name",
+ CASE "fields"."RDB$FIELD_TYPE"
+ WHEN 7 THEN \'SMALLINT\'
+ WHEN 8 THEN \'INTEGER\'
+ WHEN 9 THEN \'QUAD\'
+ WHEN 10 THEN \'FLOAT\'
+ WHEN 11 THEN \'DFLOAT\'
+ WHEN 12 THEN \'DATE\'
+ WHEN 13 THEN \'TIME\'
+ WHEN 14 THEN \'CHAR\'
+ WHEN 16 THEN \'INT64\'
+ WHEN 27 THEN \'DOUBLE\'
+ WHEN 35 THEN \'TIMESTAMP\'
+ WHEN 37 THEN \'VARCHAR\'
+ WHEN 40 THEN \'CSTRING\'
+ WHEN 261 THEN \'BLOB\'
+ ELSE NULL
+ END AS "type",
+ "fields"."RDB$FIELD_LENGTH" AS "max_length",
+ "rfields"."RDB$DEFAULT_VALUE" AS "default"
+ FROM "RDB$RELATION_FIELDS" "rfields"
+ JOIN "RDB$FIELDS" "fields" ON "rfields"."RDB$FIELD_SOURCE" = "fields"."RDB$FIELD_NAME"
+ WHERE "rfields"."RDB$RELATION_NAME" = '.$this->escape($table).'
+ ORDER BY "rfields"."RDB$FIELD_POSITION"';
+
+ return (($query = $this->query($sql)) !== FALSE)
+ ? $query->result_object()
+ : FALSE;
+ }
+
+ // --------------------------------------------------------------------
+
+ /**
+ * Error
+ *
+ * Returns an array containing code and message of the last
+ * database error that has occurred.
+ *
+ * @return array
+ */
+ public function error()
+ {
+ return array('code' => ibase_errcode(), 'message' => ibase_errmsg());
+ }
+
+ // --------------------------------------------------------------------
+
+ /**
+ * Update statement
+ *
+ * Generates a platform-specific update string from the supplied data
+ *
+ * @param string $table
+ * @param array $values
+ * @return string
+ */
+ protected function _update($table, $values)
+ {
+ $this->qb_limit = FALSE;
+ return parent::_update($table, $values);
+ }
+
+ // --------------------------------------------------------------------
+
+ /**
+ * Truncate statement
+ *
+ * Generates a platform-specific truncate string from the supplied data
+ *
+ * If the database does not support the TRUNCATE statement,
+ * then this method maps to 'DELETE FROM table'
+ *
+ * @param string $table
+ * @return string
+ */
+ protected function _truncate($table)
+ {
+ return 'DELETE FROM '.$table;
+ }
+
+ // --------------------------------------------------------------------
+
+ /**
+ * Delete statement
+ *
+ * Generates a platform-specific delete string from the supplied data
+ *
+ * @param string $table
+ * @return string
+ */
+ protected function _delete($table)
+ {
+ $this->qb_limit = FALSE;
+ return parent::_delete($table);
+ }
+
+ // --------------------------------------------------------------------
+
+ /**
+ * LIMIT
+ *
+ * Generates a platform-specific LIMIT clause
+ *
+ * @param string $sql SQL Query
+ * @return string
+ */
+ protected function _limit($sql)
+ {
+ // Limit clause depends on if Interbase or Firebird
+ if (stripos($this->version(), 'firebird') !== FALSE)
+ {
+ $select = 'FIRST '.$this->qb_limit
+ .($this->qb_offset ? ' SKIP '.$this->qb_offset : '');
+ }
+ else
+ {
+ $select = 'ROWS '
+ .($this->qb_offset ? $this->qb_offset.' TO '.($this->qb_limit + $this->qb_offset) : $this->qb_limit);
+ }
+
+ return preg_replace('`SELECT`i', 'SELECT '.$select, $sql, 1);
+ }
+
+ // --------------------------------------------------------------------
+
+ /**
+ * Insert batch statement
+ *
+ * Generates a platform-specific insert string from the supplied data.
+ *
+ * @param string $table Table name
+ * @param array $keys INSERT keys
+ * @param array $values INSERT values
+ * @return string|bool
+ */
+ protected function _insert_batch($table, $keys, $values)
+ {
+ return ($this->db_debug) ? $this->display_error('db_unsupported_feature') : FALSE;
+ }
+
+ // --------------------------------------------------------------------
+
+ /**
+ * Close DB Connection
+ *
+ * @return void
+ */
+ protected function _close()
+ {
+ ibase_close($this->conn_id);
+ }
+
+}
diff --git a/system/database/drivers/ibase/ibase_forge.php b/system/database/drivers/ibase/ibase_forge.php
new file mode 100644
index 000000000..2c385f154
--- /dev/null
+++ b/system/database/drivers/ibase/ibase_forge.php
@@ -0,0 +1,252 @@
+<?php
+/**
+ * CodeIgniter
+ *
+ * An open source application development framework for PHP
+ *
+ * This content is released under the MIT License (MIT)
+ *
+ * Copyright (c) 2019 - 2022, CodeIgniter Foundation
+ *
+ * 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.
+ *
+ * @package CodeIgniter
+ * @author EllisLab Dev Team
+ * @copyright Copyright (c) 2008 - 2014, EllisLab, Inc. (https://ellislab.com/)
+ * @copyright Copyright (c) 2014 - 2019, British Columbia Institute of Technology (https://bcit.ca/)
+ * @copyright Copyright (c) 2019 - 2022, CodeIgniter Foundation (https://codeigniter.com/)
+ * @license https://opensource.org/licenses/MIT MIT License
+ * @link https://codeigniter.com
+ * @since Version 3.0.0
+ * @filesource
+ */
+defined('BASEPATH') OR exit('No direct script access allowed');
+
+/**
+ * Interbase/Firebird Forge Class
+ *
+ * @category Database
+ * @author EllisLab Dev Team
+ * @link https://codeigniter.com/userguide3/database/
+ */
+class CI_DB_ibase_forge extends CI_DB_forge {
+
+ /**
+ * CREATE TABLE IF statement
+ *
+ * @var string
+ */
+ protected $_create_table_if = FALSE;
+
+ /**
+ * RENAME TABLE statement
+ *
+ * @var string
+ */
+ protected $_rename_table = FALSE;
+
+ /**
+ * DROP TABLE IF statement
+ *
+ * @var string
+ */
+ protected $_drop_table_if = FALSE;
+
+ /**
+ * UNSIGNED support
+ *
+ * @var array
+ */
+ protected $_unsigned = array(
+ 'SMALLINT' => 'INTEGER',
+ 'INTEGER' => 'INT64',
+ 'FLOAT' => 'DOUBLE PRECISION'
+ );
+
+ /**
+ * NULL value representation in CREATE/ALTER TABLE statements
+ *
+ * @var string
+ */
+ protected $_null = 'NULL';
+
+ // --------------------------------------------------------------------
+
+ /**
+ * Create database
+ *
+ * @param string $db_name
+ * @return bool
+ */
+ public function create_database($db_name)
+ {
+ // Firebird databases are flat files, so a path is required
+
+ // Hostname is needed for remote access
+ empty($this->db->hostname) OR $db_name = $this->hostname.':'.$db_name;
+
+ return parent::create_database('"'.$db_name.'"');
+ }
+
+ // --------------------------------------------------------------------
+
+ /**
+ * Drop database
+ *
+ * @param string $db_name (ignored)
+ * @return bool
+ */
+ public function drop_database($db_name)
+ {
+ if ( ! ibase_drop_db($this->conn_id))
+ {
+ return ($this->db->db_debug) ? $this->db->display_error('db_unable_to_drop') : FALSE;
+ }
+ elseif ( ! empty($this->db->data_cache['db_names']))
+ {
+ $key = array_search(strtolower($this->db->database), array_map('strtolower', $this->db->data_cache['db_names']), TRUE);
+ if ($key !== FALSE)
+ {
+ unset($this->db->data_cache['db_names'][$key]);
+ }
+ }
+
+ return TRUE;
+ }
+
+ // --------------------------------------------------------------------
+
+ /**
+ * ALTER TABLE
+ *
+ * @param string $alter_type ALTER type
+ * @param string $table Table name
+ * @param mixed $field Column definition
+ * @return string|string[]
+ */
+ protected function _alter_table($alter_type, $table, $field)
+ {
+ if (in_array($alter_type, array('DROP', 'ADD'), TRUE))
+ {
+ return parent::_alter_table($alter_type, $table, $field);
+ }
+
+ $sql = 'ALTER TABLE '.$this->db->escape_identifiers($table);
+ $sqls = array();
+ for ($i = 0, $c = count($field); $i < $c; $i++)
+ {
+ if ($field[$i]['_literal'] !== FALSE)
+ {
+ return FALSE;
+ }
+
+ if (isset($field[$i]['type']))
+ {
+ $sqls[] = $sql.' ALTER COLUMN '.$this->db->escape_identififers($field[$i]['name'])
+ .' TYPE '.$field[$i]['type'].$field[$i]['length'];
+ }
+
+ if ( ! empty($field[$i]['default']))
+ {
+ $sqls[] = $sql.' ALTER COLUMN '.$this->db->escape_identifiers($field[$i]['name'])
+ .' SET DEFAULT '.$field[$i]['default'];
+ }
+
+ if (isset($field[$i]['null']))
+ {
+ $sqls[] = 'UPDATE "RDB$RELATION_FIELDS" SET "RDB$NULL_FLAG" = '
+ .($field[$i]['null'] === TRUE ? 'NULL' : '1')
+ .' WHERE "RDB$FIELD_NAME" = '.$this->db->escape($field[$i]['name'])
+ .' AND "RDB$RELATION_NAME" = '.$this->db->escape($table);
+ }
+
+ if ( ! empty($field[$i]['new_name']))
+ {
+ $sqls[] = $sql.' ALTER COLUMN '.$this->db->escape_identifiers($field[$i]['name'])
+ .' TO '.$this->db->escape_identifiers($field[$i]['new_name']);
+ }
+ }
+
+ return $sqls;
+ }
+
+ // --------------------------------------------------------------------
+
+ /**
+ * Process column
+ *
+ * @param array $field
+ * @return string
+ */
+ protected function _process_column($field)
+ {
+ return $this->db->escape_identifiers($field['name'])
+ .' '.$field['type'].$field['length']
+ .$field['null']
+ .$field['unique']
+ .$field['default'];
+ }
+
+ // --------------------------------------------------------------------
+
+ /**
+ * Field attribute TYPE
+ *
+ * Performs a data type mapping between different databases.
+ *
+ * @param array &$attributes
+ * @return void
+ */
+ protected function _attr_type(&$attributes)
+ {
+ switch (strtoupper($attributes['TYPE']))
+ {
+ case 'TINYINT':
+ $attributes['TYPE'] = 'SMALLINT';
+ $attributes['UNSIGNED'] = FALSE;
+ return;
+ case 'MEDIUMINT':
+ $attributes['TYPE'] = 'INTEGER';
+ $attributes['UNSIGNED'] = FALSE;
+ return;
+ case 'INT':
+ $attributes['TYPE'] = 'INTEGER';
+ return;
+ case 'BIGINT':
+ $attributes['TYPE'] = 'INT64';
+ return;
+ default: return;
+ }
+ }
+
+ // --------------------------------------------------------------------
+
+ /**
+ * Field attribute AUTO_INCREMENT
+ *
+ * @param array &$attributes
+ * @param array &$field
+ * @return void
+ */
+ protected function _attr_auto_increment(&$attributes, &$field)
+ {
+ // Not supported
+ }
+
+}
diff --git a/system/database/drivers/ibase/ibase_result.php b/system/database/drivers/ibase/ibase_result.php
new file mode 100644
index 000000000..900212e6c
--- /dev/null
+++ b/system/database/drivers/ibase/ibase_result.php
@@ -0,0 +1,162 @@
+<?php
+/**
+ * CodeIgniter
+ *
+ * An open source application development framework for PHP
+ *
+ * This content is released under the MIT License (MIT)
+ *
+ * Copyright (c) 2019 - 2022, CodeIgniter Foundation
+ *
+ * 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.
+ *
+ * @package CodeIgniter
+ * @author EllisLab Dev Team
+ * @copyright Copyright (c) 2008 - 2014, EllisLab, Inc. (https://ellislab.com/)
+ * @copyright Copyright (c) 2014 - 2019, British Columbia Institute of Technology (https://bcit.ca/)
+ * @copyright Copyright (c) 2019 - 2022, CodeIgniter Foundation (https://codeigniter.com/)
+ * @license https://opensource.org/licenses/MIT MIT License
+ * @link https://codeigniter.com
+ * @since Version 3.0.0
+ * @filesource
+ */
+defined('BASEPATH') OR exit('No direct script access allowed');
+
+/**
+ * Interbase/Firebird Result Class
+ *
+ * This class extends the parent result class: CI_DB_result
+ *
+ * @category Database
+ * @author EllisLab Dev Team
+ * @link https://codeigniter.com/userguide3/database/
+ */
+class CI_DB_ibase_result extends CI_DB_result {
+
+ /**
+ * Number of fields in the result set
+ *
+ * @return int
+ */
+ public function num_fields()
+ {
+ return ibase_num_fields($this->result_id);
+ }
+
+ // --------------------------------------------------------------------
+
+ /**
+ * Fetch Field Names
+ *
+ * Generates an array of column names
+ *
+ * @return array
+ */
+ public function list_fields()
+ {
+ $field_names = array();
+ for ($i = 0, $num_fields = $this->num_fields(); $i < $num_fields; $i++)
+ {
+ $info = ibase_field_info($this->result_id, $i);
+ $field_names[] = $info['name'];
+ }
+
+ return $field_names;
+ }
+
+ // --------------------------------------------------------------------
+
+ /**
+ * Field data
+ *
+ * Generates an array of objects containing field meta-data
+ *
+ * @return array
+ */
+ public function field_data()
+ {
+ $retval = array();
+ for ($i = 0, $c = $this->num_fields(); $i < $c; $i++)
+ {
+ $info = ibase_field_info($this->result_id, $i);
+
+ $retval[$i] = new stdClass();
+ $retval[$i]->name = $info['name'];
+ $retval[$i]->type = $info['type'];
+ $retval[$i]->max_length = $info['length'];
+ }
+
+ return $retval;
+ }
+
+ // --------------------------------------------------------------------
+
+ /**
+ * Free the result
+ *
+ * @return void
+ */
+ public function free_result()
+ {
+ ibase_free_result($this->result_id);
+ }
+
+ // --------------------------------------------------------------------
+
+ /**
+ * Result - associative array
+ *
+ * Returns the result set as an array
+ *
+ * @return array
+ */
+ protected function _fetch_assoc()
+ {
+ return ibase_fetch_assoc($this->result_id, IBASE_FETCH_BLOBS);
+ }
+
+ // --------------------------------------------------------------------
+
+ /**
+ * Result - object
+ *
+ * Returns the result set as an object
+ *
+ * @param string $class_name
+ * @return object
+ */
+ protected function _fetch_object($class_name = 'stdClass')
+ {
+ $row = ibase_fetch_object($this->result_id, IBASE_FETCH_BLOBS);
+
+ if ($class_name === 'stdClass' OR ! $row)
+ {
+ return $row;
+ }
+
+ $class_name = new $class_name();
+ foreach ($row as $key => $value)
+ {
+ $class_name->$key = $value;
+ }
+
+ return $class_name;
+ }
+
+}
diff --git a/system/database/drivers/ibase/ibase_utility.php b/system/database/drivers/ibase/ibase_utility.php
new file mode 100644
index 000000000..bc87508ca
--- /dev/null
+++ b/system/database/drivers/ibase/ibase_utility.php
@@ -0,0 +1,70 @@
+<?php
+/**
+ * CodeIgniter
+ *
+ * An open source application development framework for PHP
+ *
+ * This content is released under the MIT License (MIT)
+ *
+ * Copyright (c) 2019 - 2022, CodeIgniter Foundation
+ *
+ * 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.
+ *
+ * @package CodeIgniter
+ * @author EllisLab Dev Team
+ * @copyright Copyright (c) 2008 - 2014, EllisLab, Inc. (https://ellislab.com/)
+ * @copyright Copyright (c) 2014 - 2019, British Columbia Institute of Technology (https://bcit.ca/)
+ * @copyright Copyright (c) 2019 - 2022, CodeIgniter Foundation (https://codeigniter.com/)
+ * @license https://opensource.org/licenses/MIT MIT License
+ * @link https://codeigniter.com
+ * @since Version 3.0.0
+ * @filesource
+ */
+defined('BASEPATH') OR exit('No direct script access allowed');
+
+/**
+ * Interbase/Firebird Utility Class
+ *
+ * @category Database
+ * @author EllisLab Dev Team
+ * @link https://codeigniter.com/userguide3/database/
+ */
+class CI_DB_ibase_utility extends CI_DB_utility {
+
+ /**
+ * Export
+ *
+ * @param string $filename
+ * @return mixed
+ */
+ protected function _backup($filename)
+ {
+ if ($service = ibase_service_attach($this->db->hostname, $this->db->username, $this->db->password))
+ {
+ $res = ibase_backup($service, $this->db->database, $filename.'.fbk');
+
+ // Close the service connection
+ ibase_service_detach($service);
+ return $res;
+ }
+
+ return FALSE;
+ }
+
+}
diff --git a/system/database/drivers/ibase/index.html b/system/database/drivers/ibase/index.html
new file mode 100644
index 000000000..bcb7cae34
--- /dev/null
+++ b/system/database/drivers/ibase/index.html
@@ -0,0 +1,11 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+ <title>403 Forbidden</title>
+</head>
+<body>
+
+<p>Directory access is forbidden.</p>
+
+</body>
+</html>
diff --git a/system/database/drivers/index.html b/system/database/drivers/index.html
index c942a79ce..bcb7cae34 100644
--- a/system/database/drivers/index.html
+++ b/system/database/drivers/index.html
@@ -1,4 +1,5 @@
-<html>
+<!DOCTYPE html>
+<html lang="en">
<head>
<title>403 Forbidden</title>
</head>
@@ -7,4 +8,4 @@
<p>Directory access is forbidden.</p>
</body>
-</html> \ No newline at end of file
+</html>
diff --git a/system/database/drivers/mssql/index.html b/system/database/drivers/mssql/index.html
index c942a79ce..bcb7cae34 100644
--- a/system/database/drivers/mssql/index.html
+++ b/system/database/drivers/mssql/index.html
@@ -1,4 +1,5 @@
-<html>
+<!DOCTYPE html>
+<html lang="en">
<head>
<title>403 Forbidden</title>
</head>
@@ -7,4 +8,4 @@
<p>Directory access is forbidden.</p>
</body>
-</html> \ No newline at end of file
+</html>
diff --git a/system/database/drivers/mssql/mssql_driver.php b/system/database/drivers/mssql/mssql_driver.php
index 1823edce8..3fc323a02 100644
--- a/system/database/drivers/mssql/mssql_driver.php
+++ b/system/database/drivers/mssql/mssql_driver.php
@@ -1,374 +1,263 @@
-<?php if ( ! defined('BASEPATH')) exit('No direct script access allowed');
+<?php
/**
* CodeIgniter
*
- * An open source application development framework for PHP 5.1.6 or newer
+ * An open source application development framework for PHP
*
- * @package CodeIgniter
- * @author ExpressionEngine Dev Team
- * @copyright Copyright (c) 2008 - 2014, EllisLab, Inc.
- * @license http://codeigniter.com/user_guide/license.html
- * @link http://codeigniter.com
- * @since Version 1.0
+ * This content is released under the MIT License (MIT)
+ *
+ * Copyright (c) 2019 - 2022, CodeIgniter Foundation
+ *
+ * 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.
+ *
+ * @package CodeIgniter
+ * @author EllisLab Dev Team
+ * @copyright Copyright (c) 2008 - 2014, EllisLab, Inc. (https://ellislab.com/)
+ * @copyright Copyright (c) 2014 - 2019, British Columbia Institute of Technology (https://bcit.ca/)
+ * @copyright Copyright (c) 2019 - 2022, CodeIgniter Foundation (https://codeigniter.com/)
+ * @license https://opensource.org/licenses/MIT MIT License
+ * @link https://codeigniter.com
+ * @since Version 1.3.0
* @filesource
*/
-
-// ------------------------------------------------------------------------
+defined('BASEPATH') OR exit('No direct script access allowed');
/**
* MS SQL Database Adapter Class
*
* Note: _DB is an extender class that the app controller
- * creates dynamically based on whether the active record
+ * creates dynamically based on whether the query builder
* class is being used or not.
*
* @package CodeIgniter
* @subpackage Drivers
* @category Database
- * @author ExpressionEngine Dev Team
- * @link http://codeigniter.com/user_guide/database/
+ * @author EllisLab Dev Team
+ * @link https://codeigniter.com/userguide3/database/
*/
class CI_DB_mssql_driver extends CI_DB {
- var $dbdriver = 'mssql';
-
- // The character used for escaping
- var $_escape_char = '';
-
- // clause and character used for LIKE escape sequences
- var $_like_escape_str = " ESCAPE '%s' ";
- var $_like_escape_chr = '!';
-
- /**
- * The syntax to count rows is slightly different across different
- * database engines, so this string appears in each driver and is
- * used for the count_all() and count_all_results() functions.
- */
- var $_count_string = "SELECT COUNT(*) AS ";
- var $_random_keyword = ' ASC'; // not currently supported
-
/**
- * Non-persistent database connection
+ * Database driver
*
- * @access private called by the base class
- * @return resource
+ * @var string
*/
- function db_connect()
- {
- if ($this->port != '')
- {
- $this->hostname .= ','.$this->port;
- }
-
- return @mssql_connect($this->hostname, $this->username, $this->password);
- }
+ public $dbdriver = 'mssql';
// --------------------------------------------------------------------
/**
- * Persistent database connection
+ * ORDER BY random keyword
*
- * @access private called by the base class
- * @return resource
+ * @var array
*/
- function db_pconnect()
- {
- if ($this->port != '')
- {
- $this->hostname .= ','.$this->port;
- }
-
- return @mssql_pconnect($this->hostname, $this->username, $this->password);
- }
-
- // --------------------------------------------------------------------
+ protected $_random_keyword = array('NEWID()', 'RAND(%d)');
/**
- * Reconnect
+ * Quoted identifier flag
*
- * Keep / reestablish the db connection if no queries have been
- * sent for a length of time exceeding the server's idle timeout
+ * Whether to use SQL-92 standard quoted identifier
+ * (double quotes) or brackets for identifier escaping.
*
- * @access public
- * @return void
+ * @var bool
*/
- function reconnect()
- {
- // not implemented in MSSQL
- }
+ protected $_quoted_identifier = TRUE;
// --------------------------------------------------------------------
/**
- * Select the database
+ * Class constructor
*
- * @access private called by the base class
- * @return resource
- */
- function db_select()
- {
- // Note: The brackets are required in the event that the DB name
- // contains reserved characters
- return @mssql_select_db('['.$this->database.']', $this->conn_id);
- }
-
- // --------------------------------------------------------------------
-
- /**
- * Set client character set
+ * Appends the port number to the hostname, if needed.
*
- * @access public
- * @param string
- * @param string
- * @return resource
+ * @param array $params
+ * @return void
*/
- function db_set_charset($charset, $collation)
+ public function __construct($params)
{
- // @todo - add support if needed
- return TRUE;
- }
+ parent::__construct($params);
- // --------------------------------------------------------------------
-
- /**
- * Execute the query
- *
- * @access private called by the base class
- * @param string an SQL query
- * @return resource
- */
- function _execute($sql)
- {
- $sql = $this->_prep_query($sql);
- return @mssql_query($sql, $this->conn_id);
+ if ( ! empty($this->port))
+ {
+ $this->hostname .= (DIRECTORY_SEPARATOR === '\\' ? ',' : ':').$this->port;
+ }
}
// --------------------------------------------------------------------
/**
- * Prep the query
- *
- * If needed, each database adapter can prep the query string
+ * Non-persistent database connection
*
- * @access private called by execute()
- * @param string an SQL query
- * @return string
+ * @param bool $persistent
+ * @return resource
*/
- function _prep_query($sql)
+ public function db_connect($persistent = FALSE)
{
- return $sql;
- }
+ ini_set('mssql.charset', $this->char_set);
+ $this->conn_id = ($persistent)
+ ? mssql_pconnect($this->hostname, $this->username, $this->password)
+ : mssql_connect($this->hostname, $this->username, $this->password);
- // --------------------------------------------------------------------
-
- /**
- * Begin Transaction
- *
- * @access public
- * @return bool
- */
- function trans_begin($test_mode = FALSE)
- {
- if ( ! $this->trans_enabled)
+ if ( ! $this->conn_id)
{
- return TRUE;
+ return FALSE;
}
- // When transactions are nested we only begin/commit/rollback the outermost ones
- if ($this->_trans_depth > 0)
+ // ----------------------------------------------------------------
+
+ // Select the DB... assuming a database name is specified in the config file
+ if ($this->database !== '' && ! $this->db_select())
{
- return TRUE;
+ log_message('error', 'Unable to select database: '.$this->database);
+
+ return ($this->db_debug === TRUE)
+ ? $this->display_error('db_unable_to_select', $this->database)
+ : FALSE;
}
- // Reset the transaction failure flag.
- // If the $test_mode flag is set to TRUE transactions will be rolled back
- // even if the queries produce a successful result.
- $this->_trans_failure = ($test_mode === TRUE) ? TRUE : FALSE;
+ // Determine how identifiers are escaped
+ $query = $this->query('SELECT CASE WHEN (@@OPTIONS | 256) = @@OPTIONS THEN 1 ELSE 0 END AS qi');
+ $query = $query->row_array();
+ $this->_quoted_identifier = empty($query) ? FALSE : (bool) $query['qi'];
+ $this->_escape_char = ($this->_quoted_identifier) ? '"' : array('[', ']');
- $this->simple_query('BEGIN TRAN');
- return TRUE;
+ return $this->conn_id;
}
// --------------------------------------------------------------------
/**
- * Commit Transaction
+ * Select the database
*
- * @access public
+ * @param string $database
* @return bool
*/
- function trans_commit()
+ public function db_select($database = '')
{
- if ( ! $this->trans_enabled)
+ if ($database === '')
{
- return TRUE;
+ $database = $this->database;
}
- // When transactions are nested we only begin/commit/rollback the outermost ones
- if ($this->_trans_depth > 0)
+ // Note: Escaping is required in the event that the DB name
+ // contains reserved characters.
+ if (mssql_select_db('['.$database.']', $this->conn_id))
{
+ $this->database = $database;
+ $this->data_cache = array();
return TRUE;
}
- $this->simple_query('COMMIT TRAN');
- return TRUE;
+ return FALSE;
}
// --------------------------------------------------------------------
/**
- * Rollback Transaction
+ * Execute the query
*
- * @access public
- * @return bool
+ * @param string $sql an SQL query
+ * @return mixed resource if rows are returned, bool otherwise
*/
- function trans_rollback()
+ protected function _execute($sql)
{
- if ( ! $this->trans_enabled)
- {
- return TRUE;
- }
-
- // When transactions are nested we only begin/commit/rollback the outermost ones
- if ($this->_trans_depth > 0)
- {
- return TRUE;
- }
-
- $this->simple_query('ROLLBACK TRAN');
- return TRUE;
+ return mssql_query($sql, $this->conn_id);
}
// --------------------------------------------------------------------
/**
- * Escape String
+ * Begin Transaction
*
- * @access public
- * @param string
- * @param bool whether or not the string will be used in a LIKE condition
- * @return string
+ * @return bool
*/
- function escape_str($str, $like = FALSE)
+ protected function _trans_begin()
{
- if (is_array($str))
- {
- foreach ($str as $key => $val)
- {
- $str[$key] = $this->escape_str($val, $like);
- }
-
- return $str;
- }
-
- // Escape single quotes
- $str = str_replace("'", "''", remove_invisible_characters($str));
-
- // escape LIKE condition wildcards
- if ($like === TRUE)
- {
- $str = str_replace(
- array($this->_like_escape_chr, '%', '_'),
- array($this->_like_escape_chr.$this->_like_escape_chr, $this->_like_escape_chr.'%', $this->_like_escape_chr.'_'),
- $str
- );
- }
-
- return $str;
+ return $this->simple_query('BEGIN TRAN');
}
// --------------------------------------------------------------------
/**
- * Affected Rows
+ * Commit Transaction
*
- * @access public
- * @return integer
+ * @return bool
*/
- function affected_rows()
+ protected function _trans_commit()
{
- return @mssql_rows_affected($this->conn_id);
+ return $this->simple_query('COMMIT TRAN');
}
// --------------------------------------------------------------------
/**
- * Insert ID
- *
- * Returns the last id created in the Identity column.
- *
- * @access public
- * @return integer
- */
- function insert_id()
+ * Rollback Transaction
+ *
+ * @return bool
+ */
+ protected function _trans_rollback()
{
- $ver = self::_parse_major_version($this->version());
- $sql = ($ver >= 8 ? "SELECT SCOPE_IDENTITY() AS last_id" : "SELECT @@IDENTITY AS last_id");
- $query = $this->query($sql);
- $row = $query->row();
- return $row->last_id;
+ return $this->simple_query('ROLLBACK TRAN');
}
// --------------------------------------------------------------------
/**
- * Parse major version
- *
- * Grabs the major version number from the
- * database server version string passed in.
- *
- * @access private
- * @param string $version
- * @return int16 major version number
- */
- function _parse_major_version($version)
+ * Affected Rows
+ *
+ * @return int
+ */
+ public function affected_rows()
{
- preg_match('/([0-9]+)\.([0-9]+)\.([0-9]+)/', $version, $ver_info);
- return $ver_info[1]; // return the major version b/c that's all we're interested in.
+ return mssql_rows_affected($this->conn_id);
}
// --------------------------------------------------------------------
/**
- * Version number query string
- *
- * @access public
- * @return string
- */
- function _version()
+ * Insert ID
+ *
+ * Returns the last id created in the Identity column.
+ *
+ * @return string
+ */
+ public function insert_id()
{
- return "SELECT @@VERSION AS ver";
+ $query = version_compare($this->version(), '8', '>=')
+ ? 'SELECT SCOPE_IDENTITY() AS last_id'
+ : 'SELECT @@IDENTITY AS last_id';
+
+ $query = $this->query($query);
+ $query = $query->row();
+ return $query->last_id;
}
// --------------------------------------------------------------------
/**
- * "Count All" query
- *
- * Generates a platform-specific query string that counts all records in
- * the specified database
+ * Version number query string
*
- * @access public
- * @param string
* @return string
*/
- function count_all($table = '')
+ protected function _version()
{
- if ($table == '')
- {
- return 0;
- }
-
- $query = $this->query($this->_count_string . $this->_protect_identifiers('numrows') . " FROM " . $this->_protect_identifiers($table, TRUE, NULL, FALSE));
-
- if ($query->num_rows() == 0)
- {
- return 0;
- }
-
- $row = $query->row();
- $this->_reset_select();
- return (int) $row->numrows;
+ return "SELECT SERVERPROPERTY('ProductVersion') AS ver";
}
// --------------------------------------------------------------------
@@ -378,22 +267,22 @@ class CI_DB_mssql_driver extends CI_DB {
*
* Generates a platform-specific query string so that the table names can be fetched
*
- * @access private
- * @param boolean
+ * @param bool $prefix_limit
* @return string
*/
- function _list_tables($prefix_limit = FALSE)
+ protected function _list_tables($prefix_limit = FALSE)
{
- $sql = "SELECT name FROM sysobjects WHERE type = 'U' ORDER BY name";
+ $sql = 'SELECT '.$this->escape_identifiers('name')
+ .' FROM '.$this->escape_identifiers('sysobjects')
+ .' WHERE '.$this->escape_identifiers('type')." = 'U'";
- // for future compatibility
- if ($prefix_limit !== FALSE AND $this->dbprefix != '')
+ if ($prefix_limit !== FALSE && $this->dbprefix !== '')
{
- //$sql .= " LIKE '".$this->escape_like_str($this->dbprefix)."%' ".sprintf($this->_like_escape_str, $this->_like_escape_chr);
- return FALSE; // not currently supported
+ $sql .= ' AND '.$this->escape_identifiers('name')." LIKE '".$this->escape_like_str($this->dbprefix)."%' "
+ .sprintf($this->_like_escape_str, $this->_like_escape_chr);
}
- return $sql;
+ return $sql.' ORDER BY '.$this->escape_identifiers('name');
}
// --------------------------------------------------------------------
@@ -403,246 +292,204 @@ class CI_DB_mssql_driver extends CI_DB {
*
* Generates a platform-specific query string so that the column names can be fetched
*
- * @access private
- * @param string the table name
+ * @param string $table
* @return string
*/
- function _list_columns($table = '')
+ protected function _list_columns($table = '')
{
- return "SELECT * FROM INFORMATION_SCHEMA.Columns WHERE TABLE_NAME = '".$table."'";
+ return 'SELECT COLUMN_NAME
+ FROM INFORMATION_SCHEMA.Columns
+ WHERE UPPER(TABLE_NAME) = '.$this->escape(strtoupper($table));
}
// --------------------------------------------------------------------
/**
- * Field data query
- *
- * Generates a platform-specific query so that the column data can be retrieved
+ * Returns an object with field data
*
- * @access public
- * @param string the table name
- * @return object
+ * @param string $table
+ * @return array
*/
- function _field_data($table)
+ public function field_data($table)
{
- return "SELECT TOP 1 * FROM ".$table;
- }
+ $sql = 'SELECT COLUMN_NAME, DATA_TYPE, CHARACTER_MAXIMUM_LENGTH, NUMERIC_PRECISION, COLUMN_DEFAULT
+ FROM INFORMATION_SCHEMA.Columns
+ WHERE UPPER(TABLE_NAME) = '.$this->escape(strtoupper($table));
- // --------------------------------------------------------------------
-
- /**
- * The error message string
- *
- * @access private
- * @return string
- */
- function _error_message()
- {
- return mssql_get_last_message();
- }
-
- // --------------------------------------------------------------------
-
- /**
- * The error message number
- *
- * @access private
- * @return integer
- */
- function _error_number()
- {
- // Are error numbers supported?
- return '';
- }
-
- // --------------------------------------------------------------------
-
- /**
- * Escape the SQL Identifiers
- *
- * This function escapes column and table names
- *
- * @access private
- * @param string
- * @return string
- */
- function _escape_identifiers($item)
- {
- if ($this->_escape_char == '')
- {
- return $item;
- }
-
- foreach ($this->_reserved_identifiers as $id)
+ if (($query = $this->query($sql)) === FALSE)
{
- if (strpos($item, '.'.$id) !== FALSE)
- {
- $str = $this->_escape_char. str_replace('.', $this->_escape_char.'.', $item);
-
- // remove duplicates if the user already included the escape
- return preg_replace('/['.$this->_escape_char.']+/', $this->_escape_char, $str);
- }
+ return FALSE;
}
+ $query = $query->result_object();
- if (strpos($item, '.') !== FALSE)
- {
- $str = $this->_escape_char.str_replace('.', $this->_escape_char.'.'.$this->_escape_char, $item).$this->_escape_char;
- }
- else
+ $retval = array();
+ for ($i = 0, $c = count($query); $i < $c; $i++)
{
- $str = $this->_escape_char.$item.$this->_escape_char;
+ $retval[$i] = new stdClass();
+ $retval[$i]->name = $query[$i]->COLUMN_NAME;
+ $retval[$i]->type = $query[$i]->DATA_TYPE;
+ $retval[$i]->max_length = ($query[$i]->CHARACTER_MAXIMUM_LENGTH > 0) ? $query[$i]->CHARACTER_MAXIMUM_LENGTH : $query[$i]->NUMERIC_PRECISION;
+ $retval[$i]->default = $query[$i]->COLUMN_DEFAULT;
}
- // remove duplicates if the user already included the escape
- return preg_replace('/['.$this->_escape_char.']+/', $this->_escape_char, $str);
+ return $retval;
}
// --------------------------------------------------------------------
/**
- * From Tables
+ * Error
*
- * This function implicitly groups FROM tables so there is no confusion
- * about operator precedence in harmony with SQL standards
+ * Returns an array containing code and message of the last
+ * database error that has occurred.
*
- * @access public
- * @param type
- * @return type
+ * @return array
*/
- function _from_tables($tables)
+ public function error()
{
- if ( ! is_array($tables))
+ // We need this because the error info is discarded by the
+ // server the first time you request it, and query() already
+ // calls error() once for logging purposes when a query fails.
+ static $error = array('code' => 0, 'message' => NULL);
+
+ $message = mssql_get_last_message();
+ if ( ! empty($message))
{
- $tables = array($tables);
+ $error['code'] = $this->query('SELECT @@ERROR AS code')->row()->code;
+ $error['message'] = $message;
}
- return implode(', ', $tables);
+ return $error;
}
// --------------------------------------------------------------------
/**
- * Insert statement
+ * Update statement
*
- * Generates a platform-specific insert string from the supplied data
+ * Generates a platform-specific update string from the supplied data
*
- * @access public
- * @param string the table name
- * @param array the insert keys
- * @param array the insert values
+ * @param string $table
+ * @param array $values
* @return string
*/
- function _insert($table, $keys, $values)
+ protected function _update($table, $values)
{
- return "INSERT INTO ".$table." (".implode(', ', $keys).") VALUES (".implode(', ', $values).")";
+ $this->qb_limit = FALSE;
+ $this->qb_orderby = array();
+ return parent::_update($table, $values);
}
// --------------------------------------------------------------------
/**
- * Update statement
+ * Truncate statement
*
- * Generates a platform-specific update string from the supplied data
+ * Generates a platform-specific truncate string from the supplied data
+ *
+ * If the database does not support the TRUNCATE statement,
+ * then this method maps to 'DELETE FROM table'
*
- * @access public
- * @param string the table name
- * @param array the update data
- * @param array the where clause
- * @param array the orderby clause
- * @param array the limit clause
+ * @param string $table
* @return string
*/
- function _update($table, $values, $where, $orderby = array(), $limit = FALSE)
+ protected function _truncate($table)
{
- foreach ($values as $key => $val)
- {
- $valstr[] = $key." = ".$val;
- }
-
- $limit = ( ! $limit) ? '' : ' LIMIT '.$limit;
-
- $orderby = (count($orderby) >= 1)?' ORDER BY '.implode(", ", $orderby):'';
-
- $sql = "UPDATE ".$table." SET ".implode(', ', $valstr);
-
- $sql .= ($where != '' AND count($where) >=1) ? " WHERE ".implode(" ", $where) : '';
-
- $sql .= $orderby.$limit;
-
- return $sql;
+ return 'TRUNCATE TABLE '.$table;
}
-
// --------------------------------------------------------------------
/**
- * Truncate statement
+ * Delete statement
*
- * Generates a platform-specific truncate string from the supplied data
- * If the database does not support the truncate() command
- * This function maps to "DELETE FROM table"
+ * Generates a platform-specific delete string from the supplied data
*
- * @access public
- * @param string the table name
+ * @param string $table
* @return string
*/
- function _truncate($table)
+ protected function _delete($table)
{
- return "TRUNCATE ".$table;
+ if ($this->qb_limit)
+ {
+ return 'WITH ci_delete AS (SELECT TOP '.$this->qb_limit.' * FROM '.$table.$this->_compile_wh('qb_where').') DELETE FROM ci_delete';
+ }
+
+ return parent::_delete($table);
}
// --------------------------------------------------------------------
/**
- * Delete statement
+ * LIMIT
*
- * Generates a platform-specific delete string from the supplied data
+ * Generates a platform-specific LIMIT clause
*
- * @access public
- * @param string the table name
- * @param array the where clause
- * @param string the limit clause
+ * @param string $sql SQL Query
* @return string
*/
- function _delete($table, $where = array(), $like = array(), $limit = FALSE)
+ protected function _limit($sql)
{
- $conditions = '';
+ $limit = $this->qb_offset + $this->qb_limit;
- if (count($where) > 0 OR count($like) > 0)
+ // As of SQL Server 2005 (9.0.*) ROW_NUMBER() is supported,
+ // however an ORDER BY clause is required for it to work
+ if (version_compare($this->version(), '9', '>=') && $this->qb_offset && ! empty($this->qb_orderby))
{
- $conditions = "\nWHERE ";
- $conditions .= implode("\n", $this->ar_where);
+ $orderby = $this->_compile_order_by();
- if (count($where) > 0 && count($like) > 0)
+ // We have to strip the ORDER BY clause
+ $sql = trim(substr($sql, 0, strrpos($sql, $orderby)));
+
+ // Get the fields to select from our subquery, so that we can avoid CI_rownum appearing in the actual results
+ if (count($this->qb_select) === 0 OR strpos(implode(',', $this->qb_select), '*') !== FALSE)
{
- $conditions .= " AND ";
+ $select = '*'; // Inevitable
+ }
+ else
+ {
+ // Use only field names and their aliases, everything else is out of our scope.
+ $select = array();
+ $field_regexp = ($this->_quoted_identifier)
+ ? '("[^\"]+")' : '(\[[^\]]+\])';
+ for ($i = 0, $c = count($this->qb_select); $i < $c; $i++)
+ {
+ $select[] = preg_match('/(?:\s|\.)'.$field_regexp.'$/i', $this->qb_select[$i], $m)
+ ? $m[1] : $this->qb_select[$i];
+ }
+ $select = implode(', ', $select);
}
- $conditions .= implode("\n", $like);
- }
- $limit = ( ! $limit) ? '' : ' LIMIT '.$limit;
+ return 'SELECT '.$select." FROM (\n\n"
+ .preg_replace('/^(SELECT( DISTINCT)?)/i', '\\1 ROW_NUMBER() OVER('.trim($orderby).') AS '.$this->escape_identifiers('CI_rownum').', ', $sql)
+ ."\n\n) ".$this->escape_identifiers('CI_subquery')
+ ."\nWHERE ".$this->escape_identifiers('CI_rownum').' BETWEEN '.($this->qb_offset + 1).' AND '.$limit;
+ }
- return "DELETE FROM ".$table.$conditions.$limit;
+ return preg_replace('/(^\SELECT (DISTINCT)?)/i','\\1 TOP '.$limit.' ', $sql);
}
// --------------------------------------------------------------------
/**
- * Limit string
+ * Insert batch statement
*
- * Generates a platform-specific LIMIT clause
+ * Generates a platform-specific insert string from the supplied data.
*
- * @access public
- * @param string the sql query string
- * @param integer the number of rows to limit the query to
- * @param integer the offset value
- * @return string
+ * @param string $table Table name
+ * @param array $keys INSERT keys
+ * @param array $values INSERT values
+ * @return string|bool
*/
- function _limit($sql, $limit, $offset)
+ protected function _insert_batch($table, $keys, $values)
{
- $i = $limit + $offset;
+ // Multiple-value inserts are only supported as of SQL Server 2008
+ if (version_compare($this->version(), '10', '>='))
+ {
+ return parent::_insert_batch($table, $keys, $values);
+ }
- return preg_replace('/(^\SELECT (DISTINCT)?)/i','\\1 TOP '.$i.' ', $sql);
+ return ($this->db_debug) ? $this->display_error('db_unsupported_feature') : FALSE;
}
// --------------------------------------------------------------------
@@ -650,18 +497,11 @@ class CI_DB_mssql_driver extends CI_DB {
/**
* Close DB Connection
*
- * @access public
- * @param resource
* @return void
*/
- function _close($conn_id)
+ protected function _close()
{
- @mssql_close($conn_id);
+ mssql_close($this->conn_id);
}
}
-
-
-
-/* End of file mssql_driver.php */
-/* Location: ./system/database/drivers/mssql/mssql_driver.php */ \ No newline at end of file
diff --git a/system/database/drivers/mssql/mssql_forge.php b/system/database/drivers/mssql/mssql_forge.php
index acb4dc50b..f9dee915e 100644
--- a/system/database/drivers/mssql/mssql_forge.php
+++ b/system/database/drivers/mssql/mssql_forge.php
@@ -1,248 +1,152 @@
-<?php if ( ! defined('BASEPATH')) exit('No direct script access allowed');
+<?php
/**
* CodeIgniter
*
- * An open source application development framework for PHP 5.1.6 or newer
+ * An open source application development framework for PHP
*
- * @package CodeIgniter
- * @author ExpressionEngine Dev Team
- * @copyright Copyright (c) 2008 - 2014, EllisLab, Inc.
- * @license http://codeigniter.com/user_guide/license.html
- * @link http://codeigniter.com
- * @since Version 1.0
+ * This content is released under the MIT License (MIT)
+ *
+ * Copyright (c) 2019 - 2022, CodeIgniter Foundation
+ *
+ * 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.
+ *
+ * @package CodeIgniter
+ * @author EllisLab Dev Team
+ * @copyright Copyright (c) 2008 - 2014, EllisLab, Inc. (https://ellislab.com/)
+ * @copyright Copyright (c) 2014 - 2019, British Columbia Institute of Technology (https://bcit.ca/)
+ * @copyright Copyright (c) 2019 - 2022, CodeIgniter Foundation (https://codeigniter.com/)
+ * @license https://opensource.org/licenses/MIT MIT License
+ * @link https://codeigniter.com
+ * @since Version 1.3.0
* @filesource
*/
-
-// ------------------------------------------------------------------------
+defined('BASEPATH') OR exit('No direct script access allowed');
/**
* MS SQL Forge Class
*
+ * @package CodeIgniter
+ * @subpackage Drivers
* @category Database
- * @author ExpressionEngine Dev Team
- * @link http://codeigniter.com/user_guide/database/
+ * @author EllisLab Dev Team
+ * @link https://codeigniter.com/userguide3/database/
*/
class CI_DB_mssql_forge extends CI_DB_forge {
/**
- * Create database
+ * CREATE TABLE IF statement
*
- * @access private
- * @param string the database name
- * @return bool
+ * @var string
*/
- function _create_database($name)
- {
- return "CREATE DATABASE ".$name;
- }
-
- // --------------------------------------------------------------------
+ protected $_create_table_if = "IF NOT EXISTS (SELECT * FROM sysobjects WHERE ID = object_id(N'%s') AND OBJECTPROPERTY(id, N'IsUserTable') = 1)\nCREATE TABLE";
/**
- * Drop database
+ * DROP TABLE IF statement
*
- * @access private
- * @param string the database name
- * @return bool
+ * @var string
*/
- function _drop_database($name)
- {
- return "DROP DATABASE ".$name;
- }
-
- // --------------------------------------------------------------------
+ protected $_drop_table_if = "IF EXISTS (SELECT * FROM sysobjects WHERE ID = object_id(N'%s') AND OBJECTPROPERTY(id, N'IsUserTable') = 1)\nDROP TABLE";
/**
- * Drop Table
+ * UNSIGNED support
*
- * @access private
- * @return bool
+ * @var array
*/
- function _drop_table($table)
- {
- return "DROP TABLE ".$this->db->_escape_identifiers($table);
- }
+ protected $_unsigned = array(
+ 'TINYINT' => 'SMALLINT',
+ 'SMALLINT' => 'INT',
+ 'INT' => 'BIGINT',
+ 'REAL' => 'FLOAT'
+ );
// --------------------------------------------------------------------
/**
- * Create Table
+ * ALTER TABLE
*
- * @access private
- * @param string the table name
- * @param array the fields
- * @param mixed primary key(s)
- * @param mixed key(s)
- * @param boolean should 'IF NOT EXISTS' be added to the SQL
- * @return bool
+ * @param string $alter_type ALTER type
+ * @param string $table Table name
+ * @param mixed $field Column definition
+ * @return string|string[]
*/
- function _create_table($table, $fields, $primary_keys, $keys, $if_not_exists)
+ protected function _alter_table($alter_type, $table, $field)
{
- $sql = 'CREATE TABLE ';
-
- if ($if_not_exists === TRUE)
- {
- $sql .= 'IF NOT EXISTS ';
- }
-
- $sql .= $this->db->_escape_identifiers($table)." (";
- $current_field_count = 0;
-
- foreach ($fields as $field=>$attributes)
- {
- // Numeric field names aren't allowed in databases, so if the key is
- // numeric, we know it was assigned by PHP and the developer manually
- // entered the field information, so we'll simply add it to the list
- if (is_numeric($field))
- {
- $sql .= "\n\t$attributes";
- }
- else
- {
- $attributes = array_change_key_case($attributes, CASE_UPPER);
-
- $sql .= "\n\t".$this->db->_protect_identifiers($field);
-
- $sql .= ' '.$attributes['TYPE'];
-
- if (array_key_exists('CONSTRAINT', $attributes))
- {
- $sql .= '('.$attributes['CONSTRAINT'].')';
- }
-
- if (array_key_exists('UNSIGNED', $attributes) && $attributes['UNSIGNED'] === TRUE)
- {
- $sql .= ' UNSIGNED';
- }
-
- if (array_key_exists('DEFAULT', $attributes))
- {
- $sql .= ' DEFAULT \''.$attributes['DEFAULT'].'\'';
- }
-
- if (array_key_exists('NULL', $attributes) && $attributes['NULL'] === TRUE)
- {
- $sql .= ' NULL';
- }
- else
- {
- $sql .= ' NOT NULL';
- }
-
- if (array_key_exists('AUTO_INCREMENT', $attributes) && $attributes['AUTO_INCREMENT'] === TRUE)
- {
- $sql .= ' AUTO_INCREMENT';
- }
- }
-
- // don't add a comma on the end of the last field
- if (++$current_field_count < count($fields))
- {
- $sql .= ',';
- }
- }
-
- if (count($primary_keys) > 0)
+ if (in_array($alter_type, array('ADD', 'DROP'), TRUE))
{
- $primary_keys = $this->db->_protect_identifiers($primary_keys);
- $sql .= ",\n\tPRIMARY KEY (" . implode(', ', $primary_keys) . ")";
+ return parent::_alter_table($alter_type, $table, $field);
}
- if (is_array($keys) && count($keys) > 0)
+ $sql = 'ALTER TABLE '.$this->db->escape_identifiers($table).' ALTER COLUMN ';
+ $sqls = array();
+ for ($i = 0, $c = count($field); $i < $c; $i++)
{
- foreach ($keys as $key)
- {
- if (is_array($key))
- {
- $key = $this->db->_protect_identifiers($key);
- }
- else
- {
- $key = array($this->db->_protect_identifiers($key));
- }
-
- $sql .= ",\n\tFOREIGN KEY (" . implode(', ', $key) . ")";
- }
+ $sqls[] = $sql.$this->_process_column($field[$i]);
}
- $sql .= "\n)";
-
- return $sql;
+ return $sqls;
}
// --------------------------------------------------------------------
/**
- * Alter table query
+ * Field attribute TYPE
*
- * Generates a platform-specific query so that a table can be altered
- * Called by add_column(), drop_column(), and column_alter(),
+ * Performs a data type mapping between different databases.
*
- * @access private
- * @param string the ALTER type (ADD, DROP, CHANGE)
- * @param string the column name
- * @param string the table name
- * @param string the column definition
- * @param string the default value
- * @param boolean should 'NOT NULL' be added
- * @param string the field after which we should add the new field
- * @return object
+ * @param array &$attributes
+ * @return void
*/
- function _alter_table($alter_type, $table, $column_name, $column_definition = '', $default_value = '', $null = '', $after_field = '')
+ protected function _attr_type(&$attributes)
{
- $sql = 'ALTER TABLE '.$this->db->_protect_identifiers($table)." $alter_type ".$this->db->_protect_identifiers($column_name);
-
- // DROP has everything it needs now.
- if ($alter_type == 'DROP')
- {
- return $sql;
- }
-
- $sql .= " $column_definition";
-
- if ($default_value != '')
- {
- $sql .= " DEFAULT \"$default_value\"";
- }
-
- if ($null === NULL)
- {
- $sql .= ' NULL';
- }
- else
+ if (isset($attributes['CONSTRAINT']) && strpos($attributes['TYPE'], 'INT') !== FALSE)
{
- $sql .= ' NOT NULL';
+ unset($attributes['CONSTRAINT']);
}
- if ($after_field != '')
+ switch (strtoupper($attributes['TYPE']))
{
- $sql .= ' AFTER ' . $this->db->_protect_identifiers($after_field);
+ case 'MEDIUMINT':
+ $attributes['TYPE'] = 'INTEGER';
+ $attributes['UNSIGNED'] = FALSE;
+ return;
+ case 'INTEGER':
+ $attributes['TYPE'] = 'INT';
+ return;
+ default: return;
}
-
- return $sql;
-
}
// --------------------------------------------------------------------
/**
- * Rename a table
- *
- * Generates a platform-specific query so that a table can be renamed
+ * Field attribute AUTO_INCREMENT
*
- * @access private
- * @param string the old table name
- * @param string the new table name
- * @return string
+ * @param array &$attributes
+ * @param array &$field
+ * @return void
*/
- function _rename_table($table_name, $new_table_name)
+ protected function _attr_auto_increment(&$attributes, &$field)
{
- // I think this syntax will work, but can find little documentation on renaming tables in MSSQL
- $sql = 'ALTER TABLE '.$this->db->_protect_identifiers($table_name)." RENAME TO ".$this->db->_protect_identifiers($new_table_name);
- return $sql;
+ if ( ! empty($attributes['AUTO_INCREMENT']) && $attributes['AUTO_INCREMENT'] === TRUE && stripos($field['type'], 'int') !== FALSE)
+ {
+ $field['auto_increment'] = ' IDENTITY(1,1)';
+ }
}
}
-
-/* End of file mssql_forge.php */
-/* Location: ./system/database/drivers/mssql/mssql_forge.php */ \ No newline at end of file
diff --git a/system/database/drivers/mssql/mssql_result.php b/system/database/drivers/mssql/mssql_result.php
index af4b3c541..fbe2eb1be 100644
--- a/system/database/drivers/mssql/mssql_result.php
+++ b/system/database/drivers/mssql/mssql_result.php
@@ -1,40 +1,66 @@
-<?php if ( ! defined('BASEPATH')) exit('No direct script access allowed');
+<?php
/**
* CodeIgniter
*
- * An open source application development framework for PHP 5.1.6 or newer
+ * An open source application development framework for PHP
*
- * @package CodeIgniter
- * @author ExpressionEngine Dev Team
- * @copyright Copyright (c) 2008 - 2014, EllisLab, Inc.
- * @license http://codeigniter.com/user_guide/license.html
- * @link http://codeigniter.com
- * @since Version 1.0
+ * This content is released under the MIT License (MIT)
+ *
+ * Copyright (c) 2019 - 2022, CodeIgniter Foundation
+ *
+ * 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.
+ *
+ * @package CodeIgniter
+ * @author EllisLab Dev Team
+ * @copyright Copyright (c) 2008 - 2014, EllisLab, Inc. (https://ellislab.com/)
+ * @copyright Copyright (c) 2014 - 2019, British Columbia Institute of Technology (https://bcit.ca/)
+ * @copyright Copyright (c) 2019 - 2022, CodeIgniter Foundation (https://codeigniter.com/)
+ * @license https://opensource.org/licenses/MIT MIT License
+ * @link https://codeigniter.com
+ * @since Version 1.3.0
* @filesource
*/
-
-// ------------------------------------------------------------------------
+defined('BASEPATH') OR exit('No direct script access allowed');
/**
- * MS SQL Result Class
+ * MSSQL Result Class
*
* This class extends the parent result class: CI_DB_result
*
+ * @package CodeIgniter
+ * @subpackage Drivers
* @category Database
- * @author ExpressionEngine Dev Team
- * @link http://codeigniter.com/user_guide/database/
+ * @author EllisLab Dev Team
+ * @link https://codeigniter.com/userguide3/database/
*/
class CI_DB_mssql_result extends CI_DB_result {
/**
* Number of rows in the result set
*
- * @access public
- * @return integer
+ * @return int
*/
- function num_rows()
+ public function num_rows()
{
- return @mssql_num_rows($this->result_id);
+ return is_int($this->num_rows)
+ ? $this->num_rows
+ : $this->num_rows = mssql_num_rows($this->result_id);
}
// --------------------------------------------------------------------
@@ -42,12 +68,11 @@ class CI_DB_mssql_result extends CI_DB_result {
/**
* Number of fields in the result set
*
- * @access public
- * @return integer
+ * @return int
*/
- function num_fields()
+ public function num_fields()
{
- return @mssql_num_fields($this->result_id);
+ return mssql_num_fields($this->result_id);
}
// --------------------------------------------------------------------
@@ -57,12 +82,12 @@ class CI_DB_mssql_result extends CI_DB_result {
*
* Generates an array of column names
*
- * @access public
* @return array
*/
- function list_fields()
+ public function list_fields()
{
$field_names = array();
+ mssql_field_seek($this->result_id, 0);
while ($field = mssql_fetch_field($this->result_id))
{
$field_names[] = $field->name;
@@ -78,22 +103,19 @@ class CI_DB_mssql_result extends CI_DB_result {
*
* Generates an array of objects containing field meta-data
*
- * @access public
* @return array
*/
- function field_data()
+ public function field_data()
{
$retval = array();
- while ($field = mssql_fetch_field($this->result_id))
+ for ($i = 0, $c = $this->num_fields(); $i < $c; $i++)
{
- $F = new stdClass();
- $F->name = $field->name;
- $F->type = $field->type;
- $F->max_length = $field->max_length;
- $F->primary_key = 0;
- $F->default = '';
-
- $retval[] = $F;
+ $field = mssql_fetch_field($this->result_id, $i);
+
+ $retval[$i] = new stdClass();
+ $retval[$i]->name = $field->name;
+ $retval[$i]->type = $field->type;
+ $retval[$i]->max_length = $field->max_length;
}
return $retval;
@@ -104,9 +126,9 @@ class CI_DB_mssql_result extends CI_DB_result {
/**
* Free the result
*
- * @return null
+ * @return void
*/
- function free_result()
+ public function free_result()
{
if (is_resource($this->result_id))
{
@@ -120,14 +142,14 @@ class CI_DB_mssql_result extends CI_DB_result {
/**
* Data Seek
*
- * Moves the internal pointer to the desired offset. We call
+ * Moves the internal pointer to the desired offset. We call
* this internally before fetching results to make sure the
- * result set starts at zero
+ * result set starts at zero.
*
- * @access private
- * @return array
+ * @param int $n
+ * @return bool
*/
- function _data_seek($n = 0)
+ public function data_seek($n = 0)
{
return mssql_data_seek($this->result_id, $n);
}
@@ -139,10 +161,9 @@ class CI_DB_mssql_result extends CI_DB_result {
*
* Returns the result set as an array
*
- * @access private
* @return array
*/
- function _fetch_assoc()
+ protected function _fetch_assoc()
{
return mssql_fetch_assoc($this->result_id);
}
@@ -154,16 +175,25 @@ class CI_DB_mssql_result extends CI_DB_result {
*
* Returns the result set as an object
*
- * @access private
+ * @param string $class_name
* @return object
*/
- function _fetch_object()
+ protected function _fetch_object($class_name = 'stdClass')
{
- return mssql_fetch_object($this->result_id);
- }
+ $row = mssql_fetch_object($this->result_id);
-}
+ if ($class_name === 'stdClass' OR ! $row)
+ {
+ return $row;
+ }
+ $class_name = new $class_name();
+ foreach ($row as $key => $value)
+ {
+ $class_name->$key = $value;
+ }
-/* End of file mssql_result.php */
-/* Location: ./system/database/drivers/mssql/mssql_result.php */ \ No newline at end of file
+ return $class_name;
+ }
+
+}
diff --git a/system/database/drivers/mssql/mssql_utility.php b/system/database/drivers/mssql/mssql_utility.php
index 0e4e7be8c..a739dc823 100644
--- a/system/database/drivers/mssql/mssql_utility.php
+++ b/system/database/drivers/mssql/mssql_utility.php
@@ -1,88 +1,78 @@
-<?php if ( ! defined('BASEPATH')) exit('No direct script access allowed');
+<?php
/**
* CodeIgniter
*
- * An open source application development framework for PHP 5.1.6 or newer
+ * An open source application development framework for PHP
*
- * @package CodeIgniter
- * @author ExpressionEngine Dev Team
- * @copyright Copyright (c) 2008 - 2014, EllisLab, Inc.
- * @license http://codeigniter.com/user_guide/license.html
- * @link http://codeigniter.com
- * @since Version 1.0
+ * This content is released under the MIT License (MIT)
+ *
+ * Copyright (c) 2019 - 2022, CodeIgniter Foundation
+ *
+ * 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.
+ *
+ * @package CodeIgniter
+ * @author EllisLab Dev Team
+ * @copyright Copyright (c) 2008 - 2014, EllisLab, Inc. (https://ellislab.com/)
+ * @copyright Copyright (c) 2014 - 2019, British Columbia Institute of Technology (https://bcit.ca/)
+ * @copyright Copyright (c) 2019 - 2022, CodeIgniter Foundation (https://codeigniter.com/)
+ * @license https://opensource.org/licenses/MIT MIT License
+ * @link https://codeigniter.com
+ * @since Version 1.3.0
* @filesource
*/
-
-// ------------------------------------------------------------------------
+defined('BASEPATH') OR exit('No direct script access allowed');
/**
* MS SQL Utility Class
*
+ * @package CodeIgniter
+ * @subpackage Drivers
* @category Database
- * @author ExpressionEngine Dev Team
- * @link http://codeigniter.com/user_guide/database/
+ * @author EllisLab Dev Team
+ * @link https://codeigniter.com/userguide3/database/
*/
class CI_DB_mssql_utility extends CI_DB_utility {
/**
- * List databases
- *
- * @access private
- * @return bool
- */
- function _list_databases()
- {
- return "EXEC sp_helpdb"; // Can also be: EXEC sp_databases
- }
-
- // --------------------------------------------------------------------
-
- /**
- * Optimize table query
- *
- * Generates a platform-specific query so that a table can be optimized
+ * List databases statement
*
- * @access private
- * @param string the table name
- * @return object
+ * @var string
*/
- function _optimize_table($table)
- {
- return FALSE; // Is this supported in MS SQL?
- }
-
- // --------------------------------------------------------------------
+ protected $_list_databases = 'EXEC sp_helpdb'; // Can also be: EXEC sp_databases
/**
- * Repair table query
+ * OPTIMIZE TABLE statement
*
- * Generates a platform-specific query so that a table can be repaired
- *
- * @access private
- * @param string the table name
- * @return object
+ * @var string
*/
- function _repair_table($table)
- {
- return FALSE; // Is this supported in MS SQL?
- }
-
- // --------------------------------------------------------------------
+ protected $_optimize_table = 'ALTER INDEX all ON %s REORGANIZE';
/**
- * MSSQL Export
+ * Export
*
- * @access private
- * @param array Preferences
- * @return mixed
+ * @param array $params Preferences
+ * @return bool
*/
- function _backup($params = array())
+ protected function _backup($params = array())
{
// Currently unsupported
- return $this->db->display_error('db_unsuported_feature');
+ return $this->db->display_error('db_unsupported_feature');
}
}
-
-/* End of file mssql_utility.php */
-/* Location: ./system/database/drivers/mssql/mssql_utility.php */ \ No newline at end of file
diff --git a/system/database/drivers/mysql/index.html b/system/database/drivers/mysql/index.html
index c942a79ce..bcb7cae34 100644
--- a/system/database/drivers/mysql/index.html
+++ b/system/database/drivers/mysql/index.html
@@ -1,4 +1,5 @@
-<html>
+<!DOCTYPE html>
+<html lang="en">
<head>
<title>403 Forbidden</title>
</head>
@@ -7,4 +8,4 @@
<p>Directory access is forbidden.</p>
</body>
-</html> \ No newline at end of file
+</html>
diff --git a/system/database/drivers/mysql/mysql_driver.php b/system/database/drivers/mysql/mysql_driver.php
index 832b9d83e..5c4d2d8fb 100644
--- a/system/database/drivers/mysql/mysql_driver.php
+++ b/system/database/drivers/mysql/mysql_driver.php
@@ -1,94 +1,188 @@
-<?php if ( ! defined('BASEPATH')) exit('No direct script access allowed');
+<?php
/**
* CodeIgniter
*
- * An open source application development framework for PHP 5.1.6 or newer
+ * An open source application development framework for PHP
*
- * @package CodeIgniter
- * @author ExpressionEngine Dev Team
- * @copyright Copyright (c) 2008 - 2014, EllisLab, Inc.
- * @license http://codeigniter.com/user_guide/license.html
- * @link http://codeigniter.com
- * @since Version 1.0
+ * This content is released under the MIT License (MIT)
+ *
+ * Copyright (c) 2019 - 2022, CodeIgniter Foundation
+ *
+ * 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.
+ *
+ * @package CodeIgniter
+ * @author EllisLab Dev Team
+ * @copyright Copyright (c) 2008 - 2014, EllisLab, Inc. (https://ellislab.com/)
+ * @copyright Copyright (c) 2014 - 2019, British Columbia Institute of Technology (https://bcit.ca/)
+ * @copyright Copyright (c) 2019 - 2022, CodeIgniter Foundation (https://codeigniter.com/)
+ * @license https://opensource.org/licenses/MIT MIT License
+ * @link https://codeigniter.com
+ * @since Version 1.0.0
* @filesource
*/
-
-// ------------------------------------------------------------------------
+defined('BASEPATH') OR exit('No direct script access allowed');
/**
* MySQL Database Adapter Class
*
* Note: _DB is an extender class that the app controller
- * creates dynamically based on whether the active record
+ * creates dynamically based on whether the query builder
* class is being used or not.
*
* @package CodeIgniter
* @subpackage Drivers
* @category Database
- * @author ExpressionEngine Dev Team
- * @link http://codeigniter.com/user_guide/database/
+ * @author EllisLab Dev Team
+ * @link https://codeigniter.com/userguide3/database/
*/
class CI_DB_mysql_driver extends CI_DB {
- var $dbdriver = 'mysql';
-
- // The character used for escaping
- var $_escape_char = '`';
+ /**
+ * Database driver
+ *
+ * @var string
+ */
+ public $dbdriver = 'mysql';
- // clause and character used for LIKE escape sequences - not used in MySQL
- var $_like_escape_str = '';
- var $_like_escape_chr = '';
+ /**
+ * Compression flag
+ *
+ * @var bool
+ */
+ public $compress = FALSE;
/**
+ * DELETE hack flag
+ *
* Whether to use the MySQL "delete hack" which allows the number
* of affected rows to be shown. Uses a preg_replace when enabled,
* adding a bit more processing to all queries.
+ *
+ * @var bool
*/
- var $delete_hack = TRUE;
+ public $delete_hack = TRUE;
/**
- * The syntax to count rows is slightly different across different
- * database engines, so this string appears in each driver and is
- * used for the count_all() and count_all_results() functions.
+ * Strict ON flag
+ *
+ * Whether we're running in strict SQL mode.
+ *
+ * @var bool
*/
- var $_count_string = 'SELECT COUNT(*) AS ';
- var $_random_keyword = ' RAND()'; // database specific random keyword
+ public $stricton;
+
+ // --------------------------------------------------------------------
- // whether SET NAMES must be used to set the character set
- var $use_set_names;
-
/**
- * Non-persistent database connection
+ * Identifier escape character
*
- * @access private called by the base class
- * @return resource
+ * @var string
*/
- function db_connect()
+ protected $_escape_char = '`';
+
+ // --------------------------------------------------------------------
+
+ /**
+ * Class constructor
+ *
+ * @param array $params
+ * @return void
+ */
+ public function __construct($params)
{
- if ($this->port != '')
+ parent::__construct($params);
+
+ if ( ! empty($this->port))
{
$this->hostname .= ':'.$this->port;
}
-
- return @mysql_connect($this->hostname, $this->username, $this->password, TRUE);
}
// --------------------------------------------------------------------
/**
- * Persistent database connection
+ * Non-persistent database connection
*
- * @access private called by the base class
+ * @param bool $persistent
* @return resource
*/
- function db_pconnect()
+ public function db_connect($persistent = FALSE)
{
- if ($this->port != '')
+ $client_flags = ($this->compress === FALSE) ? 0 : MYSQL_CLIENT_COMPRESS;
+
+ if ($this->encrypt === TRUE)
{
- $this->hostname .= ':'.$this->port;
+ $client_flags = $client_flags | MYSQL_CLIENT_SSL;
+ }
+
+ // Error suppression is necessary mostly due to PHP 5.5+ issuing E_DEPRECATED messages
+ $this->conn_id = ($persistent === TRUE)
+ ? mysql_pconnect($this->hostname, $this->username, $this->password, $client_flags)
+ : mysql_connect($this->hostname, $this->username, $this->password, TRUE, $client_flags);
+
+ // ----------------------------------------------------------------
+
+ // Select the DB... assuming a database name is specified in the config file
+ if ($this->database !== '' && ! $this->db_select())
+ {
+ log_message('error', 'Unable to select database: '.$this->database);
+
+ return ($this->db_debug === TRUE)
+ ? $this->display_error('db_unable_to_select', $this->database)
+ : FALSE;
+ }
+
+ if (is_resource($this->conn_id))
+ {
+ if ( ! mysql_set_charset($this->char_set, $this->conn_id))
+ {
+ log_message('error', "Database: Unable to set the configured connection charset ('{$this->char_set}').");
+ $this->close();
+ return ($this->db->debug) ? $this->display_error('db_unable_to_set_charset', $this->char_set) : FALSE;
+ }
+
+ if (isset($this->stricton))
+ {
+ if ($this->stricton)
+ {
+ $this->simple_query('SET SESSION sql_mode = CONCAT(@@sql_mode, ",", "STRICT_ALL_TABLES")');
+ }
+ else
+ {
+ $this->simple_query(
+ 'SET SESSION sql_mode =
+ REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(
+ @@sql_mode,
+ "STRICT_ALL_TABLES,", ""),
+ ",STRICT_ALL_TABLES", ""),
+ "STRICT_ALL_TABLES", ""),
+ "STRICT_TRANS_TABLES,", ""),
+ ",STRICT_TRANS_TABLES", ""),
+ "STRICT_TRANS_TABLES", "")'
+ );
+ }
+ }
+
+ return $this->conn_id;
}
- return @mysql_pconnect($this->hostname, $this->username, $this->password);
+ return FALSE;
}
// --------------------------------------------------------------------
@@ -99,10 +193,9 @@ class CI_DB_mysql_driver extends CI_DB {
* Keep / reestablish the db connection if no queries have been
* sent for a length of time exceeding the server's idle timeout
*
- * @access public
* @return void
*/
- function reconnect()
+ public function reconnect()
{
if (mysql_ping($this->conn_id) === FALSE)
{
@@ -115,53 +208,46 @@ class CI_DB_mysql_driver extends CI_DB {
/**
* Select the database
*
- * @access private called by the base class
- * @return resource
- */
- function db_select()
- {
- return @mysql_select_db($this->database, $this->conn_id);
- }
-
- // --------------------------------------------------------------------
-
- /**
- * Set client character set
- *
- * @access public
- * @param string
- * @param string
- * @return resource
+ * @param string $database
+ * @return bool
*/
- function db_set_charset($charset, $collation)
+ public function db_select($database = '')
{
- if ( ! isset($this->use_set_names))
+ if ($database === '')
{
- // mysql_set_charset() requires PHP >= 5.2.3 and MySQL >= 5.0.7, use SET NAMES as fallback
- $this->use_set_names = (version_compare(PHP_VERSION, '5.2.3', '>=') && version_compare(mysql_get_server_info(), '5.0.7', '>=')) ? FALSE : TRUE;
+ $database = $this->database;
}
- if ($this->use_set_names === TRUE)
+ if (mysql_select_db($database, $this->conn_id))
{
- return @mysql_query("SET NAMES '".$this->escape_str($charset)."' COLLATE '".$this->escape_str($collation)."'", $this->conn_id);
- }
- else
- {
- return @mysql_set_charset($charset, $this->conn_id);
+ $this->database = $database;
+ $this->data_cache = array();
+ return TRUE;
}
+
+ return FALSE;
}
// --------------------------------------------------------------------
/**
- * Version number query string
+ * Database version number
*
- * @access public
* @return string
*/
- function _version()
+ public function version()
{
- return "SELECT version() AS ver";
+ if (isset($this->data_cache['version']))
+ {
+ return $this->data_cache['version'];
+ }
+
+ if ( ! $this->conn_id OR ($version = mysql_get_server_info($this->conn_id)) === FALSE)
+ {
+ return FALSE;
+ }
+
+ return $this->data_cache['version'] = $version;
}
// --------------------------------------------------------------------
@@ -169,14 +255,12 @@ class CI_DB_mysql_driver extends CI_DB {
/**
* Execute the query
*
- * @access private called by the base class
- * @param string an SQL query
- * @return resource
+ * @param string $sql an SQL query
+ * @return mixed
*/
- function _execute($sql)
+ protected function _execute($sql)
{
- $sql = $this->_prep_query($sql);
- return @mysql_query($sql, $this->conn_id);
+ return mysql_query($this->_prep_query($sql), $this->conn_id);
}
// --------------------------------------------------------------------
@@ -186,20 +270,16 @@ class CI_DB_mysql_driver extends CI_DB {
*
* If needed, each database adapter can prep the query string
*
- * @access private called by execute()
- * @param string an SQL query
+ * @param string $sql an SQL query
* @return string
*/
- function _prep_query($sql)
+ protected function _prep_query($sql)
{
- // "DELETE FROM TABLE" returns 0 affected rows This hack modifies
- // the query so that it returns the number of affected rows
- if ($this->delete_hack === TRUE)
+ // mysql_affected_rows() returns 0 for "DELETE FROM TABLE" queries. This hack
+ // modifies the query so that it a proper number of affected rows is returned.
+ if ($this->delete_hack === TRUE && preg_match('/^\s*DELETE\s+FROM\s+(\S+)\s*$/i', $sql))
{
- if (preg_match('/^\s*DELETE\s+FROM\s+(\S+)\s*$/i', $sql))
- {
- $sql = preg_replace("/^\s*DELETE\s+FROM\s+(\S+)\s*$/", "DELETE FROM \\1 WHERE 1=1", $sql);
- }
+ return trim($sql).' WHERE 1=1';
}
return $sql;
@@ -210,30 +290,12 @@ class CI_DB_mysql_driver extends CI_DB {
/**
* Begin Transaction
*
- * @access public
* @return bool
*/
- function trans_begin($test_mode = FALSE)
+ protected function _trans_begin()
{
- if ( ! $this->trans_enabled)
- {
- return TRUE;
- }
-
- // When transactions are nested we only begin/commit/rollback the outermost ones
- if ($this->_trans_depth > 0)
- {
- return TRUE;
- }
-
- // Reset the transaction failure flag.
- // If the $test_mode flag is set to TRUE transactions will be rolled back
- // even if the queries produce a successful result.
- $this->_trans_failure = ($test_mode === TRUE) ? TRUE : FALSE;
-
$this->simple_query('SET AUTOCOMMIT=0');
- $this->simple_query('START TRANSACTION'); // can also be BEGIN or BEGIN WORK
- return TRUE;
+ return $this->simple_query('START TRANSACTION'); // can also be BEGIN or BEGIN WORK
}
// --------------------------------------------------------------------
@@ -241,25 +303,17 @@ class CI_DB_mysql_driver extends CI_DB {
/**
* Commit Transaction
*
- * @access public
* @return bool
*/
- function trans_commit()
+ protected function _trans_commit()
{
- if ( ! $this->trans_enabled)
+ if ($this->simple_query('COMMIT'))
{
+ $this->simple_query('SET AUTOCOMMIT=1');
return TRUE;
}
- // When transactions are nested we only begin/commit/rollback the outermost ones
- if ($this->_trans_depth > 0)
- {
- return TRUE;
- }
-
- $this->simple_query('COMMIT');
- $this->simple_query('SET AUTOCOMMIT=1');
- return TRUE;
+ return FALSE;
}
// --------------------------------------------------------------------
@@ -267,69 +321,30 @@ class CI_DB_mysql_driver extends CI_DB {
/**
* Rollback Transaction
*
- * @access public
* @return bool
*/
- function trans_rollback()
+ protected function _trans_rollback()
{
- if ( ! $this->trans_enabled)
+ if ($this->simple_query('ROLLBACK'))
{
+ $this->simple_query('SET AUTOCOMMIT=1');
return TRUE;
}
- // When transactions are nested we only begin/commit/rollback the outermost ones
- if ($this->_trans_depth > 0)
- {
- return TRUE;
- }
-
- $this->simple_query('ROLLBACK');
- $this->simple_query('SET AUTOCOMMIT=1');
- return TRUE;
+ return FALSE;
}
// --------------------------------------------------------------------
/**
- * Escape String
+ * Platform-dependent string escape
*
- * @access public
* @param string
- * @param bool whether or not the string will be used in a LIKE condition
* @return string
*/
- function escape_str($str, $like = FALSE)
+ protected function _escape_str($str)
{
- if (is_array($str))
- {
- foreach ($str as $key => $val)
- {
- $str[$key] = $this->escape_str($val, $like);
- }
-
- return $str;
- }
-
- if (function_exists('mysql_real_escape_string') AND is_resource($this->conn_id))
- {
- $str = mysql_real_escape_string($str, $this->conn_id);
- }
- elseif (function_exists('mysql_escape_string'))
- {
- $str = mysql_escape_string($str);
- }
- else
- {
- $str = addslashes($str);
- }
-
- // escape LIKE condition wildcards
- if ($like === TRUE)
- {
- $str = str_replace(array('%', '_'), array('\\%', '\\_'), $str);
- }
-
- return $str;
+ return mysql_real_escape_string($str, $this->conn_id);
}
// --------------------------------------------------------------------
@@ -337,12 +352,11 @@ class CI_DB_mysql_driver extends CI_DB {
/**
* Affected Rows
*
- * @access public
- * @return integer
+ * @return int
*/
- function affected_rows()
+ public function affected_rows()
{
- return @mysql_affected_rows($this->conn_id);
+ return mysql_affected_rows($this->conn_id);
}
// --------------------------------------------------------------------
@@ -350,43 +364,11 @@ class CI_DB_mysql_driver extends CI_DB {
/**
* Insert ID
*
- * @access public
- * @return integer
+ * @return int
*/
- function insert_id()
+ public function insert_id()
{
- return @mysql_insert_id($this->conn_id);
- }
-
- // --------------------------------------------------------------------
-
- /**
- * "Count All" query
- *
- * Generates a platform-specific query string that counts all records in
- * the specified database
- *
- * @access public
- * @param string
- * @return string
- */
- function count_all($table = '')
- {
- if ($table == '')
- {
- return 0;
- }
-
- $query = $this->query($this->_count_string . $this->_protect_identifiers('numrows') . " FROM " . $this->_protect_identifiers($table, TRUE, NULL, FALSE));
-
- if ($query->num_rows() == 0)
- {
- return 0;
- }
-
- $row = $query->row();
- $this->_reset_select();
- return (int) $row->numrows;
+ return mysql_insert_id($this->conn_id);
}
// --------------------------------------------------------------------
@@ -396,17 +378,16 @@ class CI_DB_mysql_driver extends CI_DB {
*
* Generates a platform-specific query string so that the table names can be fetched
*
- * @access private
- * @param boolean
+ * @param bool $prefix_limit
* @return string
*/
- function _list_tables($prefix_limit = FALSE)
+ protected function _list_tables($prefix_limit = FALSE)
{
- $sql = "SHOW TABLES FROM ".$this->_escape_char.$this->database.$this->_escape_char;
+ $sql = 'SHOW TABLES FROM '.$this->_escape_char.$this->database.$this->_escape_char;
- if ($prefix_limit !== FALSE AND $this->dbprefix != '')
+ if ($prefix_limit !== FALSE && $this->dbprefix !== '')
{
- $sql .= " LIKE '".$this->escape_like_str($this->dbprefix)."%'";
+ return $sql." LIKE '".$this->escape_like_str($this->dbprefix)."%'";
}
return $sql;
@@ -419,343 +400,81 @@ class CI_DB_mysql_driver extends CI_DB {
*
* Generates a platform-specific query string so that the column names can be fetched
*
- * @access public
- * @param string the table name
+ * @param string $table
* @return string
*/
- function _list_columns($table = '')
+ protected function _list_columns($table = '')
{
- return "SHOW COLUMNS FROM ".$this->_protect_identifiers($table, TRUE, NULL, FALSE);
+ return 'SHOW COLUMNS FROM '.$this->protect_identifiers($table, TRUE, NULL, FALSE);
}
// --------------------------------------------------------------------
/**
- * Field data query
- *
- * Generates a platform-specific query so that the column data can be retrieved
+ * Returns an object with field data
*
- * @access public
- * @param string the table name
- * @return object
+ * @param string $table
+ * @return array
*/
- function _field_data($table)
+ public function field_data($table)
{
- return "DESCRIBE ".$table;
- }
-
- // --------------------------------------------------------------------
-
- /**
- * The error message string
- *
- * @access private
- * @return string
- */
- function _error_message()
- {
- return mysql_error($this->conn_id);
- }
-
- // --------------------------------------------------------------------
-
- /**
- * The error message number
- *
- * @access private
- * @return integer
- */
- function _error_number()
- {
- return mysql_errno($this->conn_id);
- }
-
- // --------------------------------------------------------------------
-
- /**
- * Escape the SQL Identifiers
- *
- * This function escapes column and table names
- *
- * @access private
- * @param string
- * @return string
- */
- function _escape_identifiers($item)
- {
- if ($this->_escape_char == '')
+ if (($query = $this->query('SHOW COLUMNS FROM '.$this->protect_identifiers($table, TRUE, NULL, FALSE))) === FALSE)
{
- return $item;
+ return FALSE;
}
+ $query = $query->result_object();
- foreach ($this->_reserved_identifiers as $id)
+ $retval = array();
+ for ($i = 0, $c = count($query); $i < $c; $i++)
{
- if (strpos($item, '.'.$id) !== FALSE)
- {
- $str = $this->_escape_char. str_replace('.', $this->_escape_char.'.', $item);
+ $retval[$i] = new stdClass();
+ $retval[$i]->name = $query[$i]->Field;
- // remove duplicates if the user already included the escape
- return preg_replace('/['.$this->_escape_char.']+/', $this->_escape_char, $str);
- }
- }
+ sscanf($query[$i]->Type, '%[a-z](%d)',
+ $retval[$i]->type,
+ $retval[$i]->max_length
+ );
- if (strpos($item, '.') !== FALSE)
- {
- $str = $this->_escape_char.str_replace('.', $this->_escape_char.'.'.$this->_escape_char, $item).$this->_escape_char;
+ $retval[$i]->default = $query[$i]->Default;
+ $retval[$i]->primary_key = (int) ($query[$i]->Key === 'PRI');
}
- else
- {
- $str = $this->_escape_char.$item.$this->_escape_char;
- }
-
- // remove duplicates if the user already included the escape
- return preg_replace('/['.$this->_escape_char.']+/', $this->_escape_char, $str);
- }
-
- // --------------------------------------------------------------------
-
- /**
- * From Tables
- *
- * This function implicitly groups FROM tables so there is no confusion
- * about operator precedence in harmony with SQL standards
- *
- * @access public
- * @param type
- * @return type
- */
- function _from_tables($tables)
- {
- if ( ! is_array($tables))
- {
- $tables = array($tables);
- }
-
- return '('.implode(', ', $tables).')';
- }
-
- // --------------------------------------------------------------------
- /**
- * Insert statement
- *
- * Generates a platform-specific insert string from the supplied data
- *
- * @access public
- * @param string the table name
- * @param array the insert keys
- * @param array the insert values
- * @return string
- */
- function _insert($table, $keys, $values)
- {
- return "INSERT INTO ".$table." (".implode(', ', $keys).") VALUES (".implode(', ', $values).")";
+ return $retval;
}
// --------------------------------------------------------------------
-
/**
- * Replace statement
+ * Error
*
- * Generates a platform-specific replace string from the supplied data
+ * Returns an array containing code and message of the last
+ * database error that has occurred.
*
- * @access public
- * @param string the table name
- * @param array the insert keys
- * @param array the insert values
- * @return string
+ * @return array
*/
- function _replace($table, $keys, $values)
+ public function error()
{
- return "REPLACE INTO ".$table." (".implode(', ', $keys).") VALUES (".implode(', ', $values).")";
+ return array('code' => mysql_errno($this->conn_id), 'message' => mysql_error($this->conn_id));
}
// --------------------------------------------------------------------
/**
- * Insert_batch statement
+ * FROM tables
*
- * Generates a platform-specific insert string from the supplied data
+ * Groups tables in FROM clauses if needed, so there is no confusion
+ * about operator precedence.
*
- * @access public
- * @param string the table name
- * @param array the insert keys
- * @param array the insert values
* @return string
*/
- function _insert_batch($table, $keys, $values)
+ protected function _from_tables()
{
- return "INSERT INTO ".$table." (".implode(', ', $keys).") VALUES ".implode(', ', $values);
- }
-
- // --------------------------------------------------------------------
-
-
- /**
- * Update statement
- *
- * Generates a platform-specific update string from the supplied data
- *
- * @access public
- * @param string the table name
- * @param array the update data
- * @param array the where clause
- * @param array the orderby clause
- * @param array the limit clause
- * @return string
- */
- function _update($table, $values, $where, $orderby = array(), $limit = FALSE)
- {
- foreach ($values as $key => $val)
+ if ( ! empty($this->qb_join) && count($this->qb_from) > 1)
{
- $valstr[] = $key . ' = ' . $val;
+ return '('.implode(', ', $this->qb_from).')';
}
- $limit = ( ! $limit) ? '' : ' LIMIT '.$limit;
-
- $orderby = (count($orderby) >= 1)?' ORDER BY '.implode(", ", $orderby):'';
-
- $sql = "UPDATE ".$table." SET ".implode(', ', $valstr);
-
- $sql .= ($where != '' AND count($where) >=1) ? " WHERE ".implode(" ", $where) : '';
-
- $sql .= $orderby.$limit;
-
- return $sql;
- }
-
- // --------------------------------------------------------------------
-
-
- /**
- * Update_Batch statement
- *
- * Generates a platform-specific batch update string from the supplied data
- *
- * @access public
- * @param string the table name
- * @param array the update data
- * @param array the where clause
- * @return string
- */
- function _update_batch($table, $values, $index, $where = NULL)
- {
- $ids = array();
- $where = ($where != '' AND count($where) >=1) ? implode(" ", $where).' AND ' : '';
-
- foreach ($values as $key => $val)
- {
- $ids[] = $val[$index];
-
- foreach (array_keys($val) as $field)
- {
- if ($field != $index)
- {
- $final[$field][] = 'WHEN '.$index.' = '.$val[$index].' THEN '.$val[$field];
- }
- }
- }
-
- $sql = "UPDATE ".$table." SET ";
- $cases = '';
-
- foreach ($final as $k => $v)
- {
- $cases .= $k.' = CASE '."\n";
- foreach ($v as $row)
- {
- $cases .= $row."\n";
- }
-
- $cases .= 'ELSE '.$k.' END, ';
- }
-
- $sql .= substr($cases, 0, -2);
-
- $sql .= ' WHERE '.$where.$index.' IN ('.implode(',', $ids).')';
-
- return $sql;
- }
-
- // --------------------------------------------------------------------
-
-
- /**
- * Truncate statement
- *
- * Generates a platform-specific truncate string from the supplied data
- * If the database does not support the truncate() command
- * This function maps to "DELETE FROM table"
- *
- * @access public
- * @param string the table name
- * @return string
- */
- function _truncate($table)
- {
- return "TRUNCATE ".$table;
- }
-
- // --------------------------------------------------------------------
-
- /**
- * Delete statement
- *
- * Generates a platform-specific delete string from the supplied data
- *
- * @access public
- * @param string the table name
- * @param array the where clause
- * @param string the limit clause
- * @return string
- */
- function _delete($table, $where = array(), $like = array(), $limit = FALSE)
- {
- $conditions = '';
-
- if (count($where) > 0 OR count($like) > 0)
- {
- $conditions = "\nWHERE ";
- $conditions .= implode("\n", $this->ar_where);
-
- if (count($where) > 0 && count($like) > 0)
- {
- $conditions .= " AND ";
- }
- $conditions .= implode("\n", $like);
- }
-
- $limit = ( ! $limit) ? '' : ' LIMIT '.$limit;
-
- return "DELETE FROM ".$table.$conditions.$limit;
- }
-
- // --------------------------------------------------------------------
-
- /**
- * Limit string
- *
- * Generates a platform-specific LIMIT clause
- *
- * @access public
- * @param string the sql query string
- * @param integer the number of rows to limit the query to
- * @param integer the offset value
- * @return string
- */
- function _limit($sql, $limit, $offset)
- {
- if ($offset == 0)
- {
- $offset = '';
- }
- else
- {
- $offset .= ", ";
- }
-
- return $sql."LIMIT ".$offset.$limit;
+ return implode(', ', $this->qb_from);
}
// --------------------------------------------------------------------
@@ -763,17 +482,13 @@ class CI_DB_mysql_driver extends CI_DB {
/**
* Close DB Connection
*
- * @access public
- * @param resource
* @return void
*/
- function _close($conn_id)
+ protected function _close()
{
- @mysql_close($conn_id);
+ // Error suppression to avoid annoying E_WARNINGs in cases
+ // where the connection has already been closed for some reason.
+ @mysql_close($this->conn_id);
}
}
-
-
-/* End of file mysql_driver.php */
-/* Location: ./system/database/drivers/mysql/mysql_driver.php */ \ No newline at end of file
diff --git a/system/database/drivers/mysql/mysql_forge.php b/system/database/drivers/mysql/mysql_forge.php
index bc419d1bd..410ea2d0c 100644
--- a/system/database/drivers/mysql/mysql_forge.php
+++ b/system/database/drivers/mysql/mysql_forge.php
@@ -1,144 +1,124 @@
-<?php if ( ! defined('BASEPATH')) exit('No direct script access allowed');
+<?php
/**
* CodeIgniter
*
- * An open source application development framework for PHP 5.1.6 or newer
+ * An open source application development framework for PHP
*
- * @package CodeIgniter
- * @author ExpressionEngine Dev Team
- * @copyright Copyright (c) 2008 - 2014, EllisLab, Inc.
- * @license http://codeigniter.com/user_guide/license.html
- * @link http://codeigniter.com
- * @since Version 1.0
+ * This content is released under the MIT License (MIT)
+ *
+ * Copyright (c) 2019 - 2022, CodeIgniter Foundation
+ *
+ * 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.
+ *
+ * @package CodeIgniter
+ * @author EllisLab Dev Team
+ * @copyright Copyright (c) 2008 - 2014, EllisLab, Inc. (https://ellislab.com/)
+ * @copyright Copyright (c) 2014 - 2019, British Columbia Institute of Technology (https://bcit.ca/)
+ * @copyright Copyright (c) 2019 - 2022, CodeIgniter Foundation (https://codeigniter.com/)
+ * @license https://opensource.org/licenses/MIT MIT License
+ * @link https://codeigniter.com
+ * @since Version 1.0.0
* @filesource
*/
-
-// ------------------------------------------------------------------------
+defined('BASEPATH') OR exit('No direct script access allowed');
/**
* MySQL Forge Class
*
* @category Database
- * @author ExpressionEngine Dev Team
- * @link http://codeigniter.com/user_guide/database/
+ * @author EllisLab Dev Team
+ * @link https://codeigniter.com/userguide3/database/
*/
class CI_DB_mysql_forge extends CI_DB_forge {
/**
- * Create database
+ * CREATE DATABASE statement
*
- * @access private
- * @param string the database name
- * @return bool
+ * @var string
*/
- function _create_database($name)
- {
- return "CREATE DATABASE ".$name;
- }
+ protected $_create_database = 'CREATE DATABASE %s CHARACTER SET %s COLLATE %s';
- // --------------------------------------------------------------------
+ /**
+ * CREATE TABLE keys flag
+ *
+ * Whether table keys are created from within the
+ * CREATE TABLE statement.
+ *
+ * @var bool
+ */
+ protected $_create_table_keys = TRUE;
/**
- * Drop database
+ * UNSIGNED support
*
- * @access private
- * @param string the database name
- * @return bool
+ * @var array
*/
- function _drop_database($name)
- {
- return "DROP DATABASE ".$name;
- }
+ protected $_unsigned = array(
+ 'TINYINT',
+ 'SMALLINT',
+ 'MEDIUMINT',
+ 'INT',
+ 'INTEGER',
+ 'BIGINT',
+ 'REAL',
+ 'DOUBLE',
+ 'DOUBLE PRECISION',
+ 'FLOAT',
+ 'DECIMAL',
+ 'NUMERIC'
+ );
+
+ /**
+ * NULL value representation in CREATE/ALTER TABLE statements
+ *
+ * @var string
+ */
+ protected $_null = 'NULL';
// --------------------------------------------------------------------
/**
- * Process Fields
+ * CREATE TABLE attributes
*
- * @access private
- * @param mixed the fields
+ * @param array $attributes Associative array of table attributes
* @return string
*/
- function _process_fields($fields)
+ protected function _create_table_attr($attributes)
{
- $current_field_count = 0;
$sql = '';
- foreach ($fields as $field=>$attributes)
+ foreach (array_keys($attributes) as $key)
{
- // Numeric field names aren't allowed in databases, so if the key is
- // numeric, we know it was assigned by PHP and the developer manually
- // entered the field information, so we'll simply add it to the list
- if (is_numeric($field))
+ if (is_string($key))
{
- $sql .= "\n\t$attributes";
+ $sql .= ' '.strtoupper($key).' = '.$attributes[$key];
}
- else
- {
- $attributes = array_change_key_case($attributes, CASE_UPPER);
-
- $sql .= "\n\t".$this->db->_protect_identifiers($field);
-
- if (array_key_exists('NAME', $attributes))
- {
- $sql .= ' '.$this->db->_protect_identifiers($attributes['NAME']).' ';
- }
-
- if (array_key_exists('TYPE', $attributes))
- {
- $sql .= ' '.$attributes['TYPE'];
-
- if (array_key_exists('CONSTRAINT', $attributes))
- {
- switch ($attributes['TYPE'])
- {
- case 'decimal':
- case 'float':
- case 'numeric':
- $sql .= '('.implode(',', $attributes['CONSTRAINT']).')';
- break;
-
- case 'enum':
- case 'set':
- $sql .= '("'.implode('","', $attributes['CONSTRAINT']).'")';
- break;
-
- default:
- $sql .= '('.$attributes['CONSTRAINT'].')';
- }
- }
- }
-
- if (array_key_exists('UNSIGNED', $attributes) && $attributes['UNSIGNED'] === TRUE)
- {
- $sql .= ' UNSIGNED';
- }
-
- if (array_key_exists('DEFAULT', $attributes))
- {
- $sql .= ' DEFAULT \''.$attributes['DEFAULT'].'\'';
- }
-
- if (array_key_exists('NULL', $attributes) && $attributes['NULL'] === TRUE)
- {
- $sql .= ' NULL';
- }
- else
- {
- $sql .= ' NOT NULL';
- }
+ }
- if (array_key_exists('AUTO_INCREMENT', $attributes) && $attributes['AUTO_INCREMENT'] === TRUE)
- {
- $sql .= ' AUTO_INCREMENT';
- }
- }
+ if ( ! empty($this->db->char_set) && ! strpos($sql, 'CHARACTER SET') && ! strpos($sql, 'CHARSET'))
+ {
+ $sql .= ' DEFAULT CHARACTER SET = '.$this->db->char_set;
+ }
- // don't add a comma on the end of the last field
- if (++$current_field_count < count($fields))
- {
- $sql .= ',';
- }
+ if ( ! empty($this->db->dbcollat) && ! strpos($sql, 'COLLATE'))
+ {
+ $sql .= ' COLLATE = '.$this->db->dbcollat;
}
return $sql;
@@ -147,127 +127,117 @@ class CI_DB_mysql_forge extends CI_DB_forge {
// --------------------------------------------------------------------
/**
- * Create Table
+ * ALTER TABLE
*
- * @access private
- * @param string the table name
- * @param mixed the fields
- * @param mixed primary key(s)
- * @param mixed key(s)
- * @param boolean should 'IF NOT EXISTS' be added to the SQL
- * @return bool
+ * @param string $alter_type ALTER type
+ * @param string $table Table name
+ * @param mixed $field Column definition
+ * @return string|string[]
*/
- function _create_table($table, $fields, $primary_keys, $keys, $if_not_exists)
+ protected function _alter_table($alter_type, $table, $field)
{
- $sql = 'CREATE TABLE ';
-
- if ($if_not_exists === TRUE)
+ if ($alter_type === 'DROP')
{
- $sql .= 'IF NOT EXISTS ';
+ return parent::_alter_table($alter_type, $table, $field);
}
- $sql .= $this->db->_escape_identifiers($table)." (";
-
- $sql .= $this->_process_fields($fields);
-
- if (count($primary_keys) > 0)
+ $sql = 'ALTER TABLE '.$this->db->escape_identifiers($table);
+ for ($i = 0, $c = count($field); $i < $c; $i++)
{
- $key_name = $this->db->_protect_identifiers(implode('_', $primary_keys));
- $primary_keys = $this->db->_protect_identifiers($primary_keys);
- $sql .= ",\n\tPRIMARY KEY ".$key_name." (" . implode(', ', $primary_keys) . ")";
- }
-
- if (is_array($keys) && count($keys) > 0)
- {
- foreach ($keys as $key)
+ if ($field[$i]['_literal'] !== FALSE)
+ {
+ $field[$i] = ($alter_type === 'ADD')
+ ? "\n\tADD ".$field[$i]['_literal']
+ : "\n\tMODIFY ".$field[$i]['_literal'];
+ }
+ else
{
- if (is_array($key))
+ if ($alter_type === 'ADD')
{
- $key_name = $this->db->_protect_identifiers(implode('_', $key));
- $key = $this->db->_protect_identifiers($key);
+ $field[$i]['_literal'] = "\n\tADD ";
}
else
{
- $key_name = $this->db->_protect_identifiers($key);
- $key = array($key_name);
+ $field[$i]['_literal'] = empty($field[$i]['new_name']) ? "\n\tMODIFY " : "\n\tCHANGE ";
}
- $sql .= ",\n\tKEY {$key_name} (" . implode(', ', $key) . ")";
+ $field[$i] = $field[$i]['_literal'].$this->_process_column($field[$i]);
}
}
- $sql .= "\n) DEFAULT CHARACTER SET {$this->db->char_set} COLLATE {$this->db->dbcollat};";
-
- return $sql;
+ return array($sql.implode(',', $field));
}
// --------------------------------------------------------------------
/**
- * Drop Table
+ * Process column
*
- * @access private
+ * @param array $field
* @return string
*/
- function _drop_table($table)
+ protected function _process_column($field)
{
- return "DROP TABLE IF EXISTS ".$this->db->_escape_identifiers($table);
+ $extra_clause = isset($field['after'])
+ ? ' AFTER '.$this->db->escape_identifiers($field['after']) : '';
+
+ if (empty($extra_clause) && isset($field['first']) && $field['first'] === TRUE)
+ {
+ $extra_clause = ' FIRST';
+ }
+
+ return $this->db->escape_identifiers($field['name'])
+ .(empty($field['new_name']) ? '' : ' '.$this->db->escape_identifiers($field['new_name']))
+ .' '.$field['type'].$field['length']
+ .$field['unsigned']
+ .$field['null']
+ .$field['default']
+ .$field['auto_increment']
+ .$field['unique']
+ .(empty($field['comment']) ? '' : ' COMMENT '.$field['comment'])
+ .$extra_clause;
}
// --------------------------------------------------------------------
/**
- * Alter table query
- *
- * Generates a platform-specific query so that a table can be altered
- * Called by add_column(), drop_column(), and column_alter(),
+ * Process indexes
*
- * @access private
- * @param string the ALTER type (ADD, DROP, CHANGE)
- * @param string the column name
- * @param array fields
- * @param string the field after which we should add the new field
- * @return object
+ * @param string $table (ignored)
+ * @return string
*/
- function _alter_table($alter_type, $table, $fields, $after_field = '')
+ protected function _process_indexes($table)
{
- $sql = 'ALTER TABLE '.$this->db->_protect_identifiers($table)." $alter_type ";
+ $sql = '';
- // DROP has everything it needs now.
- if ($alter_type == 'DROP')
+ for ($i = 0, $c = count($this->keys); $i < $c; $i++)
{
- return $sql.$this->db->_protect_identifiers($fields);
- }
+ if (is_array($this->keys[$i]))
+ {
+ for ($i2 = 0, $c2 = count($this->keys[$i]); $i2 < $c2; $i2++)
+ {
+ if ( ! isset($this->fields[$this->keys[$i][$i2]]))
+ {
+ unset($this->keys[$i][$i2]);
+ continue;
+ }
+ }
+ }
+ elseif ( ! isset($this->fields[$this->keys[$i]]))
+ {
+ unset($this->keys[$i]);
+ continue;
+ }
- $sql .= $this->_process_fields($fields);
+ is_array($this->keys[$i]) OR $this->keys[$i] = array($this->keys[$i]);
- if ($after_field != '')
- {
- $sql .= ' AFTER ' . $this->db->_protect_identifiers($after_field);
+ $sql .= ",\n\tKEY ".$this->db->escape_identifiers(implode('_', $this->keys[$i]))
+ .' ('.implode(', ', $this->db->escape_identifiers($this->keys[$i])).')';
}
- return $sql;
- }
-
- // --------------------------------------------------------------------
+ $this->keys = array();
- /**
- * Rename a table
- *
- * Generates a platform-specific query so that a table can be renamed
- *
- * @access private
- * @param string the old table name
- * @param string the new table name
- * @return string
- */
- function _rename_table($table_name, $new_table_name)
- {
- $sql = 'ALTER TABLE '.$this->db->_protect_identifiers($table_name)." RENAME TO ".$this->db->_protect_identifiers($new_table_name);
return $sql;
}
}
-
-/* End of file mysql_forge.php */
-/* Location: ./system/database/drivers/mysql/mysql_forge.php */ \ No newline at end of file
diff --git a/system/database/drivers/mysql/mysql_result.php b/system/database/drivers/mysql/mysql_result.php
index 27a0132d0..05fc36ea7 100644
--- a/system/database/drivers/mysql/mysql_result.php
+++ b/system/database/drivers/mysql/mysql_result.php
@@ -1,19 +1,42 @@
-<?php if ( ! defined('BASEPATH')) exit('No direct script access allowed');
+<?php
/**
* CodeIgniter
*
- * An open source application development framework for PHP 5.1.6 or newer
+ * An open source application development framework for PHP
*
- * @package CodeIgniter
- * @author ExpressionEngine Dev Team
- * @copyright Copyright (c) 2008 - 2014, EllisLab, Inc.
- * @license http://codeigniter.com/user_guide/license.html
- * @link http://codeigniter.com
- * @since Version 1.0
+ * This content is released under the MIT License (MIT)
+ *
+ * Copyright (c) 2019 - 2022, CodeIgniter Foundation
+ *
+ * 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.
+ *
+ * @package CodeIgniter
+ * @author EllisLab Dev Team
+ * @copyright Copyright (c) 2008 - 2014, EllisLab, Inc. (https://ellislab.com/)
+ * @copyright Copyright (c) 2014 - 2019, British Columbia Institute of Technology (https://bcit.ca/)
+ * @copyright Copyright (c) 2019 - 2022, CodeIgniter Foundation (https://codeigniter.com/)
+ * @license https://opensource.org/licenses/MIT MIT License
+ * @link https://codeigniter.com
+ * @since Version 1.0.0
* @filesource
*/
-
-// --------------------------------------------------------------------
+defined('BASEPATH') OR exit('No direct script access allowed');
/**
* MySQL Result Class
@@ -21,20 +44,36 @@
* This class extends the parent result class: CI_DB_result
*
* @category Database
- * @author ExpressionEngine Dev Team
- * @link http://codeigniter.com/user_guide/database/
+ * @author EllisLab Dev Team
+ * @link https://codeigniter.com/userguide3/database/
*/
class CI_DB_mysql_result extends CI_DB_result {
/**
+ * Class constructor
+ *
+ * @param object &$driver_object
+ * @return void
+ */
+ public function __construct(&$driver_object)
+ {
+ parent::__construct($driver_object);
+
+ // Required, due to mysql_data_seek() causing nightmares
+ // with empty result sets
+ $this->num_rows = mysql_num_rows($this->result_id);
+ }
+
+ // --------------------------------------------------------------------
+
+ /**
* Number of rows in the result set
*
- * @access public
- * @return integer
+ * @return int
*/
- function num_rows()
+ public function num_rows()
{
- return @mysql_num_rows($this->result_id);
+ return $this->num_rows;
}
// --------------------------------------------------------------------
@@ -42,12 +81,11 @@ class CI_DB_mysql_result extends CI_DB_result {
/**
* Number of fields in the result set
*
- * @access public
- * @return integer
+ * @return int
*/
- function num_fields()
+ public function num_fields()
{
- return @mysql_num_fields($this->result_id);
+ return mysql_num_fields($this->result_id);
}
// --------------------------------------------------------------------
@@ -57,12 +95,12 @@ class CI_DB_mysql_result extends CI_DB_result {
*
* Generates an array of column names
*
- * @access public
* @return array
*/
- function list_fields()
+ public function list_fields()
{
$field_names = array();
+ mysql_field_seek($this->result_id, 0);
while ($field = mysql_fetch_field($this->result_id))
{
$field_names[] = $field->name;
@@ -78,27 +116,18 @@ class CI_DB_mysql_result extends CI_DB_result {
*
* Generates an array of objects containing field meta-data
*
- * @access public
* @return array
*/
- function field_data()
+ public function field_data()
{
$retval = array();
- while ($field = mysql_fetch_object($this->result_id))
+ for ($i = 0, $c = $this->num_fields(); $i < $c; $i++)
{
- preg_match('/([a-zA-Z]+)(\(\d+\))?/', $field->Type, $matches);
-
- $type = (array_key_exists(1, $matches)) ? $matches[1] : NULL;
- $length = (array_key_exists(2, $matches)) ? preg_replace('/[^\d]/', '', $matches[2]) : NULL;
-
- $F = new stdClass();
- $F->name = $field->Field;
- $F->type = $type;
- $F->default = $field->Default;
- $F->max_length = $length;
- $F->primary_key = ( $field->Key == 'PRI' ? 1 : 0 );
-
- $retval[] = $F;
+ $retval[$i] = new stdClass();
+ $retval[$i]->name = mysql_field_name($this->result_id, $i);
+ $retval[$i]->type = mysql_field_type($this->result_id, $i);
+ $retval[$i]->max_length = mysql_field_len($this->result_id, $i);
+ $retval[$i]->primary_key = (int) (strpos(mysql_field_flags($this->result_id, $i), 'primary_key') !== FALSE);
}
return $retval;
@@ -109,9 +138,9 @@ class CI_DB_mysql_result extends CI_DB_result {
/**
* Free the result
*
- * @return null
+ * @return void
*/
- function free_result()
+ public function free_result()
{
if (is_resource($this->result_id))
{
@@ -125,16 +154,18 @@ class CI_DB_mysql_result extends CI_DB_result {
/**
* Data Seek
*
- * Moves the internal pointer to the desired offset. We call
+ * Moves the internal pointer to the desired offset. We call
* this internally before fetching results to make sure the
- * result set starts at zero
+ * result set starts at zero.
*
- * @access private
- * @return array
+ * @param int $n
+ * @return bool
*/
- function _data_seek($n = 0)
+ public function data_seek($n = 0)
{
- return mysql_data_seek($this->result_id, $n);
+ return $this->num_rows
+ ? mysql_data_seek($this->result_id, $n)
+ : FALSE;
}
// --------------------------------------------------------------------
@@ -144,10 +175,9 @@ class CI_DB_mysql_result extends CI_DB_result {
*
* Returns the result set as an array
*
- * @access private
* @return array
*/
- function _fetch_assoc()
+ protected function _fetch_assoc()
{
return mysql_fetch_assoc($this->result_id);
}
@@ -159,16 +189,12 @@ class CI_DB_mysql_result extends CI_DB_result {
*
* Returns the result set as an object
*
- * @access private
+ * @param string $class_name
* @return object
*/
- function _fetch_object()
+ protected function _fetch_object($class_name = 'stdClass')
{
- return mysql_fetch_object($this->result_id);
+ return mysql_fetch_object($this->result_id, $class_name);
}
}
-
-
-/* End of file mysql_result.php */
-/* Location: ./system/database/drivers/mysql/mysql_result.php */ \ No newline at end of file
diff --git a/system/database/drivers/mysql/mysql_utility.php b/system/database/drivers/mysql/mysql_utility.php
index f22c3d043..0564a5a39 100644
--- a/system/database/drivers/mysql/mysql_utility.php
+++ b/system/database/drivers/mysql/mysql_utility.php
@@ -1,83 +1,84 @@
-<?php if ( ! defined('BASEPATH')) exit('No direct script access allowed');
+<?php
/**
* CodeIgniter
*
- * An open source application development framework for PHP 5.1.6 or newer
+ * An open source application development framework for PHP
*
- * @package CodeIgniter
- * @author ExpressionEngine Dev Team
- * @copyright Copyright (c) 2008 - 2014, EllisLab, Inc.
- * @license http://codeigniter.com/user_guide/license.html
- * @link http://codeigniter.com
- * @since Version 1.0
+ * This content is released under the MIT License (MIT)
+ *
+ * Copyright (c) 2019 - 2022, CodeIgniter Foundation
+ *
+ * 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.
+ *
+ * @package CodeIgniter
+ * @author EllisLab Dev Team
+ * @copyright Copyright (c) 2008 - 2014, EllisLab, Inc. (https://ellislab.com/)
+ * @copyright Copyright (c) 2014 - 2019, British Columbia Institute of Technology (https://bcit.ca/)
+ * @copyright Copyright (c) 2019 - 2022, CodeIgniter Foundation (https://codeigniter.com/)
+ * @license https://opensource.org/licenses/MIT MIT License
+ * @link https://codeigniter.com
+ * @since Version 1.0.0
* @filesource
*/
-
-// ------------------------------------------------------------------------
+defined('BASEPATH') OR exit('No direct script access allowed');
/**
* MySQL Utility Class
*
* @category Database
- * @author ExpressionEngine Dev Team
- * @link http://codeigniter.com/user_guide/database/
+ * @author EllisLab Dev Team
+ * @link https://codeigniter.com/userguide3/database/
*/
class CI_DB_mysql_utility extends CI_DB_utility {
/**
- * List databases
+ * List databases statement
*
- * @access private
- * @return bool
+ * @var string
*/
- function _list_databases()
- {
- return "SHOW DATABASES";
- }
-
- // --------------------------------------------------------------------
+ protected $_list_databases = 'SHOW DATABASES';
/**
- * Optimize table query
+ * OPTIMIZE TABLE statement
*
- * Generates a platform-specific query so that a table can be optimized
- *
- * @access private
- * @param string the table name
- * @return object
+ * @var string
*/
- function _optimize_table($table)
- {
- return "OPTIMIZE TABLE ".$this->db->_escape_identifiers($table);
- }
-
- // --------------------------------------------------------------------
+ protected $_optimize_table = 'OPTIMIZE TABLE %s';
/**
- * Repair table query
+ * REPAIR TABLE statement
*
- * Generates a platform-specific query so that a table can be repaired
- *
- * @access private
- * @param string the table name
- * @return object
+ * @var string
*/
- function _repair_table($table)
- {
- return "REPAIR TABLE ".$this->db->_escape_identifiers($table);
- }
+ protected $_repair_table = 'REPAIR TABLE %s';
// --------------------------------------------------------------------
+
/**
- * MySQL Export
+ * Export
*
- * @access private
- * @param array Preferences
+ * @param array $params Preferences
* @return mixed
*/
- function _backup($params = array())
+ protected function _backup($params = array())
{
- if (count($params) == 0)
+ if (count($params) === 0)
{
return FALSE;
}
@@ -87,16 +88,23 @@ class CI_DB_mysql_utility extends CI_DB_utility {
// Build the output
$output = '';
- foreach ((array)$tables as $table)
+
+ // Do we need to include a statement to disable foreign key checks?
+ if ($foreign_key_checks === FALSE)
+ {
+ $output .= 'SET foreign_key_checks = 0;'.$newline;
+ }
+
+ foreach ( (array) $tables as $table)
{
// Is the table in the "ignore" list?
- if (in_array($table, (array)$ignore, TRUE))
+ if (in_array($table, (array) $ignore, TRUE))
{
continue;
}
// Get the table schema
- $query = $this->db->query("SHOW CREATE TABLE `".$this->db->database.'`.`'.$table.'`');
+ $query = $this->db->query('SHOW CREATE TABLE '.$this->db->escape_identifiers($this->db->database.'.'.$table));
// No result means the table name was invalid
if ($query === FALSE)
@@ -107,9 +115,9 @@ class CI_DB_mysql_utility extends CI_DB_utility {
// Write out the table schema
$output .= '#'.$newline.'# TABLE STRUCTURE FOR: '.$table.$newline.'#'.$newline.$newline;
- if ($add_drop == TRUE)
+ if ($add_drop === TRUE)
{
- $output .= 'DROP TABLE IF EXISTS '.$table.';'.$newline.$newline;
+ $output .= 'DROP TABLE IF EXISTS '.$this->db->protect_identifiers($table).';'.$newline.$newline;
}
$i = 0;
@@ -123,21 +131,21 @@ class CI_DB_mysql_utility extends CI_DB_utility {
}
// If inserts are not needed we're done...
- if ($add_insert == FALSE)
+ if ($add_insert === FALSE)
{
continue;
}
// Grab all the data from the current table
- $query = $this->db->query("SELECT * FROM $table");
+ $query = $this->db->query('SELECT * FROM '.$this->db->protect_identifiers($table));
- if ($query->num_rows() == 0)
+ if ($query->num_rows() === 0)
{
continue;
}
// Fetch the field names and determine if the field is an
- // integer type. We use this info to decide whether to
+ // integer type. We use this info to decide whether to
// surround the data with quotes or not
$i = 0;
@@ -146,20 +154,17 @@ class CI_DB_mysql_utility extends CI_DB_utility {
while ($field = mysql_fetch_field($query->result_id))
{
// Most versions of MySQL store timestamp as a string
- $is_int[$i] = (in_array(
- strtolower(mysql_field_type($query->result_id, $i)),
- array('tinyint', 'smallint', 'mediumint', 'int', 'bigint'), //, 'timestamp'),
- TRUE)
- ) ? TRUE : FALSE;
+ $is_int[$i] = in_array(strtolower(mysql_field_type($query->result_id, $i)),
+ array('tinyint', 'smallint', 'mediumint', 'int', 'bigint'), //, 'timestamp'),
+ TRUE);
// Create a string of field names
- $field_str .= '`'.$field->name.'`, ';
+ $field_str .= $this->db->escape_identifiers($field->name).', ';
$i++;
}
// Trim off the end comma
- $field_str = preg_replace( "/, $/" , "" , $field_str);
-
+ $field_str = preg_replace('/, $/' , '', $field_str);
// Build the insert string
foreach ($query->result_array() as $row)
@@ -177,14 +182,7 @@ class CI_DB_mysql_utility extends CI_DB_utility {
else
{
// Escape the data if it's not an integer
- if ($is_int[$i] == FALSE)
- {
- $val_str .= $this->db->escape($v);
- }
- else
- {
- $val_str .= $v;
- }
+ $val_str .= ($is_int[$i] === FALSE) ? $this->db->escape($v) : $v;
}
// Append a comma
@@ -193,18 +191,22 @@ class CI_DB_mysql_utility extends CI_DB_utility {
}
// Remove the comma at the end of the string
- $val_str = preg_replace( "/, $/" , "" , $val_str);
+ $val_str = preg_replace('/, $/' , '', $val_str);
// Build the INSERT string
- $output .= 'INSERT INTO '.$table.' ('.$field_str.') VALUES ('.$val_str.');'.$newline;
+ $output .= 'INSERT INTO '.$this->db->protect_identifiers($table).' ('.$field_str.') VALUES ('.$val_str.');'.$newline;
}
$output .= $newline.$newline;
}
+ // Do we need to include a statement to re-enable foreign key checks?
+ if ($foreign_key_checks === FALSE)
+ {
+ $output .= 'SET foreign_key_checks = 1;'.$newline;
+ }
+
return $output;
}
-}
-/* End of file mysql_utility.php */
-/* Location: ./system/database/drivers/mysql/mysql_utility.php */ \ No newline at end of file
+}
diff --git a/system/database/drivers/mysqli/index.html b/system/database/drivers/mysqli/index.html
index c942a79ce..bcb7cae34 100644
--- a/system/database/drivers/mysqli/index.html
+++ b/system/database/drivers/mysqli/index.html
@@ -1,4 +1,5 @@
-<html>
+<!DOCTYPE html>
+<html lang="en">
<head>
<title>403 Forbidden</title>
</head>
@@ -7,4 +8,4 @@
<p>Directory access is forbidden.</p>
</body>
-</html> \ No newline at end of file
+</html>
diff --git a/system/database/drivers/mysqli/mysqli_driver.php b/system/database/drivers/mysqli/mysqli_driver.php
index a0896d8aa..61e7adee3 100644
--- a/system/database/drivers/mysqli/mysqli_driver.php
+++ b/system/database/drivers/mysqli/mysqli_driver.php
@@ -1,413 +1,331 @@
-<?php if ( ! defined('BASEPATH')) exit('No direct script access allowed');
+<?php
/**
* CodeIgniter
*
- * An open source application development framework for PHP 5.1.6 or newer
+ * An open source application development framework for PHP
*
- * @package CodeIgniter
- * @author ExpressionEngine Dev Team
- * @copyright Copyright (c) 2008 - 2014, EllisLab, Inc.
- * @license http://codeigniter.com/user_guide/license.html
- * @link http://codeigniter.com
- * @since Version 1.0
+ * This content is released under the MIT License (MIT)
+ *
+ * Copyright (c) 2019 - 2022, CodeIgniter Foundation
+ *
+ * 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.
+ *
+ * @package CodeIgniter
+ * @author EllisLab Dev Team
+ * @copyright Copyright (c) 2008 - 2014, EllisLab, Inc. (https://ellislab.com/)
+ * @copyright Copyright (c) 2014 - 2019, British Columbia Institute of Technology (https://bcit.ca/)
+ * @copyright Copyright (c) 2019 - 2022, CodeIgniter Foundation (https://codeigniter.com/)
+ * @license https://opensource.org/licenses/MIT MIT License
+ * @link https://codeigniter.com
+ * @since Version 1.3.0
* @filesource
*/
-
-// ------------------------------------------------------------------------
+defined('BASEPATH') OR exit('No direct script access allowed');
/**
- * MySQLi Database Adapter Class - MySQLi only works with PHP 5
+ * MySQLi Database Adapter Class
*
* Note: _DB is an extender class that the app controller
- * creates dynamically based on whether the active record
+ * creates dynamically based on whether the query builder
* class is being used or not.
*
* @package CodeIgniter
* @subpackage Drivers
* @category Database
- * @author ExpressionEngine Dev Team
- * @link http://codeigniter.com/user_guide/database/
+ * @author EllisLab Dev Team
+ * @link https://codeigniter.com/userguide3/database/
*/
class CI_DB_mysqli_driver extends CI_DB {
- var $dbdriver = 'mysqli';
-
- // The character used for escaping
- var $_escape_char = '`';
-
- // clause and character used for LIKE escape sequences - not used in MySQL
- var $_like_escape_str = '';
- var $_like_escape_chr = '';
+ /**
+ * Database driver
+ *
+ * @var string
+ */
+ public $dbdriver = 'mysqli';
/**
- * The syntax to count rows is slightly different across different
- * database engines, so this string appears in each driver and is
- * used for the count_all() and count_all_results() functions.
+ * Compression flag
+ *
+ * @var bool
*/
- var $_count_string = "SELECT COUNT(*) AS ";
- var $_random_keyword = ' RAND()'; // database specific random keyword
+ public $compress = FALSE;
/**
+ * DELETE hack flag
+ *
* Whether to use the MySQL "delete hack" which allows the number
* of affected rows to be shown. Uses a preg_replace when enabled,
* adding a bit more processing to all queries.
- */
- var $delete_hack = TRUE;
-
- // whether SET NAMES must be used to set the character set
- var $use_set_names;
-
- // --------------------------------------------------------------------
-
- /**
- * Non-persistent database connection
*
- * @access private called by the base class
- * @return resource
+ * @var bool
*/
- function db_connect()
- {
- if ($this->port != '')
- {
- return @mysqli_connect($this->hostname, $this->username, $this->password, $this->database, $this->port);
- }
- else
- {
- return @mysqli_connect($this->hostname, $this->username, $this->password, $this->database);
- }
-
- }
-
- // --------------------------------------------------------------------
+ public $delete_hack = TRUE;
/**
- * Persistent database connection
+ * Strict ON flag
+ *
+ * Whether we're running in strict SQL mode.
*
- * @access private called by the base class
- * @return resource
+ * @var bool
*/
- function db_pconnect()
- {
- return $this->db_connect();
- }
+ public $stricton;
// --------------------------------------------------------------------
/**
- * Reconnect
- *
- * Keep / reestablish the db connection if no queries have been
- * sent for a length of time exceeding the server's idle timeout
+ * Identifier escape character
*
- * @access public
- * @return void
+ * @var string
*/
- function reconnect()
- {
- if (mysqli_ping($this->conn_id) === FALSE)
- {
- $this->conn_id = FALSE;
- }
- }
+ protected $_escape_char = '`';
// --------------------------------------------------------------------
/**
- * Select the database
+ * MySQLi object
*
- * @access private called by the base class
- * @return resource
+ * Has to be preserved without being assigned to $conn_id.
+ *
+ * @var MySQLi
*/
- function db_select()
- {
- return @mysqli_select_db($this->conn_id, $this->database);
- }
+ protected $_mysqli;
// --------------------------------------------------------------------
/**
- * Set client character set
+ * Database connection
*
- * @access private
- * @param string
- * @param string
- * @return resource
+ * @param bool $persistent
+ * @return object
*/
- function _db_set_charset($charset, $collation)
+ public function db_connect($persistent = FALSE)
{
- if ( ! isset($this->use_set_names))
+ // PHP 8.1 changes default error handling mode from silent to exceptions - reverse that
+ if (is_php('8.1'))
{
- // mysqli_set_charset() requires MySQL >= 5.0.7, use SET NAMES as fallback
- $this->use_set_names = (version_compare(mysqli_get_server_info($this->conn_id), '5.0.7', '>=')) ? FALSE : TRUE;
+ $mysqli_driver = new mysqli_driver();
+ $mysqli_driver->report_mode = MYSQLI_REPORT_OFF;
}
- if ($this->use_set_names === TRUE)
+ // Do we have a socket path?
+ if ($this->hostname[0] === '/')
{
- return @mysqli_query($this->conn_id, "SET NAMES '".$this->escape_str($charset)."' COLLATE '".$this->escape_str($collation)."'");
+ $hostname = NULL;
+ $port = NULL;
+ $socket = $this->hostname;
}
else
{
- return @mysqli_set_charset($this->conn_id, $charset);
+ $hostname = ($persistent === TRUE)
+ ? 'p:'.$this->hostname : $this->hostname;
+ $port = empty($this->port) ? NULL : $this->port;
+ $socket = NULL;
}
- }
-
- // --------------------------------------------------------------------
-
- /**
- * Version number query string
- *
- * @access public
- * @return string
- */
- function _version()
- {
- return "SELECT version() AS ver";
- }
- // --------------------------------------------------------------------
-
- /**
- * Execute the query
- *
- * @access private called by the base class
- * @param string an SQL query
- * @return resource
- */
- function _execute($sql)
- {
- $sql = $this->_prep_query($sql);
- $result = @mysqli_query($this->conn_id, $sql);
- return $result;
- }
+ $client_flags = ($this->compress === TRUE) ? MYSQLI_CLIENT_COMPRESS : 0;
+ $this->_mysqli = mysqli_init();
- // --------------------------------------------------------------------
+ $this->_mysqli->options(MYSQLI_OPT_CONNECT_TIMEOUT, 10);
- /**
- * Prep the query
- *
- * If needed, each database adapter can prep the query string
- *
- * @access private called by execute()
- * @param string an SQL query
- * @return string
- */
- function _prep_query($sql)
- {
- // "DELETE FROM TABLE" returns 0 affected rows This hack modifies
- // the query so that it returns the number of affected rows
- if ($this->delete_hack === TRUE)
+ if (isset($this->stricton))
{
- if (preg_match('/^\s*DELETE\s+FROM\s+(\S+)\s*$/i', $sql))
+ if ($this->stricton)
{
- $sql = preg_replace("/^\s*DELETE\s+FROM\s+(\S+)\s*$/", "DELETE FROM \\1 WHERE 1=1", $sql);
+ $this->_mysqli->options(MYSQLI_INIT_COMMAND, 'SET SESSION sql_mode = CONCAT(@@sql_mode, ",", "STRICT_ALL_TABLES")');
+ }
+ else
+ {
+ $this->_mysqli->options(MYSQLI_INIT_COMMAND,
+ 'SET SESSION sql_mode =
+ REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(
+ @@sql_mode,
+ "STRICT_ALL_TABLES,", ""),
+ ",STRICT_ALL_TABLES", ""),
+ "STRICT_ALL_TABLES", ""),
+ "STRICT_TRANS_TABLES,", ""),
+ ",STRICT_TRANS_TABLES", ""),
+ "STRICT_TRANS_TABLES", "")'
+ );
}
}
- return $sql;
- }
+ if (is_array($this->encrypt))
+ {
+ $ssl = array();
+ empty($this->encrypt['ssl_key']) OR $ssl['key'] = $this->encrypt['ssl_key'];
+ empty($this->encrypt['ssl_cert']) OR $ssl['cert'] = $this->encrypt['ssl_cert'];
+ empty($this->encrypt['ssl_ca']) OR $ssl['ca'] = $this->encrypt['ssl_ca'];
+ empty($this->encrypt['ssl_capath']) OR $ssl['capath'] = $this->encrypt['ssl_capath'];
+ empty($this->encrypt['ssl_cipher']) OR $ssl['cipher'] = $this->encrypt['ssl_cipher'];
+
+ if (isset($this->encrypt['ssl_verify']))
+ {
+ $client_flags |= MYSQLI_CLIENT_SSL;
- // --------------------------------------------------------------------
+ if ($this->encrypt['ssl_verify'])
+ {
+ defined('MYSQLI_OPT_SSL_VERIFY_SERVER_CERT') && $this->_mysqli->options(MYSQLI_OPT_SSL_VERIFY_SERVER_CERT, TRUE);
+ }
+ // Apparently (when it exists), setting MYSQLI_OPT_SSL_VERIFY_SERVER_CERT
+ // to FALSE didn't do anything, so PHP 5.6.16 introduced yet another
+ // constant ...
+ //
+ // https://secure.php.net/ChangeLog-5.php#5.6.16
+ // https://bugs.php.net/bug.php?id=68344
+ elseif (defined('MYSQLI_CLIENT_SSL_DONT_VERIFY_SERVER_CERT'))
+ {
+ $client_flags |= MYSQLI_CLIENT_SSL_DONT_VERIFY_SERVER_CERT;
+ }
+ }
- /**
- * Begin Transaction
- *
- * @access public
- * @return bool
- */
- function trans_begin($test_mode = FALSE)
- {
- if ( ! $this->trans_enabled)
- {
- return TRUE;
+ if ( ! empty($ssl))
+ {
+ $client_flags |= MYSQLI_CLIENT_SSL;
+ $this->_mysqli->ssl_set(
+ isset($ssl['key']) ? $ssl['key'] : NULL,
+ isset($ssl['cert']) ? $ssl['cert'] : NULL,
+ isset($ssl['ca']) ? $ssl['ca'] : NULL,
+ isset($ssl['capath']) ? $ssl['capath'] : NULL,
+ isset($ssl['cipher']) ? $ssl['cipher'] : NULL
+ );
+ }
}
- // When transactions are nested we only begin/commit/rollback the outermost ones
- if ($this->_trans_depth > 0)
+ if ($this->_mysqli->real_connect($hostname, $this->username, $this->password, $this->database, $port, $socket, $client_flags))
{
- return TRUE;
- }
+ // Prior to version 5.7.3, MySQL silently downgrades to an unencrypted connection if SSL setup fails
+ if (
+ ($client_flags & MYSQLI_CLIENT_SSL)
+ && version_compare($this->_mysqli->client_info, '5.7.3', '<=')
+ && empty($this->_mysqli->query("SHOW STATUS LIKE 'ssl_cipher'")->fetch_object()->Value)
+ )
+ {
+ $this->_mysqli->close();
+ $message = 'MySQLi was configured for an SSL connection, but got an unencrypted connection instead!';
+ log_message('error', $message);
+ return ($this->db_debug) ? $this->display_error($message, '', TRUE) : FALSE;
+ }
- // Reset the transaction failure flag.
- // If the $test_mode flag is set to TRUE transactions will be rolled back
- // even if the queries produce a successful result.
- $this->_trans_failure = ($test_mode === TRUE) ? TRUE : FALSE;
+ if ( ! $this->_mysqli->set_charset($this->char_set))
+ {
+ log_message('error', "Database: Unable to set the configured connection charset ('{$this->char_set}').");
+ $this->_mysqli->close();
+ return ($this->db->db_debug) ? $this->display_error('db_unable_to_set_charset', $this->char_set) : FALSE;
+ }
- $this->simple_query('SET AUTOCOMMIT=0');
- $this->simple_query('START TRANSACTION'); // can also be BEGIN or BEGIN WORK
- return TRUE;
+ return $this->_mysqli;
+ }
+
+ return FALSE;
}
// --------------------------------------------------------------------
/**
- * Commit Transaction
+ * Reconnect
*
- * @access public
- * @return bool
+ * Keep / reestablish the db connection if no queries have been
+ * sent for a length of time exceeding the server's idle timeout
+ *
+ * @return void
*/
- function trans_commit()
+ public function reconnect()
{
- if ( ! $this->trans_enabled)
- {
- return TRUE;
- }
-
- // When transactions are nested we only begin/commit/rollback the outermost ones
- if ($this->_trans_depth > 0)
+ if ($this->conn_id !== FALSE && $this->conn_id->ping() === FALSE)
{
- return TRUE;
+ $this->conn_id = FALSE;
}
-
- $this->simple_query('COMMIT');
- $this->simple_query('SET AUTOCOMMIT=1');
- return TRUE;
}
// --------------------------------------------------------------------
/**
- * Rollback Transaction
+ * Select the database
*
- * @access public
+ * @param string $database
* @return bool
*/
- function trans_rollback()
+ public function db_select($database = '')
{
- if ( ! $this->trans_enabled)
+ if ($database === '')
{
- return TRUE;
+ $database = $this->database;
}
- // When transactions are nested we only begin/commit/rollback the outermost ones
- if ($this->_trans_depth > 0)
+ if ($this->conn_id->select_db($database))
{
+ $this->database = $database;
+ $this->data_cache = array();
return TRUE;
}
- $this->simple_query('ROLLBACK');
- $this->simple_query('SET AUTOCOMMIT=1');
- return TRUE;
+ return FALSE;
}
// --------------------------------------------------------------------
/**
- * Escape String
+ * Database version number
*
- * @access public
- * @param string
- * @param bool whether or not the string will be used in a LIKE condition
* @return string
*/
- function escape_str($str, $like = FALSE)
+ public function version()
{
- if (is_array($str))
+ if (isset($this->data_cache['version']))
{
- foreach ($str as $key => $val)
- {
- $str[$key] = $this->escape_str($val, $like);
- }
-
- return $str;
- }
-
- if (function_exists('mysqli_real_escape_string') AND is_object($this->conn_id))
- {
- $str = mysqli_real_escape_string($this->conn_id, $str);
- }
- elseif (function_exists('mysql_escape_string'))
- {
- $str = mysql_escape_string($str);
- }
- else
- {
- $str = addslashes($str);
+ return $this->data_cache['version'];
}
- // escape LIKE condition wildcards
- if ($like === TRUE)
- {
- $str = str_replace(array('%', '_'), array('\\%', '\\_'), $str);
- }
-
- return $str;
+ return $this->data_cache['version'] = $this->conn_id->server_info;
}
// --------------------------------------------------------------------
/**
- * Affected Rows
- *
- * @access public
- * @return integer
- */
- function affected_rows()
- {
- return @mysqli_affected_rows($this->conn_id);
- }
-
- // --------------------------------------------------------------------
-
- /**
- * Insert ID
- *
- * @access public
- * @return integer
- */
- function insert_id()
- {
- return @mysqli_insert_id($this->conn_id);
- }
-
- // --------------------------------------------------------------------
-
- /**
- * "Count All" query
- *
- * Generates a platform-specific query string that counts all records in
- * the specified database
+ * Execute the query
*
- * @access public
- * @param string
- * @return string
+ * @param string $sql an SQL query
+ * @return mixed
*/
- function count_all($table = '')
+ protected function _execute($sql)
{
- if ($table == '')
- {
- return 0;
- }
-
- $query = $this->query($this->_count_string . $this->_protect_identifiers('numrows') . " FROM " . $this->_protect_identifiers($table, TRUE, NULL, FALSE));
-
- if ($query->num_rows() == 0)
- {
- return 0;
- }
-
- $row = $query->row();
- $this->_reset_select();
- return (int) $row->numrows;
+ return $this->conn_id->query($this->_prep_query($sql));
}
// --------------------------------------------------------------------
/**
- * List table query
+ * Prep the query
*
- * Generates a platform-specific query string so that the table names can be fetched
+ * If needed, each database adapter can prep the query string
*
- * @access private
- * @param boolean
+ * @param string $sql an SQL query
* @return string
*/
- function _list_tables($prefix_limit = FALSE)
+ protected function _prep_query($sql)
{
- $sql = "SHOW TABLES FROM ".$this->_escape_char.$this->database.$this->_escape_char;
-
- if ($prefix_limit !== FALSE AND $this->dbprefix != '')
+ // mysqli_affected_rows() returns 0 for "DELETE FROM TABLE" queries. This hack
+ // modifies the query so that it a proper number of affected rows is returned.
+ if ($this->delete_hack === TRUE && preg_match('/^\s*DELETE\s+FROM\s+(\S+)\s*$/i', $sql))
{
- $sql .= " LIKE '".$this->escape_like_str($this->dbprefix)."%'";
+ return trim($sql).' WHERE 1=1';
}
return $sql;
@@ -416,342 +334,203 @@ class CI_DB_mysqli_driver extends CI_DB {
// --------------------------------------------------------------------
/**
- * Show column query
- *
- * Generates a platform-specific query string so that the column names can be fetched
+ * Begin Transaction
*
- * @access public
- * @param string the table name
- * @return string
+ * @return bool
*/
- function _list_columns($table = '')
+ protected function _trans_begin()
{
- return "SHOW COLUMNS FROM ".$this->_protect_identifiers($table, TRUE, NULL, FALSE);
+ $this->conn_id->autocommit(FALSE);
+ return is_php('5.5')
+ ? $this->conn_id->begin_transaction()
+ : $this->simple_query('START TRANSACTION'); // can also be BEGIN or BEGIN WORK
}
// --------------------------------------------------------------------
/**
- * Field data query
- *
- * Generates a platform-specific query so that the column data can be retrieved
+ * Commit Transaction
*
- * @access public
- * @param string the table name
- * @return object
+ * @return bool
*/
- function _field_data($table)
+ protected function _trans_commit()
{
- return "DESCRIBE ".$table;
- }
-
- // --------------------------------------------------------------------
+ if ($this->conn_id->commit())
+ {
+ $this->conn_id->autocommit(TRUE);
+ return TRUE;
+ }
- /**
- * The error message string
- *
- * @access private
- * @return string
- */
- function _error_message()
- {
- return mysqli_error($this->conn_id);
+ return FALSE;
}
// --------------------------------------------------------------------
/**
- * The error message number
+ * Rollback Transaction
*
- * @access private
- * @return integer
+ * @return bool
*/
- function _error_number()
+ protected function _trans_rollback()
{
- return mysqli_errno($this->conn_id);
+ if ($this->conn_id->rollback())
+ {
+ $this->conn_id->autocommit(TRUE);
+ return TRUE;
+ }
+
+ return FALSE;
}
// --------------------------------------------------------------------
/**
- * Escape the SQL Identifiers
- *
- * This function escapes column and table names
+ * Platform-dependent string escape
*
- * @access private
* @param string
* @return string
*/
- function _escape_identifiers($item)
+ protected function _escape_str($str)
{
- if ($this->_escape_char == '')
- {
- return $item;
- }
-
- foreach ($this->_reserved_identifiers as $id)
- {
- if (strpos($item, '.'.$id) !== FALSE)
- {
- $str = $this->_escape_char. str_replace('.', $this->_escape_char.'.', $item);
-
- // remove duplicates if the user already included the escape
- return preg_replace('/['.$this->_escape_char.']+/', $this->_escape_char, $str);
- }
- }
-
- if (strpos($item, '.') !== FALSE)
- {
- $str = $this->_escape_char.str_replace('.', $this->_escape_char.'.'.$this->_escape_char, $item).$this->_escape_char;
- }
- else
- {
- $str = $this->_escape_char.$item.$this->_escape_char;
- }
-
- // remove duplicates if the user already included the escape
- return preg_replace('/['.$this->_escape_char.']+/', $this->_escape_char, $str);
+ return $this->conn_id->real_escape_string($str);
}
// --------------------------------------------------------------------
/**
- * From Tables
- *
- * This function implicitly groups FROM tables so there is no confusion
- * about operator precedence in harmony with SQL standards
+ * Affected Rows
*
- * @access public
- * @param type
- * @return type
+ * @return int
*/
- function _from_tables($tables)
+ public function affected_rows()
{
- if ( ! is_array($tables))
- {
- $tables = array($tables);
- }
-
- return '('.implode(', ', $tables).')';
+ return $this->conn_id->affected_rows;
}
// --------------------------------------------------------------------
/**
- * Insert statement
- *
- * Generates a platform-specific insert string from the supplied data
+ * Insert ID
*
- * @access public
- * @param string the table name
- * @param array the insert keys
- * @param array the insert values
- * @return string
+ * @return int
*/
- function _insert($table, $keys, $values)
+ public function insert_id()
{
- return "INSERT INTO ".$table." (".implode(', ', $keys).") VALUES (".implode(', ', $values).")";
+ return $this->conn_id->insert_id;
}
// --------------------------------------------------------------------
/**
- * Insert_batch statement
+ * List table query
*
- * Generates a platform-specific insert string from the supplied data
+ * Generates a platform-specific query string so that the table names can be fetched
*
- * @access public
- * @param string the table name
- * @param array the insert keys
- * @param array the insert values
+ * @param bool $prefix_limit
* @return string
*/
- function _insert_batch($table, $keys, $values)
+ protected function _list_tables($prefix_limit = FALSE)
{
- return "INSERT INTO ".$table." (".implode(', ', $keys).") VALUES ".implode(', ', $values);
- }
-
- // --------------------------------------------------------------------
+ $sql = 'SHOW TABLES FROM '.$this->_escape_char.$this->database.$this->_escape_char;
+ if ($prefix_limit !== FALSE && $this->dbprefix !== '')
+ {
+ return $sql." LIKE '".$this->escape_like_str($this->dbprefix)."%'";
+ }
- /**
- * Replace statement
- *
- * Generates a platform-specific replace string from the supplied data
- *
- * @access public
- * @param string the table name
- * @param array the insert keys
- * @param array the insert values
- * @return string
- */
- function _replace($table, $keys, $values)
- {
- return "REPLACE INTO ".$table." (".implode(', ', $keys).") VALUES (".implode(', ', $values).")";
+ return $sql;
}
-
+
// --------------------------------------------------------------------
/**
- * Update statement
+ * Show column query
*
- * Generates a platform-specific update string from the supplied data
+ * Generates a platform-specific query string so that the column names can be fetched
*
- * @access public
- * @param string the table name
- * @param array the update data
- * @param array the where clause
- * @param array the orderby clause
- * @param array the limit clause
+ * @param string $table
* @return string
*/
- function _update($table, $values, $where, $orderby = array(), $limit = FALSE)
+ protected function _list_columns($table = '')
{
- foreach ($values as $key => $val)
- {
- $valstr[] = $key." = ".$val;
- }
-
- $limit = ( ! $limit) ? '' : ' LIMIT '.$limit;
-
- $orderby = (count($orderby) >= 1)?' ORDER BY '.implode(", ", $orderby):'';
-
- $sql = "UPDATE ".$table." SET ".implode(', ', $valstr);
-
- $sql .= ($where != '' AND count($where) >=1) ? " WHERE ".implode(" ", $where) : '';
-
- $sql .= $orderby.$limit;
-
- return $sql;
+ return 'SHOW COLUMNS FROM '.$this->protect_identifiers($table, TRUE, NULL, FALSE);
}
// --------------------------------------------------------------------
/**
- * Update_Batch statement
- *
- * Generates a platform-specific batch update string from the supplied data
+ * Returns an object with field data
*
- * @access public
- * @param string the table name
- * @param array the update data
- * @param array the where clause
- * @return string
+ * @param string $table
+ * @return array
*/
- function _update_batch($table, $values, $index, $where = NULL)
+ public function field_data($table)
{
- $ids = array();
- $where = ($where != '' AND count($where) >=1) ? implode(" ", $where).' AND ' : '';
-
- foreach ($values as $key => $val)
+ if (($query = $this->query('SHOW COLUMNS FROM '.$this->protect_identifiers($table, TRUE, NULL, FALSE))) === FALSE)
{
- $ids[] = $val[$index];
-
- foreach (array_keys($val) as $field)
- {
- if ($field != $index)
- {
- $final[$field][] = 'WHEN '.$index.' = '.$val[$index].' THEN '.$val[$field];
- }
- }
+ return FALSE;
}
+ $query = $query->result_object();
- $sql = "UPDATE ".$table." SET ";
- $cases = '';
-
- foreach ($final as $k => $v)
+ $retval = array();
+ for ($i = 0, $c = count($query); $i < $c; $i++)
{
- $cases .= $k.' = CASE '."\n";
- foreach ($v as $row)
- {
- $cases .= $row."\n";
- }
-
- $cases .= 'ELSE '.$k.' END, ';
- }
+ $retval[$i] = new stdClass();
+ $retval[$i]->name = $query[$i]->Field;
- $sql .= substr($cases, 0, -2);
+ sscanf($query[$i]->Type, '%[a-z](%d)',
+ $retval[$i]->type,
+ $retval[$i]->max_length
+ );
- $sql .= ' WHERE '.$where.$index.' IN ('.implode(',', $ids).')';
+ $retval[$i]->default = $query[$i]->Default;
+ $retval[$i]->primary_key = (int) ($query[$i]->Key === 'PRI');
+ }
- return $sql;
+ return $retval;
}
// --------------------------------------------------------------------
/**
- * Truncate statement
+ * Error
*
- * Generates a platform-specific truncate string from the supplied data
- * If the database does not support the truncate() command
- * This function maps to "DELETE FROM table"
+ * Returns an array containing code and message of the last
+ * database error that has occurred.
*
- * @access public
- * @param string the table name
- * @return string
+ * @return array
*/
- function _truncate($table)
+ public function error()
{
- return "TRUNCATE ".$table;
- }
-
- // --------------------------------------------------------------------
-
- /**
- * Delete statement
- *
- * Generates a platform-specific delete string from the supplied data
- *
- * @access public
- * @param string the table name
- * @param array the where clause
- * @param string the limit clause
- * @return string
- */
- function _delete($table, $where = array(), $like = array(), $limit = FALSE)
- {
- $conditions = '';
-
- if (count($where) > 0 OR count($like) > 0)
+ if ( ! empty($this->_mysqli->connect_errno))
{
- $conditions = "\nWHERE ";
- $conditions .= implode("\n", $this->ar_where);
-
- if (count($where) > 0 && count($like) > 0)
- {
- $conditions .= " AND ";
- }
- $conditions .= implode("\n", $like);
+ return array(
+ 'code' => $this->_mysqli->connect_errno,
+ 'message' => $this->_mysqli->connect_error
+ );
}
- $limit = ( ! $limit) ? '' : ' LIMIT '.$limit;
-
- return "DELETE FROM ".$table.$conditions.$limit;
+ return array('code' => $this->conn_id->errno, 'message' => $this->conn_id->error);
}
// --------------------------------------------------------------------
/**
- * Limit string
+ * FROM tables
*
- * Generates a platform-specific LIMIT clause
+ * Groups tables in FROM clauses if needed, so there is no confusion
+ * about operator precedence.
*
- * @access public
- * @param string the sql query string
- * @param integer the number of rows to limit the query to
- * @param integer the offset value
* @return string
*/
- function _limit($sql, $limit, $offset)
+ protected function _from_tables()
{
- $sql .= "LIMIT ".$limit;
-
- if ($offset > 0)
+ if ( ! empty($this->qb_join) && count($this->qb_from) > 1)
{
- $sql .= " OFFSET ".$offset;
+ return '('.implode(', ', $this->qb_from).')';
}
- return $sql;
+ return implode(', ', $this->qb_from);
}
// --------------------------------------------------------------------
@@ -759,18 +538,11 @@ class CI_DB_mysqli_driver extends CI_DB {
/**
* Close DB Connection
*
- * @access public
- * @param resource
* @return void
*/
- function _close($conn_id)
+ protected function _close()
{
- @mysqli_close($conn_id);
+ $this->conn_id->close();
}
-
}
-
-
-/* End of file mysqli_driver.php */
-/* Location: ./system/database/drivers/mysqli/mysqli_driver.php */ \ No newline at end of file
diff --git a/system/database/drivers/mysqli/mysqli_forge.php b/system/database/drivers/mysqli/mysqli_forge.php
index f11cd7f26..992c7720f 100644
--- a/system/database/drivers/mysqli/mysqli_forge.php
+++ b/system/database/drivers/mysqli/mysqli_forge.php
@@ -1,129 +1,126 @@
-<?php if ( ! defined('BASEPATH')) exit('No direct script access allowed');
+<?php
/**
* CodeIgniter
*
- * An open source application development framework for PHP 5.1.6 or newer
+ * An open source application development framework for PHP
*
- * @package CodeIgniter
- * @author ExpressionEngine Dev Team
- * @copyright Copyright (c) 2008 - 2014, EllisLab, Inc.
- * @license http://codeigniter.com/user_guide/license.html
- * @link http://codeigniter.com
- * @since Version 1.0
+ * This content is released under the MIT License (MIT)
+ *
+ * Copyright (c) 2019 - 2022, CodeIgniter Foundation
+ *
+ * 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.
+ *
+ * @package CodeIgniter
+ * @author EllisLab Dev Team
+ * @copyright Copyright (c) 2008 - 2014, EllisLab, Inc. (https://ellislab.com/)
+ * @copyright Copyright (c) 2014 - 2019, British Columbia Institute of Technology (https://bcit.ca/)
+ * @copyright Copyright (c) 2019 - 2022, CodeIgniter Foundation (https://codeigniter.com/)
+ * @license https://opensource.org/licenses/MIT MIT License
+ * @link https://codeigniter.com
+ * @since Version 1.3.0
* @filesource
*/
-
-// ------------------------------------------------------------------------
+defined('BASEPATH') OR exit('No direct script access allowed');
/**
* MySQLi Forge Class
*
+ * @package CodeIgniter
+ * @subpackage Drivers
* @category Database
- * @author ExpressionEngine Dev Team
- * @link http://codeigniter.com/user_guide/database/
+ * @author EllisLab Dev Team
+ * @link https://codeigniter.com/userguide3/database/
*/
class CI_DB_mysqli_forge extends CI_DB_forge {
/**
- * Create database
+ * CREATE DATABASE statement
*
- * @access private
- * @param string the database name
- * @return bool
+ * @var string
*/
- function _create_database($name)
- {
- return "CREATE DATABASE ".$name;
- }
+ protected $_create_database = 'CREATE DATABASE %s CHARACTER SET %s COLLATE %s';
- // --------------------------------------------------------------------
+ /**
+ * CREATE TABLE keys flag
+ *
+ * Whether table keys are created from within the
+ * CREATE TABLE statement.
+ *
+ * @var bool
+ */
+ protected $_create_table_keys = TRUE;
/**
- * Drop database
+ * UNSIGNED support
*
- * @access private
- * @param string the database name
- * @return bool
+ * @var array
*/
- function _drop_database($name)
- {
- return "DROP DATABASE ".$name;
- }
+ protected $_unsigned = array(
+ 'TINYINT',
+ 'SMALLINT',
+ 'MEDIUMINT',
+ 'INT',
+ 'INTEGER',
+ 'BIGINT',
+ 'REAL',
+ 'DOUBLE',
+ 'DOUBLE PRECISION',
+ 'FLOAT',
+ 'DECIMAL',
+ 'NUMERIC'
+ );
+
+ /**
+ * NULL value representation in CREATE/ALTER TABLE statements
+ *
+ * @var string
+ */
+ protected $_null = 'NULL';
// --------------------------------------------------------------------
/**
- * Process Fields
+ * CREATE TABLE attributes
*
- * @access private
- * @param mixed the fields
+ * @param array $attributes Associative array of table attributes
* @return string
*/
- function _process_fields($fields)
+ protected function _create_table_attr($attributes)
{
- $current_field_count = 0;
$sql = '';
- foreach ($fields as $field=>$attributes)
+ foreach (array_keys($attributes) as $key)
{
- // Numeric field names aren't allowed in databases, so if the key is
- // numeric, we know it was assigned by PHP and the developer manually
- // entered the field information, so we'll simply add it to the list
- if (is_numeric($field))
+ if (is_string($key))
{
- $sql .= "\n\t$attributes";
+ $sql .= ' '.strtoupper($key).' = '.$attributes[$key];
}
- else
- {
- $attributes = array_change_key_case($attributes, CASE_UPPER);
-
- $sql .= "\n\t".$this->db->_protect_identifiers($field);
-
- if (array_key_exists('NAME', $attributes))
- {
- $sql .= ' '.$this->db->_protect_identifiers($attributes['NAME']).' ';
- }
-
- if (array_key_exists('TYPE', $attributes))
- {
- $sql .= ' '.$attributes['TYPE'];
- }
-
- if (array_key_exists('CONSTRAINT', $attributes))
- {
- $sql .= '('.$attributes['CONSTRAINT'].')';
- }
-
- if (array_key_exists('UNSIGNED', $attributes) && $attributes['UNSIGNED'] === TRUE)
- {
- $sql .= ' UNSIGNED';
- }
-
- if (array_key_exists('DEFAULT', $attributes))
- {
- $sql .= ' DEFAULT \''.$attributes['DEFAULT'].'\'';
- }
-
- if (array_key_exists('NULL', $attributes) && $attributes['NULL'] === TRUE)
- {
- $sql .= ' NULL';
- }
- else
- {
- $sql .= ' NOT NULL';
- }
+ }
- if (array_key_exists('AUTO_INCREMENT', $attributes) && $attributes['AUTO_INCREMENT'] === TRUE)
- {
- $sql .= ' AUTO_INCREMENT';
- }
- }
+ if ( ! empty($this->db->char_set) && ! strpos($sql, 'CHARACTER SET') && ! strpos($sql, 'CHARSET'))
+ {
+ $sql .= ' DEFAULT CHARACTER SET = '.$this->db->char_set;
+ }
- // don't add a comma on the end of the last field
- if (++$current_field_count < count($fields))
- {
- $sql .= ',';
- }
+ if ( ! empty($this->db->dbcollat) && ! strpos($sql, 'COLLATE'))
+ {
+ $sql .= ' COLLATE = '.$this->db->dbcollat;
}
return $sql;
@@ -132,127 +129,117 @@ class CI_DB_mysqli_forge extends CI_DB_forge {
// --------------------------------------------------------------------
/**
- * Create Table
+ * ALTER TABLE
*
- * @access private
- * @param string the table name
- * @param mixed the fields
- * @param mixed primary key(s)
- * @param mixed key(s)
- * @param boolean should 'IF NOT EXISTS' be added to the SQL
- * @return bool
+ * @param string $alter_type ALTER type
+ * @param string $table Table name
+ * @param mixed $field Column definition
+ * @return string|string[]
*/
- function _create_table($table, $fields, $primary_keys, $keys, $if_not_exists)
+ protected function _alter_table($alter_type, $table, $field)
{
- $sql = 'CREATE TABLE ';
-
- if ($if_not_exists === TRUE)
+ if ($alter_type === 'DROP')
{
- $sql .= 'IF NOT EXISTS ';
+ return parent::_alter_table($alter_type, $table, $field);
}
- $sql .= $this->db->_escape_identifiers($table)." (";
-
- $sql .= $this->_process_fields($fields);
-
- if (count($primary_keys) > 0)
+ $sql = 'ALTER TABLE '.$this->db->escape_identifiers($table);
+ for ($i = 0, $c = count($field); $i < $c; $i++)
{
- $key_name = $this->db->_protect_identifiers(implode('_', $primary_keys));
- $primary_keys = $this->db->_protect_identifiers($primary_keys);
- $sql .= ",\n\tPRIMARY KEY ".$key_name." (" . implode(', ', $primary_keys) . ")";
- }
-
- if (is_array($keys) && count($keys) > 0)
- {
- foreach ($keys as $key)
+ if ($field[$i]['_literal'] !== FALSE)
+ {
+ $field[$i] = ($alter_type === 'ADD')
+ ? "\n\tADD ".$field[$i]['_literal']
+ : "\n\tMODIFY ".$field[$i]['_literal'];
+ }
+ else
{
- if (is_array($key))
+ if ($alter_type === 'ADD')
{
- $key_name = $this->db->_protect_identifiers(implode('_', $key));
- $key = $this->db->_protect_identifiers($key);
+ $field[$i]['_literal'] = "\n\tADD ";
}
else
{
- $key_name = $this->db->_protect_identifiers($key);
- $key = array($key_name);
+ $field[$i]['_literal'] = empty($field[$i]['new_name']) ? "\n\tMODIFY " : "\n\tCHANGE ";
}
- $sql .= ",\n\tKEY {$key_name} (" . implode(', ', $key) . ")";
+ $field[$i] = $field[$i]['_literal'].$this->_process_column($field[$i]);
}
}
- $sql .= "\n) DEFAULT CHARACTER SET {$this->db->char_set} COLLATE {$this->db->dbcollat};";
-
- return $sql;
+ return array($sql.implode(',', $field));
}
// --------------------------------------------------------------------
/**
- * Drop Table
+ * Process column
*
- * @access private
+ * @param array $field
* @return string
*/
- function _drop_table($table)
+ protected function _process_column($field)
{
- return "DROP TABLE IF EXISTS ".$this->db->_escape_identifiers($table);
+ $extra_clause = isset($field['after'])
+ ? ' AFTER '.$this->db->escape_identifiers($field['after']) : '';
+
+ if (empty($extra_clause) && isset($field['first']) && $field['first'] === TRUE)
+ {
+ $extra_clause = ' FIRST';
+ }
+
+ return $this->db->escape_identifiers($field['name'])
+ .(empty($field['new_name']) ? '' : ' '.$this->db->escape_identifiers($field['new_name']))
+ .' '.$field['type'].$field['length']
+ .$field['unsigned']
+ .$field['null']
+ .$field['default']
+ .$field['auto_increment']
+ .$field['unique']
+ .(empty($field['comment']) ? '' : ' COMMENT '.$field['comment'])
+ .$extra_clause;
}
// --------------------------------------------------------------------
/**
- * Alter table query
+ * Process indexes
*
- * Generates a platform-specific query so that a table can be altered
- * Called by add_column(), drop_column(), and column_alter(),
- *
- * @access private
- * @param string the ALTER type (ADD, DROP, CHANGE)
- * @param string the column name
- * @param array fields
- * @param string the field after which we should add the new field
- * @return object
+ * @param string $table (ignored)
+ * @return string
*/
- function _alter_table($alter_type, $table, $fields, $after_field = '')
+ protected function _process_indexes($table)
{
- $sql = 'ALTER TABLE '.$this->db->_protect_identifiers($table)." $alter_type ";
+ $sql = '';
- // DROP has everything it needs now.
- if ($alter_type == 'DROP')
+ for ($i = 0, $c = count($this->keys); $i < $c; $i++)
{
- return $sql.$this->db->_protect_identifiers($fields);
- }
+ if (is_array($this->keys[$i]))
+ {
+ for ($i2 = 0, $c2 = count($this->keys[$i]); $i2 < $c2; $i2++)
+ {
+ if ( ! isset($this->fields[$this->keys[$i][$i2]]))
+ {
+ unset($this->keys[$i][$i2]);
+ continue;
+ }
+ }
+ }
+ elseif ( ! isset($this->fields[$this->keys[$i]]))
+ {
+ unset($this->keys[$i]);
+ continue;
+ }
- $sql .= $this->_process_fields($fields);
+ is_array($this->keys[$i]) OR $this->keys[$i] = array($this->keys[$i]);
- if ($after_field != '')
- {
- $sql .= ' AFTER ' . $this->db->_protect_identifiers($after_field);
+ $sql .= ",\n\tKEY ".$this->db->escape_identifiers(implode('_', $this->keys[$i]))
+ .' ('.implode(', ', $this->db->escape_identifiers($this->keys[$i])).')';
}
- return $sql;
- }
-
- // --------------------------------------------------------------------
+ $this->keys = array();
- /**
- * Rename a table
- *
- * Generates a platform-specific query so that a table can be renamed
- *
- * @access private
- * @param string the old table name
- * @param string the new table name
- * @return string
- */
- function _rename_table($table_name, $new_table_name)
- {
- $sql = 'ALTER TABLE '.$this->db->_protect_identifiers($table_name)." RENAME TO ".$this->db->_protect_identifiers($new_table_name);
return $sql;
}
}
-
-/* End of file mysqli_forge.php */
-/* Location: ./system/database/drivers/mysqli/mysqli_forge.php */ \ No newline at end of file
diff --git a/system/database/drivers/mysqli/mysqli_result.php b/system/database/drivers/mysqli/mysqli_result.php
index 4be381a4a..8c4f94d18 100644
--- a/system/database/drivers/mysqli/mysqli_result.php
+++ b/system/database/drivers/mysqli/mysqli_result.php
@@ -1,40 +1,66 @@
-<?php if ( ! defined('BASEPATH')) exit('No direct script access allowed');
+<?php
/**
* CodeIgniter
*
- * An open source application development framework for PHP 5.1.6 or newer
+ * An open source application development framework for PHP
*
- * @package CodeIgniter
- * @author ExpressionEngine Dev Team
- * @copyright Copyright (c) 2008 - 2014, EllisLab, Inc.
- * @license http://codeigniter.com/user_guide/license.html
- * @link http://codeigniter.com
- * @since Version 1.0
+ * This content is released under the MIT License (MIT)
+ *
+ * Copyright (c) 2019 - 2022, CodeIgniter Foundation
+ *
+ * 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.
+ *
+ * @package CodeIgniter
+ * @author EllisLab Dev Team
+ * @copyright Copyright (c) 2008 - 2014, EllisLab, Inc. (https://ellislab.com/)
+ * @copyright Copyright (c) 2014 - 2019, British Columbia Institute of Technology (https://bcit.ca/)
+ * @copyright Copyright (c) 2019 - 2022, CodeIgniter Foundation (https://codeigniter.com/)
+ * @license https://opensource.org/licenses/MIT MIT License
+ * @link https://codeigniter.com
+ * @since Version 1.3.0
* @filesource
*/
-
-// ------------------------------------------------------------------------
+defined('BASEPATH') OR exit('No direct script access allowed');
/**
* MySQLi Result Class
*
* This class extends the parent result class: CI_DB_result
*
+ * @package CodeIgniter
+ * @subpackage Drivers
* @category Database
- * @author ExpressionEngine Dev Team
- * @link http://codeigniter.com/user_guide/database/
+ * @author EllisLab Dev Team
+ * @link https://codeigniter.com/userguide3/database/
*/
class CI_DB_mysqli_result extends CI_DB_result {
/**
* Number of rows in the result set
*
- * @access public
- * @return integer
+ * @return int
*/
- function num_rows()
+ public function num_rows()
{
- return @mysqli_num_rows($this->result_id);
+ return is_int($this->num_rows)
+ ? $this->num_rows
+ : $this->num_rows = $this->result_id->num_rows;
}
// --------------------------------------------------------------------
@@ -42,12 +68,11 @@ class CI_DB_mysqli_result extends CI_DB_result {
/**
* Number of fields in the result set
*
- * @access public
- * @return integer
+ * @return int
*/
- function num_fields()
+ public function num_fields()
{
- return @mysqli_num_fields($this->result_id);
+ return $this->result_id->field_count;
}
// --------------------------------------------------------------------
@@ -57,13 +82,13 @@ class CI_DB_mysqli_result extends CI_DB_result {
*
* Generates an array of column names
*
- * @access public
* @return array
*/
- function list_fields()
+ public function list_fields()
{
$field_names = array();
- while ($field = mysqli_fetch_field($this->result_id))
+ $this->result_id->field_seek(0);
+ while ($field = $this->result_id->fetch_field())
{
$field_names[] = $field->name;
}
@@ -78,44 +103,83 @@ class CI_DB_mysqli_result extends CI_DB_result {
*
* Generates an array of objects containing field meta-data
*
- * @access public
* @return array
*/
- function field_data()
+ public function field_data()
{
$retval = array();
- while ($field = mysqli_fetch_object($this->result_id))
+ $field_data = $this->result_id->fetch_fields();
+ for ($i = 0, $c = count($field_data); $i < $c; $i++)
{
- preg_match('/([a-zA-Z]+)(\(\d+\))?/', $field->Type, $matches);
-
- $type = (array_key_exists(1, $matches)) ? $matches[1] : NULL;
- $length = (array_key_exists(2, $matches)) ? preg_replace('/[^\d]/', '', $matches[2]) : NULL;
-
- $F = new stdClass();
- $F->name = $field->Field;
- $F->type = $type;
- $F->default = $field->Default;
- $F->max_length = $length;
- $F->primary_key = ( $field->Key == 'PRI' ? 1 : 0 );
-
- $retval[] = $F;
+ $retval[$i] = new stdClass();
+ $retval[$i]->name = $field_data[$i]->name;
+ $retval[$i]->type = static::_get_field_type($field_data[$i]->type);
+ $retval[$i]->max_length = $field_data[$i]->max_length;
+ $retval[$i]->primary_key = (int) ($field_data[$i]->flags & MYSQLI_PRI_KEY_FLAG);
+ $retval[$i]->default = $field_data[$i]->def;
}
return $retval;
}
-
+
+ // --------------------------------------------------------------------
+
+ /**
+ * Get field type
+ *
+ * Extracts field type info from the bitflags returned by
+ * mysqli_result::fetch_fields()
+ *
+ * @used-by CI_DB_mysqli_result::field_data()
+ * @param int $type
+ * @return string
+ */
+ private static function _get_field_type($type)
+ {
+ static $map;
+ isset($map) OR $map = array(
+ MYSQLI_TYPE_DECIMAL => 'decimal',
+ MYSQLI_TYPE_BIT => 'bit',
+ MYSQLI_TYPE_TINY => 'tinyint',
+ MYSQLI_TYPE_SHORT => 'smallint',
+ MYSQLI_TYPE_INT24 => 'mediumint',
+ MYSQLI_TYPE_LONG => 'int',
+ MYSQLI_TYPE_LONGLONG => 'bigint',
+ MYSQLI_TYPE_FLOAT => 'float',
+ MYSQLI_TYPE_DOUBLE => 'double',
+ MYSQLI_TYPE_TIMESTAMP => 'timestamp',
+ MYSQLI_TYPE_DATE => 'date',
+ MYSQLI_TYPE_TIME => 'time',
+ MYSQLI_TYPE_DATETIME => 'datetime',
+ MYSQLI_TYPE_YEAR => 'year',
+ MYSQLI_TYPE_NEWDATE => 'date',
+ MYSQLI_TYPE_INTERVAL => 'interval',
+ MYSQLI_TYPE_ENUM => 'enum',
+ MYSQLI_TYPE_SET => 'set',
+ MYSQLI_TYPE_TINY_BLOB => 'tinyblob',
+ MYSQLI_TYPE_MEDIUM_BLOB => 'mediumblob',
+ MYSQLI_TYPE_BLOB => 'blob',
+ MYSQLI_TYPE_LONG_BLOB => 'longblob',
+ MYSQLI_TYPE_STRING => 'char',
+ MYSQLI_TYPE_VAR_STRING => 'varchar',
+ MYSQLI_TYPE_GEOMETRY => 'geometry'
+ );
+
+ return isset($map[$type]) ? $map[$type] : $type;
+ }
+
// --------------------------------------------------------------------
/**
* Free the result
*
- * @return null
+ * @return void
*/
- function free_result()
+ public function free_result()
{
if (is_object($this->result_id))
{
- mysqli_free_result($this->result_id);
+ $this->result_id->free();
$this->result_id = FALSE;
}
}
@@ -125,16 +189,16 @@ class CI_DB_mysqli_result extends CI_DB_result {
/**
* Data Seek
*
- * Moves the internal pointer to the desired offset. We call
+ * Moves the internal pointer to the desired offset. We call
* this internally before fetching results to make sure the
- * result set starts at zero
+ * result set starts at zero.
*
- * @access private
- * @return array
+ * @param int $n
+ * @return bool
*/
- function _data_seek($n = 0)
+ public function data_seek($n = 0)
{
- return mysqli_data_seek($this->result_id, $n);
+ return $this->result_id->data_seek($n);
}
// --------------------------------------------------------------------
@@ -144,12 +208,11 @@ class CI_DB_mysqli_result extends CI_DB_result {
*
* Returns the result set as an array
*
- * @access private
* @return array
*/
- function _fetch_assoc()
+ protected function _fetch_assoc()
{
- return mysqli_fetch_assoc($this->result_id);
+ return $this->result_id->fetch_assoc();
}
// --------------------------------------------------------------------
@@ -159,16 +222,12 @@ class CI_DB_mysqli_result extends CI_DB_result {
*
* Returns the result set as an object
*
- * @access private
+ * @param string $class_name
* @return object
*/
- function _fetch_object()
+ protected function _fetch_object($class_name = 'stdClass')
{
- return mysqli_fetch_object($this->result_id);
+ return $this->result_id->fetch_object($class_name);
}
}
-
-
-/* End of file mysqli_result.php */
-/* Location: ./system/database/drivers/mysqli/mysqli_result.php */ \ No newline at end of file
diff --git a/system/database/drivers/mysqli/mysqli_utility.php b/system/database/drivers/mysqli/mysqli_utility.php
index bc613a557..6a7d4191d 100644
--- a/system/database/drivers/mysqli/mysqli_utility.php
+++ b/system/database/drivers/mysqli/mysqli_utility.php
@@ -1,87 +1,212 @@
-<?php if ( ! defined('BASEPATH')) exit('No direct script access allowed');
+<?php
/**
* CodeIgniter
*
- * An open source application development framework for PHP 5.1.6 or newer
+ * An open source application development framework for PHP
*
- * @package CodeIgniter
- * @author ExpressionEngine Dev Team
- * @copyright Copyright (c) 2008 - 2014, EllisLab, Inc.
- * @license http://codeigniter.com/user_guide/license.html
- * @link http://codeigniter.com
- * @since Version 1.0
+ * This content is released under the MIT License (MIT)
+ *
+ * Copyright (c) 2019 - 2022, CodeIgniter Foundation
+ *
+ * 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.
+ *
+ * @package CodeIgniter
+ * @author EllisLab Dev Team
+ * @copyright Copyright (c) 2008 - 2014, EllisLab, Inc. (https://ellislab.com/)
+ * @copyright Copyright (c) 2014 - 2019, British Columbia Institute of Technology (https://bcit.ca/)
+ * @copyright Copyright (c) 2019 - 2022, CodeIgniter Foundation (https://codeigniter.com/)
+ * @license https://opensource.org/licenses/MIT MIT License
+ * @link https://codeigniter.com
+ * @since Version 1.3.0
* @filesource
*/
-
-// ------------------------------------------------------------------------
+defined('BASEPATH') OR exit('No direct script access allowed');
/**
* MySQLi Utility Class
*
+ * @package CodeIgniter
+ * @subpackage Drivers
* @category Database
- * @author ExpressionEngine Dev Team
- * @link http://codeigniter.com/user_guide/database/
+ * @author EllisLab Dev Team
+ * @link https://codeigniter.com/userguide3/database/
*/
class CI_DB_mysqli_utility extends CI_DB_utility {
/**
- * List databases
+ * List databases statement
*
- * @access private
- * @return bool
+ * @var string
*/
- function _list_databases()
- {
- return "SHOW DATABASES";
- }
-
- // --------------------------------------------------------------------
+ protected $_list_databases = 'SHOW DATABASES';
/**
- * Optimize table query
+ * OPTIMIZE TABLE statement
*
- * Generates a platform-specific query so that a table can be optimized
- *
- * @access private
- * @param string the table name
- * @return object
+ * @var string
*/
- function _optimize_table($table)
- {
- return "OPTIMIZE TABLE ".$this->db->_escape_identifiers($table);
- }
-
- // --------------------------------------------------------------------
+ protected $_optimize_table = 'OPTIMIZE TABLE %s';
/**
- * Repair table query
- *
- * Generates a platform-specific query so that a table can be repaired
+ * REPAIR TABLE statement
*
- * @access private
- * @param string the table name
- * @return object
+ * @var string
*/
- function _repair_table($table)
- {
- return "REPAIR TABLE ".$this->db->_escape_identifiers($table);
- }
+ protected $_repair_table = 'REPAIR TABLE %s';
// --------------------------------------------------------------------
/**
- * MySQLi Export
+ * Export
*
- * @access private
- * @param array Preferences
+ * @param array $params Preferences
* @return mixed
*/
- function _backup($params = array())
+ protected function _backup($params = array())
{
- // Currently unsupported
- return $this->db->display_error('db_unsuported_feature');
+ if (count($params) === 0)
+ {
+ return FALSE;
+ }
+
+ // Extract the prefs for simplicity
+ extract($params);
+
+ // Build the output
+ $output = '';
+
+ // Do we need to include a statement to disable foreign key checks?
+ if ($foreign_key_checks === FALSE)
+ {
+ $output .= 'SET foreign_key_checks = 0;'.$newline;
+ }
+
+ foreach ( (array) $tables as $table)
+ {
+ // Is the table in the "ignore" list?
+ if (in_array($table, (array) $ignore, TRUE))
+ {
+ continue;
+ }
+
+ // Get the table schema
+ $query = $this->db->query('SHOW CREATE TABLE '.$this->db->escape_identifiers($this->db->database.'.'.$table));
+
+ // No result means the table name was invalid
+ if ($query === FALSE)
+ {
+ continue;
+ }
+
+ // Write out the table schema
+ $output .= '#'.$newline.'# TABLE STRUCTURE FOR: '.$table.$newline.'#'.$newline.$newline;
+
+ if ($add_drop === TRUE)
+ {
+ $output .= 'DROP TABLE IF EXISTS '.$this->db->protect_identifiers($table).';'.$newline.$newline;
+ }
+
+ $i = 0;
+ $result = $query->result_array();
+ foreach ($result[0] as $val)
+ {
+ if ($i++ % 2)
+ {
+ $output .= $val.';'.$newline.$newline;
+ }
+ }
+
+ // If inserts are not needed we're done...
+ if ($add_insert === FALSE)
+ {
+ continue;
+ }
+
+ // Grab all the data from the current table
+ $query = $this->db->query('SELECT * FROM '.$this->db->protect_identifiers($table));
+
+ if ($query->num_rows() === 0)
+ {
+ continue;
+ }
+
+ // Fetch the field names and determine if the field is an
+ // integer type. We use this info to decide whether to
+ // surround the data with quotes or not
+
+ $i = 0;
+ $field_str = '';
+ $is_int = array();
+ while ($field = $query->result_id->fetch_field())
+ {
+ // Most versions of MySQL store timestamp as a string
+ $is_int[$i] = in_array($field->type, array(MYSQLI_TYPE_TINY, MYSQLI_TYPE_SHORT, MYSQLI_TYPE_INT24, MYSQLI_TYPE_LONG), TRUE);
+
+ // Create a string of field names
+ $field_str .= $this->db->escape_identifiers($field->name).', ';
+ $i++;
+ }
+
+ // Trim off the end comma
+ $field_str = preg_replace('/, $/' , '', $field_str);
+
+ // Build the insert string
+ foreach ($query->result_array() as $row)
+ {
+ $val_str = '';
+
+ $i = 0;
+ foreach ($row as $v)
+ {
+ // Is the value NULL?
+ if ($v === NULL)
+ {
+ $val_str .= 'NULL';
+ }
+ else
+ {
+ // Escape the data if it's not an integer
+ $val_str .= ($is_int[$i] === FALSE) ? $this->db->escape($v) : $v;
+ }
+
+ // Append a comma
+ $val_str .= ', ';
+ $i++;
+ }
+
+ // Remove the comma at the end of the string
+ $val_str = preg_replace('/, $/' , '', $val_str);
+
+ // Build the INSERT string
+ $output .= 'INSERT INTO '.$this->db->protect_identifiers($table).' ('.$field_str.') VALUES ('.$val_str.');'.$newline;
+ }
+
+ $output .= $newline.$newline;
+ }
+
+ // Do we need to include a statement to re-enable foreign key checks?
+ if ($foreign_key_checks === FALSE)
+ {
+ $output .= 'SET foreign_key_checks = 1;'.$newline;
+ }
+
+ return $output;
}
-}
-/* End of file mysqli_utility.php */
-/* Location: ./system/database/drivers/mysqli/mysqli_utility.php */ \ No newline at end of file
+}
diff --git a/system/database/drivers/oci8/index.html b/system/database/drivers/oci8/index.html
index c942a79ce..bcb7cae34 100644
--- a/system/database/drivers/oci8/index.html
+++ b/system/database/drivers/oci8/index.html
@@ -1,4 +1,5 @@
-<html>
+<!DOCTYPE html>
+<html lang="en">
<head>
<title>403 Forbidden</title>
</head>
@@ -7,4 +8,4 @@
<p>Directory access is forbidden.</p>
</body>
-</html> \ No newline at end of file
+</html>
diff --git a/system/database/drivers/oci8/oci8_driver.php b/system/database/drivers/oci8/oci8_driver.php
index 6255b330a..6f8b21d75 100644
--- a/system/database/drivers/oci8/oci8_driver.php
+++ b/system/database/drivers/oci8/oci8_driver.php
@@ -1,32 +1,55 @@
-<?php if ( ! defined('BASEPATH')) exit('No direct script access allowed');
+<?php
/**
* CodeIgniter
*
- * An open source application development framework for PHP 5.1.6 or newer
+ * An open source application development framework for PHP
*
- * @package CodeIgniter
- * @author ExpressionEngine Dev Team
- * @copyright Copyright (c) 2008 - 2014, EllisLab, Inc.
- * @license http://codeigniter.com/user_guide/license.html
- * @link http://codeigniter.com
- * @since Version 1.0
+ * This content is released under the MIT License (MIT)
+ *
+ * Copyright (c) 2019 - 2022, CodeIgniter Foundation
+ *
+ * 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.
+ *
+ * @package CodeIgniter
+ * @author EllisLab Dev Team
+ * @copyright Copyright (c) 2008 - 2014, EllisLab, Inc. (https://ellislab.com/)
+ * @copyright Copyright (c) 2014 - 2019, British Columbia Institute of Technology (https://bcit.ca/)
+ * @copyright Copyright (c) 2019 - 2022, CodeIgniter Foundation (https://codeigniter.com/)
+ * @license https://opensource.org/licenses/MIT MIT License
+ * @link https://codeigniter.com
+ * @since Version 1.4.1
* @filesource
*/
-
-// ------------------------------------------------------------------------
+defined('BASEPATH') OR exit('No direct script access allowed');
/**
* oci8 Database Adapter Class
*
* Note: _DB is an extender class that the app controller
- * creates dynamically based on whether the active record
+ * creates dynamically based on whether the query builder
* class is being used or not.
*
* @package CodeIgniter
* @subpackage Drivers
* @category Database
- * @author ExpressionEngine Dev Team
- * @link http://codeigniter.com/user_guide/database/
+ * @author EllisLab Dev Team
+ * @link https://codeigniter.com/userguide3/database/
*/
/**
@@ -36,442 +59,301 @@
* permit access to oracle databases
*
* @author Kelly McArdle
- *
*/
-
class CI_DB_oci8_driver extends CI_DB {
- var $dbdriver = 'oci8';
-
- // The character used for excaping
- var $_escape_char = '"';
-
- // clause and character used for LIKE escape sequences
- var $_like_escape_str = " escape '%s' ";
- var $_like_escape_chr = '!';
-
/**
- * The syntax to count rows is slightly different across different
- * database engines, so this string appears in each driver and is
- * used for the count_all() and count_all_results() functions.
- */
- var $_count_string = "SELECT COUNT(1) AS ";
- var $_random_keyword = ' ASC'; // not currently supported
-
- // Set "auto commit" by default
- var $_commit = OCI_COMMIT_ON_SUCCESS;
-
- // need to track statement id and cursor id
- var $stmt_id;
- var $curs_id;
-
- // if we use a limit, we will add a field that will
- // throw off num_fields later
- var $limit_used;
-
- /**
- * Non-persistent database connection
+ * Database driver
*
- * @access private called by the base class
- * @return resource
+ * @var string
*/
- public function db_connect()
- {
- return @oci_connect($this->username, $this->password, $this->hostname, $this->char_set);
- }
-
- // --------------------------------------------------------------------
+ public $dbdriver = 'oci8';
/**
- * Persistent database connection
+ * Commit mode flag
*
- * @access private called by the base class
- * @return resource
+ * @var int
*/
- public function db_pconnect()
- {
- return @oci_pconnect($this->username, $this->password, $this->hostname, $this->char_set);
- }
-
- // --------------------------------------------------------------------
+ public $commit_mode = OCI_COMMIT_ON_SUCCESS;
/**
- * Reconnect
+ * Limit used flag
*
- * Keep / reestablish the db connection if no queries have been
- * sent for a length of time exceeding the server's idle timeout
+ * If we use LIMIT, we'll add a field that will
+ * throw off num_fields later.
*
- * @access public
- * @return void
+ * @var bool
*/
- public function reconnect()
- {
- // not implemented in oracle
- return;
- }
-
- // --------------------------------------------------------------------
+ public $limit_used = FALSE;
/**
- * Select the database
+ * Error cache
*
- * @access private called by the base class
- * @return resource
- */
- public function db_select()
- {
- // Not in Oracle - schemas are actually usernames
- return TRUE;
- }
-
- // --------------------------------------------------------------------
-
- /**
- * Set client character set
+ * Cached error info about failed queries.
+ * Used so that statement IDs can be released immediately.
*
- * @access public
- * @param string
- * @param string
- * @return resource
+ * @var array|false
*/
- public function db_set_charset($charset, $collation)
- {
- // @todo - add support if needed
- return TRUE;
- }
-
- // --------------------------------------------------------------------
+ protected $_error = FALSE;
/**
- * Version number query string
+ * Affected rows
+ *
+ * Cached result of oci_num_rows().
+ * Used so that statement IDs can be released immediately.
*
- * @access protected
- * @return string
+ * @var int|false
*/
- protected function _version()
- {
- return oci_server_version($this->conn_id);
- }
+ protected $_affected_rows = FALSE;
// --------------------------------------------------------------------
/**
- * Execute the query
+ * List of reserved identifiers
+ *
+ * Identifiers that must NOT be escaped.
*
- * @access protected called by the base class
- * @param string an SQL query
- * @return resource
+ * @var string[]
*/
- protected function _execute($sql)
- {
- // oracle must parse the query before it is run. All of the actions with
- // the query are based on the statement id returned by ociparse
- $this->stmt_id = FALSE;
- $this->_set_stmt_id($sql);
- oci_set_prefetch($this->stmt_id, 1000);
- return @oci_execute($this->stmt_id, $this->_commit);
- }
+ protected $_reserved_identifiers = array('*', 'rownum');
/**
- * Generate a statement ID
+ * ORDER BY random keyword
*
- * @access private
- * @param string an SQL query
- * @return none
+ * @var array
*/
- private function _set_stmt_id($sql)
- {
- if ( ! is_resource($this->stmt_id))
- {
- $this->stmt_id = oci_parse($this->conn_id, $this->_prep_query($sql));
- }
- }
-
- // --------------------------------------------------------------------
+ protected $_random_keyword = array('ASC', 'ASC'); // not currently supported
/**
- * Prep the query
+ * COUNT string
*
- * If needed, each database adapter can prep the query string
+ * @used-by CI_DB_driver::count_all()
+ * @used-by CI_DB_query_builder::count_all_results()
*
- * @access private called by execute()
- * @param string an SQL query
- * @return string
+ * @var string
*/
- private function _prep_query($sql)
- {
- return $sql;
- }
+ protected $_count_string = 'SELECT COUNT(1) AS ';
// --------------------------------------------------------------------
/**
- * getCursor. Returns a cursor from the datbase
+ * Class constructor
*
- * @access public
- * @return cursor id
+ * @param array $params
+ * @return void
*/
- public function get_cursor()
+ public function __construct($params)
{
- $this->curs_id = oci_new_cursor($this->conn_id);
- return $this->curs_id;
- }
+ parent::__construct($params);
- // --------------------------------------------------------------------
+ $valid_dsns = array(
+ 'tns' => '/^\(DESCRIPTION=(\(.+\)){2,}\)$/', // TNS
+ // Easy Connect string (Oracle 10g+)
+ 'ec' => '/^(\/\/)?[a-z0-9.:_-]+(:[1-9][0-9]{0,4})?(\/[a-z0-9$_]+)?(:[^\/])?(\/[a-z0-9$_]+)?$/i',
+ 'in' => '/^[a-z0-9$_]+$/i' // Instance name (defined in tnsnames.ora)
+ );
- /**
- * Stored Procedure. Executes a stored procedure
- *
- * @access public
- * @param package package stored procedure is in
- * @param procedure stored procedure to execute
- * @param params array of parameters
- * @return array
- *
- * params array keys
- *
- * KEY OPTIONAL NOTES
- * name no the name of the parameter should be in :<param_name> format
- * value no the value of the parameter. If this is an OUT or IN OUT parameter,
- * this should be a reference to a variable
- * type yes the type of the parameter
- * length yes the max size of the parameter
- */
- public function stored_procedure($package, $procedure, $params)
- {
- if ($package == '' OR $procedure == '' OR ! is_array($params))
+ /* Space characters don't have any effect when actually
+ * connecting, but can be a hassle while validating the DSN.
+ */
+ $this->dsn = str_replace(array("\n", "\r", "\t", ' '), '', $this->dsn);
+
+ if ($this->dsn !== '')
{
- if ($this->db_debug)
+ foreach ($valid_dsns as $regexp)
{
- log_message('error', 'Invalid query: '.$package.'.'.$procedure);
- return $this->display_error('db_invalid_query');
+ if (preg_match($regexp, $this->dsn))
+ {
+ return;
+ }
}
- return FALSE;
}
- // build the query string
- $sql = "begin $package.$procedure(";
+ // Legacy support for TNS in the hostname configuration field
+ $this->hostname = str_replace(array("\n", "\r", "\t", ' '), '', $this->hostname);
+ if (preg_match($valid_dsns['tns'], $this->hostname))
+ {
+ $this->dsn = $this->hostname;
+ return;
+ }
+ elseif ($this->hostname !== '' && strpos($this->hostname, '/') === FALSE && strpos($this->hostname, ':') === FALSE
+ && (( ! empty($this->port) && ctype_digit($this->port)) OR $this->database !== ''))
+ {
+ /* If the hostname field isn't empty, doesn't contain
+ * ':' and/or '/' and if port and/or database aren't
+ * empty, then the hostname field is most likely indeed
+ * just a hostname. Therefore we'll try and build an
+ * Easy Connect string from these 3 settings, assuming
+ * that the database field is a service name.
+ */
+ $this->dsn = $this->hostname
+ .(( ! empty($this->port) && ctype_digit($this->port)) ? ':'.$this->port : '')
+ .($this->database !== '' ? '/'.ltrim($this->database, '/') : '');
+
+ if (preg_match($valid_dsns['ec'], $this->dsn))
+ {
+ return;
+ }
+ }
- $have_cursor = FALSE;
- foreach ($params as $param)
+ /* At this point, we can only try and validate the hostname and
+ * database fields separately as DSNs.
+ */
+ if (preg_match($valid_dsns['ec'], $this->hostname) OR preg_match($valid_dsns['in'], $this->hostname))
{
- $sql .= $param['name'] . ",";
+ $this->dsn = $this->hostname;
+ return;
+ }
- if (array_key_exists('type', $param) && ($param['type'] === OCI_B_CURSOR))
+ $this->database = str_replace(array("\n", "\r", "\t", ' '), '', $this->database);
+ foreach ($valid_dsns as $regexp)
+ {
+ if (preg_match($regexp, $this->database))
{
- $have_cursor = TRUE;
+ return;
}
}
- $sql = trim($sql, ",") . "); end;";
- $this->stmt_id = FALSE;
- $this->_set_stmt_id($sql);
- $this->_bind_params($params);
- $this->query($sql, FALSE, $have_cursor);
+ /* Well - OK, an empty string should work as well.
+ * PHP will try to use environment variables to
+ * determine which Oracle instance to connect to.
+ */
+ $this->dsn = '';
}
// --------------------------------------------------------------------
/**
- * Bind parameters
+ * Non-persistent database connection
*
- * @access private
- * @return none
+ * @param bool $persistent
+ * @return resource
*/
- private function _bind_params($params)
+ public function db_connect($persistent = FALSE)
{
- if ( ! is_array($params) OR ! is_resource($this->stmt_id))
- {
- return;
- }
-
- foreach ($params as $param)
- {
- foreach (array('name', 'value', 'type', 'length') as $val)
- {
- if ( ! isset($param[$val]))
- {
- $param[$val] = '';
- }
- }
-
- oci_bind_by_name($this->stmt_id, $param['name'], $param['value'], $param['length'], $param['type']);
- }
+ $func = ($persistent === TRUE) ? 'oci_pconnect' : 'oci_connect';
+ return empty($this->char_set)
+ ? $func($this->username, $this->password, $this->dsn)
+ : $func($this->username, $this->password, $this->dsn, $this->char_set);
}
// --------------------------------------------------------------------
/**
- * Begin Transaction
+ * Database version number
*
- * @access public
- * @return bool
+ * @return string
*/
- public function trans_begin($test_mode = FALSE)
+ public function version()
{
- if ( ! $this->trans_enabled)
+ if (isset($this->data_cache['version']))
{
- return TRUE;
+ return $this->data_cache['version'];
}
- // When transactions are nested we only begin/commit/rollback the outermost ones
- if ($this->_trans_depth > 0)
+ if ( ! $this->conn_id OR ($version_string = oci_server_version($this->conn_id)) === FALSE)
{
- return TRUE;
+ return FALSE;
+ }
+ elseif (preg_match('#Release\s(\d+(?:\.\d+)+)#', $version_string, $match))
+ {
+ return $this->data_cache['version'] = $match[1];
}
- // Reset the transaction failure flag.
- // If the $test_mode flag is set to TRUE transactions will be rolled back
- // even if the queries produce a successful result.
- $this->_trans_failure = ($test_mode === TRUE) ? TRUE : FALSE;
-
- $this->_commit = OCI_DEFAULT;
- return TRUE;
+ return FALSE;
}
// --------------------------------------------------------------------
/**
- * Commit Transaction
+ * Execute the query
*
- * @access public
- * @return bool
+ * @param string $sql an SQL query
+ * @return resource
*/
- public function trans_commit()
+ protected function _execute($sql)
{
- if ( ! $this->trans_enabled)
- {
- return TRUE;
- }
+ /* Oracle must parse the query before it is run. All of the actions with
+ * the query are based on the statement id returned by oci_parse().
+ */
+ $this->result_id = oci_parse($this->conn_id, $sql);
+ oci_set_prefetch($this->result_id, 1000);
+ $result = oci_execute($this->result_id, $this->commit_mode);
+ $this->_error = oci_error($this->result_id);
+ $this->is_write_type($sql) && $this->_affected_rows = oci_num_rows($this->result_id);
- // When transactions are nested we only begin/commit/rollback the outermost ones
- if ($this->_trans_depth > 0)
+ if ($this->is_write_type($sql) OR $result === FALSE)
{
- return TRUE;
+ oci_free_statement($this->result_id);
+ return $result;
}
- $ret = oci_commit($this->conn_id);
- $this->_commit = OCI_COMMIT_ON_SUCCESS;
- return $ret;
+ return $this->result_id;
}
// --------------------------------------------------------------------
/**
- * Rollback Transaction
+ * Begin Transaction
*
- * @access public
* @return bool
*/
- public function trans_rollback()
+ protected function _trans_begin()
{
- if ( ! $this->trans_enabled)
- {
- return TRUE;
- }
-
- // When transactions are nested we only begin/commit/rollback the outermost ones
- if ($this->_trans_depth > 0)
- {
- return TRUE;
- }
-
- $ret = oci_rollback($this->conn_id);
- $this->_commit = OCI_COMMIT_ON_SUCCESS;
- return $ret;
+ $this->commit_mode = OCI_NO_AUTO_COMMIT;
+ return TRUE;
}
// --------------------------------------------------------------------
/**
- * Escape String
+ * Commit Transaction
*
- * @access public
- * @param string
- * @param bool whether or not the string will be used in a LIKE condition
- * @return string
+ * @return bool
*/
- public function escape_str($str, $like = FALSE)
+ protected function _trans_commit()
{
- if (is_array($str))
- {
- foreach ($str as $key => $val)
- {
- $str[$key] = $this->escape_str($val, $like);
- }
-
- return $str;
- }
-
- $str = remove_invisible_characters($str);
-
- // escape LIKE condition wildcards
- if ($like === TRUE)
- {
- $str = str_replace( array('%', '_', $this->_like_escape_chr),
- array($this->_like_escape_chr.'%', $this->_like_escape_chr.'_', $this->_like_escape_chr.$this->_like_escape_chr),
- $str);
- }
+ $this->commit_mode = OCI_COMMIT_ON_SUCCESS;
- return $str;
+ return oci_commit($this->conn_id);
}
// --------------------------------------------------------------------
/**
- * Affected Rows
+ * Rollback Transaction
*
- * @access public
- * @return integer
+ * @return bool
*/
- public function affected_rows()
+ protected function _trans_rollback()
{
- return @oci_num_rows($this->stmt_id);
+ $this->commit_mode = OCI_COMMIT_ON_SUCCESS;
+ return oci_rollback($this->conn_id);
}
// --------------------------------------------------------------------
/**
- * Insert ID
+ * Affected Rows
*
- * @access public
- * @return integer
+ * @return int
*/
- public function insert_id()
+ public function affected_rows()
{
- // not supported in oracle
- return $this->display_error('db_unsupported_function');
+ return $this->_affected_rows;
}
// --------------------------------------------------------------------
/**
- * "Count All" query
- *
- * Generates a platform-specific query string that counts all records in
- * the specified database
+ * Insert ID
*
- * @access public
- * @param string
- * @return string
+ * @return int
*/
- public function count_all($table = '')
+ public function insert_id()
{
- if ($table == '')
- {
- return 0;
- }
-
- $query = $this->query($this->_count_string . $this->_protect_identifiers('numrows') . " FROM " . $this->_protect_identifiers($table, TRUE, NULL, FALSE));
-
- if ($query == FALSE)
- {
- return 0;
- }
-
- $row = $query->row();
- $this->_reset_select();
- return (int) $row->numrows;
+ // not supported in oracle
+ return $this->display_error('db_unsupported_function');
}
// --------------------------------------------------------------------
@@ -481,17 +363,17 @@ class CI_DB_oci8_driver extends CI_DB {
*
* Generates a platform-specific query string so that the table names can be fetched
*
- * @access protected
- * @param boolean
+ * @param bool $prefix_limit
* @return string
*/
protected function _list_tables($prefix_limit = FALSE)
{
- $sql = "SELECT TABLE_NAME FROM ALL_TABLES";
+ $sql = 'SELECT "TABLE_NAME" FROM "ALL_TABLES"';
- if ($prefix_limit !== FALSE AND $this->dbprefix != '')
+ if ($prefix_limit !== FALSE && $this->dbprefix !== '')
{
- $sql .= " WHERE TABLE_NAME LIKE '".$this->escape_like_str($this->dbprefix)."%' ".sprintf($this->_like_escape_str, $this->_like_escape_chr);
+ return $sql.' WHERE "TABLE_NAME" LIKE \''.$this->escape_like_str($this->dbprefix)."%' "
+ .sprintf($this->_like_escape_str, $this->_like_escape_chr);
}
return $sql;
@@ -504,155 +386,126 @@ class CI_DB_oci8_driver extends CI_DB {
*
* Generates a platform-specific query string so that the column names can be fetched
*
- * @access protected
- * @param string the table name
- * @return string
+ * @param string $table
+ * @return string
*/
protected function _list_columns($table = '')
{
- return "SELECT COLUMN_NAME FROM all_tab_columns WHERE table_name = '$table'";
- }
-
- // --------------------------------------------------------------------
-
- /**
- * Field data query
- *
- * Generates a platform-specific query so that the column data can be retrieved
- *
- * @access public
- * @param string the table name
- * @return object
- */
- protected function _field_data($table)
- {
- return "SELECT * FROM ".$table." where rownum = 1";
- }
-
- // --------------------------------------------------------------------
+ if (strpos($table, '.') !== FALSE)
+ {
+ sscanf($table, '%[^.].%s', $owner, $table);
+ }
+ else
+ {
+ $owner = $this->username;
+ }
- /**
- * The error message string
- *
- * @access protected
- * @return string
- */
- protected function _error_message()
- {
- // If the error was during connection, no conn_id should be passed
- $error = is_resource($this->conn_id) ? oci_error($this->conn_id) : oci_error();
- return $error['message'];
+ return 'SELECT COLUMN_NAME FROM ALL_TAB_COLUMNS
+ WHERE UPPER(OWNER) = '.$this->escape(strtoupper($owner)).'
+ AND UPPER(TABLE_NAME) = '.$this->escape(strtoupper($table));
}
// --------------------------------------------------------------------
/**
- * The error message number
+ * Returns an object with field data
*
- * @access protected
- * @return integer
+ * @param string $table
+ * @return array
*/
- protected function _error_number()
+ public function field_data($table)
{
- // Same as _error_message()
- $error = is_resource($this->conn_id) ? oci_error($this->conn_id) : oci_error();
- return $error['code'];
- }
+ if (strpos($table, '.') !== FALSE)
+ {
+ sscanf($table, '%[^.].%s', $owner, $table);
+ }
+ else
+ {
+ $owner = $this->username;
+ }
- // --------------------------------------------------------------------
+ $sql = 'SELECT COLUMN_NAME, DATA_TYPE, CHAR_LENGTH, DATA_PRECISION, DATA_LENGTH, DATA_DEFAULT, NULLABLE
+ FROM ALL_TAB_COLUMNS
+ WHERE UPPER(OWNER) = '.$this->escape(strtoupper($owner)).'
+ AND UPPER(TABLE_NAME) = '.$this->escape(strtoupper($table));
- /**
- * Escape the SQL Identifiers
- *
- * This function escapes column and table names
- *
- * @access protected
- * @param string
- * @return string
- */
- protected function _escape_identifiers($item)
- {
- if ($this->_escape_char == '')
+ if (($query = $this->query($sql)) === FALSE)
{
- return $item;
+ return FALSE;
}
+ $query = $query->result_object();
- foreach ($this->_reserved_identifiers as $id)
+ $retval = array();
+ for ($i = 0, $c = count($query); $i < $c; $i++)
{
- if (strpos($item, '.'.$id) !== FALSE)
- {
- $str = $this->_escape_char. str_replace('.', $this->_escape_char.'.', $item);
+ $retval[$i] = new stdClass();
+ $retval[$i]->name = $query[$i]->COLUMN_NAME;
+ $retval[$i]->type = $query[$i]->DATA_TYPE;
- // remove duplicates if the user already included the escape
- return preg_replace('/['.$this->_escape_char.']+/', $this->_escape_char, $str);
+ $length = ($query[$i]->CHAR_LENGTH > 0)
+ ? $query[$i]->CHAR_LENGTH : $query[$i]->DATA_PRECISION;
+ if ($length === NULL)
+ {
+ $length = $query[$i]->DATA_LENGTH;
}
- }
+ $retval[$i]->max_length = $length;
- if (strpos($item, '.') !== FALSE)
- {
- $str = $this->_escape_char.str_replace('.', $this->_escape_char.'.'.$this->_escape_char, $item).$this->_escape_char;
- }
- else
- {
- $str = $this->_escape_char.$item.$this->_escape_char;
+ $default = $query[$i]->DATA_DEFAULT;
+ if ($default === NULL && $query[$i]->NULLABLE === 'N')
+ {
+ $default = '';
+ }
+ $retval[$i]->default = $default;
}
- // remove duplicates if the user already included the escape
- return preg_replace('/['.$this->_escape_char.']+/', $this->_escape_char, $str);
+ return $retval;
}
// --------------------------------------------------------------------
/**
- * From Tables
+ * Error
*
- * This function implicitly groups FROM tables so there is no confusion
- * about operator precedence in harmony with SQL standards
+ * Returns an array containing code and message of the last
+ * database error that has occurred.
*
- * @access protected
- * @param type
- * @return type
+ * @return array
*/
- protected function _from_tables($tables)
+ public function error()
{
- if ( ! is_array($tables))
+ if ( ! empty($this->_error))
{
- $tables = array($tables);
+ return $this->_error;
}
- return implode(', ', $tables);
- }
-
- // --------------------------------------------------------------------
+ // oci_error() returns an array that already contains
+ // 'code' and 'message' keys, but it can return false
+ // if there was no error ....
+ if (is_resource($this->conn_id))
+ {
+ $error = oci_error($this->conn_id);
+ }
+ else
+ {
+ $error = oci_error();
+ }
- /**
- * Insert statement
- *
- * Generates a platform-specific insert string from the supplied data
- *
- * @access public
- * @param string the table name
- * @param array the insert keys
- * @param array the insert values
- * @return string
- */
- protected function _insert($table, $keys, $values)
- {
- return "INSERT INTO ".$table." (".implode(', ', $keys).") VALUES (".implode(', ', $values).")";
+ return is_array($error)
+ ? $error
+ : array('code' => '', 'message' => '');
}
// --------------------------------------------------------------------
/**
- * Insert_batch statement
+ * Insert batch statement
*
* Generates a platform-specific insert string from the supplied data
*
- * @access protected
- * @param string the table name
- * @param array the insert keys
- * @param array the insert values
- * @return string
+ * @param string $table Table name
+ * @param array $keys INSERT keys
+ * @param array $values INSERT values
+ * @return string
*/
protected function _insert_batch($table, $keys, $values)
{
@@ -661,47 +514,10 @@ class CI_DB_oci8_driver extends CI_DB {
for ($i = 0, $c = count($values); $i < $c; $i++)
{
- $sql .= ' INTO ' . $table . ' (' . $keys . ') VALUES ' . $values[$i] . "\n";
- }
-
- $sql .= 'SELECT * FROM dual';
-
- return $sql;
- }
-
- // --------------------------------------------------------------------
-
- /**
- * Update statement
- *
- * Generates a platform-specific update string from the supplied data
- *
- * @access protected
- * @param string the table name
- * @param array the update data
- * @param array the where clause
- * @param array the orderby clause
- * @param array the limit clause
- * @return string
- */
- protected function _update($table, $values, $where, $orderby = array(), $limit = FALSE)
- {
- foreach ($values as $key => $val)
- {
- $valstr[] = $key." = ".$val;
+ $sql .= ' INTO '.$table.' ('.$keys.') VALUES '.$values[$i]."\n";
}
- $limit = ( ! $limit) ? '' : ' LIMIT '.$limit;
-
- $orderby = (count($orderby) >= 1)?' ORDER BY '.implode(", ", $orderby):'';
-
- $sql = "UPDATE ".$table." SET ".implode(', ', $valstr);
-
- $sql .= ($where != '' AND count($where) >=1) ? " WHERE ".implode(" ", $where) : '';
-
- $sql .= $orderby.$limit;
-
- return $sql;
+ return $sql.'SELECT * FROM dual';
}
// --------------------------------------------------------------------
@@ -710,16 +526,16 @@ class CI_DB_oci8_driver extends CI_DB {
* Truncate statement
*
* Generates a platform-specific truncate string from the supplied data
- * If the database does not support the truncate() command
- * This function maps to "DELETE FROM table"
*
- * @access protected
- * @param string the table name
+ * If the database does not support the TRUNCATE statement,
+ * then this method maps to 'DELETE FROM table'
+ *
+ * @param string $table
* @return string
*/
protected function _truncate($table)
{
- return "TRUNCATE TABLE ".$table;
+ return 'TRUNCATE TABLE '.$table;
}
// --------------------------------------------------------------------
@@ -729,60 +545,43 @@ class CI_DB_oci8_driver extends CI_DB {
*
* Generates a platform-specific delete string from the supplied data
*
- * @access protected
- * @param string the table name
- * @param array the where clause
- * @param string the limit clause
+ * @param string $table
* @return string
*/
- protected function _delete($table, $where = array(), $like = array(), $limit = FALSE)
+ protected function _delete($table)
{
- $conditions = '';
-
- if (count($where) > 0 OR count($like) > 0)
+ if ($this->qb_limit)
{
- $conditions = "\nWHERE ";
- $conditions .= implode("\n", $this->ar_where);
-
- if (count($where) > 0 && count($like) > 0)
- {
- $conditions .= " AND ";
- }
- $conditions .= implode("\n", $like);
+ $this->where('rownum <= ',$this->qb_limit, FALSE);
+ $this->qb_limit = FALSE;
}
- $limit = ( ! $limit) ? '' : ' LIMIT '.$limit;
-
- return "DELETE FROM ".$table.$conditions.$limit;
+ return parent::_delete($table);
}
// --------------------------------------------------------------------
/**
- * Limit string
+ * LIMIT
*
* Generates a platform-specific LIMIT clause
*
- * @access protected
- * @param string the sql query string
- * @param integer the number of rows to limit the query to
- * @param integer the offset value
- * @return string
+ * @param string $sql SQL Query
+ * @return string
*/
- protected function _limit($sql, $limit, $offset)
+ protected function _limit($sql)
{
- $limit = $offset + $limit;
- $newsql = "SELECT * FROM (select inner_query.*, rownum rnum FROM ($sql) inner_query WHERE rownum < $limit)";
-
- if ($offset != 0)
+ if (version_compare($this->version(), '12.1', '>='))
{
- $newsql .= " WHERE rnum >= $offset";
+ // OFFSET-FETCH can be used only with the ORDER BY clause
+ empty($this->qb_orderby) && $sql .= ' ORDER BY 1';
+
+ return $sql.' OFFSET '.(int) $this->qb_offset.' ROWS FETCH NEXT '.$this->qb_limit.' ROWS ONLY';
}
- // remember that we used limits
$this->limit_used = TRUE;
-
- return $newsql;
+ return 'SELECT * FROM (SELECT inner_query.*, rownum rnum FROM ('.$sql.') inner_query WHERE rownum < '.($this->qb_offset + $this->qb_limit + 1).')'
+ .($this->qb_offset ? ' WHERE rnum >= '.($this->qb_offset + 1) : '');
}
// --------------------------------------------------------------------
@@ -790,19 +589,29 @@ class CI_DB_oci8_driver extends CI_DB {
/**
* Close DB Connection
*
- * @access protected
- * @param resource
- * @return void
+ * @return void
*/
- protected function _close($conn_id)
+ protected function _close()
{
- @oci_close($conn_id);
+ if (is_resource($this->result_id))
+ {
+ oci_free_statement($this->result_id);
+ }
+
+ oci_close($this->conn_id);
}
+ // --------------------------------------------------------------------
+ /**
+ * We need to reset our $limit_used hack flag, so it doesn't propagate
+ * to subsequent queries.
+ *
+ * @return void
+ */
+ protected function _reset_select()
+ {
+ $this->limit_used = FALSE;
+ parent::_reset_select();
+ }
}
-
-
-
-/* End of file oci8_driver.php */
-/* Location: ./system/database/drivers/oci8/oci8_driver.php */
diff --git a/system/database/drivers/oci8/oci8_forge.php b/system/database/drivers/oci8/oci8_forge.php
index ab45220f3..9910b1156 100644
--- a/system/database/drivers/oci8/oci8_forge.php
+++ b/system/database/drivers/oci8/oci8_forge.php
@@ -1,248 +1,217 @@
-<?php if ( ! defined('BASEPATH')) exit('No direct script access allowed');
+<?php
/**
* CodeIgniter
*
- * An open source application development framework for PHP 5.1.6 or newer
+ * An open source application development framework for PHP
*
- * @package CodeIgniter
- * @author ExpressionEngine Dev Team
- * @copyright Copyright (c) 2008 - 2014, EllisLab, Inc.
- * @license http://codeigniter.com/user_guide/license.html
- * @link http://codeigniter.com
- * @since Version 1.0
+ * This content is released under the MIT License (MIT)
+ *
+ * Copyright (c) 2019 - 2022, CodeIgniter Foundation
+ *
+ * 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.
+ *
+ * @package CodeIgniter
+ * @author EllisLab Dev Team
+ * @copyright Copyright (c) 2008 - 2014, EllisLab, Inc. (https://ellislab.com/)
+ * @copyright Copyright (c) 2014 - 2019, British Columbia Institute of Technology (https://bcit.ca/)
+ * @copyright Copyright (c) 2019 - 2022, CodeIgniter Foundation (https://codeigniter.com/)
+ * @license https://opensource.org/licenses/MIT MIT License
+ * @link https://codeigniter.com
+ * @since Version 1.4.1
* @filesource
*/
-
-// ------------------------------------------------------------------------
+defined('BASEPATH') OR exit('No direct script access allowed');
/**
* Oracle Forge Class
*
* @category Database
- * @author ExpressionEngine Dev Team
- * @link http://codeigniter.com/user_guide/database/
+ * @author EllisLab Dev Team
+ * @link https://codeigniter.com/userguide3/database/
*/
class CI_DB_oci8_forge extends CI_DB_forge {
/**
- * Create database
+ * CREATE DATABASE statement
*
- * @access public
- * @param string the database name
- * @return bool
+ * @var string
*/
- function _create_database($name)
- {
- return FALSE;
- }
+ protected $_create_database = FALSE;
- // --------------------------------------------------------------------
+ /**
+ * CREATE TABLE IF statement
+ *
+ * @var string
+ */
+ protected $_create_table_if = FALSE;
/**
- * Drop database
+ * DROP DATABASE statement
*
- * @access private
- * @param string the database name
- * @return bool
+ * @var string
*/
- function _drop_database($name)
- {
- return FALSE;
- }
+ protected $_drop_database = FALSE;
+
+ /**
+ * DROP TABLE IF statement
+ *
+ * @var string
+ */
+ protected $_drop_table_if = FALSE;
+
+ /**
+ * UNSIGNED support
+ *
+ * @var bool|array
+ */
+ protected $_unsigned = FALSE;
+
+ /**
+ * NULL value representation in CREATE/ALTER TABLE statements
+ *
+ * @var string
+ */
+ protected $_null = 'NULL';
// --------------------------------------------------------------------
/**
- * Create Table
+ * ALTER TABLE
*
- * @access private
- * @param string the table name
- * @param array the fields
- * @param mixed primary key(s)
- * @param mixed key(s)
- * @param boolean should 'IF NOT EXISTS' be added to the SQL
- * @return bool
+ * @param string $alter_type ALTER type
+ * @param string $table Table name
+ * @param mixed $field Column definition
+ * @return string|string[]
*/
- function _create_table($table, $fields, $primary_keys, $keys, $if_not_exists)
+ protected function _alter_table($alter_type, $table, $field)
{
- $sql = 'CREATE TABLE ';
-
- if ($if_not_exists === TRUE)
+ if ($alter_type === 'DROP')
{
- $sql .= 'IF NOT EXISTS ';
+ return parent::_alter_table($alter_type, $table, $field);
+ }
+ elseif ($alter_type === 'CHANGE')
+ {
+ $alter_type = 'MODIFY';
}
- $sql .= $this->db->_escape_identifiers($table)." (";
- $current_field_count = 0;
-
- foreach ($fields as $field=>$attributes)
+ $sql = 'ALTER TABLE '.$this->db->escape_identifiers($table);
+ $sqls = array();
+ for ($i = 0, $c = count($field); $i < $c; $i++)
{
- // Numeric field names aren't allowed in databases, so if the key is
- // numeric, we know it was assigned by PHP and the developer manually
- // entered the field information, so we'll simply add it to the list
- if (is_numeric($field))
+ if ($field[$i]['_literal'] !== FALSE)
{
- $sql .= "\n\t$attributes";
+ $field[$i] = "\n\t".$field[$i]['_literal'];
}
else
{
- $attributes = array_change_key_case($attributes, CASE_UPPER);
-
- $sql .= "\n\t".$this->db->_protect_identifiers($field);
+ $field[$i]['_literal'] = "\n\t".$this->_process_column($field[$i]);
- $sql .= ' '.$attributes['TYPE'];
-
- if (array_key_exists('CONSTRAINT', $attributes))
+ if ( ! empty($field[$i]['comment']))
{
- $sql .= '('.$attributes['CONSTRAINT'].')';
+ $sqls[] = 'COMMENT ON COLUMN '
+ .$this->db->escape_identifiers($table).'.'.$this->db->escape_identifiers($field[$i]['name'])
+ .' IS '.$field[$i]['comment'];
}
- if (array_key_exists('UNSIGNED', $attributes) && $attributes['UNSIGNED'] === TRUE)
+ if ($alter_type === 'MODIFY' && ! empty($field[$i]['new_name']))
{
- $sql .= ' UNSIGNED';
+ $sqls[] = $sql.' RENAME COLUMN '.$this->db->escape_identifiers($field[$i]['name'])
+ .' TO '.$this->db->escape_identifiers($field[$i]['new_name']);
}
- if (array_key_exists('DEFAULT', $attributes))
- {
- $sql .= ' DEFAULT \''.$attributes['DEFAULT'].'\'';
- }
-
- if (array_key_exists('NULL', $attributes) && $attributes['NULL'] === TRUE)
- {
- $sql .= ' NULL';
- }
- else
- {
- $sql .= ' NOT NULL';
- }
-
- if (array_key_exists('AUTO_INCREMENT', $attributes) && $attributes['AUTO_INCREMENT'] === TRUE)
- {
- $sql .= ' AUTO_INCREMENT';
- }
+ $field[$i] = "\n\t".$field[$i]['_literal'];
}
-
- // don't add a comma on the end of the last field
- if (++$current_field_count < count($fields))
- {
- $sql .= ',';
- }
- }
-
- if (count($primary_keys) > 0)
- {
- $primary_keys = $this->db->_protect_identifiers($primary_keys);
- $sql .= ",\n\tPRIMARY KEY (" . implode(', ', $primary_keys) . ")";
}
- if (is_array($keys) && count($keys) > 0)
- {
- foreach ($keys as $key)
- {
- if (is_array($key))
- {
- $key = $this->db->_protect_identifiers($key);
- }
- else
- {
- $key = array($this->db->_protect_identifiers($key));
- }
+ $sql .= ' '.$alter_type.' ';
+ $sql .= (count($field) === 1)
+ ? $field[0]
+ : '('.implode(',', $field).')';
- $sql .= ",\n\tUNIQUE COLUMNS (" . implode(', ', $key) . ")";
- }
- }
-
- $sql .= "\n)";
-
- return $sql;
+ // RENAME COLUMN must be executed after MODIFY
+ array_unshift($sqls, $sql);
+ return $sqls;
}
// --------------------------------------------------------------------
/**
- * Drop Table
+ * Field attribute AUTO_INCREMENT
*
- * @access private
- * @return bool
+ * @param array &$attributes
+ * @param array &$field
+ * @return void
*/
- function _drop_table($table)
+ protected function _attr_auto_increment(&$attributes, &$field)
{
- return FALSE;
+ if ( ! empty($attributes['AUTO_INCREMENT']) && $attributes['AUTO_INCREMENT'] === TRUE && stripos($field['type'], 'number') !== FALSE && version_compare($this->db->version(), '12.1', '>='))
+ {
+ $field['auto_increment'] = ' GENERATED ALWAYS AS IDENTITY';
+ }
}
// --------------------------------------------------------------------
/**
- * Alter table query
- *
- * Generates a platform-specific query so that a table can be altered
- * Called by add_column(), drop_column(), and column_alter(),
+ * Process column
*
- * @access private
- * @param string the ALTER type (ADD, DROP, CHANGE)
- * @param string the column name
- * @param string the table name
- * @param string the column definition
- * @param string the default value
- * @param boolean should 'NOT NULL' be added
- * @param string the field after which we should add the new field
- * @return object
+ * @param array $field
+ * @return string
*/
- function _alter_table($alter_type, $table, $column_name, $column_definition = '', $default_value = '', $null = '', $after_field = '')
+ protected function _process_column($field)
{
- $sql = 'ALTER TABLE '.$this->db->_protect_identifiers($table)." $alter_type ".$this->db->_protect_identifiers($column_name);
-
- // DROP has everything it needs now.
- if ($alter_type == 'DROP')
- {
- return $sql;
- }
-
- $sql .= " $column_definition";
-
- if ($default_value != '')
- {
- $sql .= " DEFAULT \"$default_value\"";
- }
-
- if ($null === NULL)
- {
- $sql .= ' NULL';
- }
- else
- {
- $sql .= ' NOT NULL';
- }
-
- if ($after_field != '')
- {
- $sql .= ' AFTER ' . $this->db->_protect_identifiers($after_field);
- }
-
- return $sql;
-
+ return $this->db->escape_identifiers($field['name'])
+ .' '.$field['type'].$field['length']
+ .$field['unsigned']
+ .$field['default']
+ .$field['auto_increment']
+ .$field['null']
+ .$field['unique'];
}
// --------------------------------------------------------------------
/**
- * Rename a table
+ * Field attribute TYPE
*
- * Generates a platform-specific query so that a table can be renamed
+ * Performs a data type mapping between different databases.
*
- * @access private
- * @param string the old table name
- * @param string the new table name
- * @return string
+ * @param array &$attributes
+ * @return void
*/
- function _rename_table($table_name, $new_table_name)
+ protected function _attr_type(&$attributes)
{
- $sql = 'ALTER TABLE '.$this->db->_protect_identifiers($table_name)." RENAME TO ".$this->db->_protect_identifiers($new_table_name);
- return $sql;
+ switch (strtoupper($attributes['TYPE']))
+ {
+ case 'TINYINT':
+ $attributes['TYPE'] = 'NUMBER';
+ return;
+ case 'MEDIUMINT':
+ $attributes['TYPE'] = 'NUMBER';
+ return;
+ case 'INT':
+ $attributes['TYPE'] = 'NUMBER';
+ return;
+ case 'BIGINT':
+ $attributes['TYPE'] = 'NUMBER';
+ return;
+ default: return;
+ }
}
-
-
}
-
-/* End of file oci8_forge.php */
-/* Location: ./system/database/drivers/oci8/oci8_forge.php */ \ No newline at end of file
diff --git a/system/database/drivers/oci8/oci8_result.php b/system/database/drivers/oci8/oci8_result.php
index cdbee6870..3b042fb75 100644
--- a/system/database/drivers/oci8/oci8_result.php
+++ b/system/database/drivers/oci8/oci8_result.php
@@ -1,19 +1,42 @@
-<?php if ( ! defined('BASEPATH')) exit('No direct script access allowed');
+<?php
/**
* CodeIgniter
*
- * An open source application development framework for PHP 5.1.6 or newer
+ * An open source application development framework for PHP
*
- * @package CodeIgniter
- * @author ExpressionEngine Dev Team
- * @copyright Copyright (c) 2008 - 2014, EllisLab, Inc.
- * @license http://codeigniter.com/user_guide/license.html
- * @link http://codeigniter.com
- * @since Version 1.0
+ * This content is released under the MIT License (MIT)
+ *
+ * Copyright (c) 2019 - 2022, CodeIgniter Foundation
+ *
+ * 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.
+ *
+ * @package CodeIgniter
+ * @author EllisLab Dev Team
+ * @copyright Copyright (c) 2008 - 2014, EllisLab, Inc. (https://ellislab.com/)
+ * @copyright Copyright (c) 2014 - 2019, British Columbia Institute of Technology (https://bcit.ca/)
+ * @copyright Copyright (c) 2019 - 2022, CodeIgniter Foundation (https://codeigniter.com/)
+ * @license https://opensource.org/licenses/MIT MIT License
+ * @link https://codeigniter.com
+ * @since Version 1.4.1
* @filesource
*/
-
-// ------------------------------------------------------------------------
+defined('BASEPATH') OR exit('No direct script access allowed');
/**
* oci8 Result Class
@@ -21,37 +44,41 @@
* This class extends the parent result class: CI_DB_result
*
* @category Database
- * @author ExpressionEngine Dev Team
- * @link http://codeigniter.com/user_guide/database/
+ * @author EllisLab Dev Team
+ * @link https://codeigniter.com/userguide3/database/
*/
class CI_DB_oci8_result extends CI_DB_result {
- public $stmt_id;
- public $curs_id;
+ /**
+ * Limit used flag
+ *
+ * @var bool
+ */
public $limit_used;
/**
- * Number of rows in the result set.
+ * Commit mode flag
*
- * Oracle doesn't have a graceful way to retun the number of rows
- * so we have to use what amounts to a hack.
+ * @var int
+ */
+ public $commit_mode;
+
+ // --------------------------------------------------------------------
+
+ /**
+ * Class constructor
*
- * @return integer
+ * @param object &$driver_object
+ * @return void
*/
- public function num_rows()
+ public function __construct(&$driver_object)
{
- if ($this->num_rows === 0 && count($this->result_array()) > 0)
- {
- $this->num_rows = count($this->result_array());
- @oci_execute($this->stmt_id, OCI_DEFAULT);
-
- if ($this->curs_id)
- {
- @oci_execute($this->curs_id, OCI_DEFAULT);
- }
- }
+ parent::__construct($driver_object);
- return $this->num_rows;
+ $this->result_id = $driver_object->result_id;
+ $this->limit_used = $driver_object->limit_used;
+ $this->commit_mode =& $driver_object->commit_mode;
+ $driver_object->result_id = FALSE;
}
// --------------------------------------------------------------------
@@ -59,20 +86,14 @@ class CI_DB_oci8_result extends CI_DB_result {
/**
* Number of fields in the result set
*
- * @access public
- * @return integer
+ * @return int
*/
public function num_fields()
{
- $count = @oci_num_fields($this->stmt_id);
+ $count = oci_num_fields($this->result_id);
// if we used a limit we subtract it
- if ($this->limit_used)
- {
- $count = $count - 1;
- }
-
- return $count;
+ return ($this->limit_used) ? $count - 1 : $count;
}
// --------------------------------------------------------------------
@@ -82,7 +103,6 @@ class CI_DB_oci8_result extends CI_DB_result {
*
* Generates an array of column names
*
- * @access public
* @return array
*/
public function list_fields()
@@ -90,7 +110,7 @@ class CI_DB_oci8_result extends CI_DB_result {
$field_names = array();
for ($c = 1, $fieldCount = $this->num_fields(); $c <= $fieldCount; $c++)
{
- $field_names[] = oci_field_name($this->stmt_id, $c);
+ $field_names[] = oci_field_name($this->result_id, $c);
}
return $field_names;
}
@@ -102,18 +122,17 @@ class CI_DB_oci8_result extends CI_DB_result {
*
* Generates an array of objects containing field meta-data
*
- * @access public
- * @return array
+ * @return array
*/
public function field_data()
{
$retval = array();
for ($c = 1, $fieldCount = $this->num_fields(); $c <= $fieldCount; $c++)
{
- $F = new stdClass();
- $F->name = oci_field_name($this->stmt_id, $c);
- $F->type = oci_field_type($this->stmt_id, $c);
- $F->max_length = oci_field_size($this->stmt_id, $c);
+ $F = new stdClass();
+ $F->name = oci_field_name($this->result_id, $c);
+ $F->type = oci_field_type($this->result_id, $c);
+ $F->max_length = oci_field_size($this->result_id, $c);
$retval[] = $F;
}
@@ -126,7 +145,7 @@ class CI_DB_oci8_result extends CI_DB_result {
/**
* Free the result
*
- * @return null
+ * @return void
*/
public function free_result()
{
@@ -144,13 +163,11 @@ class CI_DB_oci8_result extends CI_DB_result {
*
* Returns the result set as an array
*
- * @access protected
- * @return array
+ * @return array
*/
protected function _fetch_assoc()
{
- $id = ($this->curs_id) ? $this->curs_id : $this->stmt_id;
- return oci_fetch_assoc($id);
+ return oci_fetch_assoc($this->result_id);
}
// --------------------------------------------------------------------
@@ -160,58 +177,39 @@ class CI_DB_oci8_result extends CI_DB_result {
*
* Returns the result set as an object
*
- * @access protected
- * @return object
+ * @param string $class_name
+ * @return object
*/
- protected function _fetch_object()
+ protected function _fetch_object($class_name = 'stdClass')
{
- $id = ($this->curs_id) ? $this->curs_id : $this->stmt_id;
- return @oci_fetch_object($id);
- }
-
- // --------------------------------------------------------------------
+ $row = oci_fetch_object($this->result_id);
- /**
- * Query result. "array" version.
- *
- * @access public
- * @return array
- */
- public function result_array()
- {
- if (count($this->result_array) > 0)
+ if ($class_name === 'stdClass' OR ! $row)
{
- return $this->result_array;
+ return $row;
}
- $row = NULL;
- while ($row = $this->_fetch_assoc())
+ $class_name = new $class_name();
+ foreach ($row as $key => $value)
{
- $this->result_array[] = $row;
+ $class_name->$key = $value;
}
- return $this->result_array;
+ return $class_name;
}
// --------------------------------------------------------------------
/**
- * Data Seek
+ * Destructor
*
- * Moves the internal pointer to the desired offset. We call
- * this internally before fetching results to make sure the
- * result set starts at zero
+ * Attempt to free remaining statement IDs.
*
- * @access protected
- * @return array
+ * @see https://github.com/bcit-ci/CodeIgniter/pull/5896
+ * @return void
*/
- protected function _data_seek($n = 0)
+ public function __destruct()
{
- return FALSE; // Not needed
+ $this->free_result();
}
-
}
-
-
-/* End of file oci8_result.php */
-/* Location: ./system/database/drivers/oci8/oci8_result.php */
diff --git a/system/database/drivers/oci8/oci8_utility.php b/system/database/drivers/oci8/oci8_utility.php
index bdad0255d..bcce114f8 100644
--- a/system/database/drivers/oci8/oci8_utility.php
+++ b/system/database/drivers/oci8/oci8_utility.php
@@ -1,87 +1,69 @@
-<?php if ( ! defined('BASEPATH')) exit('No direct script access allowed');
+<?php
/**
* CodeIgniter
*
- * An open source application development framework for PHP 5.1.6 or newer
+ * An open source application development framework for PHP
*
- * @package CodeIgniter
- * @author ExpressionEngine Dev Team
- * @copyright Copyright (c) 2008 - 2014, EllisLab, Inc.
- * @license http://codeigniter.com/user_guide/license.html
- * @link http://codeigniter.com
- * @since Version 1.0
+ * This content is released under the MIT License (MIT)
+ *
+ * Copyright (c) 2019 - 2022, CodeIgniter Foundation
+ *
+ * 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.
+ *
+ * @package CodeIgniter
+ * @author EllisLab Dev Team
+ * @copyright Copyright (c) 2008 - 2014, EllisLab, Inc. (https://ellislab.com/)
+ * @copyright Copyright (c) 2014 - 2019, British Columbia Institute of Technology (https://bcit.ca/)
+ * @copyright Copyright (c) 2019 - 2022, CodeIgniter Foundation (https://codeigniter.com/)
+ * @license https://opensource.org/licenses/MIT MIT License
+ * @link https://codeigniter.com
+ * @since Version 1.4.1
* @filesource
*/
-
-// ------------------------------------------------------------------------
+defined('BASEPATH') OR exit('No direct script access allowed');
/**
* Oracle Utility Class
*
* @category Database
- * @author ExpressionEngine Dev Team
- * @link http://codeigniter.com/user_guide/database/
+ * @author EllisLab Dev Team
+ * @link https://codeigniter.com/userguide3/database/
*/
class CI_DB_oci8_utility extends CI_DB_utility {
/**
- * List databases
+ * List databases statement
*
- * @access private
- * @return bool
+ * @var string
*/
- function _list_databases()
- {
- return FALSE;
- }
-
- // --------------------------------------------------------------------
+ protected $_list_databases = 'SELECT username FROM dba_users'; // Schemas are actual usernames
/**
- * Optimize table query
+ * Export
*
- * Generates a platform-specific query so that a table can be optimized
- *
- * @access private
- * @param string the table name
- * @return object
- */
- function _optimize_table($table)
- {
- return FALSE; // Is this supported in Oracle?
- }
-
- // --------------------------------------------------------------------
-
- /**
- * Repair table query
- *
- * Generates a platform-specific query so that a table can be repaired
- *
- * @access private
- * @param string the table name
- * @return object
- */
- function _repair_table($table)
- {
- return FALSE; // Is this supported in Oracle?
- }
-
- // --------------------------------------------------------------------
-
- /**
- * Oracle Export
- *
- * @access private
- * @param array Preferences
+ * @param array $params Preferences
* @return mixed
*/
- function _backup($params = array())
+ protected function _backup($params = array())
{
// Currently unsupported
- return $this->db->display_error('db_unsuported_feature');
+ return $this->db->display_error('db_unsupported_feature');
}
-}
-/* End of file oci8_utility.php */
-/* Location: ./system/database/drivers/oci8/oci8_utility.php */ \ No newline at end of file
+}
diff --git a/system/database/drivers/odbc/index.html b/system/database/drivers/odbc/index.html
index c942a79ce..bcb7cae34 100644
--- a/system/database/drivers/odbc/index.html
+++ b/system/database/drivers/odbc/index.html
@@ -1,4 +1,5 @@
-<html>
+<!DOCTYPE html>
+<html lang="en">
<head>
<title>403 Forbidden</title>
</head>
@@ -7,4 +8,4 @@
<p>Directory access is forbidden.</p>
</body>
-</html> \ No newline at end of file
+</html>
diff --git a/system/database/drivers/odbc/odbc_driver.php b/system/database/drivers/odbc/odbc_driver.php
index 0e82d57ae..cfb9d5733 100644
--- a/system/database/drivers/odbc/odbc_driver.php
+++ b/system/database/drivers/odbc/odbc_driver.php
@@ -1,617 +1,415 @@
-<?php if ( ! defined('BASEPATH')) exit('No direct script access allowed');
+<?php
/**
* CodeIgniter
*
- * An open source application development framework for PHP 5.1.6 or newer
+ * An open source application development framework for PHP
*
- * @package CodeIgniter
- * @author ExpressionEngine Dev Team
- * @copyright Copyright (c) 2008 - 2014, EllisLab, Inc.
- * @license http://codeigniter.com/user_guide/license.html
- * @link http://codeigniter.com
- * @since Version 1.0
+ * This content is released under the MIT License (MIT)
+ *
+ * Copyright (c) 2019 - 2022, CodeIgniter Foundation
+ *
+ * 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.
+ *
+ * @package CodeIgniter
+ * @author EllisLab Dev Team
+ * @copyright Copyright (c) 2008 - 2014, EllisLab, Inc. (https://ellislab.com/)
+ * @copyright Copyright (c) 2014 - 2019, British Columbia Institute of Technology (https://bcit.ca/)
+ * @copyright Copyright (c) 2019 - 2022, CodeIgniter Foundation (https://codeigniter.com/)
+ * @license https://opensource.org/licenses/MIT MIT License
+ * @link https://codeigniter.com
+ * @since Version 1.3.0
* @filesource
*/
-
-// ------------------------------------------------------------------------
+defined('BASEPATH') OR exit('No direct script access allowed');
/**
* ODBC Database Adapter Class
*
* Note: _DB is an extender class that the app controller
- * creates dynamically based on whether the active record
+ * creates dynamically based on whether the query builder
* class is being used or not.
*
* @package CodeIgniter
* @subpackage Drivers
* @category Database
- * @author ExpressionEngine Dev Team
- * @link http://codeigniter.com/user_guide/database/
+ * @author EllisLab Dev Team
+ * @link https://codeigniter.com/userguide3/database/
*/
-class CI_DB_odbc_driver extends CI_DB {
-
- var $dbdriver = 'odbc';
-
- // the character used to excape - not necessary for ODBC
- var $_escape_char = '';
-
- // clause and character used for LIKE escape sequences
- var $_like_escape_str = " {escape '%s'} ";
- var $_like_escape_chr = '!';
+class CI_DB_odbc_driver extends CI_DB_driver {
/**
- * The syntax to count rows is slightly different across different
- * database engines, so this string appears in each driver and is
- * used for the count_all() and count_all_results() functions.
+ * Database driver
+ *
+ * @var string
*/
- var $_count_string = "SELECT COUNT(*) AS ";
- var $_random_keyword;
-
-
- function __construct($params)
- {
- parent::__construct($params);
-
- $this->_random_keyword = ' RND('.time().')'; // database specific random keyword
- }
+ public $dbdriver = 'odbc';
/**
- * Non-persistent database connection
+ * Database schema
*
- * @access private called by the base class
- * @return resource
+ * @var string
*/
- function db_connect()
- {
- return @odbc_connect($this->hostname, $this->username, $this->password);
- }
+ public $schema = 'public';
// --------------------------------------------------------------------
/**
- * Persistent database connection
+ * Identifier escape character
*
- * @access private called by the base class
- * @return resource
+ * Must be empty for ODBC.
+ *
+ * @var string
*/
- function db_pconnect()
- {
- return @odbc_pconnect($this->hostname, $this->username, $this->password);
- }
-
- // --------------------------------------------------------------------
+ protected $_escape_char = '';
/**
- * Reconnect
- *
- * Keep / reestablish the db connection if no queries have been
- * sent for a length of time exceeding the server's idle timeout
+ * ESCAPE statement string
*
- * @access public
- * @return void
+ * @var string
*/
- function reconnect()
- {
- // not implemented in odbc
- }
-
- // --------------------------------------------------------------------
+ protected $_like_escape_str = " {escape '%s'} ";
/**
- * Select the database
+ * ORDER BY random keyword
*
- * @access private called by the base class
- * @return resource
+ * @var array
*/
- function db_select()
- {
- // Not needed for ODBC
- return TRUE;
- }
+ protected $_random_keyword = array('RND()', 'RND(%d)');
// --------------------------------------------------------------------
/**
- * Set client character set
+ * ODBC result ID resource returned from odbc_prepare()
*
- * @access public
- * @param string
- * @param string
- * @return resource
+ * @var resource
*/
- function db_set_charset($charset, $collation)
- {
- // @todo - add support if needed
- return TRUE;
- }
-
- // --------------------------------------------------------------------
+ private $odbc_result;
/**
- * Version number query string
+ * Values to use with odbc_execute() for prepared statements
*
- * @access public
- * @return string
+ * @var array
*/
- function _version()
- {
- return "SELECT version() AS ver";
- }
+ private $binds = array();
// --------------------------------------------------------------------
/**
- * Execute the query
+ * Class constructor
*
- * @access private called by the base class
- * @param string an SQL query
- * @return resource
+ * @param array $params
+ * @return void
*/
- function _execute($sql)
+ public function __construct($params)
{
- $sql = $this->_prep_query($sql);
- return @odbc_exec($this->conn_id, $sql);
+ parent::__construct($params);
+
+ // Legacy support for DSN in the hostname field
+ if (empty($this->dsn))
+ {
+ $this->dsn = $this->hostname;
+ }
}
// --------------------------------------------------------------------
/**
- * Prep the query
- *
- * If needed, each database adapter can prep the query string
+ * Non-persistent database connection
*
- * @access private called by execute()
- * @param string an SQL query
- * @return string
+ * @param bool $persistent
+ * @return resource
*/
- function _prep_query($sql)
+ public function db_connect($persistent = FALSE)
{
- return $sql;
+ return ($persistent === TRUE)
+ ? odbc_pconnect($this->dsn, $this->username, $this->password)
+ : odbc_connect($this->dsn, $this->username, $this->password);
}
// --------------------------------------------------------------------
/**
- * Begin Transaction
+ * Compile Bindings
*
- * @access public
- * @return bool
+ * @param string $sql SQL statement
+ * @param array $binds An array of values to bind
+ * @return string
*/
- function trans_begin($test_mode = FALSE)
+ public function compile_binds($sql, $binds)
{
- if ( ! $this->trans_enabled)
+ if (empty($binds) OR empty($this->bind_marker) OR strpos($sql, $this->bind_marker) === FALSE)
{
- return TRUE;
+ return $sql;
}
-
- // When transactions are nested we only begin/commit/rollback the outermost ones
- if ($this->_trans_depth > 0)
+ elseif ( ! is_array($binds))
{
- return TRUE;
+ $binds = array($binds);
+ $bind_count = 1;
+ }
+ else
+ {
+ // Make sure we're using numeric keys
+ $binds = array_values($binds);
+ $bind_count = count($binds);
}
- // Reset the transaction failure flag.
- // If the $test_mode flag is set to TRUE transactions will be rolled back
- // even if the queries produce a successful result.
- $this->_trans_failure = ($test_mode === TRUE) ? TRUE : FALSE;
-
- return odbc_autocommit($this->conn_id, FALSE);
- }
-
- // --------------------------------------------------------------------
+ // We'll need the marker length later
+ $ml = strlen($this->bind_marker);
- /**
- * Commit Transaction
- *
- * @access public
- * @return bool
- */
- function trans_commit()
- {
- if ( ! $this->trans_enabled)
+ // Make sure not to replace a chunk inside a string that happens to match the bind marker
+ if ($c = preg_match_all("/'[^']*'|\"[^\"]*\"/i", $sql, $matches))
{
- return TRUE;
+ $c = preg_match_all('/'.preg_quote($this->bind_marker, '/').'/i',
+ str_replace($matches[0],
+ str_replace($this->bind_marker, str_repeat(' ', $ml), $matches[0]),
+ $sql, $c),
+ $matches, PREG_OFFSET_CAPTURE);
+
+ // Bind values' count must match the count of markers in the query
+ if ($bind_count !== $c)
+ {
+ return $sql;
+ }
}
-
- // When transactions are nested we only begin/commit/rollback the outermost ones
- if ($this->_trans_depth > 0)
+ elseif (($c = preg_match_all('/'.preg_quote($this->bind_marker, '/').'/i', $sql, $matches, PREG_OFFSET_CAPTURE)) !== $bind_count)
{
- return TRUE;
+ return $sql;
}
- $ret = odbc_commit($this->conn_id);
- odbc_autocommit($this->conn_id, TRUE);
- return $ret;
- }
-
- // --------------------------------------------------------------------
-
- /**
- * Rollback Transaction
- *
- * @access public
- * @return bool
- */
- function trans_rollback()
- {
- if ( ! $this->trans_enabled)
+ if ($this->bind_marker !== '?')
{
- return TRUE;
+ do
+ {
+ $c--;
+ $sql = substr_replace($sql, '?', $matches[0][$c][1], $ml);
+ }
+ while ($c !== 0);
}
- // When transactions are nested we only begin/commit/rollback the outermost ones
- if ($this->_trans_depth > 0)
+ if (FALSE !== ($this->odbc_result = odbc_prepare($this->conn_id, $sql)))
{
- return TRUE;
+ $this->binds = array_values($binds);
}
- $ret = odbc_rollback($this->conn_id);
- odbc_autocommit($this->conn_id, TRUE);
- return $ret;
+ return $sql;
}
// --------------------------------------------------------------------
/**
- * Escape String
+ * Execute the query
*
- * @access public
- * @param string
- * @param bool whether or not the string will be used in a LIKE condition
- * @return string
+ * @param string $sql an SQL query
+ * @return resource
*/
- function escape_str($str, $like = FALSE)
+ protected function _execute($sql)
{
- if (is_array($str))
+ if ( ! isset($this->odbc_result))
{
- foreach ($str as $key => $val)
- {
- $str[$key] = $this->escape_str($val, $like);
- }
-
- return $str;
+ return odbc_exec($this->conn_id, $sql);
+ }
+ elseif ($this->odbc_result === FALSE)
+ {
+ return FALSE;
}
- // ODBC doesn't require escaping
- $str = remove_invisible_characters($str);
-
- // escape LIKE condition wildcards
- if ($like === TRUE)
+ if (TRUE === ($success = odbc_execute($this->odbc_result, $this->binds)))
{
- $str = str_replace( array('%', '_', $this->_like_escape_chr),
- array($this->_like_escape_chr.'%', $this->_like_escape_chr.'_', $this->_like_escape_chr.$this->_like_escape_chr),
- $str);
+ // For queries that return result sets, return the result_id resource on success
+ $this->is_write_type($sql) OR $success = $this->odbc_result;
}
- return $str;
- }
+ $this->odbc_result = NULL;
+ $this->binds = array();
- // --------------------------------------------------------------------
-
- /**
- * Affected Rows
- *
- * @access public
- * @return integer
- */
- function affected_rows()
- {
- return @odbc_num_rows($this->conn_id);
+ return $success;
}
// --------------------------------------------------------------------
/**
- * Insert ID
+ * Begin Transaction
*
- * @access public
- * @return integer
+ * @return bool
*/
- function insert_id()
+ protected function _trans_begin()
{
- return @odbc_insert_id($this->conn_id);
+ return odbc_autocommit($this->conn_id, FALSE);
}
// --------------------------------------------------------------------
/**
- * "Count All" query
- *
- * Generates a platform-specific query string that counts all records in
- * the specified database
+ * Commit Transaction
*
- * @access public
- * @param string
- * @return string
+ * @return bool
*/
- function count_all($table = '')
+ protected function _trans_commit()
{
- if ($table == '')
+ if (odbc_commit($this->conn_id))
{
- return 0;
- }
-
- $query = $this->query($this->_count_string . $this->_protect_identifiers('numrows') . " FROM " . $this->_protect_identifiers($table, TRUE, NULL, FALSE));
-
- if ($query->num_rows() == 0)
- {
- return 0;
+ odbc_autocommit($this->conn_id, TRUE);
+ return TRUE;
}
- $row = $query->row();
- $this->_reset_select();
- return (int) $row->numrows;
+ return FALSE;
}
// --------------------------------------------------------------------
/**
- * Show table query
- *
- * Generates a platform-specific query string so that the table names can be fetched
+ * Rollback Transaction
*
- * @access private
- * @param boolean
- * @return string
+ * @return bool
*/
- function _list_tables($prefix_limit = FALSE)
+ protected function _trans_rollback()
{
- $sql = "SHOW TABLES FROM `".$this->database."`";
-
- if ($prefix_limit !== FALSE AND $this->dbprefix != '')
+ if (odbc_rollback($this->conn_id))
{
- //$sql .= " LIKE '".$this->escape_like_str($this->dbprefix)."%' ".sprintf($this->_like_escape_str, $this->_like_escape_chr);
- return FALSE; // not currently supported
+ odbc_autocommit($this->conn_id, TRUE);
+ return TRUE;
}
- return $sql;
+ return FALSE;
}
// --------------------------------------------------------------------
/**
- * Show column query
- *
- * Generates a platform-specific query string so that the column names can be fetched
+ * Determines if a query is a "write" type.
*
- * @access public
- * @param string the table name
- * @return string
- */
- function _list_columns($table = '')
- {
- return "SHOW COLUMNS FROM ".$table;
- }
-
- // --------------------------------------------------------------------
-
- /**
- * Field data query
- *
- * Generates a platform-specific query so that the column data can be retrieved
- *
- * @access public
- * @param string the table name
- * @return object
+ * @param string An SQL query string
+ * @return bool
*/
- function _field_data($table)
+ public function is_write_type($sql)
{
- return "SELECT TOP 1 FROM ".$table;
- }
-
- // --------------------------------------------------------------------
+ if (preg_match('#^(INSERT|UPDATE).*RETURNING\s.+(\,\s?.+)*$#is', $sql))
+ {
+ return FALSE;
+ }
- /**
- * The error message string
- *
- * @access private
- * @return string
- */
- function _error_message()
- {
- return odbc_errormsg($this->conn_id);
+ return parent::is_write_type($sql);
}
// --------------------------------------------------------------------
/**
- * The error message number
+ * Platform-dependent string escape
*
- * @access private
- * @return integer
- */
- function _error_number()
- {
- return odbc_error($this->conn_id);
- }
-
- // --------------------------------------------------------------------
-
- /**
- * Escape the SQL Identifiers
- *
- * This function escapes column and table names
- *
- * @access private
* @param string
* @return string
*/
- function _escape_identifiers($item)
+ protected function _escape_str($str)
{
- if ($this->_escape_char == '')
- {
- return $item;
- }
-
- foreach ($this->_reserved_identifiers as $id)
- {
- if (strpos($item, '.'.$id) !== FALSE)
- {
- $str = $this->_escape_char. str_replace('.', $this->_escape_char.'.', $item);
-
- // remove duplicates if the user already included the escape
- return preg_replace('/['.$this->_escape_char.']+/', $this->_escape_char, $str);
- }
- }
-
- if (strpos($item, '.') !== FALSE)
- {
- $str = $this->_escape_char.str_replace('.', $this->_escape_char.'.'.$this->_escape_char, $item).$this->_escape_char;
- }
- else
- {
- $str = $this->_escape_char.$item.$this->_escape_char;
- }
-
- // remove duplicates if the user already included the escape
- return preg_replace('/['.$this->_escape_char.']+/', $this->_escape_char, $str);
+ $this->display_error('db_unsupported_feature');
}
// --------------------------------------------------------------------
/**
- * From Tables
- *
- * This function implicitly groups FROM tables so there is no confusion
- * about operator precedence in harmony with SQL standards
+ * Affected Rows
*
- * @access public
- * @param type
- * @return type
+ * @return int
*/
- function _from_tables($tables)
+ public function affected_rows()
{
- if ( ! is_array($tables))
- {
- $tables = array($tables);
- }
-
- return '('.implode(', ', $tables).')';
+ return odbc_num_rows($this->result_id);
}
// --------------------------------------------------------------------
/**
- * Insert statement
- *
- * Generates a platform-specific insert string from the supplied data
+ * Insert ID
*
- * @access public
- * @param string the table name
- * @param array the insert keys
- * @param array the insert values
- * @return string
+ * @return bool
*/
- function _insert($table, $keys, $values)
+ public function insert_id()
{
- return "INSERT INTO ".$table." (".implode(', ', $keys).") VALUES (".implode(', ', $values).")";
+ return ($this->db_debug) ? $this->display_error('db_unsupported_feature') : FALSE;
}
// --------------------------------------------------------------------
/**
- * Update statement
+ * Show table query
*
- * Generates a platform-specific update string from the supplied data
+ * Generates a platform-specific query string so that the table names can be fetched
*
- * @access public
- * @param string the table name
- * @param array the update data
- * @param array the where clause
- * @param array the orderby clause
- * @param array the limit clause
+ * @param bool $prefix_limit
* @return string
*/
- function _update($table, $values, $where, $orderby = array(), $limit = FALSE)
+ protected function _list_tables($prefix_limit = FALSE)
{
- foreach ($values as $key => $val)
+ $sql = "SELECT table_name FROM information_schema.tables WHERE table_schema = '".$this->schema."'";
+
+ if ($prefix_limit !== FALSE && $this->dbprefix !== '')
{
- $valstr[] = $key." = ".$val;
+ return $sql." AND table_name LIKE '".$this->escape_like_str($this->dbprefix)."%' "
+ .sprintf($this->_like_escape_str, $this->_like_escape_chr);
}
- $limit = ( ! $limit) ? '' : ' LIMIT '.$limit;
-
- $orderby = (count($orderby) >= 1)?' ORDER BY '.implode(", ", $orderby):'';
-
- $sql = "UPDATE ".$table." SET ".implode(', ', $valstr);
-
- $sql .= ($where != '' AND count($where) >=1) ? " WHERE ".implode(" ", $where) : '';
-
- $sql .= $orderby.$limit;
-
return $sql;
}
-
// --------------------------------------------------------------------
/**
- * Truncate statement
+ * Show column query
*
- * Generates a platform-specific truncate string from the supplied data
- * If the database does not support the truncate() command
- * This function maps to "DELETE FROM table"
+ * Generates a platform-specific query string so that the column names can be fetched
*
- * @access public
- * @param string the table name
+ * @param string $table
* @return string
*/
- function _truncate($table)
+ protected function _list_columns($table = '')
{
- return $this->_delete($table);
+ return 'SHOW COLUMNS FROM '.$table;
}
// --------------------------------------------------------------------
/**
- * Delete statement
+ * Field data query
*
- * Generates a platform-specific delete string from the supplied data
+ * Generates a platform-specific query so that the column data can be retrieved
*
- * @access public
- * @param string the table name
- * @param array the where clause
- * @param string the limit clause
+ * @param string $table
* @return string
*/
- function _delete($table, $where = array(), $like = array(), $limit = FALSE)
+ protected function _field_data($table)
{
- $conditions = '';
-
- if (count($where) > 0 OR count($like) > 0)
- {
- $conditions = "\nWHERE ";
- $conditions .= implode("\n", $this->ar_where);
-
- if (count($where) > 0 && count($like) > 0)
- {
- $conditions .= " AND ";
- }
- $conditions .= implode("\n", $like);
- }
-
- $limit = ( ! $limit) ? '' : ' LIMIT '.$limit;
-
- return "DELETE FROM ".$table.$conditions.$limit;
+ return 'SELECT TOP 1 FROM '.$table;
}
// --------------------------------------------------------------------
/**
- * Limit string
+ * Error
*
- * Generates a platform-specific LIMIT clause
+ * Returns an array containing code and message of the last
+ * database error that has occurred.
*
- * @access public
- * @param string the sql query string
- * @param integer the number of rows to limit the query to
- * @param integer the offset value
- * @return string
+ * @return array
*/
- function _limit($sql, $limit, $offset)
+ public function error()
{
- // Does ODBC doesn't use the LIMIT clause?
- return $sql;
+ return array('code' => odbc_error($this->conn_id), 'message' => odbc_errormsg($this->conn_id));
}
// --------------------------------------------------------------------
@@ -619,19 +417,10 @@ class CI_DB_odbc_driver extends CI_DB {
/**
* Close DB Connection
*
- * @access public
- * @param resource
* @return void
*/
- function _close($conn_id)
+ protected function _close()
{
- @odbc_close($conn_id);
+ odbc_close($this->conn_id);
}
-
-
}
-
-
-
-/* End of file odbc_driver.php */
-/* Location: ./system/database/drivers/odbc/odbc_driver.php */ \ No newline at end of file
diff --git a/system/database/drivers/odbc/odbc_forge.php b/system/database/drivers/odbc/odbc_forge.php
index 46ba5c5bc..115d08a3d 100644
--- a/system/database/drivers/odbc/odbc_forge.php
+++ b/system/database/drivers/odbc/odbc_forge.php
@@ -1,266 +1,87 @@
-<?php if ( ! defined('BASEPATH')) exit('No direct script access allowed');
+<?php
/**
* CodeIgniter
*
- * An open source application development framework for PHP 5.1.6 or newer
+ * An open source application development framework for PHP
*
- * @package CodeIgniter
- * @author ExpressionEngine Dev Team
- * @copyright Copyright (c) 2008 - 2014, EllisLab, Inc.
- * @license http://codeigniter.com/user_guide/license.html
- * @link http://codeigniter.com
- * @since Version 1.0
+ * This content is released under the MIT License (MIT)
+ *
+ * Copyright (c) 2019 - 2022, CodeIgniter Foundation
+ *
+ * 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.
+ *
+ * @package CodeIgniter
+ * @author EllisLab Dev Team
+ * @copyright Copyright (c) 2008 - 2014, EllisLab, Inc. (https://ellislab.com/)
+ * @copyright Copyright (c) 2014 - 2019, British Columbia Institute of Technology (https://bcit.ca/)
+ * @copyright Copyright (c) 2019 - 2022, CodeIgniter Foundation (https://codeigniter.com/)
+ * @license https://opensource.org/licenses/MIT MIT License
+ * @link https://codeigniter.com
+ * @since Version 1.3.0
* @filesource
*/
-
-// ------------------------------------------------------------------------
+defined('BASEPATH') OR exit('No direct script access allowed');
/**
* ODBC Forge Class
*
+ * @package CodeIgniter
+ * @subpackage Drivers
* @category Database
- * @author ExpressionEngine Dev Team
- * @link http://codeigniter.com/database/
+ * @author EllisLab Dev Team
+ * @link https://codeigniter.com/database/
*/
class CI_DB_odbc_forge extends CI_DB_forge {
/**
- * Create database
- *
- * @access private
- * @param string the database name
- * @return bool
- */
- function _create_database()
- {
- // ODBC has no "create database" command since it's
- // designed to connect to an existing database
- if ($this->db->db_debug)
- {
- return $this->db->display_error('db_unsuported_feature');
- }
- return FALSE;
- }
-
- // --------------------------------------------------------------------
-
- /**
- * Drop database
+ * CREATE TABLE IF statement
*
- * @access private
- * @param string the database name
- * @return bool
+ * @var string
*/
- function _drop_database($name)
- {
- // ODBC has no "drop database" command since it's
- // designed to connect to an existing database
- if ($this->db->db_debug)
- {
- return $this->db->display_error('db_unsuported_feature');
- }
- return FALSE;
- }
-
- // --------------------------------------------------------------------
-
- /**
- * Create Table
- *
- * @access private
- * @param string the table name
- * @param array the fields
- * @param mixed primary key(s)
- * @param mixed key(s)
- * @param boolean should 'IF NOT EXISTS' be added to the SQL
- * @return bool
- */
- function _create_table($table, $fields, $primary_keys, $keys, $if_not_exists)
- {
- $sql = 'CREATE TABLE ';
-
- if ($if_not_exists === TRUE)
- {
- $sql .= 'IF NOT EXISTS ';
- }
-
- $sql .= $this->db->_escape_identifiers($table)." (";
- $current_field_count = 0;
-
- foreach ($fields as $field=>$attributes)
- {
- // Numeric field names aren't allowed in databases, so if the key is
- // numeric, we know it was assigned by PHP and the developer manually
- // entered the field information, so we'll simply add it to the list
- if (is_numeric($field))
- {
- $sql .= "\n\t$attributes";
- }
- else
- {
- $attributes = array_change_key_case($attributes, CASE_UPPER);
-
- $sql .= "\n\t".$this->db->_protect_identifiers($field);
-
- $sql .= ' '.$attributes['TYPE'];
-
- if (array_key_exists('CONSTRAINT', $attributes))
- {
- $sql .= '('.$attributes['CONSTRAINT'].')';
- }
-
- if (array_key_exists('UNSIGNED', $attributes) && $attributes['UNSIGNED'] === TRUE)
- {
- $sql .= ' UNSIGNED';
- }
-
- if (array_key_exists('DEFAULT', $attributes))
- {
- $sql .= ' DEFAULT \''.$attributes['DEFAULT'].'\'';
- }
-
- if (array_key_exists('NULL', $attributes) && $attributes['NULL'] === TRUE)
- {
- $sql .= ' NULL';
- }
- else
- {
- $sql .= ' NOT NULL';
- }
-
- if (array_key_exists('AUTO_INCREMENT', $attributes) && $attributes['AUTO_INCREMENT'] === TRUE)
- {
- $sql .= ' AUTO_INCREMENT';
- }
- }
-
- // don't add a comma on the end of the last field
- if (++$current_field_count < count($fields))
- {
- $sql .= ',';
- }
- }
-
- if (count($primary_keys) > 0)
- {
- $primary_keys = $this->db->_protect_identifiers($primary_keys);
- $sql .= ",\n\tPRIMARY KEY (" . implode(', ', $primary_keys) . ")";
- }
-
- if (is_array($keys) && count($keys) > 0)
- {
- foreach ($keys as $key)
- {
- if (is_array($key))
- {
- $key = $this->db->_protect_identifiers($key);
- }
- else
- {
- $key = array($this->db->_protect_identifiers($key));
- }
-
- $sql .= ",\n\tFOREIGN KEY (" . implode(', ', $key) . ")";
- }
- }
-
- $sql .= "\n)";
-
- return $sql;
- }
-
- // --------------------------------------------------------------------
+ protected $_create_table_if = FALSE;
/**
- * Drop Table
+ * DROP TABLE IF statement
*
- * @access private
- * @return bool
+ * @var string
*/
- function _drop_table($table)
- {
- // Not a supported ODBC feature
- if ($this->db->db_debug)
- {
- return $this->db->display_error('db_unsuported_feature');
- }
- return FALSE;
- }
-
- // --------------------------------------------------------------------
+ protected $_drop_table_if = FALSE;
/**
- * Alter table query
- *
- * Generates a platform-specific query so that a table can be altered
- * Called by add_column(), drop_column(), and column_alter(),
+ * UNSIGNED support
*
- * @access private
- * @param string the ALTER type (ADD, DROP, CHANGE)
- * @param string the column name
- * @param string the table name
- * @param string the column definition
- * @param string the default value
- * @param boolean should 'NOT NULL' be added
- * @param string the field after which we should add the new field
- * @return object
+ * @var bool|array
*/
- function _alter_table($alter_type, $table, $column_name, $column_definition = '', $default_value = '', $null = '', $after_field = '')
- {
- $sql = 'ALTER TABLE '.$this->db->_protect_identifiers($table)." $alter_type ".$this->db->_protect_identifiers($column_name);
-
- // DROP has everything it needs now.
- if ($alter_type == 'DROP')
- {
- return $sql;
- }
-
- $sql .= " $column_definition";
-
- if ($default_value != '')
- {
- $sql .= " DEFAULT \"$default_value\"";
- }
-
- if ($null === NULL)
- {
- $sql .= ' NULL';
- }
- else
- {
- $sql .= ' NOT NULL';
- }
-
- if ($after_field != '')
- {
- $sql .= ' AFTER ' . $this->db->_protect_identifiers($after_field);
- }
-
- return $sql;
-
- }
-
+ protected $_unsigned = FALSE;
// --------------------------------------------------------------------
/**
- * Rename a table
+ * Field attribute AUTO_INCREMENT
*
- * Generates a platform-specific query so that a table can be renamed
- *
- * @access private
- * @param string the old table name
- * @param string the new table name
- * @return string
+ * @param array &$attributes
+ * @param array &$field
+ * @return void
*/
- function _rename_table($table_name, $new_table_name)
+ protected function _attr_auto_increment(&$attributes, &$field)
{
- $sql = 'ALTER TABLE '.$this->db->_protect_identifiers($table_name)." RENAME TO ".$this->db->_protect_identifiers($new_table_name);
- return $sql;
+ // Not supported (in most databases at least)
}
-
}
-
-/* End of file odbc_forge.php */
-/* Location: ./system/database/drivers/odbc/odbc_forge.php */ \ No newline at end of file
diff --git a/system/database/drivers/odbc/odbc_result.php b/system/database/drivers/odbc/odbc_result.php
index 0963e9796..e5847f108 100644
--- a/system/database/drivers/odbc/odbc_result.php
+++ b/system/database/drivers/odbc/odbc_result.php
@@ -1,40 +1,83 @@
-<?php if ( ! defined('BASEPATH')) exit('No direct script access allowed');
+<?php
/**
* CodeIgniter
*
- * An open source application development framework for PHP 5.1.6 or newer
+ * An open source application development framework for PHP
*
- * @package CodeIgniter
- * @author ExpressionEngine Dev Team
- * @copyright Copyright (c) 2008 - 2014, EllisLab, Inc.
- * @license http://codeigniter.com/user_guide/license.html
- * @link http://codeigniter.com
- * @since Version 1.0
+ * This content is released under the MIT License (MIT)
+ *
+ * Copyright (c) 2019 - 2022, CodeIgniter Foundation
+ *
+ * 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.
+ *
+ * @package CodeIgniter
+ * @author EllisLab Dev Team
+ * @copyright Copyright (c) 2008 - 2014, EllisLab, Inc. (https://ellislab.com/)
+ * @copyright Copyright (c) 2014 - 2019, British Columbia Institute of Technology (https://bcit.ca/)
+ * @copyright Copyright (c) 2019 - 2022, CodeIgniter Foundation (https://codeigniter.com/)
+ * @license https://opensource.org/licenses/MIT MIT License
+ * @link https://codeigniter.com
+ * @since Version 1.3.0
* @filesource
*/
-
-// ------------------------------------------------------------------------
+defined('BASEPATH') OR exit('No direct script access allowed');
/**
* ODBC Result Class
*
* This class extends the parent result class: CI_DB_result
*
+ * @package CodeIgniter
+ * @subpackage Drivers
* @category Database
- * @author ExpressionEngine Dev Team
- * @link http://codeigniter.com/user_guide/database/
+ * @author EllisLab Dev Team
+ * @link https://codeigniter.com/userguide3/database/
*/
class CI_DB_odbc_result extends CI_DB_result {
/**
* Number of rows in the result set
*
- * @access public
- * @return integer
+ * @return int
*/
- function num_rows()
+ public function num_rows()
{
- return @odbc_num_rows($this->result_id);
+ if (is_int($this->num_rows))
+ {
+ return $this->num_rows;
+ }
+ elseif (($this->num_rows = odbc_num_rows($this->result_id)) !== -1)
+ {
+ return $this->num_rows;
+ }
+
+ // Work-around for ODBC subdrivers that don't support num_rows()
+ if (count($this->result_array) > 0)
+ {
+ return $this->num_rows = count($this->result_array);
+ }
+ elseif (count($this->result_object) > 0)
+ {
+ return $this->num_rows = count($this->result_object);
+ }
+
+ return $this->num_rows = count($this->result_array());
}
// --------------------------------------------------------------------
@@ -42,12 +85,11 @@ class CI_DB_odbc_result extends CI_DB_result {
/**
* Number of fields in the result set
*
- * @access public
- * @return integer
+ * @return int
*/
- function num_fields()
+ public function num_fields()
{
- return @odbc_num_fields($this->result_id);
+ return odbc_num_fields($this->result_id);
}
// --------------------------------------------------------------------
@@ -57,15 +99,19 @@ class CI_DB_odbc_result extends CI_DB_result {
*
* Generates an array of column names
*
- * @access public
* @return array
*/
- function list_fields()
+ public function list_fields()
{
$field_names = array();
- for ($i = 0; $i < $this->num_fields(); $i++)
+ $num_fields = $this->num_fields();
+
+ if ($num_fields > 0)
{
- $field_names[] = odbc_field_name($this->result_id, $i);
+ for ($i = 1; $i <= $num_fields; $i++)
+ {
+ $field_names[] = odbc_field_name($this->result_id, $i);
+ }
}
return $field_names;
@@ -78,22 +124,19 @@ class CI_DB_odbc_result extends CI_DB_result {
*
* Generates an array of objects containing field meta-data
*
- * @access public
* @return array
*/
- function field_data()
+ public function field_data()
{
$retval = array();
- for ($i = 0; $i < $this->num_fields(); $i++)
+ for ($i = 0, $odbc_index = 1, $c = $this->num_fields(); $i < $c; $i++, $odbc_index++)
{
- $F = new stdClass();
- $F->name = odbc_field_name($this->result_id, $i);
- $F->type = odbc_field_type($this->result_id, $i);
- $F->max_length = odbc_field_len($this->result_id, $i);
- $F->primary_key = 0;
- $F->default = '';
-
- $retval[] = $F;
+ $retval[$i] = new stdClass();
+ $retval[$i]->name = odbc_field_name($this->result_id, $odbc_index);
+ $retval[$i]->type = odbc_field_type($this->result_id, $odbc_index);
+ $retval[$i]->max_length = odbc_field_len($this->result_id, $odbc_index);
+ $retval[$i]->primary_key = 0;
+ $retval[$i]->default = '';
}
return $retval;
@@ -104,9 +147,9 @@ class CI_DB_odbc_result extends CI_DB_result {
/**
* Free the result
*
- * @return null
+ * @return void
*/
- function free_result()
+ public function free_result()
{
if (is_resource($this->result_id))
{
@@ -118,111 +161,109 @@ class CI_DB_odbc_result extends CI_DB_result {
// --------------------------------------------------------------------
/**
- * Data Seek
+ * Result - associative array
*
- * Moves the internal pointer to the desired offset. We call
- * this internally before fetching results to make sure the
- * result set starts at zero
+ * Returns the result set as an array
*
- * @access private
* @return array
*/
- function _data_seek($n = 0)
+ protected function _fetch_assoc()
{
- return FALSE;
+ return odbc_fetch_array($this->result_id);
}
// --------------------------------------------------------------------
/**
- * Result - associative array
+ * Result - object
*
- * Returns the result set as an array
+ * Returns the result set as an object
*
- * @access private
- * @return array
+ * @param string $class_name
+ * @return object
*/
- function _fetch_assoc()
+ protected function _fetch_object($class_name = 'stdClass')
{
- if (function_exists('odbc_fetch_object'))
+ $row = odbc_fetch_object($this->result_id);
+
+ if ($class_name === 'stdClass' OR ! $row)
{
- return odbc_fetch_array($this->result_id);
+ return $row;
}
- else
+
+ $class_name = new $class_name();
+ foreach ($row as $key => $value)
{
- return $this->_odbc_fetch_array($this->result_id);
+ $class_name->$key = $value;
}
+
+ return $class_name;
}
- // --------------------------------------------------------------------
+}
+// --------------------------------------------------------------------
+
+if ( ! function_exists('odbc_fetch_array'))
+{
/**
- * Result - object
+ * ODBC Fetch array
*
- * Returns the result set as an object
+ * Emulates the native odbc_fetch_array() function when
+ * it is not available (odbc_fetch_array() requires unixODBC)
*
- * @access private
- * @return object
+ * @param resource &$result
+ * @param int $rownumber
+ * @return array
*/
- function _fetch_object()
+ function odbc_fetch_array(&$result, $rownumber = 1)
{
- if (function_exists('odbc_fetch_object'))
+ $rs = array();
+ if ( ! odbc_fetch_into($result, $rs, $rownumber))
{
- return odbc_fetch_object($this->result_id);
+ return FALSE;
}
- else
+
+ $rs_assoc = array();
+ foreach ($rs as $k => $v)
{
- return $this->_odbc_fetch_object($this->result_id);
+ $field_name = odbc_field_name($result, $k+1);
+ $rs_assoc[$field_name] = $v;
}
+
+ return $rs_assoc;
}
+}
+// --------------------------------------------------------------------
+if ( ! function_exists('odbc_fetch_object'))
+{
/**
- * Result - object
+ * ODBC Fetch object
*
- * subsititutes the odbc_fetch_object function when
- * not available (odbc_fetch_object requires unixODBC)
+ * Emulates the native odbc_fetch_object() function when
+ * it is not available.
*
- * @access private
+ * @param resource &$result
+ * @param int $rownumber
* @return object
*/
- function _odbc_fetch_object(& $odbc_result) {
+ function odbc_fetch_object(&$result, $rownumber = 1)
+ {
$rs = array();
- $rs_obj = FALSE;
- if (odbc_fetch_into($odbc_result, $rs)) {
- foreach ($rs as $k=>$v) {
- $field_name= odbc_field_name($odbc_result, $k+1);
- $rs_obj->$field_name = $v;
- }
+ if ( ! odbc_fetch_into($result, $rs, $rownumber))
+ {
+ return FALSE;
}
- return $rs_obj;
- }
-
- /**
- * Result - array
- *
- * subsititutes the odbc_fetch_array function when
- * not available (odbc_fetch_array requires unixODBC)
- *
- * @access private
- * @return array
- */
- function _odbc_fetch_array(& $odbc_result) {
- $rs = array();
- $rs_assoc = FALSE;
- if (odbc_fetch_into($odbc_result, $rs)) {
- $rs_assoc=array();
- foreach ($rs as $k=>$v) {
- $field_name= odbc_field_name($odbc_result, $k+1);
- $rs_assoc[$field_name] = $v;
- }
+ $rs_object = new stdClass();
+ foreach ($rs as $k => $v)
+ {
+ $field_name = odbc_field_name($result, $k+1);
+ $rs_object->$field_name = $v;
}
- return $rs_assoc;
- }
+ return $rs_object;
+ }
}
-
-
-/* End of file odbc_result.php */
-/* Location: ./system/database/drivers/odbc/odbc_result.php */ \ No newline at end of file
diff --git a/system/database/drivers/odbc/odbc_utility.php b/system/database/drivers/odbc/odbc_utility.php
index 293e21b7d..a69ed000c 100644
--- a/system/database/drivers/odbc/odbc_utility.php
+++ b/system/database/drivers/odbc/odbc_utility.php
@@ -1,103 +1,64 @@
-<?php if ( ! defined('BASEPATH')) exit('No direct script access allowed');
+<?php
/**
* CodeIgniter
*
- * An open source application development framework for PHP 5.1.6 or newer
+ * An open source application development framework for PHP
*
- * @package CodeIgniter
- * @author ExpressionEngine Dev Team
- * @copyright Copyright (c) 2008 - 2014, EllisLab, Inc.
- * @license http://codeigniter.com/user_guide/license.html
- * @link http://codeigniter.com
- * @since Version 1.0
+ * This content is released under the MIT License (MIT)
+ *
+ * Copyright (c) 2019 - 2022, CodeIgniter Foundation
+ *
+ * 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.
+ *
+ * @package CodeIgniter
+ * @author EllisLab Dev Team
+ * @copyright Copyright (c) 2008 - 2014, EllisLab, Inc. (https://ellislab.com/)
+ * @copyright Copyright (c) 2014 - 2019, British Columbia Institute of Technology (https://bcit.ca/)
+ * @copyright Copyright (c) 2019 - 2022, CodeIgniter Foundation (https://codeigniter.com/)
+ * @license https://opensource.org/licenses/MIT MIT License
+ * @link https://codeigniter.com
+ * @since Version 1.3.0
* @filesource
*/
-
-// ------------------------------------------------------------------------
+defined('BASEPATH') OR exit('No direct script access allowed');
/**
* ODBC Utility Class
*
+ * @package CodeIgniter
+ * @subpackage Drivers
* @category Database
- * @author ExpressionEngine Dev Team
- * @link http://codeigniter.com/database/
+ * @author EllisLab Dev Team
+ * @link https://codeigniter.com/database/
*/
class CI_DB_odbc_utility extends CI_DB_utility {
/**
- * List databases
- *
- * @access private
- * @return bool
- */
- function _list_databases()
- {
- // Not sure if ODBC lets you list all databases...
- if ($this->db->db_debug)
- {
- return $this->db->display_error('db_unsuported_feature');
- }
- return FALSE;
- }
-
- // --------------------------------------------------------------------
-
- /**
- * Optimize table query
- *
- * Generates a platform-specific query so that a table can be optimized
- *
- * @access private
- * @param string the table name
- * @return object
- */
- function _optimize_table($table)
- {
- // Not a supported ODBC feature
- if ($this->db->db_debug)
- {
- return $this->db->display_error('db_unsuported_feature');
- }
- return FALSE;
- }
-
- // --------------------------------------------------------------------
-
- /**
- * Repair table query
- *
- * Generates a platform-specific query so that a table can be repaired
- *
- * @access private
- * @param string the table name
- * @return object
- */
- function _repair_table($table)
- {
- // Not a supported ODBC feature
- if ($this->db->db_debug)
- {
- return $this->db->display_error('db_unsuported_feature');
- }
- return FALSE;
- }
-
- // --------------------------------------------------------------------
-
- /**
- * ODBC Export
+ * Export
*
- * @access private
- * @param array Preferences
+ * @param array $params Preferences
* @return mixed
*/
- function _backup($params = array())
+ protected function _backup($params = array())
{
// Currently unsupported
- return $this->db->display_error('db_unsuported_feature');
+ return $this->db->display_error('db_unsupported_feature');
}
}
-
-/* End of file odbc_utility.php */
-/* Location: ./system/database/drivers/odbc/odbc_utility.php */ \ No newline at end of file
diff --git a/system/database/drivers/pdo/index.html b/system/database/drivers/pdo/index.html
index c942a79ce..bcb7cae34 100644
--- a/system/database/drivers/pdo/index.html
+++ b/system/database/drivers/pdo/index.html
@@ -1,4 +1,5 @@
-<html>
+<!DOCTYPE html>
+<html lang="en">
<head>
<title>403 Forbidden</title>
</head>
@@ -7,4 +8,4 @@
<p>Directory access is forbidden.</p>
</body>
-</html> \ No newline at end of file
+</html>
diff --git a/system/database/drivers/pdo/pdo_driver.php b/system/database/drivers/pdo/pdo_driver.php
index e0e7dab65..559e86555 100644
--- a/system/database/drivers/pdo/pdo_driver.php
+++ b/system/database/drivers/pdo/pdo_driver.php
@@ -1,791 +1,338 @@
-<?php if ( ! defined('BASEPATH')) exit('No direct script access allowed');
+<?php
/**
* CodeIgniter
*
- * An open source application development framework for PHP 5.1.6 or newer
+ * An open source application development framework for PHP
*
- * @package CodeIgniter
- * @copyright Copyright (c) 2008 - 2014, EllisLab, Inc.
- * @license http://codeigniter.com/user_guide/license.html
- * @author EllisLab Dev Team
- * @link http://codeigniter.com
- * @since Version 2.1.2
+ * This content is released under the MIT License (MIT)
+ *
+ * Copyright (c) 2019 - 2022, CodeIgniter Foundation
+ *
+ * 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.
+ *
+ * @package CodeIgniter
+ * @author EllisLab Dev Team
+ * @copyright Copyright (c) 2008 - 2014, EllisLab, Inc. (https://ellislab.com/)
+ * @copyright Copyright (c) 2014 - 2019, British Columbia Institute of Technology (https://bcit.ca/)
+ * @copyright Copyright (c) 2019 - 2022, CodeIgniter Foundation (https://codeigniter.com/)
+ * @license https://opensource.org/licenses/MIT MIT License
+ * @link https://codeigniter.com
+ * @since Version 2.1.0
* @filesource
*/
-
-// ------------------------------------------------------------------------
+defined('BASEPATH') OR exit('No direct script access allowed');
/**
* PDO Database Adapter Class
*
* Note: _DB is an extender class that the app controller
- * creates dynamically based on whether the active record
+ * creates dynamically based on whether the query builder
* class is being used or not.
*
* @package CodeIgniter
* @subpackage Drivers
* @category Database
* @author EllisLab Dev Team
- * @link http://codeigniter.com/user_guide/database/
+ * @link https://codeigniter.com/userguide3/database/
*/
class CI_DB_pdo_driver extends CI_DB {
- var $dbdriver = 'pdo';
-
- // the character used to excape - not necessary for PDO
- var $_escape_char = '';
- var $_like_escape_str;
- var $_like_escape_chr;
-
-
/**
- * The syntax to count rows is slightly different across different
- * database engines, so this string appears in each driver and is
- * used for the count_all() and count_all_results() functions.
- */
- var $_count_string = "SELECT COUNT(*) AS ";
- var $_random_keyword;
-
- var $options = array();
-
- function __construct($params)
- {
- parent::__construct($params);
-
- // clause and character used for LIKE escape sequences
- if (strpos($this->hostname, 'mysql') !== FALSE)
- {
- $this->_like_escape_str = '';
- $this->_like_escape_chr = '';
-
- //Prior to this version, the charset can't be set in the dsn
- if(is_php('5.3.6'))
- {
- $this->hostname .= ";charset={$this->char_set}";
- }
-
- //Set the charset with the connection options
- $this->options['PDO::MYSQL_ATTR_INIT_COMMAND'] = "SET NAMES {$this->char_set}";
- }
- elseif (strpos($this->hostname, 'odbc') !== FALSE)
- {
- $this->_like_escape_str = " {escape '%s'} ";
- $this->_like_escape_chr = '!';
- }
- else
- {
- $this->_like_escape_str = " ESCAPE '%s' ";
- $this->_like_escape_chr = '!';
- }
-
- empty($this->database) OR $this->hostname .= ';dbname='.$this->database;
-
- $this->trans_enabled = FALSE;
-
- $this->_random_keyword = ' RND('.time().')'; // database specific random keyword
- }
-
- /**
- * Non-persistent database connection
+ * Database driver
*
- * @access private called by the base class
- * @return resource
+ * @var string
*/
- function db_connect()
- {
- $this->options['PDO::ATTR_ERRMODE'] = PDO::ERRMODE_SILENT;
-
- return new PDO($this->hostname, $this->username, $this->password, $this->options);
- }
-
- // --------------------------------------------------------------------
+ public $dbdriver = 'pdo';
/**
- * Persistent database connection
+ * PDO Options
*
- * @access private called by the base class
- * @return resource
+ * @var array
*/
- function db_pconnect()
- {
- $this->options['PDO::ATTR_ERRMODE'] = PDO::ERRMODE_SILENT;
- $this->options['PDO::ATTR_PERSISTENT'] = TRUE;
-
- return new PDO($this->hostname, $this->username, $this->password, $this->options);
- }
+ public $options = array();
// --------------------------------------------------------------------
/**
- * Reconnect
+ * Class constructor
*
- * Keep / reestablish the db connection if no queries have been
- * sent for a length of time exceeding the server's idle timeout
+ * Validates the DSN string and/or detects the subdriver.
*
- * @access public
+ * @param array $params
* @return void
*/
- function reconnect()
+ public function __construct($params)
{
- if ($this->db->db_debug)
- {
- return $this->db->display_error('db_unsuported_feature');
- }
- return FALSE;
- }
-
- // --------------------------------------------------------------------
-
- /**
- * Select the database
- *
- * @access private called by the base class
- * @return resource
- */
- function db_select()
- {
- // Not needed for PDO
- return TRUE;
- }
-
- // --------------------------------------------------------------------
-
- /**
- * Set client character set
- *
- * @access public
- * @param string
- * @param string
- * @return resource
- */
- function db_set_charset($charset, $collation)
- {
- // @todo - add support if needed
- return TRUE;
- }
-
- // --------------------------------------------------------------------
-
- /**
- * Version number query string
- *
- * @access public
- * @return string
- */
- function _version()
- {
- return $this->conn_id->getAttribute(PDO::ATTR_CLIENT_VERSION);
- }
-
- // --------------------------------------------------------------------
-
- /**
- * Execute the query
- *
- * @access private called by the base class
- * @param string an SQL query
- * @return object
- */
- function _execute($sql)
- {
- $sql = $this->_prep_query($sql);
- $result_id = $this->conn_id->prepare($sql);
+ parent::__construct($params);
- if (is_object($result_id) && ($result = $result_id->execute()))
+ if (preg_match('/([^:]+):/', $this->dsn, $match) && count($match) === 2)
{
- if (is_numeric(stripos($sql, 'SELECT')))
- {
- $this->affect_rows = count($result_id->fetchAll());
- }
- else
- {
- $this->affect_rows = $result_id->rowCount();
- }
+ // If there is a minimum valid dsn string pattern found, we're done
+ // This is for general PDO users, who tend to have a full DSN string.
+ $this->subdriver = $match[1];
+ return;
}
- else
+ // Legacy support for DSN specified in the hostname field
+ elseif (preg_match('/([^:]+):/', $this->hostname, $match) && count($match) === 2)
{
- $this->affect_rows = 0;
- $result = FALSE;
+ $this->dsn = $this->hostname;
+ $this->hostname = NULL;
+ $this->subdriver = $match[1];
+ return;
}
-
- return $result;
- }
-
- // --------------------------------------------------------------------
-
- /**
- * Prep the query
- *
- * If needed, each database adapter can prep the query string
- *
- * @access private called by execute()
- * @param string an SQL query
- * @return string
- */
- function _prep_query($sql)
- {
- return $sql;
- }
-
- // --------------------------------------------------------------------
-
- /**
- * Begin Transaction
- *
- * @access public
- * @return bool
- */
- function trans_begin($test_mode = FALSE)
- {
- if ( ! $this->trans_enabled)
+ elseif (in_array($this->subdriver, array('mssql', 'sybase'), TRUE))
{
- return TRUE;
+ $this->subdriver = 'dblib';
}
-
- // When transactions are nested we only begin/commit/rollback the outermost ones
- if ($this->_trans_depth > 0)
+ elseif ($this->subdriver === '4D')
{
- return TRUE;
+ $this->subdriver = '4d';
}
-
- // Reset the transaction failure flag.
- // If the $test_mode flag is set to TRUE transactions will be rolled back
- // even if the queries produce a successful result.
- $this->_trans_failure = (bool) ($test_mode === TRUE);
-
- return $this->conn_id->beginTransaction();
- }
-
- // --------------------------------------------------------------------
-
- /**
- * Commit Transaction
- *
- * @access public
- * @return bool
- */
- function trans_commit()
- {
- if ( ! $this->trans_enabled)
+ elseif ( ! in_array($this->subdriver, array('4d', 'cubrid', 'dblib', 'firebird', 'ibm', 'informix', 'mysql', 'oci', 'odbc', 'pgsql', 'sqlite', 'sqlsrv'), TRUE))
{
- return TRUE;
- }
+ log_message('error', 'PDO: Invalid or non-existent subdriver');
- // When transactions are nested we only begin/commit/rollback the outermost ones
- if ($this->_trans_depth > 0)
- {
- return TRUE;
+ if ($this->db_debug)
+ {
+ show_error('Invalid or non-existent PDO subdriver');
+ }
}
- $ret = $this->conn->commit();
- return $ret;
+ $this->dsn = NULL;
}
// --------------------------------------------------------------------
/**
- * Rollback Transaction
+ * Database connection
*
- * @access public
- * @return bool
+ * @param bool $persistent
+ * @return object
*/
- function trans_rollback()
+ public function db_connect($persistent = FALSE)
{
- if ( ! $this->trans_enabled)
+ if ($persistent === TRUE)
{
- return TRUE;
+ $this->options[PDO::ATTR_PERSISTENT] = TRUE;
}
- // When transactions are nested we only begin/commit/rollback the outermost ones
- if ($this->_trans_depth > 0)
+ // From PHP8.0, default PDO::ATTR_ERRMODE is changed
+ // from PDO::ERRMODE_SILENT to PDO::ERRMODE_EXCEPTION
+ // as https://wiki.php.net/rfc/pdo_default_errmode
+ if ( ! isset($this->options[PDO::ATTR_ERRMODE]))
{
- return TRUE;
+ $this->options[PDO::ATTR_ERRMODE] = PDO::ERRMODE_SILENT;
}
- $ret = $this->conn_id->rollBack();
- return $ret;
- }
-
- // --------------------------------------------------------------------
-
- /**
- * Escape String
- *
- * @access public
- * @param string
- * @param bool whether or not the string will be used in a LIKE condition
- * @return string
- */
- function escape_str($str, $like = FALSE)
- {
- if (is_array($str))
- {
- foreach ($str as $key => $val)
- {
- $str[$key] = $this->escape_str($val, $like);
- }
-
- return $str;
- }
-
- //Escape the string
- $str = $this->conn_id->quote($str);
-
- //If there are duplicated quotes, trim them away
- if (strpos($str, "'") === 0)
+ try
{
- $str = substr($str, 1, -1);
+ return new PDO($this->dsn, $this->username, $this->password, $this->options);
}
-
- // escape LIKE condition wildcards
- if ($like === TRUE)
- {
- $str = str_replace( array('%', '_', $this->_like_escape_chr),
- array($this->_like_escape_chr.'%', $this->_like_escape_chr.'_', $this->_like_escape_chr.$this->_like_escape_chr),
- $str);
- }
-
- return $str;
- }
-
- // --------------------------------------------------------------------
-
- /**
- * Affected Rows
- *
- * @access public
- * @return integer
- */
- function affected_rows()
- {
- return $this->affect_rows;
- }
-
- // --------------------------------------------------------------------
-
- /**
- * Insert ID
- *
- * @access public
- * @return integer
- */
- function insert_id($name=NULL)
- {
- //Convenience method for postgres insertid
- if (strpos($this->hostname, 'pgsql') !== FALSE)
+ catch (PDOException $e)
{
- $v = $this->_version();
-
- $table = func_num_args() > 0 ? func_get_arg(0) : NULL;
-
- if ($table == NULL && $v >= '8.1')
+ if ($this->db_debug && empty($this->failover))
{
- $sql='SELECT LASTVAL() as ins_id';
+ $this->display_error($e->getMessage(), '', TRUE);
}
- $query = $this->query($sql);
- $row = $query->row();
- return $row->ins_id;
- }
- else
- {
- return $this->conn_id->lastInsertId($name);
+
+ return FALSE;
}
}
// --------------------------------------------------------------------
/**
- * "Count All" query
- *
- * Generates a platform-specific query string that counts all records in
- * the specified database
+ * Database version number
*
- * @access public
- * @param string
* @return string
*/
- function count_all($table = '')
+ public function version()
{
- if ($table == '')
+ if (isset($this->data_cache['version']))
{
- return 0;
+ return $this->data_cache['version'];
}
- $query = $this->query($this->_count_string . $this->_protect_identifiers('numrows') . " FROM " . $this->_protect_identifiers($table, TRUE, NULL, FALSE));
-
- if ($query->num_rows() == 0)
+ // Not all subdrivers support the getAttribute() method
+ try
{
- return 0;
+ return $this->data_cache['version'] = $this->conn_id->getAttribute(PDO::ATTR_SERVER_VERSION);
}
-
- $row = $query->row();
- $this->_reset_select();
- return (int) $row->numrows;
- }
-
- // --------------------------------------------------------------------
-
- /**
- * Show table query
- *
- * Generates a platform-specific query string so that the table names can be fetched
- *
- * @access private
- * @param boolean
- * @return string
- */
- function _list_tables($prefix_limit = FALSE)
- {
- $sql = "SHOW TABLES FROM `".$this->database."`";
-
- if ($prefix_limit !== FALSE AND $this->dbprefix != '')
+ catch (PDOException $e)
{
- //$sql .= " LIKE '".$this->escape_like_str($this->dbprefix)."%' ".sprintf($this->_like_escape_str, $this->_like_escape_chr);
- return FALSE; // not currently supported
+ return parent::version();
}
-
- return $sql;
}
// --------------------------------------------------------------------
/**
- * Show column query
- *
- * Generates a platform-specific query string so that the column names can be fetched
+ * Execute the query
*
- * @access public
- * @param string the table name
- * @return string
+ * @param string $sql SQL query
+ * @return mixed
*/
- function _list_columns($table = '')
+ protected function _execute($sql)
{
- return "SHOW COLUMNS FROM ".$table;
+ return $this->conn_id->query($sql);
}
// --------------------------------------------------------------------
/**
- * Field data query
- *
- * Generates a platform-specific query so that the column data can be retrieved
+ * Begin Transaction
*
- * @access public
- * @param string the table name
- * @return object
+ * @return bool
*/
- function _field_data($table)
+ protected function _trans_begin()
{
- return "SELECT TOP 1 FROM ".$table;
+ return $this->conn_id->beginTransaction();
}
// --------------------------------------------------------------------
/**
- * The error message string
+ * Commit Transaction
*
- * @access private
- * @return string
+ * @return bool
*/
- function _error_message()
+ protected function _trans_commit()
{
- $error_array = $this->conn_id->errorInfo();
- return $error_array[2];
+ return $this->conn_id->commit();
}
// --------------------------------------------------------------------
/**
- * The error message number
+ * Rollback Transaction
*
- * @access private
- * @return integer
+ * @return bool
*/
- function _error_number()
+ protected function _trans_rollback()
{
- return $this->conn_id->errorCode();
+ return $this->conn_id->rollBack();
}
// --------------------------------------------------------------------
/**
- * Escape the SQL Identifiers
- *
- * This function escapes column and table names
+ * Platform-dependent string escape
*
- * @access private
* @param string
* @return string
*/
- function _escape_identifiers($item)
+ protected function _escape_str($str)
{
- if ($this->_escape_char == '')
- {
- return $item;
- }
-
- foreach ($this->_reserved_identifiers as $id)
- {
- if (strpos($item, '.'.$id) !== FALSE)
- {
- $str = $this->_escape_char. str_replace('.', $this->_escape_char.'.', $item);
-
- // remove duplicates if the user already included the escape
- return preg_replace('/['.$this->_escape_char.']+/', $this->_escape_char, $str);
- }
- }
-
- if (strpos($item, '.') !== FALSE)
- {
- $str = $this->_escape_char.str_replace('.', $this->_escape_char.'.'.$this->_escape_char, $item).$this->_escape_char;
-
- }
- else
- {
- $str = $this->_escape_char.$item.$this->_escape_char;
- }
+ // Escape the string
+ $str = $this->conn_id->quote($str);
- // remove duplicates if the user already included the escape
- return preg_replace('/['.$this->_escape_char.']+/', $this->_escape_char, $str);
+ // If there are duplicated quotes, trim them away
+ return ($str[0] === "'")
+ ? substr($str, 1, -1)
+ : $str;
}
// --------------------------------------------------------------------
/**
- * From Tables
- *
- * This function implicitly groups FROM tables so there is no confusion
- * about operator precedence in harmony with SQL standards
+ * Affected Rows
*
- * @access public
- * @param type
- * @return type
+ * @return int
*/
- function _from_tables($tables)
+ public function affected_rows()
{
- if ( ! is_array($tables))
- {
- $tables = array($tables);
- }
-
- return (count($tables) == 1) ? $tables[0] : '('.implode(', ', $tables).')';
+ return is_object($this->result_id) ? $this->result_id->rowCount() : 0;
}
// --------------------------------------------------------------------
/**
- * Insert statement
- *
- * Generates a platform-specific insert string from the supplied data
- *
- * @access public
- * @param string the table name
- * @param array the insert keys
- * @param array the insert values
- * @return string
- */
- function _insert($table, $keys, $values)
- {
- return "INSERT INTO ".$table." (".implode(', ', $keys).") VALUES (".implode(', ', $values).")";
- }
-
- // --------------------------------------------------------------------
-
- /**
- * Insert_batch statement
- *
- * Generates a platform-specific insert string from the supplied data
+ * Insert ID
*
- * @access public
- * @param string the table name
- * @param array the insert keys
- * @param array the insert values
- * @return string
+ * @param string $name
+ * @return int
*/
- function _insert_batch($table, $keys, $values)
+ public function insert_id($name = NULL)
{
- return "INSERT INTO ".$table." (".implode(', ', $keys).") VALUES ".implode(', ', $values);
+ return $this->conn_id->lastInsertId($name);
}
// --------------------------------------------------------------------
/**
- * Update statement
+ * Field data query
*
- * Generates a platform-specific update string from the supplied data
+ * Generates a platform-specific query so that the column data can be retrieved
*
- * @access public
- * @param string the table name
- * @param array the update data
- * @param array the where clause
- * @param array the orderby clause
- * @param array the limit clause
+ * @param string $table
* @return string
*/
- function _update($table, $values, $where, $orderby = array(), $limit = FALSE)
+ protected function _field_data($table)
{
- foreach ($values as $key => $val)
- {
- $valstr[] = $key." = ".$val;
- }
-
- $limit = ( ! $limit) ? '' : ' LIMIT '.$limit;
-
- $orderby = (count($orderby) >= 1)?' ORDER BY '.implode(", ", $orderby):'';
-
- $sql = "UPDATE ".$table." SET ".implode(', ', $valstr);
-
- $sql .= ($where != '' AND count($where) >=1) ? " WHERE ".implode(" ", $where) : '';
-
- $sql .= $orderby.$limit;
-
- return $sql;
+ return 'SELECT TOP 1 * FROM '.$this->protect_identifiers($table);
}
-
+
// --------------------------------------------------------------------
/**
- * Update_Batch statement
+ * Error
*
- * Generates a platform-specific batch update string from the supplied data
+ * Returns an array containing code and message of the last
+ * database error that has occurred.
*
- * @access public
- * @param string the table name
- * @param array the update data
- * @param array the where clause
- * @return string
+ * @return array
*/
- function _update_batch($table, $values, $index, $where = NULL)
+ public function error()
{
- $ids = array();
- $where = ($where != '' AND count($where) >=1) ? implode(" ", $where).' AND ' : '';
+ $error = array('code' => '00000', 'message' => '');
+ $pdo_error = $this->conn_id->errorInfo();
- foreach ($values as $key => $val)
+ if (empty($pdo_error[0]))
{
- $ids[] = $val[$index];
-
- foreach (array_keys($val) as $field)
- {
- if ($field != $index)
- {
- $final[$field][] = 'WHEN '.$index.' = '.$val[$index].' THEN '.$val[$field];
- }
- }
+ return $error;
}
- $sql = "UPDATE ".$table." SET ";
- $cases = '';
-
- foreach ($final as $k => $v)
+ $error['code'] = isset($pdo_error[1]) ? $pdo_error[0].'/'.$pdo_error[1] : $pdo_error[0];
+ if (isset($pdo_error[2]))
{
- $cases .= $k.' = CASE '."\n";
- foreach ($v as $row)
- {
- $cases .= $row."\n";
- }
-
- $cases .= 'ELSE '.$k.' END, ';
+ $error['message'] = $pdo_error[2];
}
- $sql .= substr($cases, 0, -2);
-
- $sql .= ' WHERE '.$where.$index.' IN ('.implode(',', $ids).')';
-
- return $sql;
+ return $error;
}
-
// --------------------------------------------------------------------
/**
* Truncate statement
*
* Generates a platform-specific truncate string from the supplied data
- * If the database does not support the truncate() command
- * This function maps to "DELETE FROM table"
*
- * @access public
- * @param string the table name
- * @return string
- */
- function _truncate($table)
- {
- return $this->_delete($table);
- }
-
- // --------------------------------------------------------------------
-
- /**
- * Delete statement
- *
- * Generates a platform-specific delete string from the supplied data
- *
- * @access public
- * @param string the table name
- * @param array the where clause
- * @param string the limit clause
- * @return string
- */
- function _delete($table, $where = array(), $like = array(), $limit = FALSE)
- {
- $conditions = '';
-
- if (count($where) > 0 OR count($like) > 0)
- {
- $conditions = "\nWHERE ";
- $conditions .= implode("\n", $this->ar_where);
-
- if (count($where) > 0 && count($like) > 0)
- {
- $conditions .= " AND ";
- }
- $conditions .= implode("\n", $like);
- }
-
- $limit = ( ! $limit) ? '' : ' LIMIT '.$limit;
-
- return "DELETE FROM ".$table.$conditions.$limit;
- }
-
- // --------------------------------------------------------------------
-
- /**
- * Limit string
- *
- * Generates a platform-specific LIMIT clause
+ * If the database does not support the TRUNCATE statement,
+ * then this method maps to 'DELETE FROM table'
*
- * @access public
- * @param string the sql query string
- * @param integer the number of rows to limit the query to
- * @param integer the offset value
+ * @param string $table
* @return string
*/
- function _limit($sql, $limit, $offset)
+ protected function _truncate($table)
{
- if (strpos($this->hostname, 'cubrid') !== FALSE || strpos($this->hostname, 'sqlite') !== FALSE)
- {
- if ($offset == 0)
- {
- $offset = '';
- }
- else
- {
- $offset .= ", ";
- }
-
- return $sql."LIMIT ".$offset.$limit;
- }
- else
- {
- $sql .= "LIMIT ".$limit;
-
- if ($offset > 0)
- {
- $sql .= " OFFSET ".$offset;
- }
-
- return $sql;
- }
+ return 'TRUNCATE TABLE '.$table;
}
// --------------------------------------------------------------------
@@ -793,19 +340,12 @@ class CI_DB_pdo_driver extends CI_DB {
/**
* Close DB Connection
*
- * @access public
- * @param resource
* @return void
*/
- function _close($conn_id)
+ protected function _close()
{
- $this->conn_id = null;
+ $this->result_id = FALSE;
+ $this->conn_id = FALSE;
}
-
}
-
-
-
-/* End of file pdo_driver.php */
-/* Location: ./system/database/drivers/pdo/pdo_driver.php */ \ No newline at end of file
diff --git a/system/database/drivers/pdo/pdo_forge.php b/system/database/drivers/pdo/pdo_forge.php
index f7beb0a9a..b35ff6777 100644
--- a/system/database/drivers/pdo/pdo_forge.php
+++ b/system/database/drivers/pdo/pdo_forge.php
@@ -1,266 +1,66 @@
-<?php if ( ! defined('BASEPATH')) exit('No direct script access allowed');
+<?php
/**
* CodeIgniter
*
- * An open source application development framework for PHP 5.1.6 or newer
+ * An open source application development framework for PHP
*
- * @package CodeIgniter
- * @copyright Copyright (c) 2008 - 2014, EllisLab, Inc.
- * @license http://codeigniter.com/user_guide/license.html
- * @author EllisLab Dev Team
- * @link http://codeigniter.com
- * @since Version 2.1.2
+ * This content is released under the MIT License (MIT)
+ *
+ * Copyright (c) 2019 - 2022, CodeIgniter Foundation
+ *
+ * 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.
+ *
+ * @package CodeIgniter
+ * @author EllisLab Dev Team
+ * @copyright Copyright (c) 2008 - 2014, EllisLab, Inc. (https://ellislab.com/)
+ * @copyright Copyright (c) 2014 - 2019, British Columbia Institute of Technology (https://bcit.ca/)
+ * @copyright Copyright (c) 2019 - 2022, CodeIgniter Foundation (https://codeigniter.com/)
+ * @license https://opensource.org/licenses/MIT MIT License
+ * @link https://codeigniter.com
+ * @since Version 2.1.0
* @filesource
*/
-
-// ------------------------------------------------------------------------
+defined('BASEPATH') OR exit('No direct script access allowed');
/**
* PDO Forge Class
*
+ * @package CodeIgniter
+ * @subpackage Drivers
* @category Database
* @author EllisLab Dev Team
- * @link http://codeigniter.com/database/
+ * @link https://codeigniter.com/database/
*/
class CI_DB_pdo_forge extends CI_DB_forge {
/**
- * Create database
- *
- * @access private
- * @param string the database name
- * @return bool
- */
- function _create_database()
- {
- // PDO has no "create database" command since it's
- // designed to connect to an existing database
- if ($this->db->db_debug)
- {
- return $this->db->display_error('db_unsuported_feature');
- }
- return FALSE;
- }
-
- // --------------------------------------------------------------------
-
- /**
- * Drop database
- *
- * @access private
- * @param string the database name
- * @return bool
- */
- function _drop_database($name)
- {
- // PDO has no "drop database" command since it's
- // designed to connect to an existing database
- if ($this->db->db_debug)
- {
- return $this->db->display_error('db_unsuported_feature');
- }
- return FALSE;
- }
-
- // --------------------------------------------------------------------
-
- /**
- * Create Table
- *
- * @access private
- * @param string the table name
- * @param array the fields
- * @param mixed primary key(s)
- * @param mixed key(s)
- * @param boolean should 'IF NOT EXISTS' be added to the SQL
- * @return bool
- */
- function _create_table($table, $fields, $primary_keys, $keys, $if_not_exists)
- {
- $sql = 'CREATE TABLE ';
-
- if ($if_not_exists === TRUE)
- {
- $sql .= 'IF NOT EXISTS ';
- }
-
- $sql .= $this->db->_escape_identifiers($table)." (";
- $current_field_count = 0;
-
- foreach ($fields as $field=>$attributes)
- {
- // Numeric field names aren't allowed in databases, so if the key is
- // numeric, we know it was assigned by PHP and the developer manually
- // entered the field information, so we'll simply add it to the list
- if (is_numeric($field))
- {
- $sql .= "\n\t$attributes";
- }
- else
- {
- $attributes = array_change_key_case($attributes, CASE_UPPER);
-
- $sql .= "\n\t".$this->db->_protect_identifiers($field);
-
- $sql .= ' '.$attributes['TYPE'];
-
- if (array_key_exists('CONSTRAINT', $attributes))
- {
- $sql .= '('.$attributes['CONSTRAINT'].')';
- }
-
- if (array_key_exists('UNSIGNED', $attributes) && $attributes['UNSIGNED'] === TRUE)
- {
- $sql .= ' UNSIGNED';
- }
-
- if (array_key_exists('DEFAULT', $attributes))
- {
- $sql .= ' DEFAULT \''.$attributes['DEFAULT'].'\'';
- }
-
- if (array_key_exists('NULL', $attributes) && $attributes['NULL'] === TRUE)
- {
- $sql .= ' NULL';
- }
- else
- {
- $sql .= ' NOT NULL';
- }
-
- if (array_key_exists('AUTO_INCREMENT', $attributes) && $attributes['AUTO_INCREMENT'] === TRUE)
- {
- $sql .= ' AUTO_INCREMENT';
- }
- }
-
- // don't add a comma on the end of the last field
- if (++$current_field_count < count($fields))
- {
- $sql .= ',';
- }
- }
-
- if (count($primary_keys) > 0)
- {
- $primary_keys = $this->db->_protect_identifiers($primary_keys);
- $sql .= ",\n\tPRIMARY KEY (" . implode(', ', $primary_keys) . ")";
- }
-
- if (is_array($keys) && count($keys) > 0)
- {
- foreach ($keys as $key)
- {
- if (is_array($key))
- {
- $key = $this->db->_protect_identifiers($key);
- }
- else
- {
- $key = array($this->db->_protect_identifiers($key));
- }
-
- $sql .= ",\n\tFOREIGN KEY (" . implode(', ', $key) . ")";
- }
- }
-
- $sql .= "\n)";
-
- return $sql;
- }
-
- // --------------------------------------------------------------------
-
- /**
- * Drop Table
+ * CREATE TABLE IF statement
*
- * @access private
- * @return bool
+ * @var string
*/
- function _drop_table($table)
- {
- // Not a supported PDO feature
- if ($this->db->db_debug)
- {
- return $this->db->display_error('db_unsuported_feature');
- }
- return FALSE;
- }
-
- // --------------------------------------------------------------------
+ protected $_create_table_if = FALSE;
/**
- * Alter table query
+ * DROP TABLE IF statement
*
- * Generates a platform-specific query so that a table can be altered
- * Called by add_column(), drop_column(), and column_alter(),
- *
- * @access private
- * @param string the ALTER type (ADD, DROP, CHANGE)
- * @param string the column name
- * @param string the table name
- * @param string the column definition
- * @param string the default value
- * @param boolean should 'NOT NULL' be added
- * @param string the field after which we should add the new field
- * @return object
+ * @var string
*/
- function _alter_table($alter_type, $table, $column_name, $column_definition = '', $default_value = '', $null = '', $after_field = '')
- {
- $sql = 'ALTER TABLE '.$this->db->_protect_identifiers($table)." $alter_type ".$this->db->_protect_identifiers($column_name);
-
- // DROP has everything it needs now.
- if ($alter_type == 'DROP')
- {
- return $sql;
- }
-
- $sql .= " $column_definition";
-
- if ($default_value != '')
- {
- $sql .= " DEFAULT \"$default_value\"";
- }
-
- if ($null === NULL)
- {
- $sql .= ' NULL';
- }
- else
- {
- $sql .= ' NOT NULL';
- }
-
- if ($after_field != '')
- {
- $sql .= ' AFTER ' . $this->db->_protect_identifiers($after_field);
- }
-
- return $sql;
-
- }
-
-
- // --------------------------------------------------------------------
-
- /**
- * Rename a table
- *
- * Generates a platform-specific query so that a table can be renamed
- *
- * @access private
- * @param string the old table name
- * @param string the new table name
- * @return string
- */
- function _rename_table($table_name, $new_table_name)
- {
- $sql = 'ALTER TABLE '.$this->db->_protect_identifiers($table_name)." RENAME TO ".$this->db->_protect_identifiers($new_table_name);
- return $sql;
- }
-
+ protected $_drop_table_if = FALSE;
}
-
-/* End of file pdo_forge.php */
-/* Location: ./system/database/drivers/pdo/pdo_forge.php */ \ No newline at end of file
diff --git a/system/database/drivers/pdo/pdo_result.php b/system/database/drivers/pdo/pdo_result.php
index 4843df43b..bf9e12376 100644
--- a/system/database/drivers/pdo/pdo_result.php
+++ b/system/database/drivers/pdo/pdo_result.php
@@ -1,33 +1,56 @@
-<?php if ( ! defined('BASEPATH')) exit('No direct script access allowed');
+<?php
/**
* CodeIgniter
*
- * An open source application development framework for PHP 5.1.6 or newer
+ * An open source application development framework for PHP
*
- * @package CodeIgniter
- * @copyright Copyright (c) 2008 - 2014, EllisLab, Inc.
- * @license http://codeigniter.com/user_guide/license.html
- * @author EllisLab Dev Team
- * @link http://codeigniter.com
- * @since Version 2.1.2
+ * This content is released under the MIT License (MIT)
+ *
+ * Copyright (c) 2019 - 2022, CodeIgniter Foundation
+ *
+ * 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.
+ *
+ * @package CodeIgniter
+ * @author EllisLab Dev Team
+ * @copyright Copyright (c) 2008 - 2014, EllisLab, Inc. (https://ellislab.com/)
+ * @copyright Copyright (c) 2014 - 2019, British Columbia Institute of Technology (https://bcit.ca/)
+ * @copyright Copyright (c) 2019 - 2022, CodeIgniter Foundation (https://codeigniter.com/)
+ * @license https://opensource.org/licenses/MIT MIT License
+ * @link https://codeigniter.com
+ * @since Version 2.1.0
* @filesource
*/
-
-// ------------------------------------------------------------------------
+defined('BASEPATH') OR exit('No direct script access allowed');
/**
* PDO Result Class
*
* This class extends the parent result class: CI_DB_result
*
+ * @package CodeIgniter
+ * @subpackage Drivers
* @category Database
* @author EllisLab Dev Team
- * @link http://codeigniter.com/user_guide/database/
+ * @link https://codeigniter.com/userguide3/database/
*/
class CI_DB_pdo_result extends CI_DB_result {
- public $num_rows;
-
/**
* Number of rows in the result set
*
@@ -39,14 +62,20 @@ class CI_DB_pdo_result extends CI_DB_result {
{
return $this->num_rows;
}
- elseif (($this->num_rows = $this->result_id->rowCount()) > 0)
+ elseif (count($this->result_array) > 0)
{
- return $this->num_rows;
+ return $this->num_rows = count($this->result_array);
+ }
+ elseif (count($this->result_object) > 0)
+ {
+ return $this->num_rows = count($this->result_object);
+ }
+ elseif (($num_rows = $this->result_id->rowCount()) > 0)
+ {
+ return $this->num_rows = $num_rows;
}
- $this->num_rows = count($this->result_id->fetchAll());
- $this->result_id->execute();
- return $this->num_rows;
+ return $this->num_rows = count($this->result_array());
}
// --------------------------------------------------------------------
@@ -54,10 +83,9 @@ class CI_DB_pdo_result extends CI_DB_result {
/**
* Number of fields in the result set
*
- * @access public
- * @return integer
+ * @return int
*/
- function num_fields()
+ public function num_fields()
{
return $this->result_id->columnCount();
}
@@ -69,16 +97,20 @@ class CI_DB_pdo_result extends CI_DB_result {
*
* Generates an array of column names
*
- * @access public
- * @return array
+ * @return bool
*/
- function list_fields()
+ public function list_fields()
{
- if ($this->db->db_debug)
+ $field_names = array();
+ for ($i = 0, $c = $this->num_fields(); $i < $c; $i++)
{
- return $this->db->display_error('db_unsuported_feature');
+ // Might trigger an E_WARNING due to not all subdrivers
+ // supporting getColumnMeta()
+ $field_names[$i] = @$this->result_id->getColumnMeta($i);
+ $field_names[$i] = $field_names[$i]['name'];
}
- return FALSE;
+
+ return $field_names;
}
// --------------------------------------------------------------------
@@ -88,28 +120,34 @@ class CI_DB_pdo_result extends CI_DB_result {
*
* Generates an array of objects containing field meta-data
*
- * @access public
* @return array
*/
- function field_data()
+ public function field_data()
{
- $data = array();
-
try
{
- for($i = 0; $i < $this->num_fields(); $i++)
+ $retval = array();
+
+ for ($i = 0, $c = $this->num_fields(); $i < $c; $i++)
{
- $data[] = $this->result_id->getColumnMeta($i);
+ $field = $this->result_id->getColumnMeta($i);
+
+ $retval[$i] = new stdClass();
+ $retval[$i]->name = $field['name'];
+ $retval[$i]->type = isset($field['native_type']) ? $field['native_type'] : null;
+ $retval[$i]->max_length = ($field['len'] > 0) ? $field['len'] : NULL;
+ $retval[$i]->primary_key = (int) ( ! empty($field['flags']) && in_array('primary_key', $field['flags'], TRUE));
}
-
- return $data;
+
+ return $retval;
}
catch (Exception $e)
{
if ($this->db->db_debug)
{
- return $this->db->display_error('db_unsuported_feature');
+ return $this->db->display_error('db_unsupported_feature');
}
+
return FALSE;
}
}
@@ -119,9 +157,9 @@ class CI_DB_pdo_result extends CI_DB_result {
/**
* Free the result
*
- * @return null
+ * @return void
*/
- function free_result()
+ public function free_result()
{
if (is_object($this->result_id))
{
@@ -132,31 +170,13 @@ class CI_DB_pdo_result extends CI_DB_result {
// --------------------------------------------------------------------
/**
- * Data Seek
- *
- * Moves the internal pointer to the desired offset. We call
- * this internally before fetching results to make sure the
- * result set starts at zero
- *
- * @access private
- * @return array
- */
- function _data_seek($n = 0)
- {
- return FALSE;
- }
-
- // --------------------------------------------------------------------
-
- /**
* Result - associative array
*
* Returns the result set as an array
*
- * @access private
* @return array
*/
- function _fetch_assoc()
+ protected function _fetch_assoc()
{
return $this->result_id->fetch(PDO::FETCH_ASSOC);
}
@@ -168,16 +188,12 @@ class CI_DB_pdo_result extends CI_DB_result {
*
* Returns the result set as an object
*
- * @access private
+ * @param string $class_name
* @return object
*/
- function _fetch_object()
- {
- return $this->result_id->fetchObject();
+ protected function _fetch_object($class_name = 'stdClass')
+ {
+ return $this->result_id->fetchObject($class_name);
}
}
-
-
-/* End of file pdo_result.php */
-/* Location: ./system/database/drivers/pdo/pdo_result.php */ \ No newline at end of file
diff --git a/system/database/drivers/pdo/pdo_utility.php b/system/database/drivers/pdo/pdo_utility.php
index 042ccef8d..2094ef4a9 100644
--- a/system/database/drivers/pdo/pdo_utility.php
+++ b/system/database/drivers/pdo/pdo_utility.php
@@ -1,103 +1,64 @@
-<?php if ( ! defined('BASEPATH')) exit('No direct script access allowed');
+<?php
/**
* CodeIgniter
*
- * An open source application development framework for PHP 5.1.6 or newer
+ * An open source application development framework for PHP
*
- * @package CodeIgniter
- * @copyright Copyright (c) 2008 - 2014, EllisLab, Inc.
- * @license http://codeigniter.com/user_guide/license.html
- * @author EllisLab Dev Team
- * @link http://codeigniter.com
- * @since Version 2.1.2
+ * This content is released under the MIT License (MIT)
+ *
+ * Copyright (c) 2019 - 2022, CodeIgniter Foundation
+ *
+ * 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.
+ *
+ * @package CodeIgniter
+ * @author EllisLab Dev Team
+ * @copyright Copyright (c) 2008 - 2014, EllisLab, Inc. (https://ellislab.com/)
+ * @copyright Copyright (c) 2014 - 2019, British Columbia Institute of Technology (https://bcit.ca/)
+ * @copyright Copyright (c) 2019 - 2022, CodeIgniter Foundation (https://codeigniter.com/)
+ * @license https://opensource.org/licenses/MIT MIT License
+ * @link https://codeigniter.com
+ * @since Version 2.1.0
* @filesource
*/
-
-// ------------------------------------------------------------------------
+defined('BASEPATH') OR exit('No direct script access allowed');
/**
* PDO Utility Class
*
+ * @package CodeIgniter
+ * @subpackage Drivers
* @category Database
* @author EllisLab Dev Team
- * @link http://codeigniter.com/database/
+ * @link https://codeigniter.com/database/
*/
class CI_DB_pdo_utility extends CI_DB_utility {
/**
- * List databases
- *
- * @access private
- * @return bool
- */
- function _list_databases()
- {
- // Not sure if PDO lets you list all databases...
- if ($this->db->db_debug)
- {
- return $this->db->display_error('db_unsuported_feature');
- }
- return FALSE;
- }
-
- // --------------------------------------------------------------------
-
- /**
- * Optimize table query
- *
- * Generates a platform-specific query so that a table can be optimized
+ * Export
*
- * @access private
- * @param string the table name
- * @return object
- */
- function _optimize_table($table)
- {
- // Not a supported PDO feature
- if ($this->db->db_debug)
- {
- return $this->db->display_error('db_unsuported_feature');
- }
- return FALSE;
- }
-
- // --------------------------------------------------------------------
-
- /**
- * Repair table query
- *
- * Generates a platform-specific query so that a table can be repaired
- *
- * @access private
- * @param string the table name
- * @return object
- */
- function _repair_table($table)
- {
- // Not a supported PDO feature
- if ($this->db->db_debug)
- {
- return $this->db->display_error('db_unsuported_feature');
- }
- return FALSE;
- }
-
- // --------------------------------------------------------------------
-
- /**
- * PDO Export
- *
- * @access private
- * @param array Preferences
+ * @param array $params Preferences
* @return mixed
*/
- function _backup($params = array())
+ protected function _backup($params = array())
{
// Currently unsupported
- return $this->db->display_error('db_unsuported_feature');
+ return $this->db->display_error('db_unsupported_feature');
}
}
-
-/* End of file pdo_utility.php */
-/* Location: ./system/database/drivers/pdo/pdo_utility.php */ \ No newline at end of file
diff --git a/system/database/drivers/pdo/subdrivers/index.html b/system/database/drivers/pdo/subdrivers/index.html
new file mode 100644
index 000000000..bcb7cae34
--- /dev/null
+++ b/system/database/drivers/pdo/subdrivers/index.html
@@ -0,0 +1,11 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+ <title>403 Forbidden</title>
+</head>
+<body>
+
+<p>Directory access is forbidden.</p>
+
+</body>
+</html>
diff --git a/system/database/drivers/pdo/subdrivers/pdo_4d_driver.php b/system/database/drivers/pdo/subdrivers/pdo_4d_driver.php
new file mode 100644
index 000000000..8d5b2f68f
--- /dev/null
+++ b/system/database/drivers/pdo/subdrivers/pdo_4d_driver.php
@@ -0,0 +1,201 @@
+<?php
+/**
+ * CodeIgniter
+ *
+ * An open source application development framework for PHP
+ *
+ * This content is released under the MIT License (MIT)
+ *
+ * Copyright (c) 2019 - 2022, CodeIgniter Foundation
+ *
+ * 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.
+ *
+ * @package CodeIgniter
+ * @author EllisLab Dev Team
+ * @copyright Copyright (c) 2008 - 2014, EllisLab, Inc. (https://ellislab.com/)
+ * @copyright Copyright (c) 2014 - 2019, British Columbia Institute of Technology (https://bcit.ca/)
+ * @copyright Copyright (c) 2019 - 2022, CodeIgniter Foundation (https://codeigniter.com/)
+ * @license https://opensource.org/licenses/MIT MIT License
+ * @link https://codeigniter.com
+ * @since Version 3.0.0
+ * @filesource
+ */
+defined('BASEPATH') OR exit('No direct script access allowed');
+
+/**
+ * PDO 4D Database Adapter Class
+ *
+ * Note: _DB is an extender class that the app controller
+ * creates dynamically based on whether the query builder
+ * class is being used or not.
+ *
+ * @package CodeIgniter
+ * @subpackage Drivers
+ * @category Database
+ * @author EllisLab Dev Team
+ * @link https://codeigniter.com/userguide3/database/
+ */
+class CI_DB_pdo_4d_driver extends CI_DB_pdo_driver {
+
+ /**
+ * Sub-driver
+ *
+ * @var string
+ */
+ public $subdriver = '4d';
+
+ /**
+ * Identifier escape character
+ *
+ * @var string[]
+ */
+ protected $_escape_char = array('[', ']');
+
+ // --------------------------------------------------------------------
+
+ /**
+ * Class constructor
+ *
+ * Builds the DSN if not already set.
+ *
+ * @param array $params
+ * @return void
+ */
+ public function __construct($params)
+ {
+ parent::__construct($params);
+
+ if (empty($this->dsn))
+ {
+ $this->dsn = '4D:host='.(empty($this->hostname) ? '127.0.0.1' : $this->hostname);
+
+ empty($this->port) OR $this->dsn .= ';port='.$this->port;
+ empty($this->database) OR $this->dsn .= ';dbname='.$this->database;
+ empty($this->char_set) OR $this->dsn .= ';charset='.$this->char_set;
+ }
+ elseif ( ! empty($this->char_set) && strpos($this->dsn, 'charset=', 3) === FALSE)
+ {
+ $this->dsn .= ';charset='.$this->char_set;
+ }
+ }
+
+ // --------------------------------------------------------------------
+
+ /**
+ * Show table query
+ *
+ * Generates a platform-specific query string so that the table names can be fetched
+ *
+ * @param bool $prefix_limit
+ * @return string
+ */
+ protected function _list_tables($prefix_limit = FALSE)
+ {
+ $sql = 'SELECT '.$this->escape_identifiers('TABLE_NAME').' FROM '.$this->escape_identifiers('_USER_TABLES');
+
+ if ($prefix_limit === TRUE && $this->dbprefix !== '')
+ {
+ $sql .= ' WHERE '.$this->escape_identifiers('TABLE_NAME')." LIKE '".$this->escape_like_str($this->dbprefix)."%' "
+ .sprintf($this->_like_escape_str, $this->_like_escape_chr);
+ }
+
+ return $sql;
+ }
+
+ // --------------------------------------------------------------------
+
+ /**
+ * Show column query
+ *
+ * Generates a platform-specific query string so that the column names can be fetched
+ *
+ * @param string $table
+ * @return string
+ */
+ protected function _list_columns($table = '')
+ {
+ return 'SELECT '.$this->escape_identifiers('COLUMN_NAME').' FROM '.$this->escape_identifiers('_USER_COLUMNS')
+ .' WHERE '.$this->escape_identifiers('TABLE_NAME').' = '.$this->escape($table);
+ }
+
+ // --------------------------------------------------------------------
+
+ /**
+ * Field data query
+ *
+ * Generates a platform-specific query so that the column data can be retrieved
+ *
+ * @param string $table
+ * @return string
+ */
+ protected function _field_data($table)
+ {
+ return 'SELECT * FROM '.$this->protect_identifiers($table, TRUE, NULL, FALSE).' LIMIT 1';
+ }
+
+ // --------------------------------------------------------------------
+
+ /**
+ * Update statement
+ *
+ * Generates a platform-specific update string from the supplied data
+ *
+ * @param string $table
+ * @param array $values
+ * @return string
+ */
+ protected function _update($table, $values)
+ {
+ $this->qb_limit = FALSE;
+ $this->qb_orderby = array();
+ return parent::_update($table, $values);
+ }
+
+ // --------------------------------------------------------------------
+
+ /**
+ * Delete statement
+ *
+ * Generates a platform-specific delete string from the supplied data
+ *
+ * @param string $table
+ * @return string
+ */
+ protected function _delete($table)
+ {
+ $this->qb_limit = FALSE;
+ return parent::_delete($table);
+ }
+
+ // --------------------------------------------------------------------
+
+ /**
+ * LIMIT
+ *
+ * Generates a platform-specific LIMIT clause
+ *
+ * @param string $sql SQL Query
+ * @return string
+ */
+ protected function _limit($sql)
+ {
+ return $sql.' LIMIT '.$this->qb_limit.($this->qb_offset ? ' OFFSET '.$this->qb_offset : '');
+ }
+
+}
diff --git a/system/database/drivers/pdo/subdrivers/pdo_4d_forge.php b/system/database/drivers/pdo/subdrivers/pdo_4d_forge.php
new file mode 100644
index 000000000..28fc008a2
--- /dev/null
+++ b/system/database/drivers/pdo/subdrivers/pdo_4d_forge.php
@@ -0,0 +1,218 @@
+<?php
+/**
+ * CodeIgniter
+ *
+ * An open source application development framework for PHP
+ *
+ * This content is released under the MIT License (MIT)
+ *
+ * Copyright (c) 2019 - 2022, CodeIgniter Foundation
+ *
+ * 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.
+ *
+ * @package CodeIgniter
+ * @author EllisLab Dev Team
+ * @copyright Copyright (c) 2008 - 2014, EllisLab, Inc. (https://ellislab.com/)
+ * @copyright Copyright (c) 2014 - 2019, British Columbia Institute of Technology (https://bcit.ca/)
+ * @copyright Copyright (c) 2019 - 2022, CodeIgniter Foundation (https://codeigniter.com/)
+ * @license https://opensource.org/licenses/MIT MIT License
+ * @link https://codeigniter.com
+ * @since Version 3.0.0
+ * @filesource
+ */
+defined('BASEPATH') OR exit('No direct script access allowed');
+
+/**
+ * PDO 4D Forge Class
+ *
+ * @category Database
+ * @author EllisLab Dev Team
+ * @link https://codeigniter.com/userguide3/database/
+ */
+class CI_DB_pdo_4d_forge extends CI_DB_pdo_forge {
+
+ /**
+ * CREATE DATABASE statement
+ *
+ * @var string
+ */
+ protected $_create_database = 'CREATE SCHEMA %s';
+
+ /**
+ * DROP DATABASE statement
+ *
+ * @var string
+ */
+ protected $_drop_database = 'DROP SCHEMA %s';
+
+ /**
+ * CREATE TABLE IF statement
+ *
+ * @var string
+ */
+ protected $_create_table_if = 'CREATE TABLE IF NOT EXISTS';
+
+ /**
+ * RENAME TABLE statement
+ *
+ * @var string
+ */
+ protected $_rename_table = FALSE;
+
+ /**
+ * DROP TABLE IF statement
+ *
+ * @var string
+ */
+ protected $_drop_table_if = 'DROP TABLE IF EXISTS';
+
+ /**
+ * UNSIGNED support
+ *
+ * @var array
+ */
+ protected $_unsigned = array(
+ 'INT16' => 'INT',
+ 'SMALLINT' => 'INT',
+ 'INT' => 'INT64',
+ 'INT32' => 'INT64'
+ );
+
+ /**
+ * DEFAULT value representation in CREATE/ALTER TABLE statements
+ *
+ * @var string
+ */
+ protected $_default = FALSE;
+
+ // --------------------------------------------------------------------
+
+ /**
+ * ALTER TABLE
+ *
+ * @param string $alter_type ALTER type
+ * @param string $table Table name
+ * @param mixed $field Column definition
+ * @return string|string[]
+ */
+ protected function _alter_table($alter_type, $table, $field)
+ {
+ if (in_array($alter_type, array('ADD', 'DROP'), TRUE))
+ {
+ return parent::_alter_table($alter_type, $table, $field);
+ }
+
+ // No method of modifying columns is supported
+ return FALSE;
+ }
+
+ // --------------------------------------------------------------------
+
+ /**
+ * Process column
+ *
+ * @param array $field
+ * @return string
+ */
+ protected function _process_column($field)
+ {
+ return $this->db->escape_identifiers($field['name'])
+ .' '.$field['type'].$field['length']
+ .$field['null']
+ .$field['unique']
+ .$field['auto_increment'];
+ }
+
+ // --------------------------------------------------------------------
+
+ /**
+ * Field attribute TYPE
+ *
+ * Performs a data type mapping between different databases.
+ *
+ * @param array &$attributes
+ * @return void
+ */
+ protected function _attr_type(&$attributes)
+ {
+ switch (strtoupper($attributes['TYPE']))
+ {
+ case 'TINYINT':
+ $attributes['TYPE'] = 'SMALLINT';
+ $attributes['UNSIGNED'] = FALSE;
+ return;
+ case 'MEDIUMINT':
+ $attributes['TYPE'] = 'INTEGER';
+ $attributes['UNSIGNED'] = FALSE;
+ return;
+ case 'INTEGER':
+ $attributes['TYPE'] = 'INT';
+ return;
+ case 'BIGINT':
+ $attributes['TYPE'] = 'INT64';
+ return;
+ default: return;
+ }
+ }
+
+ // --------------------------------------------------------------------
+
+ /**
+ * Field attribute UNIQUE
+ *
+ * @param array &$attributes
+ * @param array &$field
+ * @return void
+ */
+ protected function _attr_unique(&$attributes, &$field)
+ {
+ if ( ! empty($attributes['UNIQUE']) && $attributes['UNIQUE'] === TRUE)
+ {
+ $field['unique'] = ' UNIQUE';
+
+ // UNIQUE must be used with NOT NULL
+ $field['null'] = ' NOT NULL';
+ }
+ }
+
+ // --------------------------------------------------------------------
+
+ /**
+ * Field attribute AUTO_INCREMENT
+ *
+ * @param array &$attributes
+ * @param array &$field
+ * @return void
+ */
+ protected function _attr_auto_increment(&$attributes, &$field)
+ {
+ if ( ! empty($attributes['AUTO_INCREMENT']) && $attributes['AUTO_INCREMENT'] === TRUE)
+ {
+ if (stripos($field['type'], 'int') !== FALSE)
+ {
+ $field['auto_increment'] = ' AUTO_INCREMENT';
+ }
+ elseif (strcasecmp($field['type'], 'UUID') === 0)
+ {
+ $field['auto_increment'] = ' AUTO_GENERATE';
+ }
+ }
+ }
+
+}
diff --git a/system/database/drivers/pdo/subdrivers/pdo_cubrid_driver.php b/system/database/drivers/pdo/subdrivers/pdo_cubrid_driver.php
new file mode 100644
index 000000000..c8f925852
--- /dev/null
+++ b/system/database/drivers/pdo/subdrivers/pdo_cubrid_driver.php
@@ -0,0 +1,210 @@
+<?php
+/**
+ * CodeIgniter
+ *
+ * An open source application development framework for PHP
+ *
+ * This content is released under the MIT License (MIT)
+ *
+ * Copyright (c) 2019 - 2022, CodeIgniter Foundation
+ *
+ * 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.
+ *
+ * @package CodeIgniter
+ * @author EllisLab Dev Team
+ * @copyright Copyright (c) 2008 - 2014, EllisLab, Inc. (https://ellislab.com/)
+ * @copyright Copyright (c) 2014 - 2019, British Columbia Institute of Technology (https://bcit.ca/)
+ * @copyright Copyright (c) 2019 - 2022, CodeIgniter Foundation (https://codeigniter.com/)
+ * @license https://opensource.org/licenses/MIT MIT License
+ * @link https://codeigniter.com
+ * @since Version 3.0.0
+ * @filesource
+ */
+defined('BASEPATH') OR exit('No direct script access allowed');
+
+/**
+ * PDO CUBRID Database Adapter Class
+ *
+ * Note: _DB is an extender class that the app controller
+ * creates dynamically based on whether the query builder
+ * class is being used or not.
+ *
+ * @package CodeIgniter
+ * @subpackage Drivers
+ * @category Database
+ * @author EllisLab Dev Team
+ * @link https://codeigniter.com/userguide3/database/
+ */
+class CI_DB_pdo_cubrid_driver extends CI_DB_pdo_driver {
+
+ /**
+ * Sub-driver
+ *
+ * @var string
+ */
+ public $subdriver = 'cubrid';
+
+ /**
+ * Identifier escape character
+ *
+ * @var string
+ */
+ protected $_escape_char = '`';
+
+ /**
+ * ORDER BY random keyword
+ *
+ * @var array
+ */
+ protected $_random_keyword = array('RANDOM()', 'RANDOM(%d)');
+
+ // --------------------------------------------------------------------
+
+ /**
+ * Class constructor
+ *
+ * Builds the DSN if not already set.
+ *
+ * @param array $params
+ * @return void
+ */
+ public function __construct($params)
+ {
+ parent::__construct($params);
+
+ if (empty($this->dsn))
+ {
+ $this->dsn = 'cubrid:host='.(empty($this->hostname) ? '127.0.0.1' : $this->hostname);
+
+ empty($this->port) OR $this->dsn .= ';port='.$this->port;
+ empty($this->database) OR $this->dsn .= ';dbname='.$this->database;
+ empty($this->char_set) OR $this->dsn .= ';charset='.$this->char_set;
+ }
+ }
+
+ // --------------------------------------------------------------------
+
+ /**
+ * Show table query
+ *
+ * Generates a platform-specific query string so that the table names can be fetched
+ *
+ * @param bool $prefix_limit
+ * @return string
+ */
+ protected function _list_tables($prefix_limit = FALSE)
+ {
+ $sql = 'SHOW TABLES';
+
+ if ($prefix_limit === TRUE && $this->dbprefix !== '')
+ {
+ return $sql." LIKE '".$this->escape_like_str($this->dbprefix)."%'";
+ }
+
+ return $sql;
+ }
+
+ // --------------------------------------------------------------------
+
+ /**
+ * Show column query
+ *
+ * Generates a platform-specific query string so that the column names can be fetched
+ *
+ * @param string $table
+ * @return string
+ */
+ protected function _list_columns($table = '')
+ {
+ return 'SHOW COLUMNS FROM '.$this->protect_identifiers($table, TRUE, NULL, FALSE);
+ }
+
+ // --------------------------------------------------------------------
+
+ /**
+ * Returns an object with field data
+ *
+ * @param string $table
+ * @return array
+ */
+ public function field_data($table)
+ {
+ if (($query = $this->query('SHOW COLUMNS FROM '.$this->protect_identifiers($table, TRUE, NULL, FALSE))) === FALSE)
+ {
+ return FALSE;
+ }
+ $query = $query->result_object();
+
+ $retval = array();
+ for ($i = 0, $c = count($query); $i < $c; $i++)
+ {
+ $retval[$i] = new stdClass();
+ $retval[$i]->name = $query[$i]->Field;
+
+ sscanf($query[$i]->Type, '%[a-z](%d)',
+ $retval[$i]->type,
+ $retval[$i]->max_length
+ );
+
+ $retval[$i]->default = $query[$i]->Default;
+ $retval[$i]->primary_key = (int) ($query[$i]->Key === 'PRI');
+ }
+
+ return $retval;
+ }
+
+ // --------------------------------------------------------------------
+
+ /**
+ * Truncate statement
+ *
+ * Generates a platform-specific truncate string from the supplied data
+ *
+ * If the database does not support the TRUNCATE statement,
+ * then this method maps to 'DELETE FROM table'
+ *
+ * @param string $table
+ * @return string
+ */
+ protected function _truncate($table)
+ {
+ return 'TRUNCATE '.$table;
+ }
+
+ // --------------------------------------------------------------------
+
+ /**
+ * FROM tables
+ *
+ * Groups tables in FROM clauses if needed, so there is no confusion
+ * about operator precedence.
+ *
+ * @return string
+ */
+ protected function _from_tables()
+ {
+ if ( ! empty($this->qb_join) && count($this->qb_from) > 1)
+ {
+ return '('.implode(', ', $this->qb_from).')';
+ }
+
+ return implode(', ', $this->qb_from);
+ }
+
+}
diff --git a/system/database/drivers/pdo/subdrivers/pdo_cubrid_forge.php b/system/database/drivers/pdo/subdrivers/pdo_cubrid_forge.php
new file mode 100644
index 000000000..de02983bf
--- /dev/null
+++ b/system/database/drivers/pdo/subdrivers/pdo_cubrid_forge.php
@@ -0,0 +1,231 @@
+<?php
+/**
+ * CodeIgniter
+ *
+ * An open source application development framework for PHP
+ *
+ * This content is released under the MIT License (MIT)
+ *
+ * Copyright (c) 2019 - 2022, CodeIgniter Foundation
+ *
+ * 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.
+ *
+ * @package CodeIgniter
+ * @author EllisLab Dev Team
+ * @copyright Copyright (c) 2008 - 2014, EllisLab, Inc. (https://ellislab.com/)
+ * @copyright Copyright (c) 2014 - 2019, British Columbia Institute of Technology (https://bcit.ca/)
+ * @copyright Copyright (c) 2019 - 2022, CodeIgniter Foundation (https://codeigniter.com/)
+ * @license https://opensource.org/licenses/MIT MIT License
+ * @link https://codeigniter.com
+ * @since Version 3.0.0
+ * @filesource
+ */
+defined('BASEPATH') OR exit('No direct script access allowed');
+
+/**
+ * PDO CUBRID Forge Class
+ *
+ * @category Database
+ * @author EllisLab Dev Team
+ * @link https://codeigniter.com/userguide3/database/
+ */
+class CI_DB_pdo_cubrid_forge extends CI_DB_pdo_forge {
+
+ /**
+ * CREATE DATABASE statement
+ *
+ * @var string
+ */
+ protected $_create_database = FALSE;
+
+ /**
+ * DROP DATABASE statement
+ *
+ * @var string
+ */
+ protected $_drop_database = FALSE;
+
+ /**
+ * CREATE TABLE keys flag
+ *
+ * Whether table keys are created from within the
+ * CREATE TABLE statement.
+ *
+ * @var bool
+ */
+ protected $_create_table_keys = TRUE;
+
+ /**
+ * DROP TABLE IF statement
+ *
+ * @var string
+ */
+ protected $_drop_table_if = 'DROP TABLE IF EXISTS';
+
+ /**
+ * UNSIGNED support
+ *
+ * @var array
+ */
+ protected $_unsigned = array(
+ 'SHORT' => 'INTEGER',
+ 'SMALLINT' => 'INTEGER',
+ 'INT' => 'BIGINT',
+ 'INTEGER' => 'BIGINT',
+ 'BIGINT' => 'NUMERIC',
+ 'FLOAT' => 'DOUBLE',
+ 'REAL' => 'DOUBLE'
+ );
+
+ // --------------------------------------------------------------------
+
+ /**
+ * ALTER TABLE
+ *
+ * @param string $alter_type ALTER type
+ * @param string $table Table name
+ * @param mixed $field Column definition
+ * @return string|string[]
+ */
+ protected function _alter_table($alter_type, $table, $field)
+ {
+ if (in_array($alter_type, array('DROP', 'ADD'), TRUE))
+ {
+ return parent::_alter_table($alter_type, $table, $field);
+ }
+
+ $sql = 'ALTER TABLE '.$this->db->escape_identifiers($table);
+ $sqls = array();
+ for ($i = 0, $c = count($field); $i < $c; $i++)
+ {
+ if ($field[$i]['_literal'] !== FALSE)
+ {
+ $sqls[] = $sql.' CHANGE '.$field[$i]['_literal'];
+ }
+ else
+ {
+ $alter_type = empty($field[$i]['new_name']) ? ' MODIFY ' : ' CHANGE ';
+ $sqls[] = $sql.$alter_type.$this->_process_column($field[$i]);
+ }
+ }
+
+ return $sqls;
+ }
+
+ // --------------------------------------------------------------------
+
+ /**
+ * Process column
+ *
+ * @param array $field
+ * @return string
+ */
+ protected function _process_column($field)
+ {
+ $extra_clause = isset($field['after'])
+ ? ' AFTER '.$this->db->escape_identifiers($field['after']) : '';
+
+ if (empty($extra_clause) && isset($field['first']) && $field['first'] === TRUE)
+ {
+ $extra_clause = ' FIRST';
+ }
+
+ return $this->db->escape_identifiers($field['name'])
+ .(empty($field['new_name']) ? '' : ' '.$this->db->escape_identifiers($field['new_name']))
+ .' '.$field['type'].$field['length']
+ .$field['unsigned']
+ .$field['null']
+ .$field['default']
+ .$field['auto_increment']
+ .$field['unique']
+ .$extra_clause;
+ }
+
+ // --------------------------------------------------------------------
+
+ /**
+ * Field attribute TYPE
+ *
+ * Performs a data type mapping between different databases.
+ *
+ * @param array &$attributes
+ * @return void
+ */
+ protected function _attr_type(&$attributes)
+ {
+ switch (strtoupper($attributes['TYPE']))
+ {
+ case 'TINYINT':
+ $attributes['TYPE'] = 'SMALLINT';
+ $attributes['UNSIGNED'] = FALSE;
+ return;
+ case 'MEDIUMINT':
+ $attributes['TYPE'] = 'INTEGER';
+ $attributes['UNSIGNED'] = FALSE;
+ return;
+ case 'LONGTEXT':
+ $attributes['TYPE'] = 'STRING';
+ return;
+ default: return;
+ }
+ }
+
+ // --------------------------------------------------------------------
+
+ /**
+ * Process indexes
+ *
+ * @param string $table (ignored)
+ * @return string
+ */
+ protected function _process_indexes($table)
+ {
+ $sql = '';
+
+ for ($i = 0, $c = count($this->keys); $i < $c; $i++)
+ {
+ if (is_array($this->keys[$i]))
+ {
+ for ($i2 = 0, $c2 = count($this->keys[$i]); $i2 < $c2; $i2++)
+ {
+ if ( ! isset($this->fields[$this->keys[$i][$i2]]))
+ {
+ unset($this->keys[$i][$i2]);
+ continue;
+ }
+ }
+ }
+ elseif ( ! isset($this->fields[$this->keys[$i]]))
+ {
+ unset($this->keys[$i]);
+ continue;
+ }
+
+ is_array($this->keys[$i]) OR $this->keys[$i] = array($this->keys[$i]);
+
+ $sql .= ",\n\tKEY ".$this->db->escape_identifiers(implode('_', $this->keys[$i]))
+ .' ('.implode(', ', $this->db->escape_identifiers($this->keys[$i])).')';
+ }
+
+ $this->keys = array();
+
+ return $sql;
+ }
+
+}
diff --git a/system/database/drivers/pdo/subdrivers/pdo_dblib_driver.php b/system/database/drivers/pdo/subdrivers/pdo_dblib_driver.php
new file mode 100644
index 000000000..7d8d4a29c
--- /dev/null
+++ b/system/database/drivers/pdo/subdrivers/pdo_dblib_driver.php
@@ -0,0 +1,354 @@
+<?php
+/**
+ * CodeIgniter
+ *
+ * An open source application development framework for PHP
+ *
+ * This content is released under the MIT License (MIT)
+ *
+ * Copyright (c) 2019 - 2022, CodeIgniter Foundation
+ *
+ * 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.
+ *
+ * @package CodeIgniter
+ * @author EllisLab Dev Team
+ * @copyright Copyright (c) 2008 - 2014, EllisLab, Inc. (https://ellislab.com/)
+ * @copyright Copyright (c) 2014 - 2019, British Columbia Institute of Technology (https://bcit.ca/)
+ * @copyright Copyright (c) 2019 - 2022, CodeIgniter Foundation (https://codeigniter.com/)
+ * @license https://opensource.org/licenses/MIT MIT License
+ * @link https://codeigniter.com
+ * @since Version 3.0.0
+ * @filesource
+ */
+defined('BASEPATH') OR exit('No direct script access allowed');
+
+/**
+ * PDO DBLIB Database Adapter Class
+ *
+ * Note: _DB is an extender class that the app controller
+ * creates dynamically based on whether the query builder
+ * class is being used or not.
+ *
+ * @package CodeIgniter
+ * @subpackage Drivers
+ * @category Database
+ * @author EllisLab Dev Team
+ * @link https://codeigniter.com/userguide3/database/
+ */
+class CI_DB_pdo_dblib_driver extends CI_DB_pdo_driver {
+
+ /**
+ * Sub-driver
+ *
+ * @var string
+ */
+ public $subdriver = 'dblib';
+
+ // --------------------------------------------------------------------
+
+ /**
+ * ORDER BY random keyword
+ *
+ * @var array
+ */
+ protected $_random_keyword = array('NEWID()', 'RAND(%d)');
+
+ /**
+ * Quoted identifier flag
+ *
+ * Whether to use SQL-92 standard quoted identifier
+ * (double quotes) or brackets for identifier escaping.
+ *
+ * @var bool
+ */
+ protected $_quoted_identifier;
+
+ // --------------------------------------------------------------------
+
+ /**
+ * Class constructor
+ *
+ * Builds the DSN if not already set.
+ *
+ * @param array $params
+ * @return void
+ */
+ public function __construct($params)
+ {
+ parent::__construct($params);
+
+ if (empty($this->dsn))
+ {
+ $this->dsn = $params['subdriver'].':host='.(empty($this->hostname) ? '127.0.0.1' : $this->hostname);
+
+ if ( ! empty($this->port))
+ {
+ $this->dsn .= (DIRECTORY_SEPARATOR === '\\' ? ',' : ':').$this->port;
+ }
+
+ empty($this->database) OR $this->dsn .= ';dbname='.$this->database;
+ empty($this->char_set) OR $this->dsn .= ';charset='.$this->char_set;
+ empty($this->appname) OR $this->dsn .= ';appname='.$this->appname;
+ }
+ else
+ {
+ if ( ! empty($this->char_set) && strpos($this->dsn, 'charset=', 6) === FALSE)
+ {
+ $this->dsn .= ';charset='.$this->char_set;
+ }
+
+ $this->subdriver = 'dblib';
+ }
+ }
+
+ // --------------------------------------------------------------------
+
+ /**
+ * Database connection
+ *
+ * @param bool $persistent
+ * @return object
+ */
+ public function db_connect($persistent = FALSE)
+ {
+ if ($persistent === TRUE)
+ {
+ log_message('debug', "dblib driver doesn't support persistent connections");
+ }
+
+ $this->conn_id = parent::db_connect(FALSE);
+
+ if ( ! is_object($this->conn_id))
+ {
+ return $this->conn_id;
+ }
+
+ // Determine how identifiers are escaped
+ $query = $this->query('SELECT CASE WHEN (@@OPTIONS | 256) = @@OPTIONS THEN 1 ELSE 0 END AS qi');
+ $query = $query->row_array();
+ $this->_quoted_identifier = empty($query) ? FALSE : (bool) $query['qi'];
+ $this->_escape_char = ($this->_quoted_identifier) ? '"' : array('[', ']');
+
+ return $this->conn_id;
+ }
+
+ // --------------------------------------------------------------------
+
+ /**
+ * Show table query
+ *
+ * Generates a platform-specific query string so that the table names can be fetched
+ *
+ * @param bool $prefix_limit
+ * @return string
+ */
+ protected function _list_tables($prefix_limit = FALSE)
+ {
+ $sql = 'SELECT '.$this->escape_identifiers('name')
+ .' FROM '.$this->escape_identifiers('sysobjects')
+ .' WHERE '.$this->escape_identifiers('type')." = 'U'";
+
+ if ($prefix_limit === TRUE && $this->dbprefix !== '')
+ {
+ $sql .= ' AND '.$this->escape_identifiers('name')." LIKE '".$this->escape_like_str($this->dbprefix)."%' "
+ .sprintf($this->_like_escape_str, $this->_like_escape_chr);
+ }
+
+ return $sql.' ORDER BY '.$this->escape_identifiers('name');
+ }
+
+ // --------------------------------------------------------------------
+
+ /**
+ * Show column query
+ *
+ * Generates a platform-specific query string so that the column names can be fetched
+ *
+ * @param string $table
+ * @return string
+ */
+ protected function _list_columns($table = '')
+ {
+ return 'SELECT COLUMN_NAME
+ FROM INFORMATION_SCHEMA.Columns
+ WHERE UPPER(TABLE_NAME) = '.$this->escape(strtoupper($table));
+ }
+
+ // --------------------------------------------------------------------
+
+ /**
+ * Returns an object with field data
+ *
+ * @param string $table
+ * @return array
+ */
+ public function field_data($table)
+ {
+ $sql = 'SELECT COLUMN_NAME, DATA_TYPE, CHARACTER_MAXIMUM_LENGTH, NUMERIC_PRECISION, COLUMN_DEFAULT
+ FROM INFORMATION_SCHEMA.Columns
+ WHERE UPPER(TABLE_NAME) = '.$this->escape(strtoupper($table));
+
+ if (($query = $this->query($sql)) === FALSE)
+ {
+ return FALSE;
+ }
+ $query = $query->result_object();
+
+ $retval = array();
+ for ($i = 0, $c = count($query); $i < $c; $i++)
+ {
+ $retval[$i] = new stdClass();
+ $retval[$i]->name = $query[$i]->COLUMN_NAME;
+ $retval[$i]->type = $query[$i]->DATA_TYPE;
+ $retval[$i]->max_length = ($query[$i]->CHARACTER_MAXIMUM_LENGTH > 0) ? $query[$i]->CHARACTER_MAXIMUM_LENGTH : $query[$i]->NUMERIC_PRECISION;
+ $retval[$i]->default = $query[$i]->COLUMN_DEFAULT;
+ }
+
+ return $retval;
+ }
+
+ // --------------------------------------------------------------------
+
+ /**
+ * Update statement
+ *
+ * Generates a platform-specific update string from the supplied data
+ *
+ * @param string $table
+ * @param array $values
+ * @return string
+ */
+ protected function _update($table, $values)
+ {
+ $this->qb_limit = FALSE;
+ $this->qb_orderby = array();
+ return parent::_update($table, $values);
+ }
+
+ // --------------------------------------------------------------------
+
+ /**
+ * Delete statement
+ *
+ * Generates a platform-specific delete string from the supplied data
+ *
+ * @param string $table
+ * @return string
+ */
+ protected function _delete($table)
+ {
+ if ($this->qb_limit)
+ {
+ return 'WITH ci_delete AS (SELECT TOP '.$this->qb_limit.' * FROM '.$table.$this->_compile_wh('qb_where').') DELETE FROM ci_delete';
+ }
+
+ return parent::_delete($table);
+ }
+
+ // --------------------------------------------------------------------
+
+ /**
+ * LIMIT
+ *
+ * Generates a platform-specific LIMIT clause
+ *
+ * @param string $sql SQL Query
+ * @return string
+ */
+ protected function _limit($sql)
+ {
+ $limit = $this->qb_offset + $this->qb_limit;
+
+ // As of SQL Server 2005 (9.0.*) ROW_NUMBER() is supported,
+ // however an ORDER BY clause is required for it to work
+ if (version_compare($this->version(), '9', '>=') && $this->qb_offset && ! empty($this->qb_orderby))
+ {
+ $orderby = $this->_compile_order_by();
+
+ // We have to strip the ORDER BY clause
+ $sql = trim(substr($sql, 0, strrpos($sql, $orderby)));
+
+ // Get the fields to select from our subquery, so that we can avoid CI_rownum appearing in the actual results
+ if (count($this->qb_select) === 0 OR strpos(implode(',', $this->qb_select), '*') !== FALSE)
+ {
+ $select = '*'; // Inevitable
+ }
+ else
+ {
+ // Use only field names and their aliases, everything else is out of our scope.
+ $select = array();
+ $field_regexp = ($this->_quoted_identifier)
+ ? '("[^\"]+")' : '(\[[^\]]+\])';
+ for ($i = 0, $c = count($this->qb_select); $i < $c; $i++)
+ {
+ $select[] = preg_match('/(?:\s|\.)'.$field_regexp.'$/i', $this->qb_select[$i], $m)
+ ? $m[1] : $this->qb_select[$i];
+ }
+ $select = implode(', ', $select);
+ }
+
+ return 'SELECT '.$select." FROM (\n\n"
+ .preg_replace('/^(SELECT( DISTINCT)?)/i', '\\1 ROW_NUMBER() OVER('.trim($orderby).') AS '.$this->escape_identifiers('CI_rownum').', ', $sql)
+ ."\n\n) ".$this->escape_identifiers('CI_subquery')
+ ."\nWHERE ".$this->escape_identifiers('CI_rownum').' BETWEEN '.($this->qb_offset + 1).' AND '.$limit;
+ }
+
+ return preg_replace('/(^\SELECT (DISTINCT)?)/i','\\1 TOP '.$limit.' ', $sql);
+ }
+
+ // --------------------------------------------------------------------
+
+ /**
+ * Insert batch statement
+ *
+ * Generates a platform-specific insert string from the supplied data.
+ *
+ * @param string $table Table name
+ * @param array $keys INSERT keys
+ * @param array $values INSERT values
+ * @return string|bool
+ */
+ protected function _insert_batch($table, $keys, $values)
+ {
+ // Multiple-value inserts are only supported as of SQL Server 2008
+ if (version_compare($this->version(), '10', '>='))
+ {
+ return parent::_insert_batch($table, $keys, $values);
+ }
+
+ return ($this->db_debug) ? $this->display_error('db_unsupported_feature') : FALSE;
+ }
+
+ // --------------------------------------------------------------------
+
+ /**
+ * Database version number
+ *
+ * @return string
+ */
+ public function version()
+ {
+ if (isset($this->data_cache['version']))
+ {
+ return $this->data_cache['version'];
+ }
+
+ return $this->data_cache['version'] = $this->conn_id->query("SELECT SERVERPROPERTY('ProductVersion') AS ver")->fetchColumn(0);
+ }
+}
diff --git a/system/database/drivers/pdo/subdrivers/pdo_dblib_forge.php b/system/database/drivers/pdo/subdrivers/pdo_dblib_forge.php
new file mode 100644
index 000000000..3ee352fbf
--- /dev/null
+++ b/system/database/drivers/pdo/subdrivers/pdo_dblib_forge.php
@@ -0,0 +1,150 @@
+<?php
+/**
+ * CodeIgniter
+ *
+ * An open source application development framework for PHP
+ *
+ * This content is released under the MIT License (MIT)
+ *
+ * Copyright (c) 2019 - 2022, CodeIgniter Foundation
+ *
+ * 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.
+ *
+ * @package CodeIgniter
+ * @author EllisLab Dev Team
+ * @copyright Copyright (c) 2008 - 2014, EllisLab, Inc. (https://ellislab.com/)
+ * @copyright Copyright (c) 2014 - 2019, British Columbia Institute of Technology (https://bcit.ca/)
+ * @copyright Copyright (c) 2019 - 2022, CodeIgniter Foundation (https://codeigniter.com/)
+ * @license https://opensource.org/licenses/MIT MIT License
+ * @link https://codeigniter.com
+ * @since Version 3.0.0
+ * @filesource
+ */
+defined('BASEPATH') OR exit('No direct script access allowed');
+
+/**
+ * PDO DBLIB Forge Class
+ *
+ * @category Database
+ * @author EllisLab Dev Team
+ * @link https://codeigniter.com/userguide3/database/
+ */
+class CI_DB_pdo_dblib_forge extends CI_DB_pdo_forge {
+
+ /**
+ * CREATE TABLE IF statement
+ *
+ * @var string
+ */
+ protected $_create_table_if = "IF NOT EXISTS (SELECT * FROM sysobjects WHERE ID = object_id(N'%s') AND OBJECTPROPERTY(id, N'IsUserTable') = 1)\nCREATE TABLE";
+
+ /**
+ * DROP TABLE IF statement
+ *
+ * @var string
+ */
+ protected $_drop_table_if = "IF EXISTS (SELECT * FROM sysobjects WHERE ID = object_id(N'%s') AND OBJECTPROPERTY(id, N'IsUserTable') = 1)\nDROP TABLE";
+
+ /**
+ * UNSIGNED support
+ *
+ * @var array
+ */
+ protected $_unsigned = array(
+ 'TINYINT' => 'SMALLINT',
+ 'SMALLINT' => 'INT',
+ 'INT' => 'BIGINT',
+ 'REAL' => 'FLOAT'
+ );
+
+ // --------------------------------------------------------------------
+
+ /**
+ * ALTER TABLE
+ *
+ * @param string $alter_type ALTER type
+ * @param string $table Table name
+ * @param mixed $field Column definition
+ * @return string|string[]
+ */
+ protected function _alter_table($alter_type, $table, $field)
+ {
+ if (in_array($alter_type, array('ADD', 'DROP'), TRUE))
+ {
+ return parent::_alter_table($alter_type, $table, $field);
+ }
+
+ $sql = 'ALTER TABLE '.$this->db->escape_identifiers($table).' ALTER COLUMN ';
+ $sqls = array();
+ for ($i = 0, $c = count($field); $i < $c; $i++)
+ {
+ $sqls[] = $sql.$this->_process_column($field[$i]);
+ }
+
+ return $sqls;
+ }
+
+ // --------------------------------------------------------------------
+
+ /**
+ * Field attribute TYPE
+ *
+ * Performs a data type mapping between different databases.
+ *
+ * @param array &$attributes
+ * @return void
+ */
+ protected function _attr_type(&$attributes)
+ {
+ if (isset($attributes['CONSTRAINT']) && strpos($attributes['TYPE'], 'INT') !== FALSE)
+ {
+ unset($attributes['CONSTRAINT']);
+ }
+
+ switch (strtoupper($attributes['TYPE']))
+ {
+ case 'MEDIUMINT':
+ $attributes['TYPE'] = 'INTEGER';
+ $attributes['UNSIGNED'] = FALSE;
+ return;
+ case 'INTEGER':
+ $attributes['TYPE'] = 'INT';
+ return;
+ default: return;
+ }
+ }
+
+ // --------------------------------------------------------------------
+
+ /**
+ * Field attribute AUTO_INCREMENT
+ *
+ * @param array &$attributes
+ * @param array &$field
+ * @return void
+ */
+ protected function _attr_auto_increment(&$attributes, &$field)
+ {
+ if ( ! empty($attributes['AUTO_INCREMENT']) && $attributes['AUTO_INCREMENT'] === TRUE && stripos($field['type'], 'int') !== FALSE)
+ {
+ $field['auto_increment'] = ' IDENTITY(1,1)';
+ }
+ }
+
+}
diff --git a/system/database/drivers/pdo/subdrivers/pdo_firebird_driver.php b/system/database/drivers/pdo/subdrivers/pdo_firebird_driver.php
new file mode 100644
index 000000000..977825000
--- /dev/null
+++ b/system/database/drivers/pdo/subdrivers/pdo_firebird_driver.php
@@ -0,0 +1,280 @@
+<?php
+/**
+ * CodeIgniter
+ *
+ * An open source application development framework for PHP
+ *
+ * This content is released under the MIT License (MIT)
+ *
+ * Copyright (c) 2019 - 2022, CodeIgniter Foundation
+ *
+ * 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.
+ *
+ * @package CodeIgniter
+ * @author EllisLab Dev Team
+ * @copyright Copyright (c) 2008 - 2014, EllisLab, Inc. (https://ellislab.com/)
+ * @copyright Copyright (c) 2014 - 2019, British Columbia Institute of Technology (https://bcit.ca/)
+ * @copyright Copyright (c) 2019 - 2022, CodeIgniter Foundation (https://codeigniter.com/)
+ * @license https://opensource.org/licenses/MIT MIT License
+ * @link https://codeigniter.com
+ * @since Version 3.0.0
+ * @filesource
+ */
+defined('BASEPATH') OR exit('No direct script access allowed');
+
+/**
+ * PDO Firebird Database Adapter Class
+ *
+ * Note: _DB is an extender class that the app controller
+ * creates dynamically based on whether the query builder
+ * class is being used or not.
+ *
+ * @package CodeIgniter
+ * @subpackage Drivers
+ * @category Database
+ * @author EllisLab Dev Team
+ * @link https://codeigniter.com/userguide3/database/
+ */
+class CI_DB_pdo_firebird_driver extends CI_DB_pdo_driver {
+
+ /**
+ * Sub-driver
+ *
+ * @var string
+ */
+ public $subdriver = 'firebird';
+
+ // --------------------------------------------------------------------
+
+ /**
+ * ORDER BY random keyword
+ *
+ * @var array
+ */
+ protected $_random_keyword = array('RAND()', 'RAND()');
+
+ // --------------------------------------------------------------------
+
+ /**
+ * Class constructor
+ *
+ * Builds the DSN if not already set.
+ *
+ * @param array $params
+ * @return void
+ */
+ public function __construct($params)
+ {
+ parent::__construct($params);
+
+ if (empty($this->dsn))
+ {
+ $this->dsn = 'firebird:';
+
+ if ( ! empty($this->database))
+ {
+ $this->dsn .= 'dbname='.$this->database;
+ }
+ elseif ( ! empty($this->hostname))
+ {
+ $this->dsn .= 'dbname='.$this->hostname;
+ }
+
+ empty($this->char_set) OR $this->dsn .= ';charset='.$this->char_set;
+ empty($this->role) OR $this->dsn .= ';role='.$this->role;
+ }
+ elseif ( ! empty($this->char_set) && strpos($this->dsn, 'charset=', 9) === FALSE)
+ {
+ $this->dsn .= ';charset='.$this->char_set;
+ }
+ }
+
+ // --------------------------------------------------------------------
+
+ /**
+ * Show table query
+ *
+ * Generates a platform-specific query string so that the table names can be fetched
+ *
+ * @param bool $prefix_limit
+ * @return string
+ */
+ protected function _list_tables($prefix_limit = FALSE)
+ {
+ $sql = 'SELECT "RDB$RELATION_NAME" FROM "RDB$RELATIONS" WHERE "RDB$RELATION_NAME" NOT LIKE \'RDB$%\' AND "RDB$RELATION_NAME" NOT LIKE \'MON$%\'';
+
+ if ($prefix_limit === TRUE && $this->dbprefix !== '')
+ {
+ return $sql.' AND "RDB$RELATION_NAME" LIKE \''.$this->escape_like_str($this->dbprefix)."%' "
+ .sprintf($this->_like_escape_str, $this->_like_escape_chr);
+ }
+
+ return $sql;
+ }
+
+ // --------------------------------------------------------------------
+
+ /**
+ * Show column query
+ *
+ * Generates a platform-specific query string so that the column names can be fetched
+ *
+ * @param string $table
+ * @return string
+ */
+ protected function _list_columns($table = '')
+ {
+ return 'SELECT "RDB$FIELD_NAME" FROM "RDB$RELATION_FIELDS" WHERE "RDB$RELATION_NAME" = '.$this->escape($table);
+ }
+
+ // --------------------------------------------------------------------
+
+ /**
+ * Returns an object with field data
+ *
+ * @param string $table
+ * @return array
+ */
+ public function field_data($table)
+ {
+ $sql = 'SELECT "rfields"."RDB$FIELD_NAME" AS "name",
+ CASE "fields"."RDB$FIELD_TYPE"
+ WHEN 7 THEN \'SMALLINT\'
+ WHEN 8 THEN \'INTEGER\'
+ WHEN 9 THEN \'QUAD\'
+ WHEN 10 THEN \'FLOAT\'
+ WHEN 11 THEN \'DFLOAT\'
+ WHEN 12 THEN \'DATE\'
+ WHEN 13 THEN \'TIME\'
+ WHEN 14 THEN \'CHAR\'
+ WHEN 16 THEN \'INT64\'
+ WHEN 27 THEN \'DOUBLE\'
+ WHEN 35 THEN \'TIMESTAMP\'
+ WHEN 37 THEN \'VARCHAR\'
+ WHEN 40 THEN \'CSTRING\'
+ WHEN 261 THEN \'BLOB\'
+ ELSE NULL
+ END AS "type",
+ "fields"."RDB$FIELD_LENGTH" AS "max_length",
+ "rfields"."RDB$DEFAULT_VALUE" AS "default"
+ FROM "RDB$RELATION_FIELDS" "rfields"
+ JOIN "RDB$FIELDS" "fields" ON "rfields"."RDB$FIELD_SOURCE" = "fields"."RDB$FIELD_NAME"
+ WHERE "rfields"."RDB$RELATION_NAME" = '.$this->escape($table).'
+ ORDER BY "rfields"."RDB$FIELD_POSITION"';
+
+ return (($query = $this->query($sql)) !== FALSE)
+ ? $query->result_object()
+ : FALSE;
+ }
+
+ // --------------------------------------------------------------------
+
+ /**
+ * Update statement
+ *
+ * Generates a platform-specific update string from the supplied data
+ *
+ * @param string $table
+ * @param array $values
+ * @return string
+ */
+ protected function _update($table, $values)
+ {
+ $this->qb_limit = FALSE;
+ return parent::_update($table, $values);
+ }
+
+ // --------------------------------------------------------------------
+
+ /**
+ * Truncate statement
+ *
+ * Generates a platform-specific truncate string from the supplied data
+ *
+ * If the database does not support the TRUNCATE statement,
+ * then this method maps to 'DELETE FROM table'
+ *
+ * @param string $table
+ * @return string
+ */
+ protected function _truncate($table)
+ {
+ return 'DELETE FROM '.$table;
+ }
+
+ // --------------------------------------------------------------------
+
+ /**
+ * Delete statement
+ *
+ * Generates a platform-specific delete string from the supplied data
+ *
+ * @param string $table
+ * @return string
+ */
+ protected function _delete($table)
+ {
+ $this->qb_limit = FALSE;
+ return parent::_delete($table);
+ }
+
+ // --------------------------------------------------------------------
+
+ /**
+ * LIMIT
+ *
+ * Generates a platform-specific LIMIT clause
+ *
+ * @param string $sql SQL Query
+ * @return string
+ */
+ protected function _limit($sql)
+ {
+ // Limit clause depends on if Interbase or Firebird
+ if (stripos($this->version(), 'firebird') !== FALSE)
+ {
+ $select = 'FIRST '.$this->qb_limit
+ .($this->qb_offset > 0 ? ' SKIP '.$this->qb_offset : '');
+ }
+ else
+ {
+ $select = 'ROWS '
+ .($this->qb_offset > 0 ? $this->qb_offset.' TO '.($this->qb_limit + $this->qb_offset) : $this->qb_limit);
+ }
+
+ return preg_replace('`SELECT`i', 'SELECT '.$select, $sql);
+ }
+
+ // --------------------------------------------------------------------
+
+ /**
+ * Insert batch statement
+ *
+ * Generates a platform-specific insert string from the supplied data.
+ *
+ * @param string $table Table name
+ * @param array $keys INSERT keys
+ * @param array $values INSERT values
+ * @return string|bool
+ */
+ protected function _insert_batch($table, $keys, $values)
+ {
+ return ($this->db_debug) ? $this->display_error('db_unsupported_feature') : FALSE;
+ }
+}
diff --git a/system/database/drivers/pdo/subdrivers/pdo_firebird_forge.php b/system/database/drivers/pdo/subdrivers/pdo_firebird_forge.php
new file mode 100644
index 000000000..26e052aa4
--- /dev/null
+++ b/system/database/drivers/pdo/subdrivers/pdo_firebird_forge.php
@@ -0,0 +1,238 @@
+<?php
+/**
+ * CodeIgniter
+ *
+ * An open source application development framework for PHP
+ *
+ * This content is released under the MIT License (MIT)
+ *
+ * Copyright (c) 2019 - 2022, CodeIgniter Foundation
+ *
+ * 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.
+ *
+ * @package CodeIgniter
+ * @author EllisLab Dev Team
+ * @copyright Copyright (c) 2008 - 2014, EllisLab, Inc. (https://ellislab.com/)
+ * @copyright Copyright (c) 2014 - 2019, British Columbia Institute of Technology (https://bcit.ca/)
+ * @copyright Copyright (c) 2019 - 2022, CodeIgniter Foundation (https://codeigniter.com/)
+ * @license https://opensource.org/licenses/MIT MIT License
+ * @link https://codeigniter.com
+ * @since Version 3.0.0
+ * @filesource
+ */
+defined('BASEPATH') OR exit('No direct script access allowed');
+
+/**
+ * PDO Firebird Forge Class
+ *
+ * @category Database
+ * @author EllisLab Dev Team
+ * @link https://codeigniter.com/userguide3/database/
+ */
+class CI_DB_pdo_firebird_forge extends CI_DB_pdo_forge {
+
+ /**
+ * RENAME TABLE statement
+ *
+ * @var string
+ */
+ protected $_rename_table = FALSE;
+
+ /**
+ * UNSIGNED support
+ *
+ * @var array
+ */
+ protected $_unsigned = array(
+ 'SMALLINT' => 'INTEGER',
+ 'INTEGER' => 'INT64',
+ 'FLOAT' => 'DOUBLE PRECISION'
+ );
+
+ /**
+ * NULL value representation in CREATE/ALTER TABLE statements
+ *
+ * @var string
+ */
+ protected $_null = 'NULL';
+
+ // --------------------------------------------------------------------
+
+ /**
+ * Create database
+ *
+ * @param string $db_name
+ * @return string
+ */
+ public function create_database($db_name)
+ {
+ // Firebird databases are flat files, so a path is required
+
+ // Hostname is needed for remote access
+ empty($this->db->hostname) OR $db_name = $this->hostname.':'.$db_name;
+
+ return parent::create_database('"'.$db_name.'"');
+ }
+
+ // --------------------------------------------------------------------
+
+ /**
+ * Drop database
+ *
+ * @param string $db_name (ignored)
+ * @return bool
+ */
+ public function drop_database($db_name)
+ {
+ if ( ! ibase_drop_db($this->conn_id))
+ {
+ return ($this->db->db_debug) ? $this->db->display_error('db_unable_to_drop') : FALSE;
+ }
+ elseif ( ! empty($this->db->data_cache['db_names']))
+ {
+ $key = array_search(strtolower($this->db->database), array_map('strtolower', $this->db->data_cache['db_names']), TRUE);
+ if ($key !== FALSE)
+ {
+ unset($this->db->data_cache['db_names'][$key]);
+ }
+ }
+
+ return TRUE;
+ }
+
+ // --------------------------------------------------------------------
+
+ /**
+ * ALTER TABLE
+ *
+ * @param string $alter_type ALTER type
+ * @param string $table Table name
+ * @param mixed $field Column definition
+ * @return string|string[]
+ */
+ protected function _alter_table($alter_type, $table, $field)
+ {
+ if (in_array($alter_type, array('DROP', 'ADD'), TRUE))
+ {
+ return parent::_alter_table($alter_type, $table, $field);
+ }
+
+ $sql = 'ALTER TABLE '.$this->db->escape_identifiers($table);
+ $sqls = array();
+ for ($i = 0, $c = count($field); $i < $c; $i++)
+ {
+ if ($field[$i]['_literal'] !== FALSE)
+ {
+ return FALSE;
+ }
+
+ if (isset($field[$i]['type']))
+ {
+ $sqls[] = $sql.' ALTER COLUMN '.$this->db->escape_identifiers($field[$i]['name'])
+ .' TYPE '.$field[$i]['type'].$field[$i]['length'];
+ }
+
+ if ( ! empty($field[$i]['default']))
+ {
+ $sqls[] = $sql.' ALTER COLUMN '.$this->db->escape_identifiers($field[$i]['name'])
+ .' SET '.$field[$i]['default'];
+ }
+
+ if (isset($field[$i]['null']))
+ {
+ $sqls[] = 'UPDATE "RDB$RELATION_FIELDS" SET "RDB$NULL_FLAG" = '
+ .($field[$i]['null'] === TRUE ? 'NULL' : '1')
+ .' WHERE "RDB$FIELD_NAME" = '.$this->db->escape($field[$i]['name'])
+ .' AND "RDB$RELATION_NAME" = '.$this->db->escape($table);
+ }
+
+ if ( ! empty($field[$i]['new_name']))
+ {
+ $sqls[] = $sql.' ALTER COLUMN '.$this->db->escape_identifiers($field[$i]['name'])
+ .' TO '.$this->db->escape_identifiers($field[$i]['new_name']);
+ }
+ }
+
+ return $sqls;
+ }
+
+ // --------------------------------------------------------------------
+
+ /**
+ * Process column
+ *
+ * @param array $field
+ * @return string
+ */
+ protected function _process_column($field)
+ {
+ return $this->db->escape_identifiers($field['name'])
+ .' '.$field['type'].$field['length']
+ .$field['null']
+ .$field['unique']
+ .$field['default'];
+ }
+
+ // --------------------------------------------------------------------
+
+ /**
+ * Field attribute TYPE
+ *
+ * Performs a data type mapping between different databases.
+ *
+ * @param array &$attributes
+ * @return void
+ */
+ protected function _attr_type(&$attributes)
+ {
+ switch (strtoupper($attributes['TYPE']))
+ {
+ case 'TINYINT':
+ $attributes['TYPE'] = 'SMALLINT';
+ $attributes['UNSIGNED'] = FALSE;
+ return;
+ case 'MEDIUMINT':
+ $attributes['TYPE'] = 'INTEGER';
+ $attributes['UNSIGNED'] = FALSE;
+ return;
+ case 'INT':
+ $attributes['TYPE'] = 'INTEGER';
+ return;
+ case 'BIGINT':
+ $attributes['TYPE'] = 'INT64';
+ return;
+ default: return;
+ }
+ }
+
+ // --------------------------------------------------------------------
+
+ /**
+ * Field attribute AUTO_INCREMENT
+ *
+ * @param array &$attributes
+ * @param array &$field
+ * @return void
+ */
+ protected function _attr_auto_increment(&$attributes, &$field)
+ {
+ // Not supported
+ }
+
+}
diff --git a/system/database/drivers/pdo/subdrivers/pdo_ibm_driver.php b/system/database/drivers/pdo/subdrivers/pdo_ibm_driver.php
new file mode 100644
index 000000000..aca58ecd3
--- /dev/null
+++ b/system/database/drivers/pdo/subdrivers/pdo_ibm_driver.php
@@ -0,0 +1,245 @@
+<?php
+/**
+ * CodeIgniter
+ *
+ * An open source application development framework for PHP
+ *
+ * This content is released under the MIT License (MIT)
+ *
+ * Copyright (c) 2019 - 2022, CodeIgniter Foundation
+ *
+ * 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.
+ *
+ * @package CodeIgniter
+ * @author EllisLab Dev Team
+ * @copyright Copyright (c) 2008 - 2014, EllisLab, Inc. (https://ellislab.com/)
+ * @copyright Copyright (c) 2014 - 2019, British Columbia Institute of Technology (https://bcit.ca/)
+ * @copyright Copyright (c) 2019 - 2022, CodeIgniter Foundation (https://codeigniter.com/)
+ * @license https://opensource.org/licenses/MIT MIT License
+ * @link https://codeigniter.com
+ * @since Version 3.0.0
+ * @filesource
+ */
+defined('BASEPATH') OR exit('No direct script access allowed');
+
+/**
+ * PDO IBM DB2 Database Adapter Class
+ *
+ * Note: _DB is an extender class that the app controller
+ * creates dynamically based on whether the query builder
+ * class is being used or not.
+ *
+ * @package CodeIgniter
+ * @subpackage Drivers
+ * @category Database
+ * @author EllisLab Dev Team
+ * @link https://codeigniter.com/userguide3/database/
+ */
+class CI_DB_pdo_ibm_driver extends CI_DB_pdo_driver {
+
+ /**
+ * Sub-driver
+ *
+ * @var string
+ */
+ public $subdriver = 'ibm';
+
+ // --------------------------------------------------------------------
+
+ /**
+ * Class constructor
+ *
+ * Builds the DSN if not already set.
+ *
+ * @param array $params
+ * @return void
+ */
+ public function __construct($params)
+ {
+ parent::__construct($params);
+
+ if (empty($this->dsn))
+ {
+ $this->dsn = 'ibm:';
+
+ // Pre-defined DSN
+ if (empty($this->hostname) && empty($this->HOSTNAME) && empty($this->port) && empty($this->PORT))
+ {
+ if (isset($this->DSN))
+ {
+ $this->dsn .= 'DSN='.$this->DSN;
+ }
+ elseif ( ! empty($this->database))
+ {
+ $this->dsn .= 'DSN='.$this->database;
+ }
+
+ return;
+ }
+
+ $this->dsn .= 'DRIVER='.(isset($this->DRIVER) ? '{'.$this->DRIVER.'}' : '{IBM DB2 ODBC DRIVER}').';';
+
+ if (isset($this->DATABASE))
+ {
+ $this->dsn .= 'DATABASE='.$this->DATABASE.';';
+ }
+ elseif ( ! empty($this->database))
+ {
+ $this->dsn .= 'DATABASE='.$this->database.';';
+ }
+
+ if (isset($this->HOSTNAME))
+ {
+ $this->dsn .= 'HOSTNAME='.$this->HOSTNAME.';';
+ }
+ else
+ {
+ $this->dsn .= 'HOSTNAME='.(empty($this->hostname) ? '127.0.0.1;' : $this->hostname.';');
+ }
+
+ if (isset($this->PORT))
+ {
+ $this->dsn .= 'PORT='.$this->port.';';
+ }
+ elseif ( ! empty($this->port))
+ {
+ $this->dsn .= ';PORT='.$this->port.';';
+ }
+
+ $this->dsn .= 'PROTOCOL='.(isset($this->PROTOCOL) ? $this->PROTOCOL.';' : 'TCPIP;');
+ }
+ }
+
+ // --------------------------------------------------------------------
+
+ /**
+ * Show table query
+ *
+ * Generates a platform-specific query string so that the table names can be fetched
+ *
+ * @param bool $prefix_limit
+ * @return string
+ */
+ protected function _list_tables($prefix_limit = FALSE)
+ {
+ $sql = 'SELECT "tabname" FROM "syscat"."tables"
+ WHERE "type" = \'T\' AND LOWER("tabschema") = '.$this->escape(strtolower($this->database));
+
+ if ($prefix_limit === TRUE && $this->dbprefix !== '')
+ {
+ $sql .= ' AND "tabname" LIKE \''.$this->escape_like_str($this->dbprefix)."%' "
+ .sprintf($this->_like_escape_str, $this->_like_escape_chr);
+ }
+
+ return $sql;
+ }
+
+ // --------------------------------------------------------------------
+
+ /**
+ * Show column query
+ *
+ * Generates a platform-specific query string so that the column names can be fetched
+ *
+ * @param string $table
+ * @return array
+ */
+ protected function _list_columns($table = '')
+ {
+ return 'SELECT "colname" FROM "syscat"."columns"
+ WHERE LOWER("tabschema") = '.$this->escape(strtolower($this->database)).'
+ AND LOWER("tabname") = '.$this->escape(strtolower($table));
+ }
+
+ // --------------------------------------------------------------------
+
+ /**
+ * Returns an object with field data
+ *
+ * @param string $table
+ * @return array
+ */
+ public function field_data($table)
+ {
+ $sql = 'SELECT "colname" AS "name", "typename" AS "type", "default" AS "default", "length" AS "max_length",
+ CASE "keyseq" WHEN NULL THEN 0 ELSE 1 END AS "primary_key"
+ FROM "syscat"."columns"
+ WHERE LOWER("tabschema") = '.$this->escape(strtolower($this->database)).'
+ AND LOWER("tabname") = '.$this->escape(strtolower($table)).'
+ ORDER BY "colno"';
+
+ return (($query = $this->query($sql)) !== FALSE)
+ ? $query->result_object()
+ : FALSE;
+ }
+
+ // --------------------------------------------------------------------
+
+ /**
+ * Update statement
+ *
+ * Generates a platform-specific update string from the supplied data
+ *
+ * @param string $table
+ * @param array $values
+ * @return string
+ */
+ protected function _update($table, $values)
+ {
+ $this->qb_limit = FALSE;
+ $this->qb_orderby = array();
+ return parent::_update($table, $values);
+ }
+
+ // --------------------------------------------------------------------
+
+ /**
+ * Delete statement
+ *
+ * Generates a platform-specific delete string from the supplied data
+ *
+ * @param string $table
+ * @return string
+ */
+ protected function _delete($table)
+ {
+ $this->qb_limit = FALSE;
+ return parent::_delete($table);
+ }
+
+ // --------------------------------------------------------------------
+
+ /**
+ * LIMIT
+ *
+ * Generates a platform-specific LIMIT clause
+ *
+ * @param string $sql SQL Query
+ * @return string
+ */
+ protected function _limit($sql)
+ {
+ $sql .= ' FETCH FIRST '.($this->qb_limit + $this->qb_offset).' ROWS ONLY';
+
+ return ($this->qb_offset)
+ ? 'SELECT * FROM ('.$sql.') WHERE rownum > '.$this->qb_offset
+ : $sql;
+ }
+
+}
diff --git a/system/database/drivers/pdo/subdrivers/pdo_ibm_forge.php b/system/database/drivers/pdo/subdrivers/pdo_ibm_forge.php
new file mode 100644
index 000000000..cf023d418
--- /dev/null
+++ b/system/database/drivers/pdo/subdrivers/pdo_ibm_forge.php
@@ -0,0 +1,155 @@
+<?php
+/**
+ * CodeIgniter
+ *
+ * An open source application development framework for PHP
+ *
+ * This content is released under the MIT License (MIT)
+ *
+ * Copyright (c) 2019 - 2022, CodeIgniter Foundation
+ *
+ * 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.
+ *
+ * @package CodeIgniter
+ * @author EllisLab Dev Team
+ * @copyright Copyright (c) 2008 - 2014, EllisLab, Inc. (https://ellislab.com/)
+ * @copyright Copyright (c) 2014 - 2019, British Columbia Institute of Technology (https://bcit.ca/)
+ * @copyright Copyright (c) 2019 - 2022, CodeIgniter Foundation (https://codeigniter.com/)
+ * @license https://opensource.org/licenses/MIT MIT License
+ * @link https://codeigniter.com
+ * @since Version 3.0.0
+ * @filesource
+ */
+defined('BASEPATH') OR exit('No direct script access allowed');
+
+/**
+ * PDO IBM DB2 Forge Class
+ *
+ * @category Database
+ * @author EllisLab Dev Team
+ * @link https://codeigniter.com/userguide3/database/
+ */
+class CI_DB_pdo_ibm_forge extends CI_DB_pdo_forge {
+
+ /**
+ * RENAME TABLE IF statement
+ *
+ * @var string
+ */
+ protected $_rename_table = 'RENAME TABLE %s TO %s';
+
+ /**
+ * UNSIGNED support
+ *
+ * @var array
+ */
+ protected $_unsigned = array(
+ 'SMALLINT' => 'INTEGER',
+ 'INT' => 'BIGINT',
+ 'INTEGER' => 'BIGINT'
+ );
+
+ /**
+ * DEFAULT value representation in CREATE/ALTER TABLE statements
+ *
+ * @var string
+ */
+ protected $_default = FALSE;
+
+ // --------------------------------------------------------------------
+
+ /**
+ * ALTER TABLE
+ *
+ * @param string $alter_type ALTER type
+ * @param string $table Table name
+ * @param mixed $field Column definition
+ * @return string|string[]
+ */
+ protected function _alter_table($alter_type, $table, $field)
+ {
+ if ($alter_type === 'CHANGE')
+ {
+ $alter_type = 'MODIFY';
+ }
+
+ return parent::_alter_table($alter_type, $table, $field);
+ }
+
+ // --------------------------------------------------------------------
+
+ /**
+ * Field attribute TYPE
+ *
+ * Performs a data type mapping between different databases.
+ *
+ * @param array &$attributes
+ * @return void
+ */
+ protected function _attr_type(&$attributes)
+ {
+ switch (strtoupper($attributes['TYPE']))
+ {
+ case 'TINYINT':
+ $attributes['TYPE'] = 'SMALLINT';
+ $attributes['UNSIGNED'] = FALSE;
+ return;
+ case 'MEDIUMINT':
+ $attributes['TYPE'] = 'INTEGER';
+ $attributes['UNSIGNED'] = FALSE;
+ return;
+ default: return;
+ }
+ }
+
+ // --------------------------------------------------------------------
+
+ /**
+ * Field attribute UNIQUE
+ *
+ * @param array &$attributes
+ * @param array &$field
+ * @return void
+ */
+ protected function _attr_unique(&$attributes, &$field)
+ {
+ if ( ! empty($attributes['UNIQUE']) && $attributes['UNIQUE'] === TRUE)
+ {
+ $field['unique'] = ' UNIQUE';
+
+ // UNIQUE must be used with NOT NULL
+ $field['null'] = ' NOT NULL';
+ }
+ }
+
+ // --------------------------------------------------------------------
+
+ /**
+ * Field attribute AUTO_INCREMENT
+ *
+ * @param array &$attributes
+ * @param array &$field
+ * @return void
+ */
+ protected function _attr_auto_increment(&$attributes, &$field)
+ {
+ // Not supported
+ }
+
+}
diff --git a/system/database/drivers/pdo/subdrivers/pdo_informix_driver.php b/system/database/drivers/pdo/subdrivers/pdo_informix_driver.php
new file mode 100644
index 000000000..4d230c3fd
--- /dev/null
+++ b/system/database/drivers/pdo/subdrivers/pdo_informix_driver.php
@@ -0,0 +1,310 @@
+<?php
+/**
+ * CodeIgniter
+ *
+ * An open source application development framework for PHP
+ *
+ * This content is released under the MIT License (MIT)
+ *
+ * Copyright (c) 2019 - 2022, CodeIgniter Foundation
+ *
+ * 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.
+ *
+ * @package CodeIgniter
+ * @author EllisLab Dev Team
+ * @copyright Copyright (c) 2008 - 2014, EllisLab, Inc. (https://ellislab.com/)
+ * @copyright Copyright (c) 2014 - 2019, British Columbia Institute of Technology (https://bcit.ca/)
+ * @copyright Copyright (c) 2019 - 2022, CodeIgniter Foundation (https://codeigniter.com/)
+ * @license https://opensource.org/licenses/MIT MIT License
+ * @link https://codeigniter.com
+ * @since Version 3.0.0
+ * @filesource
+ */
+defined('BASEPATH') OR exit('No direct script access allowed');
+
+/**
+ * PDO Informix Database Adapter Class
+ *
+ * Note: _DB is an extender class that the app controller
+ * creates dynamically based on whether the query builder
+ * class is being used or not.
+ *
+ * @package CodeIgniter
+ * @subpackage Drivers
+ * @category Database
+ * @author EllisLab Dev Team
+ * @link https://codeigniter.com/userguide3/database/
+ */
+class CI_DB_pdo_informix_driver extends CI_DB_pdo_driver {
+
+ /**
+ * Sub-driver
+ *
+ * @var string
+ */
+ public $subdriver = 'informix';
+
+ // --------------------------------------------------------------------
+
+ /**
+ * ORDER BY random keyword
+ *
+ * @var array
+ */
+ protected $_random_keyword = array('ASC', 'ASC'); // Currently not supported
+
+ // --------------------------------------------------------------------
+
+ /**
+ * Class constructor
+ *
+ * Builds the DSN if not already set.
+ *
+ * @param array $params
+ * @return void
+ */
+ public function __construct($params)
+ {
+ parent::__construct($params);
+
+ if (empty($this->dsn))
+ {
+ $this->dsn = 'informix:';
+
+ // Pre-defined DSN
+ if (empty($this->hostname) && empty($this->host) && empty($this->port) && empty($this->service))
+ {
+ if (isset($this->DSN))
+ {
+ $this->dsn .= 'DSN='.$this->DSN;
+ }
+ elseif ( ! empty($this->database))
+ {
+ $this->dsn .= 'DSN='.$this->database;
+ }
+
+ return;
+ }
+
+ if (isset($this->host))
+ {
+ $this->dsn .= 'host='.$this->host;
+ }
+ else
+ {
+ $this->dsn .= 'host='.(empty($this->hostname) ? '127.0.0.1' : $this->hostname);
+ }
+
+ if (isset($this->service))
+ {
+ $this->dsn .= '; service='.$this->service;
+ }
+ elseif ( ! empty($this->port))
+ {
+ $this->dsn .= '; service='.$this->port;
+ }
+
+ empty($this->database) OR $this->dsn .= '; database='.$this->database;
+ empty($this->server) OR $this->dsn .= '; server='.$this->server;
+
+ $this->dsn .= '; protocol='.(isset($this->protocol) ? $this->protocol : 'onsoctcp')
+ .'; EnableScrollableCursors=1';
+ }
+ }
+
+ // --------------------------------------------------------------------
+
+ /**
+ * Show table query
+ *
+ * Generates a platform-specific query string so that the table names can be fetched
+ *
+ * @param bool $prefix_limit
+ * @return string
+ */
+ protected function _list_tables($prefix_limit = FALSE)
+ {
+ $sql = 'SELECT "tabname" FROM "systables"
+ WHERE "tabid" > 99 AND "tabtype" = \'T\' AND LOWER("owner") = '.$this->escape(strtolower($this->username));
+
+ if ($prefix_limit === TRUE && $this->dbprefix !== '')
+ {
+ $sql .= ' AND "tabname" LIKE \''.$this->escape_like_str($this->dbprefix)."%' "
+ .sprintf($this->_like_escape_str, $this->_like_escape_chr);
+ }
+
+ return $sql;
+ }
+
+ // --------------------------------------------------------------------
+
+ /**
+ * Show column query
+ *
+ * Generates a platform-specific query string so that the column names can be fetched
+ *
+ * @param string $table
+ * @return string
+ */
+ protected function _list_columns($table = '')
+ {
+ if (strpos($table, '.') !== FALSE)
+ {
+ sscanf($table, '%[^.].%s', $owner, $table);
+ }
+ else
+ {
+ $owner = $this->username;
+ }
+
+ return 'SELECT "colname" FROM "systables", "syscolumns"
+ WHERE "systables"."tabid" = "syscolumns"."tabid"
+ AND "systables"."tabtype" = \'T\'
+ AND LOWER("systables"."owner") = '.$this->escape(strtolower($owner)).'
+ AND LOWER("systables"."tabname") = '.$this->escape(strtolower($table));
+ }
+
+ // --------------------------------------------------------------------
+
+ /**
+ * Returns an object with field data
+ *
+ * @param string $table
+ * @return array
+ */
+ public function field_data($table)
+ {
+ $sql = 'SELECT "syscolumns"."colname" AS "name",
+ CASE "syscolumns"."coltype"
+ WHEN 0 THEN \'CHAR\'
+ WHEN 1 THEN \'SMALLINT\'
+ WHEN 2 THEN \'INTEGER\'
+ WHEN 3 THEN \'FLOAT\'
+ WHEN 4 THEN \'SMALLFLOAT\'
+ WHEN 5 THEN \'DECIMAL\'
+ WHEN 6 THEN \'SERIAL\'
+ WHEN 7 THEN \'DATE\'
+ WHEN 8 THEN \'MONEY\'
+ WHEN 9 THEN \'NULL\'
+ WHEN 10 THEN \'DATETIME\'
+ WHEN 11 THEN \'BYTE\'
+ WHEN 12 THEN \'TEXT\'
+ WHEN 13 THEN \'VARCHAR\'
+ WHEN 14 THEN \'INTERVAL\'
+ WHEN 15 THEN \'NCHAR\'
+ WHEN 16 THEN \'NVARCHAR\'
+ WHEN 17 THEN \'INT8\'
+ WHEN 18 THEN \'SERIAL8\'
+ WHEN 19 THEN \'SET\'
+ WHEN 20 THEN \'MULTISET\'
+ WHEN 21 THEN \'LIST\'
+ WHEN 22 THEN \'Unnamed ROW\'
+ WHEN 40 THEN \'LVARCHAR\'
+ WHEN 41 THEN \'BLOB/CLOB/BOOLEAN\'
+ WHEN 4118 THEN \'Named ROW\'
+ ELSE "syscolumns"."coltype"
+ END AS "type",
+ "syscolumns"."collength" as "max_length",
+ CASE "sysdefaults"."type"
+ WHEN \'L\' THEN "sysdefaults"."default"
+ ELSE NULL
+ END AS "default"
+ FROM "syscolumns", "systables", "sysdefaults"
+ WHERE "syscolumns"."tabid" = "systables"."tabid"
+ AND "systables"."tabid" = "sysdefaults"."tabid"
+ AND "syscolumns"."colno" = "sysdefaults"."colno"
+ AND "systables"."tabtype" = \'T\'
+ AND LOWER("systables"."owner") = '.$this->escape(strtolower($this->username)).'
+ AND LOWER("systables"."tabname") = '.$this->escape(strtolower($table)).'
+ ORDER BY "syscolumns"."colno"';
+
+ return (($query = $this->query($sql)) !== FALSE)
+ ? $query->result_object()
+ : FALSE;
+ }
+
+ // --------------------------------------------------------------------
+
+ /**
+ * Update statement
+ *
+ * Generates a platform-specific update string from the supplied data
+ *
+ * @param string $table
+ * @param array $values
+ * @return string
+ */
+ protected function _update($table, $values)
+ {
+ $this->qb_limit = FALSE;
+ $this->qb_orderby = array();
+ return parent::_update($table, $values);
+ }
+
+ // --------------------------------------------------------------------
+
+ /**
+ * Truncate statement
+ *
+ * Generates a platform-specific truncate string from the supplied data
+ *
+ * If the database does not support the TRUNCATE statement,
+ * then this method maps to 'DELETE FROM table'
+ *
+ * @param string $table
+ * @return string
+ */
+ protected function _truncate($table)
+ {
+ return 'TRUNCATE TABLE ONLY '.$table;
+ }
+
+ // --------------------------------------------------------------------
+
+ /**
+ * Delete statement
+ *
+ * Generates a platform-specific delete string from the supplied data
+ *
+ * @param string $table
+ * @return string
+ */
+ protected function _delete($table)
+ {
+ $this->qb_limit = FALSE;
+ return parent::_delete($table);
+ }
+
+ // --------------------------------------------------------------------
+
+ /**
+ * LIMIT
+ *
+ * Generates a platform-specific LIMIT clause
+ *
+ * @param string $sql $SQL Query
+ * @return string
+ */
+ protected function _limit($sql)
+ {
+ $select = 'SELECT '.($this->qb_offset ? 'SKIP '.$this->qb_offset : '').'FIRST '.$this->qb_limit.' ';
+ return preg_replace('/^(SELECT\s)/i', $select, $sql, 1);
+ }
+
+}
diff --git a/system/database/drivers/pdo/subdrivers/pdo_informix_forge.php b/system/database/drivers/pdo/subdrivers/pdo_informix_forge.php
new file mode 100644
index 000000000..368d8dc96
--- /dev/null
+++ b/system/database/drivers/pdo/subdrivers/pdo_informix_forge.php
@@ -0,0 +1,164 @@
+<?php
+/**
+ * CodeIgniter
+ *
+ * An open source application development framework for PHP
+ *
+ * This content is released under the MIT License (MIT)
+ *
+ * Copyright (c) 2019 - 2022, CodeIgniter Foundation
+ *
+ * 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.
+ *
+ * @package CodeIgniter
+ * @author EllisLab Dev Team
+ * @copyright Copyright (c) 2008 - 2014, EllisLab, Inc. (https://ellislab.com/)
+ * @copyright Copyright (c) 2014 - 2019, British Columbia Institute of Technology (https://bcit.ca/)
+ * @copyright Copyright (c) 2019 - 2022, CodeIgniter Foundation (https://codeigniter.com/)
+ * @license https://opensource.org/licenses/MIT MIT License
+ * @link https://codeigniter.com
+ * @since Version 3.0.0
+ * @filesource
+ */
+defined('BASEPATH') OR exit('No direct script access allowed');
+
+/**
+ * PDO Informix Forge Class
+ *
+ * @category Database
+ * @author EllisLab Dev Team
+ * @link https://codeigniter.com/userguide3/database/
+ */
+class CI_DB_pdo_informix_forge extends CI_DB_pdo_forge {
+
+ /**
+ * RENAME TABLE statement
+ *
+ * @var string
+ */
+ protected $_rename_table = 'RENAME TABLE %s TO %s';
+
+ /**
+ * UNSIGNED support
+ *
+ * @var array
+ */
+ protected $_unsigned = array(
+ 'SMALLINT' => 'INTEGER',
+ 'INT' => 'BIGINT',
+ 'INTEGER' => 'BIGINT',
+ 'REAL' => 'DOUBLE PRECISION',
+ 'SMALLFLOAT' => 'DOUBLE PRECISION'
+ );
+
+ /**
+ * DEFAULT value representation in CREATE/ALTER TABLE statements
+ *
+ * @var string
+ */
+ protected $_default = ', ';
+
+ // --------------------------------------------------------------------
+
+ /**
+ * ALTER TABLE
+ *
+ * @param string $alter_type ALTER type
+ * @param string $table Table name
+ * @param mixed $field Column definition
+ * @return string|string[]
+ */
+ protected function _alter_table($alter_type, $table, $field)
+ {
+ if ($alter_type === 'CHANGE')
+ {
+ $alter_type = 'MODIFY';
+ }
+
+ return parent::_alter_table($alter_type, $table, $field);
+ }
+
+ // --------------------------------------------------------------------
+
+ /**
+ * Field attribute TYPE
+ *
+ * Performs a data type mapping between different databases.
+ *
+ * @param array &$attributes
+ * @return void
+ */
+ protected function _attr_type(&$attributes)
+ {
+ switch (strtoupper($attributes['TYPE']))
+ {
+ case 'TINYINT':
+ $attributes['TYPE'] = 'SMALLINT';
+ $attributes['UNSIGNED'] = FALSE;
+ return;
+ case 'MEDIUMINT':
+ $attributes['TYPE'] = 'INTEGER';
+ $attributes['UNSIGNED'] = FALSE;
+ return;
+ case 'BYTE':
+ case 'TEXT':
+ case 'BLOB':
+ case 'CLOB':
+ $attributes['UNIQUE'] = FALSE;
+ if (isset($attributes['DEFAULT']))
+ {
+ unset($attributes['DEFAULT']);
+ }
+ return;
+ default: return;
+ }
+ }
+
+ // --------------------------------------------------------------------
+
+ /**
+ * Field attribute UNIQUE
+ *
+ * @param array &$attributes
+ * @param array &$field
+ * @return void
+ */
+ protected function _attr_unique(&$attributes, &$field)
+ {
+ if ( ! empty($attributes['UNIQUE']) && $attributes['UNIQUE'] === TRUE)
+ {
+ $field['unique'] = ' UNIQUE CONSTRAINT '.$this->db->escape_identifiers($field['name']);
+ }
+ }
+
+ // --------------------------------------------------------------------
+
+ /**
+ * Field attribute AUTO_INCREMENT
+ *
+ * @param array &$attributes
+ * @param array &$field
+ * @return void
+ */
+ protected function _attr_auto_increment(&$attributes, &$field)
+ {
+ // Not supported
+ }
+
+}
diff --git a/system/database/drivers/pdo/subdrivers/pdo_mysql_driver.php b/system/database/drivers/pdo/subdrivers/pdo_mysql_driver.php
new file mode 100644
index 000000000..1ad854da1
--- /dev/null
+++ b/system/database/drivers/pdo/subdrivers/pdo_mysql_driver.php
@@ -0,0 +1,380 @@
+<?php
+/**
+ * CodeIgniter
+ *
+ * An open source application development framework for PHP
+ *
+ * This content is released under the MIT License (MIT)
+ *
+ * Copyright (c) 2019 - 2022, CodeIgniter Foundation
+ *
+ * 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.
+ *
+ * @package CodeIgniter
+ * @author EllisLab Dev Team
+ * @copyright Copyright (c) 2008 - 2014, EllisLab, Inc. (https://ellislab.com/)
+ * @copyright Copyright (c) 2014 - 2019, British Columbia Institute of Technology (https://bcit.ca/)
+ * @copyright Copyright (c) 2019 - 2022, CodeIgniter Foundation (https://codeigniter.com/)
+ * @license https://opensource.org/licenses/MIT MIT License
+ * @link https://codeigniter.com
+ * @since Version 3.0.0
+ * @filesource
+ */
+defined('BASEPATH') OR exit('No direct script access allowed');
+
+/**
+ * PDO MySQL Database Adapter Class
+ *
+ * Note: _DB is an extender class that the app controller
+ * creates dynamically based on whether the query builder
+ * class is being used or not.
+ *
+ * @package CodeIgniter
+ * @subpackage Drivers
+ * @category Database
+ * @author EllisLab Dev Team
+ * @link https://codeigniter.com/userguide3/database/
+ */
+class CI_DB_pdo_mysql_driver extends CI_DB_pdo_driver {
+
+ /**
+ * Sub-driver
+ *
+ * @var string
+ */
+ public $subdriver = 'mysql';
+
+ /**
+ * Compression flag
+ *
+ * @var bool
+ */
+ public $compress = FALSE;
+
+ /**
+ * Strict ON flag
+ *
+ * Whether we're running in strict SQL mode.
+ *
+ * @var bool
+ */
+ public $stricton;
+
+ // --------------------------------------------------------------------
+
+ /**
+ * Identifier escape character
+ *
+ * @var string
+ */
+ protected $_escape_char = '`';
+
+ // --------------------------------------------------------------------
+
+ /**
+ * Class constructor
+ *
+ * Builds the DSN if not already set.
+ *
+ * @param array $params
+ * @return void
+ */
+ public function __construct($params)
+ {
+ parent::__construct($params);
+
+ if (empty($this->dsn))
+ {
+ $this->dsn = 'mysql:host='.(empty($this->hostname) ? '127.0.0.1' : $this->hostname);
+
+ empty($this->port) OR $this->dsn .= ';port='.$this->port;
+ empty($this->database) OR $this->dsn .= ';dbname='.$this->database;
+ empty($this->char_set) OR $this->dsn .= ';charset='.$this->char_set;
+ }
+ elseif ( ! empty($this->char_set) && strpos($this->dsn, 'charset=', 6) === FALSE)
+ {
+ $this->dsn .= ';charset='.$this->char_set;
+ }
+ }
+
+ // --------------------------------------------------------------------
+
+ /**
+ * Database connection
+ *
+ * @param bool $persistent
+ * @return object
+ */
+ public function db_connect($persistent = FALSE)
+ {
+ if (isset($this->stricton))
+ {
+ if ($this->stricton)
+ {
+ $sql = 'CONCAT(@@sql_mode, ",", "STRICT_ALL_TABLES")';
+ }
+ else
+ {
+ $sql = 'REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(
+ @@sql_mode,
+ "STRICT_ALL_TABLES,", ""),
+ ",STRICT_ALL_TABLES", ""),
+ "STRICT_ALL_TABLES", ""),
+ "STRICT_TRANS_TABLES,", ""),
+ ",STRICT_TRANS_TABLES", ""),
+ "STRICT_TRANS_TABLES", "")';
+ }
+
+ if ( ! empty($sql))
+ {
+ if (empty($this->options[PDO::MYSQL_ATTR_INIT_COMMAND]))
+ {
+ $this->options[PDO::MYSQL_ATTR_INIT_COMMAND] = 'SET SESSION sql_mode = '.$sql;
+ }
+ else
+ {
+ $this->options[PDO::MYSQL_ATTR_INIT_COMMAND] .= ', @@session.sql_mode = '.$sql;
+ }
+ }
+ }
+
+ if ($this->compress === TRUE)
+ {
+ $this->options[PDO::MYSQL_ATTR_COMPRESS] = TRUE;
+ }
+
+ if (is_array($this->encrypt))
+ {
+ $ssl = array();
+ empty($this->encrypt['ssl_key']) OR $ssl[PDO::MYSQL_ATTR_SSL_KEY] = $this->encrypt['ssl_key'];
+ empty($this->encrypt['ssl_cert']) OR $ssl[PDO::MYSQL_ATTR_SSL_CERT] = $this->encrypt['ssl_cert'];
+ empty($this->encrypt['ssl_ca']) OR $ssl[PDO::MYSQL_ATTR_SSL_CA] = $this->encrypt['ssl_ca'];
+ empty($this->encrypt['ssl_capath']) OR $ssl[PDO::MYSQL_ATTR_SSL_CAPATH] = $this->encrypt['ssl_capath'];
+ empty($this->encrypt['ssl_cipher']) OR $ssl[PDO::MYSQL_ATTR_SSL_CIPHER] = $this->encrypt['ssl_cipher'];
+
+ if (defined('PDO::MYSQL_ATTR_SSL_VERIFY_SERVER_CERT') && isset($this->encrypt['ssl_verify']))
+ {
+ $ssl[PDO::MYSQL_ATTR_SSL_VERIFY_SERVER_CERT] = $this->encrypt['ssl_verify'];
+ }
+
+ // DO NOT use array_merge() here!
+ // It re-indexes numeric keys and the PDO_MYSQL_ATTR_SSL_* constants are integers.
+ empty($ssl) OR $this->options += $ssl;
+ }
+
+ // Prior to version 5.7.3, MySQL silently downgrades to an unencrypted connection if SSL setup fails
+ if (
+ ($pdo = parent::db_connect($persistent)) !== FALSE
+ && ! empty($ssl)
+ && version_compare($pdo->getAttribute(PDO::ATTR_CLIENT_VERSION), '5.7.3', '<=')
+ && empty($pdo->query("SHOW STATUS LIKE 'ssl_cipher'")->fetchObject()->Value)
+ )
+ {
+ $message = 'PDO_MYSQL was configured for an SSL connection, but got an unencrypted connection instead!';
+ log_message('error', $message);
+ return ($this->db_debug) ? $this->display_error($message, '', TRUE) : FALSE;
+ }
+
+ return $pdo;
+ }
+
+ // --------------------------------------------------------------------
+
+ /**
+ * Select the database
+ *
+ * @param string $database
+ * @return bool
+ */
+ public function db_select($database = '')
+ {
+ if ($database === '')
+ {
+ $database = $this->database;
+ }
+
+ if (FALSE !== $this->simple_query('USE '.$this->escape_identifiers($database)))
+ {
+ $this->database = $database;
+ $this->data_cache = array();
+ return TRUE;
+ }
+
+ return FALSE;
+ }
+
+ // --------------------------------------------------------------------
+
+ /**
+ * Begin Transaction
+ *
+ * @return bool
+ */
+ protected function _trans_begin()
+ {
+ $this->conn_id->setAttribute(PDO::ATTR_AUTOCOMMIT, FALSE);
+ return $this->conn_id->beginTransaction();
+ }
+
+ // --------------------------------------------------------------------
+
+ /**
+ * Commit Transaction
+ *
+ * @return bool
+ */
+ protected function _trans_commit()
+ {
+ if ($this->conn_id->commit())
+ {
+ $this->conn_id->setAttribute(PDO::ATTR_AUTOCOMMIT, TRUE);
+ return TRUE;
+ }
+
+ return FALSE;
+ }
+
+ // --------------------------------------------------------------------
+
+ /**
+ * Rollback Transaction
+ *
+ * @return bool
+ */
+ protected function _trans_rollback()
+ {
+ if ($this->conn_id->rollBack())
+ {
+ $this->conn_id->setAttribute(PDO::ATTR_AUTOCOMMIT, TRUE);
+ return TRUE;
+ }
+
+ return FALSE;
+ }
+
+ // --------------------------------------------------------------------
+
+ /**
+ * Show table query
+ *
+ * Generates a platform-specific query string so that the table names can be fetched
+ *
+ * @param bool $prefix_limit
+ * @return string
+ */
+ protected function _list_tables($prefix_limit = FALSE)
+ {
+ $sql = 'SHOW TABLES FROM '.$this->_escape_char.$this->database.$this->_escape_char;
+
+ if ($prefix_limit === TRUE && $this->dbprefix !== '')
+ {
+ return $sql." LIKE '".$this->escape_like_str($this->dbprefix)."%'";
+ }
+
+ return $sql;
+ }
+
+ // --------------------------------------------------------------------
+
+ /**
+ * Show column query
+ *
+ * Generates a platform-specific query string so that the column names can be fetched
+ *
+ * @param string $table
+ * @return string
+ */
+ protected function _list_columns($table = '')
+ {
+ return 'SHOW COLUMNS FROM '.$this->protect_identifiers($table, TRUE, NULL, FALSE);
+ }
+
+ // --------------------------------------------------------------------
+
+ /**
+ * Returns an object with field data
+ *
+ * @param string $table
+ * @return array
+ */
+ public function field_data($table)
+ {
+ if (($query = $this->query('SHOW COLUMNS FROM '.$this->protect_identifiers($table, TRUE, NULL, FALSE))) === FALSE)
+ {
+ return FALSE;
+ }
+ $query = $query->result_object();
+
+ $retval = array();
+ for ($i = 0, $c = count($query); $i < $c; $i++)
+ {
+ $retval[$i] = new stdClass();
+ $retval[$i]->name = $query[$i]->Field;
+
+ sscanf($query[$i]->Type, '%[a-z](%d)',
+ $retval[$i]->type,
+ $retval[$i]->max_length
+ );
+
+ $retval[$i]->default = $query[$i]->Default;
+ $retval[$i]->primary_key = (int) ($query[$i]->Key === 'PRI');
+ }
+
+ return $retval;
+ }
+
+ // --------------------------------------------------------------------
+
+ /**
+ * Truncate statement
+ *
+ * Generates a platform-specific truncate string from the supplied data
+ *
+ * If the database does not support the TRUNCATE statement,
+ * then this method maps to 'DELETE FROM table'
+ *
+ * @param string $table
+ * @return string
+ */
+ protected function _truncate($table)
+ {
+ return 'TRUNCATE '.$table;
+ }
+
+ // --------------------------------------------------------------------
+
+ /**
+ * FROM tables
+ *
+ * Groups tables in FROM clauses if needed, so there is no confusion
+ * about operator precedence.
+ *
+ * @return string
+ */
+ protected function _from_tables()
+ {
+ if ( ! empty($this->qb_join) && count($this->qb_from) > 1)
+ {
+ return '('.implode(', ', $this->qb_from).')';
+ }
+
+ return implode(', ', $this->qb_from);
+ }
+
+}
diff --git a/system/database/drivers/pdo/subdrivers/pdo_mysql_forge.php b/system/database/drivers/pdo/subdrivers/pdo_mysql_forge.php
new file mode 100644
index 000000000..8bf5cfb34
--- /dev/null
+++ b/system/database/drivers/pdo/subdrivers/pdo_mysql_forge.php
@@ -0,0 +1,257 @@
+<?php
+/**
+ * CodeIgniter
+ *
+ * An open source application development framework for PHP
+ *
+ * This content is released under the MIT License (MIT)
+ *
+ * Copyright (c) 2019 - 2022, CodeIgniter Foundation
+ *
+ * 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.
+ *
+ * @package CodeIgniter
+ * @author EllisLab Dev Team
+ * @copyright Copyright (c) 2008 - 2014, EllisLab, Inc. (https://ellislab.com/)
+ * @copyright Copyright (c) 2014 - 2019, British Columbia Institute of Technology (https://bcit.ca/)
+ * @copyright Copyright (c) 2019 - 2022, CodeIgniter Foundation (https://codeigniter.com/)
+ * @license https://opensource.org/licenses/MIT MIT License
+ * @link https://codeigniter.com
+ * @since Version 3.0.0
+ * @filesource
+ */
+defined('BASEPATH') OR exit('No direct script access allowed');
+
+/**
+ * PDO MySQL Forge Class
+ *
+ * @category Database
+ * @author EllisLab Dev Team
+ * @link https://codeigniter.com/userguide3/database/
+ */
+class CI_DB_pdo_mysql_forge extends CI_DB_pdo_forge {
+
+ /**
+ * CREATE DATABASE statement
+ *
+ * @var string
+ */
+ protected $_create_database = 'CREATE DATABASE %s CHARACTER SET %s COLLATE %s';
+
+ /**
+ * CREATE TABLE IF statement
+ *
+ * @var string
+ */
+ protected $_create_table_if = 'CREATE TABLE IF NOT EXISTS';
+
+ /**
+ * CREATE TABLE keys flag
+ *
+ * Whether table keys are created from within the
+ * CREATE TABLE statement.
+ *
+ * @var bool
+ */
+ protected $_create_table_keys = TRUE;
+
+ /**
+ * DROP TABLE IF statement
+ *
+ * @var string
+ */
+ protected $_drop_table_if = 'DROP TABLE IF EXISTS';
+
+ /**
+ * UNSIGNED support
+ *
+ * @var array
+ */
+ protected $_unsigned = array(
+ 'TINYINT',
+ 'SMALLINT',
+ 'MEDIUMINT',
+ 'INT',
+ 'INTEGER',
+ 'BIGINT',
+ 'REAL',
+ 'DOUBLE',
+ 'DOUBLE PRECISION',
+ 'FLOAT',
+ 'DECIMAL',
+ 'NUMERIC'
+ );
+
+ /**
+ * NULL value representation in CREATE/ALTER TABLE statements
+ *
+ * @var string
+ */
+ protected $_null = 'NULL';
+
+ // --------------------------------------------------------------------
+
+ /**
+ * CREATE TABLE attributes
+ *
+ * @param array $attributes Associative array of table attributes
+ * @return string
+ */
+ protected function _create_table_attr($attributes)
+ {
+ $sql = '';
+
+ foreach (array_keys($attributes) as $key)
+ {
+ if (is_string($key))
+ {
+ $sql .= ' '.strtoupper($key).' = '.$attributes[$key];
+ }
+ }
+
+ if ( ! empty($this->db->char_set) && ! strpos($sql, 'CHARACTER SET') && ! strpos($sql, 'CHARSET'))
+ {
+ $sql .= ' DEFAULT CHARACTER SET = '.$this->db->char_set;
+ }
+
+ if ( ! empty($this->db->dbcollat) && ! strpos($sql, 'COLLATE'))
+ {
+ $sql .= ' COLLATE = '.$this->db->dbcollat;
+ }
+
+ return $sql;
+ }
+
+ // --------------------------------------------------------------------
+
+ /**
+ * ALTER TABLE
+ *
+ * @param string $alter_type ALTER type
+ * @param string $table Table name
+ * @param mixed $field Column definition
+ * @return string|string[]
+ */
+ protected function _alter_table($alter_type, $table, $field)
+ {
+ if ($alter_type === 'DROP')
+ {
+ return parent::_alter_table($alter_type, $table, $field);
+ }
+
+ $sql = 'ALTER TABLE '.$this->db->escape_identifiers($table);
+ for ($i = 0, $c = count($field); $i < $c; $i++)
+ {
+ if ($field[$i]['_literal'] !== FALSE)
+ {
+ $field[$i] = ($alter_type === 'ADD')
+ ? "\n\tADD ".$field[$i]['_literal']
+ : "\n\tMODIFY ".$field[$i]['_literal'];
+ }
+ else
+ {
+ if ($alter_type === 'ADD')
+ {
+ $field[$i]['_literal'] = "\n\tADD ";
+ }
+ else
+ {
+ $field[$i]['_literal'] = empty($field[$i]['new_name']) ? "\n\tMODIFY " : "\n\tCHANGE ";
+ }
+
+ $field[$i] = $field[$i]['_literal'].$this->_process_column($field[$i]);
+ }
+ }
+
+ return array($sql.implode(',', $field));
+ }
+
+ // --------------------------------------------------------------------
+
+ /**
+ * Process column
+ *
+ * @param array $field
+ * @return string
+ */
+ protected function _process_column($field)
+ {
+ $extra_clause = isset($field['after'])
+ ? ' AFTER '.$this->db->escape_identifiers($field['after']) : '';
+
+ if (empty($extra_clause) && isset($field['first']) && $field['first'] === TRUE)
+ {
+ $extra_clause = ' FIRST';
+ }
+
+ return $this->db->escape_identifiers($field['name'])
+ .(empty($field['new_name']) ? '' : ' '.$this->db->escape_identifiers($field['new_name']))
+ .' '.$field['type'].$field['length']
+ .$field['unsigned']
+ .$field['null']
+ .$field['default']
+ .$field['auto_increment']
+ .$field['unique']
+ .(empty($field['comment']) ? '' : ' COMMENT '.$field['comment'])
+ .$extra_clause;
+ }
+
+ // --------------------------------------------------------------------
+
+ /**
+ * Process indexes
+ *
+ * @param string $table (ignored)
+ * @return string
+ */
+ protected function _process_indexes($table)
+ {
+ $sql = '';
+
+ for ($i = 0, $c = count($this->keys); $i < $c; $i++)
+ {
+ if (is_array($this->keys[$i]))
+ {
+ for ($i2 = 0, $c2 = count($this->keys[$i]); $i2 < $c2; $i2++)
+ {
+ if ( ! isset($this->fields[$this->keys[$i][$i2]]))
+ {
+ unset($this->keys[$i][$i2]);
+ continue;
+ }
+ }
+ }
+ elseif ( ! isset($this->fields[$this->keys[$i]]))
+ {
+ unset($this->keys[$i]);
+ continue;
+ }
+
+ is_array($this->keys[$i]) OR $this->keys[$i] = array($this->keys[$i]);
+
+ $sql .= ",\n\tKEY ".$this->db->escape_identifiers(implode('_', $this->keys[$i]))
+ .' ('.implode(', ', $this->db->escape_identifiers($this->keys[$i])).')';
+ }
+
+ $this->keys = array();
+
+ return $sql;
+ }
+
+}
diff --git a/system/database/drivers/pdo/subdrivers/pdo_oci_driver.php b/system/database/drivers/pdo/subdrivers/pdo_oci_driver.php
new file mode 100644
index 000000000..357369159
--- /dev/null
+++ b/system/database/drivers/pdo/subdrivers/pdo_oci_driver.php
@@ -0,0 +1,327 @@
+<?php
+/**
+ * CodeIgniter
+ *
+ * An open source application development framework for PHP
+ *
+ * This content is released under the MIT License (MIT)
+ *
+ * Copyright (c) 2019 - 2022, CodeIgniter Foundation
+ *
+ * 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.
+ *
+ * @package CodeIgniter
+ * @author EllisLab Dev Team
+ * @copyright Copyright (c) 2008 - 2014, EllisLab, Inc. (https://ellislab.com/)
+ * @copyright Copyright (c) 2014 - 2019, British Columbia Institute of Technology (https://bcit.ca/)
+ * @copyright Copyright (c) 2019 - 2022, CodeIgniter Foundation (https://codeigniter.com/)
+ * @license https://opensource.org/licenses/MIT MIT License
+ * @link https://codeigniter.com
+ * @since Version 3.0.0
+ * @filesource
+ */
+defined('BASEPATH') OR exit('No direct script access allowed');
+
+/**
+ * PDO Oracle Database Adapter Class
+ *
+ * Note: _DB is an extender class that the app controller
+ * creates dynamically based on whether the query builder
+ * class is being used or not.
+ *
+ * @package CodeIgniter
+ * @subpackage Drivers
+ * @category Database
+ * @author EllisLab Dev Team
+ * @link https://codeigniter.com/userguide3/database/
+ */
+class CI_DB_pdo_oci_driver extends CI_DB_pdo_driver {
+
+ /**
+ * Sub-driver
+ *
+ * @var string
+ */
+ public $subdriver = 'oci';
+
+ // --------------------------------------------------------------------
+
+ /**
+ * List of reserved identifiers
+ *
+ * Identifiers that must NOT be escaped.
+ *
+ * @var string[]
+ */
+ protected $_reserved_identifiers = array('*', 'rownum');
+
+ /**
+ * ORDER BY random keyword
+ *
+ * @var array
+ */
+ protected $_random_keyword = array('ASC', 'ASC'); // Currently not supported
+
+ /**
+ * COUNT string
+ *
+ * @used-by CI_DB_driver::count_all()
+ * @used-by CI_DB_query_builder::count_all_results()
+ *
+ * @var string
+ */
+ protected $_count_string = 'SELECT COUNT(1) AS ';
+
+ // --------------------------------------------------------------------
+
+ /**
+ * Class constructor
+ *
+ * Builds the DSN if not already set.
+ *
+ * @param array $params
+ * @return void
+ */
+ public function __construct($params)
+ {
+ parent::__construct($params);
+
+ if (empty($this->dsn))
+ {
+ $this->dsn = 'oci:dbname=';
+
+ // Oracle has a slightly different PDO DSN format (Easy Connect),
+ // which also supports pre-defined DSNs.
+ if (empty($this->hostname) && empty($this->port))
+ {
+ $this->dsn .= $this->database;
+ }
+ else
+ {
+ $this->dsn .= '//'.(empty($this->hostname) ? '127.0.0.1' : $this->hostname)
+ .(empty($this->port) ? '' : ':'.$this->port).'/';
+
+ empty($this->database) OR $this->dsn .= $this->database;
+ }
+
+ empty($this->char_set) OR $this->dsn .= ';charset='.$this->char_set;
+ }
+ elseif ( ! empty($this->char_set) && strpos($this->dsn, 'charset=', 4) === FALSE)
+ {
+ $this->dsn .= ';charset='.$this->char_set;
+ }
+ }
+
+ // --------------------------------------------------------------------
+
+ /**
+ * Database version number
+ *
+ * @return string
+ */
+ public function version()
+ {
+ if (isset($this->data_cache['version']))
+ {
+ return $this->data_cache['version'];
+ }
+
+ $version_string = parent::version();
+ if (preg_match('#(Release\s)?(?<version>\d+(?:\.\d+)+)#', $version_string, $match))
+ {
+ return $this->data_cache['version'] = $match['version'];
+ }
+
+ return FALSE;
+ }
+
+ // --------------------------------------------------------------------
+
+ /**
+ * Show table query
+ *
+ * Generates a platform-specific query string so that the table names can be fetched
+ *
+ * @param bool $prefix_limit
+ * @return string
+ */
+ protected function _list_tables($prefix_limit = FALSE)
+ {
+ $sql = 'SELECT "TABLE_NAME" FROM "ALL_TABLES"';
+
+ if ($prefix_limit === TRUE && $this->dbprefix !== '')
+ {
+ return $sql.' WHERE "TABLE_NAME" LIKE \''.$this->escape_like_str($this->dbprefix)."%' "
+ .sprintf($this->_like_escape_str, $this->_like_escape_chr);
+ }
+
+ return $sql;
+ }
+
+ // --------------------------------------------------------------------
+
+ /**
+ * Show column query
+ *
+ * Generates a platform-specific query string so that the column names can be fetched
+ *
+ * @param string $table
+ * @return string
+ */
+ protected function _list_columns($table = '')
+ {
+ if (strpos($table, '.') !== FALSE)
+ {
+ sscanf($table, '%[^.].%s', $owner, $table);
+ }
+ else
+ {
+ $owner = $this->username;
+ }
+
+ return 'SELECT COLUMN_NAME FROM ALL_TAB_COLUMNS
+ WHERE UPPER(OWNER) = '.$this->escape(strtoupper($owner)).'
+ AND UPPER(TABLE_NAME) = '.$this->escape(strtoupper($table));
+ }
+
+ // --------------------------------------------------------------------
+
+ /**
+ * Returns an object with field data
+ *
+ * @param string $table
+ * @return array
+ */
+ public function field_data($table)
+ {
+ if (strpos($table, '.') !== FALSE)
+ {
+ sscanf($table, '%[^.].%s', $owner, $table);
+ }
+ else
+ {
+ $owner = $this->username;
+ }
+
+ $sql = 'SELECT COLUMN_NAME, DATA_TYPE, CHAR_LENGTH, DATA_PRECISION, DATA_LENGTH, DATA_DEFAULT, NULLABLE
+ FROM ALL_TAB_COLUMNS
+ WHERE UPPER(OWNER) = '.$this->escape(strtoupper($owner)).'
+ AND UPPER(TABLE_NAME) = '.$this->escape(strtoupper($table));
+
+ if (($query = $this->query($sql)) === FALSE)
+ {
+ return FALSE;
+ }
+ $query = $query->result_object();
+
+ $retval = array();
+ for ($i = 0, $c = count($query); $i < $c; $i++)
+ {
+ $retval[$i] = new stdClass();
+ $retval[$i]->name = $query[$i]->COLUMN_NAME;
+ $retval[$i]->type = $query[$i]->DATA_TYPE;
+
+ $length = ($query[$i]->CHAR_LENGTH > 0)
+ ? $query[$i]->CHAR_LENGTH : $query[$i]->DATA_PRECISION;
+ if ($length === NULL)
+ {
+ $length = $query[$i]->DATA_LENGTH;
+ }
+ $retval[$i]->max_length = $length;
+
+ $default = $query[$i]->DATA_DEFAULT;
+ if ($default === NULL && $query[$i]->NULLABLE === 'N')
+ {
+ $default = '';
+ }
+ $retval[$i]->default = $query[$i]->COLUMN_DEFAULT;
+ }
+
+ return $retval;
+ }
+
+ // --------------------------------------------------------------------
+
+ /**
+ * Insert batch statement
+ *
+ * @param string $table Table name
+ * @param array $keys INSERT keys
+ * @param array $values INSERT values
+ * @return string
+ */
+ protected function _insert_batch($table, $keys, $values)
+ {
+ $keys = implode(', ', $keys);
+ $sql = "INSERT ALL\n";
+
+ for ($i = 0, $c = count($values); $i < $c; $i++)
+ {
+ $sql .= ' INTO '.$table.' ('.$keys.') VALUES '.$values[$i]."\n";
+ }
+
+ return $sql.'SELECT * FROM dual';
+ }
+
+ // --------------------------------------------------------------------
+
+ /**
+ * Delete statement
+ *
+ * Generates a platform-specific delete string from the supplied data
+ *
+ * @param string $table
+ * @return string
+ */
+ protected function _delete($table)
+ {
+ if ($this->qb_limit)
+ {
+ $this->where('rownum <= ',$this->qb_limit, FALSE);
+ $this->qb_limit = FALSE;
+ }
+
+ return parent::_delete($table);
+ }
+
+ // --------------------------------------------------------------------
+
+ /**
+ * LIMIT
+ *
+ * Generates a platform-specific LIMIT clause
+ *
+ * @param string $sql SQL Query
+ * @return string
+ */
+ protected function _limit($sql)
+ {
+ if (version_compare($this->version(), '12.1', '>='))
+ {
+ // OFFSET-FETCH can be used only with the ORDER BY clause
+ empty($this->qb_orderby) && $sql .= ' ORDER BY 1';
+
+ return $sql.' OFFSET '.(int) $this->qb_offset.' ROWS FETCH NEXT '.$this->qb_limit.' ROWS ONLY';
+ }
+
+ return 'SELECT * FROM (SELECT inner_query.*, rownum rnum FROM ('.$sql.') inner_query WHERE rownum < '.($this->qb_offset + $this->qb_limit + 1).')'
+ .($this->qb_offset ? ' WHERE rnum >= '.($this->qb_offset + 1): '');
+ }
+
+}
diff --git a/system/database/drivers/pdo/subdrivers/pdo_oci_forge.php b/system/database/drivers/pdo/subdrivers/pdo_oci_forge.php
new file mode 100644
index 000000000..0783cd536
--- /dev/null
+++ b/system/database/drivers/pdo/subdrivers/pdo_oci_forge.php
@@ -0,0 +1,208 @@
+<?php
+/**
+ * CodeIgniter
+ *
+ * An open source application development framework for PHP
+ *
+ * This content is released under the MIT License (MIT)
+ *
+ * Copyright (c) 2019 - 2022, CodeIgniter Foundation
+ *
+ * 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.
+ *
+ * @package CodeIgniter
+ * @author EllisLab Dev Team
+ * @copyright Copyright (c) 2008 - 2014, EllisLab, Inc. (https://ellislab.com/)
+ * @copyright Copyright (c) 2014 - 2019, British Columbia Institute of Technology (https://bcit.ca/)
+ * @copyright Copyright (c) 2019 - 2022, CodeIgniter Foundation (https://codeigniter.com/)
+ * @license https://opensource.org/licenses/MIT MIT License
+ * @link https://codeigniter.com
+ * @since Version 3.0.0
+ * @filesource
+ */
+defined('BASEPATH') OR exit('No direct script access allowed');
+
+/**
+ * PDO Oracle Forge Class
+ *
+ * @category Database
+ * @author EllisLab Dev Team
+ * @link https://codeigniter.com/userguide3/database/
+ */
+class CI_DB_pdo_oci_forge extends CI_DB_pdo_forge {
+
+ /**
+ * CREATE DATABASE statement
+ *
+ * @var string
+ */
+ protected $_create_database = FALSE;
+
+ /**
+ * CREATE TABLE IF statement
+ *
+ * @var string
+ */
+ protected $_create_table_if = FALSE;
+
+ /**
+ * DROP DATABASE statement
+ *
+ * @var string
+ */
+ protected $_drop_database = FALSE;
+
+ /**
+ * UNSIGNED support
+ *
+ * @var bool|array
+ */
+ protected $_unsigned = FALSE;
+
+ /**
+ * NULL value representation in CREATE/ALTER TABLE statements
+ *
+ * @var string
+ */
+ protected $_null = 'NULL';
+
+ // --------------------------------------------------------------------
+
+ /**
+ * ALTER TABLE
+ *
+ * @param string $alter_type ALTER type
+ * @param string $table Table name
+ * @param mixed $field Column definition
+ * @return string|string[]
+ */
+ protected function _alter_table($alter_type, $table, $field)
+ {
+ if ($alter_type === 'DROP')
+ {
+ return parent::_alter_table($alter_type, $table, $field);
+ }
+ elseif ($alter_type === 'CHANGE')
+ {
+ $alter_type = 'MODIFY';
+ }
+
+ $sql = 'ALTER TABLE '.$this->db->escape_identifiers($table);
+ $sqls = array();
+ for ($i = 0, $c = count($field); $i < $c; $i++)
+ {
+ if ($field[$i]['_literal'] !== FALSE)
+ {
+ $field[$i] = "\n\t".$field[$i]['_literal'];
+ }
+ else
+ {
+ $field[$i]['_literal'] = "\n\t".$this->_process_column($field[$i]);
+
+ if ( ! empty($field[$i]['comment']))
+ {
+ $sqls[] = 'COMMENT ON COLUMN '
+ .$this->db->escape_identifiers($table).'.'.$this->db->escape_identifiers($field[$i]['name'])
+ .' IS '.$field[$i]['comment'];
+ }
+
+ if ($alter_type === 'MODIFY' && ! empty($field[$i]['new_name']))
+ {
+ $sqls[] = $sql.' RENAME COLUMN '.$this->db->escape_identifiers($field[$i]['name'])
+ .' TO '.$this->db->escape_identifiers($field[$i]['new_name']);
+ }
+ }
+ }
+
+ $sql .= ' '.$alter_type.' ';
+ $sql .= (count($field) === 1)
+ ? $field[0]
+ : '('.implode(',', $field).')';
+
+ // RENAME COLUMN must be executed after MODIFY
+ array_unshift($sqls, $sql);
+ return $sql;
+ }
+
+ // --------------------------------------------------------------------
+
+ /**
+ * Field attribute AUTO_INCREMENT
+ *
+ * @param array &$attributes
+ * @param array &$field
+ * @return void
+ */
+ protected function _attr_auto_increment(&$attributes, &$field)
+ {
+ if ( ! empty($attributes['AUTO_INCREMENT']) && $attributes['AUTO_INCREMENT'] === TRUE && stripos($field['type'], 'number') !== FALSE && version_compare($this->db->version(), '12.1', '>='))
+ {
+ $field['auto_increment'] = ' GENERATED ALWAYS AS IDENTITY';
+ }
+ }
+
+ // --------------------------------------------------------------------
+
+ /**
+ * Process column
+ *
+ * @param array $field
+ * @return string
+ */
+ protected function _process_column($field)
+ {
+ return $this->db->escape_identifiers($field['name'])
+ .' '.$field['type'].$field['length']
+ .$field['unsigned']
+ .$field['default']
+ .$field['auto_increment']
+ .$field['null']
+ .$field['unique'];
+ }
+
+ // --------------------------------------------------------------------
+
+ /**
+ * Field attribute TYPE
+ *
+ * Performs a data type mapping between different databases.
+ *
+ * @param array &$attributes
+ * @return void
+ */
+ protected function _attr_type(&$attributes)
+ {
+ switch (strtoupper($attributes['TYPE']))
+ {
+ case 'TINYINT':
+ $attributes['TYPE'] = 'NUMBER';
+ return;
+ case 'MEDIUMINT':
+ $attributes['TYPE'] = 'NUMBER';
+ return;
+ case 'INT':
+ $attributes['TYPE'] = 'NUMBER';
+ return;
+ case 'BIGINT':
+ $attributes['TYPE'] = 'NUMBER';
+ return;
+ default: return;
+ }
+ }
+}
diff --git a/system/database/drivers/pdo/subdrivers/pdo_odbc_driver.php b/system/database/drivers/pdo/subdrivers/pdo_odbc_driver.php
new file mode 100644
index 000000000..6b7f2373e
--- /dev/null
+++ b/system/database/drivers/pdo/subdrivers/pdo_odbc_driver.php
@@ -0,0 +1,230 @@
+<?php
+/**
+ * CodeIgniter
+ *
+ * An open source application development framework for PHP
+ *
+ * This content is released under the MIT License (MIT)
+ *
+ * Copyright (c) 2019 - 2022, CodeIgniter Foundation
+ *
+ * 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.
+ *
+ * @package CodeIgniter
+ * @author EllisLab Dev Team
+ * @copyright Copyright (c) 2008 - 2014, EllisLab, Inc. (https://ellislab.com/)
+ * @copyright Copyright (c) 2014 - 2019, British Columbia Institute of Technology (https://bcit.ca/)
+ * @copyright Copyright (c) 2019 - 2022, CodeIgniter Foundation (https://codeigniter.com/)
+ * @license https://opensource.org/licenses/MIT MIT License
+ * @link https://codeigniter.com
+ * @since Version 3.0.0
+ * @filesource
+ */
+defined('BASEPATH') OR exit('No direct script access allowed');
+
+/**
+ * PDO ODBC Database Adapter Class
+ *
+ * Note: _DB is an extender class that the app controller
+ * creates dynamically based on whether the query builder
+ * class is being used or not.
+ *
+ * @package CodeIgniter
+ * @subpackage Drivers
+ * @category Database
+ * @author EllisLab Dev Team
+ * @link https://codeigniter.com/userguide3/database/
+ */
+class CI_DB_pdo_odbc_driver extends CI_DB_pdo_driver {
+
+ /**
+ * Sub-driver
+ *
+ * @var string
+ */
+ public $subdriver = 'odbc';
+
+ /**
+ * Database schema
+ *
+ * @var string
+ */
+ public $schema = 'public';
+
+ // --------------------------------------------------------------------
+
+ /**
+ * Identifier escape character
+ *
+ * Must be empty for ODBC.
+ *
+ * @var string
+ */
+ protected $_escape_char = '';
+
+ /**
+ * ESCAPE statement string
+ *
+ * @var string
+ */
+ protected $_like_escape_str = " {escape '%s'} ";
+
+ /**
+ * ORDER BY random keyword
+ *
+ * @var array
+ */
+ protected $_random_keyword = array('RND()', 'RND(%d)');
+
+ // --------------------------------------------------------------------
+
+ /**
+ * Class constructor
+ *
+ * Builds the DSN if not already set.
+ *
+ * @param array $params
+ * @return void
+ */
+ public function __construct($params)
+ {
+ parent::__construct($params);
+
+ if (empty($this->dsn))
+ {
+ $this->dsn = 'odbc:';
+
+ // Pre-defined DSN
+ if (empty($this->hostname) && empty($this->HOSTNAME) && empty($this->port) && empty($this->PORT))
+ {
+ if (isset($this->DSN))
+ {
+ $this->dsn .= 'DSN='.$this->DSN;
+ }
+ elseif ( ! empty($this->database))
+ {
+ $this->dsn .= 'DSN='.$this->database;
+ }
+
+ return;
+ }
+
+ // If the DSN is not pre-configured - try to build an IBM DB2 connection string
+ $this->dsn .= 'DRIVER='.(isset($this->DRIVER) ? '{'.$this->DRIVER.'}' : '{IBM DB2 ODBC DRIVER}').';';
+
+ if (isset($this->DATABASE))
+ {
+ $this->dsn .= 'DATABASE='.$this->DATABASE.';';
+ }
+ elseif ( ! empty($this->database))
+ {
+ $this->dsn .= 'DATABASE='.$this->database.';';
+ }
+
+ if (isset($this->HOSTNAME))
+ {
+ $this->dsn .= 'HOSTNAME='.$this->HOSTNAME.';';
+ }
+ else
+ {
+ $this->dsn .= 'HOSTNAME='.(empty($this->hostname) ? '127.0.0.1;' : $this->hostname.';');
+ }
+
+ if (isset($this->PORT))
+ {
+ $this->dsn .= 'PORT='.$this->port.';';
+ }
+ elseif ( ! empty($this->port))
+ {
+ $this->dsn .= ';PORT='.$this->port.';';
+ }
+
+ $this->dsn .= 'PROTOCOL='.(isset($this->PROTOCOL) ? $this->PROTOCOL.';' : 'TCPIP;');
+ }
+ }
+
+ // --------------------------------------------------------------------
+
+ /**
+ * Platform-dependent string escape
+ *
+ * @param string
+ * @return string
+ */
+ protected function _escape_str($str)
+ {
+ $this->display_error('db_unsupported_feature');
+ }
+
+ // --------------------------------------------------------------------
+
+ /**
+ * Determines if a query is a "write" type.
+ *
+ * @param string An SQL query string
+ * @return bool
+ */
+ public function is_write_type($sql)
+ {
+ if (preg_match('#^(INSERT|UPDATE).*RETURNING\s.+(\,\s?.+)*$#is', $sql))
+ {
+ return FALSE;
+ }
+
+ return parent::is_write_type($sql);
+ }
+
+ // --------------------------------------------------------------------
+
+ /**
+ * Show table query
+ *
+ * Generates a platform-specific query string so that the table names can be fetched
+ *
+ * @param bool $prefix_limit
+ * @return string
+ */
+ protected function _list_tables($prefix_limit = FALSE)
+ {
+ $sql = "SELECT table_name FROM information_schema.tables WHERE table_schema = '".$this->schema."'";
+
+ if ($prefix_limit !== FALSE && $this->dbprefix !== '')
+ {
+ return $sql." AND table_name LIKE '".$this->escape_like_str($this->dbprefix)."%' "
+ .sprintf($this->_like_escape_str, $this->_like_escape_chr);
+ }
+
+ return $sql;
+ }
+
+ // --------------------------------------------------------------------
+
+ /**
+ * Show column query
+ *
+ * Generates a platform-specific query string so that the column names can be fetched
+ *
+ * @param string $table
+ * @return string
+ */
+ protected function _list_columns($table = '')
+ {
+ return 'SELECT column_name FROM information_schema.columns WHERE table_name = '.$this->escape($table);
+ }
+}
diff --git a/system/database/drivers/pdo/subdrivers/pdo_odbc_forge.php b/system/database/drivers/pdo/subdrivers/pdo_odbc_forge.php
new file mode 100644
index 000000000..c9b82387a
--- /dev/null
+++ b/system/database/drivers/pdo/subdrivers/pdo_odbc_forge.php
@@ -0,0 +1,71 @@
+<?php
+/**
+ * CodeIgniter
+ *
+ * An open source application development framework for PHP
+ *
+ * This content is released under the MIT License (MIT)
+ *
+ * Copyright (c) 2019 - 2022, CodeIgniter Foundation
+ *
+ * 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.
+ *
+ * @package CodeIgniter
+ * @author EllisLab Dev Team
+ * @copyright Copyright (c) 2008 - 2014, EllisLab, Inc. (https://ellislab.com/)
+ * @copyright Copyright (c) 2014 - 2019, British Columbia Institute of Technology (https://bcit.ca/)
+ * @copyright Copyright (c) 2019 - 2022, CodeIgniter Foundation (https://codeigniter.com/)
+ * @license https://opensource.org/licenses/MIT MIT License
+ * @link https://codeigniter.com
+ * @since Version 3.0.0
+ * @filesource
+ */
+defined('BASEPATH') OR exit('No direct script access allowed');
+
+/**
+ * PDO ODBC Forge Class
+ *
+ * @category Database
+ * @author EllisLab Dev Team
+ * @link https://codeigniter.com/database/
+ */
+class CI_DB_pdo_odbc_forge extends CI_DB_pdo_forge {
+
+ /**
+ * UNSIGNED support
+ *
+ * @var bool|array
+ */
+ protected $_unsigned = FALSE;
+
+ // --------------------------------------------------------------------
+
+ /**
+ * Field attribute AUTO_INCREMENT
+ *
+ * @param array &$attributes
+ * @param array &$field
+ * @return void
+ */
+ protected function _attr_auto_increment(&$attributes, &$field)
+ {
+ // Not supported (in most databases at least)
+ }
+
+}
diff --git a/system/database/drivers/pdo/subdrivers/pdo_pgsql_driver.php b/system/database/drivers/pdo/subdrivers/pdo_pgsql_driver.php
new file mode 100644
index 000000000..297cc6f58
--- /dev/null
+++ b/system/database/drivers/pdo/subdrivers/pdo_pgsql_driver.php
@@ -0,0 +1,385 @@
+<?php
+/**
+ * CodeIgniter
+ *
+ * An open source application development framework for PHP
+ *
+ * This content is released under the MIT License (MIT)
+ *
+ * Copyright (c) 2019 - 2022, CodeIgniter Foundation
+ *
+ * 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.
+ *
+ * @package CodeIgniter
+ * @author EllisLab Dev Team
+ * @copyright Copyright (c) 2008 - 2014, EllisLab, Inc. (https://ellislab.com/)
+ * @copyright Copyright (c) 2014 - 2019, British Columbia Institute of Technology (https://bcit.ca/)
+ * @copyright Copyright (c) 2019 - 2022, CodeIgniter Foundation (https://codeigniter.com/)
+ * @license https://opensource.org/licenses/MIT MIT License
+ * @link https://codeigniter.com
+ * @since Version 3.0.0
+ * @filesource
+ */
+defined('BASEPATH') OR exit('No direct script access allowed');
+
+/**
+ * PDO PostgreSQL Database Adapter Class
+ *
+ * Note: _DB is an extender class that the app controller
+ * creates dynamically based on whether the query builder
+ * class is being used or not.
+ *
+ * @package CodeIgniter
+ * @subpackage Drivers
+ * @category Database
+ * @author EllisLab Dev Team
+ * @link https://codeigniter.com/userguide3/database/
+ */
+class CI_DB_pdo_pgsql_driver extends CI_DB_pdo_driver {
+
+ /**
+ * Sub-driver
+ *
+ * @var string
+ */
+ public $subdriver = 'pgsql';
+
+ /**
+ * Database schema
+ *
+ * @var string
+ */
+ public $schema = 'public';
+
+ // --------------------------------------------------------------------
+
+ /**
+ * ORDER BY random keyword
+ *
+ * @var array
+ */
+ protected $_random_keyword = array('RANDOM()', 'RANDOM()');
+
+ // --------------------------------------------------------------------
+
+ /**
+ * Class constructor
+ *
+ * Builds the DSN if not already set.
+ *
+ * @param array $params
+ * @return void
+ */
+ public function __construct($params)
+ {
+ parent::__construct($params);
+
+ if (empty($this->dsn))
+ {
+ $this->dsn = 'pgsql:host='.(empty($this->hostname) ? '127.0.0.1' : $this->hostname);
+
+ empty($this->port) OR $this->dsn .= ';port='.$this->port;
+ empty($this->database) OR $this->dsn .= ';dbname='.$this->database;
+
+ if ( ! empty($this->username))
+ {
+ $this->dsn .= ';user='.$this->username;
+ empty($this->password) OR $this->dsn .= ';password='.$this->password;
+ }
+ }
+ }
+
+ // --------------------------------------------------------------------
+
+ /**
+ * Database connection
+ *
+ * @param bool $persistent
+ * @return object
+ */
+ public function db_connect($persistent = FALSE)
+ {
+ $this->conn_id = parent::db_connect($persistent);
+
+ if (is_object($this->conn_id) && ! empty($this->schema))
+ {
+ $this->simple_query('SET search_path TO '.$this->schema.',public');
+ }
+
+ return $this->conn_id;
+ }
+
+ // --------------------------------------------------------------------
+
+ /**
+ * Insert ID
+ *
+ * @param string $name
+ * @return int
+ */
+ public function insert_id($name = NULL)
+ {
+ if ($name === NULL && version_compare($this->version(), '8.1', '>='))
+ {
+ $query = $this->query('SELECT LASTVAL() AS ins_id');
+ $query = $query->row();
+ return $query->ins_id;
+ }
+
+ return $this->conn_id->lastInsertId($name);
+ }
+
+ // --------------------------------------------------------------------
+
+ /**
+ * Determines if a query is a "write" type.
+ *
+ * @param string An SQL query string
+ * @return bool
+ */
+ public function is_write_type($sql)
+ {
+ if (preg_match('#^(INSERT|UPDATE).*RETURNING\s.+(\,\s?.+)*$#is', $sql))
+ {
+ return FALSE;
+ }
+
+ return parent::is_write_type($sql);
+ }
+
+ // --------------------------------------------------------------------
+
+ /**
+ * "Smart" Escape String
+ *
+ * Escapes data based on type
+ *
+ * @param string $str
+ * @return mixed
+ */
+ public function escape($str)
+ {
+ if (is_bool($str))
+ {
+ return ($str) ? 'TRUE' : 'FALSE';
+ }
+
+ return parent::escape($str);
+ }
+
+ // --------------------------------------------------------------------
+
+ /**
+ * ORDER BY
+ *
+ * @param string $orderby
+ * @param string $direction ASC, DESC or RANDOM
+ * @param bool $escape
+ * @return object
+ */
+ public function order_by($orderby, $direction = '', $escape = NULL)
+ {
+ $direction = strtoupper(trim($direction));
+ if ($direction === 'RANDOM')
+ {
+ if ( ! is_float($orderby) && ctype_digit((string) $orderby))
+ {
+ $orderby = ($orderby > 1)
+ ? (float) '0.'.$orderby
+ : (float) $orderby;
+ }
+
+ if (is_float($orderby))
+ {
+ $this->simple_query('SET SEED '.$orderby);
+ }
+
+ $orderby = $this->_random_keyword[0];
+ $direction = '';
+ $escape = FALSE;
+ }
+
+ return parent::order_by($orderby, $direction, $escape);
+ }
+
+ // --------------------------------------------------------------------
+
+ /**
+ * Show table query
+ *
+ * Generates a platform-specific query string so that the table names can be fetched
+ *
+ * @param bool $prefix_limit
+ * @return string
+ */
+ protected function _list_tables($prefix_limit = FALSE)
+ {
+ $sql = 'SELECT "table_name" FROM "information_schema"."tables" WHERE "table_schema" = \''.$this->schema."'";
+
+ if ($prefix_limit === TRUE && $this->dbprefix !== '')
+ {
+ return $sql.' AND "table_name" LIKE \''
+ .$this->escape_like_str($this->dbprefix)."%' "
+ .sprintf($this->_like_escape_str, $this->_like_escape_chr);
+ }
+
+ return $sql;
+ }
+
+ // --------------------------------------------------------------------
+
+ /**
+ * List column query
+ *
+ * Generates a platform-specific query string so that the column names can be fetched
+ *
+ * @param string $table
+ * @return string
+ */
+ protected function _list_columns($table = '')
+ {
+ return 'SELECT "column_name"
+ FROM "information_schema"."columns"
+ WHERE "table_schema" = \''.$this->schema.'\' AND LOWER("table_name") = '.$this->escape(strtolower($table));
+ }
+
+ // --------------------------------------------------------------------
+
+ /**
+ * Returns an object with field data
+ *
+ * @param string $table
+ * @return array
+ */
+ public function field_data($table)
+ {
+ $sql = 'SELECT "column_name", "data_type", "character_maximum_length", "numeric_precision", "column_default"
+ FROM "information_schema"."columns"
+ WHERE "table_schema" = \''.$this->schema.'\' AND LOWER("table_name") = '.$this->escape(strtolower($table));
+
+ if (($query = $this->query($sql)) === FALSE)
+ {
+ return FALSE;
+ }
+ $query = $query->result_object();
+
+ $retval = array();
+ for ($i = 0, $c = count($query); $i < $c; $i++)
+ {
+ $retval[$i] = new stdClass();
+ $retval[$i]->name = $query[$i]->column_name;
+ $retval[$i]->type = $query[$i]->data_type;
+ $retval[$i]->max_length = ($query[$i]->character_maximum_length > 0) ? $query[$i]->character_maximum_length : $query[$i]->numeric_precision;
+ $retval[$i]->default = $query[$i]->column_default;
+ }
+
+ return $retval;
+ }
+
+ // --------------------------------------------------------------------
+
+ /**
+ * Update statement
+ *
+ * Generates a platform-specific update string from the supplied data
+ *
+ * @param string $table
+ * @param array $values
+ * @return string
+ */
+ protected function _update($table, $values)
+ {
+ $this->qb_limit = FALSE;
+ $this->qb_orderby = array();
+ return parent::_update($table, $values);
+ }
+
+ // --------------------------------------------------------------------
+
+ /**
+ * Update_Batch statement
+ *
+ * Generates a platform-specific batch update string from the supplied data
+ *
+ * @param string $table Table name
+ * @param array $values Update data
+ * @param string $index WHERE key
+ * @return string
+ */
+ protected function _update_batch($table, $values, $index)
+ {
+ $ids = array();
+ foreach ($values as $key => $val)
+ {
+ $ids[] = $val[$index]['value'];
+
+ foreach (array_keys($val) as $field)
+ {
+ if ($field !== $index)
+ {
+ $final[$val[$field]['field']][] = 'WHEN '.$val[$index]['value'].' THEN '.$val[$field]['value'];
+ }
+ }
+ }
+
+ $cases = '';
+ foreach ($final as $k => $v)
+ {
+ $cases .= $k.' = (CASE '.$val[$index]['field']."\n"
+ .implode("\n", $v)."\n"
+ .'ELSE '.$k.' END), ';
+ }
+
+ $this->where($val[$index]['field'].' IN('.implode(',', $ids).')', NULL, FALSE);
+
+ return 'UPDATE '.$table.' SET '.substr($cases, 0, -2).$this->_compile_wh('qb_where');
+ }
+
+ // --------------------------------------------------------------------
+
+ /**
+ * Delete statement
+ *
+ * Generates a platform-specific delete string from the supplied data
+ *
+ * @param string $table
+ * @return string
+ */
+ protected function _delete($table)
+ {
+ $this->qb_limit = FALSE;
+ return parent::_delete($table);
+ }
+
+ // --------------------------------------------------------------------
+
+ /**
+ * LIMIT
+ *
+ * Generates a platform-specific LIMIT clause
+ *
+ * @param string $sql SQL Query
+ * @return string
+ */
+ protected function _limit($sql)
+ {
+ return $sql.' LIMIT '.$this->qb_limit.($this->qb_offset ? ' OFFSET '.$this->qb_offset : '');
+ }
+
+}
diff --git a/system/database/drivers/pdo/subdrivers/pdo_pgsql_forge.php b/system/database/drivers/pdo/subdrivers/pdo_pgsql_forge.php
new file mode 100644
index 000000000..cea205427
--- /dev/null
+++ b/system/database/drivers/pdo/subdrivers/pdo_pgsql_forge.php
@@ -0,0 +1,218 @@
+<?php
+/**
+ * CodeIgniter
+ *
+ * An open source application development framework for PHP
+ *
+ * This content is released under the MIT License (MIT)
+ *
+ * Copyright (c) 2019 - 2022, CodeIgniter Foundation
+ *
+ * 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.
+ *
+ * @package CodeIgniter
+ * @author EllisLab Dev Team
+ * @copyright Copyright (c) 2008 - 2014, EllisLab, Inc. (https://ellislab.com/)
+ * @copyright Copyright (c) 2014 - 2019, British Columbia Institute of Technology (https://bcit.ca/)
+ * @copyright Copyright (c) 2019 - 2022, CodeIgniter Foundation (https://codeigniter.com/)
+ * @license https://opensource.org/licenses/MIT MIT License
+ * @link https://codeigniter.com
+ * @since Version 3.0.0
+ * @filesource
+ */
+defined('BASEPATH') OR exit('No direct script access allowed');
+
+/**
+ * PDO PostgreSQL Forge Class
+ *
+ * @category Database
+ * @author EllisLab Dev Team
+ * @link https://codeigniter.com/userguide3/database/
+ */
+class CI_DB_pdo_pgsql_forge extends CI_DB_pdo_forge {
+
+ /**
+ * DROP TABLE IF statement
+ *
+ * @var string
+ */
+ protected $_drop_table_if = 'DROP TABLE IF EXISTS';
+
+ /**
+ * CREATE TABLE IF statement
+ *
+ * @var string
+ */
+ protected $_create_table_if = 'CREATE TABLE IF NOT EXISTS';
+
+ /**
+ * UNSIGNED support
+ *
+ * @var array
+ */
+ protected $_unsigned = array(
+ 'INT2' => 'INTEGER',
+ 'SMALLINT' => 'INTEGER',
+ 'INT' => 'BIGINT',
+ 'INT4' => 'BIGINT',
+ 'INTEGER' => 'BIGINT',
+ 'INT8' => 'NUMERIC',
+ 'BIGINT' => 'NUMERIC',
+ 'REAL' => 'DOUBLE PRECISION',
+ 'FLOAT' => 'DOUBLE PRECISION'
+ );
+
+ /**
+ * NULL value representation in CREATE/ALTER TABLE statements
+ *
+ * @var string
+ */
+ protected $_null = 'NULL';
+
+ // --------------------------------------------------------------------
+
+ /**
+ * Class constructor
+ *
+ * @param object &$db Database object
+ * @return void
+ */
+ public function __construct(&$db)
+ {
+ parent::__construct($db);
+
+ if (version_compare($this->db->version(), '9.0', '>'))
+ {
+ $this->create_table_if = 'CREATE TABLE IF NOT EXISTS';
+ }
+ }
+
+ // --------------------------------------------------------------------
+
+ /**
+ * ALTER TABLE
+ *
+ * @param string $alter_type ALTER type
+ * @param string $table Table name
+ * @param mixed $field Column definition
+ * @return string|string[]
+ */
+ protected function _alter_table($alter_type, $table, $field)
+ {
+ if (in_array($alter_type, array('DROP', 'ADD'), TRUE))
+ {
+ return parent::_alter_table($alter_type, $table, $field);
+ }
+
+ $sql = 'ALTER TABLE '.$this->db->escape_identifiers($table);
+ $sqls = array();
+ for ($i = 0, $c = count($field); $i < $c; $i++)
+ {
+ if ($field[$i]['_literal'] !== FALSE)
+ {
+ return FALSE;
+ }
+
+ if (version_compare($this->db->version(), '8', '>=') && isset($field[$i]['type']))
+ {
+ $sqls[] = $sql.' ALTER COLUMN '.$this->db->escape_identifiers($field[$i]['name'])
+ .' TYPE '.$field[$i]['type'].$field[$i]['length'];
+ }
+
+ if ( ! empty($field[$i]['default']))
+ {
+ $sqls[] = $sql.' ALTER COLUMN '.$this->db->escape_identifiers($field[$i]['name'])
+ .' SET '.$field[$i]['default'];
+ }
+
+ if (isset($field[$i]['null']))
+ {
+ $sqls[] = $sql.' ALTER COLUMN '.$this->db->escape_identifiers($field[$i]['name'])
+ .(trim($field[$i]['null']) === $this->_null ? ' DROP NOT NULL' : ' SET NOT NULL');
+ }
+
+ if ( ! empty($field[$i]['new_name']))
+ {
+ $sqls[] = $sql.' RENAME COLUMN '.$this->db->escape_identifiers($field[$i]['name'])
+ .' TO '.$this->db->escape_identifiers($field[$i]['new_name']);
+ }
+
+ if ( ! empty($field[$i]['comment']))
+ {
+ $sqls[] = 'COMMENT ON COLUMN '
+ .$this->db->escape_identifiers($table).'.'.$this->db->escape_identifiers($field[$i]['name'])
+ .' IS '.$field[$i]['comment'];
+ }
+ }
+
+ return $sqls;
+ }
+
+ // --------------------------------------------------------------------
+
+ /**
+ * Field attribute TYPE
+ *
+ * Performs a data type mapping between different databases.
+ *
+ * @param array &$attributes
+ * @return void
+ */
+ protected function _attr_type(&$attributes)
+ {
+ // Reset field lengths for data types that don't support it
+ if (isset($attributes['CONSTRAINT']) && stripos($attributes['TYPE'], 'int') !== FALSE)
+ {
+ $attributes['CONSTRAINT'] = NULL;
+ }
+
+ switch (strtoupper($attributes['TYPE']))
+ {
+ case 'TINYINT':
+ $attributes['TYPE'] = 'SMALLINT';
+ $attributes['UNSIGNED'] = FALSE;
+ return;
+ case 'MEDIUMINT':
+ $attributes['TYPE'] = 'INTEGER';
+ $attributes['UNSIGNED'] = FALSE;
+ return;
+ default: return;
+ }
+ }
+
+ // --------------------------------------------------------------------
+
+ /**
+ * Field attribute AUTO_INCREMENT
+ *
+ * @param array &$attributes
+ * @param array &$field
+ * @return void
+ */
+ protected function _attr_auto_increment(&$attributes, &$field)
+ {
+ if ( ! empty($attributes['AUTO_INCREMENT']) && $attributes['AUTO_INCREMENT'] === TRUE)
+ {
+ $field['type'] = ($field['type'] === 'NUMERIC')
+ ? 'BIGSERIAL'
+ : 'SERIAL';
+ }
+ }
+
+}
diff --git a/system/database/drivers/pdo/subdrivers/pdo_sqlite_driver.php b/system/database/drivers/pdo/subdrivers/pdo_sqlite_driver.php
new file mode 100644
index 000000000..24c34f2a3
--- /dev/null
+++ b/system/database/drivers/pdo/subdrivers/pdo_sqlite_driver.php
@@ -0,0 +1,214 @@
+<?php
+/**
+ * CodeIgniter
+ *
+ * An open source application development framework for PHP
+ *
+ * This content is released under the MIT License (MIT)
+ *
+ * Copyright (c) 2019 - 2022, CodeIgniter Foundation
+ *
+ * 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.
+ *
+ * @package CodeIgniter
+ * @author EllisLab Dev Team
+ * @copyright Copyright (c) 2008 - 2014, EllisLab, Inc. (https://ellislab.com/)
+ * @copyright Copyright (c) 2014 - 2019, British Columbia Institute of Technology (https://bcit.ca/)
+ * @copyright Copyright (c) 2019 - 2022, CodeIgniter Foundation (https://codeigniter.com/)
+ * @license https://opensource.org/licenses/MIT MIT License
+ * @link https://codeigniter.com
+ * @since Version 3.0.0
+ * @filesource
+ */
+defined('BASEPATH') OR exit('No direct script access allowed');
+
+/**
+ * PDO SQLite Database Adapter Class
+ *
+ * Note: _DB is an extender class that the app controller
+ * creates dynamically based on whether the query builder
+ * class is being used or not.
+ *
+ * @package CodeIgniter
+ * @subpackage Drivers
+ * @category Database
+ * @author EllisLab Dev Team
+ * @link https://codeigniter.com/userguide3/database/
+ */
+class CI_DB_pdo_sqlite_driver extends CI_DB_pdo_driver {
+
+ /**
+ * Sub-driver
+ *
+ * @var string
+ */
+ public $subdriver = 'sqlite';
+
+ // --------------------------------------------------------------------
+
+ /**
+ * ORDER BY random keyword
+ *
+ * @var array
+ */
+ protected $_random_keyword = array('RANDOM()', 'RANDOM()');
+
+ // --------------------------------------------------------------------
+
+ /**
+ * Class constructor
+ *
+ * Builds the DSN if not already set.
+ *
+ * @param array $params
+ * @return void
+ */
+ public function __construct($params)
+ {
+ parent::__construct($params);
+
+ if (empty($this->dsn))
+ {
+ $this->dsn = 'sqlite:';
+
+ if (empty($this->database) && empty($this->hostname))
+ {
+ $this->database = ':memory:';
+ }
+
+ $this->database = empty($this->database) ? $this->hostname : $this->database;
+ }
+ }
+
+ // --------------------------------------------------------------------
+
+ /**
+ * Show table query
+ *
+ * Generates a platform-specific query string so that the table names can be fetched
+ *
+ * @param bool $prefix_limit
+ * @return string
+ */
+ protected function _list_tables($prefix_limit = FALSE)
+ {
+ $sql = 'SELECT "NAME" FROM "SQLITE_MASTER" WHERE "TYPE" = \'table\'';
+
+ if ($prefix_limit === TRUE && $this->dbprefix !== '')
+ {
+ return $sql.' AND "NAME" LIKE \''.$this->escape_like_str($this->dbprefix)."%' "
+ .sprintf($this->_like_escape_str, $this->_like_escape_chr);
+ }
+
+ return $sql;
+ }
+
+ // --------------------------------------------------------------------
+
+ /**
+ * Fetch Field Names
+ *
+ * @param string $table Table name
+ * @return array
+ */
+ public function list_fields($table)
+ {
+ if (($result = $this->query('PRAGMA TABLE_INFO('.$this->protect_identifiers($table, TRUE, NULL, FALSE).')')) === FALSE)
+ {
+ return FALSE;
+ }
+
+ $fields = array();
+ foreach ($result->result_array() as $row)
+ {
+ $fields[] = $row['name'];
+ }
+
+ return $fields;
+ }
+
+ // --------------------------------------------------------------------
+
+ /**
+ * Returns an object with field data
+ *
+ * @param string $table
+ * @return array
+ */
+ public function field_data($table)
+ {
+ if (($query = $this->query('PRAGMA TABLE_INFO('.$this->protect_identifiers($table, TRUE, NULL, FALSE).')')) === FALSE)
+ {
+ return FALSE;
+ }
+
+ $query = $query->result_array();
+ if (empty($query))
+ {
+ return FALSE;
+ }
+
+ $retval = array();
+ for ($i = 0, $c = count($query); $i < $c; $i++)
+ {
+ $retval[$i] = new stdClass();
+ $retval[$i]->name = $query[$i]['name'];
+ $retval[$i]->type = $query[$i]['type'];
+ $retval[$i]->max_length = NULL;
+ $retval[$i]->default = $query[$i]['dflt_value'];
+ $retval[$i]->primary_key = isset($query[$i]['pk']) ? (int) $query[$i]['pk'] : 0;
+ }
+
+ return $retval;
+ }
+
+ // --------------------------------------------------------------------
+
+ /**
+ * Replace statement
+ *
+ * @param string $table Table name
+ * @param array $keys INSERT keys
+ * @param array $values INSERT values
+ * @return string
+ */
+ protected function _replace($table, $keys, $values)
+ {
+ return 'INSERT OR '.parent::_replace($table, $keys, $values);
+ }
+
+ // --------------------------------------------------------------------
+
+ /**
+ * Truncate statement
+ *
+ * Generates a platform-specific truncate string from the supplied data
+ *
+ * If the database does not support the TRUNCATE statement,
+ * then this method maps to 'DELETE FROM table'
+ *
+ * @param string $table
+ * @return string
+ */
+ protected function _truncate($table)
+ {
+ return 'DELETE FROM '.$table;
+ }
+
+}
diff --git a/system/database/drivers/pdo/subdrivers/pdo_sqlite_forge.php b/system/database/drivers/pdo/subdrivers/pdo_sqlite_forge.php
new file mode 100644
index 000000000..b0edcbd6c
--- /dev/null
+++ b/system/database/drivers/pdo/subdrivers/pdo_sqlite_forge.php
@@ -0,0 +1,239 @@
+<?php
+/**
+ * CodeIgniter
+ *
+ * An open source application development framework for PHP
+ *
+ * This content is released under the MIT License (MIT)
+ *
+ * Copyright (c) 2019 - 2022, CodeIgniter Foundation
+ *
+ * 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.
+ *
+ * @package CodeIgniter
+ * @author EllisLab Dev Team
+ * @copyright Copyright (c) 2008 - 2014, EllisLab, Inc. (https://ellislab.com/)
+ * @copyright Copyright (c) 2014 - 2019, British Columbia Institute of Technology (https://bcit.ca/)
+ * @copyright Copyright (c) 2019 - 2022, CodeIgniter Foundation (https://codeigniter.com/)
+ * @license https://opensource.org/licenses/MIT MIT License
+ * @link https://codeigniter.com
+ * @since Version 3.0.0
+ * @filesource
+ */
+defined('BASEPATH') OR exit('No direct script access allowed');
+
+/**
+ * PDO SQLite Forge Class
+ *
+ * @category Database
+ * @author EllisLab Dev Team
+ * @link https://codeigniter.com/userguide3/database/
+ */
+class CI_DB_pdo_sqlite_forge extends CI_DB_pdo_forge {
+
+ /**
+ * CREATE TABLE IF statement
+ *
+ * @var string
+ */
+ protected $_create_table_if = 'CREATE TABLE IF NOT EXISTS';
+
+ /**
+ * DROP TABLE IF statement
+ *
+ * @var string
+ */
+ protected $_drop_table_if = 'DROP TABLE IF EXISTS';
+
+ /**
+ * UNSIGNED support
+ *
+ * @var bool|array
+ */
+ protected $_unsigned = FALSE;
+
+ /**
+ * NULL value representation in CREATE/ALTER TABLE statements
+ *
+ * @var string
+ */
+ protected $_null = 'NULL';
+
+ // --------------------------------------------------------------------
+
+ /**
+ * Class constructor
+ *
+ * @param object &$db Database object
+ * @return void
+ */
+ public function __construct(&$db)
+ {
+ parent::__construct($db);
+
+ if (version_compare($this->db->version(), '3.3', '<'))
+ {
+ $this->_create_table_if = FALSE;
+ $this->_drop_table_if = FALSE;
+ }
+ }
+
+ // --------------------------------------------------------------------
+
+ /**
+ * Create database
+ *
+ * @param string $db_name (ignored)
+ * @return bool
+ */
+ public function create_database($db_name)
+ {
+ // In SQLite, a database is created when you connect to the database.
+ // We'll return TRUE so that an error isn't generated
+ return TRUE;
+ }
+
+ // --------------------------------------------------------------------
+
+ /**
+ * Drop database
+ *
+ * @param string $db_name (ignored)
+ * @return bool
+ */
+ public function drop_database($db_name)
+ {
+ // In SQLite, a database is dropped when we delete a file
+ if (file_exists($this->db->database))
+ {
+ // We need to close the pseudo-connection first
+ $this->db->close();
+ if ( ! @unlink($this->db->database))
+ {
+ return $this->db->db_debug ? $this->db->display_error('db_unable_to_drop') : FALSE;
+ }
+ elseif ( ! empty($this->db->data_cache['db_names']))
+ {
+ $key = array_search(strtolower($this->db->database), array_map('strtolower', $this->db->data_cache['db_names']), TRUE);
+ if ($key !== FALSE)
+ {
+ unset($this->db->data_cache['db_names'][$key]);
+ }
+ }
+
+ return TRUE;
+ }
+
+ return $this->db->db_debug ? $this->db->display_error('db_unable_to_drop') : FALSE;
+ }
+
+ // --------------------------------------------------------------------
+
+ /**
+ * ALTER TABLE
+ *
+ * @param string $alter_type ALTER type
+ * @param string $table Table name
+ * @param mixed $field Column definition
+ * @return string|string[]
+ */
+ protected function _alter_table($alter_type, $table, $field)
+ {
+ if ($alter_type === 'DROP' OR $alter_type === 'CHANGE')
+ {
+ // drop_column():
+ // BEGIN TRANSACTION;
+ // CREATE TEMPORARY TABLE t1_backup(a,b);
+ // INSERT INTO t1_backup SELECT a,b FROM t1;
+ // DROP TABLE t1;
+ // CREATE TABLE t1(a,b);
+ // INSERT INTO t1 SELECT a,b FROM t1_backup;
+ // DROP TABLE t1_backup;
+ // COMMIT;
+
+ return FALSE;
+ }
+
+ return parent::_alter_table($alter_type, $table, $field);
+ }
+
+ // --------------------------------------------------------------------
+
+ /**
+ * Process column
+ *
+ * @param array $field
+ * @return string
+ */
+ protected function _process_column($field)
+ {
+ return $this->db->escape_identifiers($field['name'])
+ .' '.$field['type']
+ .$field['auto_increment']
+ .$field['null']
+ .$field['unique']
+ .$field['default'];
+ }
+
+ // --------------------------------------------------------------------
+
+ /**
+ * Field attribute TYPE
+ *
+ * Performs a data type mapping between different databases.
+ *
+ * @param array &$attributes
+ * @return void
+ */
+ protected function _attr_type(&$attributes)
+ {
+ switch (strtoupper($attributes['TYPE']))
+ {
+ case 'ENUM':
+ case 'SET':
+ $attributes['TYPE'] = 'TEXT';
+ return;
+ default: return;
+ }
+ }
+
+ // --------------------------------------------------------------------
+
+ /**
+ * Field attribute AUTO_INCREMENT
+ *
+ * @param array &$attributes
+ * @param array &$field
+ * @return void
+ */
+ protected function _attr_auto_increment(&$attributes, &$field)
+ {
+ if ( ! empty($attributes['AUTO_INCREMENT']) && $attributes['AUTO_INCREMENT'] === TRUE && stripos($field['type'], 'int') !== FALSE)
+ {
+ $field['type'] = 'INTEGER PRIMARY KEY';
+ $field['default'] = '';
+ $field['null'] = '';
+ $field['unique'] = '';
+ $field['auto_increment'] = ' AUTOINCREMENT';
+
+ $this->primary_keys = array();
+ }
+ }
+
+}
diff --git a/system/database/drivers/pdo/subdrivers/pdo_sqlsrv_driver.php b/system/database/drivers/pdo/subdrivers/pdo_sqlsrv_driver.php
new file mode 100644
index 000000000..685b61e92
--- /dev/null
+++ b/system/database/drivers/pdo/subdrivers/pdo_sqlsrv_driver.php
@@ -0,0 +1,370 @@
+<?php
+/**
+ * CodeIgniter
+ *
+ * An open source application development framework for PHP
+ *
+ * This content is released under the MIT License (MIT)
+ *
+ * Copyright (c) 2019 - 2022, CodeIgniter Foundation
+ *
+ * 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.
+ *
+ * @package CodeIgniter
+ * @author EllisLab Dev Team
+ * @copyright Copyright (c) 2008 - 2014, EllisLab, Inc. (https://ellislab.com/)
+ * @copyright Copyright (c) 2014 - 2019, British Columbia Institute of Technology (https://bcit.ca/)
+ * @copyright Copyright (c) 2019 - 2022, CodeIgniter Foundation (https://codeigniter.com/)
+ * @license https://opensource.org/licenses/MIT MIT License
+ * @link https://codeigniter.com
+ * @since Version 3.0.0
+ * @filesource
+ */
+defined('BASEPATH') OR exit('No direct script access allowed');
+
+/**
+ * PDO SQLSRV Database Adapter Class
+ *
+ * Note: _DB is an extender class that the app controller
+ * creates dynamically based on whether the query builder
+ * class is being used or not.
+ *
+ * @package CodeIgniter
+ * @subpackage Drivers
+ * @category Database
+ * @author EllisLab Dev Team
+ * @link https://codeigniter.com/userguide3/database/
+ */
+class CI_DB_pdo_sqlsrv_driver extends CI_DB_pdo_driver {
+
+ /**
+ * Sub-driver
+ *
+ * @var string
+ */
+ public $subdriver = 'sqlsrv';
+
+ // --------------------------------------------------------------------
+
+ /**
+ * ORDER BY random keyword
+ *
+ * @var array
+ */
+ protected $_random_keyword = array('NEWID()', 'RAND(%d)');
+
+ /**
+ * Quoted identifier flag
+ *
+ * Whether to use SQL-92 standard quoted identifier
+ * (double quotes) or brackets for identifier escaping.
+ *
+ * @var bool
+ */
+ protected $_quoted_identifier;
+
+ // --------------------------------------------------------------------
+
+ /**
+ * Class constructor
+ *
+ * Builds the DSN if not already set.
+ *
+ * @param array $params
+ * @return void
+ */
+ public function __construct($params)
+ {
+ parent::__construct($params);
+
+ if (empty($this->dsn))
+ {
+ $this->dsn = 'sqlsrv:Server='.(empty($this->hostname) ? '127.0.0.1' : $this->hostname);
+
+ empty($this->port) OR $this->dsn .= ','.$this->port;
+ empty($this->database) OR $this->dsn .= ';Database='.$this->database;
+
+ // Some custom options
+
+ if (isset($this->QuotedId))
+ {
+ $this->dsn .= ';QuotedId='.$this->QuotedId;
+ $this->_quoted_identifier = (bool) $this->QuotedId;
+ }
+
+ if (isset($this->ConnectionPooling))
+ {
+ $this->dsn .= ';ConnectionPooling='.$this->ConnectionPooling;
+ }
+
+ if ($this->encrypt === TRUE)
+ {
+ $this->dsn .= ';Encrypt=1';
+ }
+
+ if (isset($this->TraceOn))
+ {
+ $this->dsn .= ';TraceOn='.$this->TraceOn;
+ }
+
+ if (isset($this->TrustServerCertificate))
+ {
+ $this->dsn .= ';TrustServerCertificate='.$this->TrustServerCertificate;
+ }
+
+ empty($this->APP) OR $this->dsn .= ';APP='.$this->APP;
+ empty($this->Failover_Partner) OR $this->dsn .= ';Failover_Partner='.$this->Failover_Partner;
+ empty($this->LoginTimeout) OR $this->dsn .= ';LoginTimeout='.$this->LoginTimeout;
+ empty($this->MultipleActiveResultSets) OR $this->dsn .= ';MultipleActiveResultSets='.$this->MultipleActiveResultSets;
+ empty($this->TraceFile) OR $this->dsn .= ';TraceFile='.$this->TraceFile;
+ empty($this->WSID) OR $this->dsn .= ';WSID='.$this->WSID;
+ }
+ elseif (preg_match('/QuotedId=(0|1)/', $this->dsn, $match))
+ {
+ $this->_quoted_identifier = (bool) $match[1];
+ }
+ }
+
+ // --------------------------------------------------------------------
+
+ /**
+ * Database connection
+ *
+ * @param bool $persistent
+ * @return object
+ */
+ public function db_connect($persistent = FALSE)
+ {
+ if ( ! empty($this->char_set) && preg_match('/utf[^8]*8/i', $this->char_set))
+ {
+ $this->options[PDO::SQLSRV_ENCODING_UTF8] = 1;
+ }
+
+ $this->conn_id = parent::db_connect($persistent);
+
+ if ( ! is_object($this->conn_id) OR is_bool($this->_quoted_identifier))
+ {
+ return $this->conn_id;
+ }
+
+ // Determine how identifiers are escaped
+ $query = $this->query('SELECT CASE WHEN (@@OPTIONS | 256) = @@OPTIONS THEN 1 ELSE 0 END AS qi');
+ $query = $query->row_array();
+ $this->_quoted_identifier = empty($query) ? FALSE : (bool) $query['qi'];
+ $this->_escape_char = ($this->_quoted_identifier) ? '"' : array('[', ']');
+
+ return $this->conn_id;
+ }
+
+ // --------------------------------------------------------------------
+
+ /**
+ * Show table query
+ *
+ * Generates a platform-specific query string so that the table names can be fetched
+ *
+ * @param bool $prefix_limit
+ * @return string
+ */
+ protected function _list_tables($prefix_limit = FALSE)
+ {
+ $sql = 'SELECT '.$this->escape_identifiers('name')
+ .' FROM '.$this->escape_identifiers('sysobjects')
+ .' WHERE '.$this->escape_identifiers('type')." = 'U'";
+
+ if ($prefix_limit === TRUE && $this->dbprefix !== '')
+ {
+ $sql .= ' AND '.$this->escape_identifiers('name')." LIKE '".$this->escape_like_str($this->dbprefix)."%' "
+ .sprintf($this->_like_escape_str, $this->_like_escape_chr);
+ }
+
+ return $sql.' ORDER BY '.$this->escape_identifiers('name');
+ }
+
+ // --------------------------------------------------------------------
+
+ /**
+ * Show column query
+ *
+ * Generates a platform-specific query string so that the column names can be fetched
+ *
+ * @param string $table
+ * @return string
+ */
+ protected function _list_columns($table = '')
+ {
+ return 'SELECT COLUMN_NAME
+ FROM INFORMATION_SCHEMA.Columns
+ WHERE UPPER(TABLE_NAME) = '.$this->escape(strtoupper($table));
+ }
+
+ // --------------------------------------------------------------------
+
+ /**
+ * Returns an object with field data
+ *
+ * @param string $table
+ * @return array
+ */
+ public function field_data($table)
+ {
+ $sql = 'SELECT COLUMN_NAME, DATA_TYPE, CHARACTER_MAXIMUM_LENGTH, NUMERIC_PRECISION, COLUMN_DEFAULT
+ FROM INFORMATION_SCHEMA.Columns
+ WHERE UPPER(TABLE_NAME) = '.$this->escape(strtoupper($table));
+
+ if (($query = $this->query($sql)) === FALSE)
+ {
+ return FALSE;
+ }
+ $query = $query->result_object();
+
+ $retval = array();
+ for ($i = 0, $c = count($query); $i < $c; $i++)
+ {
+ $retval[$i] = new stdClass();
+ $retval[$i]->name = $query[$i]->COLUMN_NAME;
+ $retval[$i]->type = $query[$i]->DATA_TYPE;
+ $retval[$i]->max_length = ($query[$i]->CHARACTER_MAXIMUM_LENGTH > 0) ? $query[$i]->CHARACTER_MAXIMUM_LENGTH : $query[$i]->NUMERIC_PRECISION;
+ $retval[$i]->default = $query[$i]->COLUMN_DEFAULT;
+ }
+
+ return $retval;
+ }
+
+ // --------------------------------------------------------------------
+
+ /**
+ * Update statement
+ *
+ * Generates a platform-specific update string from the supplied data
+ *
+ * @param string $table
+ * @param array $values
+ * @return string
+ */
+ protected function _update($table, $values)
+ {
+ $this->qb_limit = FALSE;
+ $this->qb_orderby = array();
+ return parent::_update($table, $values);
+ }
+
+ // --------------------------------------------------------------------
+
+ /**
+ * Delete statement
+ *
+ * Generates a platform-specific delete string from the supplied data
+ *
+ * @param string $table
+ * @return string
+ */
+ protected function _delete($table)
+ {
+ if ($this->qb_limit)
+ {
+ return 'WITH ci_delete AS (SELECT TOP '.$this->qb_limit.' * FROM '.$table.$this->_compile_wh('qb_where').') DELETE FROM ci_delete';
+ }
+
+ return parent::_delete($table);
+ }
+
+ // --------------------------------------------------------------------
+
+ /**
+ * LIMIT
+ *
+ * Generates a platform-specific LIMIT clause
+ *
+ * @param string $sql SQL Query
+ * @return string
+ */
+ protected function _limit($sql)
+ {
+ // As of SQL Server 2012 (11.0.*) OFFSET is supported
+ if (version_compare($this->version(), '11', '>='))
+ {
+ // SQL Server OFFSET-FETCH can be used only with the ORDER BY clause
+ empty($this->qb_orderby) && $sql .= ' ORDER BY 1';
+
+ return $sql.' OFFSET '.(int) $this->qb_offset.' ROWS FETCH NEXT '.$this->qb_limit.' ROWS ONLY';
+ }
+
+ $limit = $this->qb_offset + $this->qb_limit;
+
+ // An ORDER BY clause is required for ROW_NUMBER() to work
+ if ($this->qb_offset && ! empty($this->qb_orderby))
+ {
+ $orderby = $this->_compile_order_by();
+
+ // We have to strip the ORDER BY clause
+ $sql = trim(substr($sql, 0, strrpos($sql, $orderby)));
+
+ // Get the fields to select from our subquery, so that we can avoid CI_rownum appearing in the actual results
+ if (count($this->qb_select) === 0 OR strpos(implode(',', $this->qb_select), '*') !== FALSE)
+ {
+ $select = '*'; // Inevitable
+ }
+ else
+ {
+ // Use only field names and their aliases, everything else is out of our scope.
+ $select = array();
+ $field_regexp = ($this->_quoted_identifier)
+ ? '("[^\"]+")' : '(\[[^\]]+\])';
+ for ($i = 0, $c = count($this->qb_select); $i < $c; $i++)
+ {
+ $select[] = preg_match('/(?:\s|\.)'.$field_regexp.'$/i', $this->qb_select[$i], $m)
+ ? $m[1] : $this->qb_select[$i];
+ }
+ $select = implode(', ', $select);
+ }
+
+ return 'SELECT '.$select." FROM (\n\n"
+ .preg_replace('/^(SELECT( DISTINCT)?)/i', '\\1 ROW_NUMBER() OVER('.trim($orderby).') AS '.$this->escape_identifiers('CI_rownum').', ', $sql)
+ ."\n\n) ".$this->escape_identifiers('CI_subquery')
+ ."\nWHERE ".$this->escape_identifiers('CI_rownum').' BETWEEN '.($this->qb_offset + 1).' AND '.$limit;
+ }
+
+ return preg_replace('/(^\SELECT (DISTINCT)?)/i','\\1 TOP '.$limit.' ', $sql);
+ }
+
+ // --------------------------------------------------------------------
+
+ /**
+ * Insert batch statement
+ *
+ * Generates a platform-specific insert string from the supplied data.
+ *
+ * @param string $table Table name
+ * @param array $keys INSERT keys
+ * @param array $values INSERT values
+ * @return string|bool
+ */
+ protected function _insert_batch($table, $keys, $values)
+ {
+ // Multiple-value inserts are only supported as of SQL Server 2008
+ if (version_compare($this->version(), '10', '>='))
+ {
+ return parent::_insert_batch($table, $keys, $values);
+ }
+
+ return ($this->db_debug) ? $this->display_error('db_unsupported_feature') : FALSE;
+ }
+
+}
diff --git a/system/database/drivers/pdo/subdrivers/pdo_sqlsrv_forge.php b/system/database/drivers/pdo/subdrivers/pdo_sqlsrv_forge.php
new file mode 100644
index 000000000..07eecea61
--- /dev/null
+++ b/system/database/drivers/pdo/subdrivers/pdo_sqlsrv_forge.php
@@ -0,0 +1,150 @@
+<?php
+/**
+ * CodeIgniter
+ *
+ * An open source application development framework for PHP
+ *
+ * This content is released under the MIT License (MIT)
+ *
+ * Copyright (c) 2019 - 2022, CodeIgniter Foundation
+ *
+ * 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.
+ *
+ * @package CodeIgniter
+ * @author EllisLab Dev Team
+ * @copyright Copyright (c) 2008 - 2014, EllisLab, Inc. (https://ellislab.com/)
+ * @copyright Copyright (c) 2014 - 2019, British Columbia Institute of Technology (https://bcit.ca/)
+ * @copyright Copyright (c) 2019 - 2022, CodeIgniter Foundation (https://codeigniter.com/)
+ * @license https://opensource.org/licenses/MIT MIT License
+ * @link https://codeigniter.com
+ * @since Version 3.0.0
+ * @filesource
+ */
+defined('BASEPATH') OR exit('No direct script access allowed');
+
+/**
+ * PDO SQLSRV Forge Class
+ *
+ * @category Database
+ * @author EllisLab Dev Team
+ * @link https://codeigniter.com/userguide3/database/
+ */
+class CI_DB_pdo_sqlsrv_forge extends CI_DB_pdo_forge {
+
+ /**
+ * CREATE TABLE IF statement
+ *
+ * @var string
+ */
+ protected $_create_table_if = "IF NOT EXISTS (SELECT * FROM sysobjects WHERE ID = object_id(N'%s') AND OBJECTPROPERTY(id, N'IsUserTable') = 1)\nCREATE TABLE";
+
+ /**
+ * DROP TABLE IF statement
+ *
+ * @var string
+ */
+ protected $_drop_table_if = "IF EXISTS (SELECT * FROM sysobjects WHERE ID = object_id(N'%s') AND OBJECTPROPERTY(id, N'IsUserTable') = 1)\nDROP TABLE";
+
+ /**
+ * UNSIGNED support
+ *
+ * @var array
+ */
+ protected $_unsigned = array(
+ 'TINYINT' => 'SMALLINT',
+ 'SMALLINT' => 'INT',
+ 'INT' => 'BIGINT',
+ 'REAL' => 'FLOAT'
+ );
+
+ // --------------------------------------------------------------------
+
+ /**
+ * ALTER TABLE
+ *
+ * @param string $alter_type ALTER type
+ * @param string $table Table name
+ * @param mixed $field Column definition
+ * @return string|string[]
+ */
+ protected function _alter_table($alter_type, $table, $field)
+ {
+ if (in_array($alter_type, array('ADD', 'DROP'), TRUE))
+ {
+ return parent::_alter_table($alter_type, $table, $field);
+ }
+
+ $sql = 'ALTER TABLE '.$this->db->escape_identifiers($table).' ALTER COLUMN ';
+ $sqls = array();
+ for ($i = 0, $c = count($field); $i < $c; $i++)
+ {
+ $sqls[] = $sql.$this->_process_column($field[$i]);
+ }
+
+ return $sqls;
+ }
+
+ // --------------------------------------------------------------------
+
+ /**
+ * Field attribute TYPE
+ *
+ * Performs a data type mapping between different databases.
+ *
+ * @param array &$attributes
+ * @return void
+ */
+ protected function _attr_type(&$attributes)
+ {
+ if (isset($attributes['CONSTRAINT']) && strpos($attributes['TYPE'], 'INT') !== FALSE)
+ {
+ unset($attributes['CONSTRAINT']);
+ }
+
+ switch (strtoupper($attributes['TYPE']))
+ {
+ case 'MEDIUMINT':
+ $attributes['TYPE'] = 'INTEGER';
+ $attributes['UNSIGNED'] = FALSE;
+ return;
+ case 'INTEGER':
+ $attributes['TYPE'] = 'INT';
+ return;
+ default: return;
+ }
+ }
+
+ // --------------------------------------------------------------------
+
+ /**
+ * Field attribute AUTO_INCREMENT
+ *
+ * @param array &$attributes
+ * @param array &$field
+ * @return void
+ */
+ protected function _attr_auto_increment(&$attributes, &$field)
+ {
+ if ( ! empty($attributes['AUTO_INCREMENT']) && $attributes['AUTO_INCREMENT'] === TRUE && stripos($field['type'], 'int') !== FALSE)
+ {
+ $field['auto_increment'] = ' IDENTITY(1,1)';
+ }
+ }
+
+}
diff --git a/system/database/drivers/postgre/index.html b/system/database/drivers/postgre/index.html
index c942a79ce..bcb7cae34 100644
--- a/system/database/drivers/postgre/index.html
+++ b/system/database/drivers/postgre/index.html
@@ -1,4 +1,5 @@
-<html>
+<!DOCTYPE html>
+<html lang="en">
<head>
<title>403 Forbidden</title>
</head>
@@ -7,4 +8,4 @@
<p>Directory access is forbidden.</p>
</body>
-</html> \ No newline at end of file
+</html>
diff --git a/system/database/drivers/postgre/postgre_driver.php b/system/database/drivers/postgre/postgre_driver.php
index c9365fdb1..1cd473f2b 100644
--- a/system/database/drivers/postgre/postgre_driver.php
+++ b/system/database/drivers/postgre/postgre_driver.php
@@ -1,102 +1,171 @@
-<?php if ( ! defined('BASEPATH')) exit('No direct script access allowed');
+<?php
/**
* CodeIgniter
*
- * An open source application development framework for PHP 5.1.6 or newer
+ * An open source application development framework for PHP
*
- * @package CodeIgniter
- * @author ExpressionEngine Dev Team
- * @copyright Copyright (c) 2008 - 2014, EllisLab, Inc.
- * @license http://codeigniter.com/user_guide/license.html
- * @link http://codeigniter.com
- * @since Version 1.0
+ * This content is released under the MIT License (MIT)
+ *
+ * Copyright (c) 2019 - 2022, CodeIgniter Foundation
+ *
+ * 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.
+ *
+ * @package CodeIgniter
+ * @author EllisLab Dev Team
+ * @copyright Copyright (c) 2008 - 2014, EllisLab, Inc. (https://ellislab.com/)
+ * @copyright Copyright (c) 2014 - 2019, British Columbia Institute of Technology (https://bcit.ca/)
+ * @copyright Copyright (c) 2019 - 2022, CodeIgniter Foundation (https://codeigniter.com/)
+ * @license https://opensource.org/licenses/MIT MIT License
+ * @link https://codeigniter.com
+ * @since Version 1.3.0
* @filesource
*/
-
-// ------------------------------------------------------------------------
+defined('BASEPATH') OR exit('No direct script access allowed');
/**
* Postgre Database Adapter Class
*
* Note: _DB is an extender class that the app controller
- * creates dynamically based on whether the active record
+ * creates dynamically based on whether the query builder
* class is being used or not.
*
* @package CodeIgniter
* @subpackage Drivers
* @category Database
- * @author ExpressionEngine Dev Team
- * @link http://codeigniter.com/user_guide/database/
+ * @author EllisLab Dev Team
+ * @link https://codeigniter.com/userguide3/database/
*/
class CI_DB_postgre_driver extends CI_DB {
- var $dbdriver = 'postgre';
+ /**
+ * Database driver
+ *
+ * @var string
+ */
+ public $dbdriver = 'postgre';
- var $_escape_char = '"';
+ /**
+ * Database schema
+ *
+ * @var string
+ */
+ public $schema = 'public';
- // clause and character used for LIKE escape sequences
- var $_like_escape_str = " ESCAPE '%s' ";
- var $_like_escape_chr = '!';
+ // --------------------------------------------------------------------
/**
- * The syntax to count rows is slightly different across different
- * database engines, so this string appears in each driver and is
- * used for the count_all() and count_all_results() functions.
+ * ORDER BY random keyword
+ *
+ * @var array
*/
- var $_count_string = "SELECT COUNT(*) AS ";
- var $_random_keyword = ' RANDOM()'; // database specific random keyword
+ protected $_random_keyword = array('RANDOM()', 'RANDOM()');
+
+ // --------------------------------------------------------------------
/**
- * Connection String
+ * Build DSN
*
- * @access private
- * @return string
+ * @return void
*/
- function _connect_string()
+ protected function _build_dsn()
{
- $components = array(
- 'hostname' => 'host',
- 'port' => 'port',
- 'database' => 'dbname',
- 'username' => 'user',
- 'password' => 'password'
- );
-
- $connect_string = "";
- foreach ($components as $key => $val)
+ $this->dsn === '' OR $this->dsn = '';
+
+ if (strpos($this->hostname, '/') !== FALSE)
+ {
+ // If UNIX sockets are used, we shouldn't set a port
+ $this->port = '';
+ }
+
+ $this->hostname === '' OR $this->dsn = 'host='.$this->hostname.' ';
+
+ if ( ! empty($this->port) && ctype_digit($this->port))
{
- if (isset($this->$key) && $this->$key != '')
+ $this->dsn .= 'port='.$this->port.' ';
+ }
+
+ if ($this->username !== '')
+ {
+ $this->dsn .= 'user='.$this->username.' ';
+
+ /* An empty password is valid!
+ *
+ * $db['password'] = NULL must be done in order to ignore it.
+ */
+ $this->password === NULL OR $this->dsn .= "password='".$this->password."' ";
+ }
+
+ $this->database === '' OR $this->dsn .= 'dbname='.$this->database.' ';
+
+ /* We don't have these options as elements in our standard configuration
+ * array, but they might be set by parse_url() if the configuration was
+ * provided via string. Example:
+ *
+ * postgre://username:password@localhost:5432/database?connect_timeout=5&sslmode=1
+ */
+ foreach (array('connect_timeout', 'options', 'sslmode', 'service') as $key)
+ {
+ if (isset($this->$key) && is_string($this->$key) && $this->$key !== '')
{
- $connect_string .= " $val=".$this->$key;
+ $this->dsn .= $key."='".$this->$key."' ";
}
}
- return trim($connect_string);
+
+ $this->dsn = rtrim($this->dsn);
}
// --------------------------------------------------------------------
/**
- * Non-persistent database connection
+ * Database connection
*
- * @access private called by the base class
- * @return resource
+ * @param bool $persistent
+ * @return resource|object
*/
- function db_connect()
+ public function db_connect($persistent = FALSE)
{
- return @pg_connect($this->_connect_string());
- }
+ empty($this->dsn) && $this->_build_dsn();
+ $this->conn_id = ($persistent === TRUE)
+ ? pg_pconnect($this->dsn)
+ : pg_connect($this->dsn);
- // --------------------------------------------------------------------
+ if ($this->conn_id !== FALSE)
+ {
+ if ($persistent === TRUE
+ && pg_connection_status($this->conn_id) === PGSQL_CONNECTION_BAD
+ && pg_ping($this->conn_id) === FALSE
+ )
+ {
+ return FALSE;
+ }
- /**
- * Persistent database connection
- *
- * @access private called by the base class
- * @return resource
- */
- function db_pconnect()
- {
- return @pg_pconnect($this->_connect_string());
+ if (pg_set_client_encoding($this->conn_id, $this->char_set) !== 0)
+ {
+ log_message('error', "Database: Unable to set the configured connection charset ('{$this->char_set}').");
+ pg_close($this->conn_id);
+ return ($this->db->db_debug) ? $this->display_error('db_unable_to_set_charset', $this->char_set) : FALSE;
+ }
+
+ empty($this->schema) OR $this->simple_query('SET search_path TO '.$this->schema.',public');
+ }
+
+ return $this->conn_id;
}
// --------------------------------------------------------------------
@@ -107,10 +176,9 @@ class CI_DB_postgre_driver extends CI_DB {
* Keep / reestablish the db connection if no queries have been
* sent for a length of time exceeding the server's idle timeout
*
- * @access public
* @return void
*/
- function reconnect()
+ public function reconnect()
{
if (pg_ping($this->conn_id) === FALSE)
{
@@ -121,188 +189,134 @@ class CI_DB_postgre_driver extends CI_DB {
// --------------------------------------------------------------------
/**
- * Select the database
+ * Database version number
*
- * @access private called by the base class
- * @return resource
+ * @return string
*/
- function db_select()
+ public function version()
{
- // Not needed for Postgre so we'll return TRUE
- return TRUE;
- }
+ if (isset($this->data_cache['version']))
+ {
+ return $this->data_cache['version'];
+ }
- // --------------------------------------------------------------------
+ if ( ! $this->conn_id OR ($pg_version = pg_version($this->conn_id)) === FALSE)
+ {
+ return FALSE;
+ }
- /**
- * Set client character set
- *
- * @access public
- * @param string
- * @param string
- * @return resource
- */
- function db_set_charset($charset, $collation)
- {
- // @todo - add support if needed
- return TRUE;
+ /* If PHP was compiled with PostgreSQL lib versions earlier
+ * than 7.4, pg_version() won't return the server version
+ * and so we'll have to fall back to running a query in
+ * order to get it.
+ */
+ return (isset($pg_version['server']) && preg_match('#^(\d+\.\d+)#', $pg_version['server'], $match))
+ ? $this->data_cache['version'] = $match[1]
+ : parent::version();
}
// --------------------------------------------------------------------
/**
- * Version number query string
+ * Execute the query
*
- * @access public
- * @return string
+ * @param string $sql an SQL query
+ * @return resource|object
*/
- function _version()
+ protected function _execute($sql)
{
- $pg_version = pg_version($this->conn_id);
- return $pg_version;
+ return pg_query($this->conn_id, $sql);
}
// --------------------------------------------------------------------
/**
- * Execute the query
+ * Begin Transaction
*
- * @access private called by the base class
- * @param string an SQL query
- * @return resource
+ * @return bool
*/
- function _execute($sql)
+ protected function _trans_begin()
{
- $sql = $this->_prep_query($sql);
- return @pg_query($this->conn_id, $sql);
+ return (bool) pg_query($this->conn_id, 'BEGIN');
}
// --------------------------------------------------------------------
/**
- * Prep the query
- *
- * If needed, each database adapter can prep the query string
+ * Commit Transaction
*
- * @access private called by execute()
- * @param string an SQL query
- * @return string
+ * @return bool
*/
- function _prep_query($sql)
+ protected function _trans_commit()
{
- return $sql;
+ return (bool) pg_query($this->conn_id, 'COMMIT');
}
// --------------------------------------------------------------------
/**
- * Begin Transaction
+ * Rollback Transaction
*
- * @access public
* @return bool
*/
- function trans_begin($test_mode = FALSE)
+ protected function _trans_rollback()
{
- if ( ! $this->trans_enabled)
- {
- return TRUE;
- }
-
- // When transactions are nested we only begin/commit/rollback the outermost ones
- if ($this->_trans_depth > 0)
- {
- return TRUE;
- }
-
- // Reset the transaction failure flag.
- // If the $test_mode flag is set to TRUE transactions will be rolled back
- // even if the queries produce a successful result.
- $this->_trans_failure = ($test_mode === TRUE) ? TRUE : FALSE;
-
- return @pg_exec($this->conn_id, "begin");
+ return (bool) pg_query($this->conn_id, 'ROLLBACK');
}
// --------------------------------------------------------------------
/**
- * Commit Transaction
+ * Determines if a query is a "write" type.
*
- * @access public
+ * @param string An SQL query string
* @return bool
*/
- function trans_commit()
+ public function is_write_type($sql)
{
- if ( ! $this->trans_enabled)
- {
- return TRUE;
- }
-
- // When transactions are nested we only begin/commit/rollback the outermost ones
- if ($this->_trans_depth > 0)
+ if (preg_match('#^(INSERT|UPDATE).*RETURNING\s.+(\,\s?.+)*$#is', $sql))
{
- return TRUE;
+ return FALSE;
}
- return @pg_exec($this->conn_id, "commit");
+ return parent::is_write_type($sql);
}
// --------------------------------------------------------------------
/**
- * Rollback Transaction
+ * Platform-dependent string escape
*
- * @access public
- * @return bool
+ * @param string
+ * @return string
*/
- function trans_rollback()
+ protected function _escape_str($str)
{
- if ( ! $this->trans_enabled)
- {
- return TRUE;
- }
-
- // When transactions are nested we only begin/commit/rollback the outermost ones
- if ($this->_trans_depth > 0)
- {
- return TRUE;
- }
-
- return @pg_exec($this->conn_id, "rollback");
+ return pg_escape_string($this->conn_id, $str);
}
// --------------------------------------------------------------------
/**
- * Escape String
+ * "Smart" Escape String
*
- * @access public
- * @param string
- * @param bool whether or not the string will be used in a LIKE condition
- * @return string
+ * Escapes data based on type
+ *
+ * @param string $str
+ * @return mixed
*/
- function escape_str($str, $like = FALSE)
+ public function escape($str)
{
- if (is_array($str))
+ if (is_string($str) OR (is_object($str) && method_exists($str, '__toString')))
{
- foreach ($str as $key => $val)
- {
- $str[$key] = $this->escape_str($val, $like);
- }
-
- return $str;
+ return pg_escape_literal($this->conn_id, $str);
}
-
- $str = pg_escape_string($str);
-
- // escape LIKE condition wildcards
- if ($like === TRUE)
+ elseif (is_bool($str))
{
- $str = str_replace( array('%', '_', $this->_like_escape_chr),
- array($this->_like_escape_chr.'%', $this->_like_escape_chr.'_', $this->_like_escape_chr.$this->_like_escape_chr),
- $str);
+ return ($str) ? 'TRUE' : 'FALSE';
}
- return $str;
+ return parent::escape($str);
}
// --------------------------------------------------------------------
@@ -310,12 +324,11 @@ class CI_DB_postgre_driver extends CI_DB {
/**
* Affected Rows
*
- * @access public
- * @return integer
+ * @return int
*/
- function affected_rows()
+ public function affected_rows()
{
- return @pg_affected_rows($this->result_id);
+ return pg_affected_rows($this->result_id);
}
// --------------------------------------------------------------------
@@ -323,71 +336,44 @@ class CI_DB_postgre_driver extends CI_DB {
/**
* Insert ID
*
- * @access public
- * @return integer
+ * @return string
*/
- function insert_id()
+ public function insert_id()
{
- $v = $this->_version();
- $v = $v['server'];
+ $v = $this->version();
- $table = func_num_args() > 0 ? func_get_arg(0) : NULL;
- $column = func_num_args() > 1 ? func_get_arg(1) : NULL;
+ $table = (func_num_args() > 0) ? func_get_arg(0) : NULL;
+ $column = (func_num_args() > 1) ? func_get_arg(1) : NULL;
- if ($table == NULL && $v >= '8.1')
+ if ($table === NULL && $v >= '8.1')
{
- $sql='SELECT LASTVAL() as ins_id';
+ $sql = 'SELECT LASTVAL() AS ins_id';
}
- elseif ($table != NULL && $column != NULL && $v >= '8.0')
+ elseif ($table !== NULL)
{
- $sql = sprintf("SELECT pg_get_serial_sequence('%s','%s') as seq", $table, $column);
- $query = $this->query($sql);
- $row = $query->row();
- $sql = sprintf("SELECT CURRVAL('%s') as ins_id", $row->seq);
- }
- elseif ($table != NULL)
- {
- // seq_name passed in table parameter
- $sql = sprintf("SELECT CURRVAL('%s') as ins_id", $table);
+ if ($column !== NULL && $v >= '8.0')
+ {
+ $sql = 'SELECT pg_get_serial_sequence(\''.$table."', '".$column."') AS seq";
+ $query = $this->query($sql);
+ $query = $query->row();
+ $seq = $query->seq;
+ }
+ else
+ {
+ // seq_name passed in table parameter
+ $seq = $table;
+ }
+
+ $sql = 'SELECT CURRVAL(\''.$seq."') AS ins_id";
}
else
{
return pg_last_oid($this->result_id);
}
- $query = $this->query($sql);
- $row = $query->row();
- return $row->ins_id;
- }
-
- // --------------------------------------------------------------------
-
- /**
- * "Count All" query
- *
- * Generates a platform-specific query string that counts all records in
- * the specified database
- *
- * @access public
- * @param string
- * @return string
- */
- function count_all($table = '')
- {
- if ($table == '')
- {
- return 0;
- }
- $query = $this->query($this->_count_string . $this->_protect_identifiers('numrows') . " FROM " . $this->_protect_identifiers($table, TRUE, NULL, FALSE));
-
- if ($query->num_rows() == 0)
- {
- return 0;
- }
-
- $row = $query->row();
- $this->_reset_select();
- return (int) $row->numrows;
+ $query = $this->query($sql);
+ $query = $query->row();
+ return (int) $query->ins_id;
}
// --------------------------------------------------------------------
@@ -397,17 +383,18 @@ class CI_DB_postgre_driver extends CI_DB {
*
* Generates a platform-specific query string so that the table names can be fetched
*
- * @access private
- * @param boolean
+ * @param bool $prefix_limit
* @return string
*/
- function _list_tables($prefix_limit = FALSE)
+ protected function _list_tables($prefix_limit = FALSE)
{
- $sql = "SELECT table_name FROM information_schema.tables WHERE table_schema = 'public'";
+ $sql = 'SELECT "table_name" FROM "information_schema"."tables" WHERE "table_schema" = \''.$this->schema."'";
- if ($prefix_limit !== FALSE AND $this->dbprefix != '')
+ if ($prefix_limit !== FALSE && $this->dbprefix !== '')
{
- $sql .= " AND table_name LIKE '".$this->escape_like_str($this->dbprefix)."%' ".sprintf($this->_like_escape_str, $this->_like_escape_chr);
+ return $sql.' AND "table_name" LIKE \''
+ .$this->escape_like_str($this->dbprefix)."%' "
+ .sprintf($this->_like_escape_str, $this->_like_escape_chr);
}
return $sql;
@@ -416,212 +403,160 @@ class CI_DB_postgre_driver extends CI_DB {
// --------------------------------------------------------------------
/**
- * Show column query
+ * List column query
*
* Generates a platform-specific query string so that the column names can be fetched
*
- * @access public
- * @param string the table name
+ * @param string $table
* @return string
*/
- function _list_columns($table = '')
+ protected function _list_columns($table = '')
{
- return "SELECT column_name FROM information_schema.columns WHERE table_name ='".$table."'";
+ return 'SELECT "column_name"
+ FROM "information_schema"."columns"
+ WHERE "table_schema" = \''.$this->schema.'\' AND LOWER("table_name") = '.$this->escape(strtolower($table));
}
// --------------------------------------------------------------------
/**
- * Field data query
- *
- * Generates a platform-specific query so that the column data can be retrieved
+ * Returns an object with field data
*
- * @access public
- * @param string the table name
- * @return object
+ * @param string $table
+ * @return array
*/
- function _field_data($table)
+ public function field_data($table)
{
- return "SELECT * FROM ".$table." LIMIT 1";
- }
+ $sql = 'SELECT "column_name", "data_type", "character_maximum_length", "numeric_precision", "column_default"
+ FROM "information_schema"."columns"
+ WHERE "table_schema" = \''.$this->schema.'\' AND LOWER("table_name") = '.$this->escape(strtolower($table));
- // --------------------------------------------------------------------
+ if (($query = $this->query($sql)) === FALSE)
+ {
+ return FALSE;
+ }
+ $query = $query->result_object();
- /**
- * The error message string
- *
- * @access private
- * @return string
- */
- function _error_message()
- {
- return pg_last_error($this->conn_id);
+ $retval = array();
+ for ($i = 0, $c = count($query); $i < $c; $i++)
+ {
+ $retval[$i] = new stdClass();
+ $retval[$i]->name = $query[$i]->column_name;
+ $retval[$i]->type = $query[$i]->data_type;
+ $retval[$i]->max_length = ($query[$i]->character_maximum_length > 0) ? $query[$i]->character_maximum_length : $query[$i]->numeric_precision;
+ $retval[$i]->default = $query[$i]->column_default;
+ }
+
+ return $retval;
}
// --------------------------------------------------------------------
/**
- * The error message number
+ * Error
+ *
+ * Returns an array containing code and message of the last
+ * database error that has occurred.
*
- * @access private
- * @return integer
+ * @return array
*/
- function _error_number()
+ public function error()
{
- return '';
+ return array('code' => '', 'message' => pg_last_error($this->conn_id));
}
// --------------------------------------------------------------------
/**
- * Escape the SQL Identifiers
- *
- * This function escapes column and table names
+ * ORDER BY
*
- * @access private
- * @param string
- * @return string
+ * @param string $orderby
+ * @param string $direction ASC, DESC or RANDOM
+ * @param bool $escape
+ * @return object
*/
- function _escape_identifiers($item)
+ public function order_by($orderby, $direction = '', $escape = NULL)
{
- if ($this->_escape_char == '')
+ $direction = strtoupper(trim($direction));
+ if ($direction === 'RANDOM')
{
- return $item;
- }
-
- foreach ($this->_reserved_identifiers as $id)
- {
- if (strpos($item, '.'.$id) !== FALSE)
+ if ( ! is_float($orderby) && ctype_digit((string) $orderby))
{
- $str = $this->_escape_char. str_replace('.', $this->_escape_char.'.', $item);
+ $orderby = ($orderby > 1)
+ ? (float) '0.'.$orderby
+ : (float) $orderby;
+ }
- // remove duplicates if the user already included the escape
- return preg_replace('/['.$this->_escape_char.']+/', $this->_escape_char, $str);
+ if (is_float($orderby))
+ {
+ $this->simple_query('SET SEED '.$orderby);
}
- }
- if (strpos($item, '.') !== FALSE)
- {
- $str = $this->_escape_char.str_replace('.', $this->_escape_char.'.'.$this->_escape_char, $item).$this->_escape_char;
- }
- else
- {
- $str = $this->_escape_char.$item.$this->_escape_char;
+ $orderby = $this->_random_keyword[0];
+ $direction = '';
+ $escape = FALSE;
}
- // remove duplicates if the user already included the escape
- return preg_replace('/['.$this->_escape_char.']+/', $this->_escape_char, $str);
+ return parent::order_by($orderby, $direction, $escape);
}
// --------------------------------------------------------------------
/**
- * From Tables
- *
- * This function implicitly groups FROM tables so there is no confusion
- * about operator precedence in harmony with SQL standards
- *
- * @access public
- * @param type
- * @return type
- */
- function _from_tables($tables)
- {
- if ( ! is_array($tables))
- {
- $tables = array($tables);
- }
-
- return implode(', ', $tables);
- }
-
- // --------------------------------------------------------------------
-
- /**
- * Insert statement
+ * Update statement
*
- * Generates a platform-specific insert string from the supplied data
+ * Generates a platform-specific update string from the supplied data
*
- * @access public
- * @param string the table name
- * @param array the insert keys
- * @param array the insert values
+ * @param string $table
+ * @param array $values
* @return string
*/
- function _insert($table, $keys, $values)
+ protected function _update($table, $values)
{
- return "INSERT INTO ".$table." (".implode(', ', $keys).") VALUES (".implode(', ', $values).")";
+ $this->qb_limit = FALSE;
+ $this->qb_orderby = array();
+ return parent::_update($table, $values);
}
// --------------------------------------------------------------------
/**
- * Insert_batch statement
+ * Update_Batch statement
*
- * Generates a platform-specific insert string from the supplied data
+ * Generates a platform-specific batch update string from the supplied data
*
- * @access public
- * @param string the table name
- * @param array the insert keys
- * @param array the insert values
- * @return string
- */
- function _insert_batch($table, $keys, $values)
- {
- return "INSERT INTO ".$table." (".implode(', ', $keys).") VALUES ".implode(', ', $values);
- }
-
- // --------------------------------------------------------------------
-
- /**
- * Update statement
- *
- * Generates a platform-specific update string from the supplied data
- *
- * @access public
- * @param string the table name
- * @param array the update data
- * @param array the where clause
- * @param array the orderby clause
- * @param array the limit clause
+ * @param string $table Table name
+ * @param array $values Update data
+ * @param string $index WHERE key
* @return string
*/
- function _update($table, $values, $where, $orderby = array(), $limit = FALSE)
+ protected function _update_batch($table, $values, $index)
{
+ $ids = array();
foreach ($values as $key => $val)
{
- $valstr[] = $key." = ".$val;
- }
-
- $limit = ( ! $limit) ? '' : ' LIMIT '.$limit;
-
- $orderby = (count($orderby) >= 1)?' ORDER BY '.implode(", ", $orderby):'';
+ $ids[] = $val[$index]['value'];
- $sql = "UPDATE ".$table." SET ".implode(', ', $valstr);
-
- $sql .= ($where != '' AND count($where) >=1) ? " WHERE ".implode(" ", $where) : '';
-
- $sql .= $orderby.$limit;
+ foreach (array_keys($val) as $field)
+ {
+ if ($field !== $index)
+ {
+ $final[$val[$field]['field']][] = 'WHEN '.$val[$index]['value'].' THEN '.$val[$field]['value'];
+ }
+ }
+ }
- return $sql;
- }
+ $cases = '';
+ foreach ($final as $k => $v)
+ {
+ $cases .= $k.' = (CASE '.$val[$index]['field']."\n"
+ .implode("\n", $v)."\n"
+ .'ELSE '.$k.' END), ';
+ }
- // --------------------------------------------------------------------
+ $this->where($val[$index]['field'].' IN('.implode(',', $ids).')', NULL, FALSE);
- /**
- * Truncate statement
- *
- * Generates a platform-specific truncate string from the supplied data
- * If the database does not support the truncate() command
- * This function maps to "DELETE FROM table"
- *
- * @access public
- * @param string the table name
- * @return string
- */
- function _truncate($table)
- {
- return "TRUNCATE ".$table;
+ return 'UPDATE '.$table.' SET '.substr($cases, 0, -2).$this->_compile_wh('qb_where');
}
// --------------------------------------------------------------------
@@ -631,55 +566,28 @@ class CI_DB_postgre_driver extends CI_DB {
*
* Generates a platform-specific delete string from the supplied data
*
- * @access public
- * @param string the table name
- * @param array the where clause
- * @param string the limit clause
+ * @param string $table
* @return string
*/
- function _delete($table, $where = array(), $like = array(), $limit = FALSE)
+ protected function _delete($table)
{
- $conditions = '';
-
- if (count($where) > 0 OR count($like) > 0)
- {
- $conditions = "\nWHERE ";
- $conditions .= implode("\n", $this->ar_where);
-
- if (count($where) > 0 && count($like) > 0)
- {
- $conditions .= " AND ";
- }
- $conditions .= implode("\n", $like);
- }
-
- $limit = ( ! $limit) ? '' : ' LIMIT '.$limit;
-
- return "DELETE FROM ".$table.$conditions.$limit;
+ $this->qb_limit = FALSE;
+ return parent::_delete($table);
}
// --------------------------------------------------------------------
+
/**
- * Limit string
+ * LIMIT
*
* Generates a platform-specific LIMIT clause
*
- * @access public
- * @param string the sql query string
- * @param integer the number of rows to limit the query to
- * @param integer the offset value
+ * @param string $sql SQL Query
* @return string
*/
- function _limit($sql, $limit, $offset)
+ protected function _limit($sql)
{
- $sql .= "LIMIT ".$limit;
-
- if ($offset > 0)
- {
- $sql .= " OFFSET ".$offset;
- }
-
- return $sql;
+ return $sql.' LIMIT '.$this->qb_limit.($this->qb_offset ? ' OFFSET '.$this->qb_offset : '');
}
// --------------------------------------------------------------------
@@ -687,18 +595,11 @@ class CI_DB_postgre_driver extends CI_DB {
/**
* Close DB Connection
*
- * @access public
- * @param resource
* @return void
*/
- function _close($conn_id)
+ protected function _close()
{
- @pg_close($conn_id);
+ pg_close($this->conn_id);
}
-
}
-
-
-/* End of file postgre_driver.php */
-/* Location: ./system/database/drivers/postgre/postgre_driver.php */ \ No newline at end of file
diff --git a/system/database/drivers/postgre/postgre_forge.php b/system/database/drivers/postgre/postgre_forge.php
index d9997a433..b3db836ea 100644
--- a/system/database/drivers/postgre/postgre_forge.php
+++ b/system/database/drivers/postgre/postgre_forge.php
@@ -1,299 +1,207 @@
-<?php if ( ! defined('BASEPATH')) exit('No direct script access allowed');
+<?php
/**
* CodeIgniter
*
- * An open source application development framework for PHP 5.1.6 or newer
+ * An open source application development framework for PHP
*
- * @package CodeIgniter
- * @author ExpressionEngine Dev Team
- * @copyright Copyright (c) 2008 - 2014, EllisLab, Inc.
- * @license http://codeigniter.com/user_guide/license.html
- * @link http://codeigniter.com
- * @since Version 1.0
+ * This content is released under the MIT License (MIT)
+ *
+ * Copyright (c) 2019 - 2022, CodeIgniter Foundation
+ *
+ * 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.
+ *
+ * @package CodeIgniter
+ * @author EllisLab Dev Team
+ * @copyright Copyright (c) 2008 - 2014, EllisLab, Inc. (https://ellislab.com/)
+ * @copyright Copyright (c) 2014 - 2019, British Columbia Institute of Technology (https://bcit.ca/)
+ * @copyright Copyright (c) 2019 - 2022, CodeIgniter Foundation (https://codeigniter.com/)
+ * @license https://opensource.org/licenses/MIT MIT License
+ * @link https://codeigniter.com
+ * @since Version 1.3.0
* @filesource
*/
-
-// ------------------------------------------------------------------------
+defined('BASEPATH') OR exit('No direct script access allowed');
/**
* Postgre Forge Class
*
+ * @package CodeIgniter
+ * @subpackage Drivers
* @category Database
- * @author ExpressionEngine Dev Team
- * @link http://codeigniter.com/user_guide/database/
+ * @author EllisLab Dev Team
+ * @link https://codeigniter.com/userguide3/database/
*/
+#[\AllowDynamicProperties]
class CI_DB_postgre_forge extends CI_DB_forge {
/**
- * Create database
+ * UNSIGNED support
*
- * @access private
- * @param string the database name
- * @return bool
+ * @var array
*/
- function _create_database($name)
- {
- return "CREATE DATABASE ".$name;
- }
+ protected $_unsigned = array(
+ 'INT2' => 'INTEGER',
+ 'SMALLINT' => 'INTEGER',
+ 'INT' => 'BIGINT',
+ 'INT4' => 'BIGINT',
+ 'INTEGER' => 'BIGINT',
+ 'INT8' => 'NUMERIC',
+ 'BIGINT' => 'NUMERIC',
+ 'REAL' => 'DOUBLE PRECISION',
+ 'FLOAT' => 'DOUBLE PRECISION'
+ );
+
+ /**
+ * NULL value representation in CREATE/ALTER TABLE statements
+ *
+ * @var string
+ */
+ protected $_null = 'NULL';
// --------------------------------------------------------------------
/**
- * Drop database
+ * Class constructor
*
- * @access private
- * @param string the database name
- * @return bool
+ * @param object &$db Database object
+ * @return void
*/
- function _drop_database($name)
+ public function __construct(&$db)
{
- return "DROP DATABASE ".$name;
+ parent::__construct($db);
+
+ if (version_compare($this->db->version(), '9.0', '>'))
+ {
+ $this->create_table_if = 'CREATE TABLE IF NOT EXISTS';
+ }
}
// --------------------------------------------------------------------
/**
- * Create Table
+ * ALTER TABLE
*
- * @access private
- * @param string the table name
- * @param array the fields
- * @param mixed primary key(s)
- * @param mixed key(s)
- * @param boolean should 'IF NOT EXISTS' be added to the SQL
- * @return bool
+ * @param string $alter_type ALTER type
+ * @param string $table Table name
+ * @param mixed $field Column definition
+ * @return string|string[]
*/
- function _create_table($table, $fields, $primary_keys, $keys, $if_not_exists)
+ protected function _alter_table($alter_type, $table, $field)
{
- $sql = 'CREATE TABLE ';
-
- if ($if_not_exists === TRUE)
+ if (in_array($alter_type, array('DROP', 'ADD'), TRUE))
{
- if ($this->db->table_exists($table))
- {
- return "SELECT * FROM $table"; // Needs to return innocous but valid SQL statement
- }
+ return parent::_alter_table($alter_type, $table, $field);
}
- $sql .= $this->db->_escape_identifiers($table)." (";
- $current_field_count = 0;
-
- foreach ($fields as $field=>$attributes)
+ $sql = 'ALTER TABLE '.$this->db->escape_identifiers($table);
+ $sqls = array();
+ for ($i = 0, $c = count($field); $i < $c; $i++)
{
- // Numeric field names aren't allowed in databases, so if the key is
- // numeric, we know it was assigned by PHP and the developer manually
- // entered the field information, so we'll simply add it to the list
- if (is_numeric($field))
+ if ($field[$i]['_literal'] !== FALSE)
{
- $sql .= "\n\t$attributes";
+ return FALSE;
}
- else
- {
- $attributes = array_change_key_case($attributes, CASE_UPPER);
-
- $sql .= "\n\t".$this->db->_protect_identifiers($field);
-
- $is_unsigned = (array_key_exists('UNSIGNED', $attributes) && $attributes['UNSIGNED'] === TRUE);
- // Convert datatypes to be PostgreSQL-compatible
- switch (strtoupper($attributes['TYPE']))
- {
- case 'TINYINT':
- $attributes['TYPE'] = 'SMALLINT';
- break;
- case 'SMALLINT':
- $attributes['TYPE'] = ($is_unsigned) ? 'INTEGER' : 'SMALLINT';
- break;
- case 'MEDIUMINT':
- $attributes['TYPE'] = 'INTEGER';
- break;
- case 'INT':
- $attributes['TYPE'] = ($is_unsigned) ? 'BIGINT' : 'INTEGER';
- break;
- case 'BIGINT':
- $attributes['TYPE'] = ($is_unsigned) ? 'NUMERIC' : 'BIGINT';
- break;
- case 'DOUBLE':
- $attributes['TYPE'] = 'DOUBLE PRECISION';
- break;
- case 'DATETIME':
- $attributes['TYPE'] = 'TIMESTAMP';
- break;
- case 'LONGTEXT':
- $attributes['TYPE'] = 'TEXT';
- break;
- case 'BLOB':
- $attributes['TYPE'] = 'BYTEA';
- break;
- }
-
- // If this is an auto-incrementing primary key, use the serial data type instead
- if (in_array($field, $primary_keys) && array_key_exists('AUTO_INCREMENT', $attributes)
- && $attributes['AUTO_INCREMENT'] === TRUE)
- {
- $sql .= ' SERIAL';
- }
- else
- {
- $sql .= ' '.$attributes['TYPE'];
- }
-
- // Modified to prevent constraints with integer data types
- if (array_key_exists('CONSTRAINT', $attributes) && strpos($attributes['TYPE'], 'INT') === false)
- {
- $sql .= '('.$attributes['CONSTRAINT'].')';
- }
-
- if (array_key_exists('DEFAULT', $attributes))
- {
- $sql .= ' DEFAULT \''.$attributes['DEFAULT'].'\'';
- }
-
- if (array_key_exists('NULL', $attributes) && $attributes['NULL'] === TRUE)
- {
- $sql .= ' NULL';
- }
- else
- {
- $sql .= ' NOT NULL';
- }
-
- // Added new attribute to create unqite fields. Also works with MySQL
- if (array_key_exists('UNIQUE', $attributes) && $attributes['UNIQUE'] === TRUE)
- {
- $sql .= ' UNIQUE';
- }
+ if (version_compare($this->db->version(), '8', '>=') && isset($field[$i]['type']))
+ {
+ $sqls[] = $sql.' ALTER COLUMN '.$this->db->escape_identifiers($field[$i]['name'])
+ .' TYPE '.$field[$i]['type'].$field[$i]['length'];
}
- // don't add a comma on the end of the last field
- if (++$current_field_count < count($fields))
+ if ( ! empty($field[$i]['default']))
{
- $sql .= ',';
+ $sqls[] = $sql.' ALTER COLUMN '.$this->db->escape_identifiers($field[$i]['name'])
+ .' SET '.$field[$i]['default'];
}
- }
- if (count($primary_keys) > 0)
- {
- // Something seems to break when passing an array to _protect_identifiers()
- foreach ($primary_keys as $index => $key)
+ if (isset($field[$i]['null']))
{
- $primary_keys[$index] = $this->db->_protect_identifiers($key);
+ $sqls[] = $sql.' ALTER COLUMN '.$this->db->escape_identifiers($field[$i]['name'])
+ .(trim($field[$i]['null']) === $this->_null ? ' DROP NOT NULL' : ' SET NOT NULL');
}
- $sql .= ",\n\tPRIMARY KEY (" . implode(', ', $primary_keys) . ")";
- }
-
- $sql .= "\n);";
-
- if (is_array($keys) && count($keys) > 0)
- {
- foreach ($keys as $key)
+ if ( ! empty($field[$i]['new_name']))
{
- if (is_array($key))
- {
- $key = $this->db->_protect_identifiers($key);
- }
- else
- {
- $key = array($this->db->_protect_identifiers($key));
- }
+ $sqls[] = $sql.' RENAME COLUMN '.$this->db->escape_identifiers($field[$i]['name'])
+ .' TO '.$this->db->escape_identifiers($field[$i]['new_name']);
+ }
- foreach ($key as $field)
- {
- $sql .= "CREATE INDEX " . $table . "_" . str_replace(array('"', "'"), '', $field) . "_index ON $table ($field); ";
- }
+ if ( ! empty($field[$i]['comment']))
+ {
+ $sqls[] = 'COMMENT ON COLUMN '
+ .$this->db->escape_identifiers($table).'.'.$this->db->escape_identifiers($field[$i]['name'])
+ .' IS '.$field[$i]['comment'];
}
}
- return $sql;
- }
-
- // --------------------------------------------------------------------
-
- /**
- * Drop Table
- *
- * @access private
- * @return bool
- */
- function _drop_table($table)
- {
- return "DROP TABLE IF EXISTS ".$this->db->_escape_identifiers($table)." CASCADE";
+ return $sqls;
}
// --------------------------------------------------------------------
/**
- * Alter table query
+ * Field attribute TYPE
*
- * Generates a platform-specific query so that a table can be altered
- * Called by add_column(), drop_column(), and column_alter(),
+ * Performs a data type mapping between different databases.
*
- * @access private
- * @param string the ALTER type (ADD, DROP, CHANGE)
- * @param string the column name
- * @param string the table name
- * @param string the column definition
- * @param string the default value
- * @param boolean should 'NOT NULL' be added
- * @param string the field after which we should add the new field
- * @return object
+ * @param array &$attributes
+ * @return void
*/
- function _alter_table($alter_type, $table, $column_name, $column_definition = '', $default_value = '', $null = '', $after_field = '')
+ protected function _attr_type(&$attributes)
{
- $sql = 'ALTER TABLE '.$this->db->_protect_identifiers($table)." $alter_type ".$this->db->_protect_identifiers($column_name);
-
- // DROP has everything it needs now.
- if ($alter_type == 'DROP')
+ // Reset field lengths for data types that don't support it
+ if (isset($attributes['CONSTRAINT']) && stripos($attributes['TYPE'], 'int') !== FALSE)
{
- return $sql;
+ $attributes['CONSTRAINT'] = NULL;
}
- $sql .= " $column_definition";
-
- if ($default_value != '')
+ switch (strtoupper($attributes['TYPE']))
{
- $sql .= " DEFAULT \"$default_value\"";
+ case 'TINYINT':
+ $attributes['TYPE'] = 'SMALLINT';
+ $attributes['UNSIGNED'] = FALSE;
+ return;
+ case 'MEDIUMINT':
+ $attributes['TYPE'] = 'INTEGER';
+ $attributes['UNSIGNED'] = FALSE;
+ return;
+ default: return;
}
-
- if ($null === NULL)
- {
- $sql .= ' NULL';
- }
- else
- {
- $sql .= ' NOT NULL';
- }
-
- if ($after_field != '')
- {
- $sql .= ' AFTER ' . $this->db->_protect_identifiers($after_field);
- }
-
- return $sql;
-
}
// --------------------------------------------------------------------
/**
- * Rename a table
+ * Field attribute AUTO_INCREMENT
*
- * Generates a platform-specific query so that a table can be renamed
- *
- * @access private
- * @param string the old table name
- * @param string the new table name
- * @return string
+ * @param array &$attributes
+ * @param array &$field
+ * @return void
*/
- function _rename_table($table_name, $new_table_name)
+ protected function _attr_auto_increment(&$attributes, &$field)
{
- $sql = 'ALTER TABLE '.$this->db->_protect_identifiers($table_name)." RENAME TO ".$this->db->_protect_identifiers($new_table_name);
- return $sql;
+ if ( ! empty($attributes['AUTO_INCREMENT']) && $attributes['AUTO_INCREMENT'] === TRUE)
+ {
+ $field['type'] = ($field['type'] === 'NUMERIC')
+ ? 'BIGSERIAL'
+ : 'SERIAL';
+ }
}
-
}
-
-/* End of file postgre_forge.php */
-/* Location: ./system/database/drivers/postgre/postgre_forge.php */ \ No newline at end of file
diff --git a/system/database/drivers/postgre/postgre_result.php b/system/database/drivers/postgre/postgre_result.php
index 8655f7aee..5e4145eaf 100644
--- a/system/database/drivers/postgre/postgre_result.php
+++ b/system/database/drivers/postgre/postgre_result.php
@@ -1,40 +1,66 @@
-<?php if ( ! defined('BASEPATH')) exit('No direct script access allowed');
+<?php
/**
* CodeIgniter
*
- * An open source application development framework for PHP 5.1.6 or newer
+ * An open source application development framework for PHP
*
- * @package CodeIgniter
- * @author ExpressionEngine Dev Team
- * @copyright Copyright (c) 2008 - 2014, EllisLab, Inc.
- * @license http://codeigniter.com/user_guide/license.html
- * @link http://codeigniter.com
- * @since Version 1.0
+ * This content is released under the MIT License (MIT)
+ *
+ * Copyright (c) 2019 - 2022, CodeIgniter Foundation
+ *
+ * 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.
+ *
+ * @package CodeIgniter
+ * @author EllisLab Dev Team
+ * @copyright Copyright (c) 2008 - 2014, EllisLab, Inc. (https://ellislab.com/)
+ * @copyright Copyright (c) 2014 - 2019, British Columbia Institute of Technology (https://bcit.ca/)
+ * @copyright Copyright (c) 2019 - 2022, CodeIgniter Foundation (https://codeigniter.com/)
+ * @license https://opensource.org/licenses/MIT MIT License
+ * @link https://codeigniter.com
+ * @since Version 1.3.0
* @filesource
*/
-
-// ------------------------------------------------------------------------
+defined('BASEPATH') OR exit('No direct script access allowed');
/**
* Postgres Result Class
*
* This class extends the parent result class: CI_DB_result
*
+ * @package CodeIgniter
+ * @subpackage Drivers
* @category Database
- * @author ExpressionEngine Dev Team
- * @link http://codeigniter.com/user_guide/database/
+ * @author EllisLab Dev Team
+ * @link https://codeigniter.com/userguide3/database/
*/
class CI_DB_postgre_result extends CI_DB_result {
/**
* Number of rows in the result set
*
- * @access public
- * @return integer
+ * @return int
*/
- function num_rows()
+ public function num_rows()
{
- return @pg_num_rows($this->result_id);
+ return is_int($this->num_rows)
+ ? $this->num_rows
+ : $this->num_rows = pg_num_rows($this->result_id);
}
// --------------------------------------------------------------------
@@ -42,12 +68,11 @@ class CI_DB_postgre_result extends CI_DB_result {
/**
* Number of fields in the result set
*
- * @access public
- * @return integer
+ * @return int
*/
- function num_fields()
+ public function num_fields()
{
- return @pg_num_fields($this->result_id);
+ return pg_num_fields($this->result_id);
}
// --------------------------------------------------------------------
@@ -57,13 +82,12 @@ class CI_DB_postgre_result extends CI_DB_result {
*
* Generates an array of column names
*
- * @access public
* @return array
*/
- function list_fields()
+ public function list_fields()
{
$field_names = array();
- for ($i = 0; $i < $this->num_fields(); $i++)
+ for ($i = 0, $c = $this->num_fields(); $i < $c; $i++)
{
$field_names[] = pg_field_name($this->result_id, $i);
}
@@ -78,22 +102,17 @@ class CI_DB_postgre_result extends CI_DB_result {
*
* Generates an array of objects containing field meta-data
*
- * @access public
* @return array
*/
- function field_data()
+ public function field_data()
{
$retval = array();
- for ($i = 0; $i < $this->num_fields(); $i++)
+ for ($i = 0, $c = $this->num_fields(); $i < $c; $i++)
{
- $F = new stdClass();
- $F->name = pg_field_name($this->result_id, $i);
- $F->type = pg_field_type($this->result_id, $i);
- $F->max_length = pg_field_size($this->result_id, $i);
- $F->primary_key = 0;
- $F->default = '';
-
- $retval[] = $F;
+ $retval[$i] = new stdClass();
+ $retval[$i]->name = pg_field_name($this->result_id, $i);
+ $retval[$i]->type = pg_field_type($this->result_id, $i);
+ $retval[$i]->max_length = pg_field_size($this->result_id, $i);
}
return $retval;
@@ -104,11 +123,11 @@ class CI_DB_postgre_result extends CI_DB_result {
/**
* Free the result
*
- * @return null
+ * @return void
*/
- function free_result()
+ public function free_result()
{
- if (is_resource($this->result_id))
+ if ($this->result_id !== FALSE)
{
pg_free_result($this->result_id);
$this->result_id = FALSE;
@@ -120,14 +139,14 @@ class CI_DB_postgre_result extends CI_DB_result {
/**
* Data Seek
*
- * Moves the internal pointer to the desired offset. We call
+ * Moves the internal pointer to the desired offset. We call
* this internally before fetching results to make sure the
- * result set starts at zero
+ * result set starts at zero.
*
- * @access private
- * @return array
+ * @param int $n
+ * @return bool
*/
- function _data_seek($n = 0)
+ public function data_seek($n = 0)
{
return pg_result_seek($this->result_id, $n);
}
@@ -139,10 +158,9 @@ class CI_DB_postgre_result extends CI_DB_result {
*
* Returns the result set as an array
*
- * @access private
* @return array
*/
- function _fetch_assoc()
+ protected function _fetch_assoc()
{
return pg_fetch_assoc($this->result_id);
}
@@ -154,16 +172,12 @@ class CI_DB_postgre_result extends CI_DB_result {
*
* Returns the result set as an object
*
- * @access private
+ * @param string $class_name
* @return object
*/
- function _fetch_object()
+ protected function _fetch_object($class_name = 'stdClass')
{
- return pg_fetch_object($this->result_id);
+ return pg_fetch_object($this->result_id, NULL, $class_name);
}
}
-
-
-/* End of file postgre_result.php */
-/* Location: ./system/database/drivers/postgre/postgre_result.php */ \ No newline at end of file
diff --git a/system/database/drivers/postgre/postgre_utility.php b/system/database/drivers/postgre/postgre_utility.php
index d7af1a7ef..c8356d500 100644
--- a/system/database/drivers/postgre/postgre_utility.php
+++ b/system/database/drivers/postgre/postgre_utility.php
@@ -1,88 +1,79 @@
-<?php if ( ! defined('BASEPATH')) exit('No direct script access allowed');
+<?php
/**
* CodeIgniter
*
- * An open source application development framework for PHP 5.1.6 or newer
+ * An open source application development framework for PHP
*
- * @package CodeIgniter
- * @author ExpressionEngine Dev Team
- * @copyright Copyright (c) 2008 - 2014, EllisLab, Inc.
- * @license http://codeigniter.com/user_guide/license.html
- * @link http://codeigniter.com
- * @since Version 1.0
+ * This content is released under the MIT License (MIT)
+ *
+ * Copyright (c) 2019 - 2022, CodeIgniter Foundation
+ *
+ * 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.
+ *
+ * @package CodeIgniter
+ * @author EllisLab Dev Team
+ * @copyright Copyright (c) 2008 - 2014, EllisLab, Inc. (https://ellislab.com/)
+ * @copyright Copyright (c) 2014 - 2019, British Columbia Institute of Technology (https://bcit.ca/)
+ * @copyright Copyright (c) 2019 - 2022, CodeIgniter Foundation (https://codeigniter.com/)
+ * @license https://opensource.org/licenses/MIT MIT License
+ * @link https://codeigniter.com
+ * @since Version 1.3.0
* @filesource
*/
-
-// ------------------------------------------------------------------------
+defined('BASEPATH') OR exit('No direct script access allowed');
/**
* Postgre Utility Class
*
+ * @package CodeIgniter
+ * @subpackage Drivers
* @category Database
- * @author ExpressionEngine Dev Team
- * @link http://codeigniter.com/user_guide/database/
+ * @author EllisLab Dev Team
+ * @link https://codeigniter.com/userguide3/database/
*/
class CI_DB_postgre_utility extends CI_DB_utility {
/**
- * List databases
- *
- * @access private
- * @return bool
- */
- function _list_databases()
- {
- return "SELECT datname FROM pg_database";
- }
-
- // --------------------------------------------------------------------
-
- /**
- * Optimize table query
- *
- * Is table optimization supported in Postgre?
+ * List databases statement
*
- * @access private
- * @param string the table name
- * @return object
+ * @var string
*/
- function _optimize_table($table)
- {
- return FALSE;
- }
-
- // --------------------------------------------------------------------
+ protected $_list_databases = 'SELECT datname FROM pg_database';
/**
- * Repair table query
- *
- * Are table repairs supported in Postgre?
+ * OPTIMIZE TABLE statement
*
- * @access private
- * @param string the table name
- * @return object
+ * @var string
*/
- function _repair_table($table)
- {
- return FALSE;
- }
+ protected $_optimize_table = 'REINDEX TABLE %s';
// --------------------------------------------------------------------
/**
- * Postgre Export
+ * Export
*
- * @access private
- * @param array Preferences
+ * @param array $params Preferences
* @return mixed
*/
- function _backup($params = array())
+ protected function _backup($params = array())
{
// Currently unsupported
- return $this->db->display_error('db_unsuported_feature');
+ return $this->db->display_error('db_unsupported_feature');
}
}
-
-
-/* End of file postgre_utility.php */
-/* Location: ./system/database/drivers/postgre/postgre_utility.php */ \ No newline at end of file
diff --git a/system/database/drivers/sqlite/sqlite_driver.php b/system/database/drivers/sqlite/sqlite_driver.php
deleted file mode 100644
index 05ea6dd93..000000000
--- a/system/database/drivers/sqlite/sqlite_driver.php
+++ /dev/null
@@ -1,658 +0,0 @@
-<?php if ( ! defined('BASEPATH')) exit('No direct script access allowed');
-/**
- * CodeIgniter
- *
- * An open source application development framework for PHP 5.1.6 or newer
- *
- * @package CodeIgniter
- * @author ExpressionEngine Dev Team
- * @copyright Copyright (c) 2008 - 2014, EllisLab, Inc.
- * @license http://codeigniter.com/user_guide/license.html
- * @link http://codeigniter.com
- * @since Version 1.0
- * @filesource
- */
-
-// ------------------------------------------------------------------------
-
-
-
-/**
- * SQLite Database Adapter Class
- *
- * Note: _DB is an extender class that the app controller
- * creates dynamically based on whether the active record
- * class is being used or not.
- *
- * @package CodeIgniter
- * @subpackage Drivers
- * @category Database
- * @author ExpressionEngine Dev Team
- * @link http://codeigniter.com/user_guide/database/
- */
-class CI_DB_sqlite_driver extends CI_DB {
-
- var $dbdriver = 'sqlite';
-
- // The character used to escape with - not needed for SQLite
- var $_escape_char = '';
-
- // clause and character used for LIKE escape sequences
- var $_like_escape_str = " ESCAPE '%s' ";
- var $_like_escape_chr = '!';
-
- /**
- * The syntax to count rows is slightly different across different
- * database engines, so this string appears in each driver and is
- * used for the count_all() and count_all_results() functions.
- */
- var $_count_string = "SELECT COUNT(*) AS ";
- var $_random_keyword = ' Random()'; // database specific random keyword
-
- /**
- * Non-persistent database connection
- *
- * @access private called by the base class
- * @return resource
- */
- function db_connect()
- {
- if ( ! $conn_id = @sqlite_open($this->database, FILE_WRITE_MODE, $error))
- {
- log_message('error', $error);
-
- if ($this->db_debug)
- {
- $this->display_error($error, '', TRUE);
- }
-
- return FALSE;
- }
-
- return $conn_id;
- }
-
- // --------------------------------------------------------------------
-
- /**
- * Persistent database connection
- *
- * @access private called by the base class
- * @return resource
- */
- function db_pconnect()
- {
- if ( ! $conn_id = @sqlite_popen($this->database, FILE_WRITE_MODE, $error))
- {
- log_message('error', $error);
-
- if ($this->db_debug)
- {
- $this->display_error($error, '', TRUE);
- }
-
- return FALSE;
- }
-
- return $conn_id;
- }
-
- // --------------------------------------------------------------------
-
- /**
- * Reconnect
- *
- * Keep / reestablish the db connection if no queries have been
- * sent for a length of time exceeding the server's idle timeout
- *
- * @access public
- * @return void
- */
- function reconnect()
- {
- // not implemented in SQLite
- }
-
- // --------------------------------------------------------------------
-
- /**
- * Select the database
- *
- * @access private called by the base class
- * @return resource
- */
- function db_select()
- {
- return TRUE;
- }
-
- // --------------------------------------------------------------------
-
- /**
- * Set client character set
- *
- * @access public
- * @param string
- * @param string
- * @return resource
- */
- function db_set_charset($charset, $collation)
- {
- // @todo - add support if needed
- return TRUE;
- }
-
- // --------------------------------------------------------------------
-
- /**
- * Version number query string
- *
- * @access public
- * @return string
- */
- function _version()
- {
- return sqlite_libversion();
- }
-
- // --------------------------------------------------------------------
-
- /**
- * Execute the query
- *
- * @access private called by the base class
- * @param string an SQL query
- * @return resource
- */
- function _execute($sql)
- {
- $sql = $this->_prep_query($sql);
- return @sqlite_query($this->conn_id, $sql);
- }
-
- // --------------------------------------------------------------------
-
- /**
- * Prep the query
- *
- * If needed, each database adapter can prep the query string
- *
- * @access private called by execute()
- * @param string an SQL query
- * @return string
- */
- function _prep_query($sql)
- {
- return $sql;
- }
-
- // --------------------------------------------------------------------
-
- /**
- * Begin Transaction
- *
- * @access public
- * @return bool
- */
- function trans_begin($test_mode = FALSE)
- {
- if ( ! $this->trans_enabled)
- {
- return TRUE;
- }
-
- // When transactions are nested we only begin/commit/rollback the outermost ones
- if ($this->_trans_depth > 0)
- {
- return TRUE;
- }
-
- // Reset the transaction failure flag.
- // If the $test_mode flag is set to TRUE transactions will be rolled back
- // even if the queries produce a successful result.
- $this->_trans_failure = ($test_mode === TRUE) ? TRUE : FALSE;
-
- $this->simple_query('BEGIN TRANSACTION');
- return TRUE;
- }
-
- // --------------------------------------------------------------------
-
- /**
- * Commit Transaction
- *
- * @access public
- * @return bool
- */
- function trans_commit()
- {
- if ( ! $this->trans_enabled)
- {
- return TRUE;
- }
-
- // When transactions are nested we only begin/commit/rollback the outermost ones
- if ($this->_trans_depth > 0)
- {
- return TRUE;
- }
-
- $this->simple_query('COMMIT');
- return TRUE;
- }
-
- // --------------------------------------------------------------------
-
- /**
- * Rollback Transaction
- *
- * @access public
- * @return bool
- */
- function trans_rollback()
- {
- if ( ! $this->trans_enabled)
- {
- return TRUE;
- }
-
- // When transactions are nested we only begin/commit/rollback the outermost ones
- if ($this->_trans_depth > 0)
- {
- return TRUE;
- }
-
- $this->simple_query('ROLLBACK');
- return TRUE;
- }
-
- // --------------------------------------------------------------------
-
- /**
- * Escape String
- *
- * @access public
- * @param string
- * @param bool whether or not the string will be used in a LIKE condition
- * @return string
- */
- function escape_str($str, $like = FALSE)
- {
- if (is_array($str))
- {
- foreach ($str as $key => $val)
- {
- $str[$key] = $this->escape_str($val, $like);
- }
-
- return $str;
- }
-
- $str = sqlite_escape_string($str);
-
- // escape LIKE condition wildcards
- if ($like === TRUE)
- {
- $str = str_replace( array('%', '_', $this->_like_escape_chr),
- array($this->_like_escape_chr.'%', $this->_like_escape_chr.'_', $this->_like_escape_chr.$this->_like_escape_chr),
- $str);
- }
-
- return $str;
- }
-
- // --------------------------------------------------------------------
-
- /**
- * Affected Rows
- *
- * @access public
- * @return integer
- */
- function affected_rows()
- {
- return sqlite_changes($this->conn_id);
- }
-
- // --------------------------------------------------------------------
-
- /**
- * Insert ID
- *
- * @access public
- * @return integer
- */
- function insert_id()
- {
- return @sqlite_last_insert_rowid($this->conn_id);
- }
-
- // --------------------------------------------------------------------
-
- /**
- * "Count All" query
- *
- * Generates a platform-specific query string that counts all records in
- * the specified database
- *
- * @access public
- * @param string
- * @return string
- */
- function count_all($table = '')
- {
- if ($table == '')
- {
- return 0;
- }
-
- $query = $this->query($this->_count_string . $this->_protect_identifiers('numrows') . " FROM " . $this->_protect_identifiers($table, TRUE, NULL, FALSE));
-
- if ($query->num_rows() == 0)
- {
- return 0;
- }
-
- $row = $query->row();
- $this->_reset_select();
- return (int) $row->numrows;
- }
-
- // --------------------------------------------------------------------
-
- /**
- * List table query
- *
- * Generates a platform-specific query string so that the table names can be fetched
- *
- * @access private
- * @param boolean
- * @return string
- */
- function _list_tables($prefix_limit = FALSE)
- {
- $sql = "SELECT name from sqlite_master WHERE type='table'";
-
- if ($prefix_limit !== FALSE AND $this->dbprefix != '')
- {
- $sql .= " AND 'name' LIKE '".$this->escape_like_str($this->dbprefix)."%' ".sprintf($this->_like_escape_str, $this->_like_escape_chr);
- }
- return $sql;
- }
-
- // --------------------------------------------------------------------
-
- /**
- * Show column query
- *
- * Generates a platform-specific query string so that the column names can be fetched
- *
- * @access public
- * @param string the table name
- * @return string
- */
- function _list_columns($table = '')
- {
- // Not supported
- return FALSE;
- }
-
- // --------------------------------------------------------------------
-
- /**
- * Field data query
- *
- * Generates a platform-specific query so that the column data can be retrieved
- *
- * @access public
- * @param string the table name
- * @return object
- */
- function _field_data($table)
- {
- return "SELECT * FROM ".$table." LIMIT 1";
- }
-
- // --------------------------------------------------------------------
-
- /**
- * The error message string
- *
- * @access private
- * @return string
- */
- function _error_message()
- {
- return sqlite_error_string(sqlite_last_error($this->conn_id));
- }
-
- // --------------------------------------------------------------------
-
- /**
- * The error message number
- *
- * @access private
- * @return integer
- */
- function _error_number()
- {
- return sqlite_last_error($this->conn_id);
- }
-
- // --------------------------------------------------------------------
-
- /**
- * Escape the SQL Identifiers
- *
- * This function escapes column and table names
- *
- * @access private
- * @param string
- * @return string
- */
- function _escape_identifiers($item)
- {
- if ($this->_escape_char == '')
- {
- return $item;
- }
-
- foreach ($this->_reserved_identifiers as $id)
- {
- if (strpos($item, '.'.$id) !== FALSE)
- {
- $str = $this->_escape_char. str_replace('.', $this->_escape_char.'.', $item);
-
- // remove duplicates if the user already included the escape
- return preg_replace('/['.$this->_escape_char.']+/', $this->_escape_char, $str);
- }
- }
-
- if (strpos($item, '.') !== FALSE)
- {
- $str = $this->_escape_char.str_replace('.', $this->_escape_char.'.'.$this->_escape_char, $item).$this->_escape_char;
- }
- else
- {
- $str = $this->_escape_char.$item.$this->_escape_char;
- }
-
- // remove duplicates if the user already included the escape
- return preg_replace('/['.$this->_escape_char.']+/', $this->_escape_char, $str);
- }
-
- // --------------------------------------------------------------------
-
- /**
- * From Tables
- *
- * This function implicitly groups FROM tables so there is no confusion
- * about operator precedence in harmony with SQL standards
- *
- * @access public
- * @param type
- * @return type
- */
- function _from_tables($tables)
- {
- if ( ! is_array($tables))
- {
- $tables = array($tables);
- }
-
- return '('.implode(', ', $tables).')';
- }
-
- // --------------------------------------------------------------------
-
- /**
- * Insert statement
- *
- * Generates a platform-specific insert string from the supplied data
- *
- * @access public
- * @param string the table name
- * @param array the insert keys
- * @param array the insert values
- * @return string
- */
- function _insert($table, $keys, $values)
- {
- return "INSERT INTO ".$table." (".implode(', ', $keys).") VALUES (".implode(', ', $values).")";
- }
-
- // --------------------------------------------------------------------
-
- /**
- * Update statement
- *
- * Generates a platform-specific update string from the supplied data
- *
- * @access public
- * @param string the table name
- * @param array the update data
- * @param array the where clause
- * @param array the orderby clause
- * @param array the limit clause
- * @return string
- */
- function _update($table, $values, $where, $orderby = array(), $limit = FALSE)
- {
- foreach ($values as $key => $val)
- {
- $valstr[] = $key." = ".$val;
- }
-
- $limit = ( ! $limit) ? '' : ' LIMIT '.$limit;
-
- $orderby = (count($orderby) >= 1)?' ORDER BY '.implode(", ", $orderby):'';
-
- $sql = "UPDATE ".$table." SET ".implode(', ', $valstr);
-
- $sql .= ($where != '' AND count($where) >=1) ? " WHERE ".implode(" ", $where) : '';
-
- $sql .= $orderby.$limit;
-
- return $sql;
- }
-
-
- // --------------------------------------------------------------------
-
- /**
- * Truncate statement
- *
- * Generates a platform-specific truncate string from the supplied data
- * If the database does not support the truncate() command
- * This function maps to "DELETE FROM table"
- *
- * @access public
- * @param string the table name
- * @return string
- */
- function _truncate($table)
- {
- return $this->_delete($table);
- }
-
- // --------------------------------------------------------------------
-
- /**
- * Delete statement
- *
- * Generates a platform-specific delete string from the supplied data
- *
- * @access public
- * @param string the table name
- * @param array the where clause
- * @param string the limit clause
- * @return string
- */
- function _delete($table, $where = array(), $like = array(), $limit = FALSE)
- {
- $conditions = '';
-
- if (count($where) > 0 OR count($like) > 0)
- {
- $conditions = "\nWHERE ";
- $conditions .= implode("\n", $this->ar_where);
-
- if (count($where) > 0 && count($like) > 0)
- {
- $conditions .= " AND ";
- }
- $conditions .= implode("\n", $like);
- }
-
- $limit = ( ! $limit) ? '' : ' LIMIT '.$limit;
-
- return "DELETE FROM ".$table.$conditions.$limit;
- }
-
- // --------------------------------------------------------------------
-
- /**
- * Limit string
- *
- * Generates a platform-specific LIMIT clause
- *
- * @access public
- * @param string the sql query string
- * @param integer the number of rows to limit the query to
- * @param integer the offset value
- * @return string
- */
- function _limit($sql, $limit, $offset)
- {
- if ($offset == 0)
- {
- $offset = '';
- }
- else
- {
- $offset .= ", ";
- }
-
- return $sql."LIMIT ".$offset.$limit;
- }
-
- // --------------------------------------------------------------------
-
- /**
- * Close DB Connection
- *
- * @access public
- * @param resource
- * @return void
- */
- function _close($conn_id)
- {
- @sqlite_close($conn_id);
- }
-
-
-}
-
-
-/* End of file sqlite_driver.php */
-/* Location: ./system/database/drivers/sqlite/sqlite_driver.php */ \ No newline at end of file
diff --git a/system/database/drivers/sqlite/sqlite_forge.php b/system/database/drivers/sqlite/sqlite_forge.php
deleted file mode 100644
index a15e94d3b..000000000
--- a/system/database/drivers/sqlite/sqlite_forge.php
+++ /dev/null
@@ -1,265 +0,0 @@
-<?php if ( ! defined('BASEPATH')) exit('No direct script access allowed');
-/**
- * CodeIgniter
- *
- * An open source application development framework for PHP 5.1.6 or newer
- *
- * @package CodeIgniter
- * @author ExpressionEngine Dev Team
- * @copyright Copyright (c) 2008 - 2014, EllisLab, Inc.
- * @license http://codeigniter.com/user_guide/license.html
- * @link http://codeigniter.com
- * @since Version 1.0
- * @filesource
- */
-
-// ------------------------------------------------------------------------
-
-/**
- * SQLite Forge Class
- *
- * @category Database
- * @author ExpressionEngine Dev Team
- * @link http://codeigniter.com/user_guide/database/
- */
-class CI_DB_sqlite_forge extends CI_DB_forge {
-
- /**
- * Create database
- *
- * @access public
- * @param string the database name
- * @return bool
- */
- function _create_database()
- {
- // In SQLite, a database is created when you connect to the database.
- // We'll return TRUE so that an error isn't generated
- return TRUE;
- }
-
- // --------------------------------------------------------------------
-
- /**
- * Drop database
- *
- * @access private
- * @param string the database name
- * @return bool
- */
- function _drop_database($name)
- {
- if ( ! @file_exists($this->db->database) OR ! @unlink($this->db->database))
- {
- if ($this->db->db_debug)
- {
- return $this->db->display_error('db_unable_to_drop');
- }
- return FALSE;
- }
- return TRUE;
- }
- // --------------------------------------------------------------------
-
- /**
- * Create Table
- *
- * @access private
- * @param string the table name
- * @param array the fields
- * @param mixed primary key(s)
- * @param mixed key(s)
- * @param boolean should 'IF NOT EXISTS' be added to the SQL
- * @return bool
- */
- function _create_table($table, $fields, $primary_keys, $keys, $if_not_exists)
- {
- $sql = 'CREATE TABLE ';
-
- // IF NOT EXISTS added to SQLite in 3.3.0
- if ($if_not_exists === TRUE && version_compare($this->db->_version(), '3.3.0', '>=') === TRUE)
- {
- $sql .= 'IF NOT EXISTS ';
- }
-
- $sql .= $this->db->_escape_identifiers($table)."(";
- $current_field_count = 0;
-
- foreach ($fields as $field=>$attributes)
- {
- // Numeric field names aren't allowed in databases, so if the key is
- // numeric, we know it was assigned by PHP and the developer manually
- // entered the field information, so we'll simply add it to the list
- if (is_numeric($field))
- {
- $sql .= "\n\t$attributes";
- }
- else
- {
- $attributes = array_change_key_case($attributes, CASE_UPPER);
-
- $sql .= "\n\t".$this->db->_protect_identifiers($field);
-
- $sql .= ' '.$attributes['TYPE'];
-
- if (array_key_exists('CONSTRAINT', $attributes))
- {
- $sql .= '('.$attributes['CONSTRAINT'].')';
- }
-
- if (array_key_exists('UNSIGNED', $attributes) && $attributes['UNSIGNED'] === TRUE)
- {
- $sql .= ' UNSIGNED';
- }
-
- if (array_key_exists('DEFAULT', $attributes))
- {
- $sql .= ' DEFAULT \''.$attributes['DEFAULT'].'\'';
- }
-
- if (array_key_exists('NULL', $attributes) && $attributes['NULL'] === TRUE)
- {
- $sql .= ' NULL';
- }
- else
- {
- $sql .= ' NOT NULL';
- }
-
- if (array_key_exists('AUTO_INCREMENT', $attributes) && $attributes['AUTO_INCREMENT'] === TRUE)
- {
- $sql .= ' AUTO_INCREMENT';
- }
- }
-
- // don't add a comma on the end of the last field
- if (++$current_field_count < count($fields))
- {
- $sql .= ',';
- }
- }
-
- if (count($primary_keys) > 0)
- {
- $primary_keys = $this->db->_protect_identifiers($primary_keys);
- $sql .= ",\n\tPRIMARY KEY (" . implode(', ', $primary_keys) . ")";
- }
-
- if (is_array($keys) && count($keys) > 0)
- {
- foreach ($keys as $key)
- {
- if (is_array($key))
- {
- $key = $this->db->_protect_identifiers($key);
- }
- else
- {
- $key = array($this->db->_protect_identifiers($key));
- }
-
- $sql .= ",\n\tUNIQUE (" . implode(', ', $key) . ")";
- }
- }
-
- $sql .= "\n)";
-
- return $sql;
- }
-
- // --------------------------------------------------------------------
-
- /**
- * Drop Table
- *
- * Unsupported feature in SQLite
- *
- * @access private
- * @return bool
- */
- function _drop_table($table)
- {
- if ($this->db->db_debug)
- {
- return $this->db->display_error('db_unsuported_feature');
- }
- return array();
- }
-
- // --------------------------------------------------------------------
-
- /**
- * Alter table query
- *
- * Generates a platform-specific query so that a table can be altered
- * Called by add_column(), drop_column(), and column_alter(),
- *
- * @access private
- * @param string the ALTER type (ADD, DROP, CHANGE)
- * @param string the column name
- * @param string the table name
- * @param string the column definition
- * @param string the default value
- * @param boolean should 'NOT NULL' be added
- * @param string the field after which we should add the new field
- * @return object
- */
- function _alter_table($alter_type, $table, $column_name, $column_definition = '', $default_value = '', $null = '', $after_field = '')
- {
- $sql = 'ALTER TABLE '.$this->db->_protect_identifiers($table)." $alter_type ".$this->db->_protect_identifiers($column_name);
-
- // DROP has everything it needs now.
- if ($alter_type == 'DROP')
- {
- // SQLite does not support dropping columns
- // http://www.sqlite.org/omitted.html
- // http://www.sqlite.org/faq.html#q11
- return FALSE;
- }
-
- $sql .= " $column_definition";
-
- if ($default_value != '')
- {
- $sql .= " DEFAULT \"$default_value\"";
- }
-
- if ($null === NULL)
- {
- $sql .= ' NULL';
- }
- else
- {
- $sql .= ' NOT NULL';
- }
-
- if ($after_field != '')
- {
- $sql .= ' AFTER ' . $this->db->_protect_identifiers($after_field);
- }
-
- return $sql;
-
- }
-
- // --------------------------------------------------------------------
-
- /**
- * Rename a table
- *
- * Generates a platform-specific query so that a table can be renamed
- *
- * @access private
- * @param string the old table name
- * @param string the new table name
- * @return string
- */
- function _rename_table($table_name, $new_table_name)
- {
- $sql = 'ALTER TABLE '.$this->db->_protect_identifiers($table_name)." RENAME TO ".$this->db->_protect_identifiers($new_table_name);
- return $sql;
- }
-}
-
-/* End of file sqlite_forge.php */
-/* Location: ./system/database/drivers/sqlite/sqlite_forge.php */ \ No newline at end of file
diff --git a/system/database/drivers/sqlite/sqlite_result.php b/system/database/drivers/sqlite/sqlite_result.php
deleted file mode 100644
index 9e519dffd..000000000
--- a/system/database/drivers/sqlite/sqlite_result.php
+++ /dev/null
@@ -1,179 +0,0 @@
-<?php if ( ! defined('BASEPATH')) exit('No direct script access allowed');
-/**
- * CodeIgniter
- *
- * An open source application development framework for PHP 5.1.6 or newer
- *
- * @package CodeIgniter
- * @author ExpressionEngine Dev Team
- * @copyright Copyright (c) 2008 - 2014, EllisLab, Inc.
- * @license http://codeigniter.com/user_guide/license.html
- * @link http://codeigniter.com
- * @since Version 1.0
- * @filesource
- */
-
-// ------------------------------------------------------------------------
-
-/**
- * SQLite Result Class
- *
- * This class extends the parent result class: CI_DB_result
- *
- * @category Database
- * @author ExpressionEngine Dev Team
- * @link http://codeigniter.com/user_guide/database/
- */
-class CI_DB_sqlite_result extends CI_DB_result {
-
- /**
- * Number of rows in the result set
- *
- * @access public
- * @return integer
- */
- function num_rows()
- {
- return @sqlite_num_rows($this->result_id);
- }
-
- // --------------------------------------------------------------------
-
- /**
- * Number of fields in the result set
- *
- * @access public
- * @return integer
- */
- function num_fields()
- {
- return @sqlite_num_fields($this->result_id);
- }
-
- // --------------------------------------------------------------------
-
- /**
- * Fetch Field Names
- *
- * Generates an array of column names
- *
- * @access public
- * @return array
- */
- function list_fields()
- {
- $field_names = array();
- for ($i = 0; $i < $this->num_fields(); $i++)
- {
- $field_names[] = sqlite_field_name($this->result_id, $i);
- }
-
- return $field_names;
- }
-
- // --------------------------------------------------------------------
-
- /**
- * Field data
- *
- * Generates an array of objects containing field meta-data
- *
- * @access public
- * @return array
- */
- function field_data()
- {
- $retval = array();
- for ($i = 0; $i < $this->num_fields(); $i++)
- {
- $F = new stdClass();
- $F->name = sqlite_field_name($this->result_id, $i);
- $F->type = 'varchar';
- $F->max_length = 0;
- $F->primary_key = 0;
- $F->default = '';
-
- $retval[] = $F;
- }
-
- return $retval;
- }
-
- // --------------------------------------------------------------------
-
- /**
- * Free the result
- *
- * @return null
- */
- function free_result()
- {
- // Not implemented in SQLite
- }
-
- // --------------------------------------------------------------------
-
- /**
- * Data Seek
- *
- * Moves the internal pointer to the desired offset. We call
- * this internally before fetching results to make sure the
- * result set starts at zero
- *
- * @access private
- * @return array
- */
- function _data_seek($n = 0)
- {
- return sqlite_seek($this->result_id, $n);
- }
-
- // --------------------------------------------------------------------
-
- /**
- * Result - associative array
- *
- * Returns the result set as an array
- *
- * @access private
- * @return array
- */
- function _fetch_assoc()
- {
- return sqlite_fetch_array($this->result_id);
- }
-
- // --------------------------------------------------------------------
-
- /**
- * Result - object
- *
- * Returns the result set as an object
- *
- * @access private
- * @return object
- */
- function _fetch_object()
- {
- if (function_exists('sqlite_fetch_object'))
- {
- return sqlite_fetch_object($this->result_id);
- }
- else
- {
- $arr = sqlite_fetch_array($this->result_id, SQLITE_ASSOC);
- if (is_array($arr))
- {
- $obj = (object) $arr;
- return $obj;
- } else {
- return NULL;
- }
- }
- }
-
-}
-
-
-/* End of file sqlite_result.php */
-/* Location: ./system/database/drivers/sqlite/sqlite_result.php */ \ No newline at end of file
diff --git a/system/database/drivers/sqlite/sqlite_utility.php b/system/database/drivers/sqlite/sqlite_utility.php
deleted file mode 100644
index 481b735be..000000000
--- a/system/database/drivers/sqlite/sqlite_utility.php
+++ /dev/null
@@ -1,96 +0,0 @@
-<?php if ( ! defined('BASEPATH')) exit('No direct script access allowed');
-/**
- * CodeIgniter
- *
- * An open source application development framework for PHP 5.1.6 or newer
- *
- * @package CodeIgniter
- * @author ExpressionEngine Dev Team
- * @copyright Copyright (c) 2008 - 2014, EllisLab, Inc.
- * @license http://codeigniter.com/user_guide/license.html
- * @link http://codeigniter.com
- * @since Version 1.0
- * @filesource
- */
-
-// ------------------------------------------------------------------------
-
-/**
- * SQLite Utility Class
- *
- * @category Database
- * @author ExpressionEngine Dev Team
- * @link http://codeigniter.com/user_guide/database/
- */
-class CI_DB_sqlite_utility extends CI_DB_utility {
-
- /**
- * List databases
- *
- * I don't believe you can do a database listing with SQLite
- * since each database is its own file. I suppose we could
- * try reading a directory looking for SQLite files, but
- * that doesn't seem like a terribly good idea
- *
- * @access private
- * @return bool
- */
- function _list_databases()
- {
- if ($this->db_debug)
- {
- return $this->db->display_error('db_unsuported_feature');
- }
- return array();
- }
-
- // --------------------------------------------------------------------
-
- /**
- * Optimize table query
- *
- * Is optimization even supported in SQLite?
- *
- * @access private
- * @param string the table name
- * @return object
- */
- function _optimize_table($table)
- {
- return FALSE;
- }
-
- // --------------------------------------------------------------------
-
- /**
- * Repair table query
- *
- * Are table repairs even supported in SQLite?
- *
- * @access private
- * @param string the table name
- * @return object
- */
- function _repair_table($table)
- {
- return FALSE;
- }
-
- // --------------------------------------------------------------------
-
- /**
- * SQLite Export
- *
- * @access private
- * @param array Preferences
- * @return mixed
- */
- function _backup($params = array())
- {
- // Currently unsupported
- return $this->db->display_error('db_unsuported_feature');
- }
-}
-
-/* End of file sqlite_utility.php */
-/* Location: ./system/database/drivers/sqlite/sqlite_utility.php */ \ No newline at end of file
diff --git a/system/database/drivers/sqlite3/index.html b/system/database/drivers/sqlite3/index.html
new file mode 100644
index 000000000..bcb7cae34
--- /dev/null
+++ b/system/database/drivers/sqlite3/index.html
@@ -0,0 +1,11 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+ <title>403 Forbidden</title>
+</head>
+<body>
+
+<p>Directory access is forbidden.</p>
+
+</body>
+</html>
diff --git a/system/database/drivers/sqlite3/sqlite3_driver.php b/system/database/drivers/sqlite3/sqlite3_driver.php
new file mode 100644
index 000000000..be79ddd5c
--- /dev/null
+++ b/system/database/drivers/sqlite3/sqlite3_driver.php
@@ -0,0 +1,345 @@
+<?php
+/**
+ * CodeIgniter
+ *
+ * An open source application development framework for PHP
+ *
+ * This content is released under the MIT License (MIT)
+ *
+ * Copyright (c) 2019 - 2022, CodeIgniter Foundation
+ *
+ * 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.
+ *
+ * @package CodeIgniter
+ * @author EllisLab Dev Team
+ * @copyright Copyright (c) 2008 - 2014, EllisLab, Inc. (https://ellislab.com/)
+ * @copyright Copyright (c) 2014 - 2019, British Columbia Institute of Technology (https://bcit.ca/)
+ * @copyright Copyright (c) 2019 - 2022, CodeIgniter Foundation (https://codeigniter.com/)
+ * @license https://opensource.org/licenses/MIT MIT License
+ * @link https://codeigniter.com
+ * @since Version 3.0.0
+ * @filesource
+ */
+defined('BASEPATH') OR exit('No direct script access allowed');
+
+/**
+ * SQLite3 Database Adapter Class
+ *
+ * Note: _DB is an extender class that the app controller
+ * creates dynamically based on whether the query builder
+ * class is being used or not.
+ *
+ * @package CodeIgniter
+ * @subpackage Drivers
+ * @category Database
+ * @author Andrey Andreev
+ * @link https://codeigniter.com/userguide3/database/
+ */
+class CI_DB_sqlite3_driver extends CI_DB {
+
+ /**
+ * Database driver
+ *
+ * @var string
+ */
+ public $dbdriver = 'sqlite3';
+
+ // --------------------------------------------------------------------
+
+ /**
+ * ORDER BY random keyword
+ *
+ * @var array
+ */
+ protected $_random_keyword = array('RANDOM()', 'RANDOM()');
+
+ // --------------------------------------------------------------------
+
+ /**
+ * Non-persistent database connection
+ *
+ * @param bool $persistent
+ * @return SQLite3
+ */
+ public function db_connect($persistent = FALSE)
+ {
+ if ($persistent)
+ {
+ log_message('debug', 'SQLite3 doesn\'t support persistent connections');
+ }
+
+ try
+ {
+ return ( ! $this->password)
+ ? new SQLite3($this->database)
+ : new SQLite3($this->database, SQLITE3_OPEN_READWRITE | SQLITE3_OPEN_CREATE, $this->password);
+ }
+ catch (Exception $e)
+ {
+ return FALSE;
+ }
+ }
+
+ // --------------------------------------------------------------------
+
+ /**
+ * Database version number
+ *
+ * @return string
+ */
+ public function version()
+ {
+ if (isset($this->data_cache['version']))
+ {
+ return $this->data_cache['version'];
+ }
+
+ $version = SQLite3::version();
+ return $this->data_cache['version'] = $version['versionString'];
+ }
+
+ // --------------------------------------------------------------------
+
+ /**
+ * Execute the query
+ *
+ * @todo Implement use of SQLite3::querySingle(), if needed
+ * @param string $sql
+ * @return mixed SQLite3Result object or bool
+ */
+ protected function _execute($sql)
+ {
+ return $this->is_write_type($sql)
+ ? $this->conn_id->exec($sql)
+ : $this->conn_id->query($sql);
+ }
+
+ // --------------------------------------------------------------------
+
+ /**
+ * Begin Transaction
+ *
+ * @return bool
+ */
+ protected function _trans_begin()
+ {
+ return $this->conn_id->exec('BEGIN TRANSACTION');
+ }
+
+ // --------------------------------------------------------------------
+
+ /**
+ * Commit Transaction
+ *
+ * @return bool
+ */
+ protected function _trans_commit()
+ {
+ return $this->conn_id->exec('END TRANSACTION');
+ }
+
+ // --------------------------------------------------------------------
+
+ /**
+ * Rollback Transaction
+ *
+ * @return bool
+ */
+ protected function _trans_rollback()
+ {
+ return $this->conn_id->exec('ROLLBACK');
+ }
+
+ // --------------------------------------------------------------------
+
+ /**
+ * Platform-dependent string escape
+ *
+ * @param string
+ * @return string
+ */
+ protected function _escape_str($str)
+ {
+ return $this->conn_id->escapeString($str);
+ }
+
+ // --------------------------------------------------------------------
+
+ /**
+ * Affected Rows
+ *
+ * @return int
+ */
+ public function affected_rows()
+ {
+ return $this->conn_id->changes();
+ }
+
+ // --------------------------------------------------------------------
+
+ /**
+ * Insert ID
+ *
+ * @return int
+ */
+ public function insert_id()
+ {
+ return $this->conn_id->lastInsertRowID();
+ }
+
+ // --------------------------------------------------------------------
+
+ /**
+ * Show table query
+ *
+ * Generates a platform-specific query string so that the table names can be fetched
+ *
+ * @param bool $prefix_limit
+ * @return string
+ */
+ protected function _list_tables($prefix_limit = FALSE)
+ {
+ return 'SELECT "NAME" FROM "SQLITE_MASTER" WHERE "TYPE" = \'table\''
+ .(($prefix_limit !== FALSE && $this->dbprefix != '')
+ ? ' AND "NAME" LIKE \''.$this->escape_like_str($this->dbprefix).'%\' '.sprintf($this->_like_escape_str, $this->_like_escape_chr)
+ : '');
+ }
+
+ // --------------------------------------------------------------------
+
+ /**
+ * Fetch Field Names
+ *
+ * @param string $table Table name
+ * @return array
+ */
+ public function list_fields($table)
+ {
+ if (($result = $this->query('PRAGMA TABLE_INFO('.$this->protect_identifiers($table, TRUE, NULL, FALSE).')')) === FALSE)
+ {
+ return FALSE;
+ }
+
+ $fields = array();
+ foreach ($result->result_array() as $row)
+ {
+ $fields[] = $row['name'];
+ }
+
+ return $fields;
+ }
+
+ // --------------------------------------------------------------------
+
+ /**
+ * Returns an object with field data
+ *
+ * @param string $table
+ * @return array
+ */
+ public function field_data($table)
+ {
+ if (($query = $this->query('PRAGMA TABLE_INFO('.$this->protect_identifiers($table, TRUE, NULL, FALSE).')')) === FALSE)
+ {
+ return FALSE;
+ }
+
+ $query = $query->result_array();
+ if (empty($query))
+ {
+ return FALSE;
+ }
+
+ $retval = array();
+ for ($i = 0, $c = count($query); $i < $c; $i++)
+ {
+ $retval[$i] = new stdClass();
+ $retval[$i]->name = $query[$i]['name'];
+ $retval[$i]->type = $query[$i]['type'];
+ $retval[$i]->max_length = NULL;
+ $retval[$i]->default = $query[$i]['dflt_value'];
+ $retval[$i]->primary_key = isset($query[$i]['pk']) ? (int) $query[$i]['pk'] : 0;
+ }
+
+ return $retval;
+ }
+
+ // --------------------------------------------------------------------
+
+ /**
+ * Error
+ *
+ * Returns an array containing code and message of the last
+ * database error that has occurred.
+ *
+ * @return array
+ */
+ public function error()
+ {
+ return array('code' => $this->conn_id->lastErrorCode(), 'message' => $this->conn_id->lastErrorMsg());
+ }
+
+ // --------------------------------------------------------------------
+
+ /**
+ * Replace statement
+ *
+ * Generates a platform-specific replace string from the supplied data
+ *
+ * @param string $table Table name
+ * @param array $keys INSERT keys
+ * @param array $values INSERT values
+ * @return string
+ */
+ protected function _replace($table, $keys, $values)
+ {
+ return 'INSERT OR '.parent::_replace($table, $keys, $values);
+ }
+
+ // --------------------------------------------------------------------
+
+ /**
+ * Truncate statement
+ *
+ * Generates a platform-specific truncate string from the supplied data
+ *
+ * If the database does not support the TRUNCATE statement,
+ * then this method maps to 'DELETE FROM table'
+ *
+ * @param string $table
+ * @return string
+ */
+ protected function _truncate($table)
+ {
+ return 'DELETE FROM '.$table;
+ }
+
+ // --------------------------------------------------------------------
+
+ /**
+ * Close DB Connection
+ *
+ * @return void
+ */
+ protected function _close()
+ {
+ $this->conn_id->close();
+ }
+
+}
diff --git a/system/database/drivers/sqlite3/sqlite3_forge.php b/system/database/drivers/sqlite3/sqlite3_forge.php
new file mode 100644
index 000000000..5658b3e8f
--- /dev/null
+++ b/system/database/drivers/sqlite3/sqlite3_forge.php
@@ -0,0 +1,226 @@
+<?php
+/**
+ * CodeIgniter
+ *
+ * An open source application development framework for PHP
+ *
+ * This content is released under the MIT License (MIT)
+ *
+ * Copyright (c) 2019 - 2022, CodeIgniter Foundation
+ *
+ * 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.
+ *
+ * @package CodeIgniter
+ * @author EllisLab Dev Team
+ * @copyright Copyright (c) 2008 - 2014, EllisLab, Inc. (https://ellislab.com/)
+ * @copyright Copyright (c) 2014 - 2019, British Columbia Institute of Technology (https://bcit.ca/)
+ * @copyright Copyright (c) 2019 - 2022, CodeIgniter Foundation (https://codeigniter.com/)
+ * @license https://opensource.org/licenses/MIT MIT License
+ * @link https://codeigniter.com
+ * @since Version 3.0.0
+ * @filesource
+ */
+defined('BASEPATH') OR exit('No direct script access allowed');
+
+/**
+ * SQLite3 Forge Class
+ *
+ * @category Database
+ * @author Andrey Andreev
+ * @link https://codeigniter.com/userguide3/database/
+ */
+class CI_DB_sqlite3_forge extends CI_DB_forge {
+
+ /**
+ * UNSIGNED support
+ *
+ * @var bool|array
+ */
+ protected $_unsigned = FALSE;
+
+ /**
+ * NULL value representation in CREATE/ALTER TABLE statements
+ *
+ * @var string
+ */
+ protected $_null = 'NULL';
+
+ // --------------------------------------------------------------------
+
+ /**
+ * Class constructor
+ *
+ * @param object &$db Database object
+ * @return void
+ */
+ public function __construct(&$db)
+ {
+ parent::__construct($db);
+
+ if (version_compare($this->db->version(), '3.3', '<'))
+ {
+ $this->_create_table_if = FALSE;
+ $this->_drop_table_if = FALSE;
+ }
+ }
+
+ // --------------------------------------------------------------------
+
+ /**
+ * Create database
+ *
+ * @param string $db_name
+ * @return bool
+ */
+ public function create_database($db_name)
+ {
+ // In SQLite, a database is created when you connect to the database.
+ // We'll return TRUE so that an error isn't generated
+ return TRUE;
+ }
+
+ // --------------------------------------------------------------------
+
+ /**
+ * Drop database
+ *
+ * @param string $db_name (ignored)
+ * @return bool
+ */
+ public function drop_database($db_name)
+ {
+ // In SQLite, a database is dropped when we delete a file
+ if (file_exists($this->db->database))
+ {
+ // We need to close the pseudo-connection first
+ $this->db->close();
+ if ( ! @unlink($this->db->database))
+ {
+ return $this->db->db_debug ? $this->db->display_error('db_unable_to_drop') : FALSE;
+ }
+ elseif ( ! empty($this->db->data_cache['db_names']))
+ {
+ $key = array_search(strtolower($this->db->database), array_map('strtolower', $this->db->data_cache['db_names']), TRUE);
+ if ($key !== FALSE)
+ {
+ unset($this->db->data_cache['db_names'][$key]);
+ }
+ }
+
+ return TRUE;
+ }
+
+ return $this->db->db_debug ? $this->db->display_error('db_unable_to_drop') : FALSE;
+ }
+
+ // --------------------------------------------------------------------
+
+ /**
+ * ALTER TABLE
+ *
+ * @todo implement drop_column(), modify_column()
+ * @param string $alter_type ALTER type
+ * @param string $table Table name
+ * @param mixed $field Column definition
+ * @return string|string[]
+ */
+ protected function _alter_table($alter_type, $table, $field)
+ {
+ if ($alter_type === 'DROP' OR $alter_type === 'CHANGE')
+ {
+ // drop_column():
+ // BEGIN TRANSACTION;
+ // CREATE TEMPORARY TABLE t1_backup(a,b);
+ // INSERT INTO t1_backup SELECT a,b FROM t1;
+ // DROP TABLE t1;
+ // CREATE TABLE t1(a,b);
+ // INSERT INTO t1 SELECT a,b FROM t1_backup;
+ // DROP TABLE t1_backup;
+ // COMMIT;
+
+ return FALSE;
+ }
+
+ return parent::_alter_table($alter_type, $table, $field);
+ }
+
+ // --------------------------------------------------------------------
+
+ /**
+ * Process column
+ *
+ * @param array $field
+ * @return string
+ */
+ protected function _process_column($field)
+ {
+ return $this->db->escape_identifiers($field['name'])
+ .' '.$field['type']
+ .$field['auto_increment']
+ .$field['null']
+ .$field['unique']
+ .$field['default'];
+ }
+
+ // --------------------------------------------------------------------
+
+ /**
+ * Field attribute TYPE
+ *
+ * Performs a data type mapping between different databases.
+ *
+ * @param array &$attributes
+ * @return void
+ */
+ protected function _attr_type(&$attributes)
+ {
+ switch (strtoupper($attributes['TYPE']))
+ {
+ case 'ENUM':
+ case 'SET':
+ $attributes['TYPE'] = 'TEXT';
+ return;
+ default: return;
+ }
+ }
+
+ // --------------------------------------------------------------------
+
+ /**
+ * Field attribute AUTO_INCREMENT
+ *
+ * @param array &$attributes
+ * @param array &$field
+ * @return void
+ */
+ protected function _attr_auto_increment(&$attributes, &$field)
+ {
+ if ( ! empty($attributes['AUTO_INCREMENT']) && $attributes['AUTO_INCREMENT'] === TRUE && stripos($field['type'], 'int') !== FALSE)
+ {
+ $field['type'] = 'INTEGER PRIMARY KEY';
+ $field['default'] = '';
+ $field['null'] = '';
+ $field['unique'] = '';
+ $field['auto_increment'] = ' AUTOINCREMENT';
+
+ $this->primary_keys = array();
+ }
+ }
+
+}
diff --git a/system/database/drivers/sqlite3/sqlite3_result.php b/system/database/drivers/sqlite3/sqlite3_result.php
new file mode 100644
index 000000000..47fe9d2a3
--- /dev/null
+++ b/system/database/drivers/sqlite3/sqlite3_result.php
@@ -0,0 +1,195 @@
+<?php
+/**
+ * CodeIgniter
+ *
+ * An open source application development framework for PHP
+ *
+ * This content is released under the MIT License (MIT)
+ *
+ * Copyright (c) 2019 - 2022, CodeIgniter Foundation
+ *
+ * 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.
+ *
+ * @package CodeIgniter
+ * @author EllisLab Dev Team
+ * @copyright Copyright (c) 2008 - 2014, EllisLab, Inc. (https://ellislab.com/)
+ * @copyright Copyright (c) 2014 - 2019, British Columbia Institute of Technology (https://bcit.ca/)
+ * @copyright Copyright (c) 2019 - 2022, CodeIgniter Foundation (https://codeigniter.com/)
+ * @license https://opensource.org/licenses/MIT MIT License
+ * @link https://codeigniter.com
+ * @since Version 3.0.0
+ * @filesource
+ */
+defined('BASEPATH') OR exit('No direct script access allowed');
+
+/**
+ * SQLite3 Result Class
+ *
+ * This class extends the parent result class: CI_DB_result
+ *
+ * @category Database
+ * @author Andrey Andreev
+ * @link https://codeigniter.com/userguide3/database/
+ */
+class CI_DB_sqlite3_result extends CI_DB_result {
+
+ /**
+ * Number of fields in the result set
+ *
+ * @return int
+ */
+ public function num_fields()
+ {
+ return $this->result_id->numColumns();
+ }
+
+ // --------------------------------------------------------------------
+
+ /**
+ * Fetch Field Names
+ *
+ * Generates an array of column names
+ *
+ * @return array
+ */
+ public function list_fields()
+ {
+ $field_names = array();
+ for ($i = 0, $c = $this->num_fields(); $i < $c; $i++)
+ {
+ $field_names[] = $this->result_id->columnName($i);
+ }
+
+ return $field_names;
+ }
+
+ // --------------------------------------------------------------------
+
+ /**
+ * Field data
+ *
+ * Generates an array of objects containing field meta-data
+ *
+ * @return array
+ */
+ public function field_data()
+ {
+ static $data_types = array(
+ SQLITE3_INTEGER => 'integer',
+ SQLITE3_FLOAT => 'float',
+ SQLITE3_TEXT => 'text',
+ SQLITE3_BLOB => 'blob',
+ SQLITE3_NULL => 'null'
+ );
+
+ $retval = array();
+ for ($i = 0, $c = $this->num_fields(); $i < $c; $i++)
+ {
+ $retval[$i] = new stdClass();
+ $retval[$i]->name = $this->result_id->columnName($i);
+
+ $type = $this->result_id->columnType($i);
+ $retval[$i]->type = isset($data_types[$type]) ? $data_types[$type] : $type;
+
+ $retval[$i]->max_length = NULL;
+ }
+
+ return $retval;
+ }
+
+ // --------------------------------------------------------------------
+
+ /**
+ * Free the result
+ *
+ * @return void
+ */
+ public function free_result()
+ {
+ if (is_object($this->result_id))
+ {
+ $this->result_id->finalize();
+ $this->result_id = NULL;
+ }
+ }
+
+ // --------------------------------------------------------------------
+
+ /**
+ * Result - associative array
+ *
+ * Returns the result set as an array
+ *
+ * @return array
+ */
+ protected function _fetch_assoc()
+ {
+ return $this->result_id->fetchArray(SQLITE3_ASSOC);
+ }
+
+ // --------------------------------------------------------------------
+
+ /**
+ * Result - object
+ *
+ * Returns the result set as an object
+ *
+ * @param string $class_name
+ * @return object
+ */
+ protected function _fetch_object($class_name = 'stdClass')
+ {
+ // No native support for fetching rows as objects
+ if (($row = $this->result_id->fetchArray(SQLITE3_ASSOC)) === FALSE)
+ {
+ return FALSE;
+ }
+ elseif ($class_name === 'stdClass')
+ {
+ return (object) $row;
+ }
+
+ $class_name = new $class_name();
+ foreach (array_keys($row) as $key)
+ {
+ $class_name->$key = $row[$key];
+ }
+
+ return $class_name;
+ }
+
+ // --------------------------------------------------------------------
+
+ /**
+ * Data Seek
+ *
+ * Moves the internal pointer to the desired offset. We call
+ * this internally before fetching results to make sure the
+ * result set starts at zero.
+ *
+ * @param int $n (ignored)
+ * @return array
+ */
+ public function data_seek($n = 0)
+ {
+ // Only resetting to the start of the result set is supported
+ return ($n > 0) ? FALSE : $this->result_id->reset();
+ }
+
+}
diff --git a/system/database/drivers/sqlite3/sqlite3_utility.php b/system/database/drivers/sqlite3/sqlite3_utility.php
new file mode 100644
index 000000000..90316bccc
--- /dev/null
+++ b/system/database/drivers/sqlite3/sqlite3_utility.php
@@ -0,0 +1,62 @@
+<?php
+/**
+ * CodeIgniter
+ *
+ * An open source application development framework for PHP
+ *
+ * This content is released under the MIT License (MIT)
+ *
+ * Copyright (c) 2019 - 2022, CodeIgniter Foundation
+ *
+ * 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.
+ *
+ * @package CodeIgniter
+ * @author EllisLab Dev Team
+ * @copyright Copyright (c) 2008 - 2014, EllisLab, Inc. (https://ellislab.com/)
+ * @copyright Copyright (c) 2014 - 2019, British Columbia Institute of Technology (https://bcit.ca/)
+ * @copyright Copyright (c) 2019 - 2022, CodeIgniter Foundation (https://codeigniter.com/)
+ * @license https://opensource.org/licenses/MIT MIT License
+ * @link https://codeigniter.com
+ * @since Version 3.0.0
+ * @filesource
+ */
+defined('BASEPATH') OR exit('No direct script access allowed');
+
+/**
+ * SQLite3 Utility Class
+ *
+ * @category Database
+ * @author Andrey Andreev
+ * @link https://codeigniter.com/userguide3/database/
+ */
+class CI_DB_sqlite3_utility extends CI_DB_utility {
+
+ /**
+ * Export
+ *
+ * @param array $params Preferences
+ * @return mixed
+ */
+ protected function _backup($params = array())
+ {
+ // Not supported
+ return $this->db->display_error('db_unsupported_feature');
+ }
+
+}
diff --git a/system/database/drivers/sqlsrv/index.html b/system/database/drivers/sqlsrv/index.html
index c942a79ce..bcb7cae34 100644
--- a/system/database/drivers/sqlsrv/index.html
+++ b/system/database/drivers/sqlsrv/index.html
@@ -1,4 +1,5 @@
-<html>
+<!DOCTYPE html>
+<html lang="en">
<head>
<title>403 Forbidden</title>
</head>
@@ -7,4 +8,4 @@
<p>Directory access is forbidden.</p>
</body>
-</html> \ No newline at end of file
+</html>
diff --git a/system/database/drivers/sqlsrv/sqlsrv_driver.php b/system/database/drivers/sqlsrv/sqlsrv_driver.php
index 328c8fe7d..787779483 100644
--- a/system/database/drivers/sqlsrv/sqlsrv_driver.php
+++ b/system/database/drivers/sqlsrv/sqlsrv_driver.php
@@ -1,351 +1,281 @@
-<?php if ( ! defined('BASEPATH')) exit('No direct script access allowed');
+<?php
/**
* CodeIgniter
*
- * An open source application development framework for PHP 5.1.6 or newer
+ * An open source application development framework for PHP
*
- * @package CodeIgniter
- * @author ExpressionEngine Dev Team
- * @copyright Copyright (c) 2008 - 2014, EllisLab, Inc.
- * @license http://codeigniter.com/user_guide/license.html
- * @link http://codeigniter.com
- * @since Version 1.0
+ * This content is released under the MIT License (MIT)
+ *
+ * Copyright (c) 2019 - 2022, CodeIgniter Foundation
+ *
+ * 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.
+ *
+ * @package CodeIgniter
+ * @author EllisLab Dev Team
+ * @copyright Copyright (c) 2008 - 2014, EllisLab, Inc. (https://ellislab.com/)
+ * @copyright Copyright (c) 2014 - 2019, British Columbia Institute of Technology (https://bcit.ca/)
+ * @copyright Copyright (c) 2019 - 2022, CodeIgniter Foundation (https://codeigniter.com/)
+ * @license https://opensource.org/licenses/MIT MIT License
+ * @link https://codeigniter.com
+ * @since Version 2.0.3
* @filesource
*/
-
-// ------------------------------------------------------------------------
+defined('BASEPATH') OR exit('No direct script access allowed');
/**
* SQLSRV Database Adapter Class
*
* Note: _DB is an extender class that the app controller
- * creates dynamically based on whether the active record
+ * creates dynamically based on whether the query builder
* class is being used or not.
*
* @package CodeIgniter
* @subpackage Drivers
* @category Database
- * @author ExpressionEngine Dev Team
- * @link http://codeigniter.com/user_guide/database/
+ * @author EllisLab Dev Team
+ * @link https://codeigniter.com/userguide3/database/
*/
class CI_DB_sqlsrv_driver extends CI_DB {
- var $dbdriver = 'sqlsrv';
-
- // The character used for escaping
- var $_escape_char = '';
-
- // clause and character used for LIKE escape sequences
- var $_like_escape_str = " ESCAPE '%s' ";
- var $_like_escape_chr = '!';
-
- /**
- * The syntax to count rows is slightly different across different
- * database engines, so this string appears in each driver and is
- * used for the count_all() and count_all_results() functions.
- */
- var $_count_string = "SELECT COUNT(*) AS ";
- var $_random_keyword = ' ASC'; // not currently supported
-
/**
- * Non-persistent database connection
+ * Database driver
*
- * @access private called by the base class
- * @return resource
+ * @var string
*/
- function db_connect($pooling = false)
- {
- // Check for a UTF-8 charset being passed as CI's default 'utf8'.
- $character_set = (0 === strcasecmp('utf8', $this->char_set)) ? 'UTF-8' : $this->char_set;
-
- $connection = array(
- 'UID' => empty($this->username) ? '' : $this->username,
- 'PWD' => empty($this->password) ? '' : $this->password,
- 'Database' => $this->database,
- 'ConnectionPooling' => $pooling ? 1 : 0,
- 'CharacterSet' => $character_set,
- 'ReturnDatesAsStrings' => 1
- );
-
- // If the username and password are both empty, assume this is a
- // 'Windows Authentication Mode' connection.
- if(empty($connection['UID']) && empty($connection['PWD'])) {
- unset($connection['UID'], $connection['PWD']);
- }
-
- return sqlsrv_connect($this->hostname, $connection);
- }
-
- // --------------------------------------------------------------------
+ public $dbdriver = 'sqlsrv';
/**
- * Persistent database connection
+ * Scrollable flag
*
- * @access private called by the base class
- * @return resource
- */
- function db_pconnect()
- {
- $this->db_connect(TRUE);
- }
-
- // --------------------------------------------------------------------
-
- /**
- * Reconnect
+ * Determines what cursor type to use when executing queries.
*
- * Keep / reestablish the db connection if no queries have been
- * sent for a length of time exceeding the server's idle timeout
+ * FALSE or SQLSRV_CURSOR_FORWARD would increase performance,
+ * but would disable num_rows() (and possibly insert_id())
*
- * @access public
- * @return void
+ * @var mixed
*/
- function reconnect()
- {
- // not implemented in MSSQL
- }
+ public $scrollable;
// --------------------------------------------------------------------
/**
- * Select the database
+ * ORDER BY random keyword
*
- * @access private called by the base class
- * @return resource
+ * @var array
*/
- function db_select()
- {
- return $this->_execute('USE ' . $this->database);
- }
-
- // --------------------------------------------------------------------
+ protected $_random_keyword = array('NEWID()', 'RAND(%d)');
/**
- * Set client character set
+ * Quoted identifier flag
*
- * @access public
- * @param string
- * @param string
- * @return resource
+ * Whether to use SQL-92 standard quoted identifier
+ * (double quotes) or brackets for identifier escaping.
+ *
+ * @var bool
*/
- function db_set_charset($charset, $collation)
- {
- // @todo - add support if needed
- return TRUE;
- }
+ protected $_quoted_identifier = TRUE;
// --------------------------------------------------------------------
/**
- * Execute the query
+ * Class constructor
*
- * @access private called by the base class
- * @param string an SQL query
- * @return resource
+ * @param array $params
+ * @return void
*/
- function _execute($sql)
+ public function __construct($params)
{
- $sql = $this->_prep_query($sql);
- return sqlsrv_query($this->conn_id, $sql, null, array(
- 'Scrollable' => SQLSRV_CURSOR_STATIC,
- 'SendStreamParamsAtExec' => true
- ));
+ parent::__construct($params);
+
+ // This is only supported as of SQLSRV 3.0
+ if ($this->scrollable === NULL)
+ {
+ $this->scrollable = defined('SQLSRV_CURSOR_CLIENT_BUFFERED')
+ ? SQLSRV_CURSOR_CLIENT_BUFFERED
+ : FALSE;
+ }
}
// --------------------------------------------------------------------
/**
- * Prep the query
+ * Database connection
*
- * If needed, each database adapter can prep the query string
- *
- * @access private called by execute()
- * @param string an SQL query
- * @return string
+ * @param bool $pooling
+ * @return resource
*/
- function _prep_query($sql)
+ public function db_connect($pooling = FALSE)
{
- return $sql;
- }
+ $charset = in_array(strtolower($this->char_set), array('utf-8', 'utf8'), TRUE)
+ ? 'UTF-8' : SQLSRV_ENC_CHAR;
- // --------------------------------------------------------------------
+ $connection = array(
+ 'UID' => empty($this->username) ? '' : $this->username,
+ 'PWD' => empty($this->password) ? '' : $this->password,
+ 'Database' => $this->database,
+ 'ConnectionPooling' => ($pooling === TRUE) ? 1 : 0,
+ 'CharacterSet' => $charset,
+ 'Encrypt' => ($this->encrypt === TRUE) ? 1 : 0,
+ 'ReturnDatesAsStrings' => 1
+ );
- /**
- * Begin Transaction
- *
- * @access public
- * @return bool
- */
- function trans_begin($test_mode = FALSE)
- {
- if ( ! $this->trans_enabled)
+ // If the username and password are both empty, assume this is a
+ // 'Windows Authentication Mode' connection.
+ if (empty($connection['UID']) && empty($connection['PWD']))
{
- return TRUE;
+ unset($connection['UID'], $connection['PWD']);
}
- // When transactions are nested we only begin/commit/rollback the outermost ones
- if ($this->_trans_depth > 0)
+ if (FALSE !== ($this->conn_id = sqlsrv_connect($this->hostname, $connection)))
{
- return TRUE;
+ // Determine how identifiers are escaped
+ $query = $this->query('SELECT CASE WHEN (@@OPTIONS | 256) = @@OPTIONS THEN 1 ELSE 0 END AS qi');
+ $query = $query->row_array();
+ $this->_quoted_identifier = empty($query) ? FALSE : (bool) $query['qi'];
+ $this->_escape_char = ($this->_quoted_identifier) ? '"' : array('[', ']');
}
- // Reset the transaction failure flag.
- // If the $test_mode flag is set to TRUE transactions will be rolled back
- // even if the queries produce a successful result.
- $this->_trans_failure = ($test_mode === TRUE) ? TRUE : FALSE;
-
- return sqlsrv_begin_transaction($this->conn_id);
+ return $this->conn_id;
}
// --------------------------------------------------------------------
/**
- * Commit Transaction
+ * Select the database
*
- * @access public
+ * @param string $database
* @return bool
*/
- function trans_commit()
+ public function db_select($database = '')
{
- if ( ! $this->trans_enabled)
+ if ($database === '')
{
- return TRUE;
+ $database = $this->database;
}
- // When transactions are nested we only begin/commit/rollback the outermost ones
- if ($this->_trans_depth > 0)
+ if ($this->_execute('USE '.$this->escape_identifiers($database)))
{
+ $this->database = $database;
+ $this->data_cache = array();
return TRUE;
}
- return sqlsrv_commit($this->conn_id);
+ return FALSE;
}
// --------------------------------------------------------------------
/**
- * Rollback Transaction
+ * Execute the query
*
- * @access public
- * @return bool
+ * @param string $sql an SQL query
+ * @return resource
*/
- function trans_rollback()
+ protected function _execute($sql)
{
- if ( ! $this->trans_enabled)
- {
- return TRUE;
- }
-
- // When transactions are nested we only begin/commit/rollback the outermost ones
- if ($this->_trans_depth > 0)
- {
- return TRUE;
- }
-
- return sqlsrv_rollback($this->conn_id);
+ return ($this->scrollable === FALSE OR $this->is_write_type($sql))
+ ? sqlsrv_query($this->conn_id, $sql)
+ : sqlsrv_query($this->conn_id, $sql, NULL, array('Scrollable' => $this->scrollable));
}
// --------------------------------------------------------------------
/**
- * Escape String
+ * Begin Transaction
*
- * @access public
- * @param string
- * @param bool whether or not the string will be used in a LIKE condition
- * @return string
+ * @return bool
*/
- function escape_str($str, $like = FALSE)
+ protected function _trans_begin()
{
- // Escape single quotes
- return str_replace("'", "''", $str);
+ return sqlsrv_begin_transaction($this->conn_id);
}
// --------------------------------------------------------------------
/**
- * Affected Rows
+ * Commit Transaction
*
- * @access public
- * @return integer
+ * @return bool
*/
- function affected_rows()
+ protected function _trans_commit()
{
- return @sqlrv_rows_affected($this->conn_id);
+ return sqlsrv_commit($this->conn_id);
}
// --------------------------------------------------------------------
/**
- * Insert ID
- *
- * Returns the last id created in the Identity column.
- *
- * @access public
- * @return integer
- */
- function insert_id()
+ * Rollback Transaction
+ *
+ * @return bool
+ */
+ protected function _trans_rollback()
{
- return $this->query('select @@IDENTITY as insert_id')->row('insert_id');
+ return sqlsrv_rollback($this->conn_id);
}
// --------------------------------------------------------------------
/**
- * Parse major version
- *
- * Grabs the major version number from the
- * database server version string passed in.
- *
- * @access private
- * @param string $version
- * @return int16 major version number
- */
- function _parse_major_version($version)
+ * Affected Rows
+ *
+ * @return int
+ */
+ public function affected_rows()
{
- preg_match('/([0-9]+)\.([0-9]+)\.([0-9]+)/', $version, $ver_info);
- return $ver_info[1]; // return the major version b/c that's all we're interested in.
+ return sqlsrv_rows_affected($this->result_id);
}
// --------------------------------------------------------------------
/**
- * Version number query string
- *
- * @access public
- * @return string
- */
- function _version()
+ * Insert ID
+ *
+ * Returns the last id created in the Identity column.
+ *
+ * @return string
+ */
+ public function insert_id()
{
- $info = sqlsrv_server_info($this->conn_id);
- return sprintf("select '%s' as ver", $info['SQLServerVersion']);
+ return $this->query('SELECT SCOPE_IDENTITY() AS insert_id')->row()->insert_id;
}
// --------------------------------------------------------------------
/**
- * "Count All" query
- *
- * Generates a platform-specific query string that counts all records in
- * the specified database
+ * Database version number
*
- * @access public
- * @param string
* @return string
*/
- function count_all($table = '')
+ public function version()
{
- if ($table == '')
- return '0';
-
- $query = $this->query("SELECT COUNT(*) AS numrows FROM " . $this->dbprefix . $table);
-
- if ($query->num_rows() == 0)
- return '0';
-
- $row = $query->row();
- $this->_reset_select();
- return $row->numrows;
+ if (isset($this->data_cache['version']))
+ {
+ return $this->data_cache['version'];
+ }
+
+ if ( ! $this->conn_id OR ($info = sqlsrv_server_info($this->conn_id)) === FALSE)
+ {
+ return FALSE;
+ }
+
+ return $this->data_cache['version'] = $info['SQLServerVersion'];
}
// --------------------------------------------------------------------
@@ -355,13 +285,22 @@ class CI_DB_sqlsrv_driver extends CI_DB {
*
* Generates a platform-specific query string so that the table names can be fetched
*
- * @access private
- * @param boolean
- * @return string
+ * @param bool
+ * @return string $prefix_limit
*/
- function _list_tables($prefix_limit = FALSE)
+ protected function _list_tables($prefix_limit = FALSE)
{
- return "SELECT name FROM sysobjects WHERE type = 'U' ORDER BY name";
+ $sql = 'SELECT '.$this->escape_identifiers('name')
+ .' FROM '.$this->escape_identifiers('sysobjects')
+ .' WHERE '.$this->escape_identifiers('type')." = 'U'";
+
+ if ($prefix_limit === TRUE && $this->dbprefix !== '')
+ {
+ $sql .= ' AND '.$this->escape_identifiers('name')." LIKE '".$this->escape_like_str($this->dbprefix)."%' "
+ .sprintf($this->_escape_like_str, $this->_escape_like_chr);
+ }
+
+ return $sql.' ORDER BY '.$this->escape_identifiers('name');
}
// --------------------------------------------------------------------
@@ -371,210 +310,223 @@ class CI_DB_sqlsrv_driver extends CI_DB {
*
* Generates a platform-specific query string so that the column names can be fetched
*
- * @access private
- * @param string the table name
+ * @param string $table
* @return string
*/
- function _list_columns($table = '')
+ protected function _list_columns($table = '')
{
- return "SELECT * FROM INFORMATION_SCHEMA.Columns WHERE TABLE_NAME = '".$this->_escape_table($table)."'";
+ return 'SELECT COLUMN_NAME
+ FROM INFORMATION_SCHEMA.Columns
+ WHERE UPPER(TABLE_NAME) = '.$this->escape(strtoupper($table));
}
// --------------------------------------------------------------------
/**
- * Field data query
- *
- * Generates a platform-specific query so that the column data can be retrieved
+ * Returns an object with field data
*
- * @access public
- * @param string the table name
- * @return object
+ * @param string $table
+ * @return array
*/
- function _field_data($table)
+ public function field_data($table)
{
- return "SELECT TOP 1 * FROM " . $this->_escape_table($table);
- }
-
- // --------------------------------------------------------------------
+ $sql = 'SELECT COLUMN_NAME, DATA_TYPE, CHARACTER_MAXIMUM_LENGTH, NUMERIC_PRECISION, COLUMN_DEFAULT
+ FROM INFORMATION_SCHEMA.Columns
+ WHERE UPPER(TABLE_NAME) = '.$this->escape(strtoupper($table));
- /**
- * The error message string
- *
- * @access private
- * @return string
- */
- function _error_message()
- {
- $error = array_shift(sqlsrv_errors());
- return !empty($error['message']) ? $error['message'] : null;
- }
+ if (($query = $this->query($sql)) === FALSE)
+ {
+ return FALSE;
+ }
+ $query = $query->result_object();
- // --------------------------------------------------------------------
+ $retval = array();
+ for ($i = 0, $c = count($query); $i < $c; $i++)
+ {
+ $retval[$i] = new stdClass();
+ $retval[$i]->name = $query[$i]->COLUMN_NAME;
+ $retval[$i]->type = $query[$i]->DATA_TYPE;
+ $retval[$i]->max_length = ($query[$i]->CHARACTER_MAXIMUM_LENGTH > 0) ? $query[$i]->CHARACTER_MAXIMUM_LENGTH : $query[$i]->NUMERIC_PRECISION;
+ $retval[$i]->default = $query[$i]->COLUMN_DEFAULT;
+ }
- /**
- * The error message number
- *
- * @access private
- * @return integer
- */
- function _error_number()
- {
- $error = array_shift(sqlsrv_errors());
- return isset($error['SQLSTATE']) ? $error['SQLSTATE'] : null;
+ return $retval;
}
// --------------------------------------------------------------------
/**
- * Escape Table Name
+ * Error
*
- * This function adds backticks if the table name has a period
- * in it. Some DBs will get cranky unless periods are escaped
+ * Returns an array containing code and message of the last
+ * database error that has occurred.
*
- * @access private
- * @param string the table name
- * @return string
+ * @return array
*/
- function _escape_table($table)
+ public function error()
{
- return $table;
- }
-
+ $error = array('code' => '00000', 'message' => '');
+ $sqlsrv_errors = sqlsrv_errors(SQLSRV_ERR_ERRORS);
- /**
- * Escape the SQL Identifiers
- *
- * This function escapes column and table names
- *
- * @access private
- * @param string
- * @return string
- */
- function _escape_identifiers($item)
- {
- return $item;
- }
+ if ( ! is_array($sqlsrv_errors))
+ {
+ return $error;
+ }
- // --------------------------------------------------------------------
+ $sqlsrv_error = array_shift($sqlsrv_errors);
+ if (isset($sqlsrv_error['SQLSTATE']))
+ {
+ $error['code'] = isset($sqlsrv_error['code']) ? $sqlsrv_error['SQLSTATE'].'/'.$sqlsrv_error['code'] : $sqlsrv_error['SQLSTATE'];
+ }
+ elseif (isset($sqlsrv_error['code']))
+ {
+ $error['code'] = $sqlsrv_error['code'];
+ }
- /**
- * From Tables
- *
- * This function implicitly groups FROM tables so there is no confusion
- * about operator precedence in harmony with SQL standards
- *
- * @access public
- * @param type
- * @return type
- */
- function _from_tables($tables)
- {
- if ( ! is_array($tables))
+ if (isset($sqlsrv_error['message']))
{
- $tables = array($tables);
+ $error['message'] = $sqlsrv_error['message'];
}
- return implode(', ', $tables);
+ return $error;
}
// --------------------------------------------------------------------
/**
- * Insert statement
+ * Update statement
*
- * Generates a platform-specific insert string from the supplied data
+ * Generates a platform-specific update string from the supplied data
*
- * @access public
- * @param string the table name
- * @param array the insert keys
- * @param array the insert values
+ * @param string $table
+ * @param array $values
* @return string
*/
- function _insert($table, $keys, $values)
- {
- return "INSERT INTO ".$this->_escape_table($table)." (".implode(', ', $keys).") VALUES (".implode(', ', $values).")";
+ protected function _update($table, $values)
+ {
+ $this->qb_limit = FALSE;
+ $this->qb_orderby = array();
+ return parent::_update($table, $values);
}
// --------------------------------------------------------------------
/**
- * Update statement
+ * Truncate statement
*
- * Generates a platform-specific update string from the supplied data
+ * Generates a platform-specific truncate string from the supplied data
*
- * @access public
- * @param string the table name
- * @param array the update data
- * @param array the where clause
- * @param array the orderby clause
- * @param array the limit clause
+ * If the database does not support the TRUNCATE statement,
+ * then this method maps to 'DELETE FROM table'
+ *
+ * @param string $table
* @return string
*/
- function _update($table, $values, $where)
+ protected function _truncate($table)
{
- foreach($values as $key => $val)
- {
- $valstr[] = $key." = ".$val;
- }
-
- return "UPDATE ".$this->_escape_table($table)." SET ".implode(', ', $valstr)." WHERE ".implode(" ", $where);
+ return 'TRUNCATE TABLE '.$table;
}
-
+
// --------------------------------------------------------------------
/**
- * Truncate statement
+ * Delete statement
*
- * Generates a platform-specific truncate string from the supplied data
- * If the database does not support the truncate() command
- * This function maps to "DELETE FROM table"
+ * Generates a platform-specific delete string from the supplied data
*
- * @access public
- * @param string the table name
+ * @param string $table
* @return string
*/
- function _truncate($table)
+ protected function _delete($table)
{
- return "TRUNCATE TABLE ".$table;
+ if ($this->qb_limit)
+ {
+ return 'WITH ci_delete AS (SELECT TOP '.$this->qb_limit.' * FROM '.$table.$this->_compile_wh('qb_where').') DELETE FROM ci_delete';
+ }
+
+ return parent::_delete($table);
}
// --------------------------------------------------------------------
/**
- * Delete statement
+ * LIMIT
*
- * Generates a platform-specific delete string from the supplied data
+ * Generates a platform-specific LIMIT clause
*
- * @access public
- * @param string the table name
- * @param array the where clause
- * @param string the limit clause
+ * @param string $sql SQL Query
* @return string
*/
- function _delete($table, $where)
+ protected function _limit($sql)
{
- return "DELETE FROM ".$this->_escape_table($table)." WHERE ".implode(" ", $where);
+ // As of SQL Server 2012 (11.0.*) OFFSET is supported
+ if (version_compare($this->version(), '11', '>='))
+ {
+ // SQL Server OFFSET-FETCH can be used only with the ORDER BY clause
+ empty($this->qb_orderby) && $sql .= ' ORDER BY 1';
+
+ return $sql.' OFFSET '.(int) $this->qb_offset.' ROWS FETCH NEXT '.$this->qb_limit.' ROWS ONLY';
+ }
+
+ $limit = $this->qb_offset + $this->qb_limit;
+
+ // An ORDER BY clause is required for ROW_NUMBER() to work
+ if ($this->qb_offset && ! empty($this->qb_orderby))
+ {
+ $orderby = $this->_compile_order_by();
+
+ // We have to strip the ORDER BY clause
+ $sql = trim(substr($sql, 0, strrpos($sql, $orderby)));
+
+ // Get the fields to select from our subquery, so that we can avoid CI_rownum appearing in the actual results
+ if (count($this->qb_select) === 0 OR strpos(implode(',', $this->qb_select), '*') !== FALSE)
+ {
+ $select = '*'; // Inevitable
+ }
+ else
+ {
+ // Use only field names and their aliases, everything else is out of our scope.
+ $select = array();
+ $field_regexp = ($this->_quoted_identifier)
+ ? '("[^\"]+")' : '(\[[^\]]+\])';
+ for ($i = 0, $c = count($this->qb_select); $i < $c; $i++)
+ {
+ $select[] = preg_match('/(?:\s|\.)'.$field_regexp.'$/i', $this->qb_select[$i], $m)
+ ? $m[1] : $this->qb_select[$i];
+ }
+ $select = implode(', ', $select);
+ }
+
+ return 'SELECT '.$select." FROM (\n\n"
+ .preg_replace('/^(SELECT( DISTINCT)?)/i', '\\1 ROW_NUMBER() OVER('.trim($orderby).') AS '.$this->escape_identifiers('CI_rownum').', ', $sql)
+ ."\n\n) ".$this->escape_identifiers('CI_subquery')
+ ."\nWHERE ".$this->escape_identifiers('CI_rownum').' BETWEEN '.($this->qb_offset + 1).' AND '.$limit;
+ }
+
+ return preg_replace('/(^\SELECT (DISTINCT)?)/i','\\1 TOP '.$limit.' ', $sql);
}
// --------------------------------------------------------------------
/**
- * Limit string
+ * Insert batch statement
*
- * Generates a platform-specific LIMIT clause
+ * Generates a platform-specific insert string from the supplied data.
*
- * @access public
- * @param string the sql query string
- * @param integer the number of rows to limit the query to
- * @param integer the offset value
- * @return string
+ * @param string $table Table name
+ * @param array $keys INSERT keys
+ * @param array $values INSERT values
+ * @return string|bool
*/
- function _limit($sql, $limit, $offset)
+ protected function _insert_batch($table, $keys, $values)
{
- $i = $limit + $offset;
-
- return preg_replace('/(^\SELECT (DISTINCT)?)/i','\\1 TOP '.$i.' ', $sql);
+ // Multiple-value inserts are only supported as of SQL Server 2008
+ if (version_compare($this->version(), '10', '>='))
+ {
+ return parent::_insert_batch($table, $keys, $values);
+ }
+
+ return ($this->db_debug) ? $this->display_error('db_unsupported_feature') : FALSE;
}
// --------------------------------------------------------------------
@@ -582,18 +534,11 @@ class CI_DB_sqlsrv_driver extends CI_DB {
/**
* Close DB Connection
*
- * @access public
- * @param resource
* @return void
*/
- function _close($conn_id)
+ protected function _close()
{
- @sqlsrv_close($conn_id);
+ sqlsrv_close($this->conn_id);
}
}
-
-
-
-/* End of file mssql_driver.php */
-/* Location: ./system/database/drivers/mssql/mssql_driver.php */ \ No newline at end of file
diff --git a/system/database/drivers/sqlsrv/sqlsrv_forge.php b/system/database/drivers/sqlsrv/sqlsrv_forge.php
index 8f879d2f6..dca7f75b2 100644
--- a/system/database/drivers/sqlsrv/sqlsrv_forge.php
+++ b/system/database/drivers/sqlsrv/sqlsrv_forge.php
@@ -1,245 +1,150 @@
-<?php if ( ! defined('BASEPATH')) exit('No direct script access allowed');
+<?php
/**
* CodeIgniter
*
- * An open source application development framework for PHP 5.1.6 or newer
+ * An open source application development framework for PHP
*
- * @package CodeIgniter
- * @author ExpressionEngine Dev Team
- * @copyright Copyright (c) 2008 - 2014, EllisLab, Inc.
- * @license http://codeigniter.com/user_guide/license.html
- * @link http://codeigniter.com
- * @since Version 1.0
+ * This content is released under the MIT License (MIT)
+ *
+ * Copyright (c) 2019 - 2022, CodeIgniter Foundation
+ *
+ * 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.
+ *
+ * @package CodeIgniter
+ * @author EllisLab Dev Team
+ * @copyright Copyright (c) 2008 - 2014, EllisLab, Inc. (https://ellislab.com/)
+ * @copyright Copyright (c) 2014 - 2019, British Columbia Institute of Technology (https://bcit.ca/)
+ * @copyright Copyright (c) 2019 - 2022, CodeIgniter Foundation (https://codeigniter.com/)
+ * @license https://opensource.org/licenses/MIT MIT License
+ * @link https://codeigniter.com
+ * @since Version 2.0.3
* @filesource
*/
-
-// ------------------------------------------------------------------------
+defined('BASEPATH') OR exit('No direct script access allowed');
/**
* SQLSRV Forge Class
*
* @category Database
- * @author ExpressionEngine Dev Team
- * @link http://codeigniter.com/user_guide/database/
+ * @author EllisLab Dev Team
+ * @link https://codeigniter.com/userguide3/database/
*/
class CI_DB_sqlsrv_forge extends CI_DB_forge {
/**
- * Create database
+ * CREATE TABLE IF statement
*
- * @access private
- * @param string the database name
- * @return bool
+ * @var string
*/
- function _create_database($name)
- {
- return "CREATE DATABASE ".$name;
- }
-
- // --------------------------------------------------------------------
+ protected $_create_table_if = "IF NOT EXISTS (SELECT * FROM sysobjects WHERE ID = object_id(N'%s') AND OBJECTPROPERTY(id, N'IsUserTable') = 1)\nCREATE TABLE";
/**
- * Drop database
+ * DROP TABLE IF statement
*
- * @access private
- * @param string the database name
- * @return bool
+ * @var string
*/
- function _drop_database($name)
- {
- return "DROP DATABASE ".$name;
- }
-
- // --------------------------------------------------------------------
+ protected $_drop_table_if = "IF EXISTS (SELECT * FROM sysobjects WHERE ID = object_id(N'%s') AND OBJECTPROPERTY(id, N'IsUserTable') = 1)\nDROP TABLE";
/**
- * Drop Table
+ * UNSIGNED support
*
- * @access private
- * @return bool
+ * @var array
*/
- function _drop_table($table)
- {
- return "IF (EXISTS (SELECT * FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_SCHEMA = 'dbo' AND TABLE_NAME = '"
- .$table."')) DROP TABLE [dbo].[".$table."]";
- }
+ protected $_unsigned = array(
+ 'TINYINT' => 'SMALLINT',
+ 'SMALLINT' => 'INT',
+ 'INT' => 'BIGINT',
+ 'REAL' => 'FLOAT'
+ );
// --------------------------------------------------------------------
/**
- * Create Table
+ * ALTER TABLE
*
- * @access private
- * @param string the table name
- * @param array the fields
- * @param mixed primary key(s)
- * @param mixed key(s)
- * @param boolean should 'IF NOT EXISTS' be added to the SQL
- * @return bool
+ * @param string $alter_type ALTER type
+ * @param string $table Table name
+ * @param mixed $field Column definition
+ * @return string|string[]
*/
- function _create_table($table, $fields, $primary_keys, $keys, $if_not_exists)
+ protected function _alter_table($alter_type, $table, $field)
{
- $sql = '';
- if ($if_not_exists === TRUE)
- {
- $sql = "IF (NOT EXISTS (SELECT * FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_SCHEMA = 'dbo' AND TABLE_NAME = ";
- }
- $sql .= $this->db->_escape_identifiers($table).")) CREATE TABLE ".$this->db->_escape_identifiers($table)." (";
- $current_field_count = 0;
-
- foreach ($fields as $field=>$attributes)
- {
- // Numeric field names aren't allowed in databases, so if the key is
- // numeric, we know it was assigned by PHP and the developer manually
- // entered the field information, so we'll simply add it to the list
- if (is_numeric($field))
- {
- $sql .= "\n\t$attributes";
- }
- else
- {
- $attributes = array_change_key_case($attributes, CASE_UPPER);
-
- $sql .= "\n\t".$this->db->_protect_identifiers($field);
-
- $sql .= ' '.$attributes['TYPE'];
-
- if (array_key_exists('CONSTRAINT', $attributes))
- {
- $sql .= '('.$attributes['CONSTRAINT'].')';
- }
-
- if (array_key_exists('UNSIGNED', $attributes) && $attributes['UNSIGNED'] === TRUE)
- {
- $sql .= ' UNSIGNED';
- }
-
- if (array_key_exists('DEFAULT', $attributes))
- {
- $sql .= ' DEFAULT \''.$attributes['DEFAULT'].'\'';
- }
-
- if (array_key_exists('NULL', $attributes) && $attributes['NULL'] === TRUE)
- {
- $sql .= ' NULL';
- }
- else
- {
- $sql .= ' NOT NULL';
- }
-
- if (array_key_exists('AUTO_INCREMENT', $attributes) && $attributes['AUTO_INCREMENT'] === TRUE)
- {
- $sql .= ' IDENTITY(1,1)';
- }
- }
-
- // don't add a comma on the end of the last field
- if (++$current_field_count < count($fields))
- {
- $sql .= ',';
- }
- }
-
- if (count($primary_keys) > 0)
+ if (in_array($alter_type, array('ADD', 'DROP'), TRUE))
{
- $primary_keys = $this->db->_protect_identifiers($primary_keys);
- $sql .= ",\n\tPRIMARY KEY (" . implode(', ', $primary_keys) . ")";
+ return parent::_alter_table($alter_type, $table, $field);
}
- if (is_array($keys) && count($keys) > 0)
+ $sql = 'ALTER TABLE '.$this->db->escape_identifiers($table).' ALTER COLUMN ';
+ $sqls = array();
+ for ($i = 0, $c = count($field); $i < $c; $i++)
{
- foreach ($keys as $key)
- {
- if (is_array($key))
- {
- $key = $this->db->_protect_identifiers($key);
- }
- else
- {
- $key = array($this->db->_protect_identifiers($key));
- }
-
- $sql .= ",\n\tFOREIGN KEY (" . implode(', ', $key) . ")";
- }
+ $sqls[] = $sql.$this->_process_column($field[$i]);
}
- $sql .= "\n)";
-
- return $sql;
+ return $sqls;
}
// --------------------------------------------------------------------
/**
- * Alter table query
+ * Field attribute TYPE
*
- * Generates a platform-specific query so that a table can be altered
- * Called by add_column(), drop_column(), and column_alter(),
+ * Performs a data type mapping between different databases.
*
- * @access private
- * @param string the ALTER type (ADD, DROP, CHANGE)
- * @param string the column name
- * @param string the table name
- * @param string the column definition
- * @param string the default value
- * @param boolean should 'NOT NULL' be added
- * @param string the field after which we should add the new field
- * @return object
+ * @param array &$attributes
+ * @return void
*/
- function _alter_table($alter_type, $table, $column_name, $column_definition = '', $default_value = '', $null = '', $after_field = '')
+ protected function _attr_type(&$attributes)
{
- $sql = 'ALTER TABLE '.$this->db->_protect_identifiers($table)." $alter_type ".$this->db->_protect_identifiers($column_name);
-
- // DROP has everything it needs now.
- if ($alter_type == 'DROP')
- {
- return $sql;
- }
-
- $sql .= " $column_definition";
-
- if ($default_value != '')
- {
- $sql .= " DEFAULT \"$default_value\"";
- }
-
- if ($null === NULL)
- {
- $sql .= ' NULL';
- }
- else
+ if (isset($attributes['CONSTRAINT']) && strpos($attributes['TYPE'], 'INT') !== FALSE)
{
- $sql .= ' NOT NULL';
+ unset($attributes['CONSTRAINT']);
}
- if ($after_field != '')
+ switch (strtoupper($attributes['TYPE']))
{
- $sql .= ' AFTER ' . $this->db->_protect_identifiers($after_field);
+ case 'MEDIUMINT':
+ $attributes['TYPE'] = 'INTEGER';
+ $attributes['UNSIGNED'] = FALSE;
+ return;
+ case 'INTEGER':
+ $attributes['TYPE'] = 'INT';
+ return;
+ default: return;
}
-
- return $sql;
-
}
// --------------------------------------------------------------------
/**
- * Rename a table
- *
- * Generates a platform-specific query so that a table can be renamed
+ * Field attribute AUTO_INCREMENT
*
- * @access private
- * @param string the old table name
- * @param string the new table name
- * @return string
+ * @param array &$attributes
+ * @param array &$field
+ * @return void
*/
- function _rename_table($table_name, $new_table_name)
+ protected function _attr_auto_increment(&$attributes, &$field)
{
- return 'EXEC sp_rename '.$this->db->_protect_identifiers($table_name).", ".$this->db->_protect_identifiers($new_table_name);
+ if ( ! empty($attributes['AUTO_INCREMENT']) && $attributes['AUTO_INCREMENT'] === TRUE && stripos($field['type'], 'int') !== FALSE)
+ {
+ $field['auto_increment'] = ' IDENTITY(1,1)';
+ }
}
}
-
-/* End of file sqlsrv_forge.php */
-/* Location: ./system/database/drivers/sqlsrv/sqlsrv_forge.php */ \ No newline at end of file
diff --git a/system/database/drivers/sqlsrv/sqlsrv_result.php b/system/database/drivers/sqlsrv/sqlsrv_result.php
index a5c972669..a3a582b5c 100644
--- a/system/database/drivers/sqlsrv/sqlsrv_result.php
+++ b/system/database/drivers/sqlsrv/sqlsrv_result.php
@@ -1,19 +1,42 @@
-<?php if ( ! defined('BASEPATH')) exit('No direct script access allowed');
+<?php
/**
* CodeIgniter
*
- * An open source application development framework for PHP 5.1.6 or newer
+ * An open source application development framework for PHP
*
- * @package CodeIgniter
- * @author ExpressionEngine Dev Team
- * @copyright Copyright (c) 2008 - 2014, EllisLab, Inc.
- * @license http://codeigniter.com/user_guide/license.html
- * @link http://codeigniter.com
- * @since Version 1.0
+ * This content is released under the MIT License (MIT)
+ *
+ * Copyright (c) 2019 - 2022, CodeIgniter Foundation
+ *
+ * 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.
+ *
+ * @package CodeIgniter
+ * @author EllisLab Dev Team
+ * @copyright Copyright (c) 2008 - 2014, EllisLab, Inc. (https://ellislab.com/)
+ * @copyright Copyright (c) 2014 - 2019, British Columbia Institute of Technology (https://bcit.ca/)
+ * @copyright Copyright (c) 2019 - 2022, CodeIgniter Foundation (https://codeigniter.com/)
+ * @license https://opensource.org/licenses/MIT MIT License
+ * @link https://codeigniter.com
+ * @since Version 2.0.3
* @filesource
*/
-
-// ------------------------------------------------------------------------
+defined('BASEPATH') OR exit('No direct script access allowed');
/**
* SQLSRV Result Class
@@ -21,20 +44,51 @@
* This class extends the parent result class: CI_DB_result
*
* @category Database
- * @author ExpressionEngine Dev Team
- * @link http://codeigniter.com/user_guide/database/
+ * @author EllisLab Dev Team
+ * @link https://codeigniter.com/userguide3/database/
*/
class CI_DB_sqlsrv_result extends CI_DB_result {
/**
+ * Scrollable flag
+ *
+ * @var mixed
+ */
+ public $scrollable;
+
+ // --------------------------------------------------------------------
+
+ /**
+ * Constructor
+ *
+ * @param object $driver_object
+ * @return void
+ */
+ public function __construct(&$driver_object)
+ {
+ parent::__construct($driver_object);
+
+ $this->scrollable = $driver_object->scrollable;
+ }
+
+ // --------------------------------------------------------------------
+
+ /**
* Number of rows in the result set
*
- * @access public
- * @return integer
+ * @return int
*/
- function num_rows()
+ public function num_rows()
{
- return @sqlsrv_num_rows($this->result_id);
+ // sqlsrv_num_rows() doesn't work with the FORWARD and DYNAMIC cursors (FALSE is the same as FORWARD)
+ if ( ! in_array($this->scrollable, array(FALSE, SQLSRV_CURSOR_FORWARD, SQLSRV_CURSOR_DYNAMIC), TRUE))
+ {
+ return parent::num_rows();
+ }
+
+ return is_int($this->num_rows)
+ ? $this->num_rows
+ : $this->num_rows = sqlsrv_num_rows($this->result_id);
}
// --------------------------------------------------------------------
@@ -42,10 +96,9 @@ class CI_DB_sqlsrv_result extends CI_DB_result {
/**
* Number of fields in the result set
*
- * @access public
- * @return integer
+ * @return int
*/
- function num_fields()
+ public function num_fields()
{
return @sqlsrv_num_fields($this->result_id);
}
@@ -57,17 +110,16 @@ class CI_DB_sqlsrv_result extends CI_DB_result {
*
* Generates an array of column names
*
- * @access public
* @return array
*/
- function list_fields()
+ public function list_fields()
{
$field_names = array();
- foreach(sqlsrv_field_metadata($this->result_id) as $offset => $field)
+ foreach (sqlsrv_field_metadata($this->result_id) as $offset => $field)
{
$field_names[] = $field['Name'];
}
-
+
return $field_names;
}
@@ -78,24 +130,19 @@ class CI_DB_sqlsrv_result extends CI_DB_result {
*
* Generates an array of objects containing field meta-data
*
- * @access public
* @return array
*/
- function field_data()
+ public function field_data()
{
$retval = array();
- foreach(sqlsrv_field_metadata($this->result_id) as $offset => $field)
+ foreach (sqlsrv_field_metadata($this->result_id) as $i => $field)
{
- $F = new stdClass();
- $F->name = $field['Name'];
- $F->type = $field['Type'];
- $F->max_length = $field['Size'];
- $F->primary_key = 0;
- $F->default = '';
-
- $retval[] = $F;
+ $retval[$i] = new stdClass();
+ $retval[$i]->name = $field['Name'];
+ $retval[$i]->type = $field['Type'];
+ $retval[$i]->max_length = $field['Size'];
}
-
+
return $retval;
}
@@ -104,9 +151,9 @@ class CI_DB_sqlsrv_result extends CI_DB_result {
/**
* Free the result
*
- * @return null
+ * @return void
*/
- function free_result()
+ public function free_result()
{
if (is_resource($this->result_id))
{
@@ -118,31 +165,13 @@ class CI_DB_sqlsrv_result extends CI_DB_result {
// --------------------------------------------------------------------
/**
- * Data Seek
- *
- * Moves the internal pointer to the desired offset. We call
- * this internally before fetching results to make sure the
- * result set starts at zero
- *
- * @access private
- * @return array
- */
- function _data_seek($n = 0)
- {
- // Not implemented
- }
-
- // --------------------------------------------------------------------
-
- /**
* Result - associative array
*
* Returns the result set as an array
*
- * @access private
* @return array
*/
- function _fetch_assoc()
+ protected function _fetch_assoc()
{
return sqlsrv_fetch_array($this->result_id, SQLSRV_FETCH_ASSOC);
}
@@ -154,16 +183,12 @@ class CI_DB_sqlsrv_result extends CI_DB_result {
*
* Returns the result set as an object
*
- * @access private
+ * @param string $class_name
* @return object
*/
- function _fetch_object()
+ protected function _fetch_object($class_name = 'stdClass')
{
- return sqlsrv_fetch_object($this->result_id);
+ return sqlsrv_fetch_object($this->result_id, $class_name);
}
}
-
-
-/* End of file mssql_result.php */
-/* Location: ./system/database/drivers/mssql/mssql_result.php */ \ No newline at end of file
diff --git a/system/database/drivers/sqlsrv/sqlsrv_utility.php b/system/database/drivers/sqlsrv/sqlsrv_utility.php
index 0004bfdd2..e51bc7236 100644
--- a/system/database/drivers/sqlsrv/sqlsrv_utility.php
+++ b/system/database/drivers/sqlsrv/sqlsrv_utility.php
@@ -1,88 +1,78 @@
-<?php if ( ! defined('BASEPATH')) exit('No direct script access allowed');
+<?php
/**
* CodeIgniter
*
- * An open source application development framework for PHP 5.1.6 or newer
+ * An open source application development framework for PHP
*
- * @package CodeIgniter
- * @author ExpressionEngine Dev Team
- * @copyright Copyright (c) 2008 - 2014, EllisLab, Inc.
- * @license http://codeigniter.com/user_guide/license.html
- * @link http://codeigniter.com
- * @since Version 1.0
+ * This content is released under the MIT License (MIT)
+ *
+ * Copyright (c) 2019 - 2022, CodeIgniter Foundation
+ *
+ * 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.
+ *
+ * @package CodeIgniter
+ * @author EllisLab Dev Team
+ * @copyright Copyright (c) 2008 - 2014, EllisLab, Inc. (https://ellislab.com/)
+ * @copyright Copyright (c) 2014 - 2019, British Columbia Institute of Technology (https://bcit.ca/)
+ * @copyright Copyright (c) 2019 - 2022, CodeIgniter Foundation (https://codeigniter.com/)
+ * @license https://opensource.org/licenses/MIT MIT License
+ * @link https://codeigniter.com
+ * @since Version 2.0.3
* @filesource
*/
-
-// ------------------------------------------------------------------------
+defined('BASEPATH') OR exit('No direct script access allowed');
/**
* SQLSRV Utility Class
*
* @category Database
- * @author ExpressionEngine Dev Team
- * @link http://codeigniter.com/user_guide/database/
+ * @author EllisLab Dev Team
+ * @link https://codeigniter.com/userguide3/database/
*/
class CI_DB_sqlsrv_utility extends CI_DB_utility {
/**
- * List databases
+ * List databases statement
*
- * @access private
- * @return bool
+ * @var string
*/
- function _list_databases()
- {
- return "EXEC sp_helpdb"; // Can also be: EXEC sp_databases
- }
-
- // --------------------------------------------------------------------
+ protected $_list_databases = 'EXEC sp_helpdb'; // Can also be: EXEC sp_databases
/**
- * Optimize table query
- *
- * Generates a platform-specific query so that a table can be optimized
+ * OPTIMIZE TABLE statement
*
- * @access private
- * @param string the table name
- * @return object
+ * @var string
*/
- function _optimize_table($table)
- {
- return FALSE; // Is this supported in MS SQL?
- }
-
- // --------------------------------------------------------------------
-
- /**
- * Repair table query
- *
- * Generates a platform-specific query so that a table can be repaired
- *
- * @access private
- * @param string the table name
- * @return object
- */
- function _repair_table($table)
- {
- return FALSE; // Is this supported in MS SQL?
- }
+ protected $_optimize_table = 'ALTER INDEX all ON %s REORGANIZE';
// --------------------------------------------------------------------
/**
- * MSSQL Export
+ * Export
*
- * @access private
- * @param array Preferences
- * @return mixed
+ * @param array $params Preferences
+ * @return bool
*/
- function _backup($params = array())
+ protected function _backup($params = array())
{
// Currently unsupported
- return $this->db->display_error('db_unsuported_feature');
+ return $this->db->display_error('db_unsupported_feature');
}
}
-
-/* End of file mssql_utility.php */
-/* Location: ./system/database/drivers/mssql/mssql_utility.php */ \ No newline at end of file
diff --git a/system/database/index.html b/system/database/index.html
index c942a79ce..bcb7cae34 100644
--- a/system/database/index.html
+++ b/system/database/index.html
@@ -1,4 +1,5 @@
-<html>
+<!DOCTYPE html>
+<html lang="en">
<head>
<title>403 Forbidden</title>
</head>
@@ -7,4 +8,4 @@
<p>Directory access is forbidden.</p>
</body>
-</html> \ No newline at end of file
+</html>
diff --git a/system/fonts/index.html b/system/fonts/index.html
index c942a79ce..bcb7cae34 100644
--- a/system/fonts/index.html
+++ b/system/fonts/index.html
@@ -1,4 +1,5 @@
-<html>
+<!DOCTYPE html>
+<html lang="en">
<head>
<title>403 Forbidden</title>
</head>
@@ -7,4 +8,4 @@
<p>Directory access is forbidden.</p>
</body>
-</html> \ No newline at end of file
+</html>
diff --git a/system/helpers/array_helper.php b/system/helpers/array_helper.php
index 2e620dbe5..0617fdec4 100644
--- a/system/helpers/array_helper.php
+++ b/system/helpers/array_helper.php
@@ -1,19 +1,42 @@
-<?php if ( ! defined('BASEPATH')) exit('No direct script access allowed');
+<?php
/**
* CodeIgniter
*
- * An open source application development framework for PHP 5.1.6 or newer
+ * An open source application development framework for PHP
*
- * @package CodeIgniter
- * @author ExpressionEngine Dev Team
- * @copyright Copyright (c) 2008 - 2014, EllisLab, Inc.
- * @license http://codeigniter.com/user_guide/license.html
- * @link http://codeigniter.com
- * @since Version 1.0
+ * This content is released under the MIT License (MIT)
+ *
+ * Copyright (c) 2019 - 2022, CodeIgniter Foundation
+ *
+ * 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.
+ *
+ * @package CodeIgniter
+ * @author EllisLab Dev Team
+ * @copyright Copyright (c) 2008 - 2014, EllisLab, Inc. (https://ellislab.com/)
+ * @copyright Copyright (c) 2014 - 2019, British Columbia Institute of Technology (https://bcit.ca/)
+ * @copyright Copyright (c) 2019 - 2022, CodeIgniter Foundation (https://codeigniter.com/)
+ * @license https://opensource.org/licenses/MIT MIT License
+ * @link https://codeigniter.com
+ * @since Version 1.0.0
* @filesource
*/
-
-// ------------------------------------------------------------------------
+defined('BASEPATH') OR exit('No direct script access allowed');
/**
* CodeIgniter Array Helpers
@@ -21,99 +44,73 @@
* @package CodeIgniter
* @subpackage Helpers
* @category Helpers
- * @author ExpressionEngine Dev Team
- * @link http://codeigniter.com/user_guide/helpers/array_helper.html
+ * @author EllisLab Dev Team
+ * @link https://codeigniter.com/userguide3/helpers/array_helper.html
*/
// ------------------------------------------------------------------------
-/**
- * Element
- *
- * Lets you determine whether an array index is set and whether it has a value.
- * If the element is empty it returns FALSE (or whatever you specify as the default value.)
- *
- * @access public
- * @param string
- * @param array
- * @param mixed
- * @return mixed depends on what the array contains
- */
if ( ! function_exists('element'))
{
- function element($item, $array, $default = FALSE)
+ /**
+ * Element
+ *
+ * Lets you determine whether an array index is set and whether it has a value.
+ * If the element is empty it returns NULL (or whatever you specify as the default value.)
+ *
+ * @param string
+ * @param array
+ * @param mixed
+ * @return mixed depends on what the array contains
+ */
+ function element($item, array $array, $default = NULL)
{
- if ( ! isset($array[$item]) OR $array[$item] == "")
- {
- return $default;
- }
-
- return $array[$item];
+ return array_key_exists($item, $array) ? $array[$item] : $default;
}
}
// ------------------------------------------------------------------------
-/**
- * Random Element - Takes an array as input and returns a random element
- *
- * @access public
- * @param array
- * @return mixed depends on what the array contains
- */
if ( ! function_exists('random_element'))
{
+ /**
+ * Random Element - Takes an array as input and returns a random element
+ *
+ * @param array
+ * @return mixed depends on what the array contains
+ */
function random_element($array)
{
- if ( ! is_array($array))
- {
- return $array;
- }
-
- return $array[array_rand($array)];
+ return is_array($array) ? $array[array_rand($array)] : $array;
}
}
// --------------------------------------------------------------------
-/**
- * Elements
- *
- * Returns only the array items specified. Will return a default value if
- * it is not set.
- *
- * @access public
- * @param array
- * @param array
- * @param mixed
- * @return mixed depends on what the array contains
- */
if ( ! function_exists('elements'))
{
- function elements($items, $array, $default = FALSE)
+ /**
+ * Elements
+ *
+ * Returns only the array items specified. Will return a default value if
+ * it is not set.
+ *
+ * @param array
+ * @param array
+ * @param mixed
+ * @return mixed depends on what the array contains
+ */
+ function elements($items, array $array, $default = NULL)
{
$return = array();
-
- if ( ! is_array($items))
- {
- $items = array($items);
- }
-
+
+ is_array($items) OR $items = array($items);
+
foreach ($items as $item)
{
- if (isset($array[$item]))
- {
- $return[$item] = $array[$item];
- }
- else
- {
- $return[$item] = $default;
- }
+ $return[$item] = array_key_exists($item, $array) ? $array[$item] : $default;
}
return $return;
}
}
-
-/* End of file array_helper.php */
-/* Location: ./system/helpers/array_helper.php */ \ No newline at end of file
diff --git a/system/helpers/captcha_helper.php b/system/helpers/captcha_helper.php
index bcc7dbc72..9f707524a 100644
--- a/system/helpers/captcha_helper.php
+++ b/system/helpers/captcha_helper.php
@@ -1,19 +1,42 @@
-<?php if ( ! defined('BASEPATH')) exit('No direct script access allowed');
+<?php
/**
* CodeIgniter
*
- * An open source application development framework for PHP 5.1.6 or newer
+ * An open source application development framework for PHP
*
- * @package CodeIgniter
- * @author ExpressionEngine Dev Team
- * @copyright Copyright (c) 2008 - 2014, EllisLab, Inc.
- * @license http://codeigniter.com/user_guide/license.html
- * @link http://codeigniter.com
- * @since Version 1.0
+ * This content is released under the MIT License (MIT)
+ *
+ * Copyright (c) 2019 - 2022, CodeIgniter Foundation
+ *
+ * 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.
+ *
+ * @package CodeIgniter
+ * @author EllisLab Dev Team
+ * @copyright Copyright (c) 2008 - 2014, EllisLab, Inc. (https://ellislab.com/)
+ * @copyright Copyright (c) 2014 - 2019, British Columbia Institute of Technology (https://bcit.ca/)
+ * @copyright Copyright (c) 2019 - 2022, CodeIgniter Foundation (https://codeigniter.com/)
+ * @license https://opensource.org/licenses/MIT MIT License
+ * @link https://codeigniter.com
+ * @since Version 1.0.0
* @filesource
*/
-
-// ------------------------------------------------------------------------
+defined('BASEPATH') OR exit('No direct script access allowed');
/**
* CodeIgniter CAPTCHA Helper
@@ -21,226 +44,339 @@
* @package CodeIgniter
* @subpackage Helpers
* @category Helpers
- * @author ExpressionEngine Dev Team
- * @link http://codeigniter.com/user_guide/helpers/xml_helper.html
+ * @author EllisLab Dev Team
+ * @link https://codeigniter.com/userguide3/helpers/captcha_helper.html
*/
// ------------------------------------------------------------------------
-/**
- * Create CAPTCHA
- *
- * @access public
- * @param array array of data for the CAPTCHA
- * @param string path to create the image in
- * @param string URL to the CAPTCHA image folder
- * @param string server path to font
- * @return string
- */
if ( ! function_exists('create_captcha'))
{
- function create_captcha($data = '', $img_path = '', $img_url = '', $font_path = '')
+ /**
+ * Create CAPTCHA
+ *
+ * @param array $data Data for the CAPTCHA
+ * @return array
+ */
+ function create_captcha($data)
{
- $defaults = array('word' => '', 'img_path' => '', 'img_url' => '', 'img_width' => '150', 'img_height' => '30', 'font_path' => '', 'expiration' => 7200);
+ $defaults = array(
+ 'word' => '',
+ 'img_path' => '',
+ 'img_url' => '',
+ 'img_width' => '150',
+ 'img_height' => '30',
+ 'img_alt' => 'captcha',
+ 'img_class' => '',
+ 'font_path' => '',
+ 'font_size' => 16,
+ 'expiration' => 7200,
+ 'word_length' => 8,
+ 'img_id' => '',
+ 'pool' => '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ',
+ 'colors' => array(
+ 'background' => array(255,255,255),
+ 'border' => array(153,102,102),
+ 'text' => array(204,153,153),
+ 'grid' => array(255,182,182)
+ )
+ );
+
+ $now = microtime(TRUE);
foreach ($defaults as $key => $val)
{
- if ( ! is_array($data))
+ if ( ! is_array($data) && empty($$key))
{
- if ( ! isset($$key) OR $$key == '')
- {
- $$key = $val;
- }
+ $$key = $val;
}
else
{
- $$key = ( ! isset($data[$key])) ? $val : $data[$key];
+ $$key = isset($data[$key]) ? $data[$key] : $val;
}
}
- if ($img_path == '' OR $img_url == '')
+ if ( ! extension_loaded('gd'))
{
+ log_message('error', 'create_captcha(): GD extension is not loaded.');
return FALSE;
}
- if ( ! @is_dir($img_path))
+ if ($img_path === '' OR $img_url === '')
{
+ log_message('error', 'create_captcha(): img_path and img_url are required.');
return FALSE;
}
- if ( ! is_writable($img_path))
+ if ( ! is_dir($img_path) OR ! is_really_writable($img_path))
{
+ log_message('error', "create_captcha(): '{$img_path}' is not a dir, nor is it writable.");
return FALSE;
}
- if ( ! extension_loaded('gd'))
+ if ($img_url !== '' OR $img_path !== '')
{
- return FALSE;
- }
-
- // -----------------------------------
- // Remove old images
- // -----------------------------------
-
- list($usec, $sec) = explode(" ", microtime());
- $now = ((float)$usec + (float)$sec);
-
- $current_dir = @opendir($img_path);
+ if ($img_path === '' OR $img_url === '')
+ {
+ log_message('error', 'create_captcha(): $img_path and $img_url are required.');
+ return FALSE;
+ }
- while ($filename = @readdir($current_dir))
- {
- if ($filename != "." and $filename != ".." and $filename != "index.html")
+ if ( ! is_dir($img_path) OR ! is_really_writable($img_path))
{
- $name = str_replace(".jpg", "", $filename);
+ log_message('error', "create_captcha(): '{$img_path}' is not a dir, nor is it writable.");
+ return FALSE;
+ }
- if (($name + $expiration) < $now)
+ /**
+ * Remove old images
+ */
+ $current_dir = @opendir($img_path);
+ while ($filename = @readdir($current_dir))
+ {
+ if (preg_match('#^(?<ts>\d{10})\.png$#', $filename, $match) && ($match['ts'] + $expiration) < $now)
{
@unlink($img_path.$filename);
}
}
- }
- @closedir($current_dir);
+ @closedir($current_dir);
+
+ // This variable will later be used later to determine whether we write to disk or output a data:image URI
+ $img_filename = $now.'.png';
+ }
+ else
+ {
+ $img_filename = NULL;
+ }
// -----------------------------------
// Do we have a "word" yet?
// -----------------------------------
- if ($word == '')
- {
- $pool = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ';
+ if (empty($word))
+ {
+ $word = '';
+ $pool_length = strlen($pool);
+ $rand_max = $pool_length - 1;
- $str = '';
- for ($i = 0; $i < 8; $i++)
+ // PHP7 or a suitable polyfill
+ if (function_exists('random_int'))
{
- $str .= substr($pool, mt_rand(0, strlen($pool) -1), 1);
+ try
+ {
+ for ($i = 0; $i < $word_length; $i++)
+ {
+ $word .= $pool[random_int(0, $rand_max)];
+ }
+ }
+ catch (Exception $e)
+ {
+ // This means fallback to the next possible
+ // alternative to random_int()
+ $word = '';
+ }
}
+ }
- $word = $str;
- }
-
- // -----------------------------------
- // Determine angle and position
- // -----------------------------------
+ if (empty($word))
+ {
+ // Nobody will have a larger character pool than
+ // 256 characters, but let's handle it just in case ...
+ //
+ // No, I do not care that the fallback to mt_rand() can
+ // handle it; if you trigger this, you're very obviously
+ // trying to break it. -- Narf
+ if ($pool_length > 256)
+ {
+ return FALSE;
+ }
- $length = strlen($word);
- $angle = ($length >= 6) ? rand(-($length-6), ($length-6)) : 0;
- $x_axis = rand(6, (360/$length)-16);
- $y_axis = ($angle >= 0 ) ? rand($img_height, $img_width) : rand(6, $img_height);
+ // We'll try using the operating system's PRNG first,
+ // which we can access through CI_Security::get_random_bytes()
+ $security = get_instance()->security;
- // -----------------------------------
- // Create image
- // -----------------------------------
+ // To avoid numerous get_random_bytes() calls, we'll
+ // just try fetching as much bytes as we need at once.
+ if (($bytes = $security->get_random_bytes($pool_length)) !== FALSE)
+ {
+ $byte_index = $word_index = 0;
+ while ($word_index < $word_length)
+ {
+ // Do we have more random data to use?
+ // It could be exhausted by previous iterations
+ // ignoring bytes higher than $rand_max.
+ if ($byte_index === $pool_length)
+ {
+ // No failures should be possible if the
+ // first get_random_bytes() call didn't
+ // return FALSE, but still ...
+ for ($i = 0; $i < 5; $i++)
+ {
+ if (($bytes = $security->get_random_bytes($pool_length)) === FALSE)
+ {
+ continue;
+ }
+
+ $byte_index = 0;
+ break;
+ }
+
+ if ($bytes === FALSE)
+ {
+ // Sadly, this means fallback to mt_rand()
+ $word = '';
+ break;
+ }
+ }
+
+ list(, $rand_index) = unpack('C', $bytes[$byte_index++]);
+ if ($rand_index > $rand_max)
+ {
+ continue;
+ }
+
+ $word .= $pool[$rand_index];
+ $word_index++;
+ }
+ }
+ }
- // PHP.net recommends imagecreatetruecolor(), but it isn't always available
- if (function_exists('imagecreatetruecolor'))
+ if (empty($word))
{
- $im = imagecreatetruecolor($img_width, $img_height);
+ for ($i = 0; $i < $word_length; $i++)
+ {
+ $word .= $pool[mt_rand(0, $rand_max)];
+ }
}
- else
+ elseif ( ! is_string($word))
{
- $im = imagecreate($img_width, $img_height);
+ $word = (string) $word;
}
// -----------------------------------
- // Assign colors
+ // Determine angle and position
// -----------------------------------
+ $length = strlen($word);
+ $angle = ($length >= 6) ? mt_rand(-($length - 6), ($length - 6)) : 0;
+ $x_axis = mt_rand(6, (360 / $length)-16);
+ $y_axis = ($angle >= 0) ? mt_rand($img_height, $img_width) : mt_rand(6, $img_height);
- $bg_color = imagecolorallocate ($im, 255, 255, 255);
- $border_color = imagecolorallocate ($im, 153, 102, 102);
- $text_color = imagecolorallocate ($im, 204, 153, 153);
- $grid_color = imagecolorallocate($im, 255, 182, 182);
- $shadow_color = imagecolorallocate($im, 255, 240, 240);
+ // Create image
+ // PHP.net recommends imagecreatetruecolor(), but it isn't always available
+ $im = function_exists('imagecreatetruecolor')
+ ? imagecreatetruecolor($img_width, $img_height)
+ : imagecreate($img_width, $img_height);
// -----------------------------------
- // Create the rectangle
- // -----------------------------------
+ // Assign colors
+ // ----------------------------------
+
+ is_array($colors) OR $colors = $defaults['colors'];
+
+ foreach (array_keys($defaults['colors']) as $key)
+ {
+ // Check for a possible missing value
+ is_array($colors[$key]) OR $colors[$key] = $defaults['colors'][$key];
+ $colors[$key] = imagecolorallocate($im, $colors[$key][0], $colors[$key][1], $colors[$key][2]);
+ }
- ImageFilledRectangle($im, 0, 0, $img_width, $img_height, $bg_color);
+ // Create the rectangle
+ ImageFilledRectangle($im, 0, 0, $img_width, $img_height, $colors['background']);
// -----------------------------------
// Create the spiral pattern
// -----------------------------------
-
$theta = 1;
$thetac = 7;
$radius = 16;
$circles = 20;
$points = 32;
- for ($i = 0; $i < ($circles * $points) - 1; $i++)
+ for ($i = 0, $cp = ($circles * $points) - 1; $i < $cp; $i++)
{
- $theta = $theta + $thetac;
- $rad = $radius * ($i / $points );
+ $theta += $thetac;
+ $rad = $radius * ($i / $points);
$x = ($rad * cos($theta)) + $x_axis;
$y = ($rad * sin($theta)) + $y_axis;
- $theta = $theta + $thetac;
+ $theta += $thetac;
$rad1 = $radius * (($i + 1) / $points);
$x1 = ($rad1 * cos($theta)) + $x_axis;
- $y1 = ($rad1 * sin($theta )) + $y_axis;
- imageline($im, $x, $y, $x1, $y1, $grid_color);
- $theta = $theta - $thetac;
+ $y1 = ($rad1 * sin($theta)) + $y_axis;
+ imageline($im, $x, $y, $x1, $y1, $colors['grid']);
+ $theta -= $thetac;
}
// -----------------------------------
// Write the text
// -----------------------------------
- $use_font = ($font_path != '' AND file_exists($font_path) AND function_exists('imagettftext')) ? TRUE : FALSE;
-
- if ($use_font == FALSE)
+ $use_font = ($font_path !== '' && file_exists($font_path) && function_exists('imagettftext'));
+ if ($use_font === FALSE)
{
- $font_size = 5;
- $x = rand(0, $img_width/($length/3));
+ ($font_size > 5) && $font_size = 5;
+ $x = mt_rand(0, $img_width / ($length / 3));
$y = 0;
}
else
{
- $font_size = 16;
- $x = rand(0, $img_width/($length/1.5));
- $y = $font_size+2;
+ ($font_size > 30) && $font_size = 30;
+ $x = mt_rand(0, $img_width / ($length / 1.5));
+ $y = $font_size + 2;
}
- for ($i = 0; $i < strlen($word); $i++)
+ for ($i = 0; $i < $length; $i++)
{
- if ($use_font == FALSE)
+ if ($use_font === FALSE)
{
- $y = rand(0 , $img_height/2);
- imagestring($im, $font_size, $x, $y, substr($word, $i, 1), $text_color);
- $x += ($font_size*2);
+ $y = mt_rand(0 , $img_height / 2);
+ imagestring($im, $font_size, $x, $y, $word[$i], $colors['text']);
+ $x += ($font_size * 2);
}
else
{
- $y = rand($img_height/2, $img_height-3);
- imagettftext($im, $font_size, $angle, $x, $y, $text_color, $font_path, substr($word, $i, 1));
+ $y = mt_rand($img_height / 2, $img_height - 3);
+ imagettftext($im, $font_size, $angle, $x, $y, $colors['text'], $font_path, $word[$i]);
$x += $font_size;
}
}
-
- // -----------------------------------
- // Create the border
- // -----------------------------------
-
- imagerectangle($im, 0, 0, $img_width-1, $img_height-1, $border_color);
+ // Create the border
+ imagerectangle($im, 0, 0, $img_width - 1, $img_height - 1, $colors['border']);
// -----------------------------------
// Generate the image
// -----------------------------------
- $img_name = $now.'.jpg';
+ if (isset($img_filename))
+ {
+ $img_src = rtrim($img_url, '/').'/'.$img_filename;
+ imagepng($im, $img_path.$img_filename);
+ }
+ else
+ {
+ // I don't see an easier way to get the image contents without writing to file
+ $buffer = fopen('php://memory', 'wb+');
+ imagepng($im, $buffer);
+ rewind($buffer);
+ $img_src = '';
+
+ // fread() will return an empty string (not FALSE) after the entire contents are read
+ while (strlen($read = fread($buffer, 4096)))
+ {
+ $img_src .= $read;
+ }
- ImageJPEG($im, $img_path.$img_name);
+ fclose($buffer);
+ $img_src = 'data:image/png;base64,'.base64_encode($img_src);
+ }
- $img = "<img src=\"$img_url$img_name\" width=\"$img_width\" height=\"$img_height\" style=\"border:0;\" alt=\" \" />";
+ $img_class = trim($img_class);
+ $img_class = (bool) strlen($img_class) ? 'class="'.$img_class.'" ' : '';
+ $img = '<img '.($img_id === '' ? '' : 'id="'.$img_id.'"').' src="'.$img_src.'" style="width: '.$img_width.'px; height: '.$img_height .'px; border: 0;" '.$img_class.'alt="'.$img_alt.'" />';
ImageDestroy($im);
- return array('word' => $word, 'time' => $now, 'image' => $img);
+ return array('word' => $word, 'time' => $now, 'image' => $img, 'filename' => $img_filename);
}
}
-
-// ------------------------------------------------------------------------
-
-/* End of file captcha_helper.php */
-/* Location: ./system/heleprs/captcha_helper.php */ \ No newline at end of file
diff --git a/system/helpers/cookie_helper.php b/system/helpers/cookie_helper.php
index 98670c193..0325e3db3 100644
--- a/system/helpers/cookie_helper.php
+++ b/system/helpers/cookie_helper.php
@@ -1,19 +1,42 @@
-<?php if ( ! defined('BASEPATH')) exit('No direct script access allowed');
+<?php
/**
* CodeIgniter
*
- * An open source application development framework for PHP 5.1.6 or newer
+ * An open source application development framework for PHP
*
- * @package CodeIgniter
- * @author ExpressionEngine Dev Team
- * @copyright Copyright (c) 2008 - 2014, EllisLab, Inc.
- * @license http://codeigniter.com/user_guide/license.html
- * @link http://codeigniter.com
- * @since Version 1.0
+ * This content is released under the MIT License (MIT)
+ *
+ * Copyright (c) 2019 - 2022, CodeIgniter Foundation
+ *
+ * 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.
+ *
+ * @package CodeIgniter
+ * @author EllisLab Dev Team
+ * @copyright Copyright (c) 2008 - 2014, EllisLab, Inc. (https://ellislab.com/)
+ * @copyright Copyright (c) 2014 - 2019, British Columbia Institute of Technology (https://bcit.ca/)
+ * @copyright Copyright (c) 2019 - 2022, CodeIgniter Foundation (https://codeigniter.com/)
+ * @license https://opensource.org/licenses/MIT MIT License
+ * @link https://codeigniter.com
+ * @since Version 1.0.0
* @filesource
*/
-
-// ------------------------------------------------------------------------
+defined('BASEPATH') OR exit('No direct script access allowed');
/**
* CodeIgniter Cookie Helpers
@@ -21,83 +44,70 @@
* @package CodeIgniter
* @subpackage Helpers
* @category Helpers
- * @author ExpressionEngine Dev Team
- * @link http://codeigniter.com/user_guide/helpers/cookie_helper.html
+ * @author EllisLab Dev Team
+ * @link https://codeigniter.com/userguide3/helpers/cookie_helper.html
*/
// ------------------------------------------------------------------------
-/**
- * Set cookie
- *
- * Accepts six parameter, or you can submit an associative
- * array in the first parameter containing all the values.
- *
- * @access public
- * @param mixed
- * @param string the value of the cookie
- * @param string the number of seconds until expiration
- * @param string the cookie domain. Usually: .yourdomain.com
- * @param string the cookie path
- * @param string the cookie prefix
- * @return void
- */
if ( ! function_exists('set_cookie'))
{
- function set_cookie($name = '', $value = '', $expire = '', $domain = '', $path = '/', $prefix = '', $secure = FALSE)
+ /**
+ * Set cookie
+ *
+ * Accepts seven parameters, or you can submit an associative
+ * array in the first parameter containing all the values.
+ *
+ * @param mixed
+ * @param string the value of the cookie
+ * @param int the number of seconds until expiration
+ * @param string the cookie domain. Usually: .yourdomain.com
+ * @param string the cookie path
+ * @param string the cookie prefix
+ * @param bool true makes the cookie secure
+ * @param bool true makes the cookie accessible via http(s) only (no javascript)
+ * @return void
+ */
+ function set_cookie($name, $value = '', $expire = 0, $domain = '', $path = '/', $prefix = '', $secure = NULL, $httponly = NULL, $samesite = NULL)
{
// Set the config file options
- $CI =& get_instance();
- $CI->input->set_cookie($name, $value, $expire, $domain, $path, $prefix, $secure);
+ get_instance()->input->set_cookie($name, $value, $expire, $domain, $path, $prefix, $secure, $httponly, $samesite);
}
}
// --------------------------------------------------------------------
-/**
- * Fetch an item from the COOKIE array
- *
- * @access public
- * @param string
- * @param bool
- * @return mixed
- */
if ( ! function_exists('get_cookie'))
{
- function get_cookie($index = '', $xss_clean = FALSE)
+ /**
+ * Fetch an item from the COOKIE array
+ *
+ * @param string
+ * @param bool
+ * @return mixed
+ */
+ function get_cookie($index, $xss_clean = FALSE)
{
- $CI =& get_instance();
-
- $prefix = '';
-
- if ( ! isset($_COOKIE[$index]) && config_item('cookie_prefix') != '')
- {
- $prefix = config_item('cookie_prefix');
- }
-
- return $CI->input->cookie($prefix.$index, $xss_clean);
+ $prefix = isset($_COOKIE[$index]) ? '' : config_item('cookie_prefix');
+ return get_instance()->input->cookie($prefix.$index, $xss_clean);
}
}
// --------------------------------------------------------------------
-/**
- * Delete a COOKIE
- *
- * @param mixed
- * @param string the cookie domain. Usually: .yourdomain.com
- * @param string the cookie path
- * @param string the cookie prefix
- * @return void
- */
if ( ! function_exists('delete_cookie'))
{
- function delete_cookie($name = '', $domain = '', $path = '/', $prefix = '')
+ /**
+ * Delete a COOKIE
+ *
+ * @param mixed
+ * @param string the cookie domain. Usually: .yourdomain.com
+ * @param string the cookie path
+ * @param string the cookie prefix
+ * @return void
+ */
+ function delete_cookie($name, $domain = '', $path = '/', $prefix = '')
{
set_cookie($name, '', '', $domain, $path, $prefix);
}
}
-
-
-/* End of file cookie_helper.php */
-/* Location: ./system/helpers/cookie_helper.php */ \ No newline at end of file
diff --git a/system/helpers/date_helper.php b/system/helpers/date_helper.php
index 27aa48241..6ea9d82bd 100644
--- a/system/helpers/date_helper.php
+++ b/system/helpers/date_helper.php
@@ -1,19 +1,42 @@
-<?php if ( ! defined('BASEPATH')) exit('No direct script access allowed');
+<?php
/**
* CodeIgniter
*
- * An open source application development framework for PHP 5.1.6 or newer
+ * An open source application development framework for PHP
*
- * @package CodeIgniter
- * @author ExpressionEngine Dev Team
- * @copyright Copyright (c) 2008 - 2014, EllisLab, Inc.
- * @license http://codeigniter.com/user_guide/license.html
- * @link http://codeigniter.com
- * @since Version 1.0
+ * This content is released under the MIT License (MIT)
+ *
+ * Copyright (c) 2019 - 2022, CodeIgniter Foundation
+ *
+ * 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.
+ *
+ * @package CodeIgniter
+ * @author EllisLab Dev Team
+ * @copyright Copyright (c) 2008 - 2014, EllisLab, Inc. (https://ellislab.com/)
+ * @copyright Copyright (c) 2014 - 2019, British Columbia Institute of Technology (https://bcit.ca/)
+ * @copyright Copyright (c) 2019 - 2022, CodeIgniter Foundation (https://codeigniter.com/)
+ * @license https://opensource.org/licenses/MIT MIT License
+ * @link https://codeigniter.com
+ * @since Version 1.0.0
* @filesource
*/
-
-// ------------------------------------------------------------------------
+defined('BASEPATH') OR exit('No direct script access allowed');
/**
* CodeIgniter Date Helpers
@@ -21,184 +44,137 @@
* @package CodeIgniter
* @subpackage Helpers
* @category Helpers
- * @author ExpressionEngine Dev Team
- * @link http://codeigniter.com/user_guide/helpers/date_helper.html
+ * @author EllisLab Dev Team
+ * @link https://codeigniter.com/userguide3/helpers/date_helper.html
*/
// ------------------------------------------------------------------------
-/**
- * Get "now" time
- *
- * Returns time() or its GMT equivalent based on the config file preference
- *
- * @access public
- * @return integer
- */
if ( ! function_exists('now'))
{
- function now()
+ /**
+ * Get "now" time
+ *
+ * Returns time() based on the timezone parameter or on the
+ * "time_reference" setting
+ *
+ * @param string
+ * @return int
+ */
+ function now($timezone = NULL)
{
- $CI =& get_instance();
-
- if (strtolower($CI->config->item('time_reference')) == 'gmt')
+ if (empty($timezone))
{
- $now = time();
- $system_time = mktime(gmdate("H", $now), gmdate("i", $now), gmdate("s", $now), gmdate("m", $now), gmdate("d", $now), gmdate("Y", $now));
-
- if (strlen($system_time) < 10)
- {
- $system_time = time();
- log_message('error', 'The Date class could not set a proper GMT timestamp so the local time() value was used.');
- }
-
- return $system_time;
+ $timezone = config_item('time_reference');
}
- else
+
+ if ($timezone === 'local' OR $timezone === date_default_timezone_get())
{
return time();
}
+
+ $datetime = new DateTime('now', new DateTimeZone($timezone));
+ sscanf($datetime->format('j-n-Y G:i:s'), '%d-%d-%d %d:%d:%d', $day, $month, $year, $hour, $minute, $second);
+
+ return mktime($hour, $minute, $second, $month, $day, $year);
}
}
// ------------------------------------------------------------------------
-/**
- * Convert MySQL Style Datecodes
- *
- * This function is identical to PHPs date() function,
- * except that it allows date codes to be formatted using
- * the MySQL style, where each code letter is preceded
- * with a percent sign: %Y %m %d etc...
- *
- * The benefit of doing dates this way is that you don't
- * have to worry about escaping your text letters that
- * match the date codes.
- *
- * @access public
- * @param string
- * @param integer
- * @return integer
- */
if ( ! function_exists('mdate'))
{
+ /**
+ * Convert MySQL Style Datecodes
+ *
+ * This function is identical to PHPs date() function,
+ * except that it allows date codes to be formatted using
+ * the MySQL style, where each code letter is preceded
+ * with a percent sign: %Y %m %d etc...
+ *
+ * The benefit of doing dates this way is that you don't
+ * have to worry about escaping your text letters that
+ * match the date codes.
+ *
+ * @param string
+ * @param int
+ * @return int
+ */
function mdate($datestr = '', $time = '')
{
- if ($datestr == '')
+ if ($datestr === '')
+ {
return '';
-
- if ($time == '')
- $time = now();
-
- $datestr = str_replace('%\\', '', preg_replace("/([a-z]+?){1}/i", "\\\\\\1", $datestr));
- return date($datestr, $time);
- }
-}
-
-// ------------------------------------------------------------------------
-
-/**
- * Standard Date
- *
- * Returns a date formatted according to the submitted standard.
- *
- * @access public
- * @param string the chosen format
- * @param integer Unix timestamp
- * @return string
- */
-if ( ! function_exists('standard_date'))
-{
- function standard_date($fmt = 'DATE_RFC822', $time = '')
- {
- $formats = array(
- 'DATE_ATOM' => '%Y-%m-%dT%H:%i:%s%Q',
- 'DATE_COOKIE' => '%l, %d-%M-%y %H:%i:%s UTC',
- 'DATE_ISO8601' => '%Y-%m-%dT%H:%i:%s%Q',
- 'DATE_RFC822' => '%D, %d %M %y %H:%i:%s %O',
- 'DATE_RFC850' => '%l, %d-%M-%y %H:%i:%s UTC',
- 'DATE_RFC1036' => '%D, %d %M %y %H:%i:%s %O',
- 'DATE_RFC1123' => '%D, %d %M %Y %H:%i:%s %O',
- 'DATE_RSS' => '%D, %d %M %Y %H:%i:%s %O',
- 'DATE_W3C' => '%Y-%m-%dT%H:%i:%s%Q'
- );
-
- if ( ! isset($formats[$fmt]))
+ }
+ elseif (empty($time))
{
- return FALSE;
+ $time = now();
}
- return mdate($formats[$fmt], $time);
+ $datestr = str_replace(
+ '%\\',
+ '',
+ preg_replace('/([a-z]+?){1}/i', '\\\\\\1', $datestr)
+ );
+
+ return date($datestr, $time);
}
}
// ------------------------------------------------------------------------
-/**
- * Timespan
- *
- * Returns a span of seconds in this format:
- * 10 days 14 hours 36 minutes 47 seconds
- *
- * @access public
- * @param integer a number of seconds
- * @param integer Unix timestamp
- * @return integer
- */
if ( ! function_exists('timespan'))
{
- function timespan($seconds = 1, $time = '')
+ /**
+ * Timespan
+ *
+ * Returns a span of seconds in this format:
+ * 10 days 14 hours 36 minutes 47 seconds
+ *
+ * @param int a number of seconds
+ * @param int Unix timestamp
+ * @param int a number of display units
+ * @return string
+ */
+ function timespan($seconds = 1, $time = '', $units = 7)
{
$CI =& get_instance();
$CI->lang->load('date');
- if ( ! is_numeric($seconds))
- {
- $seconds = 1;
- }
-
- if ( ! is_numeric($time))
- {
- $time = time();
- }
+ is_numeric($seconds) OR $seconds = 1;
+ is_numeric($time) OR $time = time();
+ is_numeric($units) OR $units = 7;
- if ($time <= $seconds)
- {
- $seconds = 1;
- }
- else
- {
- $seconds = $time - $seconds;
- }
+ $seconds = ($time <= $seconds) ? 1 : $time - $seconds;
- $str = '';
- $years = floor($seconds / 31536000);
+ $str = array();
+ $years = floor($seconds / 31557600);
if ($years > 0)
{
- $str .= $years.' '.$CI->lang->line((($years > 1) ? 'date_years' : 'date_year')).', ';
+ $str[] = $years.' '.$CI->lang->line($years > 1 ? 'date_years' : 'date_year');
}
- $seconds -= $years * 31536000;
- $months = floor($seconds / 2628000);
+ $seconds -= $years * 31557600;
+ $months = floor($seconds / 2629743);
- if ($years > 0 OR $months > 0)
+ if (count($str) < $units && ($years > 0 OR $months > 0))
{
if ($months > 0)
{
- $str .= $months.' '.$CI->lang->line((($months > 1) ? 'date_months' : 'date_month')).', ';
+ $str[] = $months.' '.$CI->lang->line($months > 1 ? 'date_months' : 'date_month');
}
- $seconds -= $months * 2628000;
+ $seconds -= $months * 2629743;
}
$weeks = floor($seconds / 604800);
- if ($years > 0 OR $months > 0 OR $weeks > 0)
+ if (count($str) < $units && ($years > 0 OR $months > 0 OR $weeks > 0))
{
if ($weeks > 0)
{
- $str .= $weeks.' '.$CI->lang->line((($weeks > 1) ? 'date_weeks' : 'date_week')).', ';
+ $str[] = $weeks.' '.$CI->lang->line($weeks > 1 ? 'date_weeks' : 'date_week');
}
$seconds -= $weeks * 604800;
@@ -206,11 +182,11 @@ if ( ! function_exists('timespan'))
$days = floor($seconds / 86400);
- if ($months > 0 OR $weeks > 0 OR $days > 0)
+ if (count($str) < $units && ($months > 0 OR $weeks > 0 OR $days > 0))
{
if ($days > 0)
{
- $str .= $days.' '.$CI->lang->line((($days > 1) ? 'date_days' : 'date_day')).', ';
+ $str[] = $days.' '.$CI->lang->line($days > 1 ? 'date_days' : 'date_day');
}
$seconds -= $days * 86400;
@@ -218,11 +194,11 @@ if ( ! function_exists('timespan'))
$hours = floor($seconds / 3600);
- if ($days > 0 OR $hours > 0)
+ if (count($str) < $units && ($days > 0 OR $hours > 0))
{
if ($hours > 0)
{
- $str .= $hours.' '.$CI->lang->line((($hours > 1) ? 'date_hours' : 'date_hour')).', ';
+ $str[] = $hours.' '.$CI->lang->line($hours > 1 ? 'date_hours' : 'date_hour');
}
$seconds -= $hours * 3600;
@@ -230,55 +206,63 @@ if ( ! function_exists('timespan'))
$minutes = floor($seconds / 60);
- if ($days > 0 OR $hours > 0 OR $minutes > 0)
+ if (count($str) < $units && ($days > 0 OR $hours > 0 OR $minutes > 0))
{
if ($minutes > 0)
{
- $str .= $minutes.' '.$CI->lang->line((($minutes > 1) ? 'date_minutes' : 'date_minute')).', ';
+ $str[] = $minutes.' '.$CI->lang->line($minutes > 1 ? 'date_minutes' : 'date_minute');
}
$seconds -= $minutes * 60;
}
- if ($str == '')
+ if (count($str) === 0)
{
- $str .= $seconds.' '.$CI->lang->line((($seconds > 1) ? 'date_seconds' : 'date_second')).', ';
+ $str[] = $seconds.' '.$CI->lang->line($seconds > 1 ? 'date_seconds' : 'date_second');
}
- return substr(trim($str), 0, -1);
+ return implode(', ', $str);
}
}
// ------------------------------------------------------------------------
-/**
- * Number of days in a month
- *
- * Takes a month/year as input and returns the number of days
- * for the given month/year. Takes leap years into consideration.
- *
- * @access public
- * @param integer a numeric month
- * @param integer a numeric year
- * @return integer
- */
if ( ! function_exists('days_in_month'))
{
+ /**
+ * Number of days in a month
+ *
+ * Takes a month/year as input and returns the number of days
+ * for the given month/year. Takes leap years into consideration.
+ *
+ * @param int a numeric month
+ * @param int a numeric year
+ * @return int
+ */
function days_in_month($month = 0, $year = '')
{
if ($month < 1 OR $month > 12)
{
return 0;
}
-
- if ( ! is_numeric($year) OR strlen($year) != 4)
+ elseif ( ! is_numeric($year) OR strlen($year) !== 4)
{
$year = date('Y');
}
+ if (defined('CAL_GREGORIAN'))
+ {
+ return cal_days_in_month(CAL_GREGORIAN, $month, $year);
+ }
+
+ if ($year >= 1970)
+ {
+ return (int) date('t', mktime(12, 0, 0, $month, 1, $year));
+ }
+
if ($month == 2)
{
- if ($year % 400 == 0 OR ($year % 4 == 0 AND $year % 100 != 0))
+ if ($year % 400 === 0 OR ($year % 4 === 0 && $year % 100 !== 0))
{
return 29;
}
@@ -291,112 +275,110 @@ if ( ! function_exists('days_in_month'))
// ------------------------------------------------------------------------
-/**
- * Converts a local Unix timestamp to GMT
- *
- * @access public
- * @param integer Unix timestamp
- * @return integer
- */
if ( ! function_exists('local_to_gmt'))
{
+ /**
+ * Converts a local Unix timestamp to GMT
+ *
+ * @param int Unix timestamp
+ * @return int
+ */
function local_to_gmt($time = '')
{
- if ($time == '')
+ if ($time === '')
+ {
$time = time();
+ }
- return mktime( gmdate("H", $time), gmdate("i", $time), gmdate("s", $time), gmdate("m", $time), gmdate("d", $time), gmdate("Y", $time));
+ return mktime(
+ gmdate('G', $time),
+ gmdate('i', $time),
+ gmdate('s', $time),
+ gmdate('n', $time),
+ gmdate('j', $time),
+ gmdate('Y', $time)
+ );
}
}
// ------------------------------------------------------------------------
-/**
- * Converts GMT time to a localized value
- *
- * Takes a Unix timestamp (in GMT) as input, and returns
- * at the local value based on the timezone and DST setting
- * submitted
- *
- * @access public
- * @param integer Unix timestamp
- * @param string timezone
- * @param bool whether DST is active
- * @return integer
- */
if ( ! function_exists('gmt_to_local'))
{
+ /**
+ * Converts GMT time to a localized value
+ *
+ * Takes a Unix timestamp (in GMT) as input, and returns
+ * at the local value based on the timezone and DST setting
+ * submitted
+ *
+ * @param int Unix timestamp
+ * @param string timezone
+ * @param bool whether DST is active
+ * @return int
+ */
function gmt_to_local($time = '', $timezone = 'UTC', $dst = FALSE)
{
- if ($time == '')
+ if ($time === '')
{
return now();
}
$time += timezones($timezone) * 3600;
- if ($dst == TRUE)
- {
- $time += 3600;
- }
-
- return $time;
+ return ($dst === TRUE) ? $time + 3600 : $time;
}
}
// ------------------------------------------------------------------------
-/**
- * Converts a MySQL Timestamp to Unix
- *
- * @access public
- * @param integer Unix timestamp
- * @return integer
- */
if ( ! function_exists('mysql_to_unix'))
{
+ /**
+ * Converts a MySQL Timestamp to Unix
+ *
+ * @param int MySQL timestamp YYYY-MM-DD HH:MM:SS
+ * @return int Unix timstamp
+ */
function mysql_to_unix($time = '')
{
// We'll remove certain characters for backward compatibility
// since the formatting changed with MySQL 4.1
// YYYY-MM-DD HH:MM:SS
- $time = str_replace('-', '', $time);
- $time = str_replace(':', '', $time);
- $time = str_replace(' ', '', $time);
+ $time = str_replace(array('-', ':', ' '), '', $time);
// YYYYMMDDHHMMSS
- return mktime(
- substr($time, 8, 2),
- substr($time, 10, 2),
- substr($time, 12, 2),
- substr($time, 4, 2),
- substr($time, 6, 2),
- substr($time, 0, 4)
- );
+ return mktime(
+ substr($time, 8, 2),
+ substr($time, 10, 2),
+ substr($time, 12, 2),
+ substr($time, 4, 2),
+ substr($time, 6, 2),
+ substr($time, 0, 4)
+ );
}
}
// ------------------------------------------------------------------------
-/**
- * Unix to "Human"
- *
- * Formats Unix timestamp to the following prototype: 2006-08-21 11:35 PM
- *
- * @access public
- * @param integer Unix timestamp
- * @param bool whether to show seconds
- * @param string format: us or euro
- * @return string
- */
if ( ! function_exists('unix_to_human'))
{
+ /**
+ * Unix to "Human"
+ *
+ * Formats Unix timestamp to the following prototype: 2006-08-21 11:35 PM
+ *
+ * @param int Unix timestamp
+ * @param bool whether to show seconds
+ * @param string format: us or euro
+ * @return string
+ */
function unix_to_human($time = '', $seconds = FALSE, $fmt = 'us')
{
- $r = date('Y', $time).'-'.date('m', $time).'-'.date('d', $time).' ';
+ $r = date('Y', $time).'-'.date('m', $time).'-'.date('d', $time).' ';
- if ($fmt == 'us')
+ if ($fmt === 'us')
{
$r .= date('h', $time).':'.date('i', $time);
}
@@ -410,9 +392,9 @@ if ( ! function_exists('unix_to_human'))
$r .= ':'.date('s', $time);
}
- if ($fmt == 'us')
+ if ($fmt === 'us')
{
- $r .= ' '.date('A', $time);
+ return $r.' '.date('A', $time);
}
return $r;
@@ -421,67 +403,46 @@ if ( ! function_exists('unix_to_human'))
// ------------------------------------------------------------------------
-/**
- * Convert "human" date to GMT
- *
- * Reverses the above process
- *
- * @access public
- * @param string format: us or euro
- * @return integer
- */
if ( ! function_exists('human_to_unix'))
{
+ /**
+ * Convert "human" date to GMT
+ *
+ * Reverses the above process
+ *
+ * @param string format: us or euro
+ * @return int
+ */
function human_to_unix($datestr = '')
{
- if ($datestr == '')
+ if ($datestr === '')
{
return FALSE;
}
- $datestr = trim($datestr);
- $datestr = preg_replace("/\040+/", ' ', $datestr);
+ $datestr = preg_replace('/\040+/', ' ', trim($datestr));
- if ( ! preg_match('/^[0-9]{2,4}\-[0-9]{1,2}\-[0-9]{1,2}\s[0-9]{1,2}:[0-9]{1,2}(?::[0-9]{1,2})?(?:\s[AP]M)?$/i', $datestr))
+ if ( ! preg_match('/^(\d{2}|\d{4})\-[0-9]{1,2}\-[0-9]{1,2}\s[0-9]{1,2}:[0-9]{1,2}(?::[0-9]{1,2})?(?:\s[AP]M)?$/i', $datestr))
{
return FALSE;
}
- $split = explode(' ', $datestr);
+ sscanf($datestr, '%d-%d-%d %s %s', $year, $month, $day, $time, $ampm);
+ sscanf($time, '%d:%d:%d', $hour, $min, $sec);
+ isset($sec) OR $sec = 0;
- $ex = explode("-", $split['0']);
-
- $year = (strlen($ex['0']) == 2) ? '20'.$ex['0'] : $ex['0'];
- $month = (strlen($ex['1']) == 1) ? '0'.$ex['1'] : $ex['1'];
- $day = (strlen($ex['2']) == 1) ? '0'.$ex['2'] : $ex['2'];
-
- $ex = explode(":", $split['1']);
-
- $hour = (strlen($ex['0']) == 1) ? '0'.$ex['0'] : $ex['0'];
- $min = (strlen($ex['1']) == 1) ? '0'.$ex['1'] : $ex['1'];
-
- if (isset($ex['2']) && preg_match('/[0-9]{1,2}/', $ex['2']))
- {
- $sec = (strlen($ex['2']) == 1) ? '0'.$ex['2'] : $ex['2'];
- }
- else
- {
- // Unless specified, seconds get set to zero.
- $sec = '00';
- }
-
- if (isset($split['2']))
+ if (isset($ampm))
{
- $ampm = strtolower($split['2']);
+ $ampm = strtolower($ampm);
- if (substr($ampm, 0, 1) == 'p' AND $hour < 12)
- $hour = $hour + 12;
-
- if (substr($ampm, 0, 1) == 'a' AND $hour == 12)
- $hour = '00';
-
- if (strlen($hour) == 1)
- $hour = '0'.$hour;
+ if ($ampm[0] === 'p' && $hour < 12)
+ {
+ $hour += 12;
+ }
+ elseif ($ampm[0] === 'a' && $hour === 12)
+ {
+ $hour = 0;
+ }
}
return mktime($hour, $min, $sec, $month, $day, $year);
@@ -490,122 +451,187 @@ if ( ! function_exists('human_to_unix'))
// ------------------------------------------------------------------------
-/**
- * Timezone Menu
- *
- * Generates a drop-down menu of timezones.
- *
- * @access public
- * @param string timezone
- * @param string classname
- * @param string menu name
- * @return string
- */
if ( ! function_exists('timezone_menu'))
{
- function timezone_menu($default = 'UTC', $class = "", $name = 'timezones')
+ /**
+ * Timezone Menu
+ *
+ * Generates a drop-down menu of timezones.
+ *
+ * @param string timezone
+ * @param string classname
+ * @param string menu name
+ * @param mixed attributes
+ * @return string
+ */
+ function timezone_menu($default = 'UTC', $class = '', $name = 'timezones', $attributes = '')
{
$CI =& get_instance();
$CI->lang->load('date');
- if ($default == 'GMT')
- $default = 'UTC';
+ $default = ($default === 'GMT') ? 'UTC' : $default;
$menu = '<select name="'.$name.'"';
- if ($class != '')
+ if ($class !== '')
{
$menu .= ' class="'.$class.'"';
}
- $menu .= ">\n";
+ $menu .= _stringify_attributes($attributes).">\n";
foreach (timezones() as $key => $val)
{
- $selected = ($default == $key) ? " selected='selected'" : '';
- $menu .= "<option value='{$key}'{$selected}>".$CI->lang->line($key)."</option>\n";
+ $selected = ($default === $key) ? ' selected="selected"' : '';
+ $menu .= '<option value="'.$key.'"'.$selected.'>'.$CI->lang->line($key)."</option>\n";
}
- $menu .= "</select>";
-
- return $menu;
+ return $menu.'</select>';
}
}
// ------------------------------------------------------------------------
-/**
- * Timezones
- *
- * Returns an array of timezones. This is a helper function
- * for various other ones in this library
- *
- * @access public
- * @param string timezone
- * @return string
- */
if ( ! function_exists('timezones'))
{
+ /**
+ * Timezones
+ *
+ * Returns an array of timezones. This is a helper function
+ * for various other ones in this library
+ *
+ * @param string timezone
+ * @return string
+ */
function timezones($tz = '')
{
// Note: Don't change the order of these even though
// some items appear to be in the wrong order
$zones = array(
- 'UM12' => -12,
- 'UM11' => -11,
- 'UM10' => -10,
- 'UM95' => -9.5,
- 'UM9' => -9,
- 'UM8' => -8,
- 'UM7' => -7,
- 'UM6' => -6,
- 'UM5' => -5,
- 'UM45' => -4.5,
- 'UM4' => -4,
- 'UM35' => -3.5,
- 'UM3' => -3,
- 'UM2' => -2,
- 'UM1' => -1,
- 'UTC' => 0,
- 'UP1' => +1,
- 'UP2' => +2,
- 'UP3' => +3,
- 'UP35' => +3.5,
- 'UP4' => +4,
- 'UP45' => +4.5,
- 'UP5' => +5,
- 'UP55' => +5.5,
- 'UP575' => +5.75,
- 'UP6' => +6,
- 'UP65' => +6.5,
- 'UP7' => +7,
- 'UP8' => +8,
- 'UP875' => +8.75,
- 'UP9' => +9,
- 'UP95' => +9.5,
- 'UP10' => +10,
- 'UP105' => +10.5,
- 'UP11' => +11,
- 'UP115' => +11.5,
- 'UP12' => +12,
- 'UP1275' => +12.75,
- 'UP13' => +13,
- 'UP14' => +14
- );
-
- if ($tz == '')
+ 'UM12' => -12,
+ 'UM11' => -11,
+ 'UM10' => -10,
+ 'UM95' => -9.5,
+ 'UM9' => -9,
+ 'UM8' => -8,
+ 'UM7' => -7,
+ 'UM6' => -6,
+ 'UM5' => -5,
+ 'UM45' => -4.5,
+ 'UM4' => -4,
+ 'UM35' => -3.5,
+ 'UM3' => -3,
+ 'UM2' => -2,
+ 'UM1' => -1,
+ 'UTC' => 0,
+ 'UP1' => +1,
+ 'UP2' => +2,
+ 'UP3' => +3,
+ 'UP35' => +3.5,
+ 'UP4' => +4,
+ 'UP45' => +4.5,
+ 'UP5' => +5,
+ 'UP55' => +5.5,
+ 'UP575' => +5.75,
+ 'UP6' => +6,
+ 'UP65' => +6.5,
+ 'UP7' => +7,
+ 'UP8' => +8,
+ 'UP875' => +8.75,
+ 'UP9' => +9,
+ 'UP95' => +9.5,
+ 'UP10' => +10,
+ 'UP105' => +10.5,
+ 'UP11' => +11,
+ 'UP115' => +11.5,
+ 'UP12' => +12,
+ 'UP1275' => +12.75,
+ 'UP13' => +13,
+ 'UP14' => +14
+ );
+
+ if ($tz === '')
{
return $zones;
}
- if ($tz == 'GMT')
- $tz = 'UTC';
-
- return ( ! isset($zones[$tz])) ? 0 : $zones[$tz];
+ return isset($zones[$tz]) ? $zones[$tz] : 0;
}
}
+// ------------------------------------------------------------------------
+
+if ( ! function_exists('date_range'))
+{
+ /**
+ * Date range
+ *
+ * Returns a list of dates within a specified period.
+ *
+ * @param int unix_start UNIX timestamp of period start date
+ * @param int unix_end|days UNIX timestamp of period end date
+ * or interval in days.
+ * @param mixed is_unix Specifies whether the second parameter
+ * is a UNIX timestamp or a day interval
+ * - TRUE or 'unix' for a timestamp
+ * - FALSE or 'days' for an interval
+ * @param string date_format Output date format, same as in date()
+ * @return array
+ */
+ function date_range($unix_start = '', $mixed = '', $is_unix = TRUE, $format = 'Y-m-d')
+ {
+ if ($unix_start == '' OR $mixed == '' OR $format == '')
+ {
+ return FALSE;
+ }
+
+ $is_unix = ! ( ! $is_unix OR $is_unix === 'days');
+
+ // Validate input and try strtotime() on invalid timestamps/intervals, just in case
+ if ( ( ! ctype_digit((string) $unix_start) && ($unix_start = @strtotime($unix_start)) === FALSE)
+ OR ( ! ctype_digit((string) $mixed) && ($is_unix === FALSE OR ($mixed = @strtotime($mixed)) === FALSE))
+ OR ($is_unix === TRUE && $mixed < $unix_start))
+ {
+ return FALSE;
+ }
+
+ if ($is_unix && ($unix_start == $mixed OR date($format, $unix_start) === date($format, $mixed)))
+ {
+ return array(date($format, $unix_start));
+ }
+
+ $range = array();
+
+ $from = new DateTime();
+ $from->setTimestamp($unix_start);
-/* End of file date_helper.php */
-/* Location: ./system/helpers/date_helper.php */ \ No newline at end of file
+ if ($is_unix)
+ {
+ $arg = new DateTime();
+ $arg->setTimestamp($mixed);
+ }
+ else
+ {
+ $arg = (int) $mixed;
+ }
+
+ $period = new DatePeriod($from, new DateInterval('P1D'), $arg);
+ foreach ($period as $date)
+ {
+ $range[] = $date->format($format);
+ }
+
+ /* If a period end date was passed to the DatePeriod constructor, it might not
+ * be in our results. Not sure if this is a bug or it's just possible because
+ * the end date might actually be less than 24 hours away from the previously
+ * generated DateTime object, but either way - we have to append it manually.
+ */
+ if ( ! is_int($arg) && $range[count($range) - 1] !== $arg->format($format))
+ {
+ $range[] = $arg->format($format);
+ }
+
+ return $range;
+ }
+}
diff --git a/system/helpers/directory_helper.php b/system/helpers/directory_helper.php
index 0c0c39c0d..d747a96b2 100644
--- a/system/helpers/directory_helper.php
+++ b/system/helpers/directory_helper.php
@@ -1,19 +1,42 @@
-<?php if ( ! defined('BASEPATH')) exit('No direct script access allowed');
+<?php
/**
* CodeIgniter
*
- * An open source application development framework for PHP 5.1.6 or newer
+ * An open source application development framework for PHP
*
- * @package CodeIgniter
- * @author ExpressionEngine Dev Team
- * @copyright Copyright (c) 2008 - 2014, EllisLab, Inc.
- * @license http://codeigniter.com/user_guide/license.html
- * @link http://codeigniter.com
- * @since Version 1.0
+ * This content is released under the MIT License (MIT)
+ *
+ * Copyright (c) 2019 - 2022, CodeIgniter Foundation
+ *
+ * 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.
+ *
+ * @package CodeIgniter
+ * @author EllisLab Dev Team
+ * @copyright Copyright (c) 2008 - 2014, EllisLab, Inc. (https://ellislab.com/)
+ * @copyright Copyright (c) 2014 - 2019, British Columbia Institute of Technology (https://bcit.ca/)
+ * @copyright Copyright (c) 2019 - 2022, CodeIgniter Foundation (https://codeigniter.com/)
+ * @license https://opensource.org/licenses/MIT MIT License
+ * @link https://codeigniter.com
+ * @since Version 1.0.0
* @filesource
*/
-
-// ------------------------------------------------------------------------
+defined('BASEPATH') OR exit('No direct script access allowed');
/**
* CodeIgniter Directory Helpers
@@ -21,26 +44,27 @@
* @package CodeIgniter
* @subpackage Helpers
* @category Helpers
- * @author ExpressionEngine Dev Team
- * @link http://codeigniter.com/user_guide/helpers/directory_helper.html
+ * @author EllisLab Dev Team
+ * @link https://codeigniter.com/userguide3/helpers/directory_helper.html
*/
// ------------------------------------------------------------------------
-/**
- * Create a Directory Map
- *
- * Reads the specified directory and builds an array
- * representation of it. Sub-folders contained with the
- * directory will be mapped as well.
- *
- * @access public
- * @param string path to source
- * @param int depth of directories to traverse (0 = fully recursive, 1 = current dir, etc)
- * @return array
- */
if ( ! function_exists('directory_map'))
{
+ /**
+ * Create a Directory Map
+ *
+ * Reads the specified directory and builds an array
+ * representation of it. Sub-folders contained with the
+ * directory will be mapped as well.
+ *
+ * @param string $source_dir Path to source
+ * @param int $directory_depth Depth of directories to traverse
+ * (0 = fully recursive, 1 = current dir, etc)
+ * @param bool $hidden Whether to show hidden files
+ * @return array
+ */
function directory_map($source_dir, $directory_depth = 0, $hidden = FALSE)
{
if ($fp = @opendir($source_dir))
@@ -52,14 +76,16 @@ if ( ! function_exists('directory_map'))
while (FALSE !== ($file = readdir($fp)))
{
// Remove '.', '..', and hidden files [optional]
- if ( ! trim($file, '.') OR ($hidden == FALSE && $file[0] == '.'))
+ if ($file === '.' OR $file === '..' OR ($hidden === FALSE && $file[0] === '.'))
{
continue;
}
- if (($directory_depth < 1 OR $new_depth > 0) && @is_dir($source_dir.$file))
+ is_dir($source_dir.$file) && $file .= DIRECTORY_SEPARATOR;
+
+ if (($directory_depth < 1 OR $new_depth > 0) && is_dir($source_dir.$file))
{
- $filedata[$file] = directory_map($source_dir.$file.DIRECTORY_SEPARATOR, $new_depth, $hidden);
+ $filedata[$file] = directory_map($source_dir.$file, $new_depth, $hidden);
}
else
{
@@ -74,7 +100,3 @@ if ( ! function_exists('directory_map'))
return FALSE;
}
}
-
-
-/* End of file directory_helper.php */
-/* Location: ./system/helpers/directory_helper.php */ \ No newline at end of file
diff --git a/system/helpers/download_helper.php b/system/helpers/download_helper.php
index 34e29447a..2c72c563a 100644
--- a/system/helpers/download_helper.php
+++ b/system/helpers/download_helper.php
@@ -1,19 +1,42 @@
-<?php if ( ! defined('BASEPATH')) exit('No direct script access allowed');
+<?php
/**
* CodeIgniter
*
- * An open source application development framework for PHP 5.1.6 or newer
+ * An open source application development framework for PHP
*
- * @package CodeIgniter
- * @author ExpressionEngine Dev Team
- * @copyright Copyright (c) 2008 - 2014, EllisLab, Inc.
- * @license http://codeigniter.com/user_guide/license.html
- * @link http://codeigniter.com
- * @since Version 1.0
+ * This content is released under the MIT License (MIT)
+ *
+ * Copyright (c) 2019 - 2022, CodeIgniter Foundation
+ *
+ * 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.
+ *
+ * @package CodeIgniter
+ * @author EllisLab Dev Team
+ * @copyright Copyright (c) 2008 - 2014, EllisLab, Inc. (https://ellislab.com/)
+ * @copyright Copyright (c) 2014 - 2019, British Columbia Institute of Technology (https://bcit.ca/)
+ * @copyright Copyright (c) 2019 - 2022, CodeIgniter Foundation (https://codeigniter.com/)
+ * @license https://opensource.org/licenses/MIT MIT License
+ * @link https://codeigniter.com
+ * @since Version 1.0.0
* @filesource
*/
-
-// ------------------------------------------------------------------------
+defined('BASEPATH') OR exit('No direct script access allowed');
/**
* CodeIgniter Download Helpers
@@ -21,87 +44,138 @@
* @package CodeIgniter
* @subpackage Helpers
* @category Helpers
- * @author ExpressionEngine Dev Team
- * @link http://codeigniter.com/user_guide/helpers/download_helper.html
+ * @author EllisLab Dev Team
+ * @link https://codeigniter.com/userguide3/helpers/download_helper.html
*/
// ------------------------------------------------------------------------
-/**
- * Force Download
- *
- * Generates headers that force a download to happen
- *
- * @access public
- * @param string filename
- * @param mixed the data to be downloaded
- * @return void
- */
if ( ! function_exists('force_download'))
{
- function force_download($filename = '', $data = '')
+ /**
+ * Force Download
+ *
+ * Generates headers that force a download to happen
+ *
+ * @param mixed filename (or an array of local file path => destination filename)
+ * @param mixed the data to be downloaded
+ * @param bool whether to try and send the actual file MIME type
+ * @return void
+ */
+ function force_download($filename = '', $data = '', $set_mime = FALSE)
{
- if ($filename == '' OR $data == '')
+ if ($filename === '' OR $data === '')
{
- return FALSE;
+ return;
}
+ elseif ($data === NULL)
+ {
+ // Is $filename an array as ['local source path' => 'destination filename']?
+ if (is_array($filename))
+ {
+ if (count($filename) !== 1)
+ {
+ return;
+ }
+
+ reset($filename);
+ $filepath = key($filename);
+ $filename = current($filename);
+
+ if (is_int($filepath))
+ {
+ return;
+ }
+ }
+ else
+ {
+ $filepath = $filename;
+ $filename = explode('/', str_replace(DIRECTORY_SEPARATOR, '/', $filename));
+ $filename = end($filename);
+ }
- // Try to determine if the filename includes a file extension.
- // We need it in order to set the MIME type
- if (FALSE === strpos($filename, '.'))
+ if ( ! @is_file($filepath) OR ($filesize = @filesize($filepath)) === FALSE)
+ {
+ return;
+ }
+ }
+ else
{
- return FALSE;
+ $filesize = strlen($data);
}
- // Grab the file extension
+ // Set the default MIME type to send
+ $mime = 'application/octet-stream';
+
$x = explode('.', $filename);
$extension = end($x);
- // Load the mime types
- if (defined('ENVIRONMENT') AND is_file(APPPATH.'config/'.ENVIRONMENT.'/mimes.php'))
- {
- include(APPPATH.'config/'.ENVIRONMENT.'/mimes.php');
- }
- elseif (is_file(APPPATH.'config/mimes.php'))
+ if ($set_mime === TRUE)
{
- include(APPPATH.'config/mimes.php');
+ if (count($x) === 1 OR $extension === '')
+ {
+ /* If we're going to detect the MIME type,
+ * we'll need a file extension.
+ */
+ return;
+ }
+
+ // Load the mime types
+ $mimes =& get_mimes();
+
+ // Only change the default MIME if we can find one
+ if (isset($mimes[$extension]))
+ {
+ $mime = is_array($mimes[$extension]) ? $mimes[$extension][0] : $mimes[$extension];
+ }
}
- // Set a default mime if we can't find it
- if ( ! isset($mimes[$extension]))
+ /* It was reported that browsers on Android 2.1 (and possibly older as well)
+ * need to have the filename extension upper-cased in order to be able to
+ * download it.
+ *
+ * Reference: http://digiblog.de/2011/04/19/android-and-the-download-file-headers/
+ */
+ if (count($x) !== 1 && isset($_SERVER['HTTP_USER_AGENT']) && preg_match('/Android\s(1|2\.[01])/', $_SERVER['HTTP_USER_AGENT']))
{
- $mime = 'application/octet-stream';
+ $x[count($x) - 1] = strtoupper($extension);
+ $filename = implode('.', $x);
}
- else
+
+ // Clean output buffer
+ if (ob_get_level() !== 0 && @ob_end_clean() === FALSE)
{
- $mime = (is_array($mimes[$extension])) ? $mimes[$extension][0] : $mimes[$extension];
+ @ob_clean();
}
+ // RFC 6266 allows for multibyte filenames, but only in UTF-8,
+ // so we have to make it conditional ...
+ $charset = strtoupper(config_item('charset'));
+ $utf8_filename = ($charset !== 'UTF-8')
+ ? get_instance()->utf8->convert_to_utf8($filename, $charset)
+ : $filename;
+ isset($utf8_filename[0]) && $utf8_filename = " filename*=UTF-8''".rawurlencode($utf8_filename);
+
// Generate the server headers
- if (strpos($_SERVER['HTTP_USER_AGENT'], "MSIE") !== FALSE)
+ header('Content-Type: '.$mime);
+ header('Content-Disposition: attachment; filename="'.$filename.'";'.$utf8_filename);
+ header('Expires: 0');
+ header('Content-Transfer-Encoding: binary');
+ header('Content-Length: '.$filesize);
+ header('Cache-Control: private, no-transform, no-store, must-revalidate');
+
+ // If we have raw data - just dump it
+ if ($data !== NULL)
{
- header('Content-Type: "'.$mime.'"');
- header('Content-Disposition: attachment; filename="'.$filename.'"');
- header('Expires: 0');
- header('Cache-Control: must-revalidate, post-check=0, pre-check=0');
- header("Content-Transfer-Encoding: binary");
- header('Pragma: public');
- header("Content-Length: ".strlen($data));
+ exit($data);
}
- else
+
+ // Flush the file
+ if (@readfile($filepath) === FALSE)
{
- header('Content-Type: "'.$mime.'"');
- header('Content-Disposition: attachment; filename="'.$filename.'"');
- header("Content-Transfer-Encoding: binary");
- header('Expires: 0');
- header('Pragma: no-cache');
- header("Content-Length: ".strlen($data));
+ return;
}
- exit($data);
+ exit;
}
}
-
-
-/* End of file download_helper.php */
-/* Location: ./system/helpers/download_helper.php */ \ No newline at end of file
diff --git a/system/helpers/email_helper.php b/system/helpers/email_helper.php
deleted file mode 100644
index 8c2e222c5..000000000
--- a/system/helpers/email_helper.php
+++ /dev/null
@@ -1,62 +0,0 @@
-<?php if ( ! defined('BASEPATH')) exit('No direct script access allowed');
-/**
- * CodeIgniter
- *
- * An open source application development framework for PHP 5.1.6 or newer
- *
- * @package CodeIgniter
- * @author ExpressionEngine Dev Team
- * @copyright Copyright (c) 2008 - 2014, EllisLab, Inc.
- * @license http://codeigniter.com/user_guide/license.html
- * @link http://codeigniter.com
- * @since Version 1.0
- * @filesource
- */
-
-// ------------------------------------------------------------------------
-
-/**
- * CodeIgniter Email Helpers
- *
- * @package CodeIgniter
- * @subpackage Helpers
- * @category Helpers
- * @author ExpressionEngine Dev Team
- * @link http://codeigniter.com/user_guide/helpers/email_helper.html
- */
-
-// ------------------------------------------------------------------------
-
-/**
- * Validate email address
- *
- * @access public
- * @return bool
- */
-if ( ! function_exists('valid_email'))
-{
- function valid_email($address)
- {
- return ( ! preg_match("/^([a-z0-9\+_\-]+)(\.[a-z0-9\+_\-]+)*@([a-z0-9\-]+\.)+[a-z]{2,6}$/ix", $address)) ? FALSE : TRUE;
- }
-}
-
-// ------------------------------------------------------------------------
-
-/**
- * Send an email
- *
- * @access public
- * @return bool
- */
-if ( ! function_exists('send_email'))
-{
- function send_email($recipient, $subject = 'Test email', $message = 'Hello World')
- {
- return mail($recipient, $subject, $message);
- }
-}
-
-
-/* End of file email_helper.php */
-/* Location: ./system/helpers/email_helper.php */ \ No newline at end of file
diff --git a/system/helpers/file_helper.php b/system/helpers/file_helper.php
index 791a4622d..a751f771e 100644
--- a/system/helpers/file_helper.php
+++ b/system/helpers/file_helper.php
@@ -1,19 +1,42 @@
-<?php if ( ! defined('BASEPATH')) exit('No direct script access allowed');
+<?php
/**
* CodeIgniter
*
- * An open source application development framework for PHP 5.1.6 or newer
+ * An open source application development framework for PHP
*
- * @package CodeIgniter
- * @author ExpressionEngine Dev Team
- * @copyright Copyright (c) 2008 - 2014, EllisLab, Inc.
- * @license http://codeigniter.com/user_guide/license.html
- * @link http://codeigniter.com
- * @since Version 1.0
+ * This content is released under the MIT License (MIT)
+ *
+ * Copyright (c) 2019 - 2022, CodeIgniter Foundation
+ *
+ * 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.
+ *
+ * @package CodeIgniter
+ * @author EllisLab Dev Team
+ * @copyright Copyright (c) 2008 - 2014, EllisLab, Inc. (https://ellislab.com/)
+ * @copyright Copyright (c) 2014 - 2019, British Columbia Institute of Technology (https://bcit.ca/)
+ * @copyright Copyright (c) 2019 - 2022, CodeIgniter Foundation (https://codeigniter.com/)
+ * @license https://opensource.org/licenses/MIT MIT License
+ * @link https://codeigniter.com
+ * @since Version 1.0.0
* @filesource
*/
-
-// ------------------------------------------------------------------------
+defined('BASEPATH') OR exit('No direct script access allowed');
/**
* CodeIgniter File Helpers
@@ -21,107 +44,71 @@
* @package CodeIgniter
* @subpackage Helpers
* @category Helpers
- * @author ExpressionEngine Dev Team
- * @link http://codeigniter.com/user_guide/helpers/file_helpers.html
+ * @author EllisLab Dev Team
+ * @link https://codeigniter.com/userguide3/helpers/file_helper.html
*/
// ------------------------------------------------------------------------
-/**
- * Read File
- *
- * Opens the file specfied in the path and returns it as a string.
- *
- * @access public
- * @param string path to file
- * @return string
- */
-if ( ! function_exists('read_file'))
+if ( ! function_exists('write_file'))
{
- function read_file($file)
+ /**
+ * Write File
+ *
+ * Writes data to the file specified in the path.
+ * Creates a new file if non-existent.
+ *
+ * @param string $path File path
+ * @param string $data Data to write
+ * @param string $mode fopen() mode (default: 'wb')
+ * @return bool
+ */
+ function write_file($path, $data, $mode = 'wb')
{
- if ( ! file_exists($file))
- {
- return FALSE;
- }
-
- if (function_exists('file_get_contents'))
- {
- return file_get_contents($file);
- }
-
- if ( ! $fp = @fopen($file, FOPEN_READ))
+ if ( ! $fp = @fopen($path, $mode))
{
return FALSE;
}
- flock($fp, LOCK_SH);
-
- $data = '';
- if (filesize($file) > 0)
- {
- $data =& fread($fp, filesize($file));
- }
-
- flock($fp, LOCK_UN);
- fclose($fp);
-
- return $data;
- }
-}
-
-// ------------------------------------------------------------------------
+ flock($fp, LOCK_EX);
-/**
- * Write File
- *
- * Writes data to the file specified in the path.
- * Creates a new file if non-existent.
- *
- * @access public
- * @param string path to file
- * @param string file data
- * @return bool
- */
-if ( ! function_exists('write_file'))
-{
- function write_file($path, $data, $mode = FOPEN_WRITE_CREATE_DESTRUCTIVE)
- {
- if ( ! $fp = @fopen($path, $mode))
+ for ($result = $written = 0, $length = strlen($data); $written < $length; $written += $result)
{
- return FALSE;
+ if (($result = fwrite($fp, substr($data, $written))) === FALSE)
+ {
+ break;
+ }
}
- flock($fp, LOCK_EX);
- fwrite($fp, $data);
flock($fp, LOCK_UN);
fclose($fp);
- return TRUE;
+ return is_int($result);
}
}
// ------------------------------------------------------------------------
-/**
- * Delete Files
- *
- * Deletes all files contained in the supplied directory path.
- * Files must be writable or owned by the system in order to be deleted.
- * If the second parameter is set to TRUE, any directories contained
- * within the supplied base directory will be nuked as well.
- *
- * @access public
- * @param string path to file
- * @param bool whether to delete any directories found in the path
- * @return bool
- */
if ( ! function_exists('delete_files'))
{
- function delete_files($path, $del_dir = FALSE, $level = 0)
+ /**
+ * Delete Files
+ *
+ * Deletes all files contained in the supplied directory path.
+ * Files must be writable or owned by the system in order to be deleted.
+ * If the second parameter is set to TRUE, any directories contained
+ * within the supplied base directory will be nuked as well.
+ *
+ * @param string $path File path
+ * @param bool $del_dir Whether to delete any directories found in the path
+ * @param bool $htdocs Whether to skip deleting .htaccess and index page files
+ * @param int $_level Current directory depth level (default: 0; internal use only)
+ * @return bool
+ */
+ function delete_files($path, $del_dir = FALSE, $htdocs = FALSE, $_level = 0)
{
// Trim the trailing slash
- $path = rtrim($path, DIRECTORY_SEPARATOR);
+ $path = rtrim($path, '/\\');
if ( ! $current_dir = @opendir($path))
{
@@ -130,49 +117,44 @@ if ( ! function_exists('delete_files'))
while (FALSE !== ($filename = @readdir($current_dir)))
{
- if ($filename != "." and $filename != "..")
+ if ($filename !== '.' && $filename !== '..')
{
- if (is_dir($path.DIRECTORY_SEPARATOR.$filename))
+ $filepath = $path.DIRECTORY_SEPARATOR.$filename;
+
+ if (is_dir($filepath) && $filename[0] !== '.' && ! is_link($filepath))
{
- // Ignore empty folders
- if (substr($filename, 0, 1) != '.')
- {
- delete_files($path.DIRECTORY_SEPARATOR.$filename, $del_dir, $level + 1);
- }
+ delete_files($filepath, $del_dir, $htdocs, $_level + 1);
}
- else
+ elseif ($htdocs !== TRUE OR ! preg_match('/^(\.htaccess|index\.(html|htm|php)|web\.config)$/i', $filename))
{
- unlink($path.DIRECTORY_SEPARATOR.$filename);
+ @unlink($filepath);
}
}
}
- @closedir($current_dir);
- if ($del_dir == TRUE AND $level > 0)
- {
- return @rmdir($path);
- }
+ closedir($current_dir);
- return TRUE;
+ return ($del_dir === TRUE && $_level > 0)
+ ? @rmdir($path)
+ : TRUE;
}
}
// ------------------------------------------------------------------------
-/**
- * Get Filenames
- *
- * Reads the specified directory and builds an array containing the filenames.
- * Any sub-folders contained within the specified path are read as well.
- *
- * @access public
- * @param string path to source
- * @param bool whether to include the path as part of the filename
- * @param bool internal variable to determine recursion status - do not use in calls
- * @return array
- */
if ( ! function_exists('get_filenames'))
{
+ /**
+ * Get Filenames
+ *
+ * Reads the specified directory and builds an array containing the filenames.
+ * Any sub-folders contained within the specified path are read as well.
+ *
+ * @param string path to source
+ * @param bool whether to include the path as part of the filename
+ * @param bool internal variable to determine recursion status - do not use in calls
+ * @return array
+ */
function get_filenames($source_dir, $include_path = FALSE, $_recursion = FALSE)
{
static $_filedata = array();
@@ -188,42 +170,41 @@ if ( ! function_exists('get_filenames'))
while (FALSE !== ($file = readdir($fp)))
{
- if (@is_dir($source_dir.$file) && strncmp($file, '.', 1) !== 0)
+ if (is_dir($source_dir.$file) && $file[0] !== '.')
{
get_filenames($source_dir.$file.DIRECTORY_SEPARATOR, $include_path, TRUE);
}
- elseif (strncmp($file, '.', 1) !== 0)
+ elseif ($file[0] !== '.')
{
- $_filedata[] = ($include_path == TRUE) ? $source_dir.$file : $file;
+ $_filedata[] = ($include_path === TRUE) ? $source_dir.$file : $file;
}
}
+
+ closedir($fp);
return $_filedata;
}
- else
- {
- return FALSE;
- }
+
+ return FALSE;
}
}
// --------------------------------------------------------------------
-/**
- * Get Directory File Information
- *
- * Reads the specified directory and builds an array containing the filenames,
- * filesize, dates, and permissions
- *
- * Any sub-folders contained within the specified path are read as well.
- *
- * @access public
- * @param string path to source
- * @param bool Look only at the top level directory specified?
- * @param bool internal variable to determine recursion status - do not use in calls
- * @return array
- */
if ( ! function_exists('get_dir_file_info'))
{
+ /**
+ * Get Directory File Information
+ *
+ * Reads the specified directory and builds an array containing the filenames,
+ * filesize, dates, and permissions
+ *
+ * Any sub-folders contained within the specified path are read as well.
+ *
+ * @param string path to source
+ * @param bool Look only at the top level directory specified?
+ * @param bool internal variable to determine recursion status - do not use in calls
+ * @return array
+ */
function get_dir_file_info($source_dir, $top_level_only = TRUE, $_recursion = FALSE)
{
static $_filedata = array();
@@ -238,49 +219,47 @@ if ( ! function_exists('get_dir_file_info'))
$source_dir = rtrim(realpath($source_dir), DIRECTORY_SEPARATOR).DIRECTORY_SEPARATOR;
}
- // foreach (scandir($source_dir, 1) as $file) // In addition to being PHP5+, scandir() is simply not as fast
+ // Used to be foreach (scandir($source_dir, 1) as $file), but scandir() is simply not as fast
while (FALSE !== ($file = readdir($fp)))
{
- if (@is_dir($source_dir.$file) AND strncmp($file, '.', 1) !== 0 AND $top_level_only === FALSE)
+ if (is_dir($source_dir.$file) && $file[0] !== '.' && $top_level_only === FALSE)
{
get_dir_file_info($source_dir.$file.DIRECTORY_SEPARATOR, $top_level_only, TRUE);
}
- elseif (strncmp($file, '.', 1) !== 0)
+ elseif ($file[0] !== '.')
{
- $_filedata[$file] = get_file_info($source_dir.$file);
- $_filedata[$file]['relative_path'] = $relative_path;
+ $filedata = get_dir_file_info($source_dir.$file);
+ $filedata['relative_path'] = $relative_path;
+ $_filedata[] = $filedata;
}
}
+ closedir($fp);
return $_filedata;
}
- else
- {
- return FALSE;
- }
+
+ return FALSE;
}
}
// --------------------------------------------------------------------
-/**
-* Get File Info
-*
-* Given a file and path, returns the name, path, size, date modified
-* Second parameter allows you to explicitly declare what information you want returned
-* Options are: name, server_path, size, date, readable, writable, executable, fileperms
-* Returns FALSE if the file cannot be found.
-*
-* @access public
-* @param string path to file
-* @param mixed array or comma separated string of information returned
-* @return array
-*/
if ( ! function_exists('get_file_info'))
{
+ /**
+ * Get File Info
+ *
+ * Given a file and path, returns the name, path, size, date modified
+ * Second parameter allows you to explicitly declare what information you want returned
+ * Options are: name, server_path, size, date, readable, writable, executable, fileperms
+ * Returns FALSE if the file cannot be found.
+ *
+ * @param string path to file
+ * @param mixed array or comma separated string of information returned
+ * @return array
+ */
function get_file_info($file, $returned_values = array('name', 'server_path', 'size', 'date'))
{
-
if ( ! file_exists($file))
{
return FALSE;
@@ -296,7 +275,7 @@ if ( ! function_exists('get_file_info'))
switch ($key)
{
case 'name':
- $fileinfo['name'] = substr(strrchr($file, DIRECTORY_SEPARATOR), 1);
+ $fileinfo['name'] = basename($file);
break;
case 'server_path':
$fileinfo['server_path'] = $file;
@@ -311,8 +290,7 @@ if ( ! function_exists('get_file_info'))
$fileinfo['readable'] = is_readable($file);
break;
case 'writable':
- // There are known problems using is_weritable on IIS. It may not be reliable - consider fileperms()
- $fileinfo['writable'] = is_writable($file);
+ $fileinfo['writable'] = is_really_writable($file);
break;
case 'executable':
$fileinfo['executable'] = is_executable($file);
@@ -329,104 +307,87 @@ if ( ! function_exists('get_file_info'))
// --------------------------------------------------------------------
-/**
- * Get Mime by Extension
- *
- * Translates a file extension into a mime type based on config/mimes.php.
- * Returns FALSE if it can't determine the type, or open the mime config file
- *
- * Note: this is NOT an accurate way of determining file mime types, and is here strictly as a convenience
- * It should NOT be trusted, and should certainly NOT be used for security
- *
- * @access public
- * @param string path to file
- * @return mixed
- */
if ( ! function_exists('get_mime_by_extension'))
{
- function get_mime_by_extension($file)
+ /**
+ * Get Mime by Extension
+ *
+ * Translates a file extension into a mime type based on config/mimes.php.
+ * Returns FALSE if it can't determine the type, or open the mime config file
+ *
+ * Note: this is NOT an accurate way of determining file mime types, and is here strictly as a convenience
+ * It should NOT be trusted, and should certainly NOT be used for security
+ *
+ * @param string $filename File name
+ * @return string
+ */
+ function get_mime_by_extension($filename)
{
- $extension = strtolower(substr(strrchr($file, '.'), 1));
-
- global $mimes;
+ static $mimes;
if ( ! is_array($mimes))
{
- if (defined('ENVIRONMENT') AND is_file(APPPATH.'config/'.ENVIRONMENT.'/mimes.php'))
- {
- include(APPPATH.'config/'.ENVIRONMENT.'/mimes.php');
- }
- elseif (is_file(APPPATH.'config/mimes.php'))
- {
- include(APPPATH.'config/mimes.php');
- }
+ $mimes = get_mimes();
- if ( ! is_array($mimes))
+ if (empty($mimes))
{
return FALSE;
}
}
- if (array_key_exists($extension, $mimes))
- {
- if (is_array($mimes[$extension]))
- {
- // Multiple mime types, just give the first one
- return current($mimes[$extension]);
- }
- else
- {
- return $mimes[$extension];
- }
- }
- else
+ $extension = strtolower(substr(strrchr($filename, '.'), 1));
+
+ if (isset($mimes[$extension]))
{
- return FALSE;
+ return is_array($mimes[$extension])
+ ? current($mimes[$extension]) // Multiple mime types, just give the first one
+ : $mimes[$extension];
}
+
+ return FALSE;
}
}
// --------------------------------------------------------------------
-/**
- * Symbolic Permissions
- *
- * Takes a numeric value representing a file's permissions and returns
- * standard symbolic notation representing that value
- *
- * @access public
- * @param int
- * @return string
- */
if ( ! function_exists('symbolic_permissions'))
{
+ /**
+ * Symbolic Permissions
+ *
+ * Takes a numeric value representing a file's permissions and returns
+ * standard symbolic notation representing that value
+ *
+ * @param int $perms Permissions
+ * @return string
+ */
function symbolic_permissions($perms)
{
- if (($perms & 0xC000) == 0xC000)
+ if (($perms & 0xC000) === 0xC000)
{
$symbolic = 's'; // Socket
}
- elseif (($perms & 0xA000) == 0xA000)
+ elseif (($perms & 0xA000) === 0xA000)
{
$symbolic = 'l'; // Symbolic Link
}
- elseif (($perms & 0x8000) == 0x8000)
+ elseif (($perms & 0x8000) === 0x8000)
{
$symbolic = '-'; // Regular
}
- elseif (($perms & 0x6000) == 0x6000)
+ elseif (($perms & 0x6000) === 0x6000)
{
$symbolic = 'b'; // Block special
}
- elseif (($perms & 0x4000) == 0x4000)
+ elseif (($perms & 0x4000) === 0x4000)
{
$symbolic = 'd'; // Directory
}
- elseif (($perms & 0x2000) == 0x2000)
+ elseif (($perms & 0x2000) === 0x2000)
{
$symbolic = 'c'; // Character special
}
- elseif (($perms & 0x1000) == 0x1000)
+ elseif (($perms & 0x1000) === 0x1000)
{
$symbolic = 'p'; // FIFO pipe
}
@@ -436,19 +397,19 @@ if ( ! function_exists('symbolic_permissions'))
}
// Owner
- $symbolic .= (($perms & 0x0100) ? 'r' : '-');
- $symbolic .= (($perms & 0x0080) ? 'w' : '-');
- $symbolic .= (($perms & 0x0040) ? (($perms & 0x0800) ? 's' : 'x' ) : (($perms & 0x0800) ? 'S' : '-'));
+ $symbolic .= (($perms & 0x0100) ? 'r' : '-')
+ .(($perms & 0x0080) ? 'w' : '-')
+ .(($perms & 0x0040) ? (($perms & 0x0800) ? 's' : 'x' ) : (($perms & 0x0800) ? 'S' : '-'));
// Group
- $symbolic .= (($perms & 0x0020) ? 'r' : '-');
- $symbolic .= (($perms & 0x0010) ? 'w' : '-');
- $symbolic .= (($perms & 0x0008) ? (($perms & 0x0400) ? 's' : 'x' ) : (($perms & 0x0400) ? 'S' : '-'));
+ $symbolic .= (($perms & 0x0020) ? 'r' : '-')
+ .(($perms & 0x0010) ? 'w' : '-')
+ .(($perms & 0x0008) ? (($perms & 0x0400) ? 's' : 'x' ) : (($perms & 0x0400) ? 'S' : '-'));
// World
- $symbolic .= (($perms & 0x0004) ? 'r' : '-');
- $symbolic .= (($perms & 0x0002) ? 'w' : '-');
- $symbolic .= (($perms & 0x0001) ? (($perms & 0x0200) ? 't' : 'x' ) : (($perms & 0x0200) ? 'T' : '-'));
+ $symbolic .= (($perms & 0x0004) ? 'r' : '-')
+ .(($perms & 0x0002) ? 'w' : '-')
+ .(($perms & 0x0001) ? (($perms & 0x0200) ? 't' : 'x' ) : (($perms & 0x0200) ? 'T' : '-'));
return $symbolic;
}
@@ -456,24 +417,19 @@ if ( ! function_exists('symbolic_permissions'))
// --------------------------------------------------------------------
-/**
- * Octal Permissions
- *
- * Takes a numeric value representing a file's permissions and returns
- * a three character string representing the file's octal permissions
- *
- * @access public
- * @param int
- * @return string
- */
if ( ! function_exists('octal_permissions'))
{
+ /**
+ * Octal Permissions
+ *
+ * Takes a numeric value representing a file's permissions and returns
+ * a three character string representing the file's octal permissions
+ *
+ * @param int $perms Permissions
+ * @return string
+ */
function octal_permissions($perms)
{
return substr(sprintf('%o', $perms), -3);
}
}
-
-
-/* End of file file_helper.php */
-/* Location: ./system/helpers/file_helper.php */ \ No newline at end of file
diff --git a/system/helpers/form_helper.php b/system/helpers/form_helper.php
index 7e2c3a0ae..191fc7e6d 100644
--- a/system/helpers/form_helper.php
+++ b/system/helpers/form_helper.php
@@ -1,19 +1,42 @@
-<?php if ( ! defined('BASEPATH')) exit('No direct script access allowed');
+<?php
/**
* CodeIgniter
*
- * An open source application development framework for PHP 5.1.6 or newer
+ * An open source application development framework for PHP
*
- * @package CodeIgniter
- * @author ExpressionEngine Dev Team
- * @copyright Copyright (c) 2008 - 2014, EllisLab, Inc.
- * @license http://codeigniter.com/user_guide/license.html
- * @link http://codeigniter.com
- * @since Version 1.0
+ * This content is released under the MIT License (MIT)
+ *
+ * Copyright (c) 2019 - 2022, CodeIgniter Foundation
+ *
+ * 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.
+ *
+ * @package CodeIgniter
+ * @author EllisLab Dev Team
+ * @copyright Copyright (c) 2008 - 2014, EllisLab, Inc. (https://ellislab.com/)
+ * @copyright Copyright (c) 2014 - 2019, British Columbia Institute of Technology (https://bcit.ca/)
+ * @copyright Copyright (c) 2019 - 2022, CodeIgniter Foundation (https://codeigniter.com/)
+ * @license https://opensource.org/licenses/MIT MIT License
+ * @link https://codeigniter.com
+ * @since Version 1.0.0
* @filesource
*/
-
-// ------------------------------------------------------------------------
+defined('BASEPATH') OR exit('No direct script access allowed');
/**
* CodeIgniter Form Helpers
@@ -21,58 +44,94 @@
* @package CodeIgniter
* @subpackage Helpers
* @category Helpers
- * @author ExpressionEngine Dev Team
- * @link http://codeigniter.com/user_guide/helpers/form_helper.html
+ * @author EllisLab Dev Team
+ * @link https://codeigniter.com/userguide3/helpers/form_helper.html
*/
// ------------------------------------------------------------------------
-/**
- * Form Declaration
- *
- * Creates the opening portion of the form.
- *
- * @access public
- * @param string the URI segments of the form destination
- * @param array a key/value pair of attributes
- * @param array a key/value pair hidden data
- * @return string
- */
if ( ! function_exists('form_open'))
{
- function form_open($action = '', $attributes = '', $hidden = array())
+ /**
+ * Form Declaration
+ *
+ * Creates the opening portion of the form.
+ *
+ * @param string the URI segments of the form destination
+ * @param array a key/value pair of attributes
+ * @param array a key/value pair hidden data
+ * @return string
+ */
+ function form_open($action = '', $attributes = array(), $hidden = array())
{
$CI =& get_instance();
- if ($attributes == '')
+ // If no action is provided then set to the current url
+ if ( ! $action)
{
- $attributes = 'method="post"';
+ $action = $CI->config->site_url($CI->uri->uri_string());
}
-
// If an action is not a full URL then turn it into one
- if ($action && strpos($action, '://') === FALSE)
+ elseif (strpos($action, '://') === FALSE)
{
$action = $CI->config->site_url($action);
}
- // If no action is provided then set to the current url
- $action OR $action = $CI->config->site_url($CI->uri->uri_string());
+ $attributes = _attributes_to_string($attributes);
- $form = '<form action="'.$action.'"';
+ if (stripos($attributes, 'method=') === FALSE)
+ {
+ $attributes .= ' method="post"';
+ }
- $form .= _attributes_to_string($attributes, TRUE);
+ if (stripos($attributes, 'accept-charset=') === FALSE)
+ {
+ $attributes .= ' accept-charset="'.strtolower(config_item('charset')).'"';
+ }
- $form .= '>';
+ $form = '<form action="'.$action.'"'.$attributes.">\n";
- // Add CSRF field if enabled, but leave it out for GET requests and requests to external websites
- if ($CI->config->item('csrf_protection') === TRUE AND ! (strpos($action, $CI->config->base_url()) === FALSE OR strpos($form, 'method="get"')))
+ if (is_array($hidden))
{
- $hidden[$CI->security->get_csrf_token_name()] = $CI->security->get_csrf_hash();
+ foreach ($hidden as $name => $value)
+ {
+ $form .= '<input type="hidden" name="'.$name.'" value="'.html_escape($value).'" />'."\n";
+ }
}
- if (is_array($hidden) AND count($hidden) > 0)
+ // Add CSRF field if enabled, but leave it out for GET requests and requests to external websites
+ if ($CI->config->item('csrf_protection') === TRUE && strpos($action, $CI->config->base_url()) !== FALSE && ! stripos($form, 'method="get"'))
{
- $form .= sprintf("<div style=\"display:none\">%s</div>", form_hidden($hidden));
+ // Prepend/append random-length "white noise" around the CSRF
+ // token input, as a form of protection against BREACH attacks
+ if (FALSE !== ($noise = $CI->security->get_random_bytes(1)))
+ {
+ list(, $noise) = unpack('c', $noise);
+ }
+ else
+ {
+ $noise = mt_rand(-128, 127);
+ }
+
+ // Prepend if $noise has a negative value, append if positive, do nothing for zero
+ $prepend = $append = '';
+ if ($noise < 0)
+ {
+ $prepend = str_repeat(" ", abs($noise));
+ }
+ elseif ($noise > 0)
+ {
+ $append = str_repeat(" ", $noise);
+ }
+
+ $form .= sprintf(
+ '%s<input type="hidden" name="%s" value="%s" />%s%s',
+ $prepend,
+ $CI->security->get_csrf_token_name(),
+ $CI->security->get_csrf_hash(),
+ $append,
+ "\n"
+ );
}
return $form;
@@ -81,19 +140,18 @@ if ( ! function_exists('form_open'))
// ------------------------------------------------------------------------
-/**
- * Form Declaration - Multipart type
- *
- * Creates the opening portion of the form, but with "multipart/form-data".
- *
- * @access public
- * @param string the URI segments of the form destination
- * @param array a key/value pair of attributes
- * @param array a key/value pair hidden data
- * @return string
- */
if ( ! function_exists('form_open_multipart'))
{
+ /**
+ * Form Declaration - Multipart type
+ *
+ * Creates the opening portion of the form, but with "multipart/form-data".
+ *
+ * @param string the URI segments of the form destination
+ * @param array a key/value pair of attributes
+ * @param array a key/value pair hidden data
+ * @return string
+ */
function form_open_multipart($action = '', $attributes = array(), $hidden = array())
{
if (is_string($attributes))
@@ -111,19 +169,19 @@ if ( ! function_exists('form_open_multipart'))
// ------------------------------------------------------------------------
-/**
- * Hidden Input Field
- *
- * Generates hidden fields. You can pass a simple key/value string or an associative
- * array with multiple values.
- *
- * @access public
- * @param mixed
- * @param string
- * @return string
- */
if ( ! function_exists('form_hidden'))
{
+ /**
+ * Hidden Input Field
+ *
+ * Generates hidden fields. You can pass a simple key/value string or
+ * an associative array with multiple values.
+ *
+ * @param mixed $name Field name
+ * @param string $value Field value
+ * @param bool $recursing
+ * @return string
+ */
function form_hidden($name, $value = '', $recursing = FALSE)
{
static $form;
@@ -139,18 +197,19 @@ if ( ! function_exists('form_hidden'))
{
form_hidden($key, $val, TRUE);
}
+
return $form;
}
if ( ! is_array($value))
{
- $form .= '<input type="hidden" name="'.$name.'" value="'.form_prep($value, $name).'" />'."\n";
+ $form .= '<input type="hidden" name="'.$name.'" value="'.html_escape($value)."\" />\n";
}
else
{
foreach ($value as $k => $v)
{
- $k = (is_int($k)) ? '' : $k;
+ $k = is_int($k) ? '' : $k;
form_hidden($name.'['.$k.']', $v, TRUE);
}
}
@@ -161,47 +220,45 @@ if ( ! function_exists('form_hidden'))
// ------------------------------------------------------------------------
-/**
- * Text Input Field
- *
- * @access public
- * @param mixed
- * @param string
- * @param string
- * @return string
- */
if ( ! function_exists('form_input'))
{
+ /**
+ * Text Input Field
+ *
+ * @param mixed
+ * @param string
+ * @param mixed
+ * @return string
+ */
function form_input($data = '', $value = '', $extra = '')
{
- $defaults = array('type' => 'text', 'name' => (( ! is_array($data)) ? $data : ''), 'value' => $value);
+ $defaults = array(
+ 'type' => 'text',
+ 'name' => is_array($data) ? '' : $data,
+ 'value' => $value
+ );
- return "<input "._parse_form_attributes($data, $defaults).$extra." />";
+ return '<input '._parse_form_attributes($data, $defaults)._attributes_to_string($extra)." />\n";
}
}
// ------------------------------------------------------------------------
-/**
- * Password Field
- *
- * Identical to the input function but adds the "password" type
- *
- * @access public
- * @param mixed
- * @param string
- * @param string
- * @return string
- */
if ( ! function_exists('form_password'))
{
+ /**
+ * Password Field
+ *
+ * Identical to the input function but adds the "password" type
+ *
+ * @param mixed
+ * @param string
+ * @param mixed
+ * @return string
+ */
function form_password($data = '', $value = '', $extra = '')
{
- if ( ! is_array($data))
- {
- $data = array('name' => $data);
- }
-
+ is_array($data) OR $data = array('name' => $data);
$data['type'] = 'password';
return form_input($data, $value, $extra);
}
@@ -209,47 +266,46 @@ if ( ! function_exists('form_password'))
// ------------------------------------------------------------------------
-/**
- * Upload Field
- *
- * Identical to the input function but adds the "file" type
- *
- * @access public
- * @param mixed
- * @param string
- * @param string
- * @return string
- */
if ( ! function_exists('form_upload'))
{
- function form_upload($data = '', $value = '', $extra = '')
+ /**
+ * Upload Field
+ *
+ * Identical to the input function but adds the "file" type
+ *
+ * @param mixed
+ * @param mixed
+ * @return string
+ */
+ function form_upload($data = '', $extra = '')
{
- if ( ! is_array($data))
- {
- $data = array('name' => $data);
- }
-
+ $defaults = array('type' => 'file', 'name' => '');
+ is_array($data) OR $data = array('name' => $data);
$data['type'] = 'file';
- return form_input($data, $value, $extra);
+
+ return '<input '._parse_form_attributes($data, $defaults)._attributes_to_string($extra)." />\n";
}
}
// ------------------------------------------------------------------------
-/**
- * Textarea field
- *
- * @access public
- * @param mixed
- * @param string
- * @param string
- * @return string
- */
if ( ! function_exists('form_textarea'))
{
+ /**
+ * Textarea field
+ *
+ * @param mixed $data
+ * @param string $value
+ * @param mixed $extra
+ * @return string
+ */
function form_textarea($data = '', $value = '', $extra = '')
{
- $defaults = array('name' => (( ! is_array($data)) ? $data : ''), 'cols' => '40', 'rows' => '10');
+ $defaults = array(
+ 'name' => is_array($data) ? '' : $data,
+ 'cols' => '40',
+ 'rows' => '10'
+ );
if ( ! is_array($data) OR ! isset($data['value']))
{
@@ -261,28 +317,29 @@ if ( ! function_exists('form_textarea'))
unset($data['value']); // textareas don't use the value attribute
}
- $name = (is_array($data)) ? $data['name'] : $data;
- return "<textarea "._parse_form_attributes($data, $defaults).$extra.">".form_prep($val, $name)."</textarea>";
+ return '<textarea '._parse_form_attributes($data, $defaults)._attributes_to_string($extra).'>'
+ .html_escape($val)
+ ."</textarea>\n";
}
}
// ------------------------------------------------------------------------
-/**
- * Multi-select menu
- *
- * @access public
- * @param string
- * @param array
- * @param mixed
- * @param string
- * @return type
- */
if ( ! function_exists('form_multiselect'))
{
+ /**
+ * Multi-select menu
+ *
+ * @param string
+ * @param array
+ * @param mixed
+ * @param mixed
+ * @return string
+ */
function form_multiselect($name = '', $options = array(), $selected = array(), $extra = '')
{
- if ( ! strpos($extra, 'multiple'))
+ $extra = _attributes_to_string($extra);
+ if (stripos($extra, 'multiple') === FALSE)
{
$extra .= ' multiple="multiple"';
}
@@ -293,91 +350,117 @@ if ( ! function_exists('form_multiselect'))
// --------------------------------------------------------------------
-/**
- * Drop-down Menu
- *
- * @access public
- * @param string
- * @param array
- * @param string
- * @param string
- * @return string
- */
if ( ! function_exists('form_dropdown'))
{
- function form_dropdown($name = '', $options = array(), $selected = array(), $extra = '')
+ /**
+ * Drop-down Menu
+ *
+ * @param mixed $data
+ * @param mixed $options
+ * @param mixed $selected
+ * @param mixed $extra
+ * @return string
+ */
+ function form_dropdown($data = '', $options = array(), $selected = array(), $extra = '')
{
- if ( ! is_array($selected))
+ $defaults = array();
+
+ if (is_array($data))
+ {
+ if (isset($data['selected']))
+ {
+ $selected = $data['selected'];
+ unset($data['selected']); // select tags don't have a selected attribute
+ }
+
+ if (isset($data['options']))
+ {
+ $options = $data['options'];
+ unset($data['options']); // select tags don't use an options attribute
+ }
+ }
+ else
{
- $selected = array($selected);
+ $defaults = array('name' => $data);
}
+ is_array($selected) OR $selected = array($selected);
+ is_array($options) OR $options = array($options);
+
// If no selected state was submitted we will attempt to set it automatically
- if (count($selected) === 0)
+ if (empty($selected))
{
- // If the form name appears in the $_POST array we have a winner!
- if (isset($_POST[$name]))
+ if (is_array($data))
+ {
+ if (isset($data['name'], $_POST[$data['name']]))
+ {
+ $selected = array($_POST[$data['name']]);
+ }
+ }
+ elseif (isset($_POST[$data]))
{
- $selected = array($_POST[$name]);
+ $selected = array($_POST[$data]);
}
}
- if ($extra != '') $extra = ' '.$extra;
+ $extra = _attributes_to_string($extra);
- $multiple = (count($selected) > 1 && strpos($extra, 'multiple') === FALSE) ? ' multiple="multiple"' : '';
+ $multiple = (count($selected) > 1 && stripos($extra, 'multiple') === FALSE) ? ' multiple="multiple"' : '';
- $form = '<select name="'.$name.'"'.$extra.$multiple.">\n";
+ $form = '<select '.rtrim(_parse_form_attributes($data, $defaults)).$extra.$multiple.">\n";
foreach ($options as $key => $val)
{
$key = (string) $key;
- if (is_array($val) && ! empty($val))
+ if (is_array($val))
{
- $form .= '<optgroup label="'.$key.'">'."\n";
+ if (empty($val))
+ {
+ continue;
+ }
+
+ $form .= '<optgroup label="'.$key."\">\n";
foreach ($val as $optgroup_key => $optgroup_val)
{
- $sel = (in_array($optgroup_key, $selected)) ? ' selected="selected"' : '';
-
- $form .= '<option value="'.$optgroup_key.'"'.$sel.'>'.(string) $optgroup_val."</option>\n";
+ $sel = in_array($optgroup_key, $selected) ? ' selected="selected"' : '';
+ $form .= '<option value="'.html_escape($optgroup_key).'"'.$sel.'>'
+ .(string) $optgroup_val."</option>\n";
}
- $form .= '</optgroup>'."\n";
+ $form .= "</optgroup>\n";
}
else
{
- $sel = (in_array($key, $selected)) ? ' selected="selected"' : '';
-
- $form .= '<option value="'.$key.'"'.$sel.'>'.(string) $val."</option>\n";
+ $form .= '<option value="'.html_escape($key).'"'
+ .(in_array($key, $selected) ? ' selected="selected"' : '').'>'
+ .(string) $val."</option>\n";
}
}
- $form .= '</select>';
-
- return $form;
+ return $form."</select>\n";
}
}
// ------------------------------------------------------------------------
-/**
- * Checkbox Field
- *
- * @access public
- * @param mixed
- * @param string
- * @param bool
- * @param string
- * @return string
- */
if ( ! function_exists('form_checkbox'))
{
+ /**
+ * Checkbox Field
+ *
+ * @param mixed
+ * @param string
+ * @param bool
+ * @param mixed
+ * @return string
+ */
function form_checkbox($data = '', $value = '', $checked = FALSE, $extra = '')
{
- $defaults = array('type' => 'checkbox', 'name' => (( ! is_array($data)) ? $data : ''), 'value' => $value);
+ $defaults = array('type' => 'checkbox', 'name' => ( ! is_array($data) ? $data : ''), 'value' => $value);
- if (is_array($data) AND array_key_exists('checked', $data))
+ if (is_array($data) && array_key_exists('checked', $data))
{
$checked = $data['checked'];
@@ -400,167 +483,159 @@ if ( ! function_exists('form_checkbox'))
unset($defaults['checked']);
}
- return "<input "._parse_form_attributes($data, $defaults).$extra." />";
+ return '<input '._parse_form_attributes($data, $defaults)._attributes_to_string($extra)." />\n";
}
}
// ------------------------------------------------------------------------
-/**
- * Radio Button
- *
- * @access public
- * @param mixed
- * @param string
- * @param bool
- * @param string
- * @return string
- */
if ( ! function_exists('form_radio'))
{
+ /**
+ * Radio Button
+ *
+ * @param mixed
+ * @param string
+ * @param bool
+ * @param mixed
+ * @return string
+ */
function form_radio($data = '', $value = '', $checked = FALSE, $extra = '')
{
- if ( ! is_array($data))
- {
- $data = array('name' => $data);
- }
-
+ is_array($data) OR $data = array('name' => $data);
$data['type'] = 'radio';
+
return form_checkbox($data, $value, $checked, $extra);
}
}
// ------------------------------------------------------------------------
-/**
- * Submit Button
- *
- * @access public
- * @param mixed
- * @param string
- * @param string
- * @return string
- */
if ( ! function_exists('form_submit'))
{
+ /**
+ * Submit Button
+ *
+ * @param mixed
+ * @param string
+ * @param mixed
+ * @return string
+ */
function form_submit($data = '', $value = '', $extra = '')
{
- $defaults = array('type' => 'submit', 'name' => (( ! is_array($data)) ? $data : ''), 'value' => $value);
+ $defaults = array(
+ 'type' => 'submit',
+ 'name' => is_array($data) ? '' : $data,
+ 'value' => $value
+ );
- return "<input "._parse_form_attributes($data, $defaults).$extra." />";
+ return '<input '._parse_form_attributes($data, $defaults)._attributes_to_string($extra)." />\n";
}
}
// ------------------------------------------------------------------------
-/**
- * Reset Button
- *
- * @access public
- * @param mixed
- * @param string
- * @param string
- * @return string
- */
if ( ! function_exists('form_reset'))
{
+ /**
+ * Reset Button
+ *
+ * @param mixed
+ * @param string
+ * @param mixed
+ * @return string
+ */
function form_reset($data = '', $value = '', $extra = '')
{
- $defaults = array('type' => 'reset', 'name' => (( ! is_array($data)) ? $data : ''), 'value' => $value);
+ $defaults = array(
+ 'type' => 'reset',
+ 'name' => is_array($data) ? '' : $data,
+ 'value' => $value
+ );
- return "<input "._parse_form_attributes($data, $defaults).$extra." />";
+ return '<input '._parse_form_attributes($data, $defaults)._attributes_to_string($extra)." />\n";
}
}
// ------------------------------------------------------------------------
-/**
- * Form Button
- *
- * @access public
- * @param mixed
- * @param string
- * @param string
- * @return string
- */
if ( ! function_exists('form_button'))
{
+ /**
+ * Form Button
+ *
+ * @param mixed
+ * @param string
+ * @param mixed
+ * @return string
+ */
function form_button($data = '', $content = '', $extra = '')
{
- $defaults = array('name' => (( ! is_array($data)) ? $data : ''), 'type' => 'button');
+ $defaults = array(
+ 'name' => is_array($data) ? '' : $data,
+ 'type' => 'button'
+ );
- if ( is_array($data) AND isset($data['content']))
+ if (is_array($data) && isset($data['content']))
{
$content = $data['content'];
unset($data['content']); // content is not an attribute
}
- return "<button "._parse_form_attributes($data, $defaults).$extra.">".$content."</button>";
+ return '<button '._parse_form_attributes($data, $defaults)._attributes_to_string($extra).'>'
+ .$content
+ ."</button>\n";
}
}
// ------------------------------------------------------------------------
-/**
- * Form Label Tag
- *
- * @access public
- * @param string The text to appear onscreen
- * @param string The id the label applies to
- * @param string Additional attributes
- * @return string
- */
if ( ! function_exists('form_label'))
{
+ /**
+ * Form Label Tag
+ *
+ * @param string The text to appear onscreen
+ * @param string The id the label applies to
+ * @param mixed Additional attributes
+ * @return string
+ */
function form_label($label_text = '', $id = '', $attributes = array())
{
$label = '<label';
- if ($id != '')
+ if ($id !== '')
{
- $label .= " for=\"$id\"";
- }
-
- if (is_array($attributes) AND count($attributes) > 0)
- {
- foreach ($attributes as $key => $val)
- {
- $label .= ' '.$key.'="'.$val.'"';
- }
+ $label .= ' for="'.$id.'"';
}
- $label .= ">$label_text</label>";
+ $label .= _attributes_to_string($attributes);
- return $label;
+ return $label.'>'.$label_text.'</label>';
}
}
// ------------------------------------------------------------------------
-/**
- * Fieldset Tag
- *
- * Used to produce <fieldset><legend>text</legend>. To close fieldset
- * use form_fieldset_close()
- *
- * @access public
- * @param string The legend text
- * @param string Additional attributes
- * @return string
- */
+
if ( ! function_exists('form_fieldset'))
{
+ /**
+ * Fieldset Tag
+ *
+ * Used to produce <fieldset><legend>text</legend>. To close fieldset
+ * use form_fieldset_close()
+ *
+ * @param string The legend text
+ * @param array Additional attributes
+ * @return string
+ */
function form_fieldset($legend_text = '', $attributes = array())
{
- $fieldset = "<fieldset";
-
- $fieldset .= _attributes_to_string($attributes, FALSE);
-
- $fieldset .= ">\n";
-
- if ($legend_text != '')
+ $fieldset = '<fieldset'._attributes_to_string($attributes).">\n";
+ if ($legend_text !== '')
{
- $fieldset .= "<legend>$legend_text</legend>\n";
+ return $fieldset.'<legend>'.$legend_text."</legend>\n";
}
return $fieldset;
@@ -569,306 +644,231 @@ if ( ! function_exists('form_fieldset'))
// ------------------------------------------------------------------------
-/**
- * Fieldset Close Tag
- *
- * @access public
- * @param string
- * @return string
- */
if ( ! function_exists('form_fieldset_close'))
{
+ /**
+ * Fieldset Close Tag
+ *
+ * @param string
+ * @return string
+ */
function form_fieldset_close($extra = '')
{
- return "</fieldset>".$extra;
+ return '</fieldset>'.$extra;
}
}
// ------------------------------------------------------------------------
-/**
- * Form Close Tag
- *
- * @access public
- * @param string
- * @return string
- */
if ( ! function_exists('form_close'))
{
+ /**
+ * Form Close Tag
+ *
+ * @param string
+ * @return string
+ */
function form_close($extra = '')
{
- return "</form>".$extra;
- }
-}
-
-// ------------------------------------------------------------------------
-
-/**
- * Form Prep
- *
- * Formats text so that it can be safely placed in a form field in the event it has HTML tags.
- *
- * @access public
- * @param string
- * @return string
- */
-if ( ! function_exists('form_prep'))
-{
- function form_prep($str = '', $field_name = '')
- {
- static $prepped_fields = array();
-
- // if the field name is an array we do this recursively
- if (is_array($str))
- {
- foreach ($str as $key => $val)
- {
- $str[$key] = form_prep($val);
- }
-
- return $str;
- }
-
- if ($str === '')
- {
- return '';
- }
-
- // we've already prepped a field with this name
- // @todo need to figure out a way to namespace this so
- // that we know the *exact* field and not just one with
- // the same name
- if (isset($prepped_fields[$field_name]))
- {
- return $str;
- }
-
- $str = htmlspecialchars($str);
-
- // In case htmlspecialchars misses these.
- $str = str_replace(array("'", '"'), array("&#39;", "&quot;"), $str);
-
- if ($field_name != '')
- {
- $prepped_fields[$field_name] = $field_name;
- }
-
- return $str;
+ return '</form>'.$extra;
}
}
// ------------------------------------------------------------------------
-/**
- * Form Value
- *
- * Grabs a value from the POST array for the specified field so you can
- * re-populate an input field or textarea. If Form Validation
- * is active it retrieves the info from the validation class
- *
- * @access public
- * @param string
- * @return mixed
- */
if ( ! function_exists('set_value'))
{
- function set_value($field = '', $default = '')
+ /**
+ * Form Value
+ *
+ * Grabs a value from the POST array for the specified field so you can
+ * re-populate an input field or textarea. If Form Validation
+ * is active it retrieves the info from the validation class
+ *
+ * @param string $field Field name
+ * @param string $default Default value
+ * @param bool $html_escape Whether to escape HTML special characters or not
+ * @return string
+ */
+ function set_value($field, $default = '', $html_escape = TRUE)
{
- if (FALSE === ($OBJ =& _get_validation_object()))
- {
- if ( ! isset($_POST[$field]))
- {
- return $default;
- }
+ $CI =& get_instance();
- return form_prep($_POST[$field], $field);
- }
+ $value = (isset($CI->form_validation) && is_object($CI->form_validation) && $CI->form_validation->has_rule($field))
+ ? $CI->form_validation->set_value($field, $default)
+ : $CI->input->post($field, FALSE);
- return form_prep($OBJ->set_value($field, $default), $field);
+ isset($value) OR $value = $default;
+ return ($html_escape) ? html_escape($value) : $value;
}
}
// ------------------------------------------------------------------------
-/**
- * Set Select
- *
- * Let's you set the selected value of a <select> menu via data in the POST array.
- * If Form Validation is active it retrieves the info from the validation class
- *
- * @access public
- * @param string
- * @param string
- * @param bool
- * @return string
- */
if ( ! function_exists('set_select'))
{
- function set_select($field = '', $value = '', $default = FALSE)
+ /**
+ * Set Select
+ *
+ * Let's you set the selected value of a <select> menu via data in the POST array.
+ * If Form Validation is active it retrieves the info from the validation class
+ *
+ * @param string
+ * @param string
+ * @param bool
+ * @return string
+ */
+ function set_select($field, $value = '', $default = FALSE)
{
- $OBJ =& _get_validation_object();
+ $CI =& get_instance();
- if ($OBJ === FALSE)
+ if (isset($CI->form_validation) && is_object($CI->form_validation) && $CI->form_validation->has_rule($field))
{
- if ( ! isset($_POST[$field]))
- {
- if (count($_POST) === 0 AND $default == TRUE)
- {
- return ' selected="selected"';
- }
- return '';
- }
-
- $field = $_POST[$field];
+ return $CI->form_validation->set_select($field, $value, $default);
+ }
+ elseif (($input = $CI->input->post($field, FALSE)) === NULL)
+ {
+ return ($default === TRUE) ? ' selected="selected"' : '';
+ }
- if (is_array($field))
- {
- if ( ! in_array($value, $field))
- {
- return '';
- }
- }
- else
+ $value = (string) $value;
+ if (is_array($input))
+ {
+ // Note: in_array('', array(0)) returns TRUE, do not use it
+ foreach ($input as &$v)
{
- if (($field == '' OR $value == '') OR ($field != $value))
+ if ($value === $v)
{
- return '';
+ return ' selected="selected"';
}
}
- return ' selected="selected"';
+ return '';
}
- return $OBJ->set_select($field, $value, $default);
+ return ($input === $value) ? ' selected="selected"' : '';
}
}
// ------------------------------------------------------------------------
-/**
- * Set Checkbox
- *
- * Let's you set the selected value of a checkbox via the value in the POST array.
- * If Form Validation is active it retrieves the info from the validation class
- *
- * @access public
- * @param string
- * @param string
- * @param bool
- * @return string
- */
if ( ! function_exists('set_checkbox'))
{
- function set_checkbox($field = '', $value = '', $default = FALSE)
+ /**
+ * Set Checkbox
+ *
+ * Let's you set the selected value of a checkbox via the value in the POST array.
+ * If Form Validation is active it retrieves the info from the validation class
+ *
+ * @param string
+ * @param string
+ * @param bool
+ * @return string
+ */
+ function set_checkbox($field, $value = '', $default = FALSE)
{
- $OBJ =& _get_validation_object();
+ $CI =& get_instance();
- if ($OBJ === FALSE)
+ if (isset($CI->form_validation) && is_object($CI->form_validation) && $CI->form_validation->has_rule($field))
{
- if ( ! isset($_POST[$field]))
- {
- if (count($_POST) === 0 AND $default == TRUE)
- {
- return ' checked="checked"';
- }
- return '';
- }
+ return $CI->form_validation->set_checkbox($field, $value, $default);
+ }
- $field = $_POST[$field];
+ // Form inputs are always strings ...
+ $value = (string) $value;
+ $input = $CI->input->post($field, FALSE);
- if (is_array($field))
- {
- if ( ! in_array($value, $field))
- {
- return '';
- }
- }
- else
+ if (is_array($input))
+ {
+ // Note: in_array('', array(0)) returns TRUE, do not use it
+ foreach ($input as &$v)
{
- if (($field == '' OR $value == '') OR ($field != $value))
+ if ($value === $v)
{
- return '';
+ return ' checked="checked"';
}
}
- return ' checked="checked"';
+ return '';
+ }
+
+ // Unchecked checkbox and radio inputs are not even submitted by browsers ...
+ if ($CI->input->method() === 'post')
+ {
+ return ($input === $value) ? ' checked="checked"' : '';
}
- return $OBJ->set_checkbox($field, $value, $default);
+ return ($default === TRUE) ? ' checked="checked"' : '';
}
}
// ------------------------------------------------------------------------
-/**
- * Set Radio
- *
- * Let's you set the selected value of a radio field via info in the POST array.
- * If Form Validation is active it retrieves the info from the validation class
- *
- * @access public
- * @param string
- * @param string
- * @param bool
- * @return string
- */
if ( ! function_exists('set_radio'))
{
- function set_radio($field = '', $value = '', $default = FALSE)
+ /**
+ * Set Radio
+ *
+ * Let's you set the selected value of a radio field via info in the POST array.
+ * If Form Validation is active it retrieves the info from the validation class
+ *
+ * @param string $field
+ * @param string $value
+ * @param bool $default
+ * @return string
+ */
+ function set_radio($field, $value = '', $default = FALSE)
{
- $OBJ =& _get_validation_object();
+ $CI =& get_instance();
- if ($OBJ === FALSE)
+ if (isset($CI->form_validation) && is_object($CI->form_validation) && $CI->form_validation->has_rule($field))
{
- if ( ! isset($_POST[$field]))
- {
- if (count($_POST) === 0 AND $default == TRUE)
- {
- return ' checked="checked"';
- }
- return '';
- }
+ return $CI->form_validation->set_radio($field, $value, $default);
+ }
- $field = $_POST[$field];
+ // Form inputs are always strings ...
+ $value = (string) $value;
+ $input = $CI->input->post($field, FALSE);
- if (is_array($field))
- {
- if ( ! in_array($value, $field))
- {
- return '';
- }
- }
- else
+ if (is_array($input))
+ {
+ // Note: in_array('', array(0)) returns TRUE, do not use it
+ foreach ($input as &$v)
{
- if (($field == '' OR $value == '') OR ($field != $value))
+ if ($value === $v)
{
- return '';
+ return ' checked="checked"';
}
}
- return ' checked="checked"';
+ return '';
}
- return $OBJ->set_radio($field, $value, $default);
+ // Unchecked checkbox and radio inputs are not even submitted by browsers ...
+ if ($CI->input->method() === 'post')
+ {
+ return ($input === $value) ? ' checked="checked"' : '';
+ }
+
+ return ($default === TRUE) ? ' checked="checked"' : '';
}
}
// ------------------------------------------------------------------------
-/**
- * Form Error
- *
- * Returns the error for a specific form field. This is a helper for the
- * form validation class.
- *
- * @access public
- * @param string
- * @param string
- * @param string
- * @return string
- */
if ( ! function_exists('form_error'))
{
+ /**
+ * Form Error
+ *
+ * Returns the error for a specific form field. This is a helper for the
+ * form validation class.
+ *
+ * @param string
+ * @param string
+ * @param string
+ * @return string
+ */
function form_error($field = '', $prefix = '', $suffix = '')
{
if (FALSE === ($OBJ =& _get_validation_object()))
@@ -882,19 +882,18 @@ if ( ! function_exists('form_error'))
// ------------------------------------------------------------------------
-/**
- * Validation Error String
- *
- * Returns all the errors associated with a form submission. This is a helper
- * function for the form validation class.
- *
- * @access public
- * @param string
- * @param string
- * @return string
- */
if ( ! function_exists('validation_errors'))
{
+ /**
+ * Validation Error String
+ *
+ * Returns all the errors associated with a form submission. This is a helper
+ * function for the form validation class.
+ *
+ * @param string
+ * @param string
+ * @return string
+ */
function validation_errors($prefix = '', $suffix = '')
{
if (FALSE === ($OBJ =& _get_validation_object()))
@@ -908,18 +907,17 @@ if ( ! function_exists('validation_errors'))
// ------------------------------------------------------------------------
-/**
- * Parse the form attributes
- *
- * Helper function used by some of the form helpers
- *
- * @access private
- * @param array
- * @param array
- * @return string
- */
if ( ! function_exists('_parse_form_attributes'))
{
+ /**
+ * Parse the form attributes
+ *
+ * Helper function used by some of the form helpers
+ *
+ * @param array $attributes List of attributes
+ * @param array $default Default values
+ * @return string
+ */
function _parse_form_attributes($attributes, $default)
{
if (is_array($attributes))
@@ -943,12 +941,16 @@ if ( ! function_exists('_parse_form_attributes'))
foreach ($default as $key => $val)
{
- if ($key == 'value')
+ if ($key === 'value')
+ {
+ $val = html_escape($val);
+ }
+ elseif ($key === 'name' && ! strlen($default['name']))
{
- $val = form_prep($val, $default['name']);
+ continue;
}
- $att .= $key . '="' . $val . '" ';
+ $att .= $key.'="'.$val.'" ';
}
return $att;
@@ -957,54 +959,32 @@ if ( ! function_exists('_parse_form_attributes'))
// ------------------------------------------------------------------------
-/**
- * Attributes To String
- *
- * Helper function used by some of the form helpers
- *
- * @access private
- * @param mixed
- * @param bool
- * @return string
- */
if ( ! function_exists('_attributes_to_string'))
{
- function _attributes_to_string($attributes, $formtag = FALSE)
+ /**
+ * Attributes To String
+ *
+ * Helper function used by some of the form helpers
+ *
+ * @param mixed
+ * @return string
+ */
+ function _attributes_to_string($attributes)
{
- if (is_string($attributes) AND strlen($attributes) > 0)
+ if (empty($attributes))
{
- if ($formtag == TRUE AND strpos($attributes, 'method=') === FALSE)
- {
- $attributes .= ' method="post"';
- }
-
- if ($formtag == TRUE AND strpos($attributes, 'accept-charset=') === FALSE)
- {
- $attributes .= ' accept-charset="'.strtolower(config_item('charset')).'"';
- }
-
- return ' '.$attributes;
+ return '';
}
- if (is_object($attributes) AND count($attributes) > 0)
+ if (is_object($attributes))
{
- $attributes = (array)$attributes;
+ $attributes = (array) $attributes;
}
- if (is_array($attributes) AND count($attributes) > 0)
+ if (is_array($attributes))
{
$atts = '';
- if ( ! isset($attributes['method']) AND $formtag === TRUE)
- {
- $atts .= ' method="post"';
- }
-
- if ( ! isset($attributes['accept-charset']) AND $formtag === TRUE)
- {
- $atts .= ' accept-charset="'.strtolower(config_item('charset')).'"';
- }
-
foreach ($attributes as $key => $val)
{
$atts .= ' '.$key.'="'.$val.'"';
@@ -1012,43 +992,45 @@ if ( ! function_exists('_attributes_to_string'))
return $atts;
}
+
+ if (is_string($attributes))
+ {
+ return ' '.$attributes;
+ }
+
+ return FALSE;
}
}
// ------------------------------------------------------------------------
-/**
- * Validation Object
- *
- * Determines what the form validation class was instantiated as, fetches
- * the object and returns it.
- *
- * @access private
- * @return mixed
- */
if ( ! function_exists('_get_validation_object'))
{
+ /**
+ * Validation Object
+ *
+ * Determines what the form validation class was instantiated as, fetches
+ * the object and returns it.
+ *
+ * @return mixed
+ */
function &_get_validation_object()
{
$CI =& get_instance();
// We set this as a variable since we're returning by reference.
$return = FALSE;
-
- if (FALSE !== ($object = $CI->load->is_loaded('form_validation')))
+
+ if (FALSE !== ($object = $CI->load->is_loaded('Form_validation')))
{
if ( ! isset($CI->$object) OR ! is_object($CI->$object))
{
return $return;
}
-
+
return $CI->$object;
}
-
+
return $return;
}
}
-
-
-/* End of file form_helper.php */
-/* Location: ./system/helpers/form_helper.php */
diff --git a/system/helpers/html_helper.php b/system/helpers/html_helper.php
index 8e6d39334..98998c7c4 100644
--- a/system/helpers/html_helper.php
+++ b/system/helpers/html_helper.php
@@ -1,19 +1,42 @@
-<?php if ( ! defined('BASEPATH')) exit('No direct script access allowed');
+<?php
/**
* CodeIgniter
*
- * An open source application development framework for PHP 5.1.6 or newer
+ * An open source application development framework for PHP
*
- * @package CodeIgniter
- * @author ExpressionEngine Dev Team
- * @copyright Copyright (c) 2008 - 2014, EllisLab, Inc.
- * @license http://codeigniter.com/user_guide/license.html
- * @link http://codeigniter.com
- * @since Version 1.0
+ * This content is released under the MIT License (MIT)
+ *
+ * Copyright (c) 2019 - 2022, CodeIgniter Foundation
+ *
+ * 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.
+ *
+ * @package CodeIgniter
+ * @author EllisLab Dev Team
+ * @copyright Copyright (c) 2008 - 2014, EllisLab, Inc. (https://ellislab.com/)
+ * @copyright Copyright (c) 2014 - 2019, British Columbia Institute of Technology (https://bcit.ca/)
+ * @copyright Copyright (c) 2019 - 2022, CodeIgniter Foundation (https://codeigniter.com/)
+ * @license https://opensource.org/licenses/MIT MIT License
+ * @link https://codeigniter.com
+ * @since Version 1.0.0
* @filesource
*/
-
-// ------------------------------------------------------------------------
+defined('BASEPATH') OR exit('No direct script access allowed');
/**
* CodeIgniter HTML Helpers
@@ -21,46 +44,43 @@
* @package CodeIgniter
* @subpackage Helpers
* @category Helpers
- * @author ExpressionEngine Dev Team
- * @link http://codeigniter.com/user_guide/helpers/html_helper.html
+ * @author EllisLab Dev Team
+ * @link https://codeigniter.com/userguide3/helpers/html_helper.html
*/
// ------------------------------------------------------------------------
-/**
- * Heading
- *
- * Generates an HTML heading tag. First param is the data.
- * Second param is the size of the heading tag.
- *
- * @access public
- * @param string
- * @param integer
- * @return string
- */
if ( ! function_exists('heading'))
{
+ /**
+ * Heading
+ *
+ * Generates an HTML heading tag.
+ *
+ * @param string content
+ * @param int heading level
+ * @param string
+ * @return string
+ */
function heading($data = '', $h = '1', $attributes = '')
{
- $attributes = ($attributes != '') ? ' '.$attributes : $attributes;
- return "<h".$h.$attributes.">".$data."</h".$h.">";
+ return '<h'.$h._stringify_attributes($attributes).'>'.$data.'</h'.$h.'>';
}
}
// ------------------------------------------------------------------------
-/**
- * Unordered List
- *
- * Generates an HTML unordered list from an single or multi-dimensional array.
- *
- * @access public
- * @param array
- * @param mixed
- * @return string
- */
if ( ! function_exists('ul'))
{
+ /**
+ * Unordered List
+ *
+ * Generates an HTML unordered list from an single or multi-dimensional array.
+ *
+ * @param array
+ * @param mixed
+ * @return string
+ */
function ul($list, $attributes = '')
{
return _list('ul', $list, $attributes);
@@ -69,18 +89,17 @@ if ( ! function_exists('ul'))
// ------------------------------------------------------------------------
-/**
- * Ordered List
- *
- * Generates an HTML ordered list from an single or multi-dimensional array.
- *
- * @access public
- * @param array
- * @param mixed
- * @return string
- */
if ( ! function_exists('ol'))
{
+ /**
+ * Ordered List
+ *
+ * Generates an HTML ordered list from an single or multi-dimensional array.
+ *
+ * @param array
+ * @param mixed
+ * @return string
+ */
function ol($list, $attributes = '')
{
return _list('ol', $list, $attributes);
@@ -89,21 +108,20 @@ if ( ! function_exists('ol'))
// ------------------------------------------------------------------------
-/**
- * Generates the list
- *
- * Generates an HTML ordered list from an single or multi-dimensional array.
- *
- * @access private
- * @param string
- * @param mixed
- * @param mixed
- * @param integer
- * @return string
- */
if ( ! function_exists('_list'))
{
- function _list($type = 'ul', $list, $attributes = '', $depth = 0)
+ /**
+ * Generates the list
+ *
+ * Generates an HTML ordered list from an single or multi-dimensional array.
+ *
+ * @param string
+ * @param mixed
+ * @param mixed
+ * @param int
+ * @return string
+ */
+ function _list($type = 'ul', $list = array(), $attributes = '', $depth = 0)
{
// If an array wasn't submitted there's nothing to do...
if ( ! is_array($list))
@@ -112,25 +130,9 @@ if ( ! function_exists('_list'))
}
// Set the indentation based on the depth
- $out = str_repeat(" ", $depth);
-
- // Were any attributes submitted? If so generate a string
- if (is_array($attributes))
- {
- $atts = '';
- foreach ($attributes as $key => $val)
- {
- $atts .= ' ' . $key . '="' . $val . '"';
- }
- $attributes = $atts;
- }
- elseif (is_string($attributes) AND strlen($attributes) > 0)
- {
- $attributes = ' '. $attributes;
- }
-
- // Write the opening list tag
- $out .= "<".$type.$attributes.">\n";
+ $out = str_repeat(' ', $depth)
+ // Write the opening list tag
+ .'<'.$type._stringify_attributes($attributes).">\n";
// Cycle through the list elements. If an array is
// encountered we will recursively call _list()
@@ -140,8 +142,7 @@ if ( ! function_exists('_list'))
{
$_last_list_item = $key;
- $out .= str_repeat(" ", $depth + 2);
- $out .= "<li>";
+ $out .= str_repeat(' ', $depth + 2).'<li>';
if ( ! is_array($val))
{
@@ -149,55 +150,32 @@ if ( ! function_exists('_list'))
}
else
{
- $out .= $_last_list_item."\n";
- $out .= _list($type, $val, '', $depth + 4);
- $out .= str_repeat(" ", $depth + 2);
+ $out .= $_last_list_item."\n"._list($type, $val, '', $depth + 4).str_repeat(' ', $depth + 2);
}
$out .= "</li>\n";
}
- // Set the indentation for the closing tag
- $out .= str_repeat(" ", $depth);
-
- // Write the closing list tag
- $out .= "</".$type.">\n";
-
- return $out;
+ // Set the indentation for the closing tag and apply it
+ return $out.str_repeat(' ', $depth).'</'.$type.">\n";
}
}
// ------------------------------------------------------------------------
-/**
- * Generates HTML BR tags based on number supplied
- *
- * @access public
- * @param integer
- * @return string
- */
-if ( ! function_exists('br'))
-{
- function br($num = 1)
- {
- return str_repeat("<br />", $num);
- }
-}
-
-// ------------------------------------------------------------------------
-
-/**
- * Image
- *
- * Generates an <img /> element
- *
- * @access public
- * @param mixed
- * @return string
- */
if ( ! function_exists('img'))
{
- function img($src = '', $index_page = FALSE)
+ /**
+ * Image
+ *
+ * Generates an <img /> element
+ *
+ * @param mixed
+ * @param bool
+ * @param mixed
+ * @return string
+ */
+ function img($src = '', $index_page = FALSE, $attributes = '')
{
if ( ! is_array($src) )
{
@@ -212,112 +190,101 @@ if ( ! function_exists('img'))
$img = '<img';
- foreach ($src as $k=>$v)
+ foreach ($src as $k => $v)
{
-
- if ($k == 'src' AND strpos($v, '://') === FALSE)
+ if ($k === 'src' && ! preg_match('#^(data:[a-z,;])|(([a-z]+:)?(?<!data:)//)#i', $v))
{
- $CI =& get_instance();
-
if ($index_page === TRUE)
{
- $img .= ' src="'.$CI->config->site_url($v).'"';
+ $img .= ' src="'.get_instance()->config->site_url($v).'"';
}
else
{
- $img .= ' src="'.$CI->config->slash_item('base_url').$v.'"';
+ $img .= ' src="'.get_instance()->config->base_url($v).'"';
}
}
else
{
- $img .= " $k=\"$v\"";
+ $img .= ' '.$k.'="'.$v.'"';
}
}
- $img .= '/>';
-
- return $img;
+ return $img._stringify_attributes($attributes).' />';
}
}
// ------------------------------------------------------------------------
-/**
- * Doctype
- *
- * Generates a page document type declaration
- *
- * Valid options are xhtml-11, xhtml-strict, xhtml-trans, xhtml-frame,
- * html4-strict, html4-trans, and html4-frame. Values are saved in the
- * doctypes config file.
- *
- * @access public
- * @param string type The doctype to be generated
- * @return string
- */
if ( ! function_exists('doctype'))
{
- function doctype($type = 'xhtml1-strict')
+ /**
+ * Doctype
+ *
+ * Generates a page document type declaration
+ *
+ * Examples of valid options: html5, xhtml-11, xhtml-strict, xhtml-trans,
+ * xhtml-frame, html4-strict, html4-trans, and html4-frame.
+ * All values are saved in the doctypes config file.
+ *
+ * @param string type The doctype to be generated
+ * @return string
+ */
+ function doctype($type = 'html5')
{
- global $_doctypes;
+ static $doctypes;
- if ( ! is_array($_doctypes))
+ if ( ! is_array($doctypes))
{
- if (defined('ENVIRONMENT') AND is_file(APPPATH.'config/'.ENVIRONMENT.'/doctypes.php'))
+ if (file_exists(APPPATH.'config/doctypes.php'))
{
- include(APPPATH.'config/'.ENVIRONMENT.'/doctypes.php');
+ include(APPPATH.'config/doctypes.php');
}
- elseif (is_file(APPPATH.'config/doctypes.php'))
+
+ if (file_exists(APPPATH.'config/'.ENVIRONMENT.'/doctypes.php'))
{
- include(APPPATH.'config/doctypes.php');
+ include(APPPATH.'config/'.ENVIRONMENT.'/doctypes.php');
}
- if ( ! is_array($_doctypes))
+ if (empty($_doctypes) OR ! is_array($_doctypes))
{
+ $doctypes = array();
return FALSE;
}
- }
- if (isset($_doctypes[$type]))
- {
- return $_doctypes[$type];
- }
- else
- {
- return FALSE;
+ $doctypes = $_doctypes;
}
+
+ return isset($doctypes[$type]) ? $doctypes[$type] : FALSE;
}
}
// ------------------------------------------------------------------------
-/**
- * Link
- *
- * Generates link to a CSS file
- *
- * @access public
- * @param mixed stylesheet hrefs or an array
- * @param string rel
- * @param string type
- * @param string title
- * @param string media
- * @param boolean should index_page be added to the css path
- * @return string
- */
if ( ! function_exists('link_tag'))
{
+ /**
+ * Link
+ *
+ * Generates link to a CSS file
+ *
+ * @param mixed stylesheet hrefs or an array
+ * @param string rel
+ * @param string type
+ * @param string title
+ * @param string media
+ * @param bool should index_page be added to the css path
+ * @return string
+ */
function link_tag($href = '', $rel = 'stylesheet', $type = 'text/css', $title = '', $media = '', $index_page = FALSE)
{
$CI =& get_instance();
-
$link = '<link ';
if (is_array($href))
{
- foreach ($href as $k=>$v)
+ foreach ($href as $k => $v)
{
- if ($k == 'href' AND strpos($v, '://') === FALSE)
+ if ($k === 'href' && ! preg_match('#^([a-z]+:)?//#i', $v))
{
if ($index_page === TRUE)
{
@@ -325,20 +292,18 @@ if ( ! function_exists('link_tag'))
}
else
{
- $link .= 'href="'.$CI->config->slash_item('base_url').$v.'" ';
+ $link .= 'href="'.$CI->config->base_url($v).'" ';
}
}
else
{
- $link .= "$k=\"$v\" ";
+ $link .= $k.'="'.$v.'" ';
}
}
-
- $link .= "/>";
}
else
{
- if ( strpos($href, '://') !== FALSE)
+ if (preg_match('#^([a-z]+:)?//#i', $href))
{
$link .= 'href="'.$href.'" ';
}
@@ -348,40 +313,39 @@ if ( ! function_exists('link_tag'))
}
else
{
- $link .= 'href="'.$CI->config->slash_item('base_url').$href.'" ';
+ $link .= 'href="'.$CI->config->base_url($href).'" ';
}
$link .= 'rel="'.$rel.'" type="'.$type.'" ';
- if ($media != '')
+ if ($media !== '')
{
$link .= 'media="'.$media.'" ';
}
- if ($title != '')
+ if ($title !== '')
{
$link .= 'title="'.$title.'" ';
}
-
- $link .= '/>';
}
-
- return $link;
+ return $link."/>\n";
}
}
// ------------------------------------------------------------------------
-/**
- * Generates meta tags from an array of key/values
- *
- * @access public
- * @param array
- * @return string
- */
if ( ! function_exists('meta'))
{
+ /**
+ * Generates meta tags from an array of key/values
+ *
+ * @param array
+ * @param string
+ * @param string
+ * @param string
+ * @return string
+ */
function meta($name = '', $content = '', $type = 'name', $newline = "\n")
{
// Since we allow the data to be passes as a string, a simple array
@@ -390,47 +354,38 @@ if ( ! function_exists('meta'))
{
$name = array(array('name' => $name, 'content' => $content, 'type' => $type, 'newline' => $newline));
}
- else
+ elseif (isset($name['name']))
{
// Turn single array into multidimensional
- if (isset($name['name']))
- {
- $name = array($name);
- }
+ $name = array($name);
}
+ $allowed_types = array('charset', 'http-equiv', 'name', 'property');
$str = '';
foreach ($name as $meta)
{
- $type = ( ! isset($meta['type']) OR $meta['type'] == 'name') ? 'name' : 'http-equiv';
- $name = ( ! isset($meta['name'])) ? '' : $meta['name'];
- $content = ( ! isset($meta['content'])) ? '' : $meta['content'];
- $newline = ( ! isset($meta['newline'])) ? "\n" : $meta['newline'];
+ // This is to preserve BC with pre-3.1 versions where only
+ // 'http-equiv' (default) and 'name' were supported.
+ if (isset($meta['type']))
+ {
+ if ($meta['type'] === 'equiv')
+ {
+ $meta['type'] = 'http-equiv';
+ }
+ elseif ( ! in_array($meta['type'], $allowed_types, TRUE))
+ {
+ $meta['type'] = 'name';
+ }
+ }
+
+ $type = isset($meta['type']) ? $meta['type'] : 'name';
+ $name = isset($meta['name']) ? $meta['name'] : '';
+ $content = isset($meta['content']) ? $meta['content'] : '';
+ $newline = isset($meta['newline']) ? $meta['newline'] : "\n";
- $str .= '<meta '.$type.'="'.$name.'" content="'.$content.'" />'.$newline;
+ $str .= '<meta '.$type.'="'.$name.($type === 'charset' ? '' : '" content="'.$content).'" />'.$newline;
}
return $str;
}
}
-
-// ------------------------------------------------------------------------
-
-/**
- * Generates non-breaking space entities based on number supplied
- *
- * @access public
- * @param integer
- * @return string
- */
-if ( ! function_exists('nbs'))
-{
- function nbs($num = 1)
- {
- return str_repeat("&nbsp;", $num);
- }
-}
-
-
-/* End of file html_helper.php */
-/* Location: ./system/helpers/html_helper.php */ \ No newline at end of file
diff --git a/system/helpers/index.html b/system/helpers/index.html
index c942a79ce..bcb7cae34 100644
--- a/system/helpers/index.html
+++ b/system/helpers/index.html
@@ -1,4 +1,5 @@
-<html>
+<!DOCTYPE html>
+<html lang="en">
<head>
<title>403 Forbidden</title>
</head>
@@ -7,4 +8,4 @@
<p>Directory access is forbidden.</p>
</body>
-</html> \ No newline at end of file
+</html>
diff --git a/system/helpers/inflector_helper.php b/system/helpers/inflector_helper.php
index e93ada0c8..75e98c817 100644
--- a/system/helpers/inflector_helper.php
+++ b/system/helpers/inflector_helper.php
@@ -1,19 +1,42 @@
-<?php if ( ! defined('BASEPATH')) exit('No direct script access allowed');
+<?php
/**
* CodeIgniter
*
- * An open source application development framework for PHP 5.1.6 or newer
+ * An open source application development framework for PHP
*
- * @package CodeIgniter
- * @author ExpressionEngine Dev Team
- * @copyright Copyright (c) 2008 - 2014, EllisLab, Inc.
- * @license http://codeigniter.com/user_guide/license.html
- * @link http://codeigniter.com
- * @since Version 1.0
+ * This content is released under the MIT License (MIT)
+ *
+ * Copyright (c) 2019 - 2022, CodeIgniter Foundation
+ *
+ * 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.
+ *
+ * @package CodeIgniter
+ * @author EllisLab Dev Team
+ * @copyright Copyright (c) 2008 - 2014, EllisLab, Inc. (https://ellislab.com/)
+ * @copyright Copyright (c) 2014 - 2019, British Columbia Institute of Technology (https://bcit.ca/)
+ * @copyright Copyright (c) 2019 - 2022, CodeIgniter Foundation (https://codeigniter.com/)
+ * @license https://opensource.org/licenses/MIT MIT License
+ * @link https://codeigniter.com
+ * @since Version 1.0.0
* @filesource
*/
-
-// ------------------------------------------------------------------------
+defined('BASEPATH') OR exit('No direct script access allowed');
/**
* CodeIgniter Inflector Helpers
@@ -21,58 +44,62 @@
* @package CodeIgniter
* @subpackage Helpers
* @category Helpers
- * @author ExpressionEngine Dev Team
- * @link http://codeigniter.com/user_guide/helpers/directory_helper.html
+ * @author EllisLab Dev Team
+ * @link https://codeigniter.com/userguide3/helpers/inflector_helper.html
*/
-
// --------------------------------------------------------------------
-/**
- * Singular
- *
- * Takes a plural word and makes it singular
- *
- * @access public
- * @param string
- * @return str
- */
if ( ! function_exists('singular'))
{
+ /**
+ * Singular
+ *
+ * Takes a plural word and makes it singular
+ *
+ * @param string $str Input string
+ * @return string
+ */
function singular($str)
{
$result = strval($str);
+ if ( ! word_is_countable($result))
+ {
+ return $result;
+ }
+
$singular_rules = array(
- '/(matr)ices$/' => '\1ix',
- '/(vert|ind)ices$/' => '\1ex',
- '/^(ox)en/' => '\1',
- '/(alias)es$/' => '\1',
- '/([octop|vir])i$/' => '\1us',
- '/(cris|ax|test)es$/' => '\1is',
- '/(shoe)s$/' => '\1',
- '/(o)es$/' => '\1',
- '/(bus|campus)es$/' => '\1',
- '/([m|l])ice$/' => '\1ouse',
- '/(x|ch|ss|sh)es$/' => '\1',
- '/(m)ovies$/' => '\1\2ovie',
- '/(s)eries$/' => '\1\2eries',
- '/([^aeiouy]|qu)ies$/' => '\1y',
- '/([lr])ves$/' => '\1f',
- '/(tive)s$/' => '\1',
- '/(hive)s$/' => '\1',
- '/([^f])ves$/' => '\1fe',
- '/(^analy)ses$/' => '\1sis',
+ '/(matr)ices$/' => '\1ix',
+ '/(vert|ind)ices$/' => '\1ex',
+ '/^(ox)en/' => '\1',
+ '/(alias)es$/' => '\1',
+ '/([octop|vir])i$/' => '\1us',
+ '/(cris|ax|test)es$/' => '\1is',
+ '/(shoe)s$/' => '\1',
+ '/(o)es$/' => '\1',
+ '/(bus|campus)es$/' => '\1',
+ '/([m|l])ice$/' => '\1ouse',
+ '/(x|ch|ss|sh)es$/' => '\1',
+ '/(m)ovies$/' => '\1\2ovie',
+ '/(s)eries$/' => '\1\2eries',
+ '/([^aeiouy]|qu)ies$/' => '\1y',
+ '/([lr])ves$/' => '\1f',
+ '/(tive)s$/' => '\1',
+ '/(hive)s$/' => '\1',
+ '/([^f])ves$/' => '\1fe',
+ '/(^analy)ses$/' => '\1sis',
'/((a)naly|(b)a|(d)iagno|(p)arenthe|(p)rogno|(s)ynop|(t)he)ses$/' => '\1\2sis',
- '/([ti])a$/' => '\1um',
- '/(p)eople$/' => '\1\2erson',
- '/(m)en$/' => '\1an',
- '/(s)tatuses$/' => '\1\2tatus',
- '/(c)hildren$/' => '\1\2hild',
- '/(n)ews$/' => '\1\2ews',
- '/([^u])s$/' => '\1',
+ '/([ti])a$/' => '\1um',
+ '/(p)eople$/' => '\1\2erson',
+ '/(m)en$/' => '\1an',
+ '/(s)tatuses$/' => '\1\2tatus',
+ '/(c)hildren$/' => '\1\2hild',
+ '/(n)ews$/' => '\1\2ews',
+ '/(quiz)zes$/' => '\1',
+ '/([^us])s$/' => '\1'
);
-
+
foreach ($singular_rules as $rule => $replacement)
{
if (preg_match($rule, $result))
@@ -88,23 +115,27 @@ if ( ! function_exists('singular'))
// --------------------------------------------------------------------
-/**
- * Plural
- *
- * Takes a singular word and makes it plural
- *
- * @access public
- * @param string
- * @param bool
- * @return str
- */
if ( ! function_exists('plural'))
{
- function plural($str, $force = FALSE)
+ /**
+ * Plural
+ *
+ * Takes a singular word and makes it plural
+ *
+ * @param string $str Input string
+ * @return string
+ */
+ function plural($str)
{
$result = strval($str);
-
+
+ if ( ! word_is_countable($result))
+ {
+ return $result;
+ }
+
$plural_rules = array(
+ '/(quiz)$/' => '\1zes', // quizzes
'/^(ox)$/' => '\1\2en', // ox
'/([m|l])ouse$/' => '\1ice', // mouse, louse
'/(matr|vert|ind)ix|ex$/' => '\1ices', // matrix, vertex, index
@@ -119,7 +150,7 @@ if ( ! function_exists('plural'))
'/(c)hild$/' => '\1hildren', // child
'/(buffal|tomat)o$/' => '\1\2oes', // buffalo, tomato
'/(bu|campu)s$/' => '\1\2ses', // bus, campus
- '/(alias|status|virus)/' => '\1es', // alias
+ '/(alias|status|virus)$/' => '\1es', // alias
'/(octop)us$/' => '\1i', // octopus
'/(ax|cris|test)is$/' => '\1es', // axis, crisis
'/s$/' => 's', // no change (compatibility)
@@ -141,63 +172,156 @@ if ( ! function_exists('plural'))
// --------------------------------------------------------------------
-/**
- * Camelize
- *
- * Takes multiple words separated by spaces or underscores and camelizes them
- *
- * @access public
- * @param string
- * @return str
- */
if ( ! function_exists('camelize'))
{
+ /**
+ * Camelize
+ *
+ * Takes multiple words separated by spaces or underscores and camelizes them
+ *
+ * @param string $str Input string
+ * @return string
+ */
function camelize($str)
{
- $str = 'x'.strtolower(trim($str));
- $str = ucwords(preg_replace('/[\s_]+/', ' ', $str));
- return substr(str_replace(' ', '', $str), 1);
+ return strtolower($str[0]).substr(str_replace(' ', '', ucwords(preg_replace('/[\s_]+/', ' ', $str))), 1);
}
}
// --------------------------------------------------------------------
-/**
- * Underscore
- *
- * Takes multiple words separated by spaces and underscores them
- *
- * @access public
- * @param string
- * @return str
- */
if ( ! function_exists('underscore'))
{
+ /**
+ * Underscore
+ *
+ * Takes multiple words separated by spaces and underscores them
+ *
+ * @param string $str Input string
+ * @return string
+ */
function underscore($str)
{
- return preg_replace('/[\s]+/', '_', strtolower(trim($str)));
+ return preg_replace('/[\s]+/', '_', trim(MB_ENABLED ? mb_strtolower($str) : strtolower($str)));
}
}
// --------------------------------------------------------------------
-/**
- * Humanize
- *
- * Takes multiple words separated by underscores and changes them to spaces
- *
- * @access public
- * @param string
- * @return str
- */
if ( ! function_exists('humanize'))
{
- function humanize($str)
+ /**
+ * Humanize
+ *
+ * Takes multiple words separated by the separator and changes them to spaces
+ *
+ * @param string $str Input string
+ * @param string $separator Input separator
+ * @return string
+ */
+ function humanize($str, $separator = '_')
+ {
+ return ucwords(preg_replace('/['.preg_quote($separator).']+/', ' ', trim(MB_ENABLED ? mb_strtolower($str) : strtolower($str))));
+ }
+}
+
+// --------------------------------------------------------------------
+
+if ( ! function_exists('word_is_countable'))
+{
+ /**
+ * Checks if the given word has a plural version.
+ *
+ * @param string $word Word to check
+ * @return bool
+ */
+ function word_is_countable($word)
+ {
+ return ! in_array(
+ strtolower($word),
+ array(
+ 'audio',
+ 'bison',
+ 'chassis',
+ 'compensation',
+ 'coreopsis',
+ 'data',
+ 'deer',
+ 'education',
+ 'emoji',
+ 'equipment',
+ 'fish',
+ 'furniture',
+ 'gold',
+ 'information',
+ 'knowledge',
+ 'love',
+ 'rain',
+ 'money',
+ 'moose',
+ 'nutrition',
+ 'offspring',
+ 'plankton',
+ 'pokemon',
+ 'police',
+ 'rice',
+ 'series',
+ 'sheep',
+ 'species',
+ 'swine',
+ 'traffic',
+ 'wheat'
+ )
+ );
+ }
+}
+
+// --------------------------------------------------------------------
+
+if ( ! function_exists('is_countable'))
+{
+ function is_countable($word)
{
- return ucwords(preg_replace('/[_]+/', ' ', strtolower(trim($str))));
+ trigger_error('is_countable() is a native PHP function since version 7.3.0; use word_is_countable() instead', E_USER_WARNING);
+ return word_is_countable($word);
}
}
+// ------------------------------------------------------------------------
+
+if ( ! function_exists('ordinal_format'))
+{
+ /**
+ * Returns the English ordinal numeral for a given number
+ *
+ * @param int $number
+ * @return string
+ */
+ function ordinal_format($number)
+ {
+ if ( ! ctype_digit((string) $number) OR $number < 1)
+ {
+ return $number;
+ }
+
+ $last_digit = array(
+ 0 => 'th',
+ 1 => 'st',
+ 2 => 'nd',
+ 3 => 'rd',
+ 4 => 'th',
+ 5 => 'th',
+ 6 => 'th',
+ 7 => 'th',
+ 8 => 'th',
+ 9 => 'th'
+ );
+
+ if (($number % 100) >= 11 && ($number % 100) <= 13)
+ {
+ return $number.'th';
+ }
-/* End of file inflector_helper.php */
-/* Location: ./system/helpers/inflector_helper.php */ \ No newline at end of file
+ return $number.$last_digit[$number % 10];
+ }
+}
diff --git a/system/helpers/language_helper.php b/system/helpers/language_helper.php
index e8a28858f..d6cc1c12c 100644
--- a/system/helpers/language_helper.php
+++ b/system/helpers/language_helper.php
@@ -1,19 +1,42 @@
-<?php if ( ! defined('BASEPATH')) exit('No direct script access allowed');
+<?php
/**
* CodeIgniter
*
- * An open source application development framework for PHP 5.1.6 or newer
+ * An open source application development framework for PHP
*
- * @package CodeIgniter
- * @author ExpressionEngine Dev Team
- * @copyright Copyright (c) 2008 - 2014, EllisLab, Inc.
- * @license http://codeigniter.com/user_guide/license.html
- * @link http://codeigniter.com
- * @since Version 1.0
+ * This content is released under the MIT License (MIT)
+ *
+ * Copyright (c) 2019 - 2022, CodeIgniter Foundation
+ *
+ * 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.
+ *
+ * @package CodeIgniter
+ * @author EllisLab Dev Team
+ * @copyright Copyright (c) 2008 - 2014, EllisLab, Inc. (https://ellislab.com/)
+ * @copyright Copyright (c) 2014 - 2019, British Columbia Institute of Technology (https://bcit.ca/)
+ * @copyright Copyright (c) 2019 - 2022, CodeIgniter Foundation (https://codeigniter.com/)
+ * @license https://opensource.org/licenses/MIT MIT License
+ * @link https://codeigniter.com
+ * @since Version 1.0.0
* @filesource
*/
-
-// ------------------------------------------------------------------------
+defined('BASEPATH') OR exit('No direct script access allowed');
/**
* CodeIgniter Language Helpers
@@ -21,38 +44,33 @@
* @package CodeIgniter
* @subpackage Helpers
* @category Helpers
- * @author ExpressionEngine Dev Team
- * @link http://codeigniter.com/user_guide/helpers/language_helper.html
+ * @author EllisLab Dev Team
+ * @link https://codeigniter.com/userguide3/helpers/language_helper.html
*/
// ------------------------------------------------------------------------
-/**
- * Lang
- *
- * Fetches a language variable and optionally outputs a form label
- *
- * @access public
- * @param string the language line
- * @param string the id of the form element
- * @return string
- */
if ( ! function_exists('lang'))
{
- function lang($line, $id = '')
+ /**
+ * Lang
+ *
+ * Fetches a language variable and optionally outputs a form label
+ *
+ * @param string $line The language line
+ * @param string $for The "for" value (id of the form element)
+ * @param array $attributes Any additional HTML attributes
+ * @return string
+ */
+ function lang($line, $for = '', $attributes = array())
{
- $CI =& get_instance();
- $line = $CI->lang->line($line);
+ $line = get_instance()->lang->line($line);
- if ($id != '')
+ if ($for !== '')
{
- $line = '<label for="'.$id.'">'.$line."</label>";
+ $line = '<label for="'.$for.'"'._stringify_attributes($attributes).'>'.$line.'</label>';
}
return $line;
}
}
-
-// ------------------------------------------------------------------------
-/* End of file language_helper.php */
-/* Location: ./system/helpers/language_helper.php */ \ No newline at end of file
diff --git a/system/helpers/number_helper.php b/system/helpers/number_helper.php
index f18fee83d..27ad80fd3 100644
--- a/system/helpers/number_helper.php
+++ b/system/helpers/number_helper.php
@@ -1,19 +1,42 @@
-<?php if ( ! defined('BASEPATH')) exit('No direct script access allowed');
+<?php
/**
* CodeIgniter
*
- * An open source application development framework for PHP 5.1.6 or newer
+ * An open source application development framework for PHP
*
- * @package CodeIgniter
- * @author ExpressionEngine Dev Team
- * @copyright Copyright (c) 2008 - 2014, EllisLab, Inc.
- * @license http://codeigniter.com/user_guide/license.html
- * @link http://codeigniter.com
- * @since Version 1.0
+ * This content is released under the MIT License (MIT)
+ *
+ * Copyright (c) 2019 - 2022, CodeIgniter Foundation
+ *
+ * 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.
+ *
+ * @package CodeIgniter
+ * @author EllisLab Dev Team
+ * @copyright Copyright (c) 2008 - 2014, EllisLab, Inc. (https://ellislab.com/)
+ * @copyright Copyright (c) 2014 - 2019, British Columbia Institute of Technology (https://bcit.ca/)
+ * @copyright Copyright (c) 2019 - 2022, CodeIgniter Foundation (https://codeigniter.com/)
+ * @license https://opensource.org/licenses/MIT MIT License
+ * @link https://codeigniter.com
+ * @since Version 1.0.0
* @filesource
*/
-
-// ------------------------------------------------------------------------
+defined('BASEPATH') OR exit('No direct script access allowed');
/**
* CodeIgniter Number Helpers
@@ -21,21 +44,21 @@
* @package CodeIgniter
* @subpackage Helpers
* @category Helpers
- * @author ExpressionEngine Dev Team
- * @link http://codeigniter.com/user_guide/helpers/number_helper.html
+ * @author EllisLab Dev Team
+ * @link https://codeigniter.com/userguide3/helpers/number_helper.html
*/
// ------------------------------------------------------------------------
-/**
- * Formats a numbers as bytes, based on size, and adds the appropriate suffix
- *
- * @access public
- * @param mixed // will be cast as int
- * @return string
- */
if ( ! function_exists('byte_format'))
{
+ /**
+ * Formats a numbers as bytes, based on size, and adds the appropriate suffix
+ *
+ * @param mixed will be cast as int
+ * @param int
+ * @return string
+ */
function byte_format($num, $precision = 1)
{
$CI =& get_instance();
@@ -70,7 +93,3 @@ if ( ! function_exists('byte_format'))
return number_format($num, $precision).' '.$unit;
}
}
-
-
-/* End of file number_helper.php */
-/* Location: ./system/helpers/number_helper.php */ \ No newline at end of file
diff --git a/system/helpers/path_helper.php b/system/helpers/path_helper.php
index 0ecb7ed3b..a8f782357 100644
--- a/system/helpers/path_helper.php
+++ b/system/helpers/path_helper.php
@@ -1,19 +1,42 @@
-<?php if ( ! defined('BASEPATH')) exit('No direct script access allowed');
+<?php
/**
* CodeIgniter
*
- * An open source application development framework for PHP 5.1.6 or newer
+ * An open source application development framework for PHP
*
- * @package CodeIgniter
- * @author ExpressionEngine Dev Team
- * @copyright Copyright (c) 2008 - 2014, EllisLab, Inc.
- * @license http://codeigniter.com/user_guide/license.html
- * @link http://codeigniter.com
- * @since Version 1.0
+ * This content is released under the MIT License (MIT)
+ *
+ * Copyright (c) 2019 - 2022, CodeIgniter Foundation
+ *
+ * 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.
+ *
+ * @package CodeIgniter
+ * @author EllisLab Dev Team
+ * @copyright Copyright (c) 2008 - 2014, EllisLab, Inc. (https://ellislab.com/)
+ * @copyright Copyright (c) 2014 - 2019, British Columbia Institute of Technology (https://bcit.ca/)
+ * @copyright Copyright (c) 2019 - 2022, CodeIgniter Foundation (https://codeigniter.com/)
+ * @license https://opensource.org/licenses/MIT MIT License
+ * @link https://codeigniter.com
+ * @since Version 1.0.0
* @filesource
*/
-
-// ------------------------------------------------------------------------
+defined('BASEPATH') OR exit('No direct script access allowed');
/**
* CodeIgniter Path Helpers
@@ -21,52 +44,40 @@
* @package CodeIgniter
* @subpackage Helpers
* @category Helpers
- * @author ExpressionEngine Dev Team
- * @link http://codeigniter.com/user_guide/helpers/xml_helper.html
+ * @author EllisLab Dev Team
+ * @link https://codeigniter.com/userguide3/helpers/path_helper.html
*/
// ------------------------------------------------------------------------
-/**
- * Set Realpath
- *
- * @access public
- * @param string
- * @param bool checks to see if the path exists
- * @return string
- */
if ( ! function_exists('set_realpath'))
{
+ /**
+ * Set Realpath
+ *
+ * @param string
+ * @param bool checks to see if the path exists
+ * @return string
+ */
function set_realpath($path, $check_existance = FALSE)
{
- // Security check to make sure the path is NOT a URL. No remote file inclusion!
- if (preg_match("#^(http:\/\/|https:\/\/|www\.|ftp|[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3})#i", $path))
+ // Security check to make sure the path is NOT a URL. No remote file inclusion!
+ if (preg_match('#^(http:\/\/|https:\/\/|www\.|ftp|php:\/\/)#i', $path) OR filter_var($path, FILTER_VALIDATE_IP) === $path)
{
show_error('The path you submitted must be a local server path, not a URL');
}
// Resolve the path
- if (function_exists('realpath') AND @realpath($path) !== FALSE)
+ if (realpath($path) !== FALSE)
{
- $path = realpath($path).'/';
+ $path = realpath($path);
}
-
- // Add a trailing slash
- $path = preg_replace("#([^/])/*$#", "\\1/", $path);
-
- // Make sure the path exists
- if ($check_existance == TRUE)
+ elseif ($check_existance && ! is_dir($path) && ! is_file($path))
{
- if ( ! is_dir($path))
- {
- show_error('Not a valid path: '.$path);
- }
+ show_error('Not a valid path: '.$path);
}
- return $path;
+ // Add a trailing slash, if this is a directory
+ return is_dir($path) ? rtrim($path, DIRECTORY_SEPARATOR).DIRECTORY_SEPARATOR : $path;
}
}
-
-
-/* End of file path_helper.php */
-/* Location: ./system/helpers/path_helper.php */ \ No newline at end of file
diff --git a/system/helpers/security_helper.php b/system/helpers/security_helper.php
index cfb1e9d2d..54851a094 100644
--- a/system/helpers/security_helper.php
+++ b/system/helpers/security_helper.php
@@ -1,19 +1,42 @@
-<?php if ( ! defined('BASEPATH')) exit('No direct script access allowed');
+<?php
/**
* CodeIgniter
*
- * An open source application development framework for PHP 5.1.6 or newer
+ * An open source application development framework for PHP
*
- * @package CodeIgniter
- * @author ExpressionEngine Dev Team
- * @copyright Copyright (c) 2008 - 2014, EllisLab, Inc.
- * @license http://codeigniter.com/user_guide/license.html
- * @link http://codeigniter.com
- * @since Version 1.0
+ * This content is released under the MIT License (MIT)
+ *
+ * Copyright (c) 2019 - 2022, CodeIgniter Foundation
+ *
+ * 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.
+ *
+ * @package CodeIgniter
+ * @author EllisLab Dev Team
+ * @copyright Copyright (c) 2008 - 2014, EllisLab, Inc. (https://ellislab.com/)
+ * @copyright Copyright (c) 2014 - 2019, British Columbia Institute of Technology (https://bcit.ca/)
+ * @copyright Copyright (c) 2019 - 2022, CodeIgniter Foundation (https://codeigniter.com/)
+ * @license https://opensource.org/licenses/MIT MIT License
+ * @link https://codeigniter.com
+ * @since Version 1.0.0
* @filesource
*/
-
-// ------------------------------------------------------------------------
+defined('BASEPATH') OR exit('No direct script access allowed');
/**
* CodeIgniter Security Helpers
@@ -21,108 +44,71 @@
* @package CodeIgniter
* @subpackage Helpers
* @category Helpers
- * @author ExpressionEngine Dev Team
- * @link http://codeigniter.com/user_guide/helpers/security_helper.html
+ * @author EllisLab Dev Team
+ * @link https://codeigniter.com/userguide3/helpers/security_helper.html
*/
// ------------------------------------------------------------------------
-/**
- * XSS Filtering
- *
- * @access public
- * @param string
- * @param bool whether or not the content is an image file
- * @return string
- */
if ( ! function_exists('xss_clean'))
{
+ /**
+ * XSS Filtering
+ *
+ * @param string
+ * @param bool whether or not the content is an image file
+ * @return string
+ */
function xss_clean($str, $is_image = FALSE)
{
- $CI =& get_instance();
- return $CI->security->xss_clean($str, $is_image);
+ return get_instance()->security->xss_clean($str, $is_image);
}
}
// ------------------------------------------------------------------------
-/**
- * Sanitize Filename
- *
- * @access public
- * @param string
- * @return string
- */
if ( ! function_exists('sanitize_filename'))
{
+ /**
+ * Sanitize Filename
+ *
+ * @param string
+ * @return string
+ */
function sanitize_filename($filename)
{
- $CI =& get_instance();
- return $CI->security->sanitize_filename($filename);
- }
-}
-
-// --------------------------------------------------------------------
-
-/**
- * Hash encode a string
- *
- * @access public
- * @param string
- * @return string
- */
-if ( ! function_exists('do_hash'))
-{
- function do_hash($str, $type = 'sha1')
- {
- if ($type == 'sha1')
- {
- return sha1($str);
- }
- else
- {
- return md5($str);
- }
+ return get_instance()->security->sanitize_filename($filename);
}
}
// ------------------------------------------------------------------------
-/**
- * Strip Image Tags
- *
- * @access public
- * @param string
- * @return string
- */
if ( ! function_exists('strip_image_tags'))
{
+ /**
+ * Strip Image Tags
+ *
+ * @param string
+ * @return string
+ */
function strip_image_tags($str)
{
- $str = preg_replace("#<img\s+.*?src\s*=\s*[\"'](.+?)[\"'].*?\>#", "\\1", $str);
- $str = preg_replace("#<img\s+.*?src\s*=\s*(.+?).*?\>#", "\\1", $str);
-
- return $str;
+ return get_instance()->security->strip_image_tags($str);
}
}
// ------------------------------------------------------------------------
-/**
- * Convert PHP tags to entities
- *
- * @access public
- * @param string
- * @return string
- */
if ( ! function_exists('encode_php_tags'))
{
+ /**
+ * Convert PHP tags to entities
+ *
+ * @param string
+ * @return string
+ */
function encode_php_tags($str)
{
- return str_replace(array('<?php', '<?PHP', '<?', '?>'), array('&lt;?php', '&lt;?PHP', '&lt;?', '?&gt;'), $str);
+ return str_replace(array('<?', '?>'), array('&lt;?', '?&gt;'), $str);
}
}
-
-
-/* End of file security_helper.php */
-/* Location: ./system/helpers/security_helper.php */ \ No newline at end of file
diff --git a/system/helpers/smiley_helper.php b/system/helpers/smiley_helper.php
deleted file mode 100644
index eb325c5cb..000000000
--- a/system/helpers/smiley_helper.php
+++ /dev/null
@@ -1,281 +0,0 @@
-<?php if ( ! defined('BASEPATH')) exit('No direct script access allowed');
-/**
- * CodeIgniter
- *
- * An open source application development framework for PHP 5.1.6 or newer
- *
- * @package CodeIgniter
- * @author ExpressionEngine Dev Team
- * @copyright Copyright (c) 2008 - 2014, EllisLab, Inc.
- * @license http://codeigniter.com/user_guide/license.html
- * @link http://codeigniter.com
- * @since Version 1.0
- * @filesource
- */
-
-// ------------------------------------------------------------------------
-
-/**
- * CodeIgniter Smiley Helpers
- *
- * @package CodeIgniter
- * @subpackage Helpers
- * @category Helpers
- * @author ExpressionEngine Dev Team
- * @link http://codeigniter.com/user_guide/helpers/smiley_helper.html
- */
-
-// ------------------------------------------------------------------------
-
-/**
- * Smiley Javascript
- *
- * Returns the javascript required for the smiley insertion. Optionally takes
- * an array of aliases to loosely couple the smiley array to the view.
- *
- * @access public
- * @param mixed alias name or array of alias->field_id pairs
- * @param string field_id if alias name was passed in
- * @return array
- */
-if ( ! function_exists('smiley_js'))
-{
- function smiley_js($alias = '', $field_id = '', $inline = TRUE)
- {
- static $do_setup = TRUE;
-
- $r = '';
-
- if ($alias != '' && ! is_array($alias))
- {
- $alias = array($alias => $field_id);
- }
-
- if ($do_setup === TRUE)
- {
- $do_setup = FALSE;
-
- $m = array();
-
- if (is_array($alias))
- {
- foreach ($alias as $name => $id)
- {
- $m[] = '"'.$name.'" : "'.$id.'"';
- }
- }
-
- $m = '{'.implode(',', $m).'}';
-
- $r .= <<<EOF
- var smiley_map = {$m};
-
- function insert_smiley(smiley, field_id) {
- var el = document.getElementById(field_id), newStart;
-
- if ( ! el && smiley_map[field_id]) {
- el = document.getElementById(smiley_map[field_id]);
-
- if ( ! el)
- return false;
- }
-
- el.focus();
- smiley = " " + smiley;
-
- if ('selectionStart' in el) {
- newStart = el.selectionStart + smiley.length;
-
- el.value = el.value.substr(0, el.selectionStart) +
- smiley +
- el.value.substr(el.selectionEnd, el.value.length);
- el.setSelectionRange(newStart, newStart);
- }
- else if (document.selection) {
- document.selection.createRange().text = smiley;
- }
- }
-EOF;
- }
- else
- {
- if (is_array($alias))
- {
- foreach ($alias as $name => $id)
- {
- $r .= 'smiley_map["'.$name.'"] = "'.$id.'";'."\n";
- }
- }
- }
-
- if ($inline)
- {
- return '<script type="text/javascript" charset="utf-8">/*<![CDATA[ */'.$r.'// ]]></script>';
- }
- else
- {
- return $r;
- }
- }
-}
-
-// ------------------------------------------------------------------------
-
-/**
- * Get Clickable Smileys
- *
- * Returns an array of image tag links that can be clicked to be inserted
- * into a form field.
- *
- * @access public
- * @param string the URL to the folder containing the smiley images
- * @return array
- */
-if ( ! function_exists('get_clickable_smileys'))
-{
- function get_clickable_smileys($image_url, $alias = '', $smileys = NULL)
- {
- // For backward compatibility with js_insert_smiley
-
- if (is_array($alias))
- {
- $smileys = $alias;
- }
-
- if ( ! is_array($smileys))
- {
- if (FALSE === ($smileys = _get_smiley_array()))
- {
- return $smileys;
- }
- }
-
- // Add a trailing slash to the file path if needed
- $image_url = rtrim($image_url, '/').'/';
-
- $used = array();
- foreach ($smileys as $key => $val)
- {
- // Keep duplicates from being used, which can happen if the
- // mapping array contains multiple identical replacements. For example:
- // :-) and :) might be replaced with the same image so both smileys
- // will be in the array.
- if (isset($used[$smileys[$key][0]]))
- {
- continue;
- }
-
- $link[] = "<a href=\"javascript:void(0);\" onclick=\"insert_smiley('".$key."', '".$alias."')\"><img src=\"".$image_url.$smileys[$key][0]."\" width=\"".$smileys[$key][1]."\" height=\"".$smileys[$key][2]."\" alt=\"".$smileys[$key][3]."\" style=\"border:0;\" /></a>";
-
- $used[$smileys[$key][0]] = TRUE;
- }
-
- return $link;
- }
-}
-
-// ------------------------------------------------------------------------
-
-/**
- * Parse Smileys
- *
- * Takes a string as input and swaps any contained smileys for the actual image
- *
- * @access public
- * @param string the text to be parsed
- * @param string the URL to the folder containing the smiley images
- * @return string
- */
-if ( ! function_exists('parse_smileys'))
-{
- function parse_smileys($str = '', $image_url = '', $smileys = NULL)
- {
- if ($image_url == '')
- {
- return $str;
- }
-
- if ( ! is_array($smileys))
- {
- if (FALSE === ($smileys = _get_smiley_array()))
- {
- return $str;
- }
- }
-
- // Add a trailing slash to the file path if needed
- $image_url = preg_replace("/(.+?)\/*$/", "\\1/", $image_url);
-
- foreach ($smileys as $key => $val)
- {
- $str = str_replace($key, "<img src=\"".$image_url.$smileys[$key][0]."\" width=\"".$smileys[$key][1]."\" height=\"".$smileys[$key][2]."\" alt=\"".$smileys[$key][3]."\" style=\"border:0;\" />", $str);
- }
-
- return $str;
- }
-}
-
-// ------------------------------------------------------------------------
-
-/**
- * Get Smiley Array
- *
- * Fetches the config/smiley.php file
- *
- * @access private
- * @return mixed
- */
-if ( ! function_exists('_get_smiley_array'))
-{
- function _get_smiley_array()
- {
- if (defined('ENVIRONMENT') AND file_exists(APPPATH.'config/'.ENVIRONMENT.'/smileys.php'))
- {
- include(APPPATH.'config/'.ENVIRONMENT.'/smileys.php');
- }
- elseif (file_exists(APPPATH.'config/smileys.php'))
- {
- include(APPPATH.'config/smileys.php');
- }
-
- if (isset($smileys) AND is_array($smileys))
- {
- return $smileys;
- }
-
- return FALSE;
- }
-}
-
-// ------------------------------------------------------------------------
-
-/**
- * JS Insert Smiley
- *
- * Generates the javascript function needed to insert smileys into a form field
- *
- * DEPRECATED as of version 1.7.2, use smiley_js instead
- *
- * @access public
- * @param string form name
- * @param string field name
- * @return string
- */
-if ( ! function_exists('js_insert_smiley'))
-{
- function js_insert_smiley($form_name = '', $form_field = '')
- {
- return <<<EOF
-<script type="text/javascript">
- function insert_smiley(smiley)
- {
- document.{$form_name}.{$form_field}.value += " " + smiley;
- }
-</script>
-EOF;
- }
-}
-
-
-/* End of file smiley_helper.php */
-/* Location: ./system/helpers/smiley_helper.php */ \ No newline at end of file
diff --git a/system/helpers/string_helper.php b/system/helpers/string_helper.php
index 9a2f2be31..0cd87e915 100644
--- a/system/helpers/string_helper.php
+++ b/system/helpers/string_helper.php
@@ -1,19 +1,42 @@
-<?php if ( ! defined('BASEPATH')) exit('No direct script access allowed');
+<?php
/**
* CodeIgniter
*
- * An open source application development framework for PHP 5.1.6 or newer
+ * An open source application development framework for PHP
*
- * @package CodeIgniter
- * @author ExpressionEngine Dev Team
- * @copyright Copyright (c) 2008 - 2014, EllisLab, Inc.
- * @license http://codeigniter.com/user_guide/license.html
- * @link http://codeigniter.com
- * @since Version 1.0
+ * This content is released under the MIT License (MIT)
+ *
+ * Copyright (c) 2019 - 2022, CodeIgniter Foundation
+ *
+ * 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.
+ *
+ * @package CodeIgniter
+ * @author EllisLab Dev Team
+ * @copyright Copyright (c) 2008 - 2014, EllisLab, Inc. (https://ellislab.com/)
+ * @copyright Copyright (c) 2014 - 2019, British Columbia Institute of Technology (https://bcit.ca/)
+ * @copyright Copyright (c) 2019 - 2022, CodeIgniter Foundation (https://codeigniter.com/)
+ * @license https://opensource.org/licenses/MIT MIT License
+ * @link https://codeigniter.com
+ * @since Version 1.0.0
* @filesource
*/
-
-// ------------------------------------------------------------------------
+defined('BASEPATH') OR exit('No direct script access allowed');
/**
* CodeIgniter String Helpers
@@ -21,60 +44,32 @@
* @package CodeIgniter
* @subpackage Helpers
* @category Helpers
- * @author ExpressionEngine Dev Team
- * @link http://codeigniter.com/user_guide/helpers/string_helper.html
- */
-
-// ------------------------------------------------------------------------
-
-/**
- * Trim Slashes
- *
- * Removes any leading/trailing slashes from a string:
- *
- * /this/that/theother/
- *
- * becomes:
- *
- * this/that/theother
- *
- * @access public
- * @param string
- * @return string
+ * @author EllisLab Dev Team
+ * @link https://codeigniter.com/userguide3/helpers/string_helper.html
*/
-if ( ! function_exists('trim_slashes'))
-{
- function trim_slashes($str)
- {
- return trim($str, '/');
- }
-}
// ------------------------------------------------------------------------
-/**
- * Strip Slashes
- *
- * Removes slashes contained in a string or in an array
- *
- * @access public
- * @param mixed string or array
- * @return mixed string or array
- */
if ( ! function_exists('strip_slashes'))
{
+ /**
+ * Strip Slashes
+ *
+ * Removes slashes contained in a string or in an array
+ *
+ * @param mixed string or array
+ * @return mixed string or array
+ */
function strip_slashes($str)
{
- if (is_array($str))
+ if ( ! is_array($str))
{
- foreach ($str as $key => $val)
- {
- $str[$key] = strip_slashes($val);
- }
+ return stripslashes($str);
}
- else
+
+ foreach ($str as $key => $val)
{
- $str = stripslashes($str);
+ $str[$key] = strip_slashes($val);
}
return $str;
@@ -83,17 +78,16 @@ if ( ! function_exists('strip_slashes'))
// ------------------------------------------------------------------------
-/**
- * Strip Quotes
- *
- * Removes single and double quotes from a string
- *
- * @access public
- * @param string
- * @return string
- */
if ( ! function_exists('strip_quotes'))
{
+ /**
+ * Strip Quotes
+ *
+ * Removes single and double quotes from a string
+ *
+ * @param string
+ * @return string
+ */
function strip_quotes($str)
{
return str_replace(array('"', "'"), '', $str);
@@ -102,17 +96,16 @@ if ( ! function_exists('strip_quotes'))
// ------------------------------------------------------------------------
-/**
- * Quotes to Entities
- *
- * Converts single and double quotes to entities
- *
- * @access public
- * @param string
- * @return string
- */
if ( ! function_exists('quotes_to_entities'))
{
+ /**
+ * Quotes to Entities
+ *
+ * Converts single and double quotes to entities
+ *
+ * @param string
+ * @return string
+ */
function quotes_to_entities($str)
{
return str_replace(array("\'","\"","'",'"'), array("&#39;","&quot;","&#39;","&quot;"), $str);
@@ -121,187 +114,143 @@ if ( ! function_exists('quotes_to_entities'))
// ------------------------------------------------------------------------
-/**
- * Reduce Double Slashes
- *
- * Converts double slashes in a string to a single slash,
- * except those found in http://
- *
- * http://www.some-site.com//index.php
- *
- * becomes:
- *
- * http://www.some-site.com/index.php
- *
- * @access public
- * @param string
- * @return string
- */
if ( ! function_exists('reduce_double_slashes'))
{
+ /**
+ * Reduce Double Slashes
+ *
+ * Converts double slashes in a string to a single slash,
+ * except those found in http://
+ *
+ * http://www.some-site.com//index.php
+ *
+ * becomes:
+ *
+ * http://www.some-site.com/index.php
+ *
+ * @param string
+ * @return string
+ */
function reduce_double_slashes($str)
{
- return preg_replace("#(^|[^:])//+#", "\\1/", $str);
+ return preg_replace('#(^|[^:])//+#', '\\1/', $str);
}
}
// ------------------------------------------------------------------------
-/**
- * Reduce Multiples
- *
- * Reduces multiple instances of a particular character. Example:
- *
- * Fred, Bill,, Joe, Jimmy
- *
- * becomes:
- *
- * Fred, Bill, Joe, Jimmy
- *
- * @access public
- * @param string
- * @param string the character you wish to reduce
- * @param bool TRUE/FALSE - whether to trim the character from the beginning/end
- * @return string
- */
if ( ! function_exists('reduce_multiples'))
{
+ /**
+ * Reduce Multiples
+ *
+ * Reduces multiple instances of a particular character. Example:
+ *
+ * Fred, Bill,, Joe, Jimmy
+ *
+ * becomes:
+ *
+ * Fred, Bill, Joe, Jimmy
+ *
+ * @param string
+ * @param string the character you wish to reduce
+ * @param bool TRUE/FALSE - whether to trim the character from the beginning/end
+ * @return string
+ */
function reduce_multiples($str, $character = ',', $trim = FALSE)
{
$str = preg_replace('#'.preg_quote($character, '#').'{2,}#', $character, $str);
-
- if ($trim === TRUE)
- {
- $str = trim($str, $character);
- }
-
- return $str;
+ return ($trim === TRUE) ? trim($str, $character) : $str;
}
}
// ------------------------------------------------------------------------
-/**
- * Create a Random String
- *
- * Useful for generating passwords or hashes.
- *
- * @access public
- * @param string type of random string. basic, alpha, alunum, numeric, nozero, unique, md5, encrypt and sha1
- * @param integer number of characters
- * @return string
- */
if ( ! function_exists('random_string'))
{
+ /**
+ * Create a "Random" String
+ *
+ * @param string type of random string. basic, alpha, alnum, numeric, nozero, md5 and sha1
+ * @param int number of characters
+ * @return string
+ */
function random_string($type = 'alnum', $len = 8)
{
- switch($type)
+ switch ($type)
{
- case 'basic' : return mt_rand();
- break;
- case 'alnum' :
- case 'numeric' :
- case 'nozero' :
- case 'alpha' :
-
- switch ($type)
- {
- case 'alpha' : $pool = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ';
- break;
- case 'alnum' : $pool = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ';
- break;
- case 'numeric' : $pool = '0123456789';
- break;
- case 'nozero' : $pool = '123456789';
- break;
- }
-
- $str = '';
- for ($i=0; $i < $len; $i++)
- {
- $str .= substr($pool, mt_rand(0, strlen($pool) -1), 1);
- }
- return $str;
- break;
- case 'unique' :
- case 'md5' :
-
- return md5(uniqid(mt_rand()));
- break;
- case 'encrypt' :
- case 'sha1' :
-
- $CI =& get_instance();
- $CI->load->helper('security');
-
- return do_hash(uniqid(mt_rand(), TRUE), 'sha1');
- break;
+ case 'basic':
+ return mt_rand();
+ case 'alnum':
+ case 'numeric':
+ case 'nozero':
+ case 'alpha':
+ switch ($type)
+ {
+ case 'alpha':
+ $pool = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ';
+ break;
+ case 'alnum':
+ $pool = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ';
+ break;
+ case 'numeric':
+ $pool = '0123456789';
+ break;
+ case 'nozero':
+ $pool = '123456789';
+ break;
+ }
+ return substr(str_shuffle(str_repeat($pool, ceil($len / strlen($pool)))), 0, $len);
+ case 'md5':
+ return md5(uniqid(mt_rand()));
+ case 'sha1':
+ return sha1(uniqid(mt_rand(), TRUE));
}
}
}
// ------------------------------------------------------------------------
-/**
- * Add's _1 to a string or increment the ending number to allow _2, _3, etc
- *
- * @param string $str required
- * @param string $separator What should the duplicate number be appended with
- * @param string $first Which number should be used for the first dupe increment
- * @return string
- */
-function increment_string($str, $separator = '_', $first = 1)
+if ( ! function_exists('increment_string'))
{
- preg_match('/(.+)'.$separator.'([0-9]+)$/', $str, $match);
-
- return isset($match[2]) ? $match[1].$separator.($match[2] + 1) : $str.$separator.$first;
+ /**
+ * Add's _1 to a string or increment the ending number to allow _2, _3, etc
+ *
+ * @param string required
+ * @param string What should the duplicate number be appended with
+ * @param string Which number should be used for the first dupe increment
+ * @return string
+ */
+ function increment_string($str, $separator = '_', $first = 1)
+ {
+ preg_match('/(.+)'.preg_quote($separator, '/').'([0-9]+)$/', $str, $match);
+ return isset($match[2]) ? $match[1].$separator.($match[2] + 1) : $str.$separator.$first;
+ }
}
// ------------------------------------------------------------------------
-/**
- * Alternator
- *
- * Allows strings to be alternated. See docs...
- *
- * @access public
- * @param string (as many parameters as needed)
- * @return string
- */
if ( ! function_exists('alternator'))
{
+ /**
+ * Alternator
+ *
+ * Allows strings to be alternated. See docs...
+ *
+ * @param string (as many parameters as needed)
+ * @return string
+ */
function alternator()
{
static $i;
- if (func_num_args() == 0)
+ if (func_num_args() === 0)
{
$i = 0;
return '';
}
+
$args = func_get_args();
return $args[($i++ % count($args))];
}
}
-
-// ------------------------------------------------------------------------
-
-/**
- * Repeater function
- *
- * @access public
- * @param string
- * @param integer number of repeats
- * @return string
- */
-if ( ! function_exists('repeater'))
-{
- function repeater($data, $num = 1)
- {
- return (($num > 0) ? str_repeat($data, $num) : '');
- }
-}
-
-
-/* End of file string_helper.php */
-/* Location: ./system/helpers/string_helper.php */ \ No newline at end of file
diff --git a/system/helpers/text_helper.php b/system/helpers/text_helper.php
index 8be50d077..506d45aca 100644
--- a/system/helpers/text_helper.php
+++ b/system/helpers/text_helper.php
@@ -1,19 +1,42 @@
-<?php if ( ! defined('BASEPATH')) exit('No direct script access allowed');
+<?php
/**
* CodeIgniter
*
- * An open source application development framework for PHP 5.1.6 or newer
+ * An open source application development framework for PHP
*
- * @package CodeIgniter
- * @author ExpressionEngine Dev Team
- * @copyright Copyright (c) 2008 - 2014, EllisLab, Inc.
- * @license http://codeigniter.com/user_guide/license.html
- * @link http://codeigniter.com
- * @since Version 1.0
+ * This content is released under the MIT License (MIT)
+ *
+ * Copyright (c) 2019 - 2022, CodeIgniter Foundation
+ *
+ * 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.
+ *
+ * @package CodeIgniter
+ * @author EllisLab Dev Team
+ * @copyright Copyright (c) 2008 - 2014, EllisLab, Inc. (https://ellislab.com/)
+ * @copyright Copyright (c) 2014 - 2019, British Columbia Institute of Technology (https://bcit.ca/)
+ * @copyright Copyright (c) 2019 - 2022, CodeIgniter Foundation (https://codeigniter.com/)
+ * @license https://opensource.org/licenses/MIT MIT License
+ * @link https://codeigniter.com
+ * @since Version 1.0.0
* @filesource
*/
-
-// ------------------------------------------------------------------------
+defined('BASEPATH') OR exit('No direct script access allowed');
/**
* CodeIgniter Text Helpers
@@ -21,35 +44,34 @@
* @package CodeIgniter
* @subpackage Helpers
* @category Helpers
- * @author ExpressionEngine Dev Team
- * @link http://codeigniter.com/user_guide/helpers/text_helper.html
+ * @author EllisLab Dev Team
+ * @link https://codeigniter.com/userguide3/helpers/text_helper.html
*/
// ------------------------------------------------------------------------
-/**
- * Word Limiter
- *
- * Limits a string to X number of words.
- *
- * @access public
- * @param string
- * @param integer
- * @param string the end character. Usually an ellipsis
- * @return string
- */
if ( ! function_exists('word_limiter'))
{
+ /**
+ * Word Limiter
+ *
+ * Limits a string to X number of words.
+ *
+ * @param string
+ * @param int
+ * @param string the end character. Usually an ellipsis
+ * @return string
+ */
function word_limiter($str, $limit = 100, $end_char = '&#8230;')
{
- if (trim($str) == '')
+ if (trim($str) === '')
{
return $str;
}
preg_match('/^\s*+(?:\S++\s*+){1,'.(int) $limit.'}/', $str, $matches);
- if (strlen($str) == strlen($matches[0]))
+ if (strlen($str) === strlen($matches[0]))
{
$end_char = '';
}
@@ -60,43 +82,43 @@ if ( ! function_exists('word_limiter'))
// ------------------------------------------------------------------------
-/**
- * Character Limiter
- *
- * Limits the string based on the character count. Preserves complete words
- * so the character count may not be exactly as specified.
- *
- * @access public
- * @param string
- * @param integer
- * @param string the end character. Usually an ellipsis
- * @return string
- */
if ( ! function_exists('character_limiter'))
{
+ /**
+ * Character Limiter
+ *
+ * Limits the string based on the character count. Preserves complete words
+ * so the character count may not be exactly as specified.
+ *
+ * @param string
+ * @param int
+ * @param string the end character. Usually an ellipsis
+ * @return string
+ */
function character_limiter($str, $n = 500, $end_char = '&#8230;')
{
- if (strlen($str) < $n)
+ if (mb_strlen($str) < $n)
{
return $str;
}
- $str = preg_replace("/\s+/", ' ', str_replace(array("\r\n", "\r", "\n"), ' ', $str));
+ // a bit complicated, but faster than preg_replace with \s+
+ $str = preg_replace('/ {2,}/', ' ', str_replace(array("\r", "\n", "\t", "\v", "\f"), ' ', $str));
- if (strlen($str) <= $n)
+ if (mb_strlen($str) <= $n)
{
return $str;
}
- $out = "";
+ $out = '';
foreach (explode(' ', trim($str)) as $val)
{
$out .= $val.' ';
- if (strlen($out) >= $n)
+ if (mb_strlen($out) >= $n)
{
$out = trim($out);
- return (strlen($out) == strlen($str)) ? $out : $out.$end_char;
+ return (mb_strlen($out) === mb_strlen($str)) ? $out : $out.$end_char;
}
}
}
@@ -104,24 +126,23 @@ if ( ! function_exists('character_limiter'))
// ------------------------------------------------------------------------
-/**
- * High ASCII to Entities
- *
- * Converts High ascii text and MS Word special characters to character entities
- *
- * @access public
- * @param string
- * @return string
- */
if ( ! function_exists('ascii_to_entities'))
{
+ /**
+ * High ASCII to Entities
+ *
+ * Converts high ASCII text and MS Word special characters to character entities
+ *
+ * @param string $str
+ * @return string
+ */
function ascii_to_entities($str)
{
- $count = 1;
- $out = '';
- $temp = array();
-
- for ($i = 0, $s = strlen($str); $i < $s; $i++)
+ $out = '';
+ $length = defined('MB_OVERLOAD_STRING')
+ ? mb_strlen($str, '8bit') - 1
+ : strlen($str) - 1;
+ for ($i = 0, $count = 1, $temp = array(); $i <= $length; $i++)
{
$ordinal = ord($str[$i]);
@@ -131,9 +152,9 @@ if ( ! function_exists('ascii_to_entities'))
If the $temp array has a value but we have moved on, then it seems only
fair that we output that entity and restart $temp before continuing. -Paul
*/
- if (count($temp) == 1)
+ if (count($temp) === 1)
{
- $out .= '&#'.array_shift($temp).';';
+ $out .= '&#'.array_shift($temp).';';
$count = 1;
}
@@ -141,21 +162,28 @@ if ( ! function_exists('ascii_to_entities'))
}
else
{
- if (count($temp) == 0)
+ if (count($temp) === 0)
{
$count = ($ordinal < 224) ? 2 : 3;
}
$temp[] = $ordinal;
- if (count($temp) == $count)
+ if (count($temp) === $count)
{
- $number = ($count == 3) ? (($temp['0'] % 16) * 4096) + (($temp['1'] % 64) * 64) + ($temp['2'] % 64) : (($temp['0'] % 32) * 64) + ($temp['1'] % 64);
+ $number = ($count === 3)
+ ? (($temp[0] % 16) * 4096) + (($temp[1] % 64) * 64) + ($temp[2] % 64)
+ : (($temp[0] % 32) * 64) + ($temp[1] % 64);
$out .= '&#'.$number.';';
$count = 1;
$temp = array();
}
+ // If this is the last iteration, just output whatever we have
+ elseif ($i === $length)
+ {
+ $out .= '&#'.implode(';', $temp).';';
+ }
}
}
@@ -165,26 +193,24 @@ if ( ! function_exists('ascii_to_entities'))
// ------------------------------------------------------------------------
-/**
- * Entities to ASCII
- *
- * Converts character entities back to ASCII
- *
- * @access public
- * @param string
- * @param bool
- * @return string
- */
if ( ! function_exists('entities_to_ascii'))
{
+ /**
+ * Entities to ASCII
+ *
+ * Converts character entities back to ASCII
+ *
+ * @param string
+ * @param bool
+ * @return string
+ */
function entities_to_ascii($str, $all = TRUE)
{
if (preg_match_all('/\&#(\d+)\;/', $str, $matches))
{
- for ($i = 0, $s = count($matches['0']); $i < $s; $i++)
+ for ($i = 0, $s = count($matches[0]); $i < $s; $i++)
{
- $digits = $matches['1'][$i];
-
+ $digits = $matches[1][$i];
$out = '';
if ($digits < 128)
@@ -194,25 +220,26 @@ if ( ! function_exists('entities_to_ascii'))
}
elseif ($digits < 2048)
{
- $out .= chr(192 + (($digits - ($digits % 64)) / 64));
- $out .= chr(128 + ($digits % 64));
+ $out .= chr(192 + (($digits - ($digits % 64)) / 64)).chr(128 + ($digits % 64));
}
else
{
- $out .= chr(224 + (($digits - ($digits % 4096)) / 4096));
- $out .= chr(128 + ((($digits % 4096) - ($digits % 64)) / 64));
- $out .= chr(128 + ($digits % 64));
+ $out .= chr(224 + (($digits - ($digits % 4096)) / 4096))
+ .chr(128 + ((($digits % 4096) - ($digits % 64)) / 64))
+ .chr(128 + ($digits % 64));
}
- $str = str_replace($matches['0'][$i], $out, $str);
+ $str = str_replace($matches[0][$i], $out, $str);
}
}
if ($all)
{
- $str = str_replace(array("&amp;", "&lt;", "&gt;", "&quot;", "&apos;", "&#45;"),
- array("&","<",">","\"", "'", "-"),
- $str);
+ return str_replace(
+ array('&amp;', '&lt;', '&gt;', '&quot;', '&apos;', '&#45;'),
+ array('&', '<', '>', '"', "'", '-'),
+ $str
+ );
}
return $str;
@@ -221,21 +248,20 @@ if ( ! function_exists('entities_to_ascii'))
// ------------------------------------------------------------------------
-/**
- * Word Censoring Function
- *
- * Supply a string and an array of disallowed words and any
- * matched words will be converted to #### or to the replacement
- * word you've submitted.
- *
- * @access public
- * @param string the text string
- * @param string the array of censoered words
- * @param string the optional replacement value
- * @return string
- */
if ( ! function_exists('word_censor'))
{
+ /**
+ * Word Censoring Function
+ *
+ * Supply a string and an array of disallowed words and any
+ * matched words will be converted to #### or to the replacement
+ * word you've submitted.
+ *
+ * @param string the text string
+ * @param string the array of censored words
+ * @param string the optional replacement value
+ * @return string
+ */
function word_censor($str, $censored, $replacement = '')
{
if ( ! is_array($censored))
@@ -253,13 +279,28 @@ if ( ! function_exists('word_censor'))
foreach ($censored as $badword)
{
- if ($replacement != '')
+ $badword = str_replace('\*', '\w*?', preg_quote($badword, '/'));
+ if ($replacement !== '')
{
- $str = preg_replace("/({$delim})(".str_replace('\*', '\w*?', preg_quote($badword, '/')).")({$delim})/i", "\\1{$replacement}\\3", $str);
+ $str = preg_replace(
+ "/({$delim})(".$badword.")({$delim})/i",
+ "\\1{$replacement}\\3",
+ $str
+ );
}
- else
+ elseif (preg_match_all("/{$delim}(".$badword."){$delim}/i", $str, $matches, PREG_PATTERN_ORDER | PREG_OFFSET_CAPTURE))
{
- $str = preg_replace("/({$delim})(".str_replace('\*', '\w*?', preg_quote($badword, '/')).")({$delim})/ie", "'\\1'.str_repeat('#', strlen('\\2')).'\\3'", $str);
+ $matches = $matches[1];
+ for ($i = count($matches) - 1; $i >= 0; $i--)
+ {
+ $length = strlen($matches[$i][0]);
+ $str = substr_replace(
+ $str,
+ str_repeat('#', $length),
+ $matches[$i][1],
+ $length
+ );
+ }
}
}
@@ -269,145 +310,146 @@ if ( ! function_exists('word_censor'))
// ------------------------------------------------------------------------
-/**
- * Code Highlighter
- *
- * Colorizes code strings
- *
- * @access public
- * @param string the text string
- * @return string
- */
if ( ! function_exists('highlight_code'))
{
+ /**
+ * Code Highlighter
+ *
+ * Colorizes code strings
+ *
+ * @param string the text string
+ * @return string
+ */
function highlight_code($str)
{
- // The highlight string function encodes and highlights
- // brackets so we need them to start raw
- $str = str_replace(array('&lt;', '&gt;'), array('<', '>'), $str);
-
- // Replace any existing PHP tags to temporary markers so they don't accidentally
- // break the string out of PHP, and thus, thwart the highlighting.
-
- $str = str_replace(array('<?', '?>', '<%', '%>', '\\', '</script>'),
- array('phptagopen', 'phptagclose', 'asptagopen', 'asptagclose', 'backslashtmp', 'scriptclose'), $str);
+ /* The highlight string function encodes and highlights
+ * brackets so we need them to start raw.
+ *
+ * Also replace any existing PHP tags to temporary markers
+ * so they don't accidentally break the string out of PHP,
+ * and thus, thwart the highlighting.
+ */
+ $str = str_replace(
+ array('&lt;', '&gt;', '<?', '?>', '<%', '%>', '\\', '</script>'),
+ array('<', '>', 'phptagopen', 'phptagclose', 'asptagopen', 'asptagclose', 'backslashtmp', 'scriptclose'),
+ $str
+ );
// The highlight_string function requires that the text be surrounded
// by PHP tags, which we will remove later
- $str = '<?php '.$str.' ?>'; // <?
-
- // All the magic happens here, baby!
- $str = highlight_string($str, TRUE);
-
- // Prior to PHP 5, the highligh function used icky <font> tags
- // so we'll replace them with <span> tags.
-
- if (abs(PHP_VERSION) < 5)
- {
- $str = str_replace(array('<font ', '</font>'), array('<span ', '</span>'), $str);
- $str = preg_replace('#color="(.*?)"#', 'style="color: \\1"', $str);
- }
+ $str = highlight_string('<?php '.$str.' ?>', TRUE);
// Remove our artificially added PHP, and the syntax highlighting that came with it
- $str = preg_replace('/<span style="color: #([A-Z0-9]+)">&lt;\?php(&nbsp;| )/i', '<span style="color: #$1">', $str);
- $str = preg_replace('/(<span style="color: #[A-Z0-9]+">.*?)\?&gt;<\/span>\n<\/span>\n<\/code>/is', "$1</span>\n</span>\n</code>", $str);
- $str = preg_replace('/<span style="color: #[A-Z0-9]+"\><\/span>/i', '', $str);
+ $str = preg_replace(
+ array(
+ '/<span style="color: #([A-Z0-9]+)">&lt;\?php(&nbsp;| )/i',
+ '/(<span style="color: #[A-Z0-9]+">.*?)\?&gt;<\/span>\n<\/span>\n<\/code>/is',
+ '/<span style="color: #[A-Z0-9]+"\><\/span>/i'
+ ),
+ array(
+ '<span style="color: #$1">',
+ "$1</span>\n</span>\n</code>",
+ ''
+ ),
+ $str
+ );
// Replace our markers back to PHP tags.
- $str = str_replace(array('phptagopen', 'phptagclose', 'asptagopen', 'asptagclose', 'backslashtmp', 'scriptclose'),
- array('&lt;?', '?&gt;', '&lt;%', '%&gt;', '\\', '&lt;/script&gt;'), $str);
-
- return $str;
+ return str_replace(
+ array('phptagopen', 'phptagclose', 'asptagopen', 'asptagclose', 'backslashtmp', 'scriptclose'),
+ array('&lt;?', '?&gt;', '&lt;%', '%&gt;', '\\', '&lt;/script&gt;'),
+ $str
+ );
}
}
// ------------------------------------------------------------------------
-/**
- * Phrase Highlighter
- *
- * Highlights a phrase within a text string
- *
- * @access public
- * @param string the text string
- * @param string the phrase you'd like to highlight
- * @param string the openging tag to precede the phrase with
- * @param string the closing tag to end the phrase with
- * @return string
- */
if ( ! function_exists('highlight_phrase'))
{
- function highlight_phrase($str, $phrase, $tag_open = '<strong>', $tag_close = '</strong>')
+ /**
+ * Phrase Highlighter
+ *
+ * Highlights a phrase within a text string
+ *
+ * @param string $str the text string
+ * @param string $phrase the phrase you'd like to highlight
+ * @param string $tag_open the openging tag to precede the phrase with
+ * @param string $tag_close the closing tag to end the phrase with
+ * @return string
+ */
+ function highlight_phrase($str, $phrase, $tag_open = '<mark>', $tag_close = '</mark>')
{
- if ($str == '')
- {
- return '';
- }
-
- if ($phrase != '')
- {
- return preg_replace('/('.preg_quote($phrase, '/').')/i', $tag_open."\\1".$tag_close, $str);
- }
-
- return $str;
+ return ($str !== '' && $phrase !== '')
+ ? preg_replace('/('.preg_quote($phrase, '/').')/i'.(UTF8_ENABLED ? 'u' : ''), $tag_open.'\\1'.$tag_close, $str)
+ : $str;
}
}
// ------------------------------------------------------------------------
-/**
- * Convert Accented Foreign Characters to ASCII
- *
- * @access public
- * @param string the text string
- * @return string
- */
if ( ! function_exists('convert_accented_characters'))
{
+ /**
+ * Convert Accented Foreign Characters to ASCII
+ *
+ * @param string $str Input string
+ * @return string
+ */
function convert_accented_characters($str)
{
- if (defined('ENVIRONMENT') AND is_file(APPPATH.'config/'.ENVIRONMENT.'/foreign_chars.php'))
- {
- include(APPPATH.'config/'.ENVIRONMENT.'/foreign_chars.php');
- }
- elseif (is_file(APPPATH.'config/foreign_chars.php'))
- {
- include(APPPATH.'config/foreign_chars.php');
- }
+ static $array_from, $array_to;
- if ( ! isset($foreign_characters))
+ if ( ! is_array($array_from))
{
- return $str;
+ if (file_exists(APPPATH.'config/foreign_chars.php'))
+ {
+ include(APPPATH.'config/foreign_chars.php');
+ }
+
+ if (file_exists(APPPATH.'config/'.ENVIRONMENT.'/foreign_chars.php'))
+ {
+ include(APPPATH.'config/'.ENVIRONMENT.'/foreign_chars.php');
+ }
+
+ if (empty($foreign_characters) OR ! is_array($foreign_characters))
+ {
+ $array_from = array();
+ $array_to = array();
+
+ return $str;
+ }
+
+ $array_from = array_keys($foreign_characters);
+ $array_to = array_values($foreign_characters);
}
- return preg_replace(array_keys($foreign_characters), array_values($foreign_characters), $str);
+ return preg_replace($array_from, $array_to, $str);
}
}
// ------------------------------------------------------------------------
-/**
- * Word Wrap
- *
- * Wraps text at the specified character. Maintains the integrity of words.
- * Anything placed between {unwrap}{/unwrap} will not be word wrapped, nor
- * will URLs.
- *
- * @access public
- * @param string the text string
- * @param integer the number of characters to wrap at
- * @return string
- */
if ( ! function_exists('word_wrap'))
{
- function word_wrap($str, $charlim = '76')
+ /**
+ * Word Wrap
+ *
+ * Wraps text at the specified character. Maintains the integrity of words.
+ * Anything placed between {unwrap}{/unwrap} will not be word wrapped, nor
+ * will URLs.
+ *
+ * @param string $str the text string
+ * @param int $charlim = 76 the number of characters to wrap at
+ * @return string
+ */
+ function word_wrap($str, $charlim = 76)
{
- // Se the character limit
- if ( ! is_numeric($charlim))
- $charlim = 76;
+ // Set the character limit
+ is_numeric($charlim) OR $charlim = 76;
// Reduce multiple spaces
- $str = preg_replace("| +|", " ", $str);
+ $str = preg_replace('| +|', ' ', $str);
// Standardize newlines
if (strpos($str, "\r") !== FALSE)
@@ -418,58 +460,56 @@ if ( ! function_exists('word_wrap'))
// If the current word is surrounded by {unwrap} tags we'll
// strip the entire chunk and replace it with a marker.
$unwrap = array();
- if (preg_match_all("|(\{unwrap\}.+?\{/unwrap\})|s", $str, $matches))
+ if (preg_match_all('|\{unwrap\}(.+?)\{/unwrap\}|s', $str, $matches))
{
- for ($i = 0; $i < count($matches['0']); $i++)
+ for ($i = 0, $c = count($matches[0]); $i < $c; $i++)
{
- $unwrap[] = $matches['1'][$i];
- $str = str_replace($matches['1'][$i], "{{unwrapped".$i."}}", $str);
+ $unwrap[] = $matches[1][$i];
+ $str = str_replace($matches[0][$i], '{{unwrapped'.$i.'}}', $str);
}
}
// Use PHP's native function to do the initial wordwrap.
// We set the cut flag to FALSE so that any individual words that are
- // too long get left alone. In the next step we'll deal with them.
+ // too long get left alone. In the next step we'll deal with them.
$str = wordwrap($str, $charlim, "\n", FALSE);
// Split the string into individual lines of text and cycle through them
- $output = "";
+ $output = '';
foreach (explode("\n", $str) as $line)
{
// Is the line within the allowed character count?
// If so we'll join it to the output and continue
- if (strlen($line) <= $charlim)
+ if (mb_strlen($line) <= $charlim)
{
$output .= $line."\n";
continue;
}
$temp = '';
- while ((strlen($line)) > $charlim)
+ while (mb_strlen($line) > $charlim)
{
// If the over-length word is a URL we won't wrap it
- if (preg_match("!\[url.+\]|://|wwww.!", $line))
+ if (preg_match('!\[url.+\]|://|www\.!', $line))
{
break;
}
// Trim the word down
- $temp .= substr($line, 0, $charlim-1);
- $line = substr($line, $charlim-1);
+ $temp .= mb_substr($line, 0, $charlim - 1);
+ $line = mb_substr($line, $charlim - 1);
}
// If $temp contains data it means we had to split up an over-length
// word into smaller chunks so we'll add it back to our current line
- if ($temp != '')
+ if ($temp !== '')
{
- $output .= $temp."\n".$line;
+ $output .= $temp."\n".$line."\n";
}
else
{
- $output .= $line;
+ $output .= $line."\n";
}
-
- $output .= "\n";
}
// Put our markers back
@@ -477,59 +517,52 @@ if ( ! function_exists('word_wrap'))
{
foreach ($unwrap as $key => $val)
{
- $output = str_replace("{{unwrapped".$key."}}", $val, $output);
+ $output = str_replace('{{unwrapped'.$key.'}}', $val, $output);
}
}
- // Remove the unwrap tags
- $output = str_replace(array('{unwrap}', '{/unwrap}'), '', $output);
-
return $output;
}
}
// ------------------------------------------------------------------------
-/**
- * Ellipsize String
- *
- * This function will strip tags from a string, split it at its max_length and ellipsize
- *
- * @param string string to ellipsize
- * @param integer max length of string
- * @param mixed int (1|0) or float, .5, .2, etc for position to split
- * @param string ellipsis ; Default '...'
- * @return string ellipsized string
- */
if ( ! function_exists('ellipsize'))
{
+ /**
+ * Ellipsize String
+ *
+ * This function will strip tags from a string, split it at its max_length and ellipsize
+ *
+ * @param string string to ellipsize
+ * @param int max length of string
+ * @param mixed int (1|0) or float, .5, .2, etc for position to split
+ * @param string ellipsis ; Default '...'
+ * @return string ellipsized string
+ */
function ellipsize($str, $max_length, $position = 1, $ellipsis = '&hellip;')
{
// Strip tags
$str = trim(strip_tags($str));
// Is the string long enough to ellipsize?
- if (strlen($str) <= $max_length)
+ if (mb_strlen($str) <= $max_length)
{
return $str;
}
- $beg = substr($str, 0, floor($max_length * $position));
-
+ $beg = mb_substr($str, 0, floor($max_length * $position));
$position = ($position > 1) ? 1 : $position;
if ($position === 1)
{
- $end = substr($str, 0, -($max_length - strlen($beg)));
+ $end = mb_substr($str, 0, -($max_length - mb_strlen($beg)));
}
else
{
- $end = substr($str, -($max_length - strlen($beg)));
+ $end = mb_substr($str, -($max_length - mb_strlen($beg)));
}
return $beg.$ellipsis.$end;
}
}
-
-/* End of file text_helper.php */
-/* Location: ./system/helpers/text_helper.php */ \ No newline at end of file
diff --git a/system/helpers/typography_helper.php b/system/helpers/typography_helper.php
index 21364fb8e..d51de084a 100644
--- a/system/helpers/typography_helper.php
+++ b/system/helpers/typography_helper.php
@@ -1,19 +1,42 @@
-<?php if ( ! defined('BASEPATH')) exit('No direct script access allowed');
+<?php
/**
* CodeIgniter
*
- * An open source application development framework for PHP 5.1.6 or newer
+ * An open source application development framework for PHP
*
- * @package CodeIgniter
- * @author ExpressionEngine Dev Team
- * @copyright Copyright (c) 2008 - 2014, EllisLab, Inc.
- * @license http://codeigniter.com/user_guide/license.html
- * @link http://codeigniter.com
- * @since Version 1.0
+ * This content is released under the MIT License (MIT)
+ *
+ * Copyright (c) 2019 - 2022, CodeIgniter Foundation
+ *
+ * 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.
+ *
+ * @package CodeIgniter
+ * @author EllisLab Dev Team
+ * @copyright Copyright (c) 2008 - 2014, EllisLab, Inc. (https://ellislab.com/)
+ * @copyright Copyright (c) 2014 - 2019, British Columbia Institute of Technology (https://bcit.ca/)
+ * @copyright Copyright (c) 2019 - 2022, CodeIgniter Foundation (https://codeigniter.com/)
+ * @license https://opensource.org/licenses/MIT MIT License
+ * @link https://codeigniter.com
+ * @since Version 1.0.0
* @filesource
*/
-
-// ------------------------------------------------------------------------
+defined('BASEPATH') OR exit('No direct script access allowed');
/**
* CodeIgniter Typography Helpers
@@ -21,73 +44,62 @@
* @package CodeIgniter
* @subpackage Helpers
* @category Helpers
- * @author ExpressionEngine Dev Team
- * @link http://codeigniter.com/user_guide/helpers/typography_helper.html
+ * @author EllisLab Dev Team
+ * @link https://codeigniter.com/userguide3/helpers/typography_helper.html
*/
// ------------------------------------------------------------------------
-/**
- * Convert newlines to HTML line breaks except within PRE tags
- *
- * @access public
- * @param string
- * @return string
- */
if ( ! function_exists('nl2br_except_pre'))
{
+ /**
+ * Convert newlines to HTML line breaks except within PRE tags
+ *
+ * @param string
+ * @return string
+ */
function nl2br_except_pre($str)
{
$CI =& get_instance();
-
$CI->load->library('typography');
-
return $CI->typography->nl2br_except_pre($str);
}
}
// ------------------------------------------------------------------------
-/**
- * Auto Typography Wrapper Function
- *
- *
- * @access public
- * @param string
- * @param bool whether to allow javascript event handlers
- * @param bool whether to reduce multiple instances of double newlines to two
- * @return string
- */
if ( ! function_exists('auto_typography'))
{
- function auto_typography($str, $strip_js_event_handlers = TRUE, $reduce_linebreaks = FALSE)
+ /**
+ * Auto Typography Wrapper Function
+ *
+ * @param string $str
+ * @param bool $reduce_linebreaks = FALSE whether to reduce multiple instances of double newlines to two
+ * @return string
+ */
+ function auto_typography($str, $reduce_linebreaks = FALSE)
{
$CI =& get_instance();
$CI->load->library('typography');
- return $CI->typography->auto_typography($str, $strip_js_event_handlers, $reduce_linebreaks);
+ return $CI->typography->auto_typography($str, $reduce_linebreaks);
}
}
-
// --------------------------------------------------------------------
-/**
- * HTML Entities Decode
- *
- * This function is a replacement for html_entity_decode()
- *
- * @access public
- * @param string
- * @return string
- */
if ( ! function_exists('entity_decode'))
{
- function entity_decode($str, $charset='UTF-8')
+ /**
+ * HTML Entities Decode
+ *
+ * This function is a replacement for html_entity_decode()
+ *
+ * @param string
+ * @param string
+ * @return string
+ */
+ function entity_decode($str, $charset = NULL)
{
- global $SEC;
- return $SEC->entity_decode($str, $charset);
+ return get_instance()->security->entity_decode($str, $charset);
}
}
-
-/* End of file typography_helper.php */
-/* Location: ./system/helpers/typography_helper.php */ \ No newline at end of file
diff --git a/system/helpers/url_helper.php b/system/helpers/url_helper.php
index 0e410d81c..e3c9bc0a4 100644
--- a/system/helpers/url_helper.php
+++ b/system/helpers/url_helper.php
@@ -1,19 +1,42 @@
-<?php if ( ! defined('BASEPATH')) exit('No direct script access allowed');
+<?php
/**
* CodeIgniter
*
- * An open source application development framework for PHP 5.1.6 or newer
+ * An open source application development framework for PHP
*
- * @package CodeIgniter
- * @author ExpressionEngine Dev Team
- * @copyright Copyright (c) 2008 - 2014, EllisLab, Inc.
- * @license http://codeigniter.com/user_guide/license.html
- * @link http://codeigniter.com
- * @since Version 1.0
+ * This content is released under the MIT License (MIT)
+ *
+ * Copyright (c) 2019 - 2022, CodeIgniter Foundation
+ *
+ * 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.
+ *
+ * @package CodeIgniter
+ * @author EllisLab Dev Team
+ * @copyright Copyright (c) 2008 - 2014, EllisLab, Inc. (https://ellislab.com/)
+ * @copyright Copyright (c) 2014 - 2019, British Columbia Institute of Technology (https://bcit.ca/)
+ * @copyright Copyright (c) 2019 - 2022, CodeIgniter Foundation (https://codeigniter.com/)
+ * @license https://opensource.org/licenses/MIT MIT License
+ * @link https://codeigniter.com
+ * @since Version 1.0.0
* @filesource
*/
-
-// ------------------------------------------------------------------------
+defined('BASEPATH') OR exit('No direct script access allowed');
/**
* CodeIgniter URL Helpers
@@ -21,66 +44,63 @@
* @package CodeIgniter
* @subpackage Helpers
* @category Helpers
- * @author ExpressionEngine Dev Team
- * @link http://codeigniter.com/user_guide/helpers/url_helper.html
+ * @author EllisLab Dev Team
+ * @link https://codeigniter.com/userguide3/helpers/url_helper.html
*/
// ------------------------------------------------------------------------
-/**
- * Site URL
- *
- * Create a local URL based on your basepath. Segments can be passed via the
- * first parameter either as a string or an array.
- *
- * @access public
- * @param string
- * @return string
- */
if ( ! function_exists('site_url'))
{
- function site_url($uri = '')
+ /**
+ * Site URL
+ *
+ * Create a local URL based on your basepath. Segments can be passed via the
+ * first parameter either as a string or an array.
+ *
+ * @param string $uri
+ * @param string $protocol
+ * @return string
+ */
+ function site_url($uri = '', $protocol = NULL)
{
- $CI =& get_instance();
- return $CI->config->site_url($uri);
+ return get_instance()->config->site_url($uri, $protocol);
}
}
// ------------------------------------------------------------------------
-/**
- * Base URL
- *
- * Create a local URL based on your basepath.
- * Segments can be passed in as a string or an array, same as site_url
- * or a URL to a file can be passed in, e.g. to an image file.
- *
- * @access public
- * @param string
- * @return string
- */
if ( ! function_exists('base_url'))
{
- function base_url($uri = '')
+ /**
+ * Base URL
+ *
+ * Create a local URL based on your basepath.
+ * Segments can be passed in as a string or an array, same as site_url
+ * or a URL to a file can be passed in, e.g. to an image file.
+ *
+ * @param string $uri
+ * @param string $protocol
+ * @return string
+ */
+ function base_url($uri = '', $protocol = NULL)
{
- $CI =& get_instance();
- return $CI->config->base_url($uri);
+ return get_instance()->config->base_url($uri, $protocol);
}
}
// ------------------------------------------------------------------------
-/**
- * Current URL
- *
- * Returns the full URL (including segments) of the page where this
- * function is placed
- *
- * @access public
- * @return string
- */
if ( ! function_exists('current_url'))
{
+ /**
+ * Current URL
+ *
+ * Returns the full URL (including segments) of the page where this
+ * function is placed
+ *
+ * @return string
+ */
function current_url()
{
$CI =& get_instance();
@@ -89,78 +109,69 @@ if ( ! function_exists('current_url'))
}
// ------------------------------------------------------------------------
-/**
- * URL String
- *
- * Returns the URI segments.
- *
- * @access public
- * @return string
- */
+
if ( ! function_exists('uri_string'))
{
+ /**
+ * URL String
+ *
+ * Returns the URI segments.
+ *
+ * @return string
+ */
function uri_string()
{
- $CI =& get_instance();
- return $CI->uri->uri_string();
+ return get_instance()->uri->uri_string();
}
}
// ------------------------------------------------------------------------
-/**
- * Index page
- *
- * Returns the "index_page" from your config file
- *
- * @access public
- * @return string
- */
if ( ! function_exists('index_page'))
{
+ /**
+ * Index page
+ *
+ * Returns the "index_page" from your config file
+ *
+ * @return string
+ */
function index_page()
{
- $CI =& get_instance();
- return $CI->config->item('index_page');
+ return get_instance()->config->item('index_page');
}
}
// ------------------------------------------------------------------------
-/**
- * Anchor Link
- *
- * Creates an anchor based on the local URL.
- *
- * @access public
- * @param string the URL
- * @param string the link title
- * @param mixed any attributes
- * @return string
- */
if ( ! function_exists('anchor'))
{
+ /**
+ * Anchor Link
+ *
+ * Creates an anchor based on the local URL.
+ *
+ * @param string the URL
+ * @param string the link title
+ * @param mixed any attributes
+ * @return string
+ */
function anchor($uri = '', $title = '', $attributes = '')
{
$title = (string) $title;
- if ( ! is_array($uri))
- {
- $site_url = ( ! preg_match('!^\w+://! i', $uri)) ? site_url($uri) : $uri;
- }
- else
- {
- $site_url = site_url($uri);
- }
+ $site_url = is_array($uri)
+ ? site_url($uri)
+ : (preg_match('#^(\w+:)?//#i', $uri) ? $uri : site_url($uri));
- if ($title == '')
+ if ($title === '')
{
$title = $site_url;
}
- if ($attributes != '')
+ if ($attributes !== '')
{
- $attributes = _parse_attributes($attributes);
+ $attributes = _stringify_attributes($attributes);
}
return '<a href="'.$site_url.'"'.$attributes.'>'.$title.'</a>';
@@ -169,139 +180,141 @@ if ( ! function_exists('anchor'))
// ------------------------------------------------------------------------
-/**
- * Anchor Link - Pop-up version
- *
- * Creates an anchor based on the local URL. The link
- * opens a new window based on the attributes specified.
- *
- * @access public
- * @param string the URL
- * @param string the link title
- * @param mixed any attributes
- * @return string
- */
if ( ! function_exists('anchor_popup'))
{
+ /**
+ * Anchor Link - Pop-up version
+ *
+ * Creates an anchor based on the local URL. The link
+ * opens a new window based on the attributes specified.
+ *
+ * @param string the URL
+ * @param string the link title
+ * @param mixed any attributes
+ * @return string
+ */
function anchor_popup($uri = '', $title = '', $attributes = FALSE)
{
$title = (string) $title;
+ $site_url = preg_match('#^(\w+:)?//#i', $uri) ? $uri : site_url($uri);
- $site_url = ( ! preg_match('!^\w+://! i', $uri)) ? site_url($uri) : $uri;
-
- if ($title == '')
+ if ($title === '')
{
$title = $site_url;
}
if ($attributes === FALSE)
{
- return "<a href='javascript:void(0);' onclick=\"window.open('".$site_url."', '_blank');\">".$title."</a>";
+ return '<a href="'.$site_url.'" onclick="window.open(\''.$site_url."', '_blank'); return false;\">".$title.'</a>';
}
if ( ! is_array($attributes))
{
- $attributes = array();
- }
+ $attributes = array($attributes);
- foreach (array('width' => '800', 'height' => '600', 'scrollbars' => 'yes', 'status' => 'yes', 'resizable' => 'yes', 'screenx' => '0', 'screeny' => '0', ) as $key => $val)
+ // Ref: https://www.w3schools.com/jsref/met_win_open.asp
+ $window_name = '_blank';
+ }
+ elseif ( ! empty($attributes['window_name']))
{
- $atts[$key] = ( ! isset($attributes[$key])) ? $val : $attributes[$key];
- unset($attributes[$key]);
+ $window_name = $attributes['window_name'];
+ unset($attributes['window_name']);
+ }
+ else
+ {
+ $window_name = '_blank';
}
- if ($attributes != '')
+ foreach (array('width' => '800', 'height' => '600', 'scrollbars' => 'yes', 'menubar' => 'no', 'status' => 'yes', 'resizable' => 'yes', 'screenx' => '0', 'screeny' => '0') as $key => $val)
{
- $attributes = _parse_attributes($attributes);
+ $atts[$key] = isset($attributes[$key]) ? $attributes[$key] : $val;
+ unset($attributes[$key]);
}
- return "<a href='javascript:void(0);' onclick=\"window.open('".$site_url."', '_blank', '"._parse_attributes($atts, TRUE)."');\"$attributes>".$title."</a>";
+ $attributes = _stringify_attributes($attributes);
+
+ return '<a href="'.$site_url
+ .'" onclick="window.open(\''.$site_url."', '".$window_name."', '"._stringify_attributes($atts, TRUE)."'); return false;\""
+ .$attributes.'>'.$title.'</a>';
}
}
// ------------------------------------------------------------------------
-/**
- * Mailto Link
- *
- * @access public
- * @param string the email address
- * @param string the link title
- * @param mixed any attributes
- * @return string
- */
if ( ! function_exists('mailto'))
{
+ /**
+ * Mailto Link
+ *
+ * @param string the email address
+ * @param string the link title
+ * @param mixed any attributes
+ * @return string
+ */
function mailto($email, $title = '', $attributes = '')
{
$title = (string) $title;
- if ($title == "")
+ if ($title === '')
{
$title = $email;
}
- $attributes = _parse_attributes($attributes);
-
- return '<a href="mailto:'.$email.'"'.$attributes.'>'.$title.'</a>';
+ return '<a href="mailto:'.$email.'"'._stringify_attributes($attributes).'>'.$title.'</a>';
}
}
// ------------------------------------------------------------------------
-/**
- * Encoded Mailto Link
- *
- * Create a spam-protected mailto link written in Javascript
- *
- * @access public
- * @param string the email address
- * @param string the link title
- * @param mixed any attributes
- * @return string
- */
if ( ! function_exists('safe_mailto'))
{
+ /**
+ * Encoded Mailto Link
+ *
+ * Create a spam-protected mailto link written in Javascript
+ *
+ * @param string the email address
+ * @param string the link title
+ * @param mixed any attributes
+ * @return string
+ */
function safe_mailto($email, $title = '', $attributes = '')
{
$title = (string) $title;
- if ($title == "")
+ if ($title === '')
{
$title = $email;
}
- for ($i = 0; $i < 16; $i++)
- {
- $x[] = substr('<a href="mailto:', $i, 1);
- }
+ $x = str_split('<a href="mailto:', 1);
- for ($i = 0; $i < strlen($email); $i++)
+ for ($i = 0, $l = strlen($email); $i < $l; $i++)
{
- $x[] = "|".ord(substr($email, $i, 1));
+ $x[] = '|'.ord($email[$i]);
}
$x[] = '"';
- if ($attributes != '')
+ if ($attributes !== '')
{
if (is_array($attributes))
{
foreach ($attributes as $key => $val)
{
- $x[] = ' '.$key.'="';
- for ($i = 0; $i < strlen($val); $i++)
+ $x[] = ' '.$key.'="';
+ for ($i = 0, $l = strlen($val); $i < $l; $i++)
{
- $x[] = "|".ord(substr($val, $i, 1));
+ $x[] = '|'.ord($val[$i]);
}
$x[] = '"';
}
}
else
{
- for ($i = 0; $i < strlen($attributes); $i++)
+ for ($i = 0, $l = strlen($attributes); $i < $l; $i++)
{
- $x[] = substr($attributes, $i, 1);
+ $x[] = $attributes[$i];
}
}
}
@@ -309,26 +322,28 @@ if ( ! function_exists('safe_mailto'))
$x[] = '>';
$temp = array();
- for ($i = 0; $i < strlen($title); $i++)
+ for ($i = 0, $l = strlen($title); $i < $l; $i++)
{
$ordinal = ord($title[$i]);
if ($ordinal < 128)
{
- $x[] = "|".$ordinal;
+ $x[] = '|'.$ordinal;
}
else
{
- if (count($temp) == 0)
+ if (count($temp) === 0)
{
$count = ($ordinal < 224) ? 2 : 3;
}
$temp[] = $ordinal;
- if (count($temp) == $count)
+ if (count($temp) === $count)
{
- $number = ($count == 3) ? (($temp['0'] % 16) * 4096) + (($temp['1'] % 64) * 64) + ($temp['2'] % 64) : (($temp['0'] % 32) * 64) + ($temp['1'] % 64);
- $x[] = "|".$number;
+ $number = ($count === 3)
+ ? (($temp[0] % 16) * 4096) + (($temp[1] % 64) * 64) + ($temp[2] % 64)
+ : (($temp[0] % 32) * 64) + ($temp[1] % 64);
+ $x[] = '|'.$number;
$count = 1;
$temp = array();
}
@@ -338,89 +353,75 @@ if ( ! function_exists('safe_mailto'))
$x[] = '<'; $x[] = '/'; $x[] = 'a'; $x[] = '>';
$x = array_reverse($x);
- ob_start();
-
- ?><script type="text/javascript">
- //<![CDATA[
- var l=new Array();
- <?php
- $i = 0;
- foreach ($x as $val){ ?>l[<?php echo $i++; ?>]='<?php echo $val; ?>';<?php } ?>
-
- for (var i = l.length-1; i >= 0; i=i-1){
- if (l[i].substring(0, 1) == '|') document.write("&#"+unescape(l[i].substring(1))+";");
- else document.write(unescape(l[i]));}
- //]]>
- </script><?php
-
- $buffer = ob_get_contents();
- ob_end_clean();
- return $buffer;
+
+ $output = "<script type=\"text/javascript\">\n"
+ ."\t//<![CDATA[\n"
+ ."\tvar l=new Array();\n";
+
+ for ($i = 0, $c = count($x); $i < $c; $i++)
+ {
+ $output .= "\tl[".$i."] = '".$x[$i]."';\n";
+ }
+
+ $output .= "\n\tfor (var i = l.length-1; i >= 0; i=i-1) {\n"
+ ."\t\tif (l[i].substring(0, 1) === '|') document.write(\"&#\"+unescape(l[i].substring(1))+\";\");\n"
+ ."\t\telse document.write(unescape(l[i]));\n"
+ ."\t}\n"
+ ."\t//]]>\n"
+ .'</script>';
+
+ return $output;
}
}
// ------------------------------------------------------------------------
-/**
- * Auto-linker
- *
- * Automatically links URL and Email addresses.
- * Note: There's a bit of extra code here to deal with
- * URLs or emails that end in a period. We'll strip these
- * off and add them after the link.
- *
- * @access public
- * @param string the string
- * @param string the type: email, url, or both
- * @param bool whether to create pop-up links
- * @return string
- */
if ( ! function_exists('auto_link'))
{
+ /**
+ * Auto-linker
+ *
+ * Automatically links URL and Email addresses.
+ * Note: There's a bit of extra code here to deal with
+ * URLs or emails that end in a period. We'll strip these
+ * off and add them after the link.
+ *
+ * @param string the string
+ * @param string the type: email, url, or both
+ * @param bool whether to create pop-up links
+ * @return string
+ */
function auto_link($str, $type = 'both', $popup = FALSE)
{
- if ($type != 'email')
+ // Find and replace any URLs.
+ if ($type !== 'email' && preg_match_all('#(\w*://|www\.)[a-z0-9]+(-+[a-z0-9]+)*(\.[a-z0-9]+(-+[a-z0-9]+)*)+(/([^\s()<>;]+\w)?/?)?#i', $str, $matches, PREG_OFFSET_CAPTURE | PREG_SET_ORDER))
{
- if (preg_match_all("#(^|\s|\()((http(s?)://)|(www\.))(\w+[^\s\)\<]+)#i", $str, $matches))
- {
- $pop = ($popup == TRUE) ? " target=\"_blank\" " : "";
+ // Set our target HTML if using popup links.
+ $target = ($popup) ? ' target="_blank" rel="noopener"' : '';
- for ($i = 0; $i < count($matches['0']); $i++)
- {
- $period = '';
- if (preg_match("|\.$|", $matches['6'][$i]))
- {
- $period = '.';
- $matches['6'][$i] = substr($matches['6'][$i], 0, -1);
- }
-
- $str = str_replace($matches['0'][$i],
- $matches['1'][$i].'<a href="http'.
- $matches['4'][$i].'://'.
- $matches['5'][$i].
- $matches['6'][$i].'"'.$pop.'>http'.
- $matches['4'][$i].'://'.
- $matches['5'][$i].
- $matches['6'][$i].'</a>'.
- $period, $str);
- }
+ // We process the links in reverse order (last -> first) so that
+ // the returned string offsets from preg_match_all() are not
+ // moved as we add more HTML.
+ foreach (array_reverse($matches) as $match)
+ {
+ // $match[0] is the matched string/link
+ // $match[1] is either a protocol prefix or 'www.'
+ //
+ // With PREG_OFFSET_CAPTURE, both of the above is an array,
+ // where the actual value is held in [0] and its offset at the [1] index.
+ $a = '<a href="'.(strpos($match[1][0], '/') ? '' : 'http://').$match[0][0].'"'.$target.'>'.$match[0][0].'</a>';
+ $str = substr_replace($str, $a, $match[0][1], strlen($match[0][0]));
}
}
- if ($type != 'url')
+ // Find and replace any emails.
+ if ($type !== 'url' && preg_match_all('#([\w\.\-\+]+@[a-z0-9\-]+\.[a-z0-9\-\.]+[^[:punct:]\s])#i', $str, $matches, PREG_OFFSET_CAPTURE))
{
- if (preg_match_all("/([a-zA-Z0-9_\.\-\+]+)@([a-zA-Z0-9\-]+)\.([a-zA-Z0-9\-\.]*)/i", $str, $matches))
+ foreach (array_reverse($matches[0]) as $match)
{
- for ($i = 0; $i < count($matches['0']); $i++)
+ if (filter_var($match[0], FILTER_VALIDATE_EMAIL) !== FALSE)
{
- $period = '';
- if (preg_match("|\.$|", $matches['3'][$i]))
- {
- $period = '.';
- $matches['3'][$i] = substr($matches['3'][$i], 0, -1);
- }
-
- $str = str_replace($matches['0'][$i], safe_mailto($matches['1'][$i].'@'.$matches['2'][$i].'.'.$matches['3'][$i]).$period, $str);
+ $str = substr_replace($str, safe_mailto($match[0]), $match[1], strlen($match[0]));
}
}
}
@@ -431,20 +432,19 @@ if ( ! function_exists('auto_link'))
// ------------------------------------------------------------------------
-/**
- * Prep URL
- *
- * Simply adds the http:// part if no scheme is included
- *
- * @access public
- * @param string the URL
- * @return string
- */
if ( ! function_exists('prep_url'))
{
+ /**
+ * Prep URL
+ *
+ * Simply adds the http:// part if no scheme is included
+ *
+ * @param string the URL
+ * @return string
+ */
function prep_url($str = '')
{
- if ($str == 'http://' OR $str == '')
+ if ($str === '')
{
return '';
}
@@ -453,7 +453,7 @@ if ( ! function_exists('prep_url'))
if ( ! $url OR ! isset($url['scheme']))
{
- $str = 'http://'.$str;
+ return 'http://'.$str;
}
return $str;
@@ -462,45 +462,35 @@ if ( ! function_exists('prep_url'))
// ------------------------------------------------------------------------
-/**
- * Create URL Title
- *
- * Takes a "title" string as input and creates a
- * human-friendly URL string with a "separator" string
- * as the word separator.
- *
- * @access public
- * @param string the string
- * @param string the separator
- * @return string
- */
if ( ! function_exists('url_title'))
{
+ /**
+ * Create URL Title
+ *
+ * Takes a "title" string as input and creates a
+ * human-friendly URL string with a "separator" string
+ * as the word separator.
+ *
+ * @param string $str Input string
+ * @param string $separator Word separator (usually '-' or '_')
+ * @param bool $lowercase Whether to transform the output string to lowercase
+ * @return string
+ */
function url_title($str, $separator = '-', $lowercase = FALSE)
{
- if ($separator == 'dash')
- {
- $separator = '-';
- }
- else if ($separator == 'underscore')
- {
- $separator = '_';
- }
-
- $q_separator = preg_quote($separator);
+ $q_separator = preg_quote($separator, '#');
$trans = array(
- '&.+?;' => '',
- '[^a-z0-9 _-]' => '',
- '\s+' => $separator,
- '('.$q_separator.')+' => $separator
+ '&.+?;' => '',
+ '[^\w\d _-]' => '',
+ '\s+' => $separator,
+ '('.$q_separator.')+' => $separator,
);
$str = strip_tags($str);
-
foreach ($trans as $key => $val)
{
- $str = preg_replace("#".$key."#i", $val, $str);
+ $str = preg_replace('#'.$key.'#i'.(UTF8_ENABLED ? 'u' : ''), $val, $str);
}
if ($lowercase === TRUE)
@@ -508,87 +498,62 @@ if ( ! function_exists('url_title'))
$str = strtolower($str);
}
- return trim($str, $separator);
+ return trim(trim($str, $separator));
}
}
// ------------------------------------------------------------------------
-/**
- * Header Redirect
- *
- * Header redirect in two flavors
- * For very fine grained control over headers, you could use the Output
- * Library's set_header() function.
- *
- * @access public
- * @param string the URL
- * @param string the method: location or redirect
- * @return string
- */
if ( ! function_exists('redirect'))
{
- function redirect($uri = '', $method = 'location', $http_response_code = 302)
+ /**
+ * Header Redirect
+ *
+ * Header redirect in two flavors
+ * For very fine grained control over headers, you could use the Output
+ * Library's set_header() function.
+ *
+ * @param string $uri URL
+ * @param string $method Redirect method
+ * 'auto', 'location' or 'refresh'
+ * @param int $code HTTP Response status code
+ * @return void
+ */
+ function redirect($uri = '', $method = 'auto', $code = NULL)
{
- if ( ! preg_match('#^https?://#i', $uri))
+ if ( ! preg_match('#^(\w+:)?//#i', $uri))
{
$uri = site_url($uri);
}
- switch($method)
+ // IIS environment likely? Use 'refresh' for better compatibility
+ if ($method === 'auto' && isset($_SERVER['SERVER_SOFTWARE']) && strpos($_SERVER['SERVER_SOFTWARE'], 'Microsoft-IIS') !== FALSE)
{
- case 'refresh' : header("Refresh:0;url=".$uri);
- break;
- default : header("Location: ".$uri, TRUE, $http_response_code);
- break;
+ $method = 'refresh';
}
- exit;
- }
-}
-
-// ------------------------------------------------------------------------
-
-/**
- * Parse out the attributes
- *
- * Some of the functions use this
- *
- * @access private
- * @param array
- * @param bool
- * @return string
- */
-if ( ! function_exists('_parse_attributes'))
-{
- function _parse_attributes($attributes, $javascript = FALSE)
- {
- if (is_string($attributes))
+ elseif ($method !== 'refresh' && (empty($code) OR ! is_numeric($code)))
{
- return ($attributes != '') ? ' '.$attributes : '';
- }
-
- $att = '';
- foreach ($attributes as $key => $val)
- {
- if ($javascript == TRUE)
+ if (isset($_SERVER['SERVER_PROTOCOL'], $_SERVER['REQUEST_METHOD']) && $_SERVER['SERVER_PROTOCOL'] === 'HTTP/1.1')
{
- $att .= $key . '=' . $val . ',';
+ $code = ($_SERVER['REQUEST_METHOD'] !== 'GET')
+ ? 303 // reference: https://en.wikipedia.org/wiki/Post/Redirect/Get
+ : 307;
}
else
{
- $att .= ' ' . $key . '="' . $val . '"';
+ $code = 302;
}
}
- if ($javascript == TRUE AND $att != '')
+ switch ($method)
{
- $att = substr($att, 0, -1);
+ case 'refresh':
+ header('Refresh:0;url='.$uri);
+ break;
+ default:
+ header('Location: '.$uri, TRUE, $code);
+ break;
}
-
- return $att;
+ exit;
}
}
-
-
-/* End of file url_helper.php */
-/* Location: ./system/helpers/url_helper.php */ \ No newline at end of file
diff --git a/system/helpers/xml_helper.php b/system/helpers/xml_helper.php
index 6c36e1cac..5e0861e9e 100644
--- a/system/helpers/xml_helper.php
+++ b/system/helpers/xml_helper.php
@@ -1,19 +1,42 @@
-<?php if ( ! defined('BASEPATH')) exit('No direct script access allowed');
+<?php
/**
* CodeIgniter
*
- * An open source application development framework for PHP 5.1.6 or newer
+ * An open source application development framework for PHP
*
- * @package CodeIgniter
- * @author ExpressionEngine Dev Team
- * @copyright Copyright (c) 2008 - 2014, EllisLab, Inc.
- * @license http://codeigniter.com/user_guide/license.html
- * @link http://codeigniter.com
- * @since Version 1.0
+ * This content is released under the MIT License (MIT)
+ *
+ * Copyright (c) 2019 - 2022, CodeIgniter Foundation
+ *
+ * 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.
+ *
+ * @package CodeIgniter
+ * @author EllisLab Dev Team
+ * @copyright Copyright (c) 2008 - 2014, EllisLab, Inc. (https://ellislab.com/)
+ * @copyright Copyright (c) 2014 - 2019, British Columbia Institute of Technology (https://bcit.ca/)
+ * @copyright Copyright (c) 2019 - 2022, CodeIgniter Foundation (https://codeigniter.com/)
+ * @license https://opensource.org/licenses/MIT MIT License
+ * @link https://codeigniter.com
+ * @since Version 1.0.0
* @filesource
*/
-
-// ------------------------------------------------------------------------
+defined('BASEPATH') OR exit('No direct script access allowed');
/**
* CodeIgniter XML Helpers
@@ -21,51 +44,48 @@
* @package CodeIgniter
* @subpackage Helpers
* @category Helpers
- * @author ExpressionEngine Dev Team
- * @link http://codeigniter.com/user_guide/helpers/xml_helper.html
+ * @author EllisLab Dev Team
+ * @link https://codeigniter.com/userguide3/helpers/xml_helper.html
*/
// ------------------------------------------------------------------------
-/**
- * Convert Reserved XML characters to Entities
- *
- * @access public
- * @param string
- * @return string
- */
if ( ! function_exists('xml_convert'))
{
+ /**
+ * Convert Reserved XML characters to Entities
+ *
+ * @param string
+ * @param bool
+ * @return string
+ */
function xml_convert($str, $protect_all = FALSE)
{
$temp = '__TEMP_AMPERSANDS__';
// Replace entities to temporary markers so that
// ampersands won't get messed up
- $str = preg_replace("/&#(\d+);/", "$temp\\1;", $str);
+ $str = preg_replace('/&#(\d+);/', $temp.'\\1;', $str);
if ($protect_all === TRUE)
{
- $str = preg_replace("/&(\w+);/", "$temp\\1;", $str);
+ $str = preg_replace('/&(\w+);/', $temp.'\\1;', $str);
}
- $str = str_replace(array("&","<",">","\"", "'", "-"),
- array("&amp;", "&lt;", "&gt;", "&quot;", "&apos;", "&#45;"),
- $str);
+ $str = str_replace(
+ array('&', '<', '>', '"', "'", '-'),
+ array('&amp;', '&lt;', '&gt;', '&quot;', '&apos;', '&#45;'),
+ $str
+ );
// Decode the temp markers back to entities
- $str = preg_replace("/$temp(\d+);/","&#\\1;",$str);
+ $str = preg_replace('/'.$temp.'(\d+);/', '&#\\1;', $str);
if ($protect_all === TRUE)
{
- $str = preg_replace("/$temp(\w+);/","&\\1;", $str);
+ return preg_replace('/'.$temp.'(\w+);/', '&\\1;', $str);
}
return $str;
}
}
-
-// ------------------------------------------------------------------------
-
-/* End of file xml_helper.php */
-/* Location: ./system/helpers/xml_helper.php */ \ No newline at end of file
diff --git a/system/index.html b/system/index.html
index c942a79ce..bcb7cae34 100644
--- a/system/index.html
+++ b/system/index.html
@@ -1,4 +1,5 @@
-<html>
+<!DOCTYPE html>
+<html lang="en">
<head>
<title>403 Forbidden</title>
</head>
@@ -7,4 +8,4 @@
<p>Directory access is forbidden.</p>
</body>
-</html> \ No newline at end of file
+</html>
diff --git a/system/language/english/calendar_lang.php b/system/language/english/calendar_lang.php
index 3e6312361..35352d600 100644
--- a/system/language/english/calendar_lang.php
+++ b/system/language/english/calendar_lang.php
@@ -1,51 +1,85 @@
<?php
+/**
+ * CodeIgniter
+ *
+ * An open source application development framework for PHP
+ *
+ * This content is released under the MIT License (MIT)
+ *
+ * Copyright (c) 2019 - 2022, CodeIgniter Foundation
+ *
+ * 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.
+ *
+ * @package CodeIgniter
+ * @author EllisLab Dev Team
+ * @copyright Copyright (c) 2008 - 2014, EllisLab, Inc. (https://ellislab.com/)
+ * @copyright Copyright (c) 2014 - 2019, British Columbia Institute of Technology (https://bcit.ca/)
+ * @copyright Copyright (c) 2019 - 2022, CodeIgniter Foundation (https://codeigniter.com/)
+ * @license https://opensource.org/licenses/MIT MIT License
+ * @link https://codeigniter.com
+ * @since Version 1.0.0
+ * @filesource
+ */
+defined('BASEPATH') OR exit('No direct script access allowed');
-$lang['cal_su'] = "Su";
-$lang['cal_mo'] = "Mo";
-$lang['cal_tu'] = "Tu";
-$lang['cal_we'] = "We";
-$lang['cal_th'] = "Th";
-$lang['cal_fr'] = "Fr";
-$lang['cal_sa'] = "Sa";
-$lang['cal_sun'] = "Sun";
-$lang['cal_mon'] = "Mon";
-$lang['cal_tue'] = "Tue";
-$lang['cal_wed'] = "Wed";
-$lang['cal_thu'] = "Thu";
-$lang['cal_fri'] = "Fri";
-$lang['cal_sat'] = "Sat";
-$lang['cal_sunday'] = "Sunday";
-$lang['cal_monday'] = "Monday";
-$lang['cal_tuesday'] = "Tuesday";
-$lang['cal_wednesday'] = "Wednesday";
-$lang['cal_thursday'] = "Thursday";
-$lang['cal_friday'] = "Friday";
-$lang['cal_saturday'] = "Saturday";
-$lang['cal_jan'] = "Jan";
-$lang['cal_feb'] = "Feb";
-$lang['cal_mar'] = "Mar";
-$lang['cal_apr'] = "Apr";
-$lang['cal_may'] = "May";
-$lang['cal_jun'] = "Jun";
-$lang['cal_jul'] = "Jul";
-$lang['cal_aug'] = "Aug";
-$lang['cal_sep'] = "Sep";
-$lang['cal_oct'] = "Oct";
-$lang['cal_nov'] = "Nov";
-$lang['cal_dec'] = "Dec";
-$lang['cal_january'] = "January";
-$lang['cal_february'] = "February";
-$lang['cal_march'] = "March";
-$lang['cal_april'] = "April";
-$lang['cal_mayl'] = "May";
-$lang['cal_june'] = "June";
-$lang['cal_july'] = "July";
-$lang['cal_august'] = "August";
-$lang['cal_september'] = "September";
-$lang['cal_october'] = "October";
-$lang['cal_november'] = "November";
-$lang['cal_december'] = "December";
-
-
-/* End of file calendar_lang.php */
-/* Location: ./system/language/english/calendar_lang.php */ \ No newline at end of file
+$lang['cal_su'] = 'Su';
+$lang['cal_mo'] = 'Mo';
+$lang['cal_tu'] = 'Tu';
+$lang['cal_we'] = 'We';
+$lang['cal_th'] = 'Th';
+$lang['cal_fr'] = 'Fr';
+$lang['cal_sa'] = 'Sa';
+$lang['cal_sun'] = 'Sun';
+$lang['cal_mon'] = 'Mon';
+$lang['cal_tue'] = 'Tue';
+$lang['cal_wed'] = 'Wed';
+$lang['cal_thu'] = 'Thu';
+$lang['cal_fri'] = 'Fri';
+$lang['cal_sat'] = 'Sat';
+$lang['cal_sunday'] = 'Sunday';
+$lang['cal_monday'] = 'Monday';
+$lang['cal_tuesday'] = 'Tuesday';
+$lang['cal_wednesday'] = 'Wednesday';
+$lang['cal_thursday'] = 'Thursday';
+$lang['cal_friday'] = 'Friday';
+$lang['cal_saturday'] = 'Saturday';
+$lang['cal_jan'] = 'Jan';
+$lang['cal_feb'] = 'Feb';
+$lang['cal_mar'] = 'Mar';
+$lang['cal_apr'] = 'Apr';
+$lang['cal_may'] = 'May';
+$lang['cal_jun'] = 'Jun';
+$lang['cal_jul'] = 'Jul';
+$lang['cal_aug'] = 'Aug';
+$lang['cal_sep'] = 'Sep';
+$lang['cal_oct'] = 'Oct';
+$lang['cal_nov'] = 'Nov';
+$lang['cal_dec'] = 'Dec';
+$lang['cal_january'] = 'January';
+$lang['cal_february'] = 'February';
+$lang['cal_march'] = 'March';
+$lang['cal_april'] = 'April';
+$lang['cal_mayl'] = 'May';
+$lang['cal_june'] = 'June';
+$lang['cal_july'] = 'July';
+$lang['cal_august'] = 'August';
+$lang['cal_september'] = 'September';
+$lang['cal_october'] = 'October';
+$lang['cal_november'] = 'November';
+$lang['cal_december'] = 'December';
diff --git a/system/language/english/date_lang.php b/system/language/english/date_lang.php
index c0ace16ef..fd184df85 100644
--- a/system/language/english/date_lang.php
+++ b/system/language/english/date_lang.php
@@ -1,22 +1,60 @@
<?php
+/**
+ * CodeIgniter
+ *
+ * An open source application development framework for PHP
+ *
+ * This content is released under the MIT License (MIT)
+ *
+ * Copyright (c) 2019 - 2022, CodeIgniter Foundation
+ *
+ * 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.
+ *
+ * @package CodeIgniter
+ * @author EllisLab Dev Team
+ * @copyright Copyright (c) 2008 - 2014, EllisLab, Inc. (https://ellislab.com/)
+ * @copyright Copyright (c) 2014 - 2019, British Columbia Institute of Technology (https://bcit.ca/)
+ * @copyright Copyright (c) 2019 - 2022, CodeIgniter Foundation (https://codeigniter.com/)
+ * @license https://opensource.org/licenses/MIT MIT License
+ * @link https://codeigniter.com
+ * @since Version 1.0.0
+ * @filesource
+ */
+defined('BASEPATH') OR exit('No direct script access allowed');
-$lang['date_year'] = "Year";
-$lang['date_years'] = "Years";
-$lang['date_month'] = "Month";
-$lang['date_months'] = "Months";
-$lang['date_week'] = "Week";
-$lang['date_weeks'] = "Weeks";
-$lang['date_day'] = "Day";
-$lang['date_days'] = "Days";
-$lang['date_hour'] = "Hour";
-$lang['date_hours'] = "Hours";
-$lang['date_minute'] = "Minute";
-$lang['date_minutes'] = "Minutes";
-$lang['date_second'] = "Second";
-$lang['date_seconds'] = "Seconds";
+$lang['date_year'] = 'Year';
+$lang['date_years'] = 'Years';
+$lang['date_month'] = 'Month';
+$lang['date_months'] = 'Months';
+$lang['date_week'] = 'Week';
+$lang['date_weeks'] = 'Weeks';
+$lang['date_day'] = 'Day';
+$lang['date_days'] = 'Days';
+$lang['date_hour'] = 'Hour';
+$lang['date_hours'] = 'Hours';
+$lang['date_minute'] = 'Minute';
+$lang['date_minutes'] = 'Minutes';
+$lang['date_second'] = 'Second';
+$lang['date_seconds'] = 'Seconds';
$lang['UM12'] = '(UTC -12:00) Baker/Howland Island';
-$lang['UM11'] = '(UTC -11:00) Samoa Time Zone, Niue';
+$lang['UM11'] = '(UTC -11:00) Niue';
$lang['UM10'] = '(UTC -10:00) Hawaii-Aleutian Standard Time, Cook Islands, Tahiti';
$lang['UM95'] = '(UTC -9:30) Marquesas Islands';
$lang['UM9'] = '(UTC -9:00) Alaska Standard Time, Gambier Islands';
@@ -33,7 +71,7 @@ $lang['UM1'] = '(UTC -1:00) Azores, Cape Verde Islands';
$lang['UTC'] = '(UTC) Greenwich Mean Time, Western European Time';
$lang['UP1'] = '(UTC +1:00) Central European Time, West Africa Time';
$lang['UP2'] = '(UTC +2:00) Central Africa Time, Eastern European Time, Kaliningrad Time';
-$lang['UP3'] = '(UTC +3:00) Moscow Time, East Africa Time';
+$lang['UP3'] = '(UTC +3:00) Moscow Time, East Africa Time, Arabia Standard Time';
$lang['UP35'] = '(UTC +3:30) Iran Standard Time';
$lang['UP4'] = '(UTC +4:00) Azerbaijan Standard Time, Samara Time';
$lang['UP45'] = '(UTC +4:30) Afghanistan';
@@ -49,13 +87,9 @@ $lang['UP9'] = '(UTC +9:00) Japan Standard Time, Korea Standard Time, Yakutsk Ti
$lang['UP95'] = '(UTC +9:30) Australian Central Standard Time';
$lang['UP10'] = '(UTC +10:00) Australian Eastern Standard Time, Vladivostok Time';
$lang['UP105'] = '(UTC +10:30) Lord Howe Island';
-$lang['UP11'] = '(UTC +11:00) Magadan Time, Solomon Islands, Vanuatu';
+$lang['UP11'] = '(UTC +11:00) Srednekolymsk Time, Solomon Islands, Vanuatu';
$lang['UP115'] = '(UTC +11:30) Norfolk Island';
$lang['UP12'] = '(UTC +12:00) Fiji, Gilbert Islands, Kamchatka Time, New Zealand Standard Time';
$lang['UP1275'] = '(UTC +12:45) Chatham Islands Standard Time';
-$lang['UP13'] = '(UTC +13:00) Phoenix Islands Time, Tonga';
+$lang['UP13'] = '(UTC +13:00) Samoa Time Zone, Phoenix Islands Time, Tonga';
$lang['UP14'] = '(UTC +14:00) Line Islands';
-
-
-/* End of file date_lang.php */
-/* Location: ./system/language/english/date_lang.php */ \ No newline at end of file
diff --git a/system/language/english/db_lang.php b/system/language/english/db_lang.php
index 79b82c73a..1bf424e57 100644
--- a/system/language/english/db_lang.php
+++ b/system/language/english/db_lang.php
@@ -1,4 +1,42 @@
<?php
+/**
+ * CodeIgniter
+ *
+ * An open source application development framework for PHP
+ *
+ * This content is released under the MIT License (MIT)
+ *
+ * Copyright (c) 2019 - 2022, CodeIgniter Foundation
+ *
+ * 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.
+ *
+ * @package CodeIgniter
+ * @author EllisLab Dev Team
+ * @copyright Copyright (c) 2008 - 2014, EllisLab, Inc. (https://ellislab.com/)
+ * @copyright Copyright (c) 2014 - 2019, British Columbia Institute of Technology (https://bcit.ca/)
+ * @copyright Copyright (c) 2019 - 2022, CodeIgniter Foundation (https://codeigniter.com/)
+ * @license https://opensource.org/licenses/MIT MIT License
+ * @link https://codeigniter.com
+ * @since Version 1.0.0
+ * @filesource
+ */
+defined('BASEPATH') OR exit('No direct script access allowed');
$lang['db_invalid_connection_str'] = 'Unable to determine the database settings based on the connection string you submitted.';
$lang['db_unable_to_connect'] = 'Unable to connect to your database server using the provided settings.';
@@ -15,8 +53,8 @@ $lang['db_field_param_missing'] = 'To fetch fields requires the name of the tabl
$lang['db_unsupported_function'] = 'This feature is not available for the database you are using.';
$lang['db_transaction_failure'] = 'Transaction failure: Rollback performed.';
$lang['db_unable_to_drop'] = 'Unable to drop the specified database.';
-$lang['db_unsuported_feature'] = 'Unsupported feature of the database platform you are using.';
-$lang['db_unsuported_compression'] = 'The file compression format you chose is not supported by your server.';
+$lang['db_unsupported_feature'] = 'Unsupported feature of the database platform you are using.';
+$lang['db_unsupported_compression'] = 'The file compression format you chose is not supported by your server.';
$lang['db_filepath_error'] = 'Unable to write data to the file path you have submitted.';
$lang['db_invalid_cache_path'] = 'The cache path you submitted is not valid or writable.';
$lang['db_table_name_required'] = 'A table name is required for that operation.';
@@ -24,6 +62,3 @@ $lang['db_column_name_required'] = 'A column name is required for that operation
$lang['db_column_definition_required'] = 'A column definition is required for that operation.';
$lang['db_unable_to_set_charset'] = 'Unable to set client connection character set: %s';
$lang['db_error_heading'] = 'A Database Error Occurred';
-
-/* End of file db_lang.php */
-/* Location: ./system/language/english/db_lang.php */ \ No newline at end of file
diff --git a/system/language/english/email_lang.php b/system/language/english/email_lang.php
index e3bd113cb..7ed083c97 100644
--- a/system/language/english/email_lang.php
+++ b/system/language/english/email_lang.php
@@ -1,24 +1,59 @@
<?php
+/**
+ * CodeIgniter
+ *
+ * An open source application development framework for PHP
+ *
+ * This content is released under the MIT License (MIT)
+ *
+ * Copyright (c) 2019 - 2022, CodeIgniter Foundation
+ *
+ * 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.
+ *
+ * @package CodeIgniter
+ * @author EllisLab Dev Team
+ * @copyright Copyright (c) 2008 - 2014, EllisLab, Inc. (https://ellislab.com/)
+ * @copyright Copyright (c) 2014 - 2019, British Columbia Institute of Technology (https://bcit.ca/)
+ * @copyright Copyright (c) 2019 - 2022, CodeIgniter Foundation (https://codeigniter.com/)
+ * @license https://opensource.org/licenses/MIT MIT License
+ * @link https://codeigniter.com
+ * @since Version 1.0.0
+ * @filesource
+ */
+defined('BASEPATH') OR exit('No direct script access allowed');
-$lang['email_must_be_array'] = "The email validation method must be passed an array.";
-$lang['email_invalid_address'] = "Invalid email address: %s";
-$lang['email_attachment_missing'] = "Unable to locate the following email attachment: %s";
-$lang['email_attachment_unreadable'] = "Unable to open this attachment: %s";
-$lang['email_no_recipients'] = "You must include recipients: To, Cc, or Bcc";
-$lang['email_send_failure_phpmail'] = "Unable to send email using PHP mail(). Your server might not be configured to send mail using this method.";
-$lang['email_send_failure_sendmail'] = "Unable to send email using PHP Sendmail. Your server might not be configured to send mail using this method.";
-$lang['email_send_failure_smtp'] = "Unable to send email using PHP SMTP. Your server might not be configured to send mail using this method.";
-$lang['email_sent'] = "Your message has been successfully sent using the following protocol: %s";
-$lang['email_no_socket'] = "Unable to open a socket to Sendmail. Please check settings.";
-$lang['email_no_hostname'] = "You did not specify a SMTP hostname.";
-$lang['email_smtp_error'] = "The following SMTP error was encountered: %s";
-$lang['email_no_smtp_unpw'] = "Error: You must assign a SMTP username and password.";
-$lang['email_failed_smtp_login'] = "Failed to send AUTH LOGIN command. Error: %s";
-$lang['email_smtp_auth_un'] = "Failed to authenticate username. Error: %s";
-$lang['email_smtp_auth_pw'] = "Failed to authenticate password. Error: %s";
-$lang['email_smtp_data_failure'] = "Unable to send data: %s";
-$lang['email_exit_status'] = "Exit status code: %s";
-
-
-/* End of file email_lang.php */
-/* Location: ./system/language/english/email_lang.php */ \ No newline at end of file
+$lang['email_must_be_array'] = 'The email validation method must be passed an array.';
+$lang['email_invalid_address'] = 'Invalid email address: %s';
+$lang['email_attachment_missing'] = 'Unable to locate the following email attachment: %s';
+$lang['email_attachment_unreadable'] = 'Unable to open this attachment: %s';
+$lang['email_no_from'] = 'Cannot send mail with no "From" header.';
+$lang['email_no_recipients'] = 'You must include recipients: To, Cc, or Bcc';
+$lang['email_send_failure_phpmail'] = 'Unable to send email using PHP mail(). Your server might not be configured to send mail using this method.';
+$lang['email_send_failure_sendmail'] = 'Unable to send email using PHP Sendmail. Your server might not be configured to send mail using this method.';
+$lang['email_send_failure_smtp'] = 'Unable to send email using PHP SMTP. Your server might not be configured to send mail using this method.';
+$lang['email_sent'] = 'Your message has been successfully sent using the following protocol: %s';
+$lang['email_no_socket'] = 'Unable to open a socket to Sendmail. Please check settings.';
+$lang['email_no_hostname'] = 'You did not specify a SMTP hostname.';
+$lang['email_smtp_error'] = 'The following SMTP error was encountered: %s';
+$lang['email_no_smtp_unpw'] = 'Error: You must assign a SMTP username and password.';
+$lang['email_failed_smtp_login'] = 'Failed to send AUTH LOGIN command. Error: %s';
+$lang['email_smtp_auth_un'] = 'Failed to authenticate username. Error: %s';
+$lang['email_smtp_auth_pw'] = 'Failed to authenticate password. Error: %s';
+$lang['email_smtp_data_failure'] = 'Unable to send data: %s';
+$lang['email_exit_status'] = 'Exit status code: %s';
diff --git a/system/language/english/form_validation_lang.php b/system/language/english/form_validation_lang.php
index 3418f29ab..fdfb3cfa6 100644
--- a/system/language/english/form_validation_lang.php
+++ b/system/language/english/form_validation_lang.php
@@ -1,29 +1,71 @@
<?php
+/**
+ * CodeIgniter
+ *
+ * An open source application development framework for PHP
+ *
+ * This content is released under the MIT License (MIT)
+ *
+ * Copyright (c) 2019 - 2022, CodeIgniter Foundation
+ *
+ * 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.
+ *
+ * @package CodeIgniter
+ * @author EllisLab Dev Team
+ * @copyright Copyright (c) 2008 - 2014, EllisLab, Inc. (https://ellislab.com/)
+ * @copyright Copyright (c) 2014 - 2019, British Columbia Institute of Technology (https://bcit.ca/)
+ * @copyright Copyright (c) 2019 - 2022, CodeIgniter Foundation (https://codeigniter.com/)
+ * @license https://opensource.org/licenses/MIT MIT License
+ * @link https://codeigniter.com
+ * @since Version 1.0.0
+ * @filesource
+ */
+defined('BASEPATH') OR exit('No direct script access allowed');
-$lang['required'] = "The %s field is required.";
-$lang['isset'] = "The %s field must have a value.";
-$lang['valid_email'] = "The %s field must contain a valid email address.";
-$lang['valid_emails'] = "The %s field must contain all valid email addresses.";
-$lang['valid_url'] = "The %s field must contain a valid URL.";
-$lang['valid_ip'] = "The %s field must contain a valid IP.";
-$lang['min_length'] = "The %s field must be at least %s characters in length.";
-$lang['max_length'] = "The %s field can not exceed %s characters in length.";
-$lang['exact_length'] = "The %s field must be exactly %s characters in length.";
-$lang['alpha'] = "The %s field may only contain alphabetical characters.";
-$lang['alpha_numeric'] = "The %s field may only contain alpha-numeric characters.";
-$lang['alpha_dash'] = "The %s field may only contain alpha-numeric characters, underscores, and dashes.";
-$lang['numeric'] = "The %s field must contain only numbers.";
-$lang['is_numeric'] = "The %s field must contain only numeric characters.";
-$lang['integer'] = "The %s field must contain an integer.";
-$lang['regex_match'] = "The %s field is not in the correct format.";
-$lang['matches'] = "The %s field does not match the %s field.";
-$lang['is_unique'] = "The %s field must contain a unique value.";
-$lang['is_natural'] = "The %s field must contain only positive numbers.";
-$lang['is_natural_no_zero'] = "The %s field must contain a number greater than zero.";
-$lang['decimal'] = "The %s field must contain a decimal number.";
-$lang['less_than'] = "The %s field must contain a number less than %s.";
-$lang['greater_than'] = "The %s field must contain a number greater than %s.";
-
-
-/* End of file form_validation_lang.php */
-/* Location: ./system/language/english/form_validation_lang.php */ \ No newline at end of file
+$lang['form_validation_required'] = 'The {field} field is required.';
+$lang['form_validation_isset'] = 'The {field} field must have a value.';
+$lang['form_validation_valid_email'] = 'The {field} field must contain a valid email address.';
+$lang['form_validation_valid_emails'] = 'The {field} field must contain all valid email addresses.';
+$lang['form_validation_valid_url'] = 'The {field} field must contain a valid URL.';
+$lang['form_validation_valid_ip'] = 'The {field} field must contain a valid IP.';
+$lang['form_validation_valid_mac'] = 'The {field} field must contain a valid MAC.';
+$lang['form_validation_valid_base64'] = 'The {field} field must contain a valid Base64 string.';
+$lang['form_validation_min_length'] = 'The {field} field must be at least {param} characters in length.';
+$lang['form_validation_max_length'] = 'The {field} field cannot exceed {param} characters in length.';
+$lang['form_validation_exact_length'] = 'The {field} field must be exactly {param} characters in length.';
+$lang['form_validation_alpha'] = 'The {field} field may only contain alphabetical characters.';
+$lang['form_validation_alpha_numeric'] = 'The {field} field may only contain alpha-numeric characters.';
+$lang['form_validation_alpha_numeric_spaces'] = 'The {field} field may only contain alpha-numeric characters and spaces.';
+$lang['form_validation_alpha_dash'] = 'The {field} field may only contain alpha-numeric characters, underscores, and dashes.';
+$lang['form_validation_numeric'] = 'The {field} field must contain only numbers.';
+$lang['form_validation_is_numeric'] = 'The {field} field must contain only numeric characters.';
+$lang['form_validation_integer'] = 'The {field} field must contain an integer.';
+$lang['form_validation_regex_match'] = 'The {field} field is not in the correct format.';
+$lang['form_validation_matches'] = 'The {field} field does not match the {param} field.';
+$lang['form_validation_differs'] = 'The {field} field must differ from the {param} field.';
+$lang['form_validation_is_unique'] = 'The {field} field must contain a unique value.';
+$lang['form_validation_is_natural'] = 'The {field} field must only contain digits.';
+$lang['form_validation_is_natural_no_zero'] = 'The {field} field must only contain digits and must be greater than zero.';
+$lang['form_validation_decimal'] = 'The {field} field must contain a decimal number.';
+$lang['form_validation_less_than'] = 'The {field} field must contain a number less than {param}.';
+$lang['form_validation_less_than_equal_to'] = 'The {field} field must contain a number less than or equal to {param}.';
+$lang['form_validation_greater_than'] = 'The {field} field must contain a number greater than {param}.';
+$lang['form_validation_greater_than_equal_to'] = 'The {field} field must contain a number greater than or equal to {param}.';
+$lang['form_validation_error_message_not_set'] = 'Unable to access an error message corresponding to your field name {field}.';
+$lang['form_validation_in_list'] = 'The {field} field must be one of: {param}.';
diff --git a/system/language/english/ftp_lang.php b/system/language/english/ftp_lang.php
index 1e5168cf8..7067b4b56 100644
--- a/system/language/english/ftp_lang.php
+++ b/system/language/english/ftp_lang.php
@@ -1,18 +1,52 @@
<?php
+/**
+ * CodeIgniter
+ *
+ * An open source application development framework for PHP
+ *
+ * This content is released under the MIT License (MIT)
+ *
+ * Copyright (c) 2019 - 2022, CodeIgniter Foundation
+ *
+ * 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.
+ *
+ * @package CodeIgniter
+ * @author EllisLab Dev Team
+ * @copyright Copyright (c) 2008 - 2014, EllisLab, Inc. (https://ellislab.com/)
+ * @copyright Copyright (c) 2014 - 2019, British Columbia Institute of Technology (https://bcit.ca/)
+ * @copyright Copyright (c) 2019 - 2022, CodeIgniter Foundation (https://codeigniter.com/)
+ * @license https://opensource.org/licenses/MIT MIT License
+ * @link https://codeigniter.com
+ * @since Version 1.0.0
+ * @filesource
+ */
+defined('BASEPATH') OR exit('No direct script access allowed');
-$lang['ftp_no_connection'] = "Unable to locate a valid connection ID. Please make sure you are connected before peforming any file routines.";
-$lang['ftp_unable_to_connect'] = "Unable to connect to your FTP server using the supplied hostname.";
-$lang['ftp_unable_to_login'] = "Unable to login to your FTP server. Please check your username and password.";
-$lang['ftp_unable_to_makdir'] = "Unable to create the directory you have specified.";
-$lang['ftp_unable_to_changedir'] = "Unable to change directories.";
-$lang['ftp_unable_to_chmod'] = "Unable to set file permissions. Please check your path. Note: This feature is only available in PHP 5 or higher.";
-$lang['ftp_unable_to_upload'] = "Unable to upload the specified file. Please check your path.";
-$lang['ftp_unable_to_download'] = "Unable to download the specified file. Please check your path.";
-$lang['ftp_no_source_file'] = "Unable to locate the source file. Please check your path.";
-$lang['ftp_unable_to_rename'] = "Unable to rename the file.";
-$lang['ftp_unable_to_delete'] = "Unable to delete the file.";
-$lang['ftp_unable_to_move'] = "Unable to move the file. Please make sure the destination directory exists.";
-
-
-/* End of file ftp_lang.php */
-/* Location: ./system/language/english/ftp_lang.php */ \ No newline at end of file
+$lang['ftp_no_connection'] = 'Unable to locate a valid connection ID. Please make sure you are connected before performing any file routines.';
+$lang['ftp_unable_to_connect'] = 'Unable to connect to your FTP server using the supplied hostname.';
+$lang['ftp_unable_to_login'] = 'Unable to login to your FTP server. Please check your username and password.';
+$lang['ftp_unable_to_mkdir'] = 'Unable to create the directory you have specified.';
+$lang['ftp_unable_to_changedir'] = 'Unable to change directories.';
+$lang['ftp_unable_to_chmod'] = 'Unable to set file permissions. Please check your path.';
+$lang['ftp_unable_to_upload'] = 'Unable to upload the specified file. Please check your path.';
+$lang['ftp_unable_to_download'] = 'Unable to download the specified file. Please check your path.';
+$lang['ftp_no_source_file'] = 'Unable to locate the source file. Please check your path.';
+$lang['ftp_unable_to_rename'] = 'Unable to rename the file.';
+$lang['ftp_unable_to_delete'] = 'Unable to delete the file.';
+$lang['ftp_unable_to_move'] = 'Unable to move the file. Please make sure the destination directory exists.';
diff --git a/system/language/english/imglib_lang.php b/system/language/english/imglib_lang.php
index 66505da07..b0a80a5d0 100644
--- a/system/language/english/imglib_lang.php
+++ b/system/language/english/imglib_lang.php
@@ -1,24 +1,59 @@
<?php
+/**
+ * CodeIgniter
+ *
+ * An open source application development framework for PHP
+ *
+ * This content is released under the MIT License (MIT)
+ *
+ * Copyright (c) 2019 - 2022, CodeIgniter Foundation
+ *
+ * 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.
+ *
+ * @package CodeIgniter
+ * @author EllisLab Dev Team
+ * @copyright Copyright (c) 2008 - 2014, EllisLab, Inc. (https://ellislab.com/)
+ * @copyright Copyright (c) 2014 - 2019, British Columbia Institute of Technology (https://bcit.ca/)
+ * @copyright Copyright (c) 2019 - 2022, CodeIgniter Foundation (https://codeigniter.com/)
+ * @license https://opensource.org/licenses/MIT MIT License
+ * @link https://codeigniter.com
+ * @since Version 1.0.0
+ * @filesource
+ */
+defined('BASEPATH') OR exit('No direct script access allowed');
-$lang['imglib_source_image_required'] = "You must specify a source image in your preferences.";
-$lang['imglib_gd_required'] = "The GD image library is required for this feature.";
-$lang['imglib_gd_required_for_props'] = "Your server must support the GD image library in order to determine the image properties.";
-$lang['imglib_unsupported_imagecreate'] = "Your server does not support the GD function required to process this type of image.";
-$lang['imglib_gif_not_supported'] = "GIF images are often not supported due to licensing restrictions. You may have to use JPG or PNG images instead.";
-$lang['imglib_jpg_not_supported'] = "JPG images are not supported.";
-$lang['imglib_png_not_supported'] = "PNG images are not supported.";
-$lang['imglib_jpg_or_png_required'] = "The image resize protocol specified in your preferences only works with JPEG or PNG image types.";
-$lang['imglib_copy_error'] = "An error was encountered while attempting to replace the file. Please make sure your file directory is writable.";
-$lang['imglib_rotate_unsupported'] = "Image rotation does not appear to be supported by your server.";
-$lang['imglib_libpath_invalid'] = "The path to your image library is not correct. Please set the correct path in your image preferences.";
-$lang['imglib_image_process_failed'] = "Image processing failed. Please verify that your server supports the chosen protocol and that the path to your image library is correct.";
-$lang['imglib_rotation_angle_required'] = "An angle of rotation is required to rotate the image.";
-$lang['imglib_writing_failed_gif'] = "GIF image.";
-$lang['imglib_invalid_path'] = "The path to the image is not correct.";
-$lang['imglib_copy_failed'] = "The image copy routine failed.";
-$lang['imglib_missing_font'] = "Unable to find a font to use.";
-$lang['imglib_save_failed'] = "Unable to save the image. Please make sure the image and file directory are writable.";
-
-
-/* End of file imglib_lang.php */
-/* Location: ./system/language/english/imglib_lang.php */ \ No newline at end of file
+$lang['imglib_source_image_required'] = 'You must specify a source image in your preferences.';
+$lang['imglib_gd_required'] = 'The GD image library is required for this feature.';
+$lang['imglib_gd_required_for_props'] = 'Your server must support the GD image library in order to determine the image properties.';
+$lang['imglib_unsupported_imagecreate'] = 'Your server does not support the GD function required to process this type of image.';
+$lang['imglib_gif_not_supported'] = 'GIF images are often not supported due to licensing restrictions. You may have to use JPG or PNG images instead.';
+$lang['imglib_jpg_not_supported'] = 'JPG images are not supported.';
+$lang['imglib_png_not_supported'] = 'PNG images are not supported.';
+$lang['imglib_webp_not_supported'] = 'WEBP images are not supported.';
+$lang['imglib_jpg_or_png_required'] = 'The image resize protocol specified in your preferences only works with JPEG or PNG image types.';
+$lang['imglib_copy_error'] = 'An error was encountered while attempting to replace the file. Please make sure your file directory is writable.';
+$lang['imglib_rotate_unsupported'] = 'Image rotation does not appear to be supported by your server.';
+$lang['imglib_libpath_invalid'] = 'The path to your image library is not correct. Please set the correct path in your image preferences.';
+$lang['imglib_image_process_failed'] = 'Image processing failed. Please verify that your server supports the chosen protocol and that the path to your image library is correct.';
+$lang['imglib_rotation_angle_required'] = 'An angle of rotation is required to rotate the image.';
+$lang['imglib_invalid_path'] = 'The path to the image is not correct.';
+$lang['imglib_invalid_image'] = 'The provided image is not valid.';
+$lang['imglib_copy_failed'] = 'The image copy routine failed.';
+$lang['imglib_missing_font'] = 'Unable to find a font to use.';
+$lang['imglib_save_failed'] = 'Unable to save the image. Please make sure the image and file directory are writable.';
diff --git a/system/language/english/index.html b/system/language/english/index.html
index c942a79ce..bcb7cae34 100644
--- a/system/language/english/index.html
+++ b/system/language/english/index.html
@@ -1,4 +1,5 @@
-<html>
+<!DOCTYPE html>
+<html lang="en">
<head>
<title>403 Forbidden</title>
</head>
@@ -7,4 +8,4 @@
<p>Directory access is forbidden.</p>
</body>
-</html> \ No newline at end of file
+</html>
diff --git a/system/language/english/migration_lang.php b/system/language/english/migration_lang.php
index f17530f00..a37036284 100644
--- a/system/language/english/migration_lang.php
+++ b/system/language/english/migration_lang.php
@@ -1,13 +1,48 @@
<?php
+/**
+ * CodeIgniter
+ *
+ * An open source application development framework for PHP
+ *
+ * This content is released under the MIT License (MIT)
+ *
+ * Copyright (c) 2019 - 2022, CodeIgniter Foundation
+ *
+ * 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.
+ *
+ * @package CodeIgniter
+ * @author EllisLab Dev Team
+ * @copyright Copyright (c) 2008 - 2014, EllisLab, Inc. (https://ellislab.com/)
+ * @copyright Copyright (c) 2014 - 2019, British Columbia Institute of Technology (https://bcit.ca/)
+ * @copyright Copyright (c) 2019 - 2022, CodeIgniter Foundation (https://codeigniter.com/)
+ * @license https://opensource.org/licenses/MIT MIT License
+ * @link https://codeigniter.com
+ * @since Version 3.0.0
+ * @filesource
+ */
+defined('BASEPATH') OR exit('No direct script access allowed');
-$lang['migration_none_found'] = "No migrations were found.";
-$lang['migration_not_found'] = "This migration could not be found.";
-$lang['migration_multiple_version'] = "This are multiple migrations with the same version number: %d.";
-$lang['migration_class_doesnt_exist'] = "The migration class \"%s\" could not be found.";
-$lang['migration_missing_up_method'] = "The migration class \"%s\" is missing an 'up' method.";
-$lang['migration_missing_down_method'] = "The migration class \"%s\" is missing an 'down' method.";
-$lang['migration_invalid_filename'] = "Migration \"%s\" has an invalid filename.";
-
-
-/* End of file migration_lang.php */
-/* Location: ./system/language/english/migration_lang.php */ \ No newline at end of file
+$lang['migration_none_found'] = 'No migrations were found.';
+$lang['migration_not_found'] = 'No migration could be found with the version number: %s.';
+$lang['migration_sequence_gap'] = 'There is a gap in the migration sequence near version number: %s.';
+$lang['migration_multiple_version'] = 'There are multiple migrations with the same version number: %s.';
+$lang['migration_class_doesnt_exist'] = 'The migration class "%s" could not be found.';
+$lang['migration_missing_up_method'] = 'The migration class "%s" is missing an "up" method.';
+$lang['migration_missing_down_method'] = 'The migration class "%s" is missing a "down" method.';
+$lang['migration_invalid_filename'] = 'Migration "%s" has an invalid filename.';
diff --git a/system/language/english/number_lang.php b/system/language/english/number_lang.php
index 908580914..38e3781da 100644
--- a/system/language/english/number_lang.php
+++ b/system/language/english/number_lang.php
@@ -1,10 +1,45 @@
<?php
+/**
+ * CodeIgniter
+ *
+ * An open source application development framework for PHP
+ *
+ * This content is released under the MIT License (MIT)
+ *
+ * Copyright (c) 2019 - 2022, CodeIgniter Foundation
+ *
+ * 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.
+ *
+ * @package CodeIgniter
+ * @author EllisLab Dev Team
+ * @copyright Copyright (c) 2008 - 2014, EllisLab, Inc. (https://ellislab.com/)
+ * @copyright Copyright (c) 2014 - 2019, British Columbia Institute of Technology (https://bcit.ca/)
+ * @copyright Copyright (c) 2019 - 2022, CodeIgniter Foundation (https://codeigniter.com/)
+ * @license https://opensource.org/licenses/MIT MIT License
+ * @link https://codeigniter.com
+ * @since Version 1.0.0
+ * @filesource
+ */
+defined('BASEPATH') OR exit('No direct script access allowed');
-$lang['terabyte_abbr'] = "TB";
-$lang['gigabyte_abbr'] = "GB";
-$lang['megabyte_abbr'] = "MB";
-$lang['kilobyte_abbr'] = "KB";
-$lang['bytes'] = "Bytes";
-
-/* End of file number_lang.php */
-/* Location: ./system/language/english/number_lang.php */ \ No newline at end of file
+$lang['terabyte_abbr'] = 'TB';
+$lang['gigabyte_abbr'] = 'GB';
+$lang['megabyte_abbr'] = 'MB';
+$lang['kilobyte_abbr'] = 'KB';
+$lang['bytes'] = 'Bytes';
diff --git a/system/language/english/pagination_lang.php b/system/language/english/pagination_lang.php
new file mode 100644
index 000000000..808a61ead
--- /dev/null
+++ b/system/language/english/pagination_lang.php
@@ -0,0 +1,44 @@
+<?php
+/**
+ * CodeIgniter
+ *
+ * An open source application development framework for PHP
+ *
+ * This content is released under the MIT License (MIT)
+ *
+ * Copyright (c) 2019 - 2022, CodeIgniter Foundation
+ *
+ * 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.
+ *
+ * @package CodeIgniter
+ * @author EllisLab Dev Team
+ * @copyright Copyright (c) 2008 - 2014, EllisLab, Inc. (https://ellislab.com/)
+ * @copyright Copyright (c) 2014 - 2019, British Columbia Institute of Technology (https://bcit.ca/)
+ * @copyright Copyright (c) 2019 - 2022, CodeIgniter Foundation (https://codeigniter.com/)
+ * @license https://opensource.org/licenses/MIT MIT License
+ * @link https://codeigniter.com
+ * @since Version 1.0.0
+ * @filesource
+ */
+defined('BASEPATH') OR exit('No direct script access allowed');
+
+$lang['pagination_first_link'] = '&lsaquo; First';
+$lang['pagination_next_link'] = '&gt;';
+$lang['pagination_prev_link'] = '&lt;';
+$lang['pagination_last_link'] = 'Last &rsaquo;';
diff --git a/system/language/english/profiler_lang.php b/system/language/english/profiler_lang.php
index 1111158c8..71a2afcb1 100644
--- a/system/language/english/profiler_lang.php
+++ b/system/language/english/profiler_lang.php
@@ -1,25 +1,61 @@
<?php
+/**
+ * CodeIgniter
+ *
+ * An open source application development framework for PHP
+ *
+ * This content is released under the MIT License (MIT)
+ *
+ * Copyright (c) 2019 - 2022, CodeIgniter Foundation
+ *
+ * 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.
+ *
+ * @package CodeIgniter
+ * @author EllisLab Dev Team
+ * @copyright Copyright (c) 2008 - 2014, EllisLab, Inc. (https://ellislab.com/)
+ * @copyright Copyright (c) 2014 - 2019, British Columbia Institute of Technology (https://bcit.ca/)
+ * @copyright Copyright (c) 2019 - 2022, CodeIgniter Foundation (https://codeigniter.com/)
+ * @license https://opensource.org/licenses/MIT MIT License
+ * @link https://codeigniter.com
+ * @since Version 1.0.0
+ * @filesource
+ */
+defined('BASEPATH') OR exit('No direct script access allowed');
-$lang['profiler_database'] = 'DATABASE';
+$lang['profiler_database'] = 'DATABASE';
$lang['profiler_controller_info'] = 'CLASS/METHOD';
-$lang['profiler_benchmarks'] = 'BENCHMARKS';
-$lang['profiler_queries'] = 'QUERIES';
-$lang['profiler_get_data'] = 'GET DATA';
-$lang['profiler_post_data'] = 'POST DATA';
-$lang['profiler_uri_string'] = 'URI STRING';
-$lang['profiler_memory_usage'] = 'MEMORY USAGE';
-$lang['profiler_config'] = 'CONFIG VARIABLES';
-$lang['profiler_session_data'] = 'SESSION DATA';
-$lang['profiler_headers'] = 'HTTP HEADERS';
-$lang['profiler_no_db'] = 'Database driver is not currently loaded';
-$lang['profiler_no_queries'] = 'No queries were run';
-$lang['profiler_no_post'] = 'No POST data exists';
-$lang['profiler_no_get'] = 'No GET data exists';
-$lang['profiler_no_uri'] = 'No URI data exists';
-$lang['profiler_no_memory'] = 'Memory Usage Unavailable';
-$lang['profiler_no_profiles'] = 'No Profile data - all Profiler sections have been disabled.';
-$lang['profiler_section_hide'] = 'Hide';
-$lang['profiler_section_show'] = 'Show';
-
-/* End of file profiler_lang.php */
-/* Location: ./system/language/english/profiler_lang.php */ \ No newline at end of file
+$lang['profiler_benchmarks'] = 'BENCHMARKS';
+$lang['profiler_queries'] = 'QUERIES';
+$lang['profiler_get_data'] = 'GET DATA';
+$lang['profiler_post_data'] = 'POST DATA';
+$lang['profiler_uri_string'] = 'URI STRING';
+$lang['profiler_memory_usage'] = 'MEMORY USAGE';
+$lang['profiler_config'] = 'CONFIG VARIABLES';
+$lang['profiler_session_data'] = 'SESSION DATA';
+$lang['profiler_headers'] = 'HTTP HEADERS';
+$lang['profiler_no_db'] = 'Database driver is not currently loaded';
+$lang['profiler_no_queries'] = 'No queries were run';
+$lang['profiler_no_post'] = 'No POST data exists';
+$lang['profiler_no_get'] = 'No GET data exists';
+$lang['profiler_no_uri'] = 'No URI data exists';
+$lang['profiler_no_memory'] = 'Memory Usage Unavailable';
+$lang['profiler_no_profiles'] = 'No Profile data - all Profiler sections have been disabled.';
+$lang['profiler_section_hide'] = 'Hide';
+$lang['profiler_section_show'] = 'Show';
+$lang['profiler_seconds'] = 'seconds';
diff --git a/system/language/english/unit_test_lang.php b/system/language/english/unit_test_lang.php
index 070bcd1f2..02366f0c6 100644
--- a/system/language/english/unit_test_lang.php
+++ b/system/language/english/unit_test_lang.php
@@ -1,25 +1,59 @@
<?php
+/**
+ * CodeIgniter
+ *
+ * An open source application development framework for PHP
+ *
+ * This content is released under the MIT License (MIT)
+ *
+ * Copyright (c) 2019 - 2022, CodeIgniter Foundation
+ *
+ * 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.
+ *
+ * @package CodeIgniter
+ * @author EllisLab Dev Team
+ * @copyright Copyright (c) 2008 - 2014, EllisLab, Inc. (https://ellislab.com/)
+ * @copyright Copyright (c) 2014 - 2019, British Columbia Institute of Technology (https://bcit.ca/)
+ * @copyright Copyright (c) 2019 - 2022, CodeIgniter Foundation (https://codeigniter.com/)
+ * @license https://opensource.org/licenses/MIT MIT License
+ * @link https://codeigniter.com
+ * @since Version 1.0.0
+ * @filesource
+ */
+defined('BASEPATH') OR exit('No direct script access allowed');
-$lang['ut_test_name'] = 'Test Name';
-$lang['ut_test_datatype'] = 'Test Datatype';
-$lang['ut_res_datatype'] = 'Expected Datatype';
-$lang['ut_result'] = 'Result';
-$lang['ut_undefined'] = 'Undefined Test Name';
-$lang['ut_file'] = 'File Name';
-$lang['ut_line'] = 'Line Number';
-$lang['ut_passed'] = 'Passed';
-$lang['ut_failed'] = 'Failed';
-$lang['ut_boolean'] = 'Boolean';
-$lang['ut_integer'] = 'Integer';
-$lang['ut_float'] = 'Float';
-$lang['ut_double'] = 'Float'; // can be the same as float
-$lang['ut_string'] = 'String';
-$lang['ut_array'] = 'Array';
-$lang['ut_object'] = 'Object';
-$lang['ut_resource'] = 'Resource';
-$lang['ut_null'] = 'Null';
-$lang['ut_notes'] = 'Notes';
-
-
-/* End of file unit_test_lang.php */
-/* Location: ./system/language/english/unit_test_lang.php */ \ No newline at end of file
+$lang['ut_test_name'] = 'Test Name';
+$lang['ut_test_datatype'] = 'Test Datatype';
+$lang['ut_res_datatype'] = 'Expected Datatype';
+$lang['ut_result'] = 'Result';
+$lang['ut_undefined'] = 'Undefined Test Name';
+$lang['ut_file'] = 'File Name';
+$lang['ut_line'] = 'Line Number';
+$lang['ut_passed'] = 'Passed';
+$lang['ut_failed'] = 'Failed';
+$lang['ut_boolean'] = 'Boolean';
+$lang['ut_integer'] = 'Integer';
+$lang['ut_float'] = 'Float';
+$lang['ut_double'] = 'Float'; // can be the same as float
+$lang['ut_string'] = 'String';
+$lang['ut_array'] = 'Array';
+$lang['ut_object'] = 'Object';
+$lang['ut_resource'] = 'Resource';
+$lang['ut_null'] = 'Null';
+$lang['ut_notes'] = 'Notes';
diff --git a/system/language/english/upload_lang.php b/system/language/english/upload_lang.php
index 4de9e9e74..bd1e20181 100644
--- a/system/language/english/upload_lang.php
+++ b/system/language/english/upload_lang.php
@@ -1,22 +1,56 @@
<?php
+/**
+ * CodeIgniter
+ *
+ * An open source application development framework for PHP
+ *
+ * This content is released under the MIT License (MIT)
+ *
+ * Copyright (c) 2019 - 2022, CodeIgniter Foundation
+ *
+ * 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.
+ *
+ * @package CodeIgniter
+ * @author EllisLab Dev Team
+ * @copyright Copyright (c) 2008 - 2014, EllisLab, Inc. (https://ellislab.com/)
+ * @copyright Copyright (c) 2014 - 2019, British Columbia Institute of Technology (https://bcit.ca/)
+ * @copyright Copyright (c) 2019 - 2022, CodeIgniter Foundation (https://codeigniter.com/)
+ * @license https://opensource.org/licenses/MIT MIT License
+ * @link https://codeigniter.com
+ * @since Version 1.0.0
+ * @filesource
+ */
+defined('BASEPATH') OR exit('No direct script access allowed');
-$lang['upload_userfile_not_set'] = "Unable to find a post variable called userfile.";
-$lang['upload_file_exceeds_limit'] = "The uploaded file exceeds the maximum allowed size in your PHP configuration file.";
-$lang['upload_file_exceeds_form_limit'] = "The uploaded file exceeds the maximum size allowed by the submission form.";
-$lang['upload_file_partial'] = "The file was only partially uploaded.";
-$lang['upload_no_temp_directory'] = "The temporary folder is missing.";
-$lang['upload_unable_to_write_file'] = "The file could not be written to disk.";
-$lang['upload_stopped_by_extension'] = "The file upload was stopped by extension.";
-$lang['upload_no_file_selected'] = "You did not select a file to upload.";
-$lang['upload_invalid_filetype'] = "The filetype you are attempting to upload is not allowed.";
-$lang['upload_invalid_filesize'] = "The file you are attempting to upload is larger than the permitted size.";
-$lang['upload_invalid_dimensions'] = "The image you are attempting to upload exceedes the maximum height or width.";
-$lang['upload_destination_error'] = "A problem was encountered while attempting to move the uploaded file to the final destination.";
-$lang['upload_no_filepath'] = "The upload path does not appear to be valid.";
-$lang['upload_no_file_types'] = "You have not specified any allowed file types.";
-$lang['upload_bad_filename'] = "The file name you submitted already exists on the server.";
-$lang['upload_not_writable'] = "The upload destination folder does not appear to be writable.";
-
-
-/* End of file upload_lang.php */
-/* Location: ./system/language/english/upload_lang.php */ \ No newline at end of file
+$lang['upload_userfile_not_set'] = 'Unable to find a post variable called userfile.';
+$lang['upload_file_exceeds_limit'] = 'The uploaded file exceeds the maximum allowed size in your PHP configuration file.';
+$lang['upload_file_exceeds_form_limit'] = 'The uploaded file exceeds the maximum size allowed by the submission form.';
+$lang['upload_file_partial'] = 'The file was only partially uploaded.';
+$lang['upload_no_temp_directory'] = 'The temporary folder is missing.';
+$lang['upload_unable_to_write_file'] = 'The file could not be written to disk.';
+$lang['upload_stopped_by_extension'] = 'The file upload was stopped by extension.';
+$lang['upload_no_file_selected'] = 'You did not select a file to upload.';
+$lang['upload_invalid_filetype'] = 'The filetype you are attempting to upload is not allowed.';
+$lang['upload_invalid_filesize'] = 'The file you are attempting to upload is larger than the permitted size.';
+$lang['upload_invalid_dimensions'] = 'The image you are attempting to upload doesn\'t fit into the allowed dimensions.';
+$lang['upload_destination_error'] = 'A problem was encountered while attempting to move the uploaded file to the final destination.';
+$lang['upload_no_filepath'] = 'The upload path does not appear to be valid.';
+$lang['upload_no_file_types'] = 'You have not specified any allowed file types.';
+$lang['upload_bad_filename'] = 'The file name you submitted already exists on the server.';
+$lang['upload_not_writable'] = 'The upload destination folder does not appear to be writable.';
diff --git a/system/language/index.html b/system/language/index.html
index c942a79ce..bcb7cae34 100644
--- a/system/language/index.html
+++ b/system/language/index.html
@@ -1,4 +1,5 @@
-<html>
+<!DOCTYPE html>
+<html lang="en">
<head>
<title>403 Forbidden</title>
</head>
@@ -7,4 +8,4 @@
<p>Directory access is forbidden.</p>
</body>
-</html> \ No newline at end of file
+</html>
diff --git a/system/libraries/Cache/Cache.php b/system/libraries/Cache/Cache.php
index 673e63de3..f3dfe25e4 100644
--- a/system/libraries/Cache/Cache.php
+++ b/system/libraries/Cache/Cache.php
@@ -1,19 +1,42 @@
-<?php if ( ! defined('BASEPATH')) exit('No direct script access allowed');
+<?php
/**
* CodeIgniter
*
- * An open source application development framework for PHP 5.1.6 or newer
+ * An open source application development framework for PHP
*
- * @package CodeIgniter
- * @author ExpressionEngine Dev Team
- * @copyright Copyright (c) 2006 - 2014 EllisLab, Inc.
- * @license http://codeigniter.com/user_guide/license.html
- * @link http://codeigniter.com
- * @since Version 2.0
+ * This content is released under the MIT License (MIT)
+ *
+ * Copyright (c) 2019 - 2022, CodeIgniter Foundation
+ *
+ * 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.
+ *
+ * @package CodeIgniter
+ * @author EllisLab Dev Team
+ * @copyright Copyright (c) 2008 - 2014, EllisLab, Inc. (https://ellislab.com/)
+ * @copyright Copyright (c) 2014 - 2019, British Columbia Institute of Technology (https://bcit.ca/)
+ * @copyright Copyright (c) 2019 - 2022, CodeIgniter Foundation (https://codeigniter.com/)
+ * @license https://opensource.org/licenses/MIT MIT License
+ * @link https://codeigniter.com
+ * @since Version 2.0.0
* @filesource
*/
-
-// ------------------------------------------------------------------------
+defined('BASEPATH') OR exit('No direct script access allowed');
/**
* CodeIgniter Caching Class
@@ -21,31 +44,83 @@
* @package CodeIgniter
* @subpackage Libraries
* @category Core
- * @author ExpressionEngine Dev Team
+ * @author EllisLab Dev Team
* @link
*/
class CI_Cache extends CI_Driver_Library {
- protected $valid_drivers = array(
- 'cache_apc', 'cache_file', 'cache_memcached', 'cache_dummy'
+ /**
+ * Valid cache drivers
+ *
+ * @var array
+ */
+ protected $valid_drivers = array(
+ 'apc',
+ 'apcu',
+ 'dummy',
+ 'file',
+ 'memcached',
+ 'redis',
+ 'wincache'
);
- protected $_cache_path = NULL; // Path of cache files (if file-based cache)
- protected $_adapter = 'dummy';
- protected $_backup_driver;
+ /**
+ * Path of cache files (if file-based cache)
+ *
+ * @var string
+ */
+ protected $_cache_path = NULL;
- // ------------------------------------------------------------------------
+ /**
+ * Reference to the driver
+ *
+ * @var mixed
+ */
+ protected $_adapter = 'dummy';
+
+ /**
+ * Fallback driver
+ *
+ * @var string
+ */
+ protected $_backup_driver = 'dummy';
+
+ /**
+ * Cache key prefix
+ *
+ * @var string
+ */
+ public $key_prefix = '';
/**
* Constructor
*
- * @param array
+ * Initialize class properties based on the configuration array.
+ *
+ * @param array $config = array()
+ * @return void
*/
public function __construct($config = array())
{
- if ( ! empty($config))
+ isset($config['adapter']) && $this->_adapter = $config['adapter'];
+ isset($config['backup']) && $this->_backup_driver = $config['backup'];
+ isset($config['key_prefix']) && $this->key_prefix = $config['key_prefix'];
+
+ // If the specified adapter isn't available, check the backup.
+ if ( ! $this->is_supported($this->_adapter))
{
- $this->_initialize($config);
+ if ( ! $this->is_supported($this->_backup_driver))
+ {
+ // Backup isn't supported either. Default to 'Dummy' driver.
+ log_message('error', 'Cache adapter "'.$this->_adapter.'" and backup "'.$this->_backup_driver.'" are both unavailable. Cache is now using "Dummy" adapter.');
+ $this->_adapter = 'dummy';
+ }
+ else
+ {
+ // Backup is supported. Set it to primary.
+ log_message('debug', 'Cache adapter "'.$this->_adapter.'" is unavailable. Falling back to "'.$this->_backup_driver.'" backup adapter.');
+ $this->_adapter = $this->_backup_driver;
+ }
}
}
@@ -54,15 +129,15 @@ class CI_Cache extends CI_Driver_Library {
/**
* Get
*
- * Look for a value in the cache. If it exists, return the data
+ * Look for a value in the cache. If it exists, return the data
* if not, return FALSE
*
- * @param string
- * @return mixed value that is stored/FALSE on failure
+ * @param string $id
+ * @return mixed value matching $id or FALSE on failure
*/
public function get($id)
{
- return $this->{$this->_adapter}->get($id);
+ return $this->{$this->_adapter}->get($this->key_prefix.$id);
}
// ------------------------------------------------------------------------
@@ -70,15 +145,15 @@ class CI_Cache extends CI_Driver_Library {
/**
* Cache Save
*
- * @param string Unique Key
- * @param mixed Data to store
- * @param int Length of time (in seconds) to cache the data
- *
- * @return boolean true on success/false on failure
+ * @param string $id Cache ID
+ * @param mixed $data Data to store
+ * @param int $ttl Cache TTL (in seconds)
+ * @param bool $raw Whether to store the raw value
+ * @return bool TRUE on success, FALSE on failure
*/
- public function save($id, $data, $ttl = 60)
+ public function save($id, $data, $ttl = 60, $raw = FALSE)
{
- return $this->{$this->_adapter}->save($id, $data, $ttl);
+ return $this->{$this->_adapter}->save($this->key_prefix.$id, $data, $ttl, $raw);
}
// ------------------------------------------------------------------------
@@ -86,86 +161,78 @@ class CI_Cache extends CI_Driver_Library {
/**
* Delete from Cache
*
- * @param mixed unique identifier of the item in the cache
- * @return boolean true on success/false on failure
+ * @param string $id Cache ID
+ * @return bool TRUE on success, FALSE on failure
*/
public function delete($id)
{
- return $this->{$this->_adapter}->delete($id);
+ return $this->{$this->_adapter}->delete($this->key_prefix.$id);
}
// ------------------------------------------------------------------------
/**
- * Clean the cache
+ * Increment a raw value
*
- * @return boolean false on failure/true on success
+ * @param string $id Cache ID
+ * @param int $offset Step/value to add
+ * @return mixed New value on success or FALSE on failure
*/
- public function clean()
+ public function increment($id, $offset = 1)
{
- return $this->{$this->_adapter}->clean();
+ return $this->{$this->_adapter}->increment($this->key_prefix.$id, $offset);
}
// ------------------------------------------------------------------------
/**
- * Cache Info
+ * Decrement a raw value
*
- * @param string user/filehits
- * @return mixed array on success, false on failure
+ * @param string $id Cache ID
+ * @param int $offset Step/value to reduce by
+ * @return mixed New value on success or FALSE on failure
*/
- public function cache_info($type = 'user')
+ public function decrement($id, $offset = 1)
{
- return $this->{$this->_adapter}->cache_info($type);
+ return $this->{$this->_adapter}->decrement($this->key_prefix.$id, $offset);
}
// ------------------------------------------------------------------------
/**
- * Get Cache Metadata
+ * Clean the cache
*
- * @param mixed key to get cache metadata on
- * @return mixed return value from child method
+ * @return bool TRUE on success, FALSE on failure
*/
- public function get_metadata($id)
+ public function clean()
{
- return $this->{$this->_adapter}->get_metadata($id);
+ return $this->{$this->_adapter}->clean();
}
// ------------------------------------------------------------------------
/**
- * Initialize
- *
- * Initialize class properties based on the configuration array.
+ * Cache Info
*
- * @param array
- * @return void
+ * @param string $type = 'user' user/filehits
+ * @return mixed array containing cache info on success OR FALSE on failure
*/
- private function _initialize($config)
+ public function cache_info($type = 'user')
{
- $default_config = array(
- 'adapter',
- 'memcached'
- );
-
- foreach ($default_config as $key)
- {
- if (isset($config[$key]))
- {
- $param = '_'.$key;
+ return $this->{$this->_adapter}->cache_info($type);
+ }
- $this->{$param} = $config[$key];
- }
- }
+ // ------------------------------------------------------------------------
- if (isset($config['backup']))
- {
- if (in_array('cache_'.$config['backup'], $this->valid_drivers))
- {
- $this->_backup_driver = $config['backup'];
- }
- }
+ /**
+ * Get Cache Metadata
+ *
+ * @param string $id key to get cache metadata on
+ * @return mixed cache item metadata
+ */
+ public function get_metadata($id)
+ {
+ return $this->{$this->_adapter}->get_metadata($this->key_prefix.$id);
}
// ------------------------------------------------------------------------
@@ -173,14 +240,14 @@ class CI_Cache extends CI_Driver_Library {
/**
* Is the requested driver supported in this environment?
*
- * @param string The driver to test.
- * @return array
+ * @param string $driver The driver to test
+ * @return array
*/
public function is_supported($driver)
{
- static $support = array();
+ static $support;
- if ( ! isset($support[$driver]))
+ if ( ! isset($support, $support[$driver]))
{
$support[$driver] = $this->{$driver}->is_supported();
}
@@ -191,24 +258,12 @@ class CI_Cache extends CI_Driver_Library {
// ------------------------------------------------------------------------
/**
- * __get()
+ * Get currently loaded driver
*
- * @param child
- * @return object
+ * @return string
*/
- public function __get($child)
+ public function get_loaded_driver()
{
- $obj = parent::__get($child);
-
- if ( ! $this->is_supported($child))
- {
- $this->_adapter = $this->_backup_driver;
- }
-
- return $obj;
+ return $this->_adapter;
}
-
}
-
-/* End of file Cache.php */
-/* Location: ./system/libraries/Cache/Cache.php */ \ No newline at end of file
diff --git a/system/libraries/Cache/drivers/Cache_apc.php b/system/libraries/Cache/drivers/Cache_apc.php
index fdc740138..229920449 100644
--- a/system/libraries/Cache/drivers/Cache_apc.php
+++ b/system/libraries/Cache/drivers/Cache_apc.php
@@ -1,19 +1,42 @@
-<?php if ( ! defined('BASEPATH')) exit('No direct script access allowed');
+<?php
/**
* CodeIgniter
*
- * An open source application development framework for PHP 5.1.6 or newer
+ * An open source application development framework for PHP
*
- * @package CodeIgniter
- * @author ExpressionEngine Dev Team
- * @copyright Copyright (c) 2006 - 2014 EllisLab, Inc.
- * @license http://codeigniter.com/user_guide/license.html
- * @link http://codeigniter.com
- * @since Version 2.0
+ * This content is released under the MIT License (MIT)
+ *
+ * Copyright (c) 2019 - 2022, CodeIgniter Foundation
+ *
+ * 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.
+ *
+ * @package CodeIgniter
+ * @author EllisLab Dev Team
+ * @copyright Copyright (c) 2008 - 2014, EllisLab, Inc. (https://ellislab.com/)
+ * @copyright Copyright (c) 2014 - 2019, British Columbia Institute of Technology (https://bcit.ca/)
+ * @copyright Copyright (c) 2019 - 2022, CodeIgniter Foundation (https://codeigniter.com/)
+ * @license https://opensource.org/licenses/MIT MIT License
+ * @link https://codeigniter.com
+ * @since Version 2.0.0
* @filesource
*/
-
-// ------------------------------------------------------------------------
+defined('BASEPATH') OR exit('No direct script access allowed');
/**
* CodeIgniter APC Caching Class
@@ -21,26 +44,44 @@
* @package CodeIgniter
* @subpackage Libraries
* @category Core
- * @author ExpressionEngine Dev Team
+ * @author EllisLab Dev Team
* @link
*/
-
class CI_Cache_apc extends CI_Driver {
/**
+ * Class constructor
+ *
+ * Only present so that an error message is logged
+ * if APC is not available.
+ *
+ * @return void
+ */
+ public function __construct()
+ {
+ if ( ! $this->is_supported())
+ {
+ log_message('error', 'Cache: Failed to initialize APC; extension not loaded/enabled?');
+ }
+ }
+
+ // ------------------------------------------------------------------------
+
+ /**
* Get
*
- * Look for a value in the cache. If it exists, return the data
+ * Look for a value in the cache. If it exists, return the data
* if not, return FALSE
*
- * @param string
- * @return mixed value that is stored/FALSE on failure
+ * @param string
+ * @return mixed value that is stored/FALSE on failure
*/
public function get($id)
{
- $data = apc_fetch($id);
+ $success = FALSE;
+ $data = apc_fetch($id, $success);
- return (is_array($data)) ? $data[0] : FALSE;
+ return ($success === TRUE) ? $data : FALSE;
}
// ------------------------------------------------------------------------
@@ -48,15 +89,15 @@ class CI_Cache_apc extends CI_Driver {
/**
* Cache Save
*
- * @param string Unique Key
- * @param mixed Data to store
- * @param int Length of time (in seconds) to cache the data
- *
- * @return boolean true on success/false on failure
+ * @param string $id Cache ID
+ * @param mixed $data Data to store
+ * @param int $ttl Length of time (in seconds) to cache the data
+ * @param bool $raw Whether to store the raw value (unused)
+ * @return bool TRUE on success, FALSE on failure
*/
- public function save($id, $data, $ttl = 60)
+ public function save($id, $data, $ttl = 60, $raw = FALSE)
{
- return apc_store($id, array($data, time(), $ttl), $ttl);
+ return apc_store($id, $data, (int) $ttl);
}
// ------------------------------------------------------------------------
@@ -64,8 +105,8 @@ class CI_Cache_apc extends CI_Driver {
/**
* Delete from Cache
*
- * @param mixed unique identifier of the item in the cache
- * @param boolean true on success/false on failure
+ * @param mixed unique identifier of the item in the cache
+ * @return bool true on success/false on failure
*/
public function delete($id)
{
@@ -75,9 +116,37 @@ class CI_Cache_apc extends CI_Driver {
// ------------------------------------------------------------------------
/**
+ * Increment a raw value
+ *
+ * @param string $id Cache ID
+ * @param int $offset Step/value to add
+ * @return mixed New value on success or FALSE on failure
+ */
+ public function increment($id, $offset = 1)
+ {
+ return apc_inc($id, $offset);
+ }
+
+ // ------------------------------------------------------------------------
+
+ /**
+ * Decrement a raw value
+ *
+ * @param string $id Cache ID
+ * @param int $offset Step/value to reduce by
+ * @return mixed New value on success or FALSE on failure
+ */
+ public function decrement($id, $offset = 1)
+ {
+ return apc_dec($id, $offset);
+ }
+
+ // ------------------------------------------------------------------------
+
+ /**
* Clean the cache
*
- * @return boolean false on failure/true on success
+ * @return bool false on failure/true on success
*/
public function clean()
{
@@ -89,8 +158,8 @@ class CI_Cache_apc extends CI_Driver {
/**
* Cache Info
*
- * @param string user/filehits
- * @return mixed array on success, false on failure
+ * @param string user/filehits
+ * @return mixed array on success, false on failure
*/
public function cache_info($type = NULL)
{
@@ -102,25 +171,35 @@ class CI_Cache_apc extends CI_Driver {
/**
* Get Cache Metadata
*
- * @param mixed key to get cache metadata on
- * @return mixed array on success/false on failure
+ * @param mixed key to get cache metadata on
+ * @return mixed array on success/false on failure
*/
public function get_metadata($id)
{
- $stored = apc_fetch($id);
-
- if (count($stored) !== 3)
+ $cache_info = apc_cache_info('user', FALSE);
+ if (empty($cache_info) OR empty($cache_info['cache_list']))
{
return FALSE;
}
- list($data, $time, $ttl) = $stored;
+ foreach ($cache_info['cache_list'] as &$entry)
+ {
+ if ($entry['info'] !== $id)
+ {
+ continue;
+ }
+
+ $success = FALSE;
+ $metadata = array(
+ 'expire' => ($entry['ttl'] ? $entry['mtime'] + $entry['ttl'] : 0),
+ 'mtime' => $entry['ttl'],
+ 'data' => apc_fetch($id, $success)
+ );
+
+ return ($success === TRUE) ? $metadata : FALSE;
+ }
- return array(
- 'expire' => $time + $ttl,
- 'mtime' => $time,
- 'data' => $data
- );
+ return FALSE;
}
// ------------------------------------------------------------------------
@@ -129,19 +208,11 @@ class CI_Cache_apc extends CI_Driver {
* is_supported()
*
* Check to see if APC is available on this system, bail if it isn't.
+ *
+ * @return bool
*/
public function is_supported()
{
- if ( ! extension_loaded('apc') OR ini_get('apc.enabled') != "1")
- {
- log_message('error', 'The APC PHP extension must be loaded to use APC Cache.');
- return FALSE;
- }
-
- return TRUE;
+ return (extension_loaded('apc') && ini_get('apc.enabled'));
}
-
}
-
-/* End of file Cache_apc.php */
-/* Location: ./system/libraries/Cache/drivers/Cache_apc.php */ \ No newline at end of file
diff --git a/system/libraries/Cache/drivers/Cache_apcu.php b/system/libraries/Cache/drivers/Cache_apcu.php
new file mode 100644
index 000000000..01f80e79b
--- /dev/null
+++ b/system/libraries/Cache/drivers/Cache_apcu.php
@@ -0,0 +1,219 @@
+<?php
+/**
+ * CodeIgniter
+ *
+ * An open source application development framework for PHP
+ *
+ * This content is released under the MIT License (MIT)
+ *
+ * Copyright (c) 2014 - 2019, British Columbia Institute of Technology
+ *
+ * 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.
+ *
+ * @package CodeIgniter
+ * @author EllisLab Dev Team
+ * @copyright Copyright (c) 2008 - 2014, EllisLab, Inc. (https://ellislab.com/)
+ * @copyright Copyright (c) 2014 - 2019, British Columbia Institute of Technology (https://bcit.ca/)
+ * @license https://opensource.org/licenses/MIT MIT License
+ * @link https://codeigniter.com
+ * @since Version 3.2.0
+ * @filesource
+ */
+defined('BASEPATH') OR exit('No direct script access allowed');
+
+/**
+ * CodeIgniter APCu Caching Class
+ *
+ * @package CodeIgniter
+ * @subpackage Libraries
+ * @category Core
+ * @author CodeIgniter Dev team
+ */
+class CI_Cache_apcu extends CI_Driver {
+
+ /**
+ * Class constructor
+ *
+ * Only present so that an error message is logged
+ * if APCu is not available.
+ *
+ * @return void
+ */
+ public function __construct()
+ {
+ if ( ! $this->is_supported())
+ {
+ log_message('error', 'Cache: Failed to initialize APCu; extension not loaded/enabled?');
+ }
+ }
+
+ // ------------------------------------------------------------------------
+
+ /**
+ * Get
+ *
+ * Look for a value in the cache. If it exists, return the data
+ * if not, return FALSE
+ *
+ * @param string
+ * @return mixed value that is stored/FALSE on failure
+ */
+ public function get($id)
+ {
+ $success = FALSE;
+ $data = apcu_fetch($id, $success);
+
+ if ($success === TRUE)
+ {
+ return is_array($data)
+ ? $data[0]
+ : $data;
+ }
+
+ return FALSE;
+ }
+
+ // ------------------------------------------------------------------------
+
+ /**
+ * Cache Save
+ *
+ * @param string $id Cache ID
+ * @param mixed $data Data to store
+ * @param int $ttl Length of time (in seconds) to cache the data
+ * @param bool $raw Whether to store the raw value
+ * @return bool TRUE on success, FALSE on failure
+ */
+ public function save($id, $data, $ttl = 60, $raw = FALSE)
+ {
+ $ttl = (int) $ttl;
+
+ return apcu_store(
+ $id,
+ ($raw === TRUE ? $data : array($data, time(), $ttl)),
+ $ttl
+ );
+ }
+
+ // ------------------------------------------------------------------------
+
+ /**
+ * Delete from Cache
+ *
+ * @param mixed unique identifier of the item in the cache
+ * @return bool true on success/false on failure
+ */
+ public function delete($id)
+ {
+ return apcu_delete($id);
+ }
+
+ // ------------------------------------------------------------------------
+
+ /**
+ * Increment a raw value
+ *
+ * @param string $id Cache ID
+ * @param int $offset Step/value to add
+ * @return mixed New value on success or FALSE on failure
+ */
+ public function increment($id, $offset = 1)
+ {
+ return apcu_inc($id, $offset);
+ }
+
+ // ------------------------------------------------------------------------
+
+ /**
+ * Decrement a raw value
+ *
+ * @param string $id Cache ID
+ * @param int $offset Step/value to reduce by
+ * @return mixed New value on success or FALSE on failure
+ */
+ public function decrement($id, $offset = 1)
+ {
+ return apcu_dec($id, $offset);
+ }
+
+ // ------------------------------------------------------------------------
+
+ /**
+ * Clean the cache
+ *
+ * @return bool false on failure/true on success
+ */
+ public function clean()
+ {
+ return apcu_clear_cache();
+ }
+
+ // ------------------------------------------------------------------------
+
+ /**
+ * Cache Info
+ *
+ * @return mixed array on success, false on failure
+ */
+ public function cache_info()
+ {
+ return apcu_cache_info();
+ }
+
+ // ------------------------------------------------------------------------
+
+ /**
+ * Get Cache Metadata
+ *
+ * @param mixed key to get cache metadata on
+ * @return mixed array on success/false on failure
+ */
+ public function get_metadata($id)
+ {
+ $success = FALSE;
+ $stored = apcu_fetch($id, $success);
+
+ if ($success === FALSE OR count($stored) !== 3)
+ {
+ return FALSE;
+ }
+
+ list($data, $time, $ttl) = $stored;
+
+ return array(
+ 'expire' => $time + $ttl,
+ 'mtime' => $time,
+ 'data' => $data
+ );
+ }
+
+ // ------------------------------------------------------------------------
+
+ /**
+ * is_supported()
+ *
+ * Check to see if APCu is available on this system, bail if it isn't.
+ *
+ * @return bool
+ */
+ public function is_supported()
+ {
+ return (extension_loaded('apcu') && ini_get('apc.enabled'));
+ }
+} \ No newline at end of file
diff --git a/system/libraries/Cache/drivers/Cache_dummy.php b/system/libraries/Cache/drivers/Cache_dummy.php
index 6c38e91ad..f3ca220f6 100644
--- a/system/libraries/Cache/drivers/Cache_dummy.php
+++ b/system/libraries/Cache/drivers/Cache_dummy.php
@@ -1,19 +1,42 @@
-<?php if ( ! defined('BASEPATH')) exit('No direct script access allowed');
+<?php
/**
* CodeIgniter
*
- * An open source application development framework for PHP 5.1.6 or newer
+ * An open source application development framework for PHP
*
- * @package CodeIgniter
- * @author ExpressionEngine Dev Team
- * @copyright Copyright (c) 2006 - 2014 EllisLab, Inc.
- * @license http://codeigniter.com/user_guide/license.html
- * @link http://codeigniter.com
- * @since Version 2.0
+ * This content is released under the MIT License (MIT)
+ *
+ * Copyright (c) 2019 - 2022, CodeIgniter Foundation
+ *
+ * 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.
+ *
+ * @package CodeIgniter
+ * @author EllisLab Dev Team
+ * @copyright Copyright (c) 2008 - 2014, EllisLab, Inc. (https://ellislab.com/)
+ * @copyright Copyright (c) 2014 - 2019, British Columbia Institute of Technology (https://bcit.ca/)
+ * @copyright Copyright (c) 2019 - 2022, CodeIgniter Foundation (https://codeigniter.com/)
+ * @license https://opensource.org/licenses/MIT MIT License
+ * @link https://codeigniter.com
+ * @since Version 2.0
* @filesource
*/
-
-// ------------------------------------------------------------------------
+defined('BASEPATH') OR exit('No direct script access allowed');
/**
* CodeIgniter Dummy Caching Class
@@ -21,10 +44,9 @@
* @package CodeIgniter
* @subpackage Libraries
* @category Core
- * @author ExpressionEngine Dev Team
+ * @author EllisLab Dev Team
* @link
*/
-
class CI_Cache_dummy extends CI_Driver {
/**
@@ -32,8 +54,8 @@ class CI_Cache_dummy extends CI_Driver {
*
* Since this is the dummy class, it's always going to return FALSE.
*
- * @param string
- * @return Boolean FALSE
+ * @param string
+ * @return bool FALSE
*/
public function get($id)
{
@@ -45,13 +67,13 @@ class CI_Cache_dummy extends CI_Driver {
/**
* Cache Save
*
- * @param string Unique Key
- * @param mixed Data to store
- * @param int Length of time (in seconds) to cache the data
- *
- * @return boolean TRUE, Simulating success
+ * @param string Unique Key
+ * @param mixed Data to store
+ * @param int Length of time (in seconds) to cache the data
+ * @param bool Whether to store the raw value
+ * @return bool TRUE, Simulating success
*/
- public function save($id, $data, $ttl = 60)
+ public function save($id, $data, $ttl = 60, $raw = FALSE)
{
return TRUE;
}
@@ -61,8 +83,8 @@ class CI_Cache_dummy extends CI_Driver {
/**
* Delete from Cache
*
- * @param mixed unique identifier of the item in the cache
- * @param boolean TRUE, simulating success
+ * @param mixed unique identifier of the item in the cache
+ * @return bool TRUE, simulating success
*/
public function delete($id)
{
@@ -72,9 +94,37 @@ class CI_Cache_dummy extends CI_Driver {
// ------------------------------------------------------------------------
/**
+ * Increment a raw value
+ *
+ * @param string $id Cache ID
+ * @param int $offset Step/value to add
+ * @return mixed New value on success or FALSE on failure
+ */
+ public function increment($id, $offset = 1)
+ {
+ return TRUE;
+ }
+
+ // ------------------------------------------------------------------------
+
+ /**
+ * Decrement a raw value
+ *
+ * @param string $id Cache ID
+ * @param int $offset Step/value to reduce by
+ * @return mixed New value on success or FALSE on failure
+ */
+ public function decrement($id, $offset = 1)
+ {
+ return TRUE;
+ }
+
+ // ------------------------------------------------------------------------
+
+ /**
* Clean the cache
*
- * @return boolean TRUE, simulating success
+ * @return bool TRUE, simulating success
*/
public function clean()
{
@@ -86,21 +136,21 @@ class CI_Cache_dummy extends CI_Driver {
/**
* Cache Info
*
- * @param string user/filehits
- * @return boolean FALSE
+ * @param string user/filehits
+ * @return bool FALSE
*/
- public function cache_info($type = NULL)
- {
- return FALSE;
- }
+ public function cache_info($type = NULL)
+ {
+ return FALSE;
+ }
// ------------------------------------------------------------------------
/**
* Get Cache Metadata
*
- * @param mixed key to get cache metadata on
- * @return boolean FALSE
+ * @param mixed key to get cache metadata on
+ * @return bool FALSE
*/
public function get_metadata($id)
{
@@ -113,7 +163,7 @@ class CI_Cache_dummy extends CI_Driver {
* Is this caching driver supported on the system?
* Of course this one is.
*
- * @return TRUE;
+ * @return bool TRUE
*/
public function is_supported()
{
@@ -121,6 +171,3 @@ class CI_Cache_dummy extends CI_Driver {
}
}
-
-/* End of file Cache_dummy.php */
-/* Location: ./system/libraries/Cache/drivers/Cache_dummy.php */ \ No newline at end of file
diff --git a/system/libraries/Cache/drivers/Cache_file.php b/system/libraries/Cache/drivers/Cache_file.php
index 2f250e764..3a4be98a9 100644
--- a/system/libraries/Cache/drivers/Cache_file.php
+++ b/system/libraries/Cache/drivers/Cache_file.php
@@ -1,45 +1,72 @@
-<?php if ( ! defined('BASEPATH')) exit('No direct script access allowed');
+<?php
/**
* CodeIgniter
*
- * An open source application development framework for PHP 5.1.6 or newer
+ * An open source application development framework for PHP
*
- * @package CodeIgniter
- * @author ExpressionEngine Dev Team
- * @copyright Copyright (c) 2006 - 2014 EllisLab, Inc.
- * @license http://codeigniter.com/user_guide/license.html
- * @link http://codeigniter.com
- * @since Version 2.0
+ * This content is released under the MIT License (MIT)
+ *
+ * Copyright (c) 2019 - 2022, CodeIgniter Foundation
+ *
+ * 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.
+ *
+ * @package CodeIgniter
+ * @author EllisLab Dev Team
+ * @copyright Copyright (c) 2008 - 2014, EllisLab, Inc. (https://ellislab.com/)
+ * @copyright Copyright (c) 2014 - 2019, British Columbia Institute of Technology (https://bcit.ca/)
+ * @copyright Copyright (c) 2019 - 2022, CodeIgniter Foundation (https://codeigniter.com/)
+ * @license https://opensource.org/licenses/MIT MIT License
+ * @link https://codeigniter.com
+ * @since Version 2.0
* @filesource
*/
-
-// ------------------------------------------------------------------------
+defined('BASEPATH') OR exit('No direct script access allowed');
/**
- * CodeIgniter Memcached Caching Class
+ * CodeIgniter File Caching Class
*
* @package CodeIgniter
* @subpackage Libraries
* @category Core
- * @author ExpressionEngine Dev Team
+ * @author EllisLab Dev Team
* @link
*/
-
class CI_Cache_file extends CI_Driver {
+ /**
+ * Directory in which to save cache files
+ *
+ * @var string
+ */
protected $_cache_path;
/**
- * Constructor
+ * Initialize file-based cache
+ *
+ * @return void
*/
public function __construct()
{
$CI =& get_instance();
$CI->load->helper('file');
-
$path = $CI->config->item('cache_path');
-
- $this->_cache_path = ($path == '') ? APPPATH.'cache/' : $path;
+ $this->_cache_path = ($path === '') ? APPPATH.'cache/' : $path;
}
// ------------------------------------------------------------------------
@@ -47,26 +74,13 @@ class CI_Cache_file extends CI_Driver {
/**
* Fetch from cache
*
- * @param mixed unique key id
- * @return mixed data on success/false on failure
+ * @param string $id Cache ID
+ * @return mixed Data on success, FALSE on failure
*/
public function get($id)
{
- if ( ! file_exists($this->_cache_path.$id))
- {
- return FALSE;
- }
-
- $data = read_file($this->_cache_path.$id);
- $data = unserialize($data);
-
- if (time() > $data['time'] + $data['ttl'])
- {
- $this->delete($id);
- return FALSE;
- }
-
- return $data['data'];
+ $data = $this->_get($id);
+ return is_array($data) ? $data['data'] : FALSE;
}
// ------------------------------------------------------------------------
@@ -74,23 +88,23 @@ class CI_Cache_file extends CI_Driver {
/**
* Save into cache
*
- * @param string unique key
- * @param mixed data to store
- * @param int length of time (in seconds) the cache is valid
- * - Default is 60 seconds
- * @return boolean true on success/false on failure
+ * @param string $id Cache ID
+ * @param mixed $data Data to store
+ * @param int $ttl Time to live in seconds
+ * @param bool $raw Whether to store the raw value (unused)
+ * @return bool TRUE on success, FALSE on failure
*/
- public function save($id, $data, $ttl = 60)
+ public function save($id, $data, $ttl = 60, $raw = FALSE)
{
$contents = array(
- 'time' => time(),
- 'ttl' => $ttl,
- 'data' => $data
- );
+ 'time' => time(),
+ 'ttl' => $ttl,
+ 'data' => $data
+ );
if (write_file($this->_cache_path.$id, serialize($contents)))
{
- @chmod($this->_cache_path.$id, 0777);
+ chmod($this->_cache_path.$id, 0640);
return TRUE;
}
@@ -102,16 +116,68 @@ class CI_Cache_file extends CI_Driver {
/**
* Delete from Cache
*
- * @param mixed unique identifier of item in cache
- * @return boolean true on success/false on failure
+ * @param mixed unique identifier of item in cache
+ * @return bool true on success/false on failure
*/
public function delete($id)
{
- try {
- return unlink($this->_cache_path.$id);
- } catch (\ErrorException $e) {
- return false;
+ return is_file($this->_cache_path.$id) ? unlink($this->_cache_path.$id) : FALSE;
+ }
+
+ // ------------------------------------------------------------------------
+
+ /**
+ * Increment a raw value
+ *
+ * @param string $id Cache ID
+ * @param int $offset Step/value to add
+ * @return New value on success, FALSE on failure
+ */
+ public function increment($id, $offset = 1)
+ {
+ $data = $this->_get($id);
+
+ if ($data === FALSE)
+ {
+ $data = array('data' => 0, 'ttl' => 60);
+ }
+ elseif ( ! is_int($data['data']))
+ {
+ return FALSE;
+ }
+
+ $new_value = $data['data'] + $offset;
+ return $this->save($id, $new_value, $data['ttl'])
+ ? $new_value
+ : FALSE;
+ }
+
+ // ------------------------------------------------------------------------
+
+ /**
+ * Decrement a raw value
+ *
+ * @param string $id Cache ID
+ * @param int $offset Step/value to reduce by
+ * @return New value on success, FALSE on failure
+ */
+ public function decrement($id, $offset = 1)
+ {
+ $data = $this->_get($id);
+
+ if ($data === FALSE)
+ {
+ $data = array('data' => 0, 'ttl' => 60);
}
+ elseif ( ! is_int($data['data']))
+ {
+ return FALSE;
+ }
+
+ $new_value = $data['data'] - $offset;
+ return $this->save($id, $new_value, $data['ttl'])
+ ? $new_value
+ : FALSE;
}
// ------------------------------------------------------------------------
@@ -119,11 +185,11 @@ class CI_Cache_file extends CI_Driver {
/**
* Clean the Cache
*
- * @return boolean false on failure/true on success
+ * @return bool false on failure/true on success
*/
public function clean()
{
- return delete_files($this->_cache_path);
+ return delete_files($this->_cache_path, FALSE, TRUE);
}
// ------------------------------------------------------------------------
@@ -133,8 +199,8 @@ class CI_Cache_file extends CI_Driver {
*
* Not supported by file-based caching
*
- * @param string user/filehits
- * @return mixed FALSE
+ * @param string user/filehits
+ * @return mixed FALSE
*/
public function cache_info($type = NULL)
{
@@ -146,31 +212,30 @@ class CI_Cache_file extends CI_Driver {
/**
* Get Cache Metadata
*
- * @param mixed key to get cache metadata on
- * @return mixed FALSE on failure, array on success.
+ * @param mixed key to get cache metadata on
+ * @return mixed FALSE on failure, array on success.
*/
public function get_metadata($id)
{
- if ( ! file_exists($this->_cache_path.$id))
+ if ( ! is_file($this->_cache_path.$id))
{
return FALSE;
}
- $data = read_file($this->_cache_path.$id);
- $data = unserialize($data);
+ $data = unserialize(file_get_contents($this->_cache_path.$id));
if (is_array($data))
{
$mtime = filemtime($this->_cache_path.$id);
- if ( ! isset($data['ttl']))
+ if ( ! isset($data['ttl'], $data['time']))
{
return FALSE;
}
return array(
- 'expire' => $mtime + $data['ttl'],
- 'mtime' => $mtime
+ 'expire' => $data['time'] + $data['ttl'],
+ 'mtime' => $mtime
);
}
@@ -184,14 +249,39 @@ class CI_Cache_file extends CI_Driver {
*
* In the file driver, check to see that the cache directory is indeed writable
*
- * @return boolean
+ * @return bool
*/
public function is_supported()
{
return is_really_writable($this->_cache_path);
}
-}
+ // ------------------------------------------------------------------------
+
+ /**
+ * Get all data
+ *
+ * Internal method to get all the relevant data about a cache item
+ *
+ * @param string $id Cache ID
+ * @return mixed Data array on success or FALSE on failure
+ */
+ protected function _get($id)
+ {
+ if ( ! is_file($this->_cache_path.$id))
+ {
+ return FALSE;
+ }
+
+ $data = unserialize(file_get_contents($this->_cache_path.$id));
-/* End of file Cache_file.php */
-/* Location: ./system/libraries/Cache/drivers/Cache_file.php */ \ No newline at end of file
+ if ($data['ttl'] > 0 && time() > $data['time'] + $data['ttl'])
+ {
+ file_exists($this->_cache_path.$id) && unlink($this->_cache_path.$id);
+ return FALSE;
+ }
+
+ return $data;
+ }
+
+}
diff --git a/system/libraries/Cache/drivers/Cache_memcached.php b/system/libraries/Cache/drivers/Cache_memcached.php
index f9d578b93..55963bb82 100644
--- a/system/libraries/Cache/drivers/Cache_memcached.php
+++ b/system/libraries/Cache/drivers/Cache_memcached.php
@@ -1,19 +1,42 @@
-<?php if ( ! defined('BASEPATH')) exit('No direct script access allowed');
+<?php
/**
* CodeIgniter
*
- * An open source application development framework for PHP 5.1.6 or newer
+ * An open source application development framework for PHP
*
- * @package CodeIgniter
- * @author ExpressionEngine Dev Team
- * @copyright Copyright (c) 2006 - 2014 EllisLab, Inc.
- * @license http://codeigniter.com/user_guide/license.html
- * @link http://codeigniter.com
- * @since Version 2.0
+ * This content is released under the MIT License (MIT)
+ *
+ * Copyright (c) 2019 - 2022, CodeIgniter Foundation
+ *
+ * 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.
+ *
+ * @package CodeIgniter
+ * @author EllisLab Dev Team
+ * @copyright Copyright (c) 2008 - 2014, EllisLab, Inc. (https://ellislab.com/)
+ * @copyright Copyright (c) 2014 - 2019, British Columbia Institute of Technology (https://bcit.ca/)
+ * @copyright Copyright (c) 2019 - 2022, CodeIgniter Foundation (https://codeigniter.com/)
+ * @license https://opensource.org/licenses/MIT MIT License
+ * @link https://codeigniter.com
+ * @since Version 2.0
* @filesource
*/
-
-// ------------------------------------------------------------------------
+defined('BASEPATH') OR exit('No direct script access allowed');
/**
* CodeIgniter Memcached Caching Class
@@ -21,35 +44,117 @@
* @package CodeIgniter
* @subpackage Libraries
* @category Core
- * @author ExpressionEngine Dev Team
+ * @author EllisLab Dev Team
* @link
*/
-
class CI_Cache_memcached extends CI_Driver {
- private $_memcached; // Holds the memcached object
+ /**
+ * Holds the memcached object
+ *
+ * @var object
+ */
+ protected $_memcached;
+
+ /**
+ * Memcached configuration
+ *
+ * @var array
+ */
+ protected $_config = array(
+ 'default' => array(
+ 'host' => '127.0.0.1',
+ 'port' => 11211,
+ 'weight' => 1
+ )
+ );
+
+ // ------------------------------------------------------------------------
+
+ /**
+ * Class constructor
+ *
+ * Setup Memcache(d)
+ *
+ * @return void
+ */
+ public function __construct()
+ {
+ // Try to load memcached server info from the config file.
+ $CI =& get_instance();
+ $defaults = $this->_config['default'];
+
+ if ($CI->config->load('memcached', TRUE, TRUE))
+ {
+ $this->_config = $CI->config->config['memcached'];
+ }
- protected $_memcache_conf = array(
- 'default' => array(
- 'default_host' => '127.0.0.1',
- 'default_port' => 11211,
- 'default_weight' => 1
- )
+ if (class_exists('Memcached', FALSE))
+ {
+ $this->_memcached = new Memcached();
+ }
+ elseif (class_exists('Memcache', FALSE))
+ {
+ $this->_memcached = new Memcache();
+ }
+ else
+ {
+ log_message('error', 'Cache: Failed to create Memcache(d) object; extension not loaded?');
+ return;
+ }
+
+ foreach ($this->_config as $cache_name => $cache_server)
+ {
+ if ( ! isset($cache_server['hostname']))
+ {
+ log_message('debug', 'Cache: Memcache(d) configuration "'.$cache_name.'" doesn\'t include a hostname; ignoring.');
+ continue;
+ }
+ elseif ($cache_server['hostname'][0] === '/')
+ {
+ $cache_server['port'] = 0;
+ }
+ elseif (empty($cache_server['port']))
+ {
+ $cache_server['port'] = $defaults['port'];
+ }
+
+ isset($cache_server['weight']) OR $cache_server['weight'] = $defaults['weight'];
+
+ if ($this->_memcached instanceof Memcache)
+ {
+ // Third parameter is persistence and defaults to TRUE.
+ $this->_memcached->addServer(
+ $cache_server['hostname'],
+ $cache_server['port'],
+ TRUE,
+ $cache_server['weight']
);
+ }
+ elseif ($this->_memcached instanceof Memcached)
+ {
+ $this->_memcached->addServer(
+ $cache_server['hostname'],
+ $cache_server['port'],
+ $cache_server['weight']
+ );
+ }
+ }
+ }
// ------------------------------------------------------------------------
/**
* Fetch from cache
*
- * @param mixed unique key id
- * @return mixed data on success/false on failure
+ * @param string $id Cache ID
+ * @return mixed Data on success, FALSE on failure
*/
public function get($id)
{
$data = $this->_memcached->get($id);
- return (is_array($data)) ? $data[0] : FALSE;
+ return is_array($data) ? $data[0] : $data;
}
// ------------------------------------------------------------------------
@@ -57,20 +162,26 @@ class CI_Cache_memcached extends CI_Driver {
/**
* Save
*
- * @param string unique identifier
- * @param mixed data being cached
- * @param int time to live
- * @return boolean true on success, false on failure
+ * @param string $id Cache ID
+ * @param mixed $data Data being cached
+ * @param int $ttl Time to live
+ * @param bool $raw Whether to store the raw value
+ * @return bool TRUE on success, FALSE on failure
*/
- public function save($id, $data, $ttl = 60)
+ public function save($id, $data, $ttl = 60, $raw = FALSE)
{
- if (get_class($this->_memcached) == 'Memcached')
+ if ($raw !== TRUE)
{
- return $this->_memcached->set($id, array($data, time(), $ttl), $ttl);
+ $data = array($data, time(), $ttl);
}
- else if (get_class($this->_memcached) == 'Memcache')
+
+ if ($this->_memcached instanceof Memcached)
{
- return $this->_memcached->set($id, array($data, time(), $ttl), 0, $ttl);
+ return $this->_memcached->set($id, $data, $ttl);
+ }
+ elseif ($this->_memcached instanceof Memcache)
+ {
+ return $this->_memcached->set($id, $data, 0, $ttl);
}
return FALSE;
@@ -81,8 +192,8 @@ class CI_Cache_memcached extends CI_Driver {
/**
* Delete from Cache
*
- * @param mixed key to be deleted.
- * @return boolean true on success, false on failure
+ * @param mixed $id key to be deleted.
+ * @return bool true on success, false on failure
*/
public function delete($id)
{
@@ -92,9 +203,47 @@ class CI_Cache_memcached extends CI_Driver {
// ------------------------------------------------------------------------
/**
+ * Increment a raw value
+ *
+ * @param string $id Cache ID
+ * @param int $offset Step/value to add
+ * @return mixed New value on success or FALSE on failure
+ */
+ public function increment($id, $offset = 1)
+ {
+ if (($result = $this->_memcached->increment($id, $offset)) === FALSE)
+ {
+ return $this->_memcached->add($id, $offset) ? $offset : FALSE;
+ }
+
+ return $result;
+ }
+
+ // ------------------------------------------------------------------------
+
+ /**
+ * Decrement a raw value
+ *
+ * @param string $id Cache ID
+ * @param int $offset Step/value to reduce by
+ * @return mixed New value on success or FALSE on failure
+ */
+ public function decrement($id, $offset = 1)
+ {
+ if (($result = $this->_memcached->decrement($id, $offset)) === FALSE)
+ {
+ return $this->_memcached->add($id, 0) ? 0 : FALSE;
+ }
+
+ return $result;
+ }
+
+ // ------------------------------------------------------------------------
+
+ /**
* Clean the Cache
*
- * @return boolean false on failure/true on success
+ * @return bool false on failure/true on success
*/
public function clean()
{
@@ -106,10 +255,9 @@ class CI_Cache_memcached extends CI_Driver {
/**
* Cache Info
*
- * @param null type not supported in memcached
- * @return mixed array on success, false on failure
+ * @return mixed array on success, false on failure
*/
- public function cache_info($type = NULL)
+ public function cache_info()
{
return $this->_memcached->getStats();
}
@@ -119,8 +267,8 @@ class CI_Cache_memcached extends CI_Driver {
/**
* Get Cache Metadata
*
- * @param mixed key to get cache metadata on
- * @return mixed FALSE on failure, array on success.
+ * @param mixed $id key to get cache metadata on
+ * @return mixed FALSE on failure, array on success.
*/
public function get_metadata($id)
{
@@ -143,72 +291,36 @@ class CI_Cache_memcached extends CI_Driver {
// ------------------------------------------------------------------------
/**
- * Setup memcached.
+ * Is supported
+ *
+ * Returns FALSE if memcached is not supported on the system.
+ * If it is, we setup the memcached object & return TRUE
+ *
+ * @return bool
*/
- private function _setup_memcached()
+ public function is_supported()
{
- // Try to load memcached server info from the config file.
- $CI =& get_instance();
- if ($CI->config->load('memcached', TRUE, TRUE))
- {
- if (is_array($CI->config->config['memcached']))
- {
- $this->_memcache_conf = NULL;
-
- foreach ($CI->config->config['memcached'] as $name => $conf)
- {
- $this->_memcache_conf[$name] = $conf;
- }
- }
- }
-
- $this->_memcached = new Memcached();
-
- foreach ($this->_memcache_conf as $name => $cache_server)
- {
- if ( ! array_key_exists('hostname', $cache_server))
- {
- $cache_server['hostname'] = $this->_default_options['default_host'];
- }
-
- if ( ! array_key_exists('port', $cache_server))
- {
- $cache_server['port'] = $this->_default_options['default_port'];
- }
-
- if ( ! array_key_exists('weight', $cache_server))
- {
- $cache_server['weight'] = $this->_default_options['default_weight'];
- }
-
- $this->_memcached->addServer(
- $cache_server['hostname'], $cache_server['port'], $cache_server['weight']
- );
- }
+ return (extension_loaded('memcached') OR extension_loaded('memcache'));
}
// ------------------------------------------------------------------------
-
/**
- * Is supported
+ * Class destructor
*
- * Returns FALSE if memcached is not supported on the system.
- * If it is, we setup the memcached object & return TRUE
+ * Closes the connection to Memcache(d) if present.
+ *
+ * @return void
*/
- public function is_supported()
+ public function __destruct()
{
- if ( ! extension_loaded('memcached'))
+ if ($this->_memcached instanceof Memcache)
{
- log_message('error', 'The Memcached Extension must be loaded to use Memcached Cache.');
- return FALSE;
+ $this->_memcached->close();
+ }
+ elseif ($this->_memcached instanceof Memcached && method_exists($this->_memcached, 'quit'))
+ {
+ $this->_memcached->quit();
}
-
- $this->_setup_memcached();
- return TRUE;
}
-
}
-
-/* End of file Cache_memcached.php */
-/* Location: ./system/libraries/Cache/drivers/Cache_memcached.php */ \ No newline at end of file
diff --git a/system/libraries/Cache/drivers/Cache_redis.php b/system/libraries/Cache/drivers/Cache_redis.php
new file mode 100644
index 000000000..22af592e7
--- /dev/null
+++ b/system/libraries/Cache/drivers/Cache_redis.php
@@ -0,0 +1,360 @@
+<?php
+/**
+ * CodeIgniter
+ *
+ * An open source application development framework for PHP
+ *
+ * This content is released under the MIT License (MIT)
+ *
+ * Copyright (c) 2019 - 2022, CodeIgniter Foundation
+ *
+ * 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.
+ *
+ * @package CodeIgniter
+ * @author EllisLab Dev Team
+ * @copyright Copyright (c) 2008 - 2014, EllisLab, Inc. (https://ellislab.com/)
+ * @copyright Copyright (c) 2014 - 2019, British Columbia Institute of Technology (https://bcit.ca/)
+ * @copyright Copyright (c) 2019 - 2022, CodeIgniter Foundation (https://codeigniter.com/)
+ * @license https://opensource.org/licenses/MIT MIT License
+ * @link https://codeigniter.com
+ * @since Version 3.0.0
+ * @filesource
+ */
+defined('BASEPATH') OR exit('No direct script access allowed');
+
+/**
+ * CodeIgniter Redis Caching Class
+ *
+ * @package CodeIgniter
+ * @subpackage Libraries
+ * @category Core
+ * @author Anton Lindqvist <anton@qvister.se>
+ * @link
+ */
+class CI_Cache_redis extends CI_Driver
+{
+ /**
+ * Default config
+ *
+ * @static
+ * @var array
+ */
+ protected static $_default_config = array(
+ 'host' => '127.0.0.1',
+ 'password' => NULL,
+ 'port' => 6379,
+ 'timeout' => 0,
+ 'database' => 0
+ );
+
+ /**
+ * Redis connection
+ *
+ * @var Redis
+ */
+ protected $_redis;
+
+ /**
+ * del()/delete() method name depending on phpRedis version
+ *
+ * @var string
+ */
+ protected static $_delete_name;
+
+ /**
+ * sRem()/sRemove() method name depending on phpRedis version
+ *
+ * @var string
+ */
+ protected static $_sRemove_name;
+
+ // ------------------------------------------------------------------------
+
+ /**
+ * Class constructor
+ *
+ * Setup Redis
+ *
+ * Loads Redis config file if present. Will halt execution
+ * if a Redis connection can't be established.
+ *
+ * @return void
+ * @throws RedisException
+ * @see Redis::connect()
+ */
+ public function __construct()
+ {
+ if ( ! $this->is_supported())
+ {
+ log_message('error', 'Cache: Failed to create Redis object; extension not loaded?');
+ return;
+ }
+
+ if ( ! isset(static::$_delete_name, static::$_sRemove_name))
+ {
+ if (version_compare(phpversion('redis'), '5', '>='))
+ {
+ static::$_delete_name = 'del';
+ static::$_sRemove_name = 'sRem';
+ }
+ else
+ {
+ static::$_delete_name = 'delete';
+ static::$_sRemove_name = 'sRemove';
+ }
+ }
+
+ $CI =& get_instance();
+
+ if ($CI->config->load('redis', TRUE, TRUE))
+ {
+ $config = array_merge(self::$_default_config, $CI->config->item('redis'));
+ }
+ else
+ {
+ $config = self::$_default_config;
+ }
+
+ $this->_redis = new Redis();
+
+ // The following calls used to be wrapped in a try ... catch
+ // and just log an error, but that only causes more errors later.
+ if ( ! $this->_redis->connect($config['host'], ($config['host'][0] === '/' ? 0 : $config['port']), $config['timeout']))
+ {
+ log_message('error', 'Cache: Redis connection failed. Check your configuration.');
+ }
+
+ if (isset($config['password']) && ! $this->_redis->auth($config['password']))
+ {
+ log_message('error', 'Cache: Redis authentication failed.');
+ }
+
+ if (isset($config['database']) && $config['database'] > 0 && ! $this->_redis->select($config['database']))
+ {
+ log_message('error', 'Cache: Redis select database failed.');
+ }
+ }
+
+ // ------------------------------------------------------------------------
+
+ /**
+ * Get cache
+ *
+ * @param string $key Cache ID
+ * @return mixed
+ */
+ public function get($key)
+ {
+ $data = $this->_redis->hMGet($key, array('__ci_type', '__ci_value'));
+
+ if ($value !== FALSE && $this->_redis->sIsMember('_ci_redis_serialized', $key))
+ {
+ return FALSE;
+ }
+
+ switch ($data['__ci_type'])
+ {
+ case 'array':
+ case 'object':
+ return unserialize($data['__ci_value']);
+ case 'boolean':
+ case 'integer':
+ case 'double': // Yes, 'double' is returned and NOT 'float'
+ case 'string':
+ case 'NULL':
+ return settype($data['__ci_value'], $data['__ci_type'])
+ ? $data['__ci_value']
+ : FALSE;
+ case 'resource':
+ default:
+ return FALSE;
+ }
+ }
+
+ // ------------------------------------------------------------------------
+
+ /**
+ * Save cache
+ *
+ * @param string $id Cache ID
+ * @param mixed $data Data to save
+ * @param int $ttl Time to live in seconds
+ * @param bool $raw Whether to store the raw value (unused)
+ * @return bool TRUE on success, FALSE on failure
+ */
+ public function save($id, $data, $ttl = 60, $raw = FALSE)
+ {
+ switch ($data_type = gettype($data))
+ {
+ case 'array':
+ case 'object':
+ $data = serialize($data);
+ break;
+ case 'boolean':
+ case 'integer':
+ case 'double': // Yes, 'double' is returned and NOT 'float'
+ case 'string':
+ case 'NULL':
+ break;
+ case 'resource':
+ default:
+ return FALSE;
+ }
+
+ if ( ! $this->_redis->hMSet($id, array('__ci_type' => $data_type, '__ci_value' => $data)))
+ {
+ return FALSE;
+ }
+ else
+ {
+ $this->_redis->{static::$_sRemove_name}('_ci_redis_serialized', $id);
+ }
+
+ return TRUE;
+ }
+
+ // ------------------------------------------------------------------------
+
+ /**
+ * Delete from cache
+ *
+ * @param string $key Cache key
+ * @return bool
+ */
+ public function delete($key)
+ {
+ if ($this->_redis->{static::$_delete_name}($key) !== 1)
+ {
+ return FALSE;
+ }
+
+ $this->_redis->{static::$_sRemove_name}('_ci_redis_serialized', $key);
+
+ return TRUE;
+ }
+
+ // ------------------------------------------------------------------------
+
+ /**
+ * Increment a raw value
+ *
+ * @param string $id Cache ID
+ * @param int $offset Step/value to add
+ * @return mixed New value on success or FALSE on failure
+ */
+ public function increment($id, $offset = 1)
+ {
+ return $this->_redis->incrBy($id, $offset);
+ }
+
+ // ------------------------------------------------------------------------
+
+ /**
+ * Decrement a raw value
+ *
+ * @param string $id Cache ID
+ * @param int $offset Step/value to reduce by
+ * @return mixed New value on success or FALSE on failure
+ */
+ public function decrement($id, $offset = 1)
+ {
+ return $this->_redis->decrBy($id, $offset);
+ }
+
+ // ------------------------------------------------------------------------
+
+ /**
+ * Clean cache
+ *
+ * @return bool
+ * @see Redis::flushDB()
+ */
+ public function clean()
+ {
+ return $this->_redis->flushDB();
+ }
+
+ // ------------------------------------------------------------------------
+
+ /**
+ * Get cache driver info
+ *
+ * @param string $type Not supported in Redis.
+ * Only included in order to offer a
+ * consistent cache API.
+ * @return array
+ * @see Redis::info()
+ */
+ public function cache_info($type = NULL)
+ {
+ return $this->_redis->info();
+ }
+
+ // ------------------------------------------------------------------------
+
+ /**
+ * Get cache metadata
+ *
+ * @param string $key Cache key
+ * @return array
+ */
+ public function get_metadata($key)
+ {
+ $value = $this->get($key);
+
+ if ($value !== FALSE)
+ {
+ return array(
+ 'expire' => time() + $this->_redis->ttl($key),
+ 'data' => $value
+ );
+ }
+
+ return FALSE;
+ }
+
+ // ------------------------------------------------------------------------
+
+ /**
+ * Check if Redis driver is supported
+ *
+ * @return bool
+ */
+ public function is_supported()
+ {
+ return extension_loaded('redis');
+ }
+
+ // ------------------------------------------------------------------------
+
+ /**
+ * Class destructor
+ *
+ * Closes the connection to Redis if present.
+ *
+ * @return void
+ */
+ public function __destruct()
+ {
+ if ($this->_redis)
+ {
+ $this->_redis->close();
+ }
+ }
+}
diff --git a/system/libraries/Cache/drivers/Cache_wincache.php b/system/libraries/Cache/drivers/Cache_wincache.php
new file mode 100644
index 000000000..bd18148f1
--- /dev/null
+++ b/system/libraries/Cache/drivers/Cache_wincache.php
@@ -0,0 +1,218 @@
+<?php
+/**
+ * CodeIgniter
+ *
+ * An open source application development framework for PHP
+ *
+ * This content is released under the MIT License (MIT)
+ *
+ * Copyright (c) 2019 - 2022, CodeIgniter Foundation
+ *
+ * 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.
+ *
+ * @package CodeIgniter
+ * @author EllisLab Dev Team
+ * @copyright Copyright (c) 2008 - 2014, EllisLab, Inc. (https://ellislab.com/)
+ * @copyright Copyright (c) 2014 - 2019, British Columbia Institute of Technology (https://bcit.ca/)
+ * @copyright Copyright (c) 2019 - 2022, CodeIgniter Foundation (https://codeigniter.com/)
+ * @license https://opensource.org/licenses/MIT MIT License
+ * @link https://codeigniter.com
+ * @since Version 3.0.0
+ * @filesource
+ */
+defined('BASEPATH') OR exit('No direct script access allowed');
+
+/**
+ * CodeIgniter Wincache Caching Class
+ *
+ * Read more about Wincache functions here:
+ * https://www.php.net/manual/en/ref.wincache.php
+ *
+ * @package CodeIgniter
+ * @subpackage Libraries
+ * @category Core
+ * @author Mike Murkovic
+ * @link
+ */
+class CI_Cache_wincache extends CI_Driver {
+
+ /**
+ * Class constructor
+ *
+ * Only present so that an error message is logged
+ * if APC is not available.
+ *
+ * @return void
+ */
+ public function __construct()
+ {
+ if ( ! $this->is_supported())
+ {
+ log_message('error', 'Cache: Failed to initialize Wincache; extension not loaded/enabled?');
+ }
+ }
+
+ // ------------------------------------------------------------------------
+
+ /**
+ * Get
+ *
+ * Look for a value in the cache. If it exists, return the data,
+ * if not, return FALSE
+ *
+ * @param string $id Cache Ide
+ * @return mixed Value that is stored/FALSE on failure
+ */
+ public function get($id)
+ {
+ $success = FALSE;
+ $data = wincache_ucache_get($id, $success);
+
+ // Success returned by reference from wincache_ucache_get()
+ return ($success) ? $data : FALSE;
+ }
+
+ // ------------------------------------------------------------------------
+
+ /**
+ * Cache Save
+ *
+ * @param string $id Cache ID
+ * @param mixed $data Data to store
+ * @param int $ttl Time to live (in seconds)
+ * @param bool $raw Whether to store the raw value (unused)
+ * @return bool true on success/false on failure
+ */
+ public function save($id, $data, $ttl = 60, $raw = FALSE)
+ {
+ return wincache_ucache_set($id, $data, $ttl);
+ }
+
+ // ------------------------------------------------------------------------
+
+ /**
+ * Delete from Cache
+ *
+ * @param mixed unique identifier of the item in the cache
+ * @return bool true on success/false on failure
+ */
+ public function delete($id)
+ {
+ return wincache_ucache_delete($id);
+ }
+
+ // ------------------------------------------------------------------------
+
+ /**
+ * Increment a raw value
+ *
+ * @param string $id Cache ID
+ * @param int $offset Step/value to add
+ * @return mixed New value on success or FALSE on failure
+ */
+ public function increment($id, $offset = 1)
+ {
+ $success = FALSE;
+ $value = wincache_ucache_inc($id, $offset, $success);
+
+ return ($success === TRUE) ? $value : FALSE;
+ }
+
+ // ------------------------------------------------------------------------
+
+ /**
+ * Decrement a raw value
+ *
+ * @param string $id Cache ID
+ * @param int $offset Step/value to reduce by
+ * @return mixed New value on success or FALSE on failure
+ */
+ public function decrement($id, $offset = 1)
+ {
+ $success = FALSE;
+ $value = wincache_ucache_dec($id, $offset, $success);
+
+ return ($success === TRUE) ? $value : FALSE;
+ }
+
+ // ------------------------------------------------------------------------
+
+ /**
+ * Clean the cache
+ *
+ * @return bool false on failure/true on success
+ */
+ public function clean()
+ {
+ return wincache_ucache_clear();
+ }
+
+ // ------------------------------------------------------------------------
+
+ /**
+ * Cache Info
+ *
+ * @return mixed array on success, false on failure
+ */
+ public function cache_info()
+ {
+ return wincache_ucache_info(TRUE);
+ }
+
+ // ------------------------------------------------------------------------
+
+ /**
+ * Get Cache Metadata
+ *
+ * @param mixed key to get cache metadata on
+ * @return mixed array on success/false on failure
+ */
+ public function get_metadata($id)
+ {
+ if ($stored = wincache_ucache_info(FALSE, $id))
+ {
+ $age = $stored['ucache_entries'][1]['age_seconds'];
+ $ttl = $stored['ucache_entries'][1]['ttl_seconds'];
+ $hitcount = $stored['ucache_entries'][1]['hitcount'];
+
+ return array(
+ 'expire' => $ttl - $age,
+ 'hitcount' => $hitcount,
+ 'age' => $age,
+ 'ttl' => $ttl
+ );
+ }
+
+ return FALSE;
+ }
+
+ // ------------------------------------------------------------------------
+
+ /**
+ * is_supported()
+ *
+ * Check to see if WinCache is available on this system, bail if it isn't.
+ *
+ * @return bool
+ */
+ public function is_supported()
+ {
+ return (extension_loaded('wincache') && ini_get('wincache.ucenabled'));
+ }
+}
diff --git a/system/libraries/Cache/drivers/index.html b/system/libraries/Cache/drivers/index.html
index c942a79ce..bcb7cae34 100644
--- a/system/libraries/Cache/drivers/index.html
+++ b/system/libraries/Cache/drivers/index.html
@@ -1,4 +1,5 @@
-<html>
+<!DOCTYPE html>
+<html lang="en">
<head>
<title>403 Forbidden</title>
</head>
@@ -7,4 +8,4 @@
<p>Directory access is forbidden.</p>
</body>
-</html> \ No newline at end of file
+</html>
diff --git a/system/libraries/Cache/index.html b/system/libraries/Cache/index.html
index c942a79ce..bcb7cae34 100644
--- a/system/libraries/Cache/index.html
+++ b/system/libraries/Cache/index.html
@@ -1,4 +1,5 @@
-<html>
+<!DOCTYPE html>
+<html lang="en">
<head>
<title>403 Forbidden</title>
</head>
@@ -7,4 +8,4 @@
<p>Directory access is forbidden.</p>
</body>
-</html> \ No newline at end of file
+</html>
diff --git a/system/libraries/Calendar.php b/system/libraries/Calendar.php
index 626097a9b..8eefc82ed 100644
--- a/system/libraries/Calendar.php
+++ b/system/libraries/Calendar.php
@@ -1,19 +1,42 @@
-<?php if ( ! defined('BASEPATH')) exit('No direct script access allowed');
+<?php
/**
* CodeIgniter
*
- * An open source application development framework for PHP 5.1.6 or newer
+ * An open source application development framework for PHP
*
- * @package CodeIgniter
- * @author ExpressionEngine Dev Team
- * @copyright Copyright (c) 2008 - 2014, EllisLab, Inc.
- * @license http://codeigniter.com/user_guide/license.html
- * @link http://codeigniter.com
- * @since Version 1.0
+ * This content is released under the MIT License (MIT)
+ *
+ * Copyright (c) 2019 - 2022, CodeIgniter Foundation
+ *
+ * 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.
+ *
+ * @package CodeIgniter
+ * @author EllisLab Dev Team
+ * @copyright Copyright (c) 2008 - 2014, EllisLab, Inc. (https://ellislab.com/)
+ * @copyright Copyright (c) 2014 - 2019, British Columbia Institute of Technology (https://bcit.ca/)
+ * @copyright Copyright (c) 2019 - 2022, CodeIgniter Foundation (https://codeigniter.com/)
+ * @license https://opensource.org/licenses/MIT MIT License
+ * @link https://codeigniter.com
+ * @since Version 1.0.0
* @filesource
*/
-
-// ------------------------------------------------------------------------
+defined('BASEPATH') OR exit('No direct script access allowed');
/**
* CodeIgniter Calendar Class
@@ -23,43 +46,96 @@
* @package CodeIgniter
* @subpackage Libraries
* @category Libraries
- * @author ExpressionEngine Dev Team
- * @link http://codeigniter.com/user_guide/libraries/calendar.html
+ * @author EllisLab Dev Team
+ * @link https://codeigniter.com/userguide3/libraries/calendar.html
*/
class CI_Calendar {
- var $CI;
- var $lang;
- var $local_time;
- var $template = '';
- var $start_day = 'sunday';
- var $month_type = 'long';
- var $day_type = 'abr';
- var $show_next_prev = FALSE;
- var $next_prev_url = '';
+ /**
+ * Calendar layout template
+ *
+ * @var mixed
+ */
+ public $template = '';
+
+ /**
+ * Replacements array for template
+ *
+ * @var array
+ */
+ public $replacements = array();
+
+ /**
+ * Day of the week to start the calendar on
+ *
+ * @var string
+ */
+ public $start_day = 'sunday';
+
+ /**
+ * How to display months
+ *
+ * @var string
+ */
+ public $month_type = 'long';
+
+ /**
+ * How to display names of days
+ *
+ * @var string
+ */
+ public $day_type = 'abr';
+
+ /**
+ * Whether to show next/prev month links
+ *
+ * @var bool
+ */
+ public $show_next_prev = FALSE;
/**
- * Constructor
+ * Url base to use for next/prev month links
*
- * Loads the calendar language file and sets the default time reference
+ * @var bool
+ */
+ public $next_prev_url = '';
+
+ /**
+ * Show days of other months
+ *
+ * @var bool
+ */
+ public $show_other_days = FALSE;
+
+ // --------------------------------------------------------------------
+
+ /**
+ * CI Singleton
+ *
+ * @var object
+ */
+ protected $CI;
+
+ // --------------------------------------------------------------------
+
+ /**
+ * Class constructor
+ *
+ * Loads the calendar language file and sets the default time reference.
+ *
+ * @uses CI_Lang::$is_loaded
+ *
+ * @param array $config Calendar options
+ * @return void
*/
public function __construct($config = array())
{
$this->CI =& get_instance();
+ $this->CI->lang->load('calendar');
- if ( ! in_array('calendar_lang.php', $this->CI->lang->is_loaded, TRUE))
- {
- $this->CI->lang->load('calendar');
- }
-
- $this->local_time = time();
+ empty($config) OR $this->initialize($config);
- if (count($config) > 0)
- {
- $this->initialize($config);
- }
-
- log_message('debug', "Calendar Class Initialized");
+ log_message('info', 'Calendar Class Initialized');
}
// --------------------------------------------------------------------
@@ -69,11 +145,10 @@ class CI_Calendar {
*
* Accepts an associative array as input, containing display preferences
*
- * @access public
* @param array config preferences
- * @return void
+ * @return CI_Calendar
*/
- function initialize($config = array())
+ public function initialize($config = array())
{
foreach ($config as $key => $val)
{
@@ -82,6 +157,14 @@ class CI_Calendar {
$this->$key = $val;
}
}
+
+ // Set the next_prev_url to the controller if required but not defined
+ if ($this->show_next_prev === TRUE && empty($this->next_prev_url))
+ {
+ $this->next_prev_url = $this->CI->config->site_url($this->CI->router->class.'/'.$this->CI->router->method);
+ }
+
+ return $this;
}
// --------------------------------------------------------------------
@@ -89,29 +172,37 @@ class CI_Calendar {
/**
* Generate the calendar
*
- * @access public
- * @param integer the year
- * @param integer the month
+ * @param int the year
+ * @param int the month
* @param array the data to be shown in the calendar cells
* @return string
*/
- function generate($year = '', $month = '', $data = array())
+ public function generate($year = '', $month = '', $data = array())
{
- // Set and validate the supplied month/year
- if ($year == '')
- $year = date("Y", $this->local_time);
+ $local_time = time();
- if ($month == '')
- $month = date("m", $this->local_time);
-
- if (strlen($year) == 1)
+ // Set and validate the supplied month/year
+ if (empty($year))
+ {
+ $year = date('Y', $local_time);
+ }
+ elseif (strlen($year) === 1)
+ {
$year = '200'.$year;
-
- if (strlen($year) == 2)
+ }
+ elseif (strlen($year) === 2)
+ {
$year = '20'.$year;
+ }
- if (strlen($month) == 1)
+ if (empty($month))
+ {
+ $month = date('m', $local_time);
+ }
+ elseif (strlen($month) === 1)
+ {
$month = '0'.$month;
+ }
$adjusted_date = $this->adjust_date($month, $year);
@@ -123,12 +214,12 @@ class CI_Calendar {
// Set the starting day of the week
$start_days = array('sunday' => 0, 'monday' => 1, 'tuesday' => 2, 'wednesday' => 3, 'thursday' => 4, 'friday' => 5, 'saturday' => 6);
- $start_day = ( ! isset($start_days[$this->start_day])) ? 0 : $start_days[$this->start_day];
+ $start_day = isset($start_days[$this->start_day]) ? $start_days[$this->start_day] : 0;
// Set the starting day number
$local_date = mktime(12, 0, 0, $month, 1, $year);
$date = getdate($local_date);
- $day = $start_day + 1 - $date["wday"];
+ $day = $start_day + 1 - $date['wday'];
while ($day > 1)
{
@@ -137,115 +228,116 @@ class CI_Calendar {
// Set the current month/year/day
// We use this to determine the "today" date
- $cur_year = date("Y", $this->local_time);
- $cur_month = date("m", $this->local_time);
- $cur_day = date("j", $this->local_time);
+ $cur_year = date('Y', $local_time);
+ $cur_month = date('m', $local_time);
+ $cur_day = date('j', $local_time);
- $is_current_month = ($cur_year == $year AND $cur_month == $month) ? TRUE : FALSE;
+ $is_current_month = ($cur_year == $year && $cur_month == $month);
// Generate the template data array
$this->parse_template();
// Begin building the calendar output
- $out = $this->temp['table_open'];
- $out .= "\n";
-
- $out .= "\n";
- $out .= $this->temp['heading_row_start'];
- $out .= "\n";
+ $out = $this->replacements['table_open']."\n\n".$this->replacements['heading_row_start']."\n";
// "previous" month link
- if ($this->show_next_prev == TRUE)
+ if ($this->show_next_prev === TRUE)
{
- // Add a trailing slash to the URL if needed
- $this->next_prev_url = preg_replace("/(.+?)\/*$/", "\\1/", $this->next_prev_url);
+ // Add a trailing slash to the URL if needed
+ $this->next_prev_url = preg_replace('/(.+?)\/*$/', '\\1/', $this->next_prev_url);
$adjusted_date = $this->adjust_date($month - 1, $year);
- $out .= str_replace('{previous_url}', $this->next_prev_url.$adjusted_date['year'].'/'.$adjusted_date['month'], $this->temp['heading_previous_cell']);
- $out .= "\n";
+ $out .= str_replace('{previous_url}', $this->next_prev_url.$adjusted_date['year'].'/'.$adjusted_date['month'], $this->replacements['heading_previous_cell'])."\n";
}
// Heading containing the month/year
- $colspan = ($this->show_next_prev == TRUE) ? 5 : 7;
+ $colspan = ($this->show_next_prev === TRUE) ? 5 : 7;
- $this->temp['heading_title_cell'] = str_replace('{colspan}', $colspan, $this->temp['heading_title_cell']);
- $this->temp['heading_title_cell'] = str_replace('{heading}', $this->get_month_name($month)."&nbsp;".$year, $this->temp['heading_title_cell']);
+ $this->replacements['heading_title_cell'] = str_replace('{colspan}', $colspan,
+ str_replace('{heading}', $this->get_month_name($month).'&nbsp;'.$year, $this->replacements['heading_title_cell']));
- $out .= $this->temp['heading_title_cell'];
- $out .= "\n";
+ $out .= $this->replacements['heading_title_cell']."\n";
// "next" month link
- if ($this->show_next_prev == TRUE)
+ if ($this->show_next_prev === TRUE)
{
$adjusted_date = $this->adjust_date($month + 1, $year);
- $out .= str_replace('{next_url}', $this->next_prev_url.$adjusted_date['year'].'/'.$adjusted_date['month'], $this->temp['heading_next_cell']);
+ $out .= str_replace('{next_url}', $this->next_prev_url.$adjusted_date['year'].'/'.$adjusted_date['month'], $this->replacements['heading_next_cell']);
}
- $out .= "\n";
- $out .= $this->temp['heading_row_end'];
- $out .= "\n";
-
- // Write the cells containing the days of the week
- $out .= "\n";
- $out .= $this->temp['week_row_start'];
- $out .= "\n";
+ $out .= "\n".$this->replacements['heading_row_end']."\n\n"
+ // Write the cells containing the days of the week
+ .$this->replacements['week_row_start']."\n";
$day_names = $this->get_day_names();
for ($i = 0; $i < 7; $i ++)
{
- $out .= str_replace('{week_day}', $day_names[($start_day + $i) %7], $this->temp['week_day_cell']);
+ $out .= str_replace('{week_day}', $day_names[($start_day + $i) %7], $this->replacements['week_day_cell']);
}
- $out .= "\n";
- $out .= $this->temp['week_row_end'];
- $out .= "\n";
+ $out .= "\n".$this->replacements['week_row_end']."\n";
// Build the main body of the calendar
while ($day <= $total_days)
{
- $out .= "\n";
- $out .= $this->temp['cal_row_start'];
- $out .= "\n";
+ $out .= "\n".$this->replacements['cal_row_start']."\n";
for ($i = 0; $i < 7; $i++)
{
- $out .= ($is_current_month == TRUE AND $day == $cur_day) ? $this->temp['cal_cell_start_today'] : $this->temp['cal_cell_start'];
-
- if ($day > 0 AND $day <= $total_days)
+ if ($day > 0 && $day <= $total_days)
{
+ $out .= ($is_current_month === TRUE && $day == $cur_day) ? $this->replacements['cal_cell_start_today'] : $this->replacements['cal_cell_start'];
+
if (isset($data[$day]))
{
// Cells with content
- $temp = ($is_current_month == TRUE AND $day == $cur_day) ? $this->temp['cal_cell_content_today'] : $this->temp['cal_cell_content'];
- $out .= str_replace('{day}', $day, str_replace('{content}', $data[$day], $temp));
+ $temp = ($is_current_month === TRUE && $day == $cur_day) ?
+ $this->replacements['cal_cell_content_today'] : $this->replacements['cal_cell_content'];
+ $out .= str_replace(array('{content}', '{day}'), array($data[$day], $day), $temp);
}
else
{
// Cells with no content
- $temp = ($is_current_month == TRUE AND $day == $cur_day) ? $this->temp['cal_cell_no_content_today'] : $this->temp['cal_cell_no_content'];
+ $temp = ($is_current_month === TRUE && $day == $cur_day) ?
+ $this->replacements['cal_cell_no_content_today'] : $this->replacements['cal_cell_no_content'];
$out .= str_replace('{day}', $day, $temp);
}
+
+ $out .= ($is_current_month === TRUE && $day == $cur_day) ? $this->replacements['cal_cell_end_today'] : $this->replacements['cal_cell_end'];
+ }
+ elseif ($this->show_other_days === TRUE)
+ {
+ $out .= $this->replacements['cal_cell_start_other'];
+
+ if ($day <= 0)
+ {
+ // Day of previous month
+ $prev_month = $this->adjust_date($month - 1, $year);
+ $prev_month_days = $this->get_total_days($prev_month['month'], $prev_month['year']);
+ $out .= str_replace('{day}', $prev_month_days + $day, $this->replacements['cal_cell_other']);
+ }
+ else
+ {
+ // Day of next month
+ $out .= str_replace('{day}', $day - $total_days, $this->replacements['cal_cell_other']);
+ }
+
+ $out .= $this->replacements['cal_cell_end_other'];
}
else
{
// Blank cells
- $out .= $this->temp['cal_cell_blank'];
+ $out .= $this->replacements['cal_cell_start'].$this->replacements['cal_cell_blank'].$this->replacements['cal_cell_end'];
}
- $out .= ($is_current_month == TRUE AND $day == $cur_day) ? $this->temp['cal_cell_end_today'] : $this->temp['cal_cell_end'];
$day++;
}
- $out .= "\n";
- $out .= $this->temp['cal_row_end'];
- $out .= "\n";
+ $out .= "\n".$this->replacements['cal_row_end']."\n";
}
- $out .= "\n";
- $out .= $this->temp['table_close'];
-
- return $out;
+ return $out .= "\n".$this->replacements['table_close'];
}
// --------------------------------------------------------------------
@@ -256,13 +348,12 @@ class CI_Calendar {
* Generates a textual month name based on the numeric
* month provided.
*
- * @access public
- * @param integer the month
+ * @param int the month
* @return string
*/
- function get_month_name($month)
+ public function get_month_name($month)
{
- if ($this->month_type == 'short')
+ if ($this->month_type === 'short')
{
$month_names = array('01' => 'cal_jan', '02' => 'cal_feb', '03' => 'cal_mar', '04' => 'cal_apr', '05' => 'cal_may', '06' => 'cal_jun', '07' => 'cal_jul', '08' => 'cal_aug', '09' => 'cal_sep', '10' => 'cal_oct', '11' => 'cal_nov', '12' => 'cal_dec');
}
@@ -271,14 +362,9 @@ class CI_Calendar {
$month_names = array('01' => 'cal_january', '02' => 'cal_february', '03' => 'cal_march', '04' => 'cal_april', '05' => 'cal_mayl', '06' => 'cal_june', '07' => 'cal_july', '08' => 'cal_august', '09' => 'cal_september', '10' => 'cal_october', '11' => 'cal_november', '12' => 'cal_december');
}
- $month = $month_names[$month];
-
- if ($this->CI->lang->line($month) === FALSE)
- {
- return ucfirst(str_replace('cal_', '', $month));
- }
-
- return $this->CI->lang->line($month);
+ return ($this->CI->lang->line($month_names[$month]) === FALSE)
+ ? ucfirst(substr($month_names[$month], 4))
+ : $this->CI->lang->line($month_names[$month]);
}
// --------------------------------------------------------------------
@@ -287,22 +373,23 @@ class CI_Calendar {
* Get Day Names
*
* Returns an array of day names (Sunday, Monday, etc.) based
- * on the type. Options: long, short, abrev
+ * on the type. Options: long, short, abr
*
- * @access public
* @param string
* @return array
*/
- function get_day_names($day_type = '')
+ public function get_day_names($day_type = '')
{
- if ($day_type != '')
+ if ($day_type !== '')
+ {
$this->day_type = $day_type;
+ }
- if ($this->day_type == 'long')
+ if ($this->day_type === 'long')
{
$day_names = array('sunday', 'monday', 'tuesday', 'wednesday', 'thursday', 'friday', 'saturday');
}
- elseif ($this->day_type == 'short')
+ elseif ($this->day_type === 'short')
{
$day_names = array('sun', 'mon', 'tue', 'wed', 'thu', 'fri', 'sat');
}
@@ -312,9 +399,9 @@ class CI_Calendar {
}
$days = array();
- foreach ($day_names as $val)
+ for ($i = 0, $c = count($day_names); $i < $c; $i++)
{
- $days[] = ($this->CI->lang->line('cal_'.$val) === FALSE) ? ucfirst($val) : $this->CI->lang->line('cal_'.$val);
+ $days[] = ($this->CI->lang->line('cal_'.$day_names[$i]) === FALSE) ? ucfirst($day_names[$i]) : $this->CI->lang->line('cal_'.$day_names[$i]);
}
return $days;
@@ -329,12 +416,11 @@ class CI_Calendar {
* For example, if you submit 13 as the month, the year will
* increment and the month will become January.
*
- * @access public
- * @param integer the month
- * @param integer the year
+ * @param int the month
+ * @param int the year
* @return array
*/
- function adjust_date($month, $year)
+ public function adjust_date($month, $year)
{
$date = array();
@@ -353,7 +439,7 @@ class CI_Calendar {
$date['year']--;
}
- if (strlen($date['month']) == 1)
+ if (strlen($date['month']) === 1)
{
$date['month'] = '0'.$date['month'];
}
@@ -366,30 +452,14 @@ class CI_Calendar {
/**
* Total days in a given month
*
- * @access public
- * @param integer the month
- * @param integer the year
- * @return integer
+ * @param int the month
+ * @param int the year
+ * @return int
*/
- function get_total_days($month, $year)
+ public function get_total_days($month, $year)
{
- $days_in_month = array(31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31);
-
- if ($month < 1 OR $month > 12)
- {
- return 0;
- }
-
- // Is the year a leap year?
- if ($month == 2)
- {
- if ($year % 400 == 0 OR ($year % 4 == 0 AND $year % 100 != 0))
- {
- return 29;
- }
- }
-
- return $days_in_month[$month - 1];
+ $this->CI->load->helper('date');
+ return days_in_month($month, $year);
}
// --------------------------------------------------------------------
@@ -399,34 +469,36 @@ class CI_Calendar {
*
* This is used in the event that the user has not created their own template
*
- * @access public
- * @return array
+ * @return array
*/
- function default_template()
+ public function default_template()
{
- return array (
- 'table_open' => '<table border="0" cellpadding="4" cellspacing="0">',
- 'heading_row_start' => '<tr>',
- 'heading_previous_cell' => '<th><a href="{previous_url}">&lt;&lt;</a></th>',
- 'heading_title_cell' => '<th colspan="{colspan}">{heading}</th>',
- 'heading_next_cell' => '<th><a href="{next_url}">&gt;&gt;</a></th>',
- 'heading_row_end' => '</tr>',
- 'week_row_start' => '<tr>',
- 'week_day_cell' => '<td>{week_day}</td>',
- 'week_row_end' => '</tr>',
- 'cal_row_start' => '<tr>',
- 'cal_cell_start' => '<td>',
- 'cal_cell_start_today' => '<td>',
- 'cal_cell_content' => '<a href="{content}">{day}</a>',
- 'cal_cell_content_today' => '<a href="{content}"><strong>{day}</strong></a>',
- 'cal_cell_no_content' => '{day}',
- 'cal_cell_no_content_today' => '<strong>{day}</strong>',
- 'cal_cell_blank' => '&nbsp;',
- 'cal_cell_end' => '</td>',
- 'cal_cell_end_today' => '</td>',
- 'cal_row_end' => '</tr>',
- 'table_close' => '</table>'
- );
+ return array(
+ 'table_open' => '<table border="0" cellpadding="4" cellspacing="0">',
+ 'heading_row_start' => '<tr>',
+ 'heading_previous_cell' => '<th><a href="{previous_url}">&lt;&lt;</a></th>',
+ 'heading_title_cell' => '<th colspan="{colspan}">{heading}</th>',
+ 'heading_next_cell' => '<th><a href="{next_url}">&gt;&gt;</a></th>',
+ 'heading_row_end' => '</tr>',
+ 'week_row_start' => '<tr>',
+ 'week_day_cell' => '<td>{week_day}</td>',
+ 'week_row_end' => '</tr>',
+ 'cal_row_start' => '<tr>',
+ 'cal_cell_start' => '<td>',
+ 'cal_cell_start_today' => '<td>',
+ 'cal_cell_start_other' => '<td style="color: #666;">',
+ 'cal_cell_content' => '<a href="{content}">{day}</a>',
+ 'cal_cell_content_today' => '<a href="{content}"><strong>{day}</strong></a>',
+ 'cal_cell_no_content' => '{day}',
+ 'cal_cell_no_content_today' => '<strong>{day}</strong>',
+ 'cal_cell_blank' => '&nbsp;',
+ 'cal_cell_other' => '{day}',
+ 'cal_cell_end' => '</td>',
+ 'cal_cell_end_today' => '</td>',
+ 'cal_cell_end_other' => '</td>',
+ 'cal_row_end' => '</tr>',
+ 'table_close' => '</table>'
+ );
}
// --------------------------------------------------------------------
@@ -437,39 +509,39 @@ class CI_Calendar {
* Harvests the data within the template {pseudo-variables}
* used to display the calendar
*
- * @access public
- * @return void
+ * @return CI_Calendar
*/
- function parse_template()
+ public function parse_template()
{
- $this->temp = $this->default_template();
+ $this->replacements = $this->default_template();
- if ($this->template == '')
+ if (empty($this->template))
{
- return;
+ return $this;
}
- $today = array('cal_cell_start_today', 'cal_cell_content_today', 'cal_cell_no_content_today', 'cal_cell_end_today');
-
- foreach (array('table_open', 'table_close', 'heading_row_start', 'heading_previous_cell', 'heading_title_cell', 'heading_next_cell', 'heading_row_end', 'week_row_start', 'week_day_cell', 'week_row_end', 'cal_row_start', 'cal_cell_start', 'cal_cell_content', 'cal_cell_no_content', 'cal_cell_blank', 'cal_cell_end', 'cal_row_end', 'cal_cell_start_today', 'cal_cell_content_today', 'cal_cell_no_content_today', 'cal_cell_end_today') as $val)
+ if (is_string($this->template))
{
- if (preg_match("/\{".$val."\}(.*?)\{\/".$val."\}/si", $this->template, $match))
- {
- $this->temp[$val] = $match['1'];
- }
- else
+ $today = array('cal_cell_start_today', 'cal_cell_content_today', 'cal_cell_no_content_today', 'cal_cell_end_today');
+
+ foreach (array('table_open', 'table_close', 'heading_row_start', 'heading_previous_cell', 'heading_title_cell', 'heading_next_cell', 'heading_row_end', 'week_row_start', 'week_day_cell', 'week_row_end', 'cal_row_start', 'cal_cell_start', 'cal_cell_content', 'cal_cell_no_content', 'cal_cell_blank', 'cal_cell_end', 'cal_row_end', 'cal_cell_start_today', 'cal_cell_content_today', 'cal_cell_no_content_today', 'cal_cell_end_today', 'cal_cell_start_other', 'cal_cell_other', 'cal_cell_end_other') as $val)
{
- if (in_array($val, $today, TRUE))
+ if (preg_match('/\{'.$val.'\}(.*?)\{\/'.$val.'\}/si', $this->template, $match))
{
- $this->temp[$val] = $this->temp[str_replace('_today', '', $val)];
+ $this->replacements[$val] = $match[1];
+ }
+ elseif (in_array($val, $today, TRUE))
+ {
+ $this->replacements[$val] = $this->replacements[substr($val, 0, -6)];
}
}
}
+ elseif (is_array($this->template))
+ {
+ $this->replacements = array_merge($this->replacements, $this->template);
+ }
+
+ return $this;
}
}
-
-// END CI_Calendar class
-
-/* End of file Calendar.php */
-/* Location: ./system/libraries/Calendar.php */ \ No newline at end of file
diff --git a/system/libraries/Cart.php b/system/libraries/Cart.php
deleted file mode 100644
index 86a01f796..000000000
--- a/system/libraries/Cart.php
+++ /dev/null
@@ -1,551 +0,0 @@
-<?php if ( ! defined('BASEPATH')) exit('No direct script access allowed');
-/**
- * CodeIgniter
- *
- * An open source application development framework for PHP 5.1.6 or newer
- *
- * @package CodeIgniter
- * @author ExpressionEngine Dev Team
- * @copyright Copyright (c) 2006 - 2014, EllisLab, Inc.
- * @license http://codeigniter.com/user_guide/license.html
- * @link http://codeigniter.com
- * @since Version 1.0
- * @filesource
- */
-
-// ------------------------------------------------------------------------
-
-/**
- * Shopping Cart Class
- *
- * @package CodeIgniter
- * @subpackage Libraries
- * @category Shopping Cart
- * @author ExpressionEngine Dev Team
- * @link http://codeigniter.com/user_guide/libraries/cart.html
- */
-class CI_Cart {
-
- // These are the regular expression rules that we use to validate the product ID and product name
- var $product_id_rules = '\.a-z0-9_-'; // alpha-numeric, dashes, underscores, or periods
- var $product_name_rules = '\.\:\-_ a-z0-9'; // alpha-numeric, dashes, underscores, colons or periods
-
- // Private variables. Do not change!
- var $CI;
- var $_cart_contents = array();
-
-
- /**
- * Shopping Class Constructor
- *
- * The constructor loads the Session class, used to store the shopping cart contents.
- */
- public function __construct($params = array())
- {
- // Set the super object to a local variable for use later
- $this->CI =& get_instance();
-
- // Are any config settings being passed manually? If so, set them
- $config = array();
- if (count($params) > 0)
- {
- foreach ($params as $key => $val)
- {
- $config[$key] = $val;
- }
- }
-
- // Load the Sessions class
- $this->CI->load->library('session', $config);
-
- // Grab the shopping cart array from the session table, if it exists
- if ($this->CI->session->userdata('cart_contents') !== FALSE)
- {
- $this->_cart_contents = $this->CI->session->userdata('cart_contents');
- }
- else
- {
- // No cart exists so we'll set some base values
- $this->_cart_contents['cart_total'] = 0;
- $this->_cart_contents['total_items'] = 0;
- }
-
- log_message('debug', "Cart Class Initialized");
- }
-
- // --------------------------------------------------------------------
-
- /**
- * Insert items into the cart and save it to the session table
- *
- * @access public
- * @param array
- * @return bool
- */
- function insert($items = array())
- {
- // Was any cart data passed? No? Bah...
- if ( ! is_array($items) OR count($items) == 0)
- {
- log_message('error', 'The insert method must be passed an array containing data.');
- return FALSE;
- }
-
- // You can either insert a single product using a one-dimensional array,
- // or multiple products using a multi-dimensional one. The way we
- // determine the array type is by looking for a required array key named "id"
- // at the top level. If it's not found, we will assume it's a multi-dimensional array.
-
- $save_cart = FALSE;
- if (isset($items['id']))
- {
- if (($rowid = $this->_insert($items)))
- {
- $save_cart = TRUE;
- }
- }
- else
- {
- foreach ($items as $val)
- {
- if (is_array($val) AND isset($val['id']))
- {
- if ($this->_insert($val))
- {
- $save_cart = TRUE;
- }
- }
- }
- }
-
- // Save the cart data if the insert was successful
- if ($save_cart == TRUE)
- {
- $this->_save_cart();
- return isset($rowid) ? $rowid : TRUE;
- }
-
- return FALSE;
- }
-
- // --------------------------------------------------------------------
-
- /**
- * Insert
- *
- * @access private
- * @param array
- * @return bool
- */
- function _insert($items = array())
- {
- // Was any cart data passed? No? Bah...
- if ( ! is_array($items) OR count($items) == 0)
- {
- log_message('error', 'The insert method must be passed an array containing data.');
- return FALSE;
- }
-
- // --------------------------------------------------------------------
-
- // Does the $items array contain an id, quantity, price, and name? These are required
- if ( ! isset($items['id']) OR ! isset($items['qty']) OR ! isset($items['price']) OR ! isset($items['name']))
- {
- log_message('error', 'The cart array must contain a product ID, quantity, price, and name.');
- return FALSE;
- }
-
- // --------------------------------------------------------------------
-
- // Prep the quantity. It can only be a number. Duh...
- $items['qty'] = trim(preg_replace('/([^0-9])/i', '', $items['qty']));
- // Trim any leading zeros
- $items['qty'] = trim(preg_replace('/(^[0]+)/i', '', $items['qty']));
-
- // If the quantity is zero or blank there's nothing for us to do
- if ( ! is_numeric($items['qty']) OR $items['qty'] == 0)
- {
- return FALSE;
- }
-
- // --------------------------------------------------------------------
-
- // Validate the product ID. It can only be alpha-numeric, dashes, underscores or periods
- // Not totally sure we should impose this rule, but it seems prudent to standardize IDs.
- // Note: These can be user-specified by setting the $this->product_id_rules variable.
- if ( ! preg_match("/^[".$this->product_id_rules."]+$/i", $items['id']))
- {
- log_message('error', 'Invalid product ID. The product ID can only contain alpha-numeric characters, dashes, and underscores');
- return FALSE;
- }
-
- // --------------------------------------------------------------------
-
- // Validate the product name. It can only be alpha-numeric, dashes, underscores, colons or periods.
- // Note: These can be user-specified by setting the $this->product_name_rules variable.
- if ( ! preg_match("/^[".$this->product_name_rules."]+$/i", $items['name']))
- {
- log_message('error', 'An invalid name was submitted as the product name: '.$items['name'].' The name can only contain alpha-numeric characters, dashes, underscores, colons, and spaces');
- return FALSE;
- }
-
- // --------------------------------------------------------------------
-
- // Prep the price. Remove anything that isn't a number or decimal point.
- $items['price'] = trim(preg_replace('/([^0-9\.])/i', '', $items['price']));
- // Trim any leading zeros
- $items['price'] = trim(preg_replace('/(^[0]+)/i', '', $items['price']));
-
- // Is the price a valid number?
- if ( ! is_numeric($items['price']))
- {
- log_message('error', 'An invalid price was submitted for product ID: '.$items['id']);
- return FALSE;
- }
-
- // --------------------------------------------------------------------
-
- // We now need to create a unique identifier for the item being inserted into the cart.
- // Every time something is added to the cart it is stored in the master cart array.
- // Each row in the cart array, however, must have a unique index that identifies not only
- // a particular product, but makes it possible to store identical products with different options.
- // For example, what if someone buys two identical t-shirts (same product ID), but in
- // different sizes? The product ID (and other attributes, like the name) will be identical for
- // both sizes because it's the same shirt. The only difference will be the size.
- // Internally, we need to treat identical submissions, but with different options, as a unique product.
- // Our solution is to convert the options array to a string and MD5 it along with the product ID.
- // This becomes the unique "row ID"
- if (isset($items['options']) AND count($items['options']) > 0)
- {
- $rowid = md5($items['id'].implode('', $items['options']));
- }
- else
- {
- // No options were submitted so we simply MD5 the product ID.
- // Technically, we don't need to MD5 the ID in this case, but it makes
- // sense to standardize the format of array indexes for both conditions
- $rowid = md5($items['id']);
- }
-
- // --------------------------------------------------------------------
-
- // Now that we have our unique "row ID", we'll add our cart items to the master array
-
- // let's unset this first, just to make sure our index contains only the data from this submission
- unset($this->_cart_contents[$rowid]);
-
- // Create a new index with our new row ID
- $this->_cart_contents[$rowid]['rowid'] = $rowid;
-
- // And add the new items to the cart array
- foreach ($items as $key => $val)
- {
- $this->_cart_contents[$rowid][$key] = $val;
- }
-
- // Woot!
- return $rowid;
- }
-
- // --------------------------------------------------------------------
-
- /**
- * Update the cart
- *
- * This function permits the quantity of a given item to be changed.
- * Typically it is called from the "view cart" page if a user makes
- * changes to the quantity before checkout. That array must contain the
- * product ID and quantity for each item.
- *
- * @access public
- * @param array
- * @param string
- * @return bool
- */
- function update($items = array())
- {
- // Was any cart data passed?
- if ( ! is_array($items) OR count($items) == 0)
- {
- return FALSE;
- }
-
- // You can either update a single product using a one-dimensional array,
- // or multiple products using a multi-dimensional one. The way we
- // determine the array type is by looking for a required array key named "id".
- // If it's not found we assume it's a multi-dimensional array
- $save_cart = FALSE;
- if (isset($items['rowid']) AND isset($items['qty']))
- {
- if ($this->_update($items) == TRUE)
- {
- $save_cart = TRUE;
- }
- }
- else
- {
- foreach ($items as $val)
- {
- if (is_array($val) AND isset($val['rowid']) AND isset($val['qty']))
- {
- if ($this->_update($val) == TRUE)
- {
- $save_cart = TRUE;
- }
- }
- }
- }
-
- // Save the cart data if the insert was successful
- if ($save_cart == TRUE)
- {
- $this->_save_cart();
- return TRUE;
- }
-
- return FALSE;
- }
-
- // --------------------------------------------------------------------
-
- /**
- * Update the cart
- *
- * This function permits the quantity of a given item to be changed.
- * Typically it is called from the "view cart" page if a user makes
- * changes to the quantity before checkout. That array must contain the
- * product ID and quantity for each item.
- *
- * @access private
- * @param array
- * @return bool
- */
- function _update($items = array())
- {
- // Without these array indexes there is nothing we can do
- if ( ! isset($items['qty']) OR ! isset($items['rowid']) OR ! isset($this->_cart_contents[$items['rowid']]))
- {
- return FALSE;
- }
-
- // Prep the quantity
- $items['qty'] = preg_replace('/([^0-9])/i', '', $items['qty']);
-
- // Is the quantity a number?
- if ( ! is_numeric($items['qty']))
- {
- return FALSE;
- }
-
- // Is the new quantity different than what is already saved in the cart?
- // If it's the same there's nothing to do
- if ($this->_cart_contents[$items['rowid']]['qty'] == $items['qty'])
- {
- return FALSE;
- }
-
- // Is the quantity zero? If so we will remove the item from the cart.
- // If the quantity is greater than zero we are updating
- if ($items['qty'] == 0)
- {
- unset($this->_cart_contents[$items['rowid']]);
- }
- else
- {
- $this->_cart_contents[$items['rowid']]['qty'] = $items['qty'];
- }
-
- return TRUE;
- }
-
- // --------------------------------------------------------------------
-
- /**
- * Save the cart array to the session DB
- *
- * @access private
- * @return bool
- */
- function _save_cart()
- {
- // Unset these so our total can be calculated correctly below
- unset($this->_cart_contents['total_items']);
- unset($this->_cart_contents['cart_total']);
-
- // Lets add up the individual prices and set the cart sub-total
- $total = 0;
- $items = 0;
- foreach ($this->_cart_contents as $key => $val)
- {
- // We make sure the array contains the proper indexes
- if ( ! is_array($val) OR ! isset($val['price']) OR ! isset($val['qty']))
- {
- continue;
- }
-
- $total += ($val['price'] * $val['qty']);
- $items += $val['qty'];
-
- // Set the subtotal
- $this->_cart_contents[$key]['subtotal'] = ($this->_cart_contents[$key]['price'] * $this->_cart_contents[$key]['qty']);
- }
-
- // Set the cart total and total items.
- $this->_cart_contents['total_items'] = $items;
- $this->_cart_contents['cart_total'] = $total;
-
- // Is our cart empty? If so we delete it from the session
- if (count($this->_cart_contents) <= 2)
- {
- $this->CI->session->unset_userdata('cart_contents');
-
- // Nothing more to do... coffee time!
- return FALSE;
- }
-
- // If we made it this far it means that our cart has data.
- // Let's pass it to the Session class so it can be stored
- $this->CI->session->set_userdata(array('cart_contents' => $this->_cart_contents));
-
- // Woot!
- return TRUE;
- }
-
- // --------------------------------------------------------------------
-
- /**
- * Cart Total
- *
- * @access public
- * @return integer
- */
- function total()
- {
- return $this->_cart_contents['cart_total'];
- }
-
- // --------------------------------------------------------------------
-
- /**
- * Total Items
- *
- * Returns the total item count
- *
- * @access public
- * @return integer
- */
- function total_items()
- {
- return $this->_cart_contents['total_items'];
- }
-
- // --------------------------------------------------------------------
-
- /**
- * Cart Contents
- *
- * Returns the entire cart array
- *
- * @access public
- * @return array
- */
- function contents()
- {
- $cart = $this->_cart_contents;
-
- // Remove these so they don't create a problem when showing the cart table
- unset($cart['total_items']);
- unset($cart['cart_total']);
-
- return $cart;
- }
-
- // --------------------------------------------------------------------
-
- /**
- * Has options
- *
- * Returns TRUE if the rowid passed to this function correlates to an item
- * that has options associated with it.
- *
- * @access public
- * @return array
- */
- function has_options($rowid = '')
- {
- if ( ! isset($this->_cart_contents[$rowid]['options']) OR count($this->_cart_contents[$rowid]['options']) === 0)
- {
- return FALSE;
- }
-
- return TRUE;
- }
-
- // --------------------------------------------------------------------
-
- /**
- * Product options
- *
- * Returns the an array of options, for a particular product row ID
- *
- * @access public
- * @return array
- */
- function product_options($rowid = '')
- {
- if ( ! isset($this->_cart_contents[$rowid]['options']))
- {
- return array();
- }
-
- return $this->_cart_contents[$rowid]['options'];
- }
-
- // --------------------------------------------------------------------
-
- /**
- * Format Number
- *
- * Returns the supplied number with commas and a decimal point.
- *
- * @access public
- * @return integer
- */
- function format_number($n = '')
- {
- if ($n == '')
- {
- return '';
- }
-
- // Remove anything that isn't a number or decimal point.
- $n = trim(preg_replace('/([^0-9\.])/i', '', $n));
-
- return number_format($n, 2, '.', ',');
- }
-
- // --------------------------------------------------------------------
-
- /**
- * Destroy the cart
- *
- * Empties the cart and kills the session
- *
- * @access public
- * @return null
- */
- function destroy()
- {
- unset($this->_cart_contents);
-
- $this->_cart_contents['cart_total'] = 0;
- $this->_cart_contents['total_items'] = 0;
-
- $this->CI->session->unset_userdata('cart_contents');
- }
-
-
-}
-
-/* End of file Cart.php */
-/* Location: ./system/libraries/Cart.php */ \ No newline at end of file
diff --git a/system/libraries/Driver.php b/system/libraries/Driver.php
index 9ae7b0c7c..7059be645 100644
--- a/system/libraries/Driver.php
+++ b/system/libraries/Driver.php
@@ -1,19 +1,42 @@
-<?php if ( ! defined('BASEPATH')) exit('No direct script access allowed');
+<?php
/**
* CodeIgniter
*
- * An open source application development framework for PHP 5.1.6 or newer
+ * An open source application development framework for PHP
*
- * @package CodeIgniter
- * @author EllisLab Dev Team
- * @copyright Copyright (c) 2006 - 2014, EllisLab, Inc.
- * @license http://codeigniter.com/user_guide/license.html
- * @link http://codeigniter.com
- * @since Version 1.0
+ * This content is released under the MIT License (MIT)
+ *
+ * Copyright (c) 2019 - 2022, CodeIgniter Foundation
+ *
+ * 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.
+ *
+ * @package CodeIgniter
+ * @author EllisLab Dev Team
+ * @copyright Copyright (c) 2008 - 2014, EllisLab, Inc. (https://ellislab.com/)
+ * @copyright Copyright (c) 2014 - 2019, British Columbia Institute of Technology (https://bcit.ca/)
+ * @copyright Copyright (c) 2019 - 2022, CodeIgniter Foundation (https://codeigniter.com/)
+ * @license https://opensource.org/licenses/MIT MIT License
+ * @link https://codeigniter.com
+ * @since Version 1.0.0
* @filesource
*/
-
-// ------------------------------------------------------------------------
+defined('BASEPATH') OR exit('No direct script access allowed');
/**
* CodeIgniter Driver Library Class
@@ -27,72 +50,150 @@
* @author EllisLab Dev Team
* @link
*/
+#[\AllowDynamicProperties]
class CI_Driver_Library {
- protected $valid_drivers = array();
+ /**
+ * Array of drivers that are available to use with the driver class
+ *
+ * @var array
+ */
+ protected $valid_drivers = array();
+
+ /**
+ * Name of the current class - usually the driver class
+ *
+ * @var string
+ */
protected $lib_name;
- // The first time a child is used it won't exist, so we instantiate it
- // subsequents calls will go straight to the proper child.
- function __get($child)
+ /**
+ * Get magic method
+ *
+ * The first time a child is used it won't exist, so we instantiate it
+ * subsequents calls will go straight to the proper child.
+ *
+ * @param string Child class name
+ * @return object Child class
+ */
+ public function __get($child)
{
+ // Try to load the driver
+ return $this->load_driver($child);
+ }
+
+ /**
+ * Load driver
+ *
+ * Separate load_driver call to support explicit driver load by library or user
+ *
+ * @param string Driver name (w/o parent prefix)
+ * @return object Child class
+ */
+ public function load_driver($child)
+ {
+ // Get CodeIgniter instance and subclass prefix
+ $prefix = config_item('subclass_prefix');
+
if ( ! isset($this->lib_name))
{
- $this->lib_name = get_class($this);
+ // Get library name without any prefix
+ $this->lib_name = str_replace(array('CI_', $prefix), '', get_class($this));
}
- // The class will be prefixed with the parent lib
- $child_class = $this->lib_name.'_'.$child;
+ // The child will be prefixed with the parent lib
+ $child_name = $this->lib_name.'_'.$child;
- // Remove the CI_ prefix and lowercase
- $lib_name = ucfirst(strtolower(str_replace('CI_', '', $this->lib_name)));
- $driver_name = strtolower(str_replace('CI_', '', $child_class));
+ // See if requested child is a valid driver
+ if ( ! in_array($child, $this->valid_drivers))
+ {
+ // The requested driver isn't valid!
+ $msg = 'Invalid driver requested: '.$child_name;
+ log_message('error', $msg);
+ show_error($msg);
+ }
+
+ // Get package paths and filename case variations to search
+ $CI = get_instance();
+ $paths = $CI->load->get_package_paths(TRUE);
- if (in_array($driver_name, array_map('strtolower', $this->valid_drivers)))
+ // Is there an extension?
+ $class_name = $prefix.$child_name;
+ $found = class_exists($class_name, FALSE);
+ if ( ! $found)
{
- // check and see if the driver is in a separate file
- if ( ! class_exists($child_class))
+ // Check for subclass file
+ foreach ($paths as $path)
{
- // check application path first
- foreach (get_instance()->load->get_package_paths(TRUE) as $path)
+ // Does the file exist?
+ $file = $path.'libraries/'.$this->lib_name.'/drivers/'.$prefix.$child_name.'.php';
+ if (file_exists($file))
{
- // loves me some nesting!
- foreach (array(ucfirst($driver_name), $driver_name) as $class)
+ // Yes - require base class from BASEPATH
+ $basepath = BASEPATH.'libraries/'.$this->lib_name.'/drivers/'.$child_name.'.php';
+ if ( ! file_exists($basepath))
{
- $filepath = $path.'libraries/'.$lib_name.'/drivers/'.$class.'.php';
-
- if (file_exists($filepath))
- {
- include_once $filepath;
- break;
- }
+ $msg = 'Unable to load the requested class: CI_'.$child_name;
+ log_message('error', $msg);
+ show_error($msg);
}
+
+ // Include both sources and mark found
+ include_once($basepath);
+ include_once($file);
+ $found = TRUE;
+ break;
}
+ }
+ }
- // it's a valid driver, but the file simply can't be found
- if ( ! class_exists($child_class))
+ // Do we need to search for the class?
+ if ( ! $found)
+ {
+ // Use standard class name
+ $class_name = 'CI_'.$child_name;
+ if ( ! class_exists($class_name, FALSE))
+ {
+ // Check package paths
+ foreach ($paths as $path)
{
- log_message('error', "Unable to load the requested driver: ".$child_class);
- show_error("Unable to load the requested driver: ".$child_class);
+ // Does the file exist?
+ $file = $path.'libraries/'.$this->lib_name.'/drivers/'.$child_name.'.php';
+ if (file_exists($file))
+ {
+ // Include source
+ include_once($file);
+ break;
+ }
}
}
+ }
- $obj = new $child_class;
- $obj->decorate($this);
- $this->$child = $obj;
- return $this->$child;
+ // Did we finally find the class?
+ if ( ! class_exists($class_name, FALSE))
+ {
+ if (class_exists($child_name, FALSE))
+ {
+ $class_name = $child_name;
+ }
+ else
+ {
+ $msg = 'Unable to load the requested driver: '.$class_name;
+ log_message('error', $msg);
+ show_error($msg);
+ }
}
- // The requested driver isn't valid!
- log_message('error', "Invalid driver requested: ".$child_class);
- show_error("Invalid driver requested: ".$child_class);
+ // Instantiate, decorate and add child
+ $obj = new $class_name();
+ $obj->decorate($this);
+ $this->$child = $obj;
+ return $this->$child;
}
- // --------------------------------------------------------------------
-
}
-// END CI_Driver_Library CLASS
+// --------------------------------------------------------------------------
/**
* CodeIgniter Driver Class
@@ -107,12 +208,35 @@ class CI_Driver_Library {
* @link
*/
class CI_Driver {
- protected $parent;
- private $methods = array();
- private $properties = array();
+ /**
+ * Instance of the parent class
+ *
+ * @var object
+ */
+ protected $_parent;
- private static $reflections = array();
+ /**
+ * List of methods in the parent class
+ *
+ * @var array
+ */
+ protected $_methods = array();
+
+ /**
+ * List of properties in the parent class
+ *
+ * @var array
+ */
+ protected $_properties = array();
+
+ /**
+ * Array of methods and properties for the parent class(es)
+ *
+ * @static
+ * @var array
+ */
+ protected static $_reflections = array();
/**
* Decorate
@@ -124,14 +248,14 @@ class CI_Driver {
*/
public function decorate($parent)
{
- $this->parent = $parent;
+ $this->_parent = $parent;
// Lock down attributes to what is defined in the class
// and speed up references in magic methods
$class_name = get_class($parent);
- if ( ! isset(self::$reflections[$class_name]))
+ if ( ! isset(self::$_reflections[$class_name]))
{
$r = new ReflectionObject($parent);
@@ -139,7 +263,7 @@ class CI_Driver {
{
if ($method->isPublic())
{
- $this->methods[] = $method->getName();
+ $this->_methods[] = $method->getName();
}
}
@@ -147,15 +271,15 @@ class CI_Driver {
{
if ($prop->isPublic())
{
- $this->properties[] = $prop->getName();
+ $this->_properties[] = $prop->getName();
}
}
- self::$reflections[$class_name] = array($this->methods, $this->properties);
+ self::$_reflections[$class_name] = array($this->_methods, $this->_properties);
}
else
{
- list($this->methods, $this->properties) = self::$reflections[$class_name];
+ list($this->_methods, $this->_properties) = self::$_reflections[$class_name];
}
}
@@ -166,21 +290,18 @@ class CI_Driver {
*
* Handles access to the parent driver library's methods
*
- * @access public
* @param string
* @param array
* @return mixed
*/
public function __call($method, $args = array())
{
- if (in_array($method, $this->methods))
+ if (in_array($method, $this->_methods))
{
- return call_user_func_array(array($this->parent, $method), $args);
+ return call_user_func_array(array($this->_parent, $method), $args);
}
- $trace = debug_backtrace();
- _exception_handler(E_ERROR, "No such method '{$method}'", $trace[1]['file'], $trace[1]['line']);
- exit;
+ throw new BadMethodCallException('No such method: '.$method.'()');
}
// --------------------------------------------------------------------
@@ -195,9 +316,9 @@ class CI_Driver {
*/
public function __get($var)
{
- if (in_array($var, $this->properties))
+ if (in_array($var, $this->_properties))
{
- return $this->parent->$var;
+ return $this->_parent->$var;
}
}
@@ -214,14 +335,10 @@ class CI_Driver {
*/
public function __set($var, $val)
{
- if (in_array($var, $this->properties))
+ if (in_array($var, $this->_properties))
{
- $this->parent->$var = $val;
+ $this->_parent->$var = $val;
}
}
}
-// END CI_Driver CLASS
-
-/* End of file Driver.php */
-/* Location: ./system/libraries/Driver.php */ \ No newline at end of file
diff --git a/system/libraries/Email.php b/system/libraries/Email.php
index 10cbc346d..1b94b8358 100644
--- a/system/libraries/Email.php
+++ b/system/libraries/Email.php
@@ -1,19 +1,42 @@
-<?php if ( ! defined('BASEPATH')) exit('No direct script access allowed');
+<?php
/**
* CodeIgniter
*
- * An open source application development framework for PHP 5.1.6 or newer
+ * An open source application development framework for PHP
*
- * @package CodeIgniter
- * @author ExpressionEngine Dev Team
- * @copyright Copyright (c) 2008 - 2014, EllisLab, Inc.
- * @license http://codeigniter.com/user_guide/license.html
- * @link http://codeigniter.com
- * @since Version 1.0
+ * This content is released under the MIT License (MIT)
+ *
+ * Copyright (c) 2019 - 2022, CodeIgniter Foundation
+ *
+ * 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.
+ *
+ * @package CodeIgniter
+ * @author EllisLab Dev Team
+ * @copyright Copyright (c) 2008 - 2014, EllisLab, Inc. (https://ellislab.com/)
+ * @copyright Copyright (c) 2014 - 2019, British Columbia Institute of Technology (https://bcit.ca/)
+ * @copyright Copyright (c) 2019 - 2022, CodeIgniter Foundation (https://codeigniter.com/)
+ * @license https://opensource.org/licenses/MIT MIT License
+ * @link https://codeigniter.com
+ * @since Version 1.0.0
* @filesource
*/
-
-// ------------------------------------------------------------------------
+defined('BASEPATH') OR exit('No direct script access allowed');
/**
* CodeIgniter Email Class
@@ -23,79 +46,353 @@
* @package CodeIgniter
* @subpackage Libraries
* @category Libraries
- * @author ExpressionEngine Dev Team
- * @link http://codeigniter.com/user_guide/libraries/email.html
+ * @author EllisLab Dev Team
+ * @link https://codeigniter.com/userguide3/libraries/email.html
*/
class CI_Email {
- var $useragent = "CodeIgniter";
- var $mailpath = "/usr/sbin/sendmail"; // Sendmail path
- var $protocol = "mail"; // mail/sendmail/smtp
- var $smtp_host = ""; // SMTP Server. Example: mail.earthlink.net
- var $smtp_user = ""; // SMTP Username
- var $smtp_pass = ""; // SMTP Password
- var $smtp_port = "25"; // SMTP Port
- var $smtp_timeout = 5; // SMTP Timeout in seconds
- var $smtp_crypto = ""; // SMTP Encryption. Can be null, tls or ssl.
- var $wordwrap = TRUE; // TRUE/FALSE Turns word-wrap on/off
- var $wrapchars = "76"; // Number of characters to wrap at.
- var $mailtype = "text"; // text/html Defines email formatting
- var $charset = "utf-8"; // Default char set: iso-8859-1 or us-ascii
- var $multipart = "mixed"; // "mixed" (in the body) or "related" (separate)
- var $alt_message = ''; // Alternative message for HTML emails
- var $validate = FALSE; // TRUE/FALSE. Enables email validation
- var $priority = "3"; // Default priority (1 - 5)
- var $newline = "\n"; // Default newline. "\r\n" or "\n" (Use "\r\n" to comply with RFC 822)
- var $crlf = "\n"; // The RFC 2045 compliant CRLF for quoted-printable is "\r\n". Apparently some servers,
- // even on the receiving end think they need to muck with CRLFs, so using "\n", while
- // distasteful, is the only thing that seems to work for all environments.
- var $send_multipart = TRUE; // TRUE/FALSE - Yahoo does not like multipart alternative, so this is an override. Set to FALSE for Yahoo.
- var $bcc_batch_mode = FALSE; // TRUE/FALSE Turns on/off Bcc batch feature
- var $bcc_batch_size = 200; // If bcc_batch_mode = TRUE, sets max number of Bccs in each batch
- var $_safe_mode = FALSE;
- var $_subject = "";
- var $_body = "";
- var $_finalbody = "";
- var $_alt_boundary = "";
- var $_atc_boundary = "";
- var $_header_str = "";
- var $_smtp_connect = "";
- var $_encoding = "8bit";
- var $_IP = FALSE;
- var $_smtp_auth = FALSE;
- var $_replyto_flag = FALSE;
- var $_debug_msg = array();
- var $_recipients = array();
- var $_cc_array = array();
- var $_bcc_array = array();
- var $_headers = array();
- var $_attach_name = array();
- var $_attach_type = array();
- var $_attach_disp = array();
- var $_protocols = array('mail', 'sendmail', 'smtp');
- var $_base_charsets = array('us-ascii', 'iso-2022-'); // 7-bit charsets (excluding language suffix)
- var $_bit_depths = array('7bit', '8bit');
- var $_priorities = array('1 (Highest)', '2 (High)', '3 (Normal)', '4 (Low)', '5 (Lowest)');
+ /**
+ * Used as the User-Agent and X-Mailer headers' value.
+ *
+ * @var string
+ */
+ public $useragent = 'CodeIgniter';
+
+ /**
+ * Path to the Sendmail binary.
+ *
+ * @var string
+ */
+ public $mailpath = '/usr/sbin/sendmail'; // Sendmail path
+
+ /**
+ * Which method to use for sending e-mails.
+ *
+ * @var string 'mail', 'sendmail' or 'smtp'
+ */
+ public $protocol = 'mail'; // mail/sendmail/smtp
+
+ /**
+ * STMP Server host
+ *
+ * @var string
+ */
+ public $smtp_host = '';
+
+ /**
+ * SMTP Username
+ *
+ * @var string
+ */
+ public $smtp_user = '';
+
+ /**
+ * SMTP Password
+ *
+ * @var string
+ */
+ public $smtp_pass = '';
+
+ /**
+ * SMTP Server port
+ *
+ * @var int
+ */
+ public $smtp_port = 25;
+
+ /**
+ * SMTP connection timeout in seconds
+ *
+ * @var int
+ */
+ public $smtp_timeout = 5;
+
+ /**
+ * SMTP persistent connection
+ *
+ * @var bool
+ */
+ public $smtp_keepalive = FALSE;
+
+ /**
+ * SMTP Encryption
+ *
+ * @var string empty, 'tls' or 'ssl'
+ */
+ public $smtp_crypto = '';
+
+ /**
+ * Whether to apply word-wrapping to the message body.
+ *
+ * @var bool
+ */
+ public $wordwrap = TRUE;
+
+ /**
+ * Number of characters to wrap at.
+ *
+ * @see CI_Email::$wordwrap
+ * @var int
+ */
+ public $wrapchars = 76;
+
+ /**
+ * Message format.
+ *
+ * @var string 'text' or 'html'
+ */
+ public $mailtype = 'text';
+
+ /**
+ * Character set (default: utf-8)
+ *
+ * @var string
+ */
+ public $charset = 'utf-8';
+
+ /**
+ * Alternative message (for HTML messages only)
+ *
+ * @var string
+ */
+ public $alt_message = '';
+
+ /**
+ * Whether to validate e-mail addresses.
+ *
+ * @var bool
+ */
+ public $validate = TRUE;
+
+ /**
+ * X-Priority header value.
+ *
+ * @var int 1-5
+ */
+ public $priority = 3; // Default priority (1 - 5)
+
+ /**
+ * Newline character sequence.
+ * Use "\r\n" to comply with RFC 822.
+ *
+ * @link https://www.ietf.org/rfc/rfc822.txt
+ * @var string "\r\n" or "\n"
+ */
+ public $newline = "\n"; // Default newline. "\r\n" or "\n" (Use "\r\n" to comply with RFC 822)
+
+ /**
+ * CRLF character sequence
+ *
+ * RFC 2045 specifies that for 'quoted-printable' encoding,
+ * "\r\n" must be used. However, it appears that some servers
+ * (even on the receiving end) don't handle it properly and
+ * switching to "\n", while improper, is the only solution
+ * that seems to work for all environments.
+ *
+ * @link https://www.ietf.org/rfc/rfc822.txt
+ * @var string
+ */
+ public $crlf = "\n";
+
+ /**
+ * Whether to use Delivery Status Notification.
+ *
+ * @var bool
+ */
+ public $dsn = FALSE;
+
+ /**
+ * Whether to send multipart alternatives.
+ * Yahoo! doesn't seem to like these.
+ *
+ * @var bool
+ */
+ public $send_multipart = TRUE;
+
+ /**
+ * Whether to send messages to BCC recipients in batches.
+ *
+ * @var bool
+ */
+ public $bcc_batch_mode = FALSE;
+
+ /**
+ * BCC Batch max number size.
+ *
+ * @see CI_Email::$bcc_batch_mode
+ * @var int
+ */
+ public $bcc_batch_size = 200;
+
+ // --------------------------------------------------------------------
+
+ /**
+ * Subject header
+ *
+ * @var string
+ */
+ protected $_subject = '';
+
+ /**
+ * Message body
+ *
+ * @var string
+ */
+ protected $_body = '';
+
+ /**
+ * Final message body to be sent.
+ *
+ * @var string
+ */
+ protected $_finalbody = '';
+
+ /**
+ * Final headers to send
+ *
+ * @var string
+ */
+ protected $_header_str = '';
+
+ /**
+ * SMTP Connection socket placeholder
+ *
+ * @var resource
+ */
+ protected $_smtp_connect = '';
+
+ /**
+ * Mail encoding
+ *
+ * @var string '8bit' or '7bit'
+ */
+ protected $_encoding = '8bit';
+
+ /**
+ * Whether to perform SMTP authentication
+ *
+ * @var bool
+ */
+ protected $_smtp_auth = FALSE;
+
+ /**
+ * Whether to send a Reply-To header
+ *
+ * @var bool
+ */
+ protected $_replyto_flag = FALSE;
+
+ /**
+ * Debug messages
+ *
+ * @see CI_Email::print_debugger()
+ * @var string
+ */
+ protected $_debug_msg = array();
+
+ /**
+ * Recipients
+ *
+ * @var string[]
+ */
+ protected $_recipients = array();
+
+ /**
+ * CC Recipients
+ *
+ * @var string[]
+ */
+ protected $_cc_array = array();
+
+ /**
+ * BCC Recipients
+ *
+ * @var string[]
+ */
+ protected $_bcc_array = array();
+
+ /**
+ * Message headers
+ *
+ * @var string[]
+ */
+ protected $_headers = array();
+
+ /**
+ * Attachment data
+ *
+ * @var array
+ */
+ protected $_attachments = array();
+
+ /**
+ * Valid $protocol values
+ *
+ * @see CI_Email::$protocol
+ * @var string[]
+ */
+ protected $_protocols = array('mail', 'sendmail', 'smtp');
+
+ /**
+ * Base charsets
+ *
+ * Character sets valid for 7-bit encoding,
+ * excluding language suffix.
+ *
+ * @var string[]
+ */
+ protected $_base_charsets = array('us-ascii', 'iso-2022-');
+
+ /**
+ * Bit depths
+ *
+ * Valid mail encodings
+ *
+ * @see CI_Email::$_encoding
+ * @var string[]
+ */
+ protected $_bit_depths = array('7bit', '8bit');
+ /**
+ * $priority translations
+ *
+ * Actual values to send with the X-Priority header
+ *
+ * @var string[]
+ */
+ protected $_priorities = array(
+ 1 => '1 (Highest)',
+ 2 => '2 (High)',
+ 3 => '3 (Normal)',
+ 4 => '4 (Low)',
+ 5 => '5 (Lowest)'
+ );
+
+ /**
+ * mbstring.func_overload flag
+ *
+ * @var bool
+ */
+ protected static $func_overload;
+
+ // --------------------------------------------------------------------
/**
* Constructor - Sets Email Preferences
*
* The constructor can be passed an array of config values
+ *
+ * @param array $config = array()
+ * @return void
*/
- public function __construct($config = array())
+ public function __construct(array $config = array())
{
- if (count($config) > 0)
- {
- $this->initialize($config);
- }
- else
- {
- $this->_smtp_auth = ($this->smtp_user == '' AND $this->smtp_pass == '') ? FALSE : TRUE;
- $this->_safe_mode = ((boolean)@ini_get("safe_mode") === FALSE) ? FALSE : TRUE;
- }
+ $this->charset = config_item('charset');
+ $this->initialize($config);
+
+ isset(self::$func_overload) OR self::$func_overload = ( ! is_php('8.0') && extension_loaded('mbstring') && @ini_get('mbstring.func_overload'));
- log_message('debug', "Email Class Initialized");
+ log_message('info', 'Email Class Initialized');
}
// --------------------------------------------------------------------
@@ -103,12 +400,13 @@ class CI_Email {
/**
* Initialize preferences
*
- * @access public
- * @param array
- * @return void
+ * @param array $config
+ * @return CI_Email
*/
- public function initialize($config = array())
+ public function initialize(array $config = array())
{
+ $this->clear();
+
foreach ($config as $key => $val)
{
if (isset($this->$key))
@@ -125,10 +423,9 @@ class CI_Email {
}
}
}
- $this->clear();
- $this->_smtp_auth = ($this->smtp_user == '' AND $this->smtp_pass == '') ? FALSE : TRUE;
- $this->_safe_mode = ((boolean)@ini_get("safe_mode") === FALSE) ? FALSE : TRUE;
+ $this->charset = strtoupper($this->charset);
+ $this->_smtp_auth = isset($this->smtp_user[0], $this->smtp_pass[0]);
return $this;
}
@@ -138,30 +435,27 @@ class CI_Email {
/**
* Initialize the Email Data
*
- * @access public
- * @return void
+ * @param bool
+ * @return CI_Email
*/
public function clear($clear_attachments = FALSE)
{
- $this->_subject = "";
- $this->_body = "";
- $this->_finalbody = "";
- $this->_header_str = "";
- $this->_replyto_flag = FALSE;
+ $this->_subject = '';
+ $this->_body = '';
+ $this->_finalbody = '';
+ $this->_header_str = '';
+ $this->_replyto_flag = FALSE;
$this->_recipients = array();
$this->_cc_array = array();
$this->_bcc_array = array();
$this->_headers = array();
$this->_debug_msg = array();
- $this->_set_header('User-Agent', $this->useragent);
- $this->_set_header('Date', $this->_set_date());
+ $this->set_header('Date', $this->_set_date());
if ($clear_attachments !== FALSE)
{
- $this->_attach_name = array();
- $this->_attach_type = array();
- $this->_attach_disp = array();
+ $this->_attachments = array();
}
return $this;
@@ -172,25 +466,29 @@ class CI_Email {
/**
* Set FROM
*
- * @access public
- * @param string
- * @param string
- * @return void
+ * @param string $from
+ * @param string $name
+ * @param string $return_path = NULL Return-Path
+ * @return CI_Email
*/
- public function from($from, $name = '')
+ public function from($from, $name = '', $return_path = NULL)
{
- if (preg_match( '/\<(.*)\>/', $from, $match))
+ if (preg_match('/\<(.*)\>/', $from, $match))
{
- $from = $match['1'];
+ $from = $match[1];
}
if ($this->validate)
{
$this->validate_email($this->_str_to_array($from));
+ if ($return_path)
+ {
+ $this->validate_email($this->_str_to_array($return_path));
+ }
}
// prepare the display name
- if ($name != '')
+ if ($name !== '')
{
// only use Q encoding if there are characters that would require it
if ( ! preg_match('/[\200-\377]/', $name))
@@ -200,12 +498,14 @@ class CI_Email {
}
else
{
- $name = $this->_prep_q_encoding($name, TRUE);
+ $name = $this->_prep_q_encoding($name);
}
}
- $this->_set_header('From', $name.' <'.$from.'>');
- $this->_set_header('Return-Path', '<'.$from.'>');
+ $this->set_header('From', $name.' <'.$from.'>');
+
+ isset($return_path) OR $return_path = $from;
+ $this->set_header('Return-Path', '<'.$return_path.'>');
return $this;
}
@@ -215,16 +515,15 @@ class CI_Email {
/**
* Set Reply-to
*
- * @access public
* @param string
* @param string
- * @return void
+ * @return CI_Email
*/
public function reply_to($replyto, $name = '')
{
- if (preg_match( '/\<(.*)\>/', $replyto, $match))
+ if (preg_match('/\<(.*)\>/', $replyto, $match))
{
- $replyto = $match['1'];
+ $replyto = $match[1];
}
if ($this->validate)
@@ -232,17 +531,21 @@ class CI_Email {
$this->validate_email($this->_str_to_array($replyto));
}
- if ($name == '')
- {
- $name = $replyto;
- }
-
- if (strncmp($name, '"', 1) != 0)
+ if ($name !== '')
{
- $name = '"'.$name.'"';
+ // only use Q encoding if there are characters that would require it
+ if ( ! preg_match('/[\200-\377]/', $name))
+ {
+ // add slashes for non-printing characters, slashes, and double quotes, and surround it in double quotes
+ $name = '"'.addcslashes($name, "\0..\37\177'\"\\").'"';
+ }
+ else
+ {
+ $name = $this->_prep_q_encoding($name);
+ }
}
- $this->_set_header('Reply-To', $name.' <'.$replyto.'>');
+ $this->set_header('Reply-To', $name.' <'.$replyto.'>');
$this->_replyto_flag = TRUE;
return $this;
@@ -253,9 +556,8 @@ class CI_Email {
/**
* Set Recipients
*
- * @access public
* @param string
- * @return void
+ * @return CI_Email
*/
public function to($to)
{
@@ -267,21 +569,12 @@ class CI_Email {
$this->validate_email($to);
}
- if ($this->_get_protocol() != 'mail')
+ if ($this->_get_protocol() !== 'mail')
{
- $this->_set_header('To', implode(", ", $to));
+ $this->set_header('To', implode(', ', $to));
}
- switch ($this->_get_protocol())
- {
- case 'smtp' :
- $this->_recipients = $to;
- break;
- case 'sendmail' :
- case 'mail' :
- $this->_recipients = implode(", ", $to);
- break;
- }
+ $this->_recipients = $to;
return $this;
}
@@ -291,23 +584,21 @@ class CI_Email {
/**
* Set CC
*
- * @access public
* @param string
- * @return void
+ * @return CI_Email
*/
public function cc($cc)
{
- $cc = $this->_str_to_array($cc);
- $cc = $this->clean_email($cc);
+ $cc = $this->clean_email($this->_str_to_array($cc));
if ($this->validate)
{
$this->validate_email($cc);
}
- $this->_set_header('Cc', implode(", ", $cc));
+ $this->set_header('Cc', implode(', ', $cc));
- if ($this->_get_protocol() == "smtp")
+ if ($this->_get_protocol() === 'smtp')
{
$this->_cc_array = $cc;
}
@@ -320,34 +611,32 @@ class CI_Email {
/**
* Set BCC
*
- * @access public
* @param string
* @param string
- * @return void
+ * @return CI_Email
*/
public function bcc($bcc, $limit = '')
{
- if ($limit != '' && is_numeric($limit))
+ if ($limit !== '' && is_numeric($limit))
{
$this->bcc_batch_mode = TRUE;
$this->bcc_batch_size = $limit;
}
- $bcc = $this->_str_to_array($bcc);
- $bcc = $this->clean_email($bcc);
+ $bcc = $this->clean_email($this->_str_to_array($bcc));
if ($this->validate)
{
$this->validate_email($bcc);
}
- if (($this->_get_protocol() == "smtp") OR ($this->bcc_batch_mode && count($bcc) > $this->bcc_batch_size))
+ if ($this->_get_protocol() === 'smtp' OR ($this->bcc_batch_mode && count($bcc) > $this->bcc_batch_size))
{
$this->_bcc_array = $bcc;
}
else
{
- $this->_set_header('Bcc', implode(", ", $bcc));
+ $this->set_header('Bcc', implode(', ', $bcc));
}
return $this;
@@ -358,14 +647,13 @@ class CI_Email {
/**
* Set Email Subject
*
- * @access public
* @param string
- * @return void
+ * @return CI_Email
*/
public function subject($subject)
{
$subject = $this->_prep_q_encoding($subject);
- $this->_set_header('Subject', $subject);
+ $this->set_header('Subject', $subject);
return $this;
}
@@ -374,43 +662,85 @@ class CI_Email {
/**
* Set Body
*
- * @access public
* @param string
- * @return void
+ * @return CI_Email
*/
public function message($body)
{
- $this->_body = rtrim(str_replace("\r", "", $body));
+ $this->_body = rtrim(str_replace("\r", '', $body));
+ return $this;
+ }
- /* strip slashes only if magic quotes is ON
- if we do it with magic quotes OFF, it strips real, user-inputted chars.
+ // --------------------------------------------------------------------
- NOTE: In PHP 5.4 get_magic_quotes_gpc() will always return 0 and
- it will probably not exist in future versions at all.
- */
- if ( ! is_php('5.4') && get_magic_quotes_gpc())
+ /**
+ * Assign file attachments
+ *
+ * @param string $file Can be local path, URL or buffered content
+ * @param string $disposition = 'attachment'
+ * @param string $newname = NULL
+ * @param string $mime = ''
+ * @return CI_Email
+ */
+ public function attach($file, $disposition = '', $newname = NULL, $mime = '')
+ {
+ if ($mime === '')
{
- $this->_body = stripslashes($this->_body);
+ if (strpos($file, '://') === FALSE && ! file_exists($file))
+ {
+ $this->_set_error_message('lang:email_attachment_missing', $file);
+ return FALSE;
+ }
+
+ if ( ! $fp = @fopen($file, 'rb'))
+ {
+ $this->_set_error_message('lang:email_attachment_unreadable', $file);
+ return FALSE;
+ }
+
+ $file_content = stream_get_contents($fp);
+ $mime = $this->_mime_types(pathinfo($file, PATHINFO_EXTENSION));
+ fclose($fp);
+ }
+ else
+ {
+ $file_content =& $file; // buffered file
}
+ $this->_attachments[] = array(
+ 'name' => array($file, $newname),
+ 'disposition' => empty($disposition) ? 'attachment' : $disposition, // Can also be 'inline' Not sure if it matters
+ 'type' => $mime,
+ 'content' => chunk_split(base64_encode($file_content)),
+ 'multipart' => 'mixed'
+ );
+
return $this;
}
// --------------------------------------------------------------------
/**
- * Assign file attachments
+ * Set and return attachment Content-ID
*
- * @access public
- * @param string
- * @return void
+ * Useful for attached inline pictures
+ *
+ * @param string $filename
+ * @return string
*/
- public function attach($filename, $disposition = 'attachment')
+ public function attachment_cid($filename)
{
- $this->_attach_name[] = $filename;
- $this->_attach_type[] = $this->_mime_types(pathinfo($filename, PATHINFO_EXTENSION));
- $this->_attach_disp[] = $disposition; // Can also be 'inline' Not sure if it matters
- return $this;
+ for ($i = 0, $c = count($this->_attachments); $i < $c; $i++)
+ {
+ if ($this->_attachments[$i]['name'][0] === $filename)
+ {
+ $this->_attachments[$i]['multipart'] = 'related';
+ $this->_attachments[$i]['cid'] = uniqid(basename($this->_attachments[$i]['name'][0]).'@');
+ return $this->_attachments[$i]['cid'];
+ }
+ }
+
+ return FALSE;
}
// --------------------------------------------------------------------
@@ -418,14 +748,14 @@ class CI_Email {
/**
* Add a Header Item
*
- * @access protected
* @param string
* @param string
- * @return void
+ * @return CI_Email
*/
- protected function _set_header($header, $value)
+ public function set_header($header, $value)
{
- $this->_headers[$header] = $value;
+ $this->_headers[$header] = str_replace(array("\n", "\r"), '', $value);
+ return $this;
}
// --------------------------------------------------------------------
@@ -433,7 +763,6 @@ class CI_Email {
/**
* Convert a String to an Array
*
- * @access protected
* @param string
* @return array
*/
@@ -441,16 +770,11 @@ class CI_Email {
{
if ( ! is_array($email))
{
- if (strpos($email, ',') !== FALSE)
- {
- $email = preg_split('/[\s,]/', $email, -1, PREG_SPLIT_NO_EMPTY);
- }
- else
- {
- $email = trim($email);
- settype($email, "array");
- }
+ return (strpos($email, ',') !== FALSE)
+ ? preg_split('/[\s,]/', $email, -1, PREG_SPLIT_NO_EMPTY)
+ : (array) trim($email);
}
+
return $email;
}
@@ -459,13 +783,12 @@ class CI_Email {
/**
* Set Multipart Value
*
- * @access public
* @param string
- * @return void
+ * @return CI_Email
*/
- public function set_alt_message($str = '')
+ public function set_alt_message($str)
{
- $this->alt_message = $str;
+ $this->alt_message = (string) $str;
return $this;
}
@@ -474,13 +797,12 @@ class CI_Email {
/**
* Set Mailtype
*
- * @access public
* @param string
- * @return void
+ * @return CI_Email
*/
public function set_mailtype($type = 'text')
{
- $this->mailtype = ($type == 'html') ? 'html' : 'text';
+ $this->mailtype = ($type === 'html') ? 'html' : 'text';
return $this;
}
@@ -489,13 +811,12 @@ class CI_Email {
/**
* Set Wordwrap
*
- * @access public
- * @param string
- * @return void
+ * @param bool
+ * @return CI_Email
*/
public function set_wordwrap($wordwrap = TRUE)
{
- $this->wordwrap = ($wordwrap === FALSE) ? FALSE : TRUE;
+ $this->wordwrap = (bool) $wordwrap;
return $this;
}
@@ -504,13 +825,12 @@ class CI_Email {
/**
* Set Protocol
*
- * @access public
* @param string
- * @return void
+ * @return CI_Email
*/
public function set_protocol($protocol = 'mail')
{
- $this->protocol = ( ! in_array($protocol, $this->_protocols, TRUE)) ? 'mail' : strtolower($protocol);
+ $this->protocol = in_array($protocol, $this->_protocols, TRUE) ? strtolower($protocol) : 'mail';
return $this;
}
@@ -519,25 +839,12 @@ class CI_Email {
/**
* Set Priority
*
- * @access public
- * @param integer
- * @return void
+ * @param int
+ * @return CI_Email
*/
public function set_priority($n = 3)
{
- if ( ! is_numeric($n))
- {
- $this->priority = 3;
- return;
- }
-
- if ($n < 1 OR $n > 5)
- {
- $this->priority = 3;
- return;
- }
-
- $this->priority = $n;
+ $this->priority = preg_match('/^[1-5]$/', $n) ? (int) $n : 3;
return $this;
}
@@ -546,20 +853,12 @@ class CI_Email {
/**
* Set Newline Character
*
- * @access public
* @param string
- * @return void
+ * @return CI_Email
*/
public function set_newline($newline = "\n")
{
- if ($newline != "\n" AND $newline != "\r\n" AND $newline != "\r")
- {
- $this->newline = "\n";
- return;
- }
-
- $this->newline = $newline;
-
+ $this->newline = in_array($newline, array("\n", "\r\n", "\r")) ? $newline : "\n";
return $this;
}
@@ -568,52 +867,26 @@ class CI_Email {
/**
* Set CRLF
*
- * @access public
* @param string
- * @return void
+ * @return CI_Email
*/
public function set_crlf($crlf = "\n")
{
- if ($crlf != "\n" AND $crlf != "\r\n" AND $crlf != "\r")
- {
- $this->crlf = "\n";
- return;
- }
-
- $this->crlf = $crlf;
-
+ $this->crlf = ($crlf !== "\n" && $crlf !== "\r\n" && $crlf !== "\r") ? "\n" : $crlf;
return $this;
}
// --------------------------------------------------------------------
/**
- * Set Message Boundary
- *
- * @access protected
- * @return void
- */
- protected function _set_boundaries()
- {
- $this->_alt_boundary = "B_ALT_".uniqid(''); // multipart/alternative
- $this->_atc_boundary = "B_ATC_".uniqid(''); // attachment boundary
- }
-
- // --------------------------------------------------------------------
-
- /**
* Get the Message ID
*
- * @access protected
* @return string
*/
protected function _get_message_id()
{
- $from = $this->_headers['Return-Path'];
- $from = str_replace(">", "", $from);
- $from = str_replace("<", "", $from);
-
- return "<".uniqid('').strstr($from, '@').">";
+ $from = str_replace(array('>', '<'), '', $this->_headers['Return-Path']);
+ return '<'.uniqid('').strstr($from, '@').'>';
}
// --------------------------------------------------------------------
@@ -621,19 +894,13 @@ class CI_Email {
/**
* Get Mail Protocol
*
- * @access protected
- * @param bool
- * @return string
+ * @return mixed
*/
- protected function _get_protocol($return = TRUE)
+ protected function _get_protocol()
{
$this->protocol = strtolower($this->protocol);
- $this->protocol = ( ! in_array($this->protocol, $this->_protocols, TRUE)) ? 'mail' : $this->protocol;
-
- if ($return == TRUE)
- {
- return $this->protocol;
- }
+ in_array($this->protocol, $this->_protocols, TRUE) OR $this->protocol = 'mail';
+ return $this->protocol;
}
// --------------------------------------------------------------------
@@ -641,26 +908,21 @@ class CI_Email {
/**
* Get Mail Encoding
*
- * @access protected
- * @param bool
* @return string
*/
- protected function _get_encoding($return = TRUE)
+ protected function _get_encoding()
{
- $this->_encoding = ( ! in_array($this->_encoding, $this->_bit_depths)) ? '8bit' : $this->_encoding;
+ in_array($this->_encoding, $this->_bit_depths) OR $this->_encoding = '8bit';
foreach ($this->_base_charsets as $charset)
{
- if (strncmp($charset, $this->charset, strlen($charset)) == 0)
+ if (strpos($this->charset, $charset) === 0)
{
$this->_encoding = '7bit';
}
}
- if ($return == TRUE)
- {
- return $this->_encoding;
- }
+ return $this->_encoding;
}
// --------------------------------------------------------------------
@@ -668,27 +930,20 @@ class CI_Email {
/**
* Get content type (text/html/attachment)
*
- * @access protected
* @return string
*/
protected function _get_content_type()
{
- if ($this->mailtype == 'html' && count($this->_attach_name) == 0)
+ if ($this->mailtype === 'html')
{
- return 'html';
+ return empty($this->_attachments) ? 'html' : 'html-attach';
}
- elseif ($this->mailtype == 'html' && count($this->_attach_name) > 0)
- {
- return 'html-attach';
- }
- elseif ($this->mailtype == 'text' && count($this->_attach_name) > 0)
+ elseif ($this->mailtype === 'text' && ! empty($this->_attachments))
{
return 'plain-attach';
}
- else
- {
- return 'plain';
- }
+
+ return 'plain';
}
// --------------------------------------------------------------------
@@ -696,17 +951,16 @@ class CI_Email {
/**
* Set RFC 822 Date
*
- * @access protected
* @return string
*/
protected function _set_date()
{
- $timezone = date("Z");
- $operator = (strncmp($timezone, '-', 1) == 0) ? '-' : '+';
+ $timezone = date('Z');
+ $operator = ($timezone[0] === '-') ? '-' : '+';
$timezone = abs($timezone);
- $timezone = floor($timezone/3600) * 100 + ($timezone % 3600 ) / 60;
+ $timezone = floor($timezone/3600) * 100 + ($timezone % 3600) / 60;
- return sprintf("%s %s%04d", date("D, j M Y H:i:s"), $operator, $timezone);
+ return sprintf('%s %s%04d', date('D, j M Y H:i:s'), $operator, $timezone);
}
// --------------------------------------------------------------------
@@ -714,12 +968,11 @@ class CI_Email {
/**
* Mime message
*
- * @access protected
* @return string
*/
protected function _get_mime_message()
{
- return "This is a multi-part message in MIME format.".$this->newline."Your email application may not support this format.";
+ return 'This is a multi-part message in MIME format.'.$this->newline.'Your email application may not support this format.';
}
// --------------------------------------------------------------------
@@ -727,7 +980,6 @@ class CI_Email {
/**
* Validate Email Address
*
- * @access public
* @param string
* @return bool
*/
@@ -756,13 +1008,24 @@ class CI_Email {
/**
* Email Validation
*
- * @access public
* @param string
* @return bool
*/
- public function valid_email($address)
+ public function valid_email($email)
{
- return ( ! preg_match("/^([a-z0-9\+_\-]+)(\.[a-z0-9\+_\-]+)*@([a-z0-9\-]+\.)+[a-z]{2,6}$/ix", $address)) ? FALSE : TRUE;
+ if (function_exists('idn_to_ascii') && preg_match('#\A([^@]+)@(.+)\z#', $email, $matches))
+ {
+ $domain = defined('INTL_IDNA_VARIANT_UTS46')
+ ? idn_to_ascii($matches[2], 0, INTL_IDNA_VARIANT_UTS46)
+ : idn_to_ascii($matches[2]);
+
+ if ($domain !== FALSE)
+ {
+ $email = $matches[1].'@'.$domain;
+ }
+ }
+
+ return (bool) filter_var($email, FILTER_VALIDATE_EMAIL);
}
// --------------------------------------------------------------------
@@ -770,7 +1033,6 @@ class CI_Email {
/**
* Clean Extended Email Address: Joe Smith <joe@smith.com>
*
- * @access public
* @param string
* @return string
*/
@@ -778,28 +1040,14 @@ class CI_Email {
{
if ( ! is_array($email))
{
- if (preg_match('/\<(.*)\>/', $email, $match))
- {
- return $match['1'];
- }
- else
- {
- return $email;
- }
+ return preg_match('/\<(.*)\>/', $email, $match) ? $match[1] : $email;
}
$clean_email = array();
foreach ($email as $addy)
{
- if (preg_match( '/\<(.*)\>/', $addy, $match))
- {
- $clean_email[] = $match['1'];
- }
- else
- {
- $clean_email[] = $addy;
- }
+ $clean_email[] = preg_match('/\<(.*)\>/', $addy, $match) ? $match[1] : $addy;
}
return $clean_email;
@@ -810,47 +1058,36 @@ class CI_Email {
/**
* Build alternative plain text message
*
- * This public function provides the raw message for use
- * in plain-text headers of HTML-formatted emails.
+ * Provides the raw message for use in plain-text headers of
+ * HTML-formatted emails.
* If the user hasn't specified his own alternative message
* it creates one by stripping the HTML
*
- * @access protected
* @return string
*/
protected function _get_alt_message()
{
- if ($this->alt_message != "")
- {
- return $this->word_wrap($this->alt_message, '76');
- }
-
- if (preg_match('/\<body.*?\>(.*)\<\/body\>/si', $this->_body, $match))
+ if ( ! empty($this->alt_message))
{
- $body = $match['1'];
- }
- else
- {
- $body = $this->_body;
+ return ($this->wordwrap)
+ ? $this->word_wrap($this->alt_message, 76)
+ : $this->alt_message;
}
- $body = trim(strip_tags($body));
- $body = preg_replace( '#<!--(.*)--\>#', "", $body);
- $body = str_replace("\t", "", $body);
+ $body = preg_match('/\<body.*?\>(.*)\<\/body\>/si', $this->_body, $match) ? $match[1] : $this->_body;
+ $body = str_replace("\t", '', preg_replace('#<!--(.*)--\>#', '', trim(strip_tags($body))));
for ($i = 20; $i >= 3; $i--)
{
- $n = "";
-
- for ($x = 1; $x <= $i; $x ++)
- {
- $n .= "\n";
- }
-
- $body = str_replace($n, "\n\n", $body);
+ $body = str_replace(str_repeat("\n", $i), "\n\n", $body);
}
- return $this->word_wrap($body, '76');
+ // Reduce multiple spaces
+ $body = preg_replace('| +|', ' ', $body);
+
+ return ($this->wordwrap)
+ ? $this->word_wrap($body, 76)
+ : $body;
}
// --------------------------------------------------------------------
@@ -858,83 +1095,79 @@ class CI_Email {
/**
* Word Wrap
*
- * @access public
* @param string
- * @param integer
+ * @param int line-length limit
* @return string
*/
- public function word_wrap($str, $charlim = '')
+ public function word_wrap($str, $charlim = NULL)
{
- // Se the character limit
- if ($charlim == '')
+ // Set the character limit, if not already present
+ if (empty($charlim))
{
- $charlim = ($this->wrapchars == "") ? "76" : $this->wrapchars;
+ $charlim = empty($this->wrapchars) ? 76 : $this->wrapchars;
}
- // Reduce multiple spaces
- $str = preg_replace("| +|", " ", $str);
-
// Standardize newlines
if (strpos($str, "\r") !== FALSE)
{
$str = str_replace(array("\r\n", "\r"), "\n", $str);
}
+ // Reduce multiple spaces at end of line
+ $str = preg_replace('| +\n|', "\n", $str);
+
// If the current word is surrounded by {unwrap} tags we'll
// strip the entire chunk and replace it with a marker.
$unwrap = array();
- if (preg_match_all("|(\{unwrap\}.+?\{/unwrap\})|s", $str, $matches))
+ if (preg_match_all('|\{unwrap\}(.+?)\{/unwrap\}|s', $str, $matches))
{
- for ($i = 0; $i < count($matches['0']); $i++)
+ for ($i = 0, $c = count($matches[0]); $i < $c; $i++)
{
- $unwrap[] = $matches['1'][$i];
- $str = str_replace($matches['1'][$i], "{{unwrapped".$i."}}", $str);
+ $unwrap[] = $matches[1][$i];
+ $str = str_replace($matches[0][$i], '{{unwrapped'.$i.'}}', $str);
}
}
- // Use PHP's native public function to do the initial wordwrap.
+ // Use PHP's native function to do the initial wordwrap.
// We set the cut flag to FALSE so that any individual words that are
- // too long get left alone. In the next step we'll deal with them.
+ // too long get left alone. In the next step we'll deal with them.
$str = wordwrap($str, $charlim, "\n", FALSE);
// Split the string into individual lines of text and cycle through them
- $output = "";
+ $output = '';
foreach (explode("\n", $str) as $line)
{
// Is the line within the allowed character count?
// If so we'll join it to the output and continue
- if (strlen($line) <= $charlim)
+ if (self::strlen($line) <= $charlim)
{
$output .= $line.$this->newline;
continue;
}
$temp = '';
- while ((strlen($line)) > $charlim)
+ do
{
// If the over-length word is a URL we won't wrap it
- if (preg_match("!\[url.+\]|://|wwww.!", $line))
+ if (preg_match('!\[url.+\]|://|www\.!', $line))
{
break;
}
// Trim the word down
- $temp .= substr($line, 0, $charlim-1);
- $line = substr($line, $charlim-1);
+ $temp .= self::substr($line, 0, $charlim - 1);
+ $line = self::substr($line, $charlim - 1);
}
+ while (self::strlen($line) > $charlim);
// If $temp contains data it means we had to split up an over-length
// word into smaller chunks so we'll add it back to our current line
- if ($temp != '')
- {
- $output .= $temp.$this->newline.$line;
- }
- else
+ if ($temp !== '')
{
- $output .= $line;
+ $output .= $temp.$this->newline;
}
- $output .= $this->newline;
+ $output .= $line.$this->newline;
}
// Put our markers back
@@ -942,7 +1175,7 @@ class CI_Email {
{
foreach ($unwrap as $key => $val)
{
- $output = str_replace("{{unwrapped".$key."}}", $val, $output);
+ $output = str_replace('{{unwrapped'.$key.'}}', $val, $output);
}
}
@@ -954,17 +1187,16 @@ class CI_Email {
/**
* Build final headers
*
- * @access protected
- * @param string
- * @return string
+ * @return void
*/
protected function _build_headers()
{
- $this->_set_header('X-Sender', $this->clean_email($this->_headers['From']));
- $this->_set_header('X-Mailer', $this->useragent);
- $this->_set_header('X-Priority', $this->_priorities[$this->priority - 1]);
- $this->_set_header('Message-ID', $this->_get_message_id());
- $this->_set_header('Mime-Version', '1.0');
+ $this->set_header('User-Agent', $this->useragent);
+ $this->set_header('X-Sender', $this->clean_email($this->_headers['From']));
+ $this->set_header('X-Mailer', $this->useragent);
+ $this->set_header('X-Priority', $this->_priorities[$this->priority]);
+ $this->set_header('Message-ID', $this->_get_message_id());
+ $this->set_header('Mime-Version', '1.0');
}
// --------------------------------------------------------------------
@@ -972,31 +1204,33 @@ class CI_Email {
/**
* Write Headers as a string
*
- * @access protected
* @return void
*/
protected function _write_headers()
{
- if ($this->protocol == 'mail')
+ if ($this->protocol === 'mail')
{
- $this->_subject = $this->_headers['Subject'];
- unset($this->_headers['Subject']);
+ if (isset($this->_headers['Subject']))
+ {
+ $this->_subject = $this->_headers['Subject'];
+ unset($this->_headers['Subject']);
+ }
}
reset($this->_headers);
- $this->_header_str = "";
+ $this->_header_str = '';
foreach ($this->_headers as $key => $val)
{
$val = trim($val);
- if ($val != "")
+ if ($val !== '')
{
- $this->_header_str .= $key.": ".$val.$this->newline;
+ $this->_header_str .= $key.': '.$val.$this->newline;
}
}
- if ($this->_get_protocol() == 'mail')
+ if ($this->_get_protocol() === 'mail')
{
$this->_header_str = rtrim($this->_header_str);
}
@@ -1007,179 +1241,225 @@ class CI_Email {
/**
* Build Final Body and attachments
*
- * @access protected
* @return void
*/
protected function _build_message()
{
- if ($this->wordwrap === TRUE AND $this->mailtype != 'html')
+ if ($this->wordwrap === TRUE && $this->mailtype !== 'html')
{
$this->_body = $this->word_wrap($this->_body);
}
- $this->_set_boundaries();
$this->_write_headers();
- $hdr = ($this->_get_protocol() == 'mail') ? $this->newline : '';
+ $hdr = ($this->_get_protocol() === 'mail') ? $this->newline : '';
$body = '';
switch ($this->_get_content_type())
{
- case 'plain' :
+ case 'plain':
- $hdr .= "Content-Type: text/plain; charset=" . $this->charset . $this->newline;
- $hdr .= "Content-Transfer-Encoding: " . $this->_get_encoding();
+ $hdr .= 'Content-Type: text/plain; charset='.$this->charset.$this->newline
+ .'Content-Transfer-Encoding: '.$this->_get_encoding();
- if ($this->_get_protocol() == 'mail')
+ if ($this->_get_protocol() === 'mail')
{
- $this->_header_str .= rtrim($hdr);
+ $this->_header_str .= $hdr;
$this->_finalbody = $this->_body;
}
else
{
- $this->_finalbody = $hdr . $this->newline . $this->newline . $this->_body;
+ $this->_finalbody = $hdr.$this->newline.$this->newline.$this->_body;
}
return;
- break;
- case 'html' :
+ case 'html':
if ($this->send_multipart === FALSE)
{
- $hdr .= "Content-Type: text/html; charset=" . $this->charset . $this->newline;
- $hdr .= "Content-Transfer-Encoding: quoted-printable";
+ $hdr .= 'Content-Type: text/html; charset='.$this->charset.$this->newline
+ .'Content-Transfer-Encoding: quoted-printable';
}
else
{
- $hdr .= "Content-Type: multipart/alternative; boundary=\"" . $this->_alt_boundary . "\"" . $this->newline . $this->newline;
+ $boundary = uniqid('B_ALT_');
+ $hdr .= 'Content-Type: multipart/alternative; boundary="'.$boundary.'"';
- $body .= $this->_get_mime_message() . $this->newline . $this->newline;
- $body .= "--" . $this->_alt_boundary . $this->newline;
+ $body .= $this->_get_mime_message().$this->newline.$this->newline
+ .'--'.$boundary.$this->newline
- $body .= "Content-Type: text/plain; charset=" . $this->charset . $this->newline;
- $body .= "Content-Transfer-Encoding: " . $this->_get_encoding() . $this->newline . $this->newline;
- $body .= $this->_get_alt_message() . $this->newline . $this->newline . "--" . $this->_alt_boundary . $this->newline;
+ .'Content-Type: text/plain; charset='.$this->charset.$this->newline
+ .'Content-Transfer-Encoding: '.$this->_get_encoding().$this->newline.$this->newline
+ .$this->_get_alt_message().$this->newline.$this->newline
+ .'--'.$boundary.$this->newline
- $body .= "Content-Type: text/html; charset=" . $this->charset . $this->newline;
- $body .= "Content-Transfer-Encoding: quoted-printable" . $this->newline . $this->newline;
+ .'Content-Type: text/html; charset='.$this->charset.$this->newline
+ .'Content-Transfer-Encoding: quoted-printable'.$this->newline.$this->newline;
}
- $this->_finalbody = $body . $this->_prep_quoted_printable($this->_body) . $this->newline . $this->newline;
+ $this->_finalbody = $body.$this->_prep_quoted_printable($this->_body).$this->newline.$this->newline;
-
- if ($this->_get_protocol() == 'mail')
+ if ($this->_get_protocol() === 'mail')
{
- $this->_header_str .= rtrim($hdr);
+ $this->_header_str .= $hdr;
}
else
{
- $this->_finalbody = $hdr . $this->_finalbody;
+ $this->_finalbody = $hdr.$this->newline.$this->newline.$this->_finalbody;
}
-
if ($this->send_multipart !== FALSE)
{
- $this->_finalbody .= "--" . $this->_alt_boundary . "--";
+ $this->_finalbody .= '--'.$boundary.'--';
}
return;
- break;
- case 'plain-attach' :
+ case 'plain-attach':
- $hdr .= "Content-Type: multipart/".$this->multipart."; boundary=\"" . $this->_atc_boundary."\"" . $this->newline . $this->newline;
+ $boundary = uniqid('B_ATC_');
+ $hdr .= 'Content-Type: multipart/mixed; boundary="'.$boundary.'"';
- if ($this->_get_protocol() == 'mail')
+ if ($this->_get_protocol() === 'mail')
{
- $this->_header_str .= rtrim($hdr);
+ $this->_header_str .= $hdr;
}
- $body .= $this->_get_mime_message() . $this->newline . $this->newline;
- $body .= "--" . $this->_atc_boundary . $this->newline;
+ $body .= $this->_get_mime_message().$this->newline
+ .$this->newline
+ .'--'.$boundary.$this->newline
+ .'Content-Type: text/plain; charset='.$this->charset.$this->newline
+ .'Content-Transfer-Encoding: '.$this->_get_encoding().$this->newline
+ .$this->newline
+ .$this->_body.$this->newline.$this->newline;
+
+ $this->_append_attachments($body, $boundary);
+
+ break;
+ case 'html-attach':
+
+ $alt_boundary = uniqid('B_ALT_');
+ $last_boundary = NULL;
- $body .= "Content-Type: text/plain; charset=" . $this->charset . $this->newline;
- $body .= "Content-Transfer-Encoding: " . $this->_get_encoding() . $this->newline . $this->newline;
+ if ($this->_attachments_have_multipart('mixed'))
+ {
+ $atc_boundary = uniqid('B_ATC_');
+ $hdr .= 'Content-Type: multipart/mixed; boundary="'.$atc_boundary.'"';
+ $last_boundary = $atc_boundary;
+ }
- $body .= $this->_body . $this->newline . $this->newline;
+ if ($this->_attachments_have_multipart('related'))
+ {
+ $rel_boundary = uniqid('B_REL_');
+ $rel_boundary_header = 'Content-Type: multipart/related; boundary="'.$rel_boundary.'"';
- break;
- case 'html-attach' :
+ if (isset($last_boundary))
+ {
+ $body .= '--'.$last_boundary.$this->newline.$rel_boundary_header;
+ }
+ else
+ {
+ $hdr .= $rel_boundary_header;
+ }
- $hdr .= "Content-Type: multipart/".$this->multipart."; boundary=\"" . $this->_atc_boundary."\"" . $this->newline . $this->newline;
+ $last_boundary = $rel_boundary;
+ }
- if ($this->_get_protocol() == 'mail')
+ if ($this->_get_protocol() === 'mail')
{
- $this->_header_str .= rtrim($hdr);
+ $this->_header_str .= $hdr;
}
- $body .= $this->_get_mime_message() . $this->newline . $this->newline;
- $body .= "--" . $this->_atc_boundary . $this->newline;
+ self::strlen($body) && $body .= $this->newline.$this->newline;
+ $body .= $this->_get_mime_message().$this->newline.$this->newline
+ .'--'.$last_boundary.$this->newline
- $body .= "Content-Type: multipart/alternative; boundary=\"" . $this->_alt_boundary . "\"" . $this->newline .$this->newline;
- $body .= "--" . $this->_alt_boundary . $this->newline;
+ .'Content-Type: multipart/alternative; boundary="'.$alt_boundary.'"'.$this->newline.$this->newline
+ .'--'.$alt_boundary.$this->newline
- $body .= "Content-Type: text/plain; charset=" . $this->charset . $this->newline;
- $body .= "Content-Transfer-Encoding: " . $this->_get_encoding() . $this->newline . $this->newline;
- $body .= $this->_get_alt_message() . $this->newline . $this->newline . "--" . $this->_alt_boundary . $this->newline;
+ .'Content-Type: text/plain; charset='.$this->charset.$this->newline
+ .'Content-Transfer-Encoding: '.$this->_get_encoding().$this->newline.$this->newline
+ .$this->_get_alt_message().$this->newline.$this->newline
+ .'--'.$alt_boundary.$this->newline
- $body .= "Content-Type: text/html; charset=" . $this->charset . $this->newline;
- $body .= "Content-Transfer-Encoding: quoted-printable" . $this->newline . $this->newline;
+ .'Content-Type: text/html; charset='.$this->charset.$this->newline
+ .'Content-Transfer-Encoding: quoted-printable'.$this->newline.$this->newline
- $body .= $this->_prep_quoted_printable($this->_body) . $this->newline . $this->newline;
- $body .= "--" . $this->_alt_boundary . "--" . $this->newline . $this->newline;
+ .$this->_prep_quoted_printable($this->_body).$this->newline.$this->newline
+ .'--'.$alt_boundary.'--'.$this->newline.$this->newline;
- break;
+ if ( ! empty($rel_boundary))
+ {
+ $body .= $this->newline.$this->newline;
+ $this->_append_attachments($body, $rel_boundary, 'related');
+ }
+
+ // multipart/mixed attachments
+ if ( ! empty($atc_boundary))
+ {
+ $body .= $this->newline.$this->newline;
+ $this->_append_attachments($body, $atc_boundary, 'mixed');
+ }
+
+ break;
}
- $attachment = array();
+ $this->_finalbody = ($this->_get_protocol() === 'mail')
+ ? $body
+ : $hdr.$this->newline.$this->newline.$body;
+ }
- $z = 0;
+ // --------------------------------------------------------------------
- for ($i=0; $i < count($this->_attach_name); $i++)
+ protected function _attachments_have_multipart($type)
+ {
+ foreach ($this->_attachments as &$attachment)
{
- $filename = $this->_attach_name[$i];
- $basename = basename($filename);
- $ctype = $this->_attach_type[$i];
-
- if ( ! file_exists($filename))
+ if ($attachment['multipart'] === $type)
{
- $this->_set_error_message('lang:email_attachment_missing', $filename);
- return FALSE;
+ return TRUE;
}
+ }
- $h = "--".$this->_atc_boundary.$this->newline;
- $h .= "Content-type: ".$ctype."; ";
- $h .= "name=\"".$basename."\"".$this->newline;
- $h .= "Content-Disposition: ".$this->_attach_disp[$i].";".$this->newline;
- $h .= "Content-Transfer-Encoding: base64".$this->newline;
+ return FALSE;
+ }
- $attachment[$z++] = $h;
- $file = filesize($filename) +1;
+ // --------------------------------------------------------------------
- if ( ! $fp = fopen($filename, FOPEN_READ))
+ /**
+ * Prepares attachment string
+ *
+ * @param string $body Message body to append to
+ * @param string $boundary Multipart boundary
+ * @param string $multipart When provided, only attachments of this type will be processed
+ * @return string
+ */
+ protected function _append_attachments(&$body, $boundary, $multipart = null)
+ {
+ for ($i = 0, $c = count($this->_attachments); $i < $c; $i++)
+ {
+ if (isset($multipart) && $this->_attachments[$i]['multipart'] !== $multipart)
{
- $this->_set_error_message('lang:email_attachment_unreadable', $filename);
- return FALSE;
+ continue;
}
- $attachment[$z++] = chunk_split(base64_encode(fread($fp, $file)));
- fclose($fp);
- }
-
- $body .= implode($this->newline, $attachment).$this->newline."--".$this->_atc_boundary."--";
+ $name = isset($this->_attachments[$i]['name'][1])
+ ? $this->_attachments[$i]['name'][1]
+ : basename($this->_attachments[$i]['name'][0]);
-
- if ($this->_get_protocol() == 'mail')
- {
- $this->_finalbody = $body;
- }
- else
- {
- $this->_finalbody = $hdr . $body;
+ $body .= '--'.$boundary.$this->newline
+ .'Content-Type: '.$this->_attachments[$i]['type'].'; name="'.$name.'"'.$this->newline
+ .'Content-Disposition: '.$this->_attachments[$i]['disposition'].';'.$this->newline
+ .'Content-Transfer-Encoding: base64'.$this->newline
+ .(empty($this->_attachments[$i]['cid']) ? '' : 'Content-ID: <'.$this->_attachments[$i]['cid'].'>'.$this->newline)
+ .$this->newline
+ .$this->_attachments[$i]['content'].$this->newline;
}
- return;
+ // $name won't be set if no attachments were appended,
+ // and therefore a boundary wouldn't be necessary
+ empty($name) OR $body .= '--'.$boundary.'--';
}
// --------------------------------------------------------------------
@@ -1188,28 +1468,42 @@ class CI_Email {
* Prep Quoted Printable
*
* Prepares string for Quoted-Printable Content-Transfer-Encoding
- * Refer to RFC 2045 http://www.ietf.org/rfc/rfc2045.txt
+ * Refer to RFC 2045 https://www.ietf.org/rfc/rfc2045.txt
*
- * @access protected
* @param string
- * @param integer
* @return string
*/
- protected function _prep_quoted_printable($str, $charlim = '')
+ protected function _prep_quoted_printable($str)
{
- // Set the character limit
- // Don't allow over 76, as that will make servers and MUAs barf
- // all over quoted-printable data
- if ($charlim == '' OR $charlim > '76')
+ // ASCII code numbers for "safe" characters that can always be
+ // used literally, without encoding, as described in RFC 2049.
+ // https://www.ietf.org/rfc/rfc2049.txt
+ static $ascii_safe_chars = array(
+ // ' ( ) + , - . / : = ?
+ 39, 40, 41, 43, 44, 45, 46, 47, 58, 61, 63,
+ // numbers
+ 48, 49, 50, 51, 52, 53, 54, 55, 56, 57,
+ // upper-case letters
+ 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90,
+ // lower-case letters
+ 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122
+ );
+
+ // We are intentionally wrapping so mail servers will encode characters
+ // properly and MUAs will behave, so {unwrap} must go!
+ $str = str_replace(array('{unwrap}', '{/unwrap}'), '', $str);
+
+ // RFC 2045 specifies CRLF as "\r\n".
+ // However, many developers choose to override that and violate
+ // the RFC rules due to (apparently) a bug in MS Exchange,
+ // which only works with "\n".
+ if ($this->crlf === "\r\n")
{
- $charlim = '76';
+ return quoted_printable_encode($str);
}
- // Reduce multiple spaces
- $str = preg_replace("| +|", " ", $str);
-
- // kill nulls
- $str = preg_replace('/\x00+/', '', $str);
+ // Reduce multiple spaces & remove nulls
+ $str = preg_replace(array('| +|', '/\x00+/'), array(' ', ''), $str);
// Standardize newlines
if (strpos($str, "\r") !== FALSE)
@@ -1217,19 +1511,12 @@ class CI_Email {
$str = str_replace(array("\r\n", "\r"), "\n", $str);
}
- // We are intentionally wrapping so mail servers will encode characters
- // properly and MUAs will behave, so {unwrap} must go!
- $str = str_replace(array('{unwrap}', '{/unwrap}'), '', $str);
-
- // Break into an array of lines
- $lines = explode("\n", $str);
-
$escape = '=';
$output = '';
- foreach ($lines as $line)
+ foreach (explode("\n", $str) as $line)
{
- $length = strlen($line);
+ $length = self::strlen($line);
$temp = '';
// Loop through each character in the line to add soft-wrap
@@ -1238,24 +1525,33 @@ class CI_Email {
for ($i = 0; $i < $length; $i++)
{
// Grab the next character
- $char = substr($line, $i, 1);
+ $char = $line[$i];
$ascii = ord($char);
// Convert spaces and tabs but only if it's the end of the line
- if ($i == ($length - 1))
+ if ($ascii === 32 OR $ascii === 9)
{
- $char = ($ascii == '32' OR $ascii == '9') ? $escape.sprintf('%02s', dechex($ascii)) : $char;
+ if ($i === ($length - 1))
+ {
+ $char = $escape.sprintf('%02s', dechex($ascii));
+ }
}
-
- // encode = signs
- if ($ascii == '61')
+ // DO NOT move this below the $ascii_safe_chars line!
+ //
+ // = (equals) signs are allowed by RFC2049, but must be encoded
+ // as they are the encoding delimiter!
+ elseif ($ascii === 61)
{
$char = $escape.strtoupper(sprintf('%02s', dechex($ascii))); // =3D
}
+ elseif ( ! in_array($ascii, $ascii_safe_chars, TRUE))
+ {
+ $char = $escape.strtoupper(sprintf('%02s', dechex($ascii)));
+ }
// If we're at the character limit, add the line to the output,
// reset our temp variable, and keep on chuggin'
- if ((strlen($temp) + strlen($char)) >= $charlim)
+ if ((self::strlen($temp) + self::strlen($char)) >= 76)
{
$output .= $temp.$escape.$this->crlf;
$temp = '';
@@ -1270,9 +1566,7 @@ class CI_Email {
}
// get rid of extra CRLF tacked onto the end
- $output = substr($output, 0, strlen($this->crlf) * -1);
-
- return $output;
+ return self::substr($output, 0, self::strlen($this->crlf) * -1);
}
// --------------------------------------------------------------------
@@ -1280,71 +1574,78 @@ class CI_Email {
/**
* Prep Q Encoding
*
- * Performs "Q Encoding" on a string for use in email headers. It's related
- * but not identical to quoted-printable, so it has its own method
+ * Performs "Q Encoding" on a string for use in email headers.
+ * It's related but not identical to quoted-printable, so it has its
+ * own method.
*
- * @access public
- * @param str
- * @param bool // set to TRUE for processing From: headers
- * @return str
+ * @param string
+ * @return string
*/
- protected function _prep_q_encoding($str, $from = FALSE)
+ protected function _prep_q_encoding($str)
{
- $str = str_replace(array("\r", "\n"), array('', ''), $str);
-
- // Line length must not exceed 76 characters, so we adjust for
- // a space, 7 extra characters =??Q??=, and the charset that we will add to each line
- $limit = 75 - 7 - strlen($this->charset);
+ $str = str_replace(array("\r", "\n"), '', $str);
- // these special characters must be converted too
- $convert = array('_', '=', '?');
-
- if ($from === TRUE)
+ if ($this->charset === 'UTF-8')
{
- $convert[] = ',';
- $convert[] = ';';
+ // Note: We used to have mb_encode_mimeheader() as the first choice
+ // here, but it turned out to be buggy and unreliable. DO NOT
+ // re-add it! -- Narf
+ if (ICONV_ENABLED === TRUE)
+ {
+ $output = @iconv_mime_encode('', $str,
+ array(
+ 'scheme' => 'Q',
+ 'line-length' => 76,
+ 'input-charset' => $this->charset,
+ 'output-charset' => $this->charset,
+ 'line-break-chars' => $this->crlf
+ )
+ );
+
+ // There are reports that iconv_mime_encode() might fail and return FALSE
+ if ($output !== FALSE)
+ {
+ // iconv_mime_encode() will always put a header field name.
+ // We've passed it an empty one, but it still prepends our
+ // encoded string with ': ', so we need to strip it.
+ return self::substr($output, 2);
+ }
+
+ $chars = iconv_strlen($str, 'UTF-8');
+ }
+ elseif (MB_ENABLED === TRUE)
+ {
+ $chars = mb_strlen($str, 'UTF-8');
+ }
}
- $output = '';
- $temp = '';
+ // We might already have this set for UTF-8
+ isset($chars) OR $chars = self::strlen($str);
- for ($i = 0, $length = strlen($str); $i < $length; $i++)
+ $output = '=?'.$this->charset.'?Q?';
+ for ($i = 0, $length = self::strlen($output); $i < $chars; $i++)
{
- // Grab the next character
- $char = substr($str, $i, 1);
- $ascii = ord($char);
+ $chr = ($this->charset === 'UTF-8' && ICONV_ENABLED === TRUE)
+ ? '='.implode('=', str_split(strtoupper(bin2hex(iconv_substr($str, $i, 1, $this->charset))), 2))
+ : '='.strtoupper(bin2hex($str[$i]));
- // convert ALL non-printable ASCII characters and our specials
- if ($ascii < 32 OR $ascii > 126 OR in_array($char, $convert))
+ // RFC 2045 sets a limit of 76 characters per line.
+ // We'll append ?= to the end of each line though.
+ if ($length + ($l = self::strlen($chr)) > 74)
{
- $char = '='.dechex($ascii);
+ $output .= '?='.$this->crlf // EOL
+ .' =?'.$this->charset.'?Q?'.$chr; // New line
+ $length = 6 + self::strlen($this->charset) + $l; // Reset the length for the new line
}
-
- // handle regular spaces a bit more compactly than =20
- if ($ascii == 32)
- {
- $char = '_';
- }
-
- // If we're at the character limit, add the line to the output,
- // reset our temp variable, and keep on chuggin'
- if ((strlen($temp) + strlen($char)) >= $limit)
+ else
{
- $output .= $temp.$this->crlf;
- $temp = '';
+ $output .= $chr;
+ $length += $l;
}
-
- // Add the character to our temporary line
- $temp .= $char;
}
- $str = $output.$temp;
-
- // wrap each line with the shebang, charset, and transfer encoding
- // the preceding space on successive lines is required for header "folding"
- $str = trim(preg_replace('/^(.*)$/m', ' =?'.$this->charset.'?Q?$1?=', $str));
-
- return $str;
+ // End the header
+ return $output.'?=';
}
// --------------------------------------------------------------------
@@ -1352,19 +1653,25 @@ class CI_Email {
/**
* Send Email
*
- * @access public
+ * @param bool $auto_clear = TRUE
* @return bool
*/
- public function send()
+ public function send($auto_clear = TRUE)
{
- if ($this->_replyto_flag == FALSE)
+ if ( ! isset($this->_headers['From']))
+ {
+ $this->_set_error_message('lang:email_no_from');
+ return FALSE;
+ }
+
+ if ($this->_replyto_flag === FALSE)
{
$this->reply_to($this->_headers['From']);
}
- if (( ! isset($this->_recipients) AND ! isset($this->_headers['To'])) AND
- ( ! isset($this->_bcc_array) AND ! isset($this->_headers['Bcc'])) AND
- ( ! isset($this->_headers['Cc'])))
+ if (empty($this->_recipients) && ! isset($this->_headers['To'])
+ && empty($this->_bcc_array) && ! isset($this->_headers['Bcc'])
+ && ! isset($this->_headers['Cc']))
{
$this->_set_error_message('lang:email_no_recipients');
return FALSE;
@@ -1372,71 +1679,71 @@ class CI_Email {
$this->_build_headers();
- if ($this->bcc_batch_mode AND count($this->_bcc_array) > 0)
+ if ($this->bcc_batch_mode && count($this->_bcc_array) > $this->bcc_batch_size)
{
- if (count($this->_bcc_array) > $this->bcc_batch_size)
- return $this->batch_bcc_send();
+ $this->batch_bcc_send();
+
+ if ($auto_clear)
+ {
+ $this->clear();
+ }
+
+ return TRUE;
}
$this->_build_message();
+ $result = $this->_spool_email();
- if ( ! $this->_spool_email())
+ if ($result && $auto_clear)
{
- return FALSE;
- }
- else
- {
- return TRUE;
+ $this->clear();
}
+
+ return $result;
}
// --------------------------------------------------------------------
/**
- * Batch Bcc Send. Sends groups of BCCs in batches
+ * Batch Bcc Send. Sends groups of BCCs in batches
*
- * @access public
- * @return bool
+ * @return void
*/
public function batch_bcc_send()
{
- $float = $this->bcc_batch_size -1;
-
- $set = "";
-
+ $float = $this->bcc_batch_size - 1;
+ $set = '';
$chunk = array();
- for ($i = 0; $i < count($this->_bcc_array); $i++)
+ for ($i = 0, $c = count($this->_bcc_array); $i < $c; $i++)
{
if (isset($this->_bcc_array[$i]))
{
- $set .= ", ".$this->_bcc_array[$i];
+ $set .= ', '.$this->_bcc_array[$i];
}
- if ($i == $float)
+ if ($i === $float)
{
- $chunk[] = substr($set, 1);
- $float = $float + $this->bcc_batch_size;
- $set = "";
+ $chunk[] = self::substr($set, 1);
+ $float += $this->bcc_batch_size;
+ $set = '';
}
- if ($i == count($this->_bcc_array)-1)
+ if ($i === $c-1)
{
- $chunk[] = substr($set, 1);
+ $chunk[] = self::substr($set, 1);
}
}
- for ($i = 0; $i < count($chunk); $i++)
+ for ($i = 0, $c = count($chunk); $i < $c; $i++)
{
unset($this->_headers['Bcc']);
- unset($bcc);
- $bcc = $this->_str_to_array($chunk[$i]);
- $bcc = $this->clean_email($bcc);
+ $bcc = $this->clean_email($this->_str_to_array($chunk[$i]));
- if ($this->protocol != 'smtp')
+ if ($this->protocol !== 'smtp')
{
- $this->_set_header('Bcc', implode(", ", $bcc));
+ $this->set_header('Bcc', implode(', ', $bcc));
}
else
{
@@ -1453,12 +1760,11 @@ class CI_Email {
/**
* Unwrap special elements
*
- * @access protected
* @return void
*/
protected function _unwrap_specials()
{
- $this->_finalbody = preg_replace_callback("/\{unwrap\}(.*?)\{\/unwrap\}/si", array($this, '_remove_nl_callback'), $this->_finalbody);
+ $this->_finalbody = preg_replace_callback('/\{unwrap\}(.*?)\{\/unwrap\}/si', array($this, '_remove_nl_callback'), $this->_finalbody);
}
// --------------------------------------------------------------------
@@ -1466,7 +1772,7 @@ class CI_Email {
/**
* Strip line-breaks via callback
*
- * @access protected
+ * @param string $matches
* @return string
*/
protected function _remove_nl_callback($matches)
@@ -1484,44 +1790,57 @@ class CI_Email {
/**
* Spool mail to the mail server
*
- * @access protected
* @return bool
*/
protected function _spool_email()
{
$this->_unwrap_specials();
- switch ($this->_get_protocol())
+ $protocol = $this->_get_protocol();
+ $method = '_send_with_'.$protocol;
+ if ( ! $this->$method())
{
- case 'mail' :
+ $this->_set_error_message('lang:email_send_failure_'.($protocol === 'mail' ? 'phpmail' : $protocol));
+ return FALSE;
+ }
- if ( ! $this->_send_with_mail())
- {
- $this->_set_error_message('lang:email_send_failure_phpmail');
- return FALSE;
- }
- break;
- case 'sendmail' :
+ $this->_set_error_message('lang:email_sent', $protocol);
+ return TRUE;
+ }
- if ( ! $this->_send_with_sendmail())
- {
- $this->_set_error_message('lang:email_send_failure_sendmail');
- return FALSE;
- }
- break;
- case 'smtp' :
+ // --------------------------------------------------------------------
- if ( ! $this->_send_with_smtp())
- {
- $this->_set_error_message('lang:email_send_failure_smtp');
- return FALSE;
- }
- break;
+ /**
+ * Validate email for shell
+ *
+ * Applies stricter, shell-safe validation to email addresses.
+ * Introduced to prevent RCE via sendmail's -f option.
+ *
+ * @see https://github.com/bcit-ci/CodeIgniter/issues/4963
+ * @see https://gist.github.com/Zenexer/40d02da5e07f151adeaeeaa11af9ab36
+ * @license https://creativecommons.org/publicdomain/zero/1.0/ CC0 1.0, Public Domain
+ *
+ * Credits for the base concept go to Paul Buonopane <paul@namepros.com>
+ *
+ * @param string $email
+ * @return bool
+ */
+ protected function _validate_email_for_shell(&$email)
+ {
+ if (function_exists('idn_to_ascii') && $atpos = strpos($email, '@'))
+ {
+ list($account, $domain) = explode('@', $email, 2);
+ $domain = defined('INTL_IDNA_VARIANT_UTS46')
+ ? idn_to_ascii($domain, 0, INTL_IDNA_VARIANT_UTS46)
+ : idn_to_ascii($domain);
+ if ($domain !== FALSE)
+ {
+ $email = $account.'@'.$domain;
+ }
}
- $this->_set_error_message('lang:email_sent', $this->_get_protocol());
- return TRUE;
+ return (filter_var($email, FILTER_VALIDATE_EMAIL) === $email && preg_match('#\A[a-z0-9._+-]+@[a-z0-9.-]{1,253}\z#i', $email));
}
// --------------------------------------------------------------------
@@ -1529,36 +1848,27 @@ class CI_Email {
/**
* Send using mail()
*
- * @access protected
* @return bool
*/
protected function _send_with_mail()
{
- if ($this->_safe_mode == TRUE)
+ if (is_array($this->_recipients))
{
- if ( ! mail($this->_recipients, $this->_subject, $this->_finalbody, $this->_header_str))
- {
- return FALSE;
- }
- else
- {
- return TRUE;
- }
+ $this->_recipients = implode(', ', $this->_recipients);
}
- else
- {
- // most documentation of sendmail using the "-f" flag lacks a space after it, however
- // we've encountered servers that seem to require it to be in place.
- if ( ! mail($this->_recipients, $this->_subject, $this->_finalbody, $this->_header_str, "-f ".$this->clean_email($this->_headers['From'])))
- {
- return FALSE;
- }
- else
- {
- return TRUE;
- }
+ // _validate_email_for_shell() below accepts by reference,
+ // so this needs to be assigned to a variable
+ $from = $this->clean_email($this->_headers['Return-Path']);
+
+ if ( ! $this->_validate_email_for_shell($from))
+ {
+ return mail($this->_recipients, $this->_subject, $this->_finalbody, $this->_header_str);
}
+
+ // most documentation of sendmail using the "-f" flag lacks a space after it, however
+ // we've encountered servers that seem to require it to be in place.
+ return mail($this->_recipients, $this->_subject, $this->_finalbody, $this->_header_str, '-f '.$from);
}
// --------------------------------------------------------------------
@@ -1566,14 +1876,24 @@ class CI_Email {
/**
* Send using Sendmail
*
- * @access protected
* @return bool
*/
protected function _send_with_sendmail()
{
- $fp = @popen($this->mailpath . " -oi -f ".$this->clean_email($this->_headers['From'])." -t", 'w');
+ // _validate_email_for_shell() below accepts by reference,
+ // so this needs to be assigned to a variable
+ $from = $this->clean_email($this->_headers['From']);
+ if ($this->_validate_email_for_shell($from))
+ {
+ $from = '-f '.$from;
+ }
+ else
+ {
+ $from = '';
+ }
- if ($fp === FALSE OR $fp === NULL)
+ // is popen() enabled?
+ if ( ! function_usable('popen') OR FALSE === ($fp = @popen($this->mailpath.' -oi '.$from.' -t', 'w')))
{
// server probably has popen disabled, so nothing we can do to get a verbose error.
return FALSE;
@@ -1584,12 +1904,7 @@ class CI_Email {
$status = pclose($fp);
- if (version_compare(PHP_VERSION, '4.2.3') == -1)
- {
- $status = $status >> 8 & 0xFF;
- }
-
- if ($status != 0)
+ if ($status !== 0)
{
$this->_set_error_message('lang:email_exit_status', $status);
$this->_set_error_message('lang:email_no_socket');
@@ -1604,103 +1919,149 @@ class CI_Email {
/**
* Send using SMTP
*
- * @access protected
* @return bool
*/
protected function _send_with_smtp()
{
- if ($this->smtp_host == '')
+ if ($this->smtp_host === '')
{
$this->_set_error_message('lang:email_no_hostname');
return FALSE;
}
- $this->_smtp_connect();
- $this->_smtp_authenticate();
+ if ( ! $this->_smtp_connect() OR ! $this->_smtp_authenticate())
+ {
+ return FALSE;
+ }
- $this->_send_command('from', $this->clean_email($this->_headers['From']));
+ if ( ! $this->_send_command('from', $this->clean_email($this->_headers['From'])))
+ {
+ $this->_smtp_end();
+ return FALSE;
+ }
foreach ($this->_recipients as $val)
{
- $this->_send_command('to', $val);
+ if ( ! $this->_send_command('to', $val))
+ {
+ $this->_smtp_end();
+ return FALSE;
+ }
}
- if (count($this->_cc_array) > 0)
+ foreach ($this->_cc_array as $val)
{
- foreach ($this->_cc_array as $val)
+ if ($val !== '' && ! $this->_send_command('to', $val))
{
- if ($val != "")
- {
- $this->_send_command('to', $val);
- }
+ $this->_smtp_end();
+ return FALSE;
}
}
- if (count($this->_bcc_array) > 0)
+ foreach ($this->_bcc_array as $val)
{
- foreach ($this->_bcc_array as $val)
+ if ($val !== '' && ! $this->_send_command('to', $val))
{
- if ($val != "")
- {
- $this->_send_command('to', $val);
- }
+ $this->_smtp_end();
+ return FALSE;
}
}
- $this->_send_command('data');
+ if ( ! $this->_send_command('data'))
+ {
+ $this->_smtp_end();
+ return FALSE;
+ }
// perform dot transformation on any lines that begin with a dot
- $this->_send_data($this->_header_str . preg_replace('/^\./m', '..$1', $this->_finalbody));
+ $this->_send_data($this->_header_str.preg_replace('/^\./m', '..$1', $this->_finalbody));
$this->_send_data('.');
-
$reply = $this->_get_smtp_data();
-
$this->_set_error_message($reply);
- if (strncmp($reply, '250', 3) != 0)
+ $this->_smtp_end();
+
+ if (strpos($reply, '250') !== 0)
{
$this->_set_error_message('lang:email_smtp_error', $reply);
return FALSE;
}
- $this->_send_command('quit');
return TRUE;
}
// --------------------------------------------------------------------
/**
+ * SMTP End
+ *
+ * Shortcut to send RSET or QUIT depending on keep-alive
+ *
+ * @return void
+ */
+ protected function _smtp_end()
+ {
+ $this->_send_command($this->smtp_keepalive ? 'reset' : 'quit');
+ }
+
+ // --------------------------------------------------------------------
+
+ /**
* SMTP Connect
*
- * @access protected
- * @param string
* @return string
*/
protected function _smtp_connect()
{
- $ssl = NULL;
- if ($this->smtp_crypto == 'ssl')
- $ssl = 'ssl://';
- $this->_smtp_connect = fsockopen($ssl.$this->smtp_host,
- $this->smtp_port,
- $errno,
- $errstr,
- $this->smtp_timeout);
+ if (is_resource($this->_smtp_connect))
+ {
+ return TRUE;
+ }
+
+ $ssl = ($this->smtp_crypto === 'ssl') ? 'ssl://' : '';
+
+ $this->_smtp_connect = fsockopen(
+ $ssl.$this->smtp_host,
+ $this->smtp_port,
+ $errno,
+ $errstr,
+ $this->smtp_timeout
+ );
if ( ! is_resource($this->_smtp_connect))
{
- $this->_set_error_message('lang:email_smtp_error', $errno." ".$errstr);
+ $this->_set_error_message('lang:email_smtp_error', $errno.' '.$errstr);
return FALSE;
}
+ stream_set_timeout($this->_smtp_connect, $this->smtp_timeout);
$this->_set_error_message($this->_get_smtp_data());
- if ($this->smtp_crypto == 'tls')
+ if ($this->smtp_crypto === 'tls')
{
$this->_send_command('hello');
$this->_send_command('starttls');
- stream_socket_enable_crypto($this->_smtp_connect, TRUE, STREAM_CRYPTO_METHOD_TLS_CLIENT);
+
+ /**
+ * STREAM_CRYPTO_METHOD_TLS_CLIENT is quite the mess ...
+ *
+ * - On PHP <5.6 it doesn't even mean TLS, but SSL 2.0, and there's no option to use actual TLS
+ * - On PHP 5.6.0-5.6.6, >=7.2 it means negotiation with any of TLS 1.0, 1.1, 1.2
+ * - On PHP 5.6.7-7.1.* it means only TLS 1.0
+ *
+ * We want the negotiation, so we'll force it below ...
+ */
+ $method = is_php('5.6')
+ ? STREAM_CRYPTO_METHOD_TLSv1_0_CLIENT | STREAM_CRYPTO_METHOD_TLSv1_1_CLIENT | STREAM_CRYPTO_METHOD_TLSv1_2_CLIENT
+ : STREAM_CRYPTO_METHOD_TLS_CLIENT;
+ $crypto = stream_socket_enable_crypto($this->_smtp_connect, TRUE, $method);
+
+ if ($crypto !== TRUE)
+ {
+ $this->_set_error_message('lang:email_smtp_error', $this->_get_smtp_data());
+ return FALSE;
+ }
}
return $this->_send_command('hello');
@@ -1711,67 +2072,70 @@ class CI_Email {
/**
* Send SMTP command
*
- * @access protected
* @param string
* @param string
- * @return string
+ * @return bool
*/
protected function _send_command($cmd, $data = '')
{
switch ($cmd)
{
- case 'hello' :
-
- if ($this->_smtp_auth OR $this->_get_encoding() == '8bit')
- $this->_send_data('EHLO '.$this->_get_hostname());
- else
- $this->_send_data('HELO '.$this->_get_hostname());
-
- $resp = 250;
- break;
- case 'starttls' :
-
- $this->_send_data('STARTTLS');
-
- $resp = 220;
- break;
- case 'from' :
-
- $this->_send_data('MAIL FROM:<'.$data.'>');
-
- $resp = 250;
- break;
- case 'to' :
-
- $this->_send_data('RCPT TO:<'.$data.'>');
-
- $resp = 250;
- break;
- case 'data' :
-
- $this->_send_data('DATA');
-
- $resp = 354;
- break;
- case 'quit' :
-
- $this->_send_data('QUIT');
+ case 'hello':
+ if ($this->_smtp_auth OR $this->_get_encoding() === '8bit')
+ {
+ $this->_send_data('EHLO '.$this->_get_hostname());
+ }
+ else
+ {
+ $this->_send_data('HELO '.$this->_get_hostname());
+ }
- $resp = 221;
- break;
+ $resp = 250;
+ break;
+ case 'starttls':
+ $this->_send_data('STARTTLS');
+ $resp = 220;
+ break;
+ case 'from':
+ $this->_send_data('MAIL FROM:<'.$data.'>');
+ $resp = 250;
+ break;
+ case 'to':
+ if ($this->dsn)
+ {
+ $this->_send_data('RCPT TO:<'.$data.'> NOTIFY=SUCCESS,DELAY,FAILURE ORCPT=rfc822;'.$data);
+ }
+ else
+ {
+ $this->_send_data('RCPT TO:<'.$data.'>');
+ }
+ $resp = 250;
+ break;
+ case 'data':
+ $this->_send_data('DATA');
+ $resp = 354;
+ break;
+ case 'reset':
+ $this->_send_data('RSET');
+ $resp = 250;
+ break;
+ case 'quit':
+ $this->_send_data('QUIT');
+ $resp = 221;
+ break;
}
$reply = $this->_get_smtp_data();
- $this->_debug_msg[] = "<pre>".$cmd.": ".$reply."</pre>";
+ $this->_debug_msg[] = '<pre>'.$cmd.': '.$reply.'</pre>';
- if (substr($reply, 0, 3) != $resp)
+ if ((int) self::substr($reply, 0, 3) !== $resp)
{
$this->_set_error_message('lang:email_smtp_error', $reply);
return FALSE;
}
- if ($cmd == 'quit')
+ if ($cmd === 'quit')
{
fclose($this->_smtp_connect);
}
@@ -1782,9 +2146,8 @@ class CI_Email {
// --------------------------------------------------------------------
/**
- * SMTP Authenticate
+ * SMTP Authenticate
*
- * @access protected
* @return bool
*/
protected function _smtp_authenticate()
@@ -1794,42 +2157,48 @@ class CI_Email {
return TRUE;
}
- if ($this->smtp_user == "" AND $this->smtp_pass == "")
+ if ($this->smtp_user === '' && $this->smtp_pass === '')
{
$this->_set_error_message('lang:email_no_smtp_unpw');
return FALSE;
}
$this->_send_data('AUTH LOGIN');
-
$reply = $this->_get_smtp_data();
- if (strncmp($reply, '334', 3) != 0)
+ if (strpos($reply, '503') === 0) // Already authenticated
+ {
+ return TRUE;
+ }
+ elseif (strpos($reply, '334') !== 0)
{
$this->_set_error_message('lang:email_failed_smtp_login', $reply);
return FALSE;
}
$this->_send_data(base64_encode($this->smtp_user));
-
$reply = $this->_get_smtp_data();
- if (strncmp($reply, '334', 3) != 0)
+ if (strpos($reply, '334') !== 0)
{
$this->_set_error_message('lang:email_smtp_auth_un', $reply);
return FALSE;
}
$this->_send_data(base64_encode($this->smtp_pass));
-
$reply = $this->_get_smtp_data();
- if (strncmp($reply, '235', 3) != 0)
+ if (strpos($reply, '235') !== 0)
{
$this->_set_error_message('lang:email_smtp_auth_pw', $reply);
return FALSE;
}
+ if ($this->smtp_keepalive)
+ {
+ $this->_smtp_auth = FALSE;
+ }
+
return TRUE;
}
@@ -1838,20 +2207,45 @@ class CI_Email {
/**
* Send SMTP data
*
- * @access protected
+ * @param string $data
* @return bool
*/
protected function _send_data($data)
{
- if ( ! fwrite($this->_smtp_connect, $data . $this->newline))
+ $data .= $this->newline;
+ for ($written = $timestamp = 0, $length = self::strlen($data); $written < $length; $written += $result)
{
- $this->_set_error_message('lang:email_smtp_data_failure', $data);
- return FALSE;
+ if (($result = fwrite($this->_smtp_connect, self::substr($data, $written))) === FALSE)
+ {
+ break;
+ }
+ // See https://bugs.php.net/bug.php?id=39598 and https://secure.php.net/manual/en/function.fwrite.php#96951
+ elseif ($result === 0)
+ {
+ if ($timestamp === 0)
+ {
+ $timestamp = time();
+ }
+ elseif ($timestamp < (time() - $this->smtp_timeout))
+ {
+ $result = FALSE;
+ break;
+ }
+
+ usleep(250000);
+ continue;
+ }
+
+ $timestamp = 0;
}
- else
+
+ if ($result === FALSE)
{
- return TRUE;
+ $this->_set_error_message('lang:email_smtp_data_failure', $data);
+ return FALSE;
}
+
+ return TRUE;
}
// --------------------------------------------------------------------
@@ -1859,18 +2253,17 @@ class CI_Email {
/**
* Get SMTP data
*
- * @access protected
* @return string
*/
protected function _get_smtp_data()
{
- $data = "";
+ $data = '';
while ($str = fgets($this->_smtp_connect, 512))
{
$data .= $str;
- if (substr($str, 3, 1) == " ")
+ if ($str[3] === ' ')
{
break;
}
@@ -1884,209 +2277,142 @@ class CI_Email {
/**
* Get Hostname
*
- * @access protected
+ * There are only two legal types of hostname - either a fully
+ * qualified domain name (eg: "mail.example.com") or an IP literal
+ * (eg: "[1.2.3.4]").
+ *
+ * @link https://tools.ietf.org/html/rfc5321#section-2.3.5
+ * @link https://cbl.abuseat.org/namingproblems.html
* @return string
*/
protected function _get_hostname()
{
- return (isset($_SERVER['SERVER_NAME'])) ? $_SERVER['SERVER_NAME'] : 'localhost.localdomain';
+ if (isset($_SERVER['SERVER_NAME']))
+ {
+ return $_SERVER['SERVER_NAME'];
+ }
+
+ return isset($_SERVER['SERVER_ADDR']) ? '['.$_SERVER['SERVER_ADDR'].']' : '[127.0.0.1]';
}
// --------------------------------------------------------------------
/**
- * Get IP
+ * Get Debug Message
*
- * @access protected
+ * @param array $include List of raw data chunks to include in the output
+ * Valid options are: 'headers', 'subject', 'body'
* @return string
*/
- protected function _get_ip()
+ public function print_debugger($include = array('headers', 'subject', 'body'))
{
- if ($this->_IP !== FALSE)
- {
- return $this->_IP;
- }
+ $msg = implode('', $this->_debug_msg);
+
+ // Determine which parts of our raw data needs to be printed
+ $raw_data = '';
+ is_array($include) OR $include = array($include);
- $cip = (isset($_SERVER['HTTP_CLIENT_IP']) AND $_SERVER['HTTP_CLIENT_IP'] != "") ? $_SERVER['HTTP_CLIENT_IP'] : FALSE;
- $rip = (isset($_SERVER['REMOTE_ADDR']) AND $_SERVER['REMOTE_ADDR'] != "") ? $_SERVER['REMOTE_ADDR'] : FALSE;
- $fip = (isset($_SERVER['HTTP_X_FORWARDED_FOR']) AND $_SERVER['HTTP_X_FORWARDED_FOR'] != "") ? $_SERVER['HTTP_X_FORWARDED_FOR'] : FALSE;
+ in_array('headers', $include, TRUE) && $raw_data = htmlspecialchars($this->_header_str)."\n";
+ in_array('subject', $include, TRUE) && $raw_data .= htmlspecialchars($this->_subject)."\n";
+ in_array('body', $include, TRUE) && $raw_data .= htmlspecialchars($this->_finalbody);
- if ($cip && $rip) $this->_IP = $cip;
- elseif ($rip) $this->_IP = $rip;
- elseif ($cip) $this->_IP = $cip;
- elseif ($fip) $this->_IP = $fip;
+ return $msg.($raw_data === '' ? '' : '<pre>'.$raw_data.'</pre>');
+ }
+
+ // --------------------------------------------------------------------
- if (strpos($this->_IP, ',') !== FALSE)
+ /**
+ * Set Message
+ *
+ * @param string $msg
+ * @param string $val = ''
+ * @return void
+ */
+ protected function _set_error_message($msg, $val = '')
+ {
+ $CI =& get_instance();
+ $CI->lang->load('email');
+
+ if (sscanf($msg, 'lang:%s', $line) !== 1 OR FALSE === ($line = $CI->lang->line($line)))
{
- $x = explode(',', $this->_IP);
- $this->_IP = end($x);
+ $this->_debug_msg[] = str_replace('%s', $val, $msg).'<br />';
}
-
- if ( ! preg_match( "/^[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}$/", $this->_IP))
+ else
{
- $this->_IP = '0.0.0.0';
+ $this->_debug_msg[] = str_replace('%s', $val, $line).'<br />';
}
-
- unset($cip);
- unset($rip);
- unset($fip);
-
- return $this->_IP;
}
// --------------------------------------------------------------------
/**
- * Get Debug Message
+ * Mime Types
*
- * @access public
+ * @param string
* @return string
*/
- public function print_debugger()
+ protected function _mime_types($ext = '')
{
- $msg = '';
+ $ext = strtolower($ext);
+
+ $mimes =& get_mimes();
- if (count($this->_debug_msg) > 0)
+ if (isset($mimes[$ext]))
{
- foreach ($this->_debug_msg as $val)
- {
- $msg .= $val;
- }
+ return is_array($mimes[$ext])
+ ? current($mimes[$ext])
+ : $mimes[$ext];
}
- $msg .= "<pre>".htmlspecialchars($this->_header_str)."\n".htmlspecialchars($this->_subject)."\n".htmlspecialchars($this->_finalbody).'</pre>';
- return $msg;
+ return 'application/x-unknown-content-type';
}
// --------------------------------------------------------------------
/**
- * Set Message
+ * Destructor
*
- * @access protected
- * @param string
- * @return string
+ * @return void
*/
- protected function _set_error_message($msg, $val = '')
+ public function __destruct()
{
- $CI =& get_instance();
- $CI->lang->load('email');
+ is_resource($this->_smtp_connect) && $this->_send_command('quit');
+ }
- if (substr($msg, 0, 5) != 'lang:' || FALSE === ($line = $CI->lang->line(substr($msg, 5))))
- {
- $this->_debug_msg[] = str_replace('%s', $val, $msg)."<br />";
- }
- else
- {
- $this->_debug_msg[] = str_replace('%s', $val, $line)."<br />";
- }
+ // --------------------------------------------------------------------
+
+ /**
+ * Byte-safe strlen()
+ *
+ * @param string $str
+ * @return int
+ */
+ protected static function strlen($str)
+ {
+ return (self::$func_overload)
+ ? mb_strlen($str, '8bit')
+ : strlen($str);
}
// --------------------------------------------------------------------
/**
- * Mime Types
+ * Byte-safe substr()
*
- * @access protected
- * @param string
+ * @param string $str
+ * @param int $start
+ * @param int $length
* @return string
*/
- protected function _mime_types($ext = "")
+ protected static function substr($str, $start, $length = NULL)
{
- $mimes = array( 'hqx' => 'application/mac-binhex40',
- 'cpt' => 'application/mac-compactpro',
- 'doc' => 'application/msword',
- 'bin' => 'application/macbinary',
- 'dms' => 'application/octet-stream',
- 'lha' => 'application/octet-stream',
- 'lzh' => 'application/octet-stream',
- 'exe' => 'application/octet-stream',
- 'class' => 'application/octet-stream',
- 'psd' => 'application/octet-stream',
- 'so' => 'application/octet-stream',
- 'sea' => 'application/octet-stream',
- 'dll' => 'application/octet-stream',
- 'oda' => 'application/oda',
- 'pdf' => 'application/pdf',
- 'ai' => 'application/postscript',
- 'eps' => 'application/postscript',
- 'ps' => 'application/postscript',
- 'smi' => 'application/smil',
- 'smil' => 'application/smil',
- 'mif' => 'application/vnd.mif',
- 'xls' => 'application/vnd.ms-excel',
- 'ppt' => 'application/vnd.ms-powerpoint',
- 'wbxml' => 'application/vnd.wap.wbxml',
- 'wmlc' => 'application/vnd.wap.wmlc',
- 'dcr' => 'application/x-director',
- 'dir' => 'application/x-director',
- 'dxr' => 'application/x-director',
- 'dvi' => 'application/x-dvi',
- 'gtar' => 'application/x-gtar',
- 'php' => 'application/x-httpd-php',
- 'php4' => 'application/x-httpd-php',
- 'php3' => 'application/x-httpd-php',
- 'phtml' => 'application/x-httpd-php',
- 'phps' => 'application/x-httpd-php-source',
- 'js' => 'application/x-javascript',
- 'swf' => 'application/x-shockwave-flash',
- 'sit' => 'application/x-stuffit',
- 'tar' => 'application/x-tar',
- 'tgz' => 'application/x-tar',
- 'xhtml' => 'application/xhtml+xml',
- 'xht' => 'application/xhtml+xml',
- 'zip' => 'application/zip',
- 'mid' => 'audio/midi',
- 'midi' => 'audio/midi',
- 'mpga' => 'audio/mpeg',
- 'mp2' => 'audio/mpeg',
- 'mp3' => 'audio/mpeg',
- 'aif' => 'audio/x-aiff',
- 'aiff' => 'audio/x-aiff',
- 'aifc' => 'audio/x-aiff',
- 'ram' => 'audio/x-pn-realaudio',
- 'rm' => 'audio/x-pn-realaudio',
- 'rpm' => 'audio/x-pn-realaudio-plugin',
- 'ra' => 'audio/x-realaudio',
- 'rv' => 'video/vnd.rn-realvideo',
- 'wav' => 'audio/x-wav',
- 'bmp' => 'image/bmp',
- 'gif' => 'image/gif',
- 'jpeg' => 'image/jpeg',
- 'jpg' => 'image/jpeg',
- 'jpe' => 'image/jpeg',
- 'png' => 'image/png',
- 'tiff' => 'image/tiff',
- 'tif' => 'image/tiff',
- 'css' => 'text/css',
- 'html' => 'text/html',
- 'htm' => 'text/html',
- 'shtml' => 'text/html',
- 'txt' => 'text/plain',
- 'text' => 'text/plain',
- 'log' => 'text/plain',
- 'rtx' => 'text/richtext',
- 'rtf' => 'text/rtf',
- 'xml' => 'text/xml',
- 'xsl' => 'text/xml',
- 'mpeg' => 'video/mpeg',
- 'mpg' => 'video/mpeg',
- 'mpe' => 'video/mpeg',
- 'qt' => 'video/quicktime',
- 'mov' => 'video/quicktime',
- 'avi' => 'video/x-msvideo',
- 'movie' => 'video/x-sgi-movie',
- 'doc' => 'application/msword',
- 'word' => 'application/msword',
- 'xl' => 'application/excel',
- 'eml' => 'message/rfc822'
- );
-
- return ( ! isset($mimes[strtolower($ext)])) ? "application/x-unknown-content-type" : $mimes[strtolower($ext)];
- }
+ if (self::$func_overload)
+ {
+ return mb_substr($str, $start, $length, '8bit');
+ }
+ return isset($length)
+ ? substr($str, $start, $length)
+ : substr($str, $start);
+ }
}
-// END CI_Email class
-
-/* End of file Email.php */
-/* Location: ./system/libraries/Email.php */
diff --git a/system/libraries/Encrypt.php b/system/libraries/Encrypt.php
deleted file mode 100644
index 8e5c1fe53..000000000
--- a/system/libraries/Encrypt.php
+++ /dev/null
@@ -1,500 +0,0 @@
-<?php if ( ! defined('BASEPATH')) exit('No direct script access allowed');
-/**
- * CodeIgniter
- *
- * An open source application development framework for PHP 5.1.6 or newer
- *
- * @package CodeIgniter
- * @author ExpressionEngine Dev Team
- * @copyright Copyright (c) 2008 - 2014, EllisLab, Inc.
- * @license http://codeigniter.com/user_guide/license.html
- * @link http://codeigniter.com
- * @since Version 1.0
- * @filesource
- */
-
-// ------------------------------------------------------------------------
-
-/**
- * CodeIgniter Encryption Class
- *
- * Provides two-way keyed encoding using Mcrypt
- *
- * @package CodeIgniter
- * @subpackage Libraries
- * @category Libraries
- * @author ExpressionEngine Dev Team
- * @link http://codeigniter.com/user_guide/libraries/encryption.html
- */
-class CI_Encrypt {
-
- var $CI;
- var $encryption_key = '';
- var $_hash_type = 'sha1';
- var $_mcrypt_exists = FALSE;
- var $_mcrypt_cipher;
- var $_mcrypt_mode;
-
- /**
- * Constructor
- *
- * Simply determines whether the mcrypt library exists.
- *
- */
- public function __construct()
- {
- $this->CI =& get_instance();
- $this->_mcrypt_exists = ( ! function_exists('mcrypt_encrypt')) ? FALSE : TRUE;
-
- if ($this->_mcrypt_exists === FALSE)
- {
- show_error('The Encrypt library requires the Mcrypt extension.');
- }
-
- log_message('debug', "Encrypt Class Initialized");
- }
-
- // --------------------------------------------------------------------
-
- /**
- * Fetch the encryption key
- *
- * Returns it as MD5 in order to have an exact-length 128 bit key.
- * Mcrypt is sensitive to keys that are not the correct length
- *
- * @access public
- * @param string
- * @return string
- */
- function get_key($key = '')
- {
- if ($key == '')
- {
- if ($this->encryption_key != '')
- {
- return $this->encryption_key;
- }
-
- $CI =& get_instance();
- $key = $CI->config->item('encryption_key');
-
- if ($key == FALSE)
- {
- show_error('In order to use the encryption class requires that you set an encryption key in your config file.');
- }
- }
-
- return md5($key);
- }
-
- // --------------------------------------------------------------------
-
- /**
- * Set the encryption key
- *
- * @access public
- * @param string
- * @return void
- */
- function set_key($key = '')
- {
- $this->encryption_key = $key;
- }
-
- // --------------------------------------------------------------------
-
- /**
- * Encode
- *
- * Encodes the message string using bitwise XOR encoding.
- * The key is combined with a random hash, and then it
- * too gets converted using XOR. The whole thing is then run
- * through mcrypt using the randomized key. The end result
- * is a double-encrypted message string that is randomized
- * with each call to this function, even if the supplied
- * message and key are the same.
- *
- * @access public
- * @param string the string to encode
- * @param string the key
- * @return string
- */
- function encode($string, $key = '')
- {
- $key = $this->get_key($key);
- $enc = $this->mcrypt_encode($string, $key);
-
- return base64_encode($enc);
- }
-
- // --------------------------------------------------------------------
-
- /**
- * Decode
- *
- * Reverses the above process
- *
- * @access public
- * @param string
- * @param string
- * @return string
- */
- function decode($string, $key = '')
- {
- $key = $this->get_key($key);
-
- if (preg_match('/[^a-zA-Z0-9\/\+=]/', $string))
- {
- return FALSE;
- }
-
- $dec = base64_decode($string);
-
- if (($dec = $this->mcrypt_decode($dec, $key)) === FALSE)
- {
- return FALSE;
- }
-
- return $dec;
- }
-
- // --------------------------------------------------------------------
-
- /**
- * Encode from Legacy
- *
- * Takes an encoded string from the original Encryption class algorithms and
- * returns a newly encoded string using the improved method added in 2.0.0
- * This allows for backwards compatibility and a method to transition to the
- * new encryption algorithms.
- *
- * For more details, see http://codeigniter.com/user_guide/installation/upgrade_200.html#encryption
- *
- * @access public
- * @param string
- * @param int (mcrypt mode constant)
- * @param string
- * @return string
- */
- function encode_from_legacy($string, $legacy_mode = MCRYPT_MODE_ECB, $key = '')
- {
- // decode it first
- // set mode temporarily to what it was when string was encoded with the legacy
- // algorithm - typically MCRYPT_MODE_ECB
- $current_mode = $this->_get_mode();
- $this->set_mode($legacy_mode);
-
- $key = $this->get_key($key);
-
- if (preg_match('/[^a-zA-Z0-9\/\+=]/', $string))
- {
- return FALSE;
- }
-
- $dec = base64_decode($string);
-
- if (($dec = $this->mcrypt_decode($dec, $key)) === FALSE)
- {
- return FALSE;
- }
-
- $dec = $this->_xor_decode($dec, $key);
-
- // set the mcrypt mode back to what it should be, typically MCRYPT_MODE_CBC
- $this->set_mode($current_mode);
-
- // and re-encode
- return base64_encode($this->mcrypt_encode($dec, $key));
- }
-
- // --------------------------------------------------------------------
-
- /**
- * XOR Decode
- *
- * Takes an encoded string and key as input and generates the
- * plain-text original message
- *
- * @access private
- * @param string
- * @param string
- * @return string
- */
- function _xor_decode($string, $key)
- {
- $string = $this->_xor_merge($string, $key);
-
- $dec = '';
- for ($i = 0; $i < strlen($string); $i++)
- {
- $dec .= (substr($string, $i++, 1) ^ substr($string, $i, 1));
- }
-
- return $dec;
- }
-
- // --------------------------------------------------------------------
-
- /**
- * XOR key + string Combiner
- *
- * Takes a string and key as input and computes the difference using XOR
- *
- * @access private
- * @param string
- * @param string
- * @return string
- */
- function _xor_merge($string, $key)
- {
- $hash = $this->hash($key);
- $str = '';
- for ($i = 0; $i < strlen($string); $i++)
- {
- $str .= substr($string, $i, 1) ^ substr($hash, ($i % strlen($hash)), 1);
- }
-
- return $str;
- }
-
- // --------------------------------------------------------------------
-
- /**
- * Encrypt using Mcrypt
- *
- * @access public
- * @param string
- * @param string
- * @return string
- */
- function mcrypt_encode($data, $key)
- {
- $init_size = mcrypt_get_iv_size($this->_get_cipher(), $this->_get_mode());
- $init_vect = mcrypt_create_iv($init_size, MCRYPT_RAND);
- return $this->_add_cipher_noise($init_vect.mcrypt_encrypt($this->_get_cipher(), $key, $data, $this->_get_mode(), $init_vect), $key);
- }
-
- // --------------------------------------------------------------------
-
- /**
- * Decrypt using Mcrypt
- *
- * @access public
- * @param string
- * @param string
- * @return string
- */
- function mcrypt_decode($data, $key)
- {
- $data = $this->_remove_cipher_noise($data, $key);
- $init_size = mcrypt_get_iv_size($this->_get_cipher(), $this->_get_mode());
-
- if ($init_size > strlen($data))
- {
- return FALSE;
- }
-
- $init_vect = substr($data, 0, $init_size);
- $data = substr($data, $init_size);
- return rtrim(mcrypt_decrypt($this->_get_cipher(), $key, $data, $this->_get_mode(), $init_vect), "\0");
- }
-
- // --------------------------------------------------------------------
-
- /**
- * Adds permuted noise to the IV + encrypted data to protect
- * against Man-in-the-middle attacks on CBC mode ciphers
- * http://www.ciphersbyritter.com/GLOSSARY.HTM#IV
- *
- * Function description
- *
- * @access private
- * @param string
- * @param string
- * @return string
- */
- function _add_cipher_noise($data, $key)
- {
- $keyhash = $this->hash($key);
- $keylen = strlen($keyhash);
- $str = '';
-
- for ($i = 0, $j = 0, $len = strlen($data); $i < $len; ++$i, ++$j)
- {
- if ($j >= $keylen)
- {
- $j = 0;
- }
-
- $str .= chr((ord($data[$i]) + ord($keyhash[$j])) % 256);
- }
-
- return $str;
- }
-
- // --------------------------------------------------------------------
-
- /**
- * Removes permuted noise from the IV + encrypted data, reversing
- * _add_cipher_noise()
- *
- * Function description
- *
- * @access public
- * @param type
- * @return type
- */
- function _remove_cipher_noise($data, $key)
- {
- $keyhash = $this->hash($key);
- $keylen = strlen($keyhash);
- $str = '';
-
- for ($i = 0, $j = 0, $len = strlen($data); $i < $len; ++$i, ++$j)
- {
- if ($j >= $keylen)
- {
- $j = 0;
- }
-
- $temp = ord($data[$i]) - ord($keyhash[$j]);
-
- if ($temp < 0)
- {
- $temp = $temp + 256;
- }
-
- $str .= chr($temp);
- }
-
- return $str;
- }
-
- // --------------------------------------------------------------------
-
- /**
- * Set the Mcrypt Cipher
- *
- * @access public
- * @param constant
- * @return string
- */
- function set_cipher($cipher)
- {
- $this->_mcrypt_cipher = $cipher;
- }
-
- // --------------------------------------------------------------------
-
- /**
- * Set the Mcrypt Mode
- *
- * @access public
- * @param constant
- * @return string
- */
- function set_mode($mode)
- {
- $this->_mcrypt_mode = $mode;
- }
-
- // --------------------------------------------------------------------
-
- /**
- * Get Mcrypt cipher Value
- *
- * @access private
- * @return string
- */
- function _get_cipher()
- {
- if ($this->_mcrypt_cipher == '')
- {
- $this->_mcrypt_cipher = MCRYPT_RIJNDAEL_256;
- }
-
- return $this->_mcrypt_cipher;
- }
-
- // --------------------------------------------------------------------
-
- /**
- * Get Mcrypt Mode Value
- *
- * @access private
- * @return string
- */
- function _get_mode()
- {
- if ($this->_mcrypt_mode == '')
- {
- $this->_mcrypt_mode = MCRYPT_MODE_CBC;
- }
-
- return $this->_mcrypt_mode;
- }
-
- // --------------------------------------------------------------------
-
- /**
- * Set the Hash type
- *
- * @access public
- * @param string
- * @return string
- */
- function set_hash($type = 'sha1')
- {
- $this->_hash_type = ($type != 'sha1' AND $type != 'md5') ? 'sha1' : $type;
- }
-
- // --------------------------------------------------------------------
-
- /**
- * Hash encode a string
- *
- * @access public
- * @param string
- * @return string
- */
- function hash($str)
- {
- return ($this->_hash_type == 'sha1') ? $this->sha1($str) : md5($str);
- }
-
- // --------------------------------------------------------------------
-
- /**
- * Generate an SHA1 Hash
- *
- * @access public
- * @param string
- * @return string
- */
- function sha1($str)
- {
- if ( ! function_exists('sha1'))
- {
- if ( ! function_exists('mhash'))
- {
- require_once(BASEPATH.'libraries/Sha1.php');
- $SH = new CI_SHA;
- return $SH->generate($str);
- }
- else
- {
- return bin2hex(mhash(MHASH_SHA1, $str));
- }
- }
- else
- {
- return sha1($str);
- }
- }
-
-}
-
-// END CI_Encrypt class
-
-/* End of file Encrypt.php */
-/* Location: ./system/libraries/Encrypt.php */
diff --git a/system/libraries/Encryption.php b/system/libraries/Encryption.php
new file mode 100644
index 000000000..572cab3fc
--- /dev/null
+++ b/system/libraries/Encryption.php
@@ -0,0 +1,939 @@
+<?php
+/**
+ * CodeIgniter
+ *
+ * An open source application development framework for PHP
+ *
+ * This content is released under the MIT License (MIT)
+ *
+ * Copyright (c) 2019 - 2022, CodeIgniter Foundation
+ *
+ * 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.
+ *
+ * @package CodeIgniter
+ * @author EllisLab Dev Team
+ * @copyright Copyright (c) 2008 - 2014, EllisLab, Inc. (https://ellislab.com/)
+ * @copyright Copyright (c) 2014 - 2019, British Columbia Institute of Technology (https://bcit.ca/)
+ * @copyright Copyright (c) 2019 - 2022, CodeIgniter Foundation (https://codeigniter.com/)
+ * @license https://opensource.org/licenses/MIT MIT License
+ * @link https://codeigniter.com
+ * @since Version 3.0.0
+ * @filesource
+ */
+defined('BASEPATH') OR exit('No direct script access allowed');
+
+/**
+ * CodeIgniter Encryption Class
+ *
+ * Provides two-way keyed encryption via PHP's MCrypt and/or OpenSSL extensions.
+ *
+ * @package CodeIgniter
+ * @subpackage Libraries
+ * @category Libraries
+ * @author Andrey Andreev
+ * @link https://codeigniter.com/userguide3/libraries/encryption.html
+ */
+class CI_Encryption {
+
+ /**
+ * Encryption cipher
+ *
+ * @var string
+ */
+ protected $_cipher = 'aes-128';
+
+ /**
+ * Cipher mode
+ *
+ * @var string
+ */
+ protected $_mode = 'cbc';
+
+ /**
+ * Cipher handle
+ *
+ * @var mixed
+ */
+ protected $_handle;
+
+ /**
+ * Encryption key
+ *
+ * @var string
+ */
+ protected $_key;
+
+ /**
+ * PHP extension to be used
+ *
+ * @var string
+ */
+ protected $_driver;
+
+ /**
+ * List of usable drivers (PHP extensions)
+ *
+ * @var array
+ */
+ protected $_drivers = array();
+
+ /**
+ * List of available modes
+ *
+ * @var array
+ */
+ protected $_modes = array(
+ 'mcrypt' => array(
+ 'cbc' => 'cbc',
+ 'ecb' => 'ecb',
+ 'ofb' => 'nofb',
+ 'ofb8' => 'ofb',
+ 'cfb' => 'ncfb',
+ 'cfb8' => 'cfb',
+ 'ctr' => 'ctr',
+ 'stream' => 'stream'
+ ),
+ 'openssl' => array(
+ 'cbc' => 'cbc',
+ 'ecb' => 'ecb',
+ 'ofb' => 'ofb',
+ 'cfb' => 'cfb',
+ 'cfb8' => 'cfb8',
+ 'ctr' => 'ctr',
+ 'stream' => '',
+ 'xts' => 'xts'
+ )
+ );
+
+ /**
+ * List of supported HMAC algorithms
+ *
+ * name => digest size pairs
+ *
+ * @var array
+ */
+ protected $_digests = array(
+ 'sha224' => 28,
+ 'sha256' => 32,
+ 'sha384' => 48,
+ 'sha512' => 64
+ );
+
+ /**
+ * mbstring.func_overload flag
+ *
+ * @var bool
+ */
+ protected static $func_overload;
+
+ // --------------------------------------------------------------------
+
+ /**
+ * Class constructor
+ *
+ * @param array $params Configuration parameters
+ * @return void
+ */
+ public function __construct(array $params = array())
+ {
+ $this->_drivers = array(
+ 'mcrypt' => defined('MCRYPT_DEV_URANDOM'),
+ 'openssl' => extension_loaded('openssl')
+ );
+
+ if ( ! $this->_drivers['mcrypt'] && ! $this->_drivers['openssl'])
+ {
+ show_error('Encryption: Unable to find an available encryption driver.');
+ }
+
+ isset(self::$func_overload) OR self::$func_overload = ( ! is_php('8.0') && extension_loaded('mbstring') && @ini_get('mbstring.func_overload'));
+ $this->initialize($params);
+
+ if ( ! isset($this->_key) && self::strlen($key = config_item('encryption_key')) > 0)
+ {
+ $this->_key = $key;
+ }
+
+ log_message('info', 'Encryption Class Initialized');
+ }
+
+ // --------------------------------------------------------------------
+
+ /**
+ * Initialize
+ *
+ * @param array $params Configuration parameters
+ * @return CI_Encryption
+ */
+ public function initialize(array $params)
+ {
+ if ( ! empty($params['driver']))
+ {
+ if (isset($this->_drivers[$params['driver']]))
+ {
+ if ($this->_drivers[$params['driver']])
+ {
+ $this->_driver = $params['driver'];
+ }
+ else
+ {
+ log_message('error', "Encryption: Driver '".$params['driver']."' is not available.");
+ }
+ }
+ else
+ {
+ log_message('error', "Encryption: Unknown driver '".$params['driver']."' cannot be configured.");
+ }
+ }
+
+ if (empty($this->_driver))
+ {
+ $this->_driver = ($this->_drivers['openssl'] === TRUE)
+ ? 'openssl'
+ : 'mcrypt';
+
+ log_message('debug', "Encryption: Auto-configured driver '".$this->_driver."'.");
+ }
+
+ empty($params['cipher']) && $params['cipher'] = $this->_cipher;
+ empty($params['key']) OR $this->_key = $params['key'];
+ $this->{'_'.$this->_driver.'_initialize'}($params);
+ return $this;
+ }
+
+ // --------------------------------------------------------------------
+
+ /**
+ * Initialize MCrypt
+ *
+ * @param array $params Configuration parameters
+ * @return void
+ */
+ protected function _mcrypt_initialize($params)
+ {
+ if ( ! empty($params['cipher']))
+ {
+ $params['cipher'] = strtolower($params['cipher']);
+ $this->_cipher_alias($params['cipher']);
+
+ if ( ! in_array($params['cipher'], mcrypt_list_algorithms(), TRUE))
+ {
+ log_message('error', 'Encryption: MCrypt cipher '.strtoupper($params['cipher']).' is not available.');
+ }
+ else
+ {
+ $this->_cipher = $params['cipher'];
+ }
+ }
+
+ if ( ! empty($params['mode']))
+ {
+ $params['mode'] = strtolower($params['mode']);
+ if ( ! isset($this->_modes['mcrypt'][$params['mode']]))
+ {
+ log_message('error', 'Encryption: MCrypt mode '.strtoupper($params['mode']).' is not available.');
+ }
+ else
+ {
+ $this->_mode = $this->_modes['mcrypt'][$params['mode']];
+ }
+ }
+
+ if (isset($this->_cipher, $this->_mode))
+ {
+ if (is_resource($this->_handle)
+ && (strtolower(mcrypt_enc_get_algorithms_name($this->_handle)) !== $this->_cipher
+ OR strtolower(mcrypt_enc_get_modes_name($this->_handle)) !== $this->_mode)
+ )
+ {
+ mcrypt_module_close($this->_handle);
+ }
+
+ if ($this->_handle = mcrypt_module_open($this->_cipher, '', $this->_mode, ''))
+ {
+ log_message('info', 'Encryption: MCrypt cipher '.strtoupper($this->_cipher).' initialized in '.strtoupper($this->_mode).' mode.');
+ }
+ else
+ {
+ log_message('error', 'Encryption: Unable to initialize MCrypt with cipher '.strtoupper($this->_cipher).' in '.strtoupper($this->_mode).' mode.');
+ }
+ }
+ }
+
+ // --------------------------------------------------------------------
+
+ /**
+ * Initialize OpenSSL
+ *
+ * @param array $params Configuration parameters
+ * @return void
+ */
+ protected function _openssl_initialize($params)
+ {
+ if ( ! empty($params['cipher']))
+ {
+ $params['cipher'] = strtolower($params['cipher']);
+ $this->_cipher_alias($params['cipher']);
+ $this->_cipher = $params['cipher'];
+ }
+
+ if ( ! empty($params['mode']))
+ {
+ $params['mode'] = strtolower($params['mode']);
+ if ( ! isset($this->_modes['openssl'][$params['mode']]))
+ {
+ log_message('error', 'Encryption: OpenSSL mode '.strtoupper($params['mode']).' is not available.');
+ }
+ else
+ {
+ $this->_mode = $this->_modes['openssl'][$params['mode']];
+ }
+ }
+
+ if (isset($this->_cipher, $this->_mode))
+ {
+ // This is mostly for the stream mode, which doesn't get suffixed in OpenSSL
+ $handle = empty($this->_mode)
+ ? $this->_cipher
+ : $this->_cipher.'-'.$this->_mode;
+
+ if ( ! in_array($handle, openssl_get_cipher_methods(), TRUE))
+ {
+ $this->_handle = NULL;
+ log_message('error', 'Encryption: Unable to initialize OpenSSL with method '.strtoupper($handle).'.');
+ }
+ else
+ {
+ $this->_handle = $handle;
+ log_message('info', 'Encryption: OpenSSL initialized with method '.strtoupper($handle).'.');
+ }
+ }
+ }
+
+ // --------------------------------------------------------------------
+
+ /**
+ * Create a random key
+ *
+ * @param int $length Output length
+ * @return string
+ */
+ public function create_key($length)
+ {
+ if (function_exists('random_bytes'))
+ {
+ try
+ {
+ return random_bytes((int) $length);
+ }
+ catch (Exception $e)
+ {
+ log_message('error', $e->getMessage());
+ return FALSE;
+ }
+ }
+ elseif (defined('MCRYPT_DEV_URANDOM'))
+ {
+ return mcrypt_create_iv($length, MCRYPT_DEV_URANDOM);
+ }
+
+ $is_secure = NULL;
+ $key = openssl_random_pseudo_bytes($length, $is_secure);
+ return ($is_secure === TRUE)
+ ? $key
+ : FALSE;
+ }
+
+ // --------------------------------------------------------------------
+
+ /**
+ * Encrypt
+ *
+ * @param string $data Input data
+ * @param array $params Input parameters
+ * @return string
+ */
+ public function encrypt($data, array $params = NULL)
+ {
+ if (($params = $this->_get_params($params)) === FALSE)
+ {
+ return FALSE;
+ }
+
+ isset($params['key']) OR $params['key'] = $this->hkdf($this->_key, 'sha512', NULL, self::strlen($this->_key), 'encryption');
+
+ if (($data = $this->{'_'.$this->_driver.'_encrypt'}($data, $params)) === FALSE)
+ {
+ return FALSE;
+ }
+
+ $params['base64'] && $data = base64_encode($data);
+
+ if (isset($params['hmac_digest']))
+ {
+ isset($params['hmac_key']) OR $params['hmac_key'] = $this->hkdf($this->_key, 'sha512', NULL, NULL, 'authentication');
+ return hash_hmac($params['hmac_digest'], $data, $params['hmac_key'], ! $params['base64']).$data;
+ }
+
+ return $data;
+ }
+
+ // --------------------------------------------------------------------
+
+ /**
+ * Encrypt via MCrypt
+ *
+ * @param string $data Input data
+ * @param array $params Input parameters
+ * @return string
+ */
+ protected function _mcrypt_encrypt($data, $params)
+ {
+ if ( ! is_resource($params['handle']))
+ {
+ return FALSE;
+ }
+
+ // The greater-than-1 comparison is mostly a work-around for a bug,
+ // where 1 is returned for ARCFour instead of 0.
+ $iv = (($iv_size = mcrypt_enc_get_iv_size($params['handle'])) > 1)
+ ? $this->create_key($iv_size)
+ : NULL;
+
+ if (mcrypt_generic_init($params['handle'], $params['key'], $iv) < 0)
+ {
+ if ($params['handle'] !== $this->_handle)
+ {
+ mcrypt_module_close($params['handle']);
+ }
+
+ return FALSE;
+ }
+
+ // Use PKCS#7 padding in order to ensure compatibility with OpenSSL
+ // and other implementations outside of PHP.
+ if (in_array(strtolower(mcrypt_enc_get_modes_name($params['handle'])), array('cbc', 'ecb'), TRUE))
+ {
+ $block_size = mcrypt_enc_get_block_size($params['handle']);
+ $pad = $block_size - (self::strlen($data) % $block_size);
+ $data .= str_repeat(chr($pad), $pad);
+ }
+
+ // Work-around for yet another strange behavior in MCrypt.
+ //
+ // When encrypting in ECB mode, the IV is ignored. Yet
+ // mcrypt_enc_get_iv_size() returns a value larger than 0
+ // even if ECB is used AND mcrypt_generic_init() complains
+ // if you don't pass an IV with length equal to the said
+ // return value.
+ //
+ // This probably would've been fine (even though still wasteful),
+ // but OpenSSL isn't that dumb and we need to make the process
+ // portable, so ...
+ $data = (mcrypt_enc_get_modes_name($params['handle']) !== 'ECB')
+ ? $iv.mcrypt_generic($params['handle'], $data)
+ : mcrypt_generic($params['handle'], $data);
+
+ mcrypt_generic_deinit($params['handle']);
+ if ($params['handle'] !== $this->_handle)
+ {
+ mcrypt_module_close($params['handle']);
+ }
+
+ return $data;
+ }
+
+ // --------------------------------------------------------------------
+
+ /**
+ * Encrypt via OpenSSL
+ *
+ * @param string $data Input data
+ * @param array $params Input parameters
+ * @return string
+ */
+ protected function _openssl_encrypt($data, $params)
+ {
+ if (empty($params['handle']))
+ {
+ return FALSE;
+ }
+
+ $iv = ($iv_size = openssl_cipher_iv_length($params['handle']))
+ ? $this->create_key($iv_size)
+ : '';
+
+ $data = openssl_encrypt(
+ $data,
+ $params['handle'],
+ $params['key'],
+ OPENSSL_RAW_DATA,
+ $iv
+ );
+
+ if ($data === FALSE)
+ {
+ return FALSE;
+ }
+
+ return $iv.$data;
+ }
+
+ // --------------------------------------------------------------------
+
+ /**
+ * Decrypt
+ *
+ * @param string $data Encrypted data
+ * @param array $params Input parameters
+ * @return string
+ */
+ public function decrypt($data, array $params = NULL)
+ {
+ if (($params = $this->_get_params($params)) === FALSE)
+ {
+ return FALSE;
+ }
+
+ if (isset($params['hmac_digest']))
+ {
+ // This might look illogical, but it is done during encryption as well ...
+ // The 'base64' value is effectively an inverted "raw data" parameter
+ $digest_size = ($params['base64'])
+ ? $this->_digests[$params['hmac_digest']] * 2
+ : $this->_digests[$params['hmac_digest']];
+
+ if (self::strlen($data) <= $digest_size)
+ {
+ return FALSE;
+ }
+
+ $hmac_input = self::substr($data, 0, $digest_size);
+ $data = self::substr($data, $digest_size);
+
+ isset($params['hmac_key']) OR $params['hmac_key'] = $this->hkdf($this->_key, 'sha512', NULL, NULL, 'authentication');
+ $hmac_check = hash_hmac($params['hmac_digest'], $data, $params['hmac_key'], ! $params['base64']);
+
+ // Time-attack-safe comparison
+ $diff = 0;
+ for ($i = 0; $i < $digest_size; $i++)
+ {
+ $diff |= ord($hmac_input[$i]) ^ ord($hmac_check[$i]);
+ }
+
+ if ($diff !== 0)
+ {
+ return FALSE;
+ }
+ }
+
+ if ($params['base64'])
+ {
+ $data = base64_decode($data);
+ }
+
+ isset($params['key']) OR $params['key'] = $this->hkdf($this->_key, 'sha512', NULL, self::strlen($this->_key), 'encryption');
+
+ return $this->{'_'.$this->_driver.'_decrypt'}($data, $params);
+ }
+
+ // --------------------------------------------------------------------
+
+ /**
+ * Decrypt via MCrypt
+ *
+ * @param string $data Encrypted data
+ * @param array $params Input parameters
+ * @return string
+ */
+ protected function _mcrypt_decrypt($data, $params)
+ {
+ if ( ! is_resource($params['handle']))
+ {
+ return FALSE;
+ }
+
+ // The greater-than-1 comparison is mostly a work-around for a bug,
+ // where 1 is returned for ARCFour instead of 0.
+ if (($iv_size = mcrypt_enc_get_iv_size($params['handle'])) > 1)
+ {
+ if (mcrypt_enc_get_modes_name($params['handle']) !== 'ECB')
+ {
+ $iv = self::substr($data, 0, $iv_size);
+ $data = self::substr($data, $iv_size);
+ }
+ else
+ {
+ // MCrypt is dumb and this is ignored, only size matters
+ $iv = str_repeat("\x0", $iv_size);
+ }
+ }
+ else
+ {
+ $iv = '';
+ }
+
+ if (mcrypt_generic_init($params['handle'], $params['key'], $iv) < 0)
+ {
+ if ($params['handle'] !== $this->_handle)
+ {
+ mcrypt_module_close($params['handle']);
+ }
+
+ return FALSE;
+ }
+
+ $data = mdecrypt_generic($params['handle'], $data);
+ // Remove PKCS#7 padding, if necessary
+ if (in_array(strtolower(mcrypt_enc_get_modes_name($params['handle'])), array('cbc', 'ecb'), TRUE))
+ {
+ $data = self::substr($data, 0, -ord($data[self::strlen($data)-1]));
+ }
+
+ mcrypt_generic_deinit($params['handle']);
+ if ($params['handle'] !== $this->_handle)
+ {
+ mcrypt_module_close($params['handle']);
+ }
+
+ return $data;
+ }
+
+ // --------------------------------------------------------------------
+
+ /**
+ * Decrypt via OpenSSL
+ *
+ * @param string $data Encrypted data
+ * @param array $params Input parameters
+ * @return string
+ */
+ protected function _openssl_decrypt($data, $params)
+ {
+ if ($iv_size = openssl_cipher_iv_length($params['handle']))
+ {
+ $iv = self::substr($data, 0, $iv_size);
+ $data = self::substr($data, $iv_size);
+ }
+ else
+ {
+ $iv = '';
+ }
+
+ return empty($params['handle'])
+ ? FALSE
+ : openssl_decrypt(
+ $data,
+ $params['handle'],
+ $params['key'],
+ OPENSSL_RAW_DATA,
+ $iv
+ );
+ }
+
+ // --------------------------------------------------------------------
+
+ /**
+ * Get params
+ *
+ * @param array $params Input parameters
+ * @return array
+ */
+ protected function _get_params($params)
+ {
+ if (empty($params))
+ {
+ return isset($this->_cipher, $this->_mode, $this->_key, $this->_handle)
+ ? array(
+ 'handle' => $this->_handle,
+ 'cipher' => $this->_cipher,
+ 'mode' => $this->_mode,
+ 'key' => NULL,
+ 'base64' => TRUE,
+ 'hmac_digest' => 'sha512',
+ 'hmac_key' => NULL
+ )
+ : FALSE;
+ }
+ elseif ( ! isset($params['cipher'], $params['mode'], $params['key']))
+ {
+ return FALSE;
+ }
+
+ if (isset($params['mode']))
+ {
+ $params['mode'] = strtolower($params['mode']);
+ if ( ! isset($this->_modes[$this->_driver][$params['mode']]))
+ {
+ return FALSE;
+ }
+
+ $params['mode'] = $this->_modes[$this->_driver][$params['mode']];
+ }
+
+ if (isset($params['hmac']) && $params['hmac'] === FALSE)
+ {
+ $params['hmac_digest'] = $params['hmac_key'] = NULL;
+ }
+ else
+ {
+ if ( ! isset($params['hmac_key']))
+ {
+ return FALSE;
+ }
+ elseif (isset($params['hmac_digest']))
+ {
+ $params['hmac_digest'] = strtolower($params['hmac_digest']);
+ if ( ! isset($this->_digests[$params['hmac_digest']]))
+ {
+ return FALSE;
+ }
+ }
+ else
+ {
+ $params['hmac_digest'] = 'sha512';
+ }
+ }
+
+ $params = array(
+ 'handle' => NULL,
+ 'cipher' => $params['cipher'],
+ 'mode' => $params['mode'],
+ 'key' => $params['key'],
+ 'base64' => isset($params['raw_data']) ? ! $params['raw_data'] : FALSE,
+ 'hmac_digest' => $params['hmac_digest'],
+ 'hmac_key' => $params['hmac_key']
+ );
+
+ $this->_cipher_alias($params['cipher']);
+ $params['handle'] = ($params['cipher'] !== $this->_cipher OR $params['mode'] !== $this->_mode)
+ ? $this->{'_'.$this->_driver.'_get_handle'}($params['cipher'], $params['mode'])
+ : $this->_handle;
+
+ return $params;
+ }
+
+ // --------------------------------------------------------------------
+
+ /**
+ * Get MCrypt handle
+ *
+ * @param string $cipher Cipher name
+ * @param string $mode Encryption mode
+ * @return resource
+ */
+ protected function _mcrypt_get_handle($cipher, $mode)
+ {
+ return mcrypt_module_open($cipher, '', $mode, '');
+ }
+
+ // --------------------------------------------------------------------
+
+ /**
+ * Get OpenSSL handle
+ *
+ * @param string $cipher Cipher name
+ * @param string $mode Encryption mode
+ * @return string
+ */
+ protected function _openssl_get_handle($cipher, $mode)
+ {
+ // OpenSSL methods aren't suffixed with '-stream' for this mode
+ return ($mode === 'stream')
+ ? $cipher
+ : $cipher.'-'.$mode;
+ }
+
+ // --------------------------------------------------------------------
+
+ /**
+ * Cipher alias
+ *
+ * Tries to translate cipher names between MCrypt and OpenSSL's "dialects".
+ *
+ * @param string $cipher Cipher name
+ * @return void
+ */
+ protected function _cipher_alias(&$cipher)
+ {
+ static $dictionary;
+
+ if (empty($dictionary))
+ {
+ $dictionary = array(
+ 'mcrypt' => array(
+ 'aes-128' => 'rijndael-128',
+ 'aes-192' => 'rijndael-128',
+ 'aes-256' => 'rijndael-128',
+ 'des3-ede3' => 'tripledes',
+ 'bf' => 'blowfish',
+ 'cast5' => 'cast-128',
+ 'rc4' => 'arcfour',
+ 'rc4-40' => 'arcfour'
+ ),
+ 'openssl' => array(
+ 'rijndael-128' => 'aes-128',
+ 'tripledes' => 'des-ede3',
+ 'blowfish' => 'bf',
+ 'cast-128' => 'cast5',
+ 'arcfour' => 'rc4-40',
+ 'rc4' => 'rc4-40'
+ )
+ );
+
+ // Notes:
+ //
+ // - Rijndael-128 is, at the same time all three of AES-128,
+ // AES-192 and AES-256. The only difference between them is
+ // the key size. Rijndael-192, Rijndael-256 on the other hand
+ // also have different block sizes and are NOT AES-compatible.
+ //
+ // - Blowfish is said to be supporting key sizes between
+ // 4 and 56 bytes, but it appears that between MCrypt and
+ // OpenSSL, only those of 16 and more bytes are compatible.
+ // Also, don't know what MCrypt's 'blowfish-compat' is.
+ //
+ // - CAST-128/CAST5 produces a longer cipher when encrypted via
+ // OpenSSL, but (strangely enough) can be decrypted by either
+ // extension anyway.
+ // Also, it appears that OpenSSL uses 16 rounds regardless of
+ // the key size, while RFC2144 says that for key sizes lower
+ // than 11 bytes, only 12 rounds should be used. This makes
+ // it portable only with keys of between 11 and 16 bytes.
+ //
+ // - RC4 (ARCFour) has a strange implementation under OpenSSL.
+ // Its 'rc4-40' cipher method seems to work flawlessly, yet
+ // there's another one, 'rc4' that only works with a 16-byte key.
+ //
+ // - DES is compatible, but doesn't need an alias.
+ //
+ // Other seemingly matching ciphers between MCrypt, OpenSSL:
+ //
+ // - RC2 is NOT compatible and only an obscure forum post
+ // confirms that it is MCrypt's fault.
+ }
+
+ if (isset($dictionary[$this->_driver][$cipher]))
+ {
+ $cipher = $dictionary[$this->_driver][$cipher];
+ }
+ }
+
+ // --------------------------------------------------------------------
+
+ /**
+ * HKDF
+ *
+ * @link https://tools.ietf.org/rfc/rfc5869.txt
+ * @param $key Input key
+ * @param $digest A SHA-2 hashing algorithm
+ * @param $salt Optional salt
+ * @param $length Output length (defaults to the selected digest size)
+ * @param $info Optional context/application-specific info
+ * @return string A pseudo-random key
+ */
+ public function hkdf($key, $digest = 'sha512', $salt = NULL, $length = NULL, $info = '')
+ {
+ if ( ! isset($this->_digests[$digest]))
+ {
+ return FALSE;
+ }
+
+ if (empty($length) OR ! is_int($length))
+ {
+ $length = $this->_digests[$digest];
+ }
+ elseif ($length > (255 * $this->_digests[$digest]))
+ {
+ return FALSE;
+ }
+
+ self::strlen($salt) OR $salt = str_repeat("\0", $this->_digests[$digest]);
+
+ $prk = hash_hmac($digest, $key, $salt, TRUE);
+ $key = '';
+ for ($key_block = '', $block_index = 1; self::strlen($key) < $length; $block_index++)
+ {
+ $key_block = hash_hmac($digest, $key_block.$info.chr($block_index), $prk, TRUE);
+ $key .= $key_block;
+ }
+
+ return self::substr($key, 0, $length);
+ }
+
+ // --------------------------------------------------------------------
+
+ /**
+ * __get() magic
+ *
+ * @param string $key Property name
+ * @return mixed
+ */
+ public function __get($key)
+ {
+ // Because aliases
+ if ($key === 'mode')
+ {
+ return array_search($this->_mode, $this->_modes[$this->_driver], TRUE);
+ }
+ elseif (in_array($key, array('cipher', 'driver', 'drivers', 'digests'), TRUE))
+ {
+ return $this->{'_'.$key};
+ }
+
+ return NULL;
+ }
+
+ // --------------------------------------------------------------------
+
+ /**
+ * Byte-safe strlen()
+ *
+ * @param string $str
+ * @return int
+ */
+ protected static function strlen($str)
+ {
+ return (self::$func_overload)
+ ? mb_strlen((string) $str, '8bit')
+ : strlen((string) $str);
+ }
+
+ // --------------------------------------------------------------------
+
+ /**
+ * Byte-safe substr()
+ *
+ * @param string $str
+ * @param int $start
+ * @param int $length
+ * @return string
+ */
+ protected static function substr($str, $start, $length = NULL)
+ {
+ if (self::$func_overload)
+ {
+ return mb_substr($str, $start, $length, '8bit');
+ }
+
+ return isset($length)
+ ? substr($str, $start, $length)
+ : substr($str, $start);
+ }
+}
diff --git a/system/libraries/Form_validation.php b/system/libraries/Form_validation.php
index 3839fe42b..dd1685db1 100644
--- a/system/libraries/Form_validation.php
+++ b/system/libraries/Form_validation.php
@@ -1,19 +1,42 @@
-<?php if ( ! defined('BASEPATH')) exit('No direct script access allowed');
+<?php
/**
* CodeIgniter
*
- * An open source application development framework for PHP 5.1.6 or newer
+ * An open source application development framework for PHP
*
- * @package CodeIgniter
- * @author ExpressionEngine Dev Team
- * @copyright Copyright (c) 2008 - 2014, EllisLab, Inc.
- * @license http://codeigniter.com/user_guide/license.html
- * @link http://codeigniter.com
- * @since Version 1.0
+ * This content is released under the MIT License (MIT)
+ *
+ * Copyright (c) 2019 - 2022, CodeIgniter Foundation
+ *
+ * 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.
+ *
+ * @package CodeIgniter
+ * @author EllisLab Dev Team
+ * @copyright Copyright (c) 2008 - 2014, EllisLab, Inc. (https://ellislab.com/)
+ * @copyright Copyright (c) 2014 - 2019, British Columbia Institute of Technology (https://bcit.ca/)
+ * @copyright Copyright (c) 2019 - 2022, CodeIgniter Foundation (https://codeigniter.com/)
+ * @license https://opensource.org/licenses/MIT MIT License
+ * @link https://codeigniter.com
+ * @since Version 1.0.0
* @filesource
*/
-
-// ------------------------------------------------------------------------
+defined('BASEPATH') OR exit('No direct script access allowed');
/**
* Form Validation Class
@@ -21,41 +44,103 @@
* @package CodeIgniter
* @subpackage Libraries
* @category Validation
- * @author ExpressionEngine Dev Team
- * @link http://codeigniter.com/user_guide/libraries/form_validation.html
+ * @author EllisLab Dev Team
+ * @link https://codeigniter.com/userguide3/libraries/form_validation.html
*/
class CI_Form_validation {
+ /**
+ * Reference to the CodeIgniter instance
+ *
+ * @var object
+ */
protected $CI;
- protected $_field_data = array();
- protected $_config_rules = array();
- protected $_error_array = array();
- protected $_error_messages = array();
- protected $_error_prefix = '<p>';
- protected $_error_suffix = '</p>';
- protected $error_string = '';
- protected $_safe_form_data = FALSE;
/**
- * Constructor
+ * Validation data for the current form submission
+ *
+ * @var array
+ */
+ protected $_field_data = array();
+
+ /**
+ * Validation rules for the current form
+ *
+ * @var array
+ */
+ protected $_config_rules = array();
+
+ /**
+ * Array of validation errors
+ *
+ * @var array
+ */
+ protected $_error_array = array();
+
+ /**
+ * Array of custom error messages
+ *
+ * @var array
+ */
+ protected $_error_messages = array();
+
+ /**
+ * Start tag for error wrapping
+ *
+ * @var string
+ */
+ protected $_error_prefix = '<p>';
+
+ /**
+ * End tag for error wrapping
+ *
+ * @var string
+ */
+ protected $_error_suffix = '</p>';
+
+ /**
+ * Custom error message
+ *
+ * @var string
+ */
+ protected $error_string = '';
+
+ /**
+ * Custom data to validate
+ *
+ * @var array
+ */
+ public $validation_data = array();
+
+ /**
+ * Initialize Form_Validation class
+ *
+ * @param array $rules
+ * @return void
*/
public function __construct($rules = array())
{
$this->CI =& get_instance();
+ // applies delimiters set in config file.
+ if (isset($rules['error_prefix']))
+ {
+ $this->_error_prefix = $rules['error_prefix'];
+ unset($rules['error_prefix']);
+ }
+ if (isset($rules['error_suffix']))
+ {
+ $this->_error_suffix = $rules['error_suffix'];
+ unset($rules['error_suffix']);
+ }
+
// Validation rules can be stored in a config file.
$this->_config_rules = $rules;
// Automatically load the form helper
$this->CI->load->helper('form');
- // Set the character encoding in MB.
- if (function_exists('mb_internal_encoding'))
- {
- mb_internal_encoding($this->CI->config->item('charset'));
- }
-
- log_message('debug', "Form Validation Class Initialized");
+ log_message('info', 'Form Validation Class Initialized');
}
// --------------------------------------------------------------------
@@ -64,86 +149,99 @@ class CI_Form_validation {
* Set Rules
*
* This function takes an array of field names and validation
- * rules as input, validates the info, and stores it
+ * rules as input, any custom error messages, validates the info,
+ * and stores it
*
- * @access public
- * @param mixed
- * @param string
- * @return void
+ * @param mixed $field
+ * @param string $label
+ * @param mixed $rules
+ * @param array $errors
+ * @return CI_Form_validation
*/
- public function set_rules($field, $label = '', $rules = '')
+ public function set_rules($field, $label = null, $rules = null, $errors = array())
{
// No reason to set rules if we have no POST data
- if (count($_POST) == 0)
+ // or a validation array has not been specified
+ if ($this->CI->input->method() !== 'post' && empty($this->validation_data))
{
return $this;
}
- // If an array was passed via the first parameter instead of indidual string
+ // If an array was passed via the first parameter instead of individual string
// values we cycle through it and recursively call this function.
if (is_array($field))
{
foreach ($field as $row)
{
// Houston, we have a problem...
- if ( ! isset($row['field']) OR ! isset($row['rules']))
+ if ( ! isset($row['field'], $row['rules']))
{
continue;
}
// If the field label wasn't passed we use the field name
- $label = ( ! isset($row['label'])) ? $row['field'] : $row['label'];
+ $label = isset($row['label']) ? $row['label'] : $row['field'];
+
+ // Add the custom error message array
+ $errors = (isset($row['errors']) && is_array($row['errors'])) ? $row['errors'] : array();
// Here we go!
- $this->set_rules($row['field'], $label, $row['rules']);
+ $this->set_rules($row['field'], $label, $row['rules'], $errors);
}
+
return $this;
}
+ elseif ( ! isset($rules))
+ {
+ throw new BadMethodCallException('Form_validation: set_rules() called without a $rules parameter');
+ }
- // No fields? Nothing to do...
- if ( ! is_string($field) OR ! is_string($rules) OR $field == '')
+ // No fields or no rules? Nothing to do...
+ if ( ! is_string($field) OR $field === '' OR empty($rules))
{
- return $this;
+ throw new RuntimeException('Form_validation: set_rules() called with an empty $rules parameter');
+ }
+ elseif ( ! is_array($rules))
+ {
+ // BC: Convert pipe-separated rules string to an array
+ if ( ! is_string($rules))
+ {
+ throw new InvalidArgumentException('Form_validation: set_rules() expect $rules to be string or array; '.gettype($rules).' given');
+ }
+
+ $rules = preg_split('/\|(?![^\[]*\])/', $rules);
}
// If the field label wasn't passed we use the field name
- $label = ($label == '') ? $field : $label;
+ $label = ($label === '') ? $field : $label;
+
+ $indexes = array();
- // Is the field name an array? We test for the existence of a bracket "[" in
- // the field name to determine this. If it is an array, we break it apart
+ // Is the field name an array? If it is an array, we break it apart
// into its components so that we can fetch the corresponding POST data later
- if (strpos($field, '[') !== FALSE AND preg_match_all('/\[(.*?)\]/', $field, $matches))
+ if (($is_array = (bool) preg_match_all('/\[(.*?)\]/', $field, $matches)) === TRUE)
{
- // Note: Due to a bug in current() that affects some versions
- // of PHP we can not pass function call directly into it
- $x = explode('[', $field);
- $indexes[] = current($x);
+ sscanf($field, '%[^[][', $indexes[0]);
- for ($i = 0; $i < count($matches['0']); $i++)
+ for ($i = 0, $c = count($matches[0]); $i < $c; $i++)
{
- if ($matches['1'][$i] != '')
+ if ($matches[1][$i] !== '')
{
- $indexes[] = $matches['1'][$i];
+ $indexes[] = $matches[1][$i];
}
}
-
- $is_array = TRUE;
- }
- else
- {
- $indexes = array();
- $is_array = FALSE;
}
// Build our master array
$this->_field_data[$field] = array(
- 'field' => $field,
- 'label' => $label,
- 'rules' => $rules,
- 'is_array' => $is_array,
- 'keys' => $indexes,
- 'postdata' => NULL,
- 'error' => ''
+ 'field' => $field,
+ 'label' => $label,
+ 'rules' => $rules,
+ 'errors' => $errors,
+ 'is_array' => $is_array,
+ 'keys' => $indexes,
+ 'postdata' => NULL,
+ 'error' => ''
);
return $this;
@@ -152,15 +250,39 @@ class CI_Form_validation {
// --------------------------------------------------------------------
/**
+ * By default, form validation uses the $_POST array to validate
+ *
+ * If an array is set through this method, then this array will
+ * be used instead of the $_POST array
+ *
+ * Note that if you are validating multiple arrays, then the
+ * reset_validation() function should be called after validating
+ * each array due to the limitations of CI's singleton
+ *
+ * @param array $data
+ * @return CI_Form_validation
+ */
+ public function set_data(array $data)
+ {
+ if ( ! empty($data))
+ {
+ $this->validation_data = $data;
+ }
+
+ return $this;
+ }
+
+ // --------------------------------------------------------------------
+
+ /**
* Set Error Message
*
- * Lets users set their own error messages on the fly. Note: The key
- * name has to match the function name that it corresponds to.
+ * Lets users set their own error messages on the fly. Note:
+ * The key name has to match the function name that it corresponds to.
*
- * @access public
- * @param string
+ * @param array
* @param string
- * @return string
+ * @return CI_Form_validation
*/
public function set_message($lang, $val = '')
{
@@ -170,7 +292,6 @@ class CI_Form_validation {
}
$this->_error_messages = array_merge($this->_error_messages, $lang);
-
return $this;
}
@@ -181,16 +302,14 @@ class CI_Form_validation {
*
* Permits a prefix/suffix to be added to each error message
*
- * @access public
* @param string
* @param string
- * @return void
+ * @return CI_Form_validation
*/
public function set_error_delimiters($prefix = '<p>', $suffix = '</p>')
{
$this->_error_prefix = $prefix;
$this->_error_suffix = $suffix;
-
return $this;
}
@@ -201,23 +320,24 @@ class CI_Form_validation {
*
* Gets the error message associated with a particular field
*
- * @access public
- * @param string the field name
- * @return void
+ * @param string $field Field name
+ * @param string $prefix HTML start tag
+ * @param string $suffix HTML end tag
+ * @return string
*/
- public function error($field = '', $prefix = '', $suffix = '')
+ public function error($field, $prefix = '', $suffix = '')
{
- if ( ! isset($this->_field_data[$field]['error']) OR $this->_field_data[$field]['error'] == '')
+ if (empty($this->_field_data[$field]['error']))
{
return '';
}
- if ($prefix == '')
+ if ($prefix === '')
{
$prefix = $this->_error_prefix;
}
- if ($suffix == '')
+ if ($suffix === '')
{
$suffix = $this->_error_suffix;
}
@@ -228,29 +348,42 @@ class CI_Form_validation {
// --------------------------------------------------------------------
/**
+ * Get Array of Error Messages
+ *
+ * Returns the error messages as an array
+ *
+ * @return array
+ */
+ public function error_array()
+ {
+ return $this->_error_array;
+ }
+
+ // --------------------------------------------------------------------
+
+ /**
* Error String
*
* Returns the error messages as a string, wrapped in the error delimiters
*
- * @access public
* @param string
* @param string
- * @return str
+ * @return string
*/
public function error_string($prefix = '', $suffix = '')
{
- // No errrors, validation passes!
+ // No errors, validation passes!
if (count($this->_error_array) === 0)
{
return '';
}
- if ($prefix == '')
+ if ($prefix === '')
{
$prefix = $this->_error_prefix;
}
- if ($suffix == '')
+ if ($suffix === '')
{
$suffix = $this->_error_suffix;
}
@@ -259,7 +392,7 @@ class CI_Form_validation {
$str = '';
foreach ($this->_error_array as $val)
{
- if ($val != '')
+ if ($val !== '')
{
$str .= $prefix.$val.$suffix."\n";
}
@@ -275,43 +408,39 @@ class CI_Form_validation {
*
* This function does all the work.
*
- * @access public
+ * @param string $config
+ * @param array $data
* @return bool
*/
- public function run($group = '')
+ public function run($config = NULL, &$data = NULL)
{
- // Do we even have any data to process? Mm?
- if (count($_POST) == 0)
- {
- return FALSE;
- }
+ $validation_array = empty($this->validation_data)
+ ? $_POST
+ : $this->validation_data;
// Does the _field_data array containing the validation rules exist?
// If not, we look to see if they were assigned via a config file
- if (count($this->_field_data) == 0)
+ if (count($this->_field_data) === 0)
{
// No validation rules? We're done...
- if (count($this->_config_rules) == 0)
+ if (empty($this->_config_rules))
{
return FALSE;
}
- // Is there a validation rule for the particular URI being accessed?
- $uri = ($group == '') ? trim($this->CI->uri->ruri_string(), '/') : $group;
-
- if ($uri != '' AND isset($this->_config_rules[$uri]))
- {
- $this->set_rules($this->_config_rules[$uri]);
- }
- else
+ if (empty($config))
{
- $this->set_rules($this->_config_rules);
+ // Is there a validation rule for the particular URI being accessed?
+ $config = trim($this->CI->uri->ruri_string(), '/');
+ isset($this->_config_rules[$config]) OR $config = $this->CI->router->class.'/'.$this->CI->router->method;
}
- // We're we able to set the rules correctly?
- if (count($this->_field_data) == 0)
+ $this->set_rules(isset($this->_config_rules[$config]) ? $this->_config_rules[$config] : $this->_config_rules);
+
+ // Were we able to set the rules correctly?
+ if (count($this->_field_data) === 0)
{
- log_message('debug', "Unable to find validation rules");
+ log_message('debug', 'Unable to find validation rules');
return FALSE;
}
}
@@ -319,47 +448,108 @@ class CI_Form_validation {
// Load the language file containing error messages
$this->CI->lang->load('form_validation');
- // Cycle through the rules for each field, match the
- // corresponding $_POST item and test for errors
- foreach ($this->_field_data as $field => $row)
+ // Cycle through the rules for each field and match the corresponding $validation_data item
+ foreach ($this->_field_data as $field => &$row)
{
- // Fetch the data from the corresponding $_POST array and cache it in the _field_data array.
+ // Fetch the data from the validation_data array item and cache it in the _field_data array.
// Depending on whether the field name is an array or a string will determine where we get it from.
-
- if ($row['is_array'] == TRUE)
+ if ($row['is_array'] === TRUE)
{
- $this->_field_data[$field]['postdata'] = $this->_reduce_array($_POST, $row['keys']);
+ $this->_field_data[$field]['postdata'] = $this->_reduce_array($validation_array, $row['keys']);
}
- else
+ elseif (isset($validation_array[$field]))
{
- if (isset($_POST[$field]) AND $_POST[$field] != "")
- {
- $this->_field_data[$field]['postdata'] = $_POST[$field];
- }
+ $this->_field_data[$field]['postdata'] = $validation_array[$field];
}
+ }
- $this->_execute($row, explode('|', $row['rules']), $this->_field_data[$field]['postdata']);
+ // Execute validation rules
+ // Note: A second foreach (for now) is required in order to avoid false-positives
+ // for rules like 'matches', which correlate to other validation fields.
+ foreach ($this->_field_data as $field => &$row)
+ {
+ // Don't try to validate if we have no rules set
+ if (empty($row['rules']))
+ {
+ continue;
+ }
+
+ $this->_execute($row, $row['rules'], $row['postdata']);
}
- // Did we end up with any errors?
- $total_errors = count($this->_error_array);
+ if ( ! empty($this->_error_array))
+ {
+ return FALSE;
+ }
- if ($total_errors > 0)
+ // Fill $data if requested, otherwise modify $_POST, as long as
+ // set_data() wasn't used (yea, I know it sounds confusing)
+ if (func_num_args() >= 2)
{
- $this->_safe_form_data = TRUE;
+ $data = empty($this->validation_data) ? $_POST : $this->validation_data;
+ $this->_reset_data_array($data);
+ return TRUE;
}
- // Now we need to re-set the POST data with the new, processed data
- $this->_reset_post_array();
+ empty($this->validation_data) && $this->_reset_data_array($_POST);
+ return TRUE;
+ }
- // No errors, validation passes!
- if ($total_errors == 0)
+ // --------------------------------------------------------------------
+
+ /**
+ * Prepare rules
+ *
+ * Re-orders the provided rules in order of importance, so that
+ * they can easily be executed later without weird checks ...
+ *
+ * "Callbacks" are given the highest priority (always called),
+ * followed by 'required' (called if callbacks didn't fail),
+ * and then every next rule depends on the previous one passing.
+ *
+ * @param array $rules
+ * @return array
+ */
+ protected function _prepare_rules($rules)
+ {
+ $new_rules = array();
+ $callbacks = array();
+
+ foreach ($rules as &$rule)
{
- return TRUE;
+ // Let 'required' always be the first (non-callback) rule
+ if ($rule === 'required')
+ {
+ array_unshift($new_rules, 'required');
+ }
+ // 'isset' is a kind of a weird alias for 'required' ...
+ elseif ($rule === 'isset' && (empty($new_rules) OR $new_rules[0] !== 'required'))
+ {
+ array_unshift($new_rules, 'isset');
+ }
+ // The old/classic 'callback_'-prefixed rules
+ elseif (is_string($rule) && strncmp('callback_', $rule, 9) === 0)
+ {
+ $callbacks[] = $rule;
+ }
+ // Proper callables
+ elseif (is_callable($rule))
+ {
+ $callbacks[] = $rule;
+ }
+ // "Named" callables; i.e. array('name' => $callable)
+ elseif (is_array($rule) && isset($rule[0], $rule[1]) && is_callable($rule[1]))
+ {
+ $callbacks[] = $rule;
+ }
+ // Everything else goes at the end of the queue
+ else
+ {
+ $new_rules[] = $rule;
+ }
}
- // Validation fails
- return FALSE;
+ return array_merge($callbacks, $new_rules);
}
// --------------------------------------------------------------------
@@ -367,34 +557,20 @@ class CI_Form_validation {
/**
* Traverse a multidimensional $_POST array index until the data is found
*
- * @access private
* @param array
* @param array
- * @param integer
+ * @param int
* @return mixed
*/
protected function _reduce_array($array, $keys, $i = 0)
{
- if (is_array($array))
+ if (is_array($array) && isset($keys[$i]))
{
- if (isset($keys[$i]))
- {
- if (isset($array[$keys[$i]]))
- {
- $array = $this->_reduce_array($array[$keys[$i]], $keys, ($i+1));
- }
- else
- {
- return NULL;
- }
- }
- else
- {
- return $array;
- }
+ return isset($array[$keys[$i]]) ? $this->_reduce_array($array[$keys[$i]], $keys, ($i+1)) : NULL;
}
- return $array;
+ // NULL must be returned for empty fields
+ return ($array === '') ? NULL : $array;
}
// --------------------------------------------------------------------
@@ -402,54 +578,36 @@ class CI_Form_validation {
/**
* Re-populate the _POST array with our finalized and processed data
*
- * @access private
- * @return null
+ * @return void
*/
- protected function _reset_post_array()
+ protected function _reset_data_array(&$data)
{
foreach ($this->_field_data as $field => $row)
{
- if ( ! is_null($row['postdata']))
+ if ($row['postdata'] !== NULL)
{
- if ($row['is_array'] == FALSE)
+ if ($row['is_array'] === FALSE)
{
- if (isset($_POST[$row['field']]))
- {
- $_POST[$row['field']] = $this->prep_for_form($row['postdata']);
- }
+ isset($data[$field]) && $data[$field] = is_array($row['postdata']) ? NULL : $row['postdata'];
}
else
{
- // start with a reference
- $post_ref =& $_POST;
+ $data_ref =& $data;
// before we assign values, make a reference to the right POST key
- if (count($row['keys']) == 1)
+ if (count($row['keys']) === 1)
{
- $post_ref =& $post_ref[current($row['keys'])];
+ $data_ref =& $data[current($row['keys'])];
}
else
{
foreach ($row['keys'] as $val)
{
- $post_ref =& $post_ref[$val];
+ $data_ref =& $data_ref[$val];
}
}
- if (is_array($row['postdata']))
- {
- $array = array();
- foreach ($row['postdata'] as $k => $v)
- {
- $array[$k] = $this->prep_for_form($v);
- }
-
- $post_ref = $array;
- }
- else
- {
- $post_ref = $this->prep_for_form($row['postdata']);
- }
+ $data_ref = $row['postdata'];
}
}
}
@@ -460,92 +618,38 @@ class CI_Form_validation {
/**
* Executes the Validation routines
*
- * @access private
* @param array
* @param array
* @param mixed
- * @param integer
+ * @param int
* @return mixed
*/
protected function _execute($row, $rules, $postdata = NULL, $cycles = 0)
{
+ $allow_arrays = in_array('is_array', $rules, TRUE);
+
// If the $_POST data is an array we will run a recursive call
- if (is_array($postdata))
+ //
+ // Note: We MUST check if the array is empty or not!
+ // Otherwise empty arrays will always pass validation.
+ if ($allow_arrays === FALSE && is_array($postdata) && ! empty($postdata))
{
foreach ($postdata as $key => $val)
{
- $this->_execute($row, $rules, $val, $cycles);
- $cycles++;
- }
-
- return;
- }
-
- // --------------------------------------------------------------------
-
- // If the field is blank, but NOT required, no further tests are necessary
- $callback = FALSE;
- if ( ! in_array('required', $rules) AND is_null($postdata))
- {
- // Before we bail out, does the rule contain a callback?
- if (preg_match("/(callback_\w+(\[.*?\])?)/", implode(' ', $rules), $match))
- {
- $callback = TRUE;
- $rules = (array('1' => $match[1]));
- }
- else
- {
- return;
- }
- }
-
- // --------------------------------------------------------------------
-
- // Isset Test. Typically this rule will only apply to checkboxes.
- if (is_null($postdata) AND $callback == FALSE)
- {
- if (in_array('isset', $rules, TRUE) OR in_array('required', $rules))
- {
- // Set the message type
- $type = (in_array('required', $rules)) ? 'required' : 'isset';
-
- if ( ! isset($this->_error_messages[$type]))
- {
- if (FALSE === ($line = $this->CI->lang->line($type)))
- {
- $line = 'The field was not set';
- }
- }
- else
- {
- $line = $this->_error_messages[$type];
- }
-
- // Build the error message
- $message = sprintf($line, $this->_translate_fieldname($row['label']));
-
- // Save the error message
- $this->_field_data[$row['field']]['error'] = $message;
-
- if ( ! isset($this->_error_array[$row['field']]))
- {
- $this->_error_array[$row['field']] = $message;
- }
+ $this->_execute($row, $rules, $val, $key);
}
return;
}
- // --------------------------------------------------------------------
-
- // Cycle through each rule and run it
- foreach ($rules As $rule)
+ $rules = $this->_prepare_rules($rules);
+ foreach ($rules as $rule)
{
$_in_array = FALSE;
// We set the $postdata variable with the current data in our master array so that
// each cycle of the loop is dealing with the processed data from the last cycle
- if ($row['is_array'] == TRUE AND is_array($this->_field_data[$row['field']]['postdata']))
+ if ($row['is_array'] === TRUE && is_array($this->_field_data[$row['field']]['postdata']))
{
// We shouldn't need this safety, but just in case there isn't an array index
// associated with this cycle we'll bail out
@@ -557,120 +661,158 @@ class CI_Form_validation {
$postdata = $this->_field_data[$row['field']]['postdata'][$cycles];
$_in_array = TRUE;
}
+ // If we get an array field, but it's not expected - then it is most likely
+ // somebody messing with the form on the client side, so we'll just consider
+ // it an empty field
+ elseif ($allow_arrays === FALSE && is_array($this->_field_data[$row['field']]['postdata']))
+ {
+ $postdata = NULL;
+ }
else
{
$postdata = $this->_field_data[$row['field']]['postdata'];
}
- // --------------------------------------------------------------------
-
// Is the rule a callback?
- $callback = FALSE;
- if (substr($rule, 0, 9) == 'callback_')
+ $callback = $callable = FALSE;
+ if (is_string($rule))
+ {
+ if (strpos($rule, 'callback_') === 0)
+ {
+ $rule = substr($rule, 9);
+ $callback = TRUE;
+ }
+ }
+ elseif (is_callable($rule))
+ {
+ $callable = TRUE;
+ }
+ elseif (is_array($rule) && isset($rule[0], $rule[1]) && is_callable($rule[1]))
{
- $rule = substr($rule, 9);
- $callback = TRUE;
+ // We have a "named" callable, so save the name
+ $callable = $rule[0];
+ $rule = $rule[1];
}
// Strip the parameter (if exists) from the rule
// Rules can contain a parameter: max_length[5]
$param = FALSE;
- if (preg_match("/(.*?)\[(.*)\]/", $rule, $match))
+ if ( ! $callable && preg_match('/(.*?)\[(.*)\]/', $rule, $match))
+ {
+ $rule = $match[1];
+ $param = $match[2];
+ }
+
+ // Ignore empty, non-required inputs with a few exceptions ...
+ if (
+ ($postdata === NULL OR ($allow_arrays === FALSE && $postdata === ''))
+ && $callback === FALSE
+ && $callable === FALSE
+ && ! in_array($rule, array('required', 'isset', 'matches'), TRUE)
+ )
{
- $rule = $match[1];
- $param = $match[2];
+ continue;
}
// Call the function that corresponds to the rule
- if ($callback === TRUE)
+ if ($callback OR $callable !== FALSE)
{
- if ( ! method_exists($this->CI, $rule))
+ if ($callback)
{
- continue;
+ if ( ! method_exists($this->CI, $rule))
+ {
+ log_message('debug', 'Unable to find callback validation rule: '.$rule);
+ $result = FALSE;
+ }
+ else
+ {
+ // Run the function and grab the result
+ $result = $this->CI->$rule($postdata, $param);
+ }
}
+ else
+ {
+ $result = is_array($rule)
+ ? $rule[0]->{$rule[1]}($postdata)
+ : $rule($postdata);
- // Run the function and grab the result
- $result = $this->CI->$rule($postdata, $param);
+ // Is $callable set to a rule name?
+ if ($callable !== FALSE)
+ {
+ $rule = $callable;
+ }
+ }
// Re-assign the result to the master data array
- if ($_in_array == TRUE)
+ if ($_in_array === TRUE)
{
- $this->_field_data[$row['field']]['postdata'][$cycles] = (is_bool($result)) ? $postdata : $result;
+ $this->_field_data[$row['field']]['postdata'][$cycles] = is_bool($result) ? $postdata : $result;
}
else
{
- $this->_field_data[$row['field']]['postdata'] = (is_bool($result)) ? $postdata : $result;
- }
-
- // If the field isn't required and we just processed a callback we'll move on...
- if ( ! in_array('required', $rules, TRUE) AND $result !== FALSE)
- {
- continue;
+ $this->_field_data[$row['field']]['postdata'] = is_bool($result) ? $postdata : $result;
}
}
- else
+ elseif ( ! method_exists($this, $rule))
{
- if ( ! method_exists($this, $rule))
+ // If our own wrapper function doesn't exist we see if a native PHP function does.
+ // Users can use any native PHP function call that has one param.
+ if (function_exists($rule))
{
- // If our own wrapper function doesn't exist we see if a native PHP function does.
- // Users can use any native PHP function call that has one param.
- if (function_exists($rule))
- {
- $result = $rule($postdata);
+ // Native PHP functions issue warnings if you pass them more parameters than they use
+ $result = ($param !== FALSE) ? $rule($postdata, $param) : $rule($postdata);
- if ($_in_array == TRUE)
- {
- $this->_field_data[$row['field']]['postdata'][$cycles] = (is_bool($result)) ? $postdata : $result;
- }
- else
- {
- $this->_field_data[$row['field']]['postdata'] = (is_bool($result)) ? $postdata : $result;
- }
+ if ($_in_array === TRUE)
+ {
+ $this->_field_data[$row['field']]['postdata'][$cycles] = is_bool($result) ? $postdata : $result;
}
else
{
- log_message('debug', "Unable to find validation rule: ".$rule);
+ $this->_field_data[$row['field']]['postdata'] = is_bool($result) ? $postdata : $result;
}
-
- continue;
}
-
+ else
+ {
+ log_message('debug', 'Unable to find validation rule: '.$rule);
+ $result = FALSE;
+ }
+ }
+ else
+ {
$result = $this->$rule($postdata, $param);
- if ($_in_array == TRUE)
+ if ($_in_array === TRUE)
{
- $this->_field_data[$row['field']]['postdata'][$cycles] = (is_bool($result)) ? $postdata : $result;
+ $this->_field_data[$row['field']]['postdata'][$cycles] = is_bool($result) ? $postdata : $result;
}
else
{
- $this->_field_data[$row['field']]['postdata'] = (is_bool($result)) ? $postdata : $result;
+ $this->_field_data[$row['field']]['postdata'] = is_bool($result) ? $postdata : $result;
}
}
- // Did the rule test negatively? If so, grab the error.
+ // Did the rule test negatively? If so, grab the error.
if ($result === FALSE)
{
- if ( ! isset($this->_error_messages[$rule]))
+ // Callable rules might not have named error messages
+ if ( ! is_string($rule))
{
- if (FALSE === ($line = $this->CI->lang->line($rule)))
- {
- $line = 'Unable to access an error message corresponding to your field name.';
- }
+ $line = $this->CI->lang->line('form_validation_error_message_not_set').'(Anonymous function)';
}
else
{
- $line = $this->_error_messages[$rule];
+ $line = $this->_get_error_message($rule, $row['field']);
}
// Is the parameter we are inserting into the error message the name
- // of another field? If so we need to grab its "field label"
- if (isset($this->_field_data[$param]) AND isset($this->_field_data[$param]['label']))
+ // of another field? If so we need to grab its "field label"
+ if (isset($this->_field_data[$param], $this->_field_data[$param]['label']))
{
$param = $this->_translate_fieldname($this->_field_data[$param]['label']);
}
// Build the error message
- $message = sprintf($line, $this->_translate_fieldname($row['label']), $param);
+ $message = $this->_build_error_msg($line, $this->_translate_fieldname($row['label']), $param);
// Save the error message
$this->_field_data[$row['field']]['error'] = $message;
@@ -688,26 +830,47 @@ class CI_Form_validation {
// --------------------------------------------------------------------
/**
+ * Get the error message for the rule
+ *
+ * @param string $rule The rule name
+ * @param string $field The field name
+ * @return string
+ */
+ protected function _get_error_message($rule, $field)
+ {
+ // check if a custom message is defined through validation config row.
+ if (isset($this->_field_data[$field]['errors'][$rule]))
+ {
+ return $this->_field_data[$field]['errors'][$rule];
+ }
+ // check if a custom message has been set using the set_message() function
+ elseif (isset($this->_error_messages[$rule]))
+ {
+ return $this->_error_messages[$rule];
+ }
+ elseif (FALSE !== ($line = $this->CI->lang->line('form_validation_'.$rule)))
+ {
+ return $line;
+ }
+
+ return $this->CI->lang->line('form_validation_error_message_not_set').'('.$rule.')';
+ }
+
+ // --------------------------------------------------------------------
+
+ /**
* Translate a field name
*
- * @access private
* @param string the field name
* @return string
*/
protected function _translate_fieldname($fieldname)
{
- // Do we need to translate the field name?
- // We look for the prefix lang: to determine this
- if (substr($fieldname, 0, 5) == 'lang:')
+ // Do we need to translate the field name? We look for the prefix 'lang:' to determine this
+ // If we find one, but there's no translation for the string - just return it
+ if (sscanf($fieldname, 'lang:%s', $line) === 1 && FALSE === ($fieldname = $this->CI->lang->line($line, FALSE)))
{
- // Grab the variable
- $line = substr($fieldname, 5);
-
- // Were we able to translate the field name? If not we use $line
- if (FALSE === ($fieldname = $this->CI->lang->line($line)))
- {
- return $line;
- }
+ return $line;
}
return $fieldname;
@@ -716,25 +879,60 @@ class CI_Form_validation {
// --------------------------------------------------------------------
/**
+ * Build an error message using the field and param.
+ *
+ * @param string The error message line
+ * @param string A field's human name
+ * @param mixed A rule's optional parameter
+ * @return string
+ */
+ protected function _build_error_msg($line, $field = '', $param = '')
+ {
+ // Check for %s in the string for legacy support.
+ if (strpos($line, '%s') !== FALSE)
+ {
+ return sprintf($line, $field, $param);
+ }
+
+ return str_replace(array('{field}', '{param}'), array($field, $param), $line);
+ }
+
+ // --------------------------------------------------------------------
+
+ /**
+ * Checks if the rule is present within the validator
+ *
+ * Permits you to check if a rule is present within the validator
+ *
+ * @param string the field name
+ * @return bool
+ */
+ public function has_rule($field)
+ {
+ return isset($this->_field_data[$field]);
+ }
+
+ // --------------------------------------------------------------------
+
+ /**
* Get the value from a form
*
* Permits you to repopulate a form field with the value it was submitted
* with, or, if that value doesn't exist, with the default
*
- * @access public
* @param string the field name
* @param string
- * @return void
+ * @return string
*/
public function set_value($field = '', $default = '')
{
- if ( ! isset($this->_field_data[$field]))
+ if ( ! isset($this->_field_data[$field], $this->_field_data[$field]['postdata']))
{
return $default;
}
// If the data is an array output them one at a time.
- // E.g: form_input('name[]', set_value('name[]');
+ // E.g: form_input('name[]', set_value('name[]');
if (is_array($this->_field_data[$field]['postdata']))
{
return array_shift($this->_field_data[$field]['postdata']);
@@ -751,37 +949,36 @@ class CI_Form_validation {
* Enables pull-down lists to be set to the value the user
* selected in the event of an error
*
- * @access public
* @param string
* @param string
+ * @param bool
* @return string
*/
public function set_select($field = '', $value = '', $default = FALSE)
{
- if ( ! isset($this->_field_data[$field]) OR ! isset($this->_field_data[$field]['postdata']))
+ if ( ! isset($this->_field_data[$field], $this->_field_data[$field]['postdata']))
{
- if ($default === TRUE AND count($this->_field_data) === 0)
- {
- return ' selected="selected"';
- }
- return '';
+ return ($default === TRUE && count($this->_field_data) === 0) ? ' selected="selected"' : '';
}
$field = $this->_field_data[$field]['postdata'];
-
+ $value = (string) $value;
if (is_array($field))
{
- if ( ! in_array($value, $field))
+ // Note: in_array('', array(0)) returns TRUE, do not use it
+ foreach ($field as &$v)
{
- return '';
+ if ($value === $v)
+ {
+ return ' selected="selected"';
+ }
}
+
+ return '';
}
- else
+ elseif (($field === '' OR $value === '') OR ($field !== $value))
{
- if (($field == '' OR $value == '') OR ($field != $value))
- {
- return '';
- }
+ return '';
}
return ' selected="selected"';
@@ -795,37 +992,36 @@ class CI_Form_validation {
* Enables radio buttons to be set to the value the user
* selected in the event of an error
*
- * @access public
* @param string
* @param string
+ * @param bool
* @return string
*/
public function set_radio($field = '', $value = '', $default = FALSE)
{
- if ( ! isset($this->_field_data[$field]) OR ! isset($this->_field_data[$field]['postdata']))
+ if ( ! isset($this->_field_data[$field], $this->_field_data[$field]['postdata']))
{
- if ($default === TRUE AND count($this->_field_data) === 0)
- {
- return ' checked="checked"';
- }
- return '';
+ return ($default === TRUE && count($this->_field_data) === 0) ? ' checked="checked"' : '';
}
$field = $this->_field_data[$field]['postdata'];
-
+ $value = (string) $value;
if (is_array($field))
{
- if ( ! in_array($value, $field))
+ // Note: in_array('', array(0)) returns TRUE, do not use it
+ foreach ($field as &$v)
{
- return '';
+ if ($value === $v)
+ {
+ return ' checked="checked"';
+ }
}
+
+ return '';
}
- else
+ elseif (($field === '' OR $value === '') OR ($field !== $value))
{
- if (($field == '' OR $value == '') OR ($field != $value))
- {
- return '';
- }
+ return '';
}
return ' checked="checked"';
@@ -839,40 +1035,15 @@ class CI_Form_validation {
* Enables checkboxes to be set to the value the user
* selected in the event of an error
*
- * @access public
* @param string
* @param string
+ * @param bool
* @return string
*/
public function set_checkbox($field = '', $value = '', $default = FALSE)
{
- if ( ! isset($this->_field_data[$field]) OR ! isset($this->_field_data[$field]['postdata']))
- {
- if ($default === TRUE AND count($this->_field_data) === 0)
- {
- return ' checked="checked"';
- }
- return '';
- }
-
- $field = $this->_field_data[$field]['postdata'];
-
- if (is_array($field))
- {
- if ( ! in_array($value, $field))
- {
- return '';
- }
- }
- else
- {
- if (($field == '' OR $value == '') OR ($field != $value))
- {
- return '';
- }
- }
-
- return ' checked="checked"';
+ // Logic is exactly the same as for radio fields
+ return $this->set_radio($field, $value, $default);
}
// --------------------------------------------------------------------
@@ -880,20 +1051,14 @@ class CI_Form_validation {
/**
* Required
*
- * @access public
* @param string
* @return bool
*/
public function required($str)
{
- if ( ! is_array($str))
- {
- return (trim($str) == '') ? FALSE : TRUE;
- }
- else
- {
- return ( ! empty($str));
- }
+ return is_array($str)
+ ? (empty($str) === FALSE)
+ : (trim((string) $str) !== '');
}
// --------------------------------------------------------------------
@@ -901,19 +1066,13 @@ class CI_Form_validation {
/**
* Performs a Regular Expression match test.
*
- * @access public
* @param string
- * @param regex
+ * @param string regex
* @return bool
*/
public function regex_match($str, $regex)
{
- if ( ! preg_match($regex, $str))
- {
- return FALSE;
- }
-
- return TRUE;
+ return (bool) preg_match($regex, $str);
}
// --------------------------------------------------------------------
@@ -921,64 +1080,68 @@ class CI_Form_validation {
/**
* Match one field to another
*
- * @access public
- * @param string
- * @param field
+ * @param string $str string to compare against
+ * @param string $field
* @return bool
*/
public function matches($str, $field)
{
- if ( ! isset($_POST[$field]))
- {
- return FALSE;
- }
+ return isset($this->_field_data[$field], $this->_field_data[$field]['postdata'])
+ ? ($str === $this->_field_data[$field]['postdata'])
+ : FALSE;
+ }
- $field = $_POST[$field];
+ // --------------------------------------------------------------------
- return ($str !== $field) ? FALSE : TRUE;
+ /**
+ * Differs from another field
+ *
+ * @param string
+ * @param string field
+ * @return bool
+ */
+ public function differs($str, $field)
+ {
+ return ! (isset($this->_field_data[$field]) && $this->_field_data[$field]['postdata'] === $str);
}
-
+
// --------------------------------------------------------------------
/**
- * Match one field to another
+ * Is Unique
*
- * @access public
- * @param string
- * @param field
+ * Check if the input value doesn't already exist
+ * in the specified database field.
+ *
+ * @param string $str
+ * @param string $field
* @return bool
*/
public function is_unique($str, $field)
{
- list($table, $field)=explode('.', $field);
- $query = $this->CI->db->limit(1)->get_where($table, array($field => $str));
-
- return $query->num_rows() === 0;
- }
+ sscanf($field, '%[^.].%[^.]', $table, $field);
+ return isset($this->CI->db)
+ ? ($this->CI->db->limit(1)->get_where($table, array($field => $str))->num_rows() === 0)
+ : FALSE;
+ }
// --------------------------------------------------------------------
/**
* Minimum Length
*
- * @access public
* @param string
- * @param value
+ * @param string
* @return bool
*/
public function min_length($str, $val)
{
- if (preg_match("/[^0-9]/", $val))
+ if ( ! is_numeric($val))
{
return FALSE;
}
- if (function_exists('mb_strlen'))
- {
- return (mb_strlen($str) < $val) ? FALSE : TRUE;
- }
-
- return (strlen($str) < $val) ? FALSE : TRUE;
+ return ($val <= mb_strlen($str));
}
// --------------------------------------------------------------------
@@ -986,24 +1149,18 @@ class CI_Form_validation {
/**
* Max Length
*
- * @access public
* @param string
- * @param value
+ * @param string
* @return bool
*/
public function max_length($str, $val)
{
- if (preg_match("/[^0-9]/", $val))
+ if ( ! is_numeric($val))
{
return FALSE;
}
- if (function_exists('mb_strlen'))
- {
- return (mb_strlen($str) > $val) ? FALSE : TRUE;
- }
-
- return (strlen($str) > $val) ? FALSE : TRUE;
+ return ($val >= mb_strlen($str));
}
// --------------------------------------------------------------------
@@ -1011,24 +1168,64 @@ class CI_Form_validation {
/**
* Exact Length
*
- * @access public
* @param string
- * @param value
+ * @param string
* @return bool
*/
public function exact_length($str, $val)
{
- if (preg_match("/[^0-9]/", $val))
+ if ( ! is_numeric($val))
+ {
+ return FALSE;
+ }
+
+ return (mb_strlen($str) === (int) $val);
+ }
+
+ // --------------------------------------------------------------------
+
+ /**
+ * Valid URL
+ *
+ * @param string $str
+ * @return bool
+ */
+ public function valid_url($str)
+ {
+ if (empty($str))
+ {
+ return FALSE;
+ }
+ elseif (preg_match('/^(?:([^:]*)\:)?\/\/(.+)$/', $str, $matches))
+ {
+ if (empty($matches[2]))
+ {
+ return FALSE;
+ }
+ elseif ( ! in_array(strtolower($matches[1]), array('http', 'https'), TRUE))
+ {
+ return FALSE;
+ }
+
+ $str = $matches[2];
+ }
+
+ // Apparently, FILTER_VALIDATE_URL doesn't reject digit-only names for some reason ...
+ // See https://github.com/bcit-ci/CodeIgniter/issues/5755
+ if (ctype_digit($str))
{
return FALSE;
}
- if (function_exists('mb_strlen'))
+ // PHP 7 accepts IPv6 addresses within square brackets as hostnames,
+ // but it appears that the PR that came in with https://bugs.php.net/bug.php?id=68039
+ // was never merged into a PHP 5 branch ... https://3v4l.org/8PsSN
+ if (preg_match('/^\[([^\]]+)\]/', $str, $matches) && ! is_php('7') && filter_var($matches[1], FILTER_VALIDATE_IP, FILTER_FLAG_IPV6) !== FALSE)
{
- return (mb_strlen($str) != $val) ? FALSE : TRUE;
+ $str = 'ipv6.host'.substr($str, strlen($matches[1]) + 2);
}
- return (strlen($str) != $val) ? FALSE : TRUE;
+ return (filter_var('http://'.$str, FILTER_VALIDATE_URL) !== FALSE);
}
// --------------------------------------------------------------------
@@ -1036,13 +1233,24 @@ class CI_Form_validation {
/**
* Valid Email
*
- * @access public
* @param string
* @return bool
*/
public function valid_email($str)
{
- return ( ! preg_match("/^([a-z0-9\+_\-]+)(\.[a-z0-9\+_\-]+)*@([a-z0-9\-]+\.)+[a-z]{2,6}$/ix", $str)) ? FALSE : TRUE;
+ if (function_exists('idn_to_ascii') && preg_match('#\A([^@]+)@(.+)\z#', $str, $matches))
+ {
+ $domain = defined('INTL_IDNA_VARIANT_UTS46')
+ ? idn_to_ascii($matches[2], 0, INTL_IDNA_VARIANT_UTS46)
+ : idn_to_ascii($matches[2]);
+
+ if ($domain !== FALSE)
+ {
+ $str = $matches[1].'@'.$domain;
+ }
+ }
+
+ return (bool) filter_var($str, FILTER_VALIDATE_EMAIL);
}
// --------------------------------------------------------------------
@@ -1050,7 +1258,6 @@ class CI_Form_validation {
/**
* Valid Emails
*
- * @access public
* @param string
* @return bool
*/
@@ -1063,7 +1270,7 @@ class CI_Form_validation {
foreach (explode(',', $str) as $email)
{
- if (trim($email) != '' && $this->valid_email(trim($email)) === FALSE)
+ if (trim($email) !== '' && $this->valid_email(trim($email)) === FALSE)
{
return FALSE;
}
@@ -1077,10 +1284,9 @@ class CI_Form_validation {
/**
* Validate IP Address
*
- * @access public
* @param string
- * @param string "ipv4" or "ipv6" to validate a specific ip format
- * @return string
+ * @param string 'ipv4' or 'ipv6' to validate a specific IP format
+ * @return bool
*/
public function valid_ip($ip, $which = '')
{
@@ -1090,15 +1296,39 @@ class CI_Form_validation {
// --------------------------------------------------------------------
/**
+ * Validate MAC address
+ *
+ * @param string $mac
+ * @return bool
+ */
+ public function valid_mac($mac)
+ {
+ if ( ! is_php('5.5'))
+ {
+ // Most common format, with either dash or colon delimiters
+ if (preg_match('#\A[0-9a-f]{2}(?<delimiter>[:-])([0-9a-f]{2}(?P=delimiter)){4}[0-9a-f]{2}\z#i', $mac))
+ {
+ return TRUE;
+ }
+
+ // The less common format; e.g. 0123.4567.89ab
+ return (bool) preg_match('#((\A|\.)[0-9a-f]{4}){3}\z#i', $mac);
+ }
+
+ return (bool) filter_var($mac, FILTER_VALIDATE_MAC);
+ }
+
+ // --------------------------------------------------------------------
+
+ /**
* Alpha
*
- * @access public
* @param string
* @return bool
*/
public function alpha($str)
{
- return ( ! preg_match("/^([a-z])+$/i", $str)) ? FALSE : TRUE;
+ return ctype_alpha($str);
}
// --------------------------------------------------------------------
@@ -1106,56 +1336,52 @@ class CI_Form_validation {
/**
* Alpha-numeric
*
- * @access public
* @param string
* @return bool
*/
public function alpha_numeric($str)
{
- return ( ! preg_match("/^([a-z0-9])+$/i", $str)) ? FALSE : TRUE;
+ return ctype_alnum((string) $str);
}
// --------------------------------------------------------------------
/**
- * Alpha-numeric with underscores and dashes
+ * Alpha-numeric w/ spaces
*
- * @access public
* @param string
* @return bool
*/
- public function alpha_dash($str)
+ public function alpha_numeric_spaces($str)
{
- return ( ! preg_match("/^([-a-z0-9_-])+$/i", $str)) ? FALSE : TRUE;
+ return (bool) preg_match('/^[A-Z0-9 ]+$/i', $str);
}
// --------------------------------------------------------------------
/**
- * Numeric
+ * Alpha-numeric with underscores and dashes
*
- * @access public
* @param string
* @return bool
*/
- public function numeric($str)
+ public function alpha_dash($str)
{
- return (bool)preg_match( '/^[\-+]?[0-9]*\.?[0-9]+$/', $str);
-
+ return (bool) preg_match('/^[a-z0-9_-]+$/i', $str);
}
// --------------------------------------------------------------------
/**
- * Is Numeric
+ * Numeric
*
- * @access public
* @param string
* @return bool
*/
- public function is_numeric($str)
+ public function numeric($str)
{
- return ( ! is_numeric($str)) ? FALSE : TRUE;
+ return (bool) preg_match('/^[\-+]?[0-9]*\.?[0-9]+$/', $str);
+
}
// --------------------------------------------------------------------
@@ -1163,7 +1389,6 @@ class CI_Form_validation {
/**
* Integer
*
- * @access public
* @param string
* @return bool
*/
@@ -1177,7 +1402,6 @@ class CI_Form_validation {
/**
* Decimal number
*
- * @access public
* @param string
* @return bool
*/
@@ -1189,19 +1413,29 @@ class CI_Form_validation {
// --------------------------------------------------------------------
/**
- * Greather than
+ * Greater than
*
- * @access public
* @param string
+ * @param int
* @return bool
*/
public function greater_than($str, $min)
{
- if ( ! is_numeric($str))
- {
- return FALSE;
- }
- return $str > $min;
+ return is_numeric($str) ? ($str > $min) : FALSE;
+ }
+
+ // --------------------------------------------------------------------
+
+ /**
+ * Equal to or Greater than
+ *
+ * @param string
+ * @param int
+ * @return bool
+ */
+ public function greater_than_equal_to($str, $min)
+ {
+ return is_numeric($str) ? ($str >= $min) : FALSE;
}
// --------------------------------------------------------------------
@@ -1209,104 +1443,83 @@ class CI_Form_validation {
/**
* Less than
*
- * @access public
* @param string
+ * @param int
* @return bool
*/
public function less_than($str, $max)
{
- if ( ! is_numeric($str))
- {
- return FALSE;
- }
- return $str < $max;
+ return is_numeric($str) ? ($str < $max) : FALSE;
}
// --------------------------------------------------------------------
/**
- * Is a Natural number (0,1,2,3, etc.)
+ * Equal to or Less than
*
- * @access public
* @param string
+ * @param int
* @return bool
*/
- public function is_natural($str)
+ public function less_than_equal_to($str, $max)
{
- return (bool) preg_match( '/^[0-9]+$/', $str);
+ return is_numeric($str) ? ($str <= $max) : FALSE;
}
// --------------------------------------------------------------------
/**
- * Is a Natural number, but not a zero (1,2,3, etc.)
+ * Value should be within an array of values
*
- * @access public
+ * @param string
* @param string
* @return bool
*/
- public function is_natural_no_zero($str)
+ public function in_list($value, $list)
{
- if ( ! preg_match( '/^[0-9]+$/', $str))
- {
- return FALSE;
- }
+ return in_array($value, explode(',', $list), TRUE);
+ }
- if ($str == 0)
- {
- return FALSE;
- }
+ // --------------------------------------------------------------------
- return TRUE;
+ /**
+ * Is a Natural number (0,1,2,3, etc.)
+ *
+ * @param string
+ * @return bool
+ */
+ public function is_natural($str)
+ {
+ return ctype_digit((string) $str);
}
// --------------------------------------------------------------------
/**
- * Valid Base64
- *
- * Tests a string for characters outside of the Base64 alphabet
- * as defined by RFC 2045 http://www.faqs.org/rfcs/rfc2045
+ * Is a Natural number, but not a zero (1,2,3, etc.)
*
- * @access public
* @param string
* @return bool
*/
- public function valid_base64($str)
+ public function is_natural_no_zero($str)
{
- return (bool) ! preg_match('/[^a-zA-Z0-9\/\+=]/', $str);
+ return ($str != 0 && ctype_digit((string) $str));
}
// --------------------------------------------------------------------
/**
- * Prep data for form
+ * Valid Base64
*
- * This function allows HTML to be safely shown in a form.
- * Special characters are converted.
+ * Tests a string for characters outside of the Base64 alphabet
+ * as defined by RFC 2045 http://www.faqs.org/rfcs/rfc2045
*
- * @access public
* @param string
- * @return string
+ * @return bool
*/
- public function prep_for_form($data = '')
+ public function valid_base64($str)
{
- if (is_array($data))
- {
- foreach ($data as $key => $val)
- {
- $data[$key] = $this->prep_for_form($val);
- }
-
- return $data;
- }
-
- if ($this->_safe_form_data == FALSE OR $data === '')
- {
- return $data;
- }
-
- return str_replace(array("'", '"', '<', '>'), array("&#39;", "&quot;", '&lt;', '&gt;'), stripslashes($data));
+ return (base64_encode(base64_decode($str)) === $str);
}
// --------------------------------------------------------------------
@@ -1314,20 +1527,14 @@ class CI_Form_validation {
/**
* Prep URL
*
- * @access public
* @param string
* @return string
*/
public function prep_url($str = '')
{
- if ($str == 'http://' OR $str == '')
- {
- return '';
- }
-
- if (substr($str, 0, 7) != 'http://' && substr($str, 0, 8) != 'https://')
+ if ($str !== '' && stripos($str, 'http://') !== 0 && stripos($str, 'https://') !== 0)
{
- $str = 'http://'.$str;
+ return 'http://'.$str;
}
return $str;
@@ -1338,45 +1545,44 @@ class CI_Form_validation {
/**
* Strip Image Tags
*
- * @access public
* @param string
* @return string
*/
public function strip_image_tags($str)
{
- return $this->CI->input->strip_image_tags($str);
+ return $this->CI->security->strip_image_tags($str);
}
// --------------------------------------------------------------------
/**
- * XSS Clean
+ * Convert PHP tags to entities
*
- * @access public
* @param string
* @return string
*/
- public function xss_clean($str)
+ public function encode_php_tags($str)
{
- return $this->CI->security->xss_clean($str);
+ return str_replace(array('<?', '?>'), array('&lt;?', '?&gt;'), $str);
}
// --------------------------------------------------------------------
/**
- * Convert PHP tags to entities
+ * Reset validation vars
*
- * @access public
- * @param string
- * @return string
+ * Prevents subsequent validation routines from being affected by the
+ * results of any previous validation routine due to the CI singleton.
+ *
+ * @return CI_Form_validation
*/
- public function encode_php_tags($str)
+ public function reset_validation()
{
- return str_replace(array('<?php', '<?PHP', '<?', '?>'), array('&lt;?php', '&lt;?PHP', '&lt;?', '?&gt;'), $str);
+ $this->_field_data = array();
+ $this->_error_array = array();
+ $this->_error_messages = array();
+ $this->error_string = '';
+ return $this;
}
}
-// END Form Validation Class
-
-/* End of file Form_validation.php */
-/* Location: ./system/libraries/Form_validation.php */
diff --git a/system/libraries/Ftp.php b/system/libraries/Ftp.php
index 1656dfb47..15a0887b7 100644
--- a/system/libraries/Ftp.php
+++ b/system/libraries/Ftp.php
@@ -1,19 +1,42 @@
-<?php if ( ! defined('BASEPATH')) exit('No direct script access allowed');
+<?php
/**
* CodeIgniter
*
- * An open source application development framework for PHP 5.1.6 or newer
+ * An open source application development framework for PHP
*
- * @package CodeIgniter
- * @author ExpressionEngine Dev Team
- * @copyright Copyright (c) 2008 - 2014, EllisLab, Inc.
- * @license http://codeigniter.com/user_guide/license.html
- * @link http://codeigniter.com
- * @since Version 1.0
+ * This content is released under the MIT License (MIT)
+ *
+ * Copyright (c) 2019 - 2022, CodeIgniter Foundation
+ *
+ * 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.
+ *
+ * @package CodeIgniter
+ * @author EllisLab Dev Team
+ * @copyright Copyright (c) 2008 - 2014, EllisLab, Inc. (https://ellislab.com/)
+ * @copyright Copyright (c) 2014 - 2019, British Columbia Institute of Technology (https://bcit.ca/)
+ * @copyright Copyright (c) 2019 - 2022, CodeIgniter Foundation (https://codeigniter.com/)
+ * @license https://opensource.org/licenses/MIT MIT License
+ * @link https://codeigniter.com
+ * @since Version 1.0.0
* @filesource
*/
-
-// ------------------------------------------------------------------------
+defined('BASEPATH') OR exit('No direct script access allowed');
/**
* FTP Class
@@ -21,33 +44,76 @@
* @package CodeIgniter
* @subpackage Libraries
* @category Libraries
- * @author ExpressionEngine Dev Team
- * @link http://codeigniter.com/user_guide/libraries/ftp.html
+ * @author EllisLab Dev Team
+ * @link https://codeigniter.com/userguide3/libraries/ftp.html
*/
class CI_FTP {
- var $hostname = '';
- var $username = '';
- var $password = '';
- var $port = 21;
- var $passive = TRUE;
- var $debug = FALSE;
- var $conn_id = FALSE;
+ /**
+ * FTP Server hostname
+ *
+ * @var string
+ */
+ public $hostname = '';
+
+ /**
+ * FTP Username
+ *
+ * @var string
+ */
+ public $username = '';
+
+ /**
+ * FTP Password
+ *
+ * @var string
+ */
+ public $password = '';
+
+ /**
+ * FTP Server port
+ *
+ * @var int
+ */
+ public $port = 21;
+
+ /**
+ * Passive mode flag
+ *
+ * @var bool
+ */
+ public $passive = TRUE;
+
+ /**
+ * Debug flag
+ *
+ * Specifies whether to display error messages.
+ *
+ * @var bool
+ */
+ public $debug = FALSE;
+ // --------------------------------------------------------------------
/**
- * Constructor - Sets Preferences
+ * Connection ID
*
- * The constructor can be passed an array of config values
+ * @var resource
+ */
+ protected $conn_id;
+
+ // --------------------------------------------------------------------
+
+ /**
+ * Constructor
+ *
+ * @param array $config
+ * @return void
*/
public function __construct($config = array())
{
- if (count($config) > 0)
- {
- $this->initialize($config);
- }
-
- log_message('debug', "FTP Class Initialized");
+ empty($config) OR $this->initialize($config);
+ log_message('info', 'FTP Class Initialized');
}
// --------------------------------------------------------------------
@@ -55,11 +121,10 @@ class CI_FTP {
/**
* Initialize preferences
*
- * @access public
- * @param array
+ * @param array $config
* @return void
*/
- function initialize($config = array())
+ public function initialize($config = array())
{
foreach ($config as $key => $val)
{
@@ -78,11 +143,10 @@ class CI_FTP {
/**
* FTP Connect
*
- * @access public
- * @param array the connection values
+ * @param array $config Connection values
* @return bool
*/
- function connect($config = array())
+ public function connect($config = array())
{
if (count($config) > 0)
{
@@ -91,24 +155,26 @@ class CI_FTP {
if (FALSE === ($this->conn_id = @ftp_connect($this->hostname, $this->port)))
{
- if ($this->debug == TRUE)
+ if ($this->debug === TRUE)
{
$this->_error('ftp_unable_to_connect');
}
+
return FALSE;
}
if ( ! $this->_login())
{
- if ($this->debug == TRUE)
+ if ($this->debug === TRUE)
{
$this->_error('ftp_unable_to_login');
}
+
return FALSE;
}
// Set passive mode if needed
- if ($this->passive == TRUE)
+ if ($this->passive === TRUE)
{
ftp_pasv($this->conn_id, TRUE);
}
@@ -121,10 +187,9 @@ class CI_FTP {
/**
* FTP Login
*
- * @access private
* @return bool
*/
- function _login()
+ protected function _login()
{
return @ftp_login($this->conn_id, $this->username, $this->password);
}
@@ -134,42 +199,41 @@ class CI_FTP {
/**
* Validates the connection ID
*
- * @access private
* @return bool
*/
- function _is_conn()
+ protected function _is_conn()
{
- if ( ! is_resource($this->conn_id))
+ if ($this->conn_id === FALSE)
{
- if ($this->debug == TRUE)
+ if ($this->debug === TRUE)
{
$this->_error('ftp_no_connection');
}
+
return FALSE;
}
+
return TRUE;
}
// --------------------------------------------------------------------
-
/**
* Change directory
*
* The second parameter lets us momentarily turn off debugging so that
* this function can be used to test for the existence of a folder
- * without throwing an error. There's no FTP equivalent to is_dir()
+ * without throwing an error. There's no FTP equivalent to is_dir()
* so we do it by trying to change to a particular directory.
* Internally, this parameter is only used by the "mirror" function below.
*
- * @access public
- * @param string
- * @param bool
+ * @param string $path
+ * @param bool $suppress_debug
* @return bool
*/
- function changedir($path = '', $supress_debug = FALSE)
+ public function changedir($path, $suppress_debug = FALSE)
{
- if ($path == '' OR ! $this->_is_conn())
+ if ( ! $this->_is_conn())
{
return FALSE;
}
@@ -178,10 +242,11 @@ class CI_FTP {
if ($result === FALSE)
{
- if ($this->debug == TRUE AND $supress_debug == FALSE)
+ if ($this->debug === TRUE && $suppress_debug === FALSE)
{
$this->_error('ftp_unable_to_changedir');
}
+
return FALSE;
}
@@ -193,13 +258,13 @@ class CI_FTP {
/**
* Create a directory
*
- * @access public
- * @param string
+ * @param string $path
+ * @param int $permissions
* @return bool
*/
- function mkdir($path = '', $permissions = NULL)
+ public function mkdir($path, $permissions = NULL)
{
- if ($path == '' OR ! $this->_is_conn())
+ if ($path === '' OR ! $this->_is_conn())
{
return FALSE;
}
@@ -208,17 +273,18 @@ class CI_FTP {
if ($result === FALSE)
{
- if ($this->debug == TRUE)
+ if ($this->debug === TRUE)
{
- $this->_error('ftp_unable_to_makdir');
+ $this->_error('ftp_unable_to_mkdir');
}
+
return FALSE;
}
// Set file permissions if needed
- if ( ! is_null($permissions))
+ if ($permissions !== NULL)
{
- $this->chmod($path, (int)$permissions);
+ $this->chmod($path, (int) $permissions);
}
return TRUE;
@@ -229,13 +295,13 @@ class CI_FTP {
/**
* Upload a file to the server
*
- * @access public
- * @param string
- * @param string
- * @param string
+ * @param string $locpath
+ * @param string $rempath
+ * @param string $mode
+ * @param int $permissions
* @return bool
*/
- function upload($locpath, $rempath, $mode = 'auto', $permissions = NULL)
+ public function upload($locpath, $rempath, $mode = 'auto', $permissions = NULL)
{
if ( ! $this->_is_conn())
{
@@ -249,30 +315,31 @@ class CI_FTP {
}
// Set the mode if not specified
- if ($mode == 'auto')
+ if ($mode === 'auto')
{
// Get the file extension so we can set the upload type
$ext = $this->_getext($locpath);
$mode = $this->_settype($ext);
}
- $mode = ($mode == 'ascii') ? FTP_ASCII : FTP_BINARY;
+ $mode = ($mode === 'ascii') ? FTP_ASCII : FTP_BINARY;
$result = @ftp_put($this->conn_id, $rempath, $locpath, $mode);
if ($result === FALSE)
{
- if ($this->debug == TRUE)
+ if ($this->debug === TRUE)
{
$this->_error('ftp_unable_to_upload');
}
+
return FALSE;
}
// Set file permissions if needed
- if ( ! is_null($permissions))
+ if ($permissions !== NULL)
{
- $this->chmod($rempath, (int)$permissions);
+ $this->chmod($rempath, (int) $permissions);
}
return TRUE;
@@ -283,13 +350,12 @@ class CI_FTP {
/**
* Download a file from a remote server to the local server
*
- * @access public
- * @param string
- * @param string
- * @param string
+ * @param string $rempath
+ * @param string $locpath
+ * @param string $mode
* @return bool
*/
- function download($rempath, $locpath, $mode = 'auto')
+ public function download($rempath, $locpath, $mode = 'auto')
{
if ( ! $this->_is_conn())
{
@@ -297,23 +363,24 @@ class CI_FTP {
}
// Set the mode if not specified
- if ($mode == 'auto')
+ if ($mode === 'auto')
{
// Get the file extension so we can set the upload type
$ext = $this->_getext($rempath);
$mode = $this->_settype($ext);
}
- $mode = ($mode == 'ascii') ? FTP_ASCII : FTP_BINARY;
+ $mode = ($mode === 'ascii') ? FTP_ASCII : FTP_BINARY;
$result = @ftp_get($this->conn_id, $locpath, $rempath, $mode);
if ($result === FALSE)
{
- if ($this->debug == TRUE)
+ if ($this->debug === TRUE)
{
$this->_error('ftp_unable_to_download');
}
+
return FALSE;
}
@@ -325,13 +392,12 @@ class CI_FTP {
/**
* Rename (or move) a file
*
- * @access public
- * @param string
- * @param string
- * @param bool
+ * @param string $old_file
+ * @param string $new_file
+ * @param bool $move
* @return bool
*/
- function rename($old_file, $new_file, $move = FALSE)
+ public function rename($old_file, $new_file, $move = FALSE)
{
if ( ! $this->_is_conn())
{
@@ -342,12 +408,11 @@ class CI_FTP {
if ($result === FALSE)
{
- if ($this->debug == TRUE)
+ if ($this->debug === TRUE)
{
- $msg = ($move == FALSE) ? 'ftp_unable_to_rename' : 'ftp_unable_to_move';
-
- $this->_error($msg);
+ $this->_error('ftp_unable_to_'.($move === FALSE ? 'rename' : 'move'));
}
+
return FALSE;
}
@@ -359,12 +424,11 @@ class CI_FTP {
/**
* Move a file
*
- * @access public
- * @param string
- * @param string
+ * @param string $old_file
+ * @param string $new_file
* @return bool
*/
- function move($old_file, $new_file)
+ public function move($old_file, $new_file)
{
return $this->rename($old_file, $new_file, TRUE);
}
@@ -374,11 +438,10 @@ class CI_FTP {
/**
* Rename (or move) a file
*
- * @access public
- * @param string
+ * @param string $filepath
* @return bool
*/
- function delete_file($filepath)
+ public function delete_file($filepath)
{
if ( ! $this->_is_conn())
{
@@ -389,10 +452,11 @@ class CI_FTP {
if ($result === FALSE)
{
- if ($this->debug == TRUE)
+ if ($this->debug === TRUE)
{
$this->_error('ftp_unable_to_delete');
}
+
return FALSE;
}
@@ -403,13 +467,12 @@ class CI_FTP {
/**
* Delete a folder and recursively delete everything (including sub-folders)
- * containted within it.
+ * contained within it.
*
- * @access public
- * @param string
+ * @param string $filepath
* @return bool
*/
- function delete_dir($filepath)
+ public function delete_dir($filepath)
{
if ( ! $this->_is_conn())
{
@@ -417,31 +480,29 @@ class CI_FTP {
}
// Add a trailing slash to the file path if needed
- $filepath = preg_replace("/(.+?)\/*$/", "\\1/", $filepath);
+ $filepath = preg_replace('/(.+?)\/*$/', '\\1/', $filepath);
$list = $this->list_files($filepath);
-
- if ($list !== FALSE AND count($list) > 0)
+ if ( ! empty($list))
{
- foreach ($list as $item)
+ for ($i = 0, $c = count($list); $i < $c; $i++)
{
- // If we can't delete the item it's probaly a folder so
- // we'll recursively call delete_dir()
- if ( ! @ftp_delete($this->conn_id, $item))
+ // If we can't delete the item it's probably a directory,
+ // so we'll recursively call delete_dir()
+ if ( ! preg_match('#/\.\.?$#', $list[$i]) && ! @ftp_delete($this->conn_id, $list[$i]))
{
- $this->delete_dir($item);
+ $this->delete_dir($filepath.$list[$i]);
}
}
}
- $result = @ftp_rmdir($this->conn_id, $filepath);
-
- if ($result === FALSE)
+ if (@ftp_rmdir($this->conn_id, $filepath) === FALSE)
{
- if ($this->debug == TRUE)
+ if ($this->debug === TRUE)
{
$this->_error('ftp_unable_to_delete');
}
+
return FALSE;
}
@@ -453,36 +514,24 @@ class CI_FTP {
/**
* Set file permissions
*
- * @access public
- * @param string the file path
- * @param string the permissions
+ * @param string $path File path
+ * @param int $perm Permissions
* @return bool
*/
- function chmod($path, $perm)
+ public function chmod($path, $perm)
{
if ( ! $this->_is_conn())
{
return FALSE;
}
- // Permissions can only be set when running PHP 5
- if ( ! function_exists('ftp_chmod'))
+ if (@ftp_chmod($this->conn_id, $perm, $path) === FALSE)
{
- if ($this->debug == TRUE)
+ if ($this->debug === TRUE)
{
$this->_error('ftp_unable_to_chmod');
}
- return FALSE;
- }
-
- $result = @ftp_chmod($this->conn_id, $perm, $path);
- if ($result === FALSE)
- {
- if ($this->debug == TRUE)
- {
- $this->_error('ftp_unable_to_chmod');
- }
return FALSE;
}
@@ -494,17 +543,14 @@ class CI_FTP {
/**
* FTP List files in the specified directory
*
- * @access public
+ * @param string $path
* @return array
*/
- function list_files($path = '.')
+ public function list_files($path = '.')
{
- if ( ! $this->_is_conn())
- {
- return FALSE;
- }
-
- return ftp_nlist($this->conn_id, $path);
+ return $this->_is_conn()
+ ? ftp_nlist($this->conn_id, $path)
+ : FALSE;
}
// ------------------------------------------------------------------------
@@ -512,16 +558,16 @@ class CI_FTP {
/**
* Read a directory and recreate it remotely
*
- * This function recursively reads a folder and everything it contains (including
- * sub-folders) and creates a mirror via FTP based on it. Whatever the directory structure
- * of the original file path will be recreated on the server.
+ * This function recursively reads a folder and everything it contains
+ * (including sub-folders) and creates a mirror via FTP based on it.
+ * Whatever the directory structure of the original file path will be
+ * recreated on the server.
*
- * @access public
- * @param string path to source with trailing slash
- * @param string path to destination - include the base folder with trailing slash
+ * @param string $locpath Path to source with trailing slash
+ * @param string $rempath Path to destination - include the base folder with trailing slash
* @return bool
*/
- function mirror($locpath, $rempath)
+ public function mirror($locpath, $rempath)
{
if ( ! $this->_is_conn())
{
@@ -531,24 +577,20 @@ class CI_FTP {
// Open the local file path
if ($fp = @opendir($locpath))
{
- // Attempt to open the remote file path.
- if ( ! $this->changedir($rempath, TRUE))
+ // Attempt to open the remote file path and try to create it, if it doesn't exist
+ if ( ! $this->changedir($rempath, TRUE) && ( ! $this->mkdir($rempath) OR ! $this->changedir($rempath)))
{
- // If it doesn't exist we'll attempt to create the direcotory
- if ( ! $this->mkdir($rempath) OR ! $this->changedir($rempath))
- {
- return FALSE;
- }
+ return FALSE;
}
// Recursively read the local directory
while (FALSE !== ($file = readdir($fp)))
{
- if (@is_dir($locpath.$file) && substr($file, 0, 1) != '.')
+ if (is_dir($locpath.$file) && $file[0] !== '.')
{
- $this->mirror($locpath.$file."/", $rempath.$file."/");
+ $this->mirror($locpath.$file.'/', $rempath.$file.'/');
}
- elseif (substr($file, 0, 1) != ".")
+ elseif ($file[0] !== '.')
{
// Get the file extension so we can se the upload type
$ext = $this->_getext($file);
@@ -557,63 +599,41 @@ class CI_FTP {
$this->upload($locpath.$file, $rempath.$file, $mode);
}
}
+
return TRUE;
}
return FALSE;
}
-
// --------------------------------------------------------------------
/**
* Extract the file extension
*
- * @access private
- * @param string
+ * @param string $filename
* @return string
*/
- function _getext($filename)
+ protected function _getext($filename)
{
- if (FALSE === strpos($filename, '.'))
- {
- return 'txt';
- }
-
- $x = explode('.', $filename);
- return end($x);
+ return (($dot = strrpos($filename, '.')) === FALSE)
+ ? 'txt'
+ : substr($filename, $dot + 1);
}
-
// --------------------------------------------------------------------
/**
* Set the upload type
*
- * @access private
- * @param string
+ * @param string $ext Filename extension
* @return string
*/
- function _settype($ext)
+ protected function _settype($ext)
{
- $text_types = array(
- 'txt',
- 'text',
- 'php',
- 'phps',
- 'php4',
- 'js',
- 'css',
- 'htm',
- 'html',
- 'phtml',
- 'shtml',
- 'log',
- 'xml'
- );
-
-
- return (in_array($ext, $text_types)) ? 'ascii' : 'binary';
+ return in_array($ext, array('txt', 'text', 'php', 'phps', 'php4', 'js', 'css', 'htm', 'html', 'phtml', 'shtml', 'log', 'xml'), TRUE)
+ ? 'ascii'
+ : 'binary';
}
// ------------------------------------------------------------------------
@@ -621,19 +641,13 @@ class CI_FTP {
/**
* Close the connection
*
- * @access public
- * @param string path to source
- * @param string path to destination
* @return bool
*/
- function close()
+ public function close()
{
- if ( ! $this->_is_conn())
- {
- return FALSE;
- }
-
- @ftp_close($this->conn_id);
+ return $this->_is_conn()
+ ? @ftp_close($this->conn_id)
+ : FALSE;
}
// ------------------------------------------------------------------------
@@ -641,20 +655,14 @@ class CI_FTP {
/**
* Display error message
*
- * @access private
- * @param string
- * @return bool
+ * @param string $line
+ * @return void
*/
- function _error($line)
+ protected function _error($line)
{
$CI =& get_instance();
$CI->lang->load('ftp');
show_error($CI->lang->line($line));
}
-
}
-// END FTP Class
-
-/* End of file Ftp.php */
-/* Location: ./system/libraries/Ftp.php */ \ No newline at end of file
diff --git a/system/libraries/Image_lib.php b/system/libraries/Image_lib.php
index eccfe41c7..4e5fc7be6 100644
--- a/system/libraries/Image_lib.php
+++ b/system/libraries/Image_lib.php
@@ -1,19 +1,42 @@
-<?php if ( ! defined('BASEPATH')) exit('No direct script access allowed');
+<?php
/**
* CodeIgniter
*
- * An open source application development framework for PHP 5.1.6 or newer
+ * An open source application development framework for PHP
*
- * @package CodeIgniter
- * @author ExpressionEngine Dev Team
- * @copyright Copyright (c) 2008 - 2014, EllisLab, Inc.
- * @license http://codeigniter.com/user_guide/license.html
- * @link http://codeigniter.com
- * @since Version 1.0
+ * This content is released under the MIT License (MIT)
+ *
+ * Copyright (c) 2019 - 2022, CodeIgniter Foundation
+ *
+ * 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.
+ *
+ * @package CodeIgniter
+ * @author EllisLab Dev Team
+ * @copyright Copyright (c) 2008 - 2014, EllisLab, Inc. (https://ellislab.com/)
+ * @copyright Copyright (c) 2014 - 2019, British Columbia Institute of Technology (https://bcit.ca/)
+ * @copyright Copyright (c) 2019 - 2022, CodeIgniter Foundation (https://codeigniter.com/)
+ * @license https://opensource.org/licenses/MIT MIT License
+ * @link https://codeigniter.com
+ * @since Version 1.0.0
* @filesource
*/
-
-// ------------------------------------------------------------------------
+defined('BASEPATH') OR exit('No direct script access allowed');
/**
* Image Manipulation class
@@ -21,65 +44,346 @@
* @package CodeIgniter
* @subpackage Libraries
* @category Image_lib
- * @author ExpressionEngine Dev Team
- * @link http://codeigniter.com/user_guide/libraries/image_lib.html
+ * @author EllisLab Dev Team
+ * @link https://codeigniter.com/userguide3/libraries/image_lib.html
*/
class CI_Image_lib {
- var $image_library = 'gd2'; // Can be: imagemagick, netpbm, gd, gd2
- var $library_path = '';
- var $dynamic_output = FALSE; // Whether to send to browser or write to disk
- var $source_image = '';
- var $new_image = '';
- var $width = '';
- var $height = '';
- var $quality = '90';
- var $create_thumb = FALSE;
- var $thumb_marker = '_thumb';
- var $maintain_ratio = TRUE; // Whether to maintain aspect ratio when resizing or use hard values
- var $master_dim = 'auto'; // auto, height, or width. Determines what to use as the master dimension
- var $rotation_angle = '';
- var $x_axis = '';
- var $y_axis = '';
+ /**
+ * PHP extension/library to use for image manipulation
+ * Can be: imagemagick, netpbm, gd, gd2
+ *
+ * @var string
+ */
+ public $image_library = 'gd2';
+
+ /**
+ * Path to the graphic library (if applicable)
+ *
+ * @var string
+ */
+ public $library_path = '';
+
+ /**
+ * Whether to send to browser or write to disk
+ *
+ * @var bool
+ */
+ public $dynamic_output = FALSE;
+
+ /**
+ * Path to original image
+ *
+ * @var string
+ */
+ public $source_image = '';
+
+ /**
+ * Path to the modified image
+ *
+ * @var string
+ */
+ public $new_image = '';
+
+ /**
+ * Image width
+ *
+ * @var int
+ */
+ public $width = '';
+
+ /**
+ * Image height
+ *
+ * @var int
+ */
+ public $height = '';
+
+ /**
+ * Quality percentage of new image
+ *
+ * @var int
+ */
+ public $quality = 90;
+
+ /**
+ * Whether to create a thumbnail
+ *
+ * @var bool
+ */
+ public $create_thumb = FALSE;
+
+ /**
+ * String to add to thumbnail version of image
+ *
+ * @var string
+ */
+ public $thumb_marker = '_thumb';
+
+ /**
+ * Whether to maintain aspect ratio when resizing or use hard values
+ *
+ * @var bool
+ */
+ public $maintain_ratio = TRUE;
+
+ /**
+ * auto, height, or width. Determines what to use as the master dimension
+ *
+ * @var string
+ */
+ public $master_dim = 'auto';
+
+ /**
+ * Angle at to rotate image
+ *
+ * @var string
+ */
+ public $rotation_angle = '';
+
+ /**
+ * X Coordinate for manipulation of the current image
+ *
+ * @var int
+ */
+ public $x_axis = '';
+
+ /**
+ * Y Coordinate for manipulation of the current image
+ *
+ * @var int
+ */
+ public $y_axis = '';
+ // --------------------------------------------------------------------------
// Watermark Vars
- var $wm_text = ''; // Watermark text if graphic is not used
- var $wm_type = 'text'; // Type of watermarking. Options: text/overlay
- var $wm_x_transp = 4;
- var $wm_y_transp = 4;
- var $wm_overlay_path = ''; // Watermark image path
- var $wm_font_path = ''; // TT font
- var $wm_font_size = 17; // Font size (different versions of GD will either use points or pixels)
- var $wm_vrt_alignment = 'B'; // Vertical alignment: T M B
- var $wm_hor_alignment = 'C'; // Horizontal alignment: L R C
- var $wm_padding = 0; // Padding around text
- var $wm_hor_offset = 0; // Lets you push text to the right
- var $wm_vrt_offset = 0; // Lets you push text down
- var $wm_font_color = '#ffffff'; // Text color
- var $wm_shadow_color = ''; // Dropshadow color
- var $wm_shadow_distance = 2; // Dropshadow distance
- var $wm_opacity = 50; // Image opacity: 1 - 100 Only works with image
+ // --------------------------------------------------------------------------
+
+ /**
+ * Watermark text if graphic is not used
+ *
+ * @var string
+ */
+ public $wm_text = '';
+
+ /**
+ * Type of watermarking. Options: text/overlay
+ *
+ * @var string
+ */
+ public $wm_type = 'text';
+
+ /**
+ * Default transparency for watermark
+ *
+ * @var int
+ */
+ public $wm_x_transp = 4;
+
+ /**
+ * Default transparency for watermark
+ *
+ * @var int
+ */
+ public $wm_y_transp = 4;
+
+ /**
+ * Watermark image path
+ *
+ * @var string
+ */
+ public $wm_overlay_path = '';
+
+ /**
+ * TT font
+ *
+ * @var string
+ */
+ public $wm_font_path = '';
+
+ /**
+ * Font size (different versions of GD will either use points or pixels)
+ *
+ * @var int
+ */
+ public $wm_font_size = 17;
+
+ /**
+ * Vertical alignment: T M B
+ *
+ * @var string
+ */
+ public $wm_vrt_alignment = 'B';
+
+ /**
+ * Horizontal alignment: L R C
+ *
+ * @var string
+ */
+ public $wm_hor_alignment = 'C';
+
+ /**
+ * Padding around text
+ *
+ * @var int
+ */
+ public $wm_padding = 0;
+
+ /**
+ * Lets you push text to the right
+ *
+ * @var int
+ */
+ public $wm_hor_offset = 0;
+
+ /**
+ * Lets you push text down
+ *
+ * @var int
+ */
+ public $wm_vrt_offset = 0;
+
+ /**
+ * Text color
+ *
+ * @var string
+ */
+ protected $wm_font_color = '#ffffff';
+
+ /**
+ * Dropshadow color
+ *
+ * @var string
+ */
+ protected $wm_shadow_color = '';
+
+ /**
+ * Dropshadow distance
+ *
+ * @var int
+ */
+ public $wm_shadow_distance = 2;
+
+ /**
+ * Image opacity: 1 - 100 Only works with image
+ *
+ * @var int
+ */
+ public $wm_opacity = 50;
+ // --------------------------------------------------------------------------
// Private Vars
- var $source_folder = '';
- var $dest_folder = '';
- var $mime_type = '';
- var $orig_width = '';
- var $orig_height = '';
- var $image_type = '';
- var $size_str = '';
- var $full_src_path = '';
- var $full_dst_path = '';
- var $create_fnc = 'imagecreatetruecolor';
- var $copy_fnc = 'imagecopyresampled';
- var $error_msg = array();
- var $wm_use_drop_shadow = FALSE;
- var $wm_use_truetype = FALSE;
-
- /**
- * Constructor
+ // --------------------------------------------------------------------------
+
+ /**
+ * Source image folder
*
- * @param string
+ * @var string
+ */
+ public $source_folder = '';
+
+ /**
+ * Destination image folder
+ *
+ * @var string
+ */
+ public $dest_folder = '';
+
+ /**
+ * Image mime-type
+ *
+ * @var string
+ */
+ public $mime_type = '';
+
+ /**
+ * Original image width
+ *
+ * @var int
+ */
+ public $orig_width = '';
+
+ /**
+ * Original image height
+ *
+ * @var int
+ */
+ public $orig_height = '';
+
+ /**
+ * Image format
+ *
+ * @var string
+ */
+ public $image_type = '';
+
+ /**
+ * Size of current image
+ *
+ * @var string
+ */
+ public $size_str = '';
+
+ /**
+ * Full path to source image
+ *
+ * @var string
+ */
+ public $full_src_path = '';
+
+ /**
+ * Full path to destination image
+ *
+ * @var string
+ */
+ public $full_dst_path = '';
+
+ /**
+ * File permissions
+ *
+ * @var int
+ */
+ public $file_permissions = 0644;
+
+ /**
+ * Name of function to create image
+ *
+ * @var string
+ */
+ public $create_fnc = 'imagecreatetruecolor';
+
+ /**
+ * Name of function to copy image
+ *
+ * @var string
+ */
+ public $copy_fnc = 'imagecopyresampled';
+
+ /**
+ * Error messages
+ *
+ * @var array
+ */
+ public $error_msg = array();
+
+ /**
+ * Whether to have a drop shadow on watermark
+ *
+ * @var bool
+ */
+ protected $wm_use_drop_shadow = FALSE;
+
+ /**
+ * Whether to use truetype fonts
+ *
+ * @var bool
+ */
+ public $wm_use_truetype = FALSE;
+
+ /**
+ * Initialize Image Library
+ *
+ * @param array $props
* @return void
*/
public function __construct($props = array())
@@ -89,7 +393,17 @@ class CI_Image_lib {
$this->initialize($props);
}
- log_message('debug', "Image Lib Class Initialized");
+ /**
+ * A work-around for some improperly formatted, but
+ * usable JPEGs; known to be produced by Samsung
+ * smartphones' front-facing cameras.
+ *
+ * @see https://github.com/bcit-ci/CodeIgniter/issues/4967
+ * @see https://bugs.php.net/bug.php?id=72404
+ */
+ ini_set('gd.jpeg_ignore_warning', 1);
+
+ log_message('info', 'Image Lib Class Initialized');
}
// --------------------------------------------------------------------
@@ -99,20 +413,41 @@ class CI_Image_lib {
*
* Resets values in case this class is used in a loop
*
- * @access public
* @return void
*/
- function clear()
+ public function clear()
{
- $props = array('source_folder', 'dest_folder', 'source_image', 'full_src_path', 'full_dst_path', 'new_image', 'image_type', 'size_str', 'quality', 'orig_width', 'orig_height', 'width', 'height', 'rotation_angle', 'x_axis', 'y_axis', 'create_fnc', 'copy_fnc', 'wm_overlay_path', 'wm_use_truetype', 'dynamic_output', 'wm_font_size', 'wm_text', 'wm_vrt_alignment', 'wm_hor_alignment', 'wm_padding', 'wm_hor_offset', 'wm_vrt_offset', 'wm_font_color', 'wm_use_drop_shadow', 'wm_shadow_color', 'wm_shadow_distance', 'wm_opacity');
+ $props = array('thumb_marker', 'library_path', 'source_image', 'new_image', 'width', 'height', 'rotation_angle', 'x_axis', 'y_axis', 'wm_text', 'wm_overlay_path', 'wm_font_path', 'wm_shadow_color', 'source_folder', 'dest_folder', 'mime_type', 'orig_width', 'orig_height', 'image_type', 'size_str', 'full_src_path', 'full_dst_path');
foreach ($props as $val)
{
$this->$val = '';
}
- // special consideration for master_dim
- $this->master_dim = 'auto';
+ $this->image_library = 'gd2';
+ $this->dynamic_output = FALSE;
+ $this->quality = 90;
+ $this->create_thumb = FALSE;
+ $this->thumb_marker = '_thumb';
+ $this->maintain_ratio = TRUE;
+ $this->master_dim = 'auto';
+ $this->wm_type = 'text';
+ $this->wm_x_transp = 4;
+ $this->wm_y_transp = 4;
+ $this->wm_font_size = 17;
+ $this->wm_vrt_alignment = 'B';
+ $this->wm_hor_alignment = 'C';
+ $this->wm_padding = 0;
+ $this->wm_hor_offset = 0;
+ $this->wm_vrt_offset = 0;
+ $this->wm_font_color = '#ffffff';
+ $this->wm_shadow_distance = 2;
+ $this->wm_opacity = 50;
+ $this->create_fnc = 'imagecreatetruecolor';
+ $this->copy_fnc = 'imagecopyresampled';
+ $this->error_msg = array();
+ $this->wm_use_drop_shadow = FALSE;
+ $this->wm_use_truetype = FALSE;
}
// --------------------------------------------------------------------
@@ -120,42 +455,62 @@ class CI_Image_lib {
/**
* initialize image preferences
*
- * @access public
* @param array
* @return bool
*/
- function initialize($props = array())
+ public function initialize($props = array())
{
- /*
- * Convert array elements into class variables
- */
+ // Convert array elements into class variables
if (count($props) > 0)
{
foreach ($props as $key => $val)
{
- $this->$key = $val;
+ if (property_exists($this, $key))
+ {
+ if (in_array($key, array('wm_font_color', 'wm_shadow_color'), TRUE))
+ {
+ if (preg_match('/^#?([0-9a-f]{3}|[0-9a-f]{6})$/i', $val, $matches))
+ {
+ /* $matches[1] contains our hex color value, but it might be
+ * both in the full 6-length format or the shortened 3-length
+ * value.
+ * We'll later need the full version, so we keep it if it's
+ * already there and if not - we'll convert to it. We can
+ * access string characters by their index as in an array,
+ * so we'll do that and use concatenation to form the final
+ * value:
+ */
+ $val = (strlen($matches[1]) === 6)
+ ? '#'.$matches[1]
+ : '#'.$matches[1][0].$matches[1][0].$matches[1][1].$matches[1][1].$matches[1][2].$matches[1][2];
+ }
+ else
+ {
+ continue;
+ }
+ }
+ elseif (in_array($key, array('width', 'height'), TRUE) && ! ctype_digit((string) $val))
+ {
+ continue;
+ }
+
+ $this->$key = $val;
+ }
}
}
- /*
- * Is there a source image?
- *
- * If not, there's no reason to continue
- *
- */
- if ($this->source_image == '')
+ // Is there a source image? If not, there's no reason to continue
+ if ($this->source_image === '')
{
$this->set_error('imglib_source_image_required');
- return FALSE;
+ return FALSE;
}
- /*
- * Is getimagesize() Available?
+ /* Is getimagesize() available?
*
* We use it to determine the image properties (width/height).
- * Note: We need to figure out how to determine image
+ * Note: We need to figure out how to determine image
* properties using ImageMagick and NetPBM
- *
*/
if ( ! function_exists('getimagesize'))
{
@@ -165,17 +520,15 @@ class CI_Image_lib {
$this->image_library = strtolower($this->image_library);
- /*
- * Set the full server path
+ /* Set the full server path
*
* The source image may or may not contain a path.
* Either way, we'll try use realpath to generate the
* full server path in order to more reliably read it.
- *
*/
- if (function_exists('realpath') AND @realpath($this->source_image) !== FALSE)
+ if (($full_source_path = realpath($this->source_image)) !== FALSE)
{
- $full_source_path = str_replace("\\", "/", realpath($this->source_image));
+ $full_source_path = str_replace('\\', '/', $full_source_path);
}
else
{
@@ -189,7 +542,7 @@ class CI_Image_lib {
// Set the Image Properties
if ( ! $this->get_image_properties($this->source_folder.$this->source_image))
{
- return FALSE;
+ return FALSE;
}
/*
@@ -197,64 +550,51 @@ class CI_Image_lib {
*
* If the user has set a "new_image" name it means
* we are making a copy of the source image. If not
- * it means we are altering the original. We'll
+ * it means we are altering the original. We'll
* set the destination filename and path accordingly.
- *
*/
- if ($this->new_image == '')
+ if ($this->new_image === '')
+ {
+ $this->dest_image = $this->source_image;
+ $this->dest_folder = $this->source_folder;
+ }
+ elseif (strpos($this->new_image, '/') === FALSE && strpos($this->new_image, '\\') === FALSE)
{
- $this->dest_image = $this->source_image;
+ $this->dest_image = $this->new_image;
$this->dest_folder = $this->source_folder;
}
else
{
- if (strpos($this->new_image, '/') === FALSE AND strpos($this->new_image, '\\') === FALSE)
+ // Is there a file name?
+ if ( ! preg_match('#\.(jpg|jpeg|gif|png)$#i', $this->new_image))
{
- $this->dest_folder = $this->source_folder;
- $this->dest_image = $this->new_image;
+ $this->dest_image = $this->source_image;
+ $this->dest_folder = $this->new_image;
}
else
{
- if (function_exists('realpath') AND @realpath($this->new_image) !== FALSE)
- {
- $full_dest_path = str_replace("\\", "/", realpath($this->new_image));
- }
- else
- {
- $full_dest_path = $this->new_image;
- }
-
- // Is there a file name?
- if ( ! preg_match("#\.(jpg|jpeg|gif|png)$#i", $full_dest_path))
- {
- $this->dest_folder = $full_dest_path.'/';
- $this->dest_image = $this->source_image;
- }
- else
- {
- $x = explode('/', $full_dest_path);
- $this->dest_image = end($x);
- $this->dest_folder = str_replace($this->dest_image, '', $full_dest_path);
- }
+ $x = explode('/', str_replace('\\', '/', $this->new_image));
+ $this->dest_image = end($x);
+ $this->dest_folder = str_replace($this->dest_image, '', $this->new_image);
}
+
+ $this->dest_folder = realpath($this->dest_folder).'/';
}
- /*
- * Compile the finalized filenames/paths
+ /* Compile the finalized filenames/paths
*
* We'll create two master strings containing the
* full server path to the source image and the
* full server path to the destination image.
* We'll also split the destination image name
* so we can insert the thumbnail marker if needed.
- *
*/
- if ($this->create_thumb === FALSE OR $this->thumb_marker == '')
+ if ($this->create_thumb === FALSE OR $this->thumb_marker === '')
{
$this->thumb_marker = '';
}
- $xp = $this->explode_name($this->dest_image);
+ $xp = $this->explode_name($this->dest_image);
$filename = $xp['name'];
$file_ext = $xp['ext'];
@@ -262,71 +602,60 @@ class CI_Image_lib {
$this->full_src_path = $this->source_folder.$this->source_image;
$this->full_dst_path = $this->dest_folder.$filename.$this->thumb_marker.$file_ext;
- /*
- * Should we maintain image proportions?
+ /* Should we maintain image proportions?
*
* When creating thumbs or copies, the target width/height
* might not be in correct proportion with the source
- * image's width/height. We'll recalculate it here.
- *
+ * image's width/height. We'll recalculate it here.
*/
- if ($this->maintain_ratio === TRUE && ($this->width != '' AND $this->height != ''))
+ if ($this->maintain_ratio === TRUE && ($this->width !== 0 OR $this->height !== 0))
{
$this->image_reproportion();
}
- /*
- * Was a width and height specified?
- *
- * If the destination width/height was
- * not submitted we will use the values
- * from the actual file
+ /* Was a width and height specified?
*
+ * If the destination width/height was not submitted we
+ * will use the values from the actual file
*/
- if ($this->width == '')
+ if ($this->width === '')
+ {
$this->width = $this->orig_width;
+ }
- if ($this->height == '')
+ if ($this->height === '')
+ {
$this->height = $this->orig_height;
+ }
// Set the quality
- $this->quality = trim(str_replace("%", "", $this->quality));
+ $this->quality = trim(str_replace('%', '', $this->quality));
- if ($this->quality == '' OR $this->quality == 0 OR ! is_numeric($this->quality))
+ if ($this->quality === '' OR $this->quality === 0 OR ! ctype_digit($this->quality))
+ {
$this->quality = 90;
+ }
// Set the x/y coordinates
- $this->x_axis = ($this->x_axis == '' OR ! is_numeric($this->x_axis)) ? 0 : $this->x_axis;
- $this->y_axis = ($this->y_axis == '' OR ! is_numeric($this->y_axis)) ? 0 : $this->y_axis;
+ is_numeric($this->x_axis) OR $this->x_axis = 0;
+ is_numeric($this->y_axis) OR $this->y_axis = 0;
// Watermark-related Stuff...
- if ($this->wm_font_color != '')
+ if ($this->wm_overlay_path !== '')
{
- if (strlen($this->wm_font_color) == 6)
- {
- $this->wm_font_color = '#'.$this->wm_font_color;
- }
+ $this->wm_overlay_path = str_replace('\\', '/', realpath($this->wm_overlay_path));
}
- if ($this->wm_shadow_color != '')
+ if ($this->wm_shadow_color !== '')
{
- if (strlen($this->wm_shadow_color) == 6)
- {
- $this->wm_shadow_color = '#'.$this->wm_shadow_color;
- }
- }
-
- if ($this->wm_overlay_path != '')
- {
- $this->wm_overlay_path = str_replace("\\", "/", realpath($this->wm_overlay_path));
+ $this->wm_use_drop_shadow = TRUE;
}
-
- if ($this->wm_shadow_color != '')
+ elseif ($this->wm_use_drop_shadow === TRUE && $this->wm_shadow_color === '')
{
- $this->wm_use_drop_shadow = TRUE;
+ $this->wm_use_drop_shadow = FALSE;
}
- if ($this->wm_font_path != '')
+ if ($this->wm_font_path !== '')
{
$this->wm_use_truetype = TRUE;
}
@@ -342,18 +671,11 @@ class CI_Image_lib {
* This is a wrapper function that chooses the proper
* resize function based on the protocol specified
*
- * @access public
* @return bool
*/
- function resize()
+ public function resize()
{
- $protocol = 'image_process_'.$this->image_library;
-
- if (preg_match('/gd2$/i', $protocol))
- {
- $protocol = 'image_process_gd';
- }
-
+ $protocol = ($this->image_library === 'gd2') ? 'image_process_gd' : 'image_process_'.$this->image_library;
return $this->$protocol('resize');
}
@@ -365,18 +687,11 @@ class CI_Image_lib {
* This is a wrapper function that chooses the proper
* cropping function based on the protocol specified
*
- * @access public
* @return bool
*/
- function crop()
+ public function crop()
{
- $protocol = 'image_process_'.$this->image_library;
-
- if (preg_match('/gd2$/i', $protocol))
- {
- $protocol = 'image_process_gd';
- }
-
+ $protocol = ($this->image_library === 'gd2') ? 'image_process_gd' : 'image_process_'.$this->image_library;
return $this->$protocol('crop');
}
@@ -388,22 +703,21 @@ class CI_Image_lib {
* This is a wrapper function that chooses the proper
* rotation function based on the protocol specified
*
- * @access public
* @return bool
*/
- function rotate()
+ public function rotate()
{
// Allowed rotation values
$degs = array(90, 180, 270, 'vrt', 'hor');
- if ($this->rotation_angle == '' OR ! in_array($this->rotation_angle, $degs))
+ if ($this->rotation_angle === '' OR ! in_array($this->rotation_angle, $degs))
{
$this->set_error('imglib_rotation_angle_required');
- return FALSE;
+ return FALSE;
}
// Reassign the width and height
- if ($this->rotation_angle == 90 OR $this->rotation_angle == 270)
+ if ($this->rotation_angle === 90 OR $this->rotation_angle === 270)
{
$this->width = $this->orig_height;
$this->height = $this->orig_width;
@@ -414,23 +728,16 @@ class CI_Image_lib {
$this->height = $this->orig_height;
}
-
// Choose resizing function
- if ($this->image_library == 'imagemagick' OR $this->image_library == 'netpbm')
+ if ($this->image_library === 'imagemagick' OR $this->image_library === 'netpbm')
{
$protocol = 'image_process_'.$this->image_library;
-
return $this->$protocol('rotate');
}
- if ($this->rotation_angle == 'hor' OR $this->rotation_angle == 'vrt')
- {
- return $this->image_mirror_gd();
- }
- else
- {
- return $this->image_rotate_gd();
- }
+ return ($this->rotation_angle === 'hor' OR $this->rotation_angle === 'vrt')
+ ? $this->image_mirror_gd()
+ : $this->image_rotate_gd();
}
// --------------------------------------------------------------------
@@ -440,36 +747,29 @@ class CI_Image_lib {
*
* This function will resize or crop
*
- * @access public
* @param string
* @return bool
*/
- function image_process_gd($action = 'resize')
+ public function image_process_gd($action = 'resize')
{
$v2_override = FALSE;
// If the target width/height match the source, AND if the new file name is not equal to the old file name
// we'll simply make a copy of the original with the new name... assuming dynamic rendering is off.
- if ($this->dynamic_output === FALSE)
+ if ($this->dynamic_output === FALSE && $this->orig_width === $this->width && $this->orig_height === $this->height)
{
- if ($this->orig_width == $this->width AND $this->orig_height == $this->height)
+ if ($this->source_image !== $this->new_image && @copy($this->full_src_path, $this->full_dst_path))
{
- if ($this->source_image != $this->new_image)
- {
- if (@copy($this->full_src_path, $this->full_dst_path))
- {
- @chmod($this->full_dst_path, FILE_WRITE_MODE);
- }
- }
-
- return TRUE;
+ chmod($this->full_dst_path, $this->file_permissions);
}
+
+ return TRUE;
}
// Let's set up our values based on the action
- if ($action == 'crop')
+ if ($action === 'crop')
{
- // Reassign the source width/height if cropping
+ // Reassign the source width/height if cropping
$this->orig_width = $this->width;
$this->orig_height = $this->height;
@@ -477,7 +777,7 @@ class CI_Image_lib {
if ($this->gd_version() !== FALSE)
{
$gd_version = str_replace('0', '', $this->gd_version());
- $v2_override = ($gd_version == 2) ? TRUE : FALSE;
+ $v2_override = ($gd_version == 2);
}
}
else
@@ -487,20 +787,21 @@ class CI_Image_lib {
$this->y_axis = 0;
}
- // Create the image handle
+ // Create the image handle
if ( ! ($src_img = $this->image_create_gd()))
{
return FALSE;
}
- // Create The Image
- //
- // old conditional which users report cause problems with shared GD libs who report themselves as "2.0 or greater"
- // it appears that this is no longer the issue that it was in 2004, so we've removed it, retaining it in the comment
- // below should that ever prove inaccurate.
- //
- // if ($this->image_library == 'gd2' AND function_exists('imagecreatetruecolor') AND $v2_override == FALSE)
- if ($this->image_library == 'gd2' AND function_exists('imagecreatetruecolor'))
+ /* Create the image
+ *
+ * Old conditional which users report cause problems with shared GD libs who report themselves as "2.0 or greater"
+ * it appears that this is no longer the issue that it was in 2004, so we've removed it, retaining it in the comment
+ * below should that ever prove inaccurate.
+ *
+ * if ($this->image_library === 'gd2' && function_exists('imagecreatetruecolor') && $v2_override === FALSE)
+ */
+ if ($this->image_library === 'gd2' && function_exists('imagecreatetruecolor'))
{
$create = 'imagecreatetruecolor';
$copy = 'imagecopyresampled';
@@ -513,7 +814,7 @@ class CI_Image_lib {
$dst_img = $create($this->width, $this->height);
- if ($this->image_type == 3) // png we can actually preserve transparency
+ if ($this->image_type === 3) // png we can actually preserve transparency
{
imagealphablending($dst_img, FALSE);
imagesavealpha($dst_img, TRUE);
@@ -521,26 +822,24 @@ class CI_Image_lib {
$copy($dst_img, $src_img, 0, 0, $this->x_axis, $this->y_axis, $this->width, $this->height, $this->orig_width, $this->orig_height);
- // Show the image
- if ($this->dynamic_output == TRUE)
+ // Show the image
+ if ($this->dynamic_output === TRUE)
{
$this->image_display_gd($dst_img);
}
- else
+ elseif ( ! $this->image_save_gd($dst_img)) // Or save it
{
- // Or save it
- if ( ! $this->image_save_gd($dst_img))
- {
- return FALSE;
- }
+ return FALSE;
}
- // Kill the file handles
+ // Kill the file handles
imagedestroy($dst_img);
imagedestroy($src_img);
- // Set the file to 777
- @chmod($this->full_dst_path, FILE_WRITE_MODE);
+ if ($this->dynamic_output !== TRUE)
+ {
+ chmod($this->full_dst_path, $this->file_permissions);
+ }
return TRUE;
}
@@ -552,65 +851,65 @@ class CI_Image_lib {
*
* This function will resize, crop or rotate
*
- * @access public
* @param string
* @return bool
*/
- function image_process_imagemagick($action = 'resize')
+ public function image_process_imagemagick($action = 'resize')
{
- // Do we have a vaild library path?
- if ($this->library_path == '')
+ // Do we have a vaild library path?
+ if ($this->library_path === '')
{
$this->set_error('imglib_libpath_invalid');
return FALSE;
}
- if ( ! preg_match("/convert$/i", $this->library_path))
+ if ( ! preg_match('/convert$/i', $this->library_path))
{
- $this->library_path = rtrim($this->library_path, '/').'/';
-
- $this->library_path .= 'convert';
+ $this->library_path = rtrim($this->library_path, '/').'/convert';
}
// Execute the command
- $cmd = $this->library_path." -quality ".$this->quality;
+ $cmd = $this->library_path.' -quality '.$this->quality;
- if ($action == 'crop')
+ if ($action === 'crop')
{
- $cmd .= " -crop ".$this->width."x".$this->height."+".$this->x_axis."+".$this->y_axis." \"$this->full_src_path\" \"$this->full_dst_path\" 2>&1";
+ $cmd .= ' -crop '.$this->width.'x'.$this->height.'+'.$this->x_axis.'+'.$this->y_axis;
}
- elseif ($action == 'rotate')
+ elseif ($action === 'rotate')
{
- switch ($this->rotation_angle)
- {
- case 'hor' : $angle = '-flop';
- break;
- case 'vrt' : $angle = '-flip';
- break;
- default : $angle = '-rotate '.$this->rotation_angle;
- break;
- }
-
- $cmd .= " ".$angle." \"$this->full_src_path\" \"$this->full_dst_path\" 2>&1";
+ $cmd .= ($this->rotation_angle === 'hor' OR $this->rotation_angle === 'vrt')
+ ? ' -flop'
+ : ' -rotate '.$this->rotation_angle;
}
- else // Resize
+ else // Resize
{
- $cmd .= " -resize ".$this->width."x".$this->height." \"$this->full_src_path\" \"$this->full_dst_path\" 2>&1";
+ if($this->maintain_ratio === TRUE)
+ {
+ $cmd .= ' -resize '.$this->width.'x'.$this->height;
+ }
+ else
+ {
+ $cmd .= ' -resize '.$this->width.'x'.$this->height.'\!';
+ }
}
- $retval = 1;
+ $cmd .= ' '.escapeshellarg($this->full_src_path).' '.escapeshellarg($this->full_dst_path).' 2>&1';
- @exec($cmd, $output, $retval);
+ $retval = 1;
+ // exec() might be disabled
+ if (function_usable('exec'))
+ {
+ @exec($cmd, $output, $retval);
+ }
- // Did it work?
+ // Did it work?
if ($retval > 0)
{
$this->set_error('imglib_image_process_failed');
return FALSE;
}
- // Set the file to 777
- @chmod($this->full_dst_path, FILE_WRITE_MODE);
+ chmod($this->full_dst_path, $this->file_permissions);
return TRUE;
}
@@ -622,52 +921,55 @@ class CI_Image_lib {
*
* This function will resize, crop or rotate
*
- * @access public
* @param string
* @return bool
*/
- function image_process_netpbm($action = 'resize')
+ public function image_process_netpbm($action = 'resize')
{
- if ($this->library_path == '')
+ if ($this->library_path === '')
{
$this->set_error('imglib_libpath_invalid');
return FALSE;
}
- // Build the resizing command
+ // Build the resizing command
switch ($this->image_type)
{
case 1 :
- $cmd_in = 'giftopnm';
- $cmd_out = 'ppmtogif';
+ $cmd_in = 'giftopnm';
+ $cmd_out = 'ppmtogif';
break;
case 2 :
- $cmd_in = 'jpegtopnm';
- $cmd_out = 'ppmtojpeg';
+ $cmd_in = 'jpegtopnm';
+ $cmd_out = 'ppmtojpeg';
break;
case 3 :
- $cmd_in = 'pngtopnm';
- $cmd_out = 'ppmtopng';
+ $cmd_in = 'pngtopnm';
+ $cmd_out = 'ppmtopng';
+ break;
+ case 18 :
+ $cmd_in = 'webptopnm';
+ $cmd_out = 'ppmtowebp';
break;
}
- if ($action == 'crop')
+ if ($action === 'crop')
{
$cmd_inner = 'pnmcut -left '.$this->x_axis.' -top '.$this->y_axis.' -width '.$this->width.' -height '.$this->height;
}
- elseif ($action == 'rotate')
+ elseif ($action === 'rotate')
{
switch ($this->rotation_angle)
{
- case 90 : $angle = 'r270';
+ case 90: $angle = 'r270';
break;
- case 180 : $angle = 'r180';
+ case 180: $angle = 'r180';
break;
- case 270 : $angle = 'r90';
+ case 270: $angle = 'r90';
break;
- case 'vrt' : $angle = 'tb';
+ case 'vrt': $angle = 'tb';
break;
- case 'hor' : $angle = 'lr';
+ case 'hor': $angle = 'lr';
break;
}
@@ -678,13 +980,16 @@ class CI_Image_lib {
$cmd_inner = 'pnmscale -xysize '.$this->width.' '.$this->height;
}
- $cmd = $this->library_path.$cmd_in.' '.$this->full_src_path.' | '.$cmd_inner.' | '.$cmd_out.' > '.$this->dest_folder.'netpbm.tmp';
+ $cmd = $this->library_path.$cmd_in.' '.escapeshellarg($this->full_src_path).' | '.$cmd_inner.' | '.$cmd_out.' > '.$this->dest_folder.'netpbm.tmp';
$retval = 1;
+ // exec() might be disabled
+ if (function_usable('exec'))
+ {
+ @exec($cmd, $output, $retval);
+ }
- @exec($cmd, $output, $retval);
-
- // Did it work?
+ // Did it work?
if ($retval > 0)
{
$this->set_error('imglib_image_process_failed');
@@ -694,9 +999,9 @@ class CI_Image_lib {
// With NetPBM we have to create a temporary image.
// If you try manipulating the original it fails so
// we have to rename the temp file.
- copy ($this->dest_folder.'netpbm.tmp', $this->full_dst_path);
- unlink ($this->dest_folder.'netpbm.tmp');
- @chmod($this->full_dst_path, FILE_WRITE_MODE);
+ copy($this->dest_folder.'netpbm.tmp', $this->full_dst_path);
+ unlink($this->dest_folder.'netpbm.tmp');
+ chmod($this->full_dst_path, $this->file_permissions);
return TRUE;
}
@@ -706,12 +1011,11 @@ class CI_Image_lib {
/**
* Image Rotate Using GD
*
- * @access public
* @return bool
*/
- function image_rotate_gd()
+ public function image_rotate_gd()
{
- // Create the image handle
+ // Create the image handle
if ( ! ($src_img = $this->image_create_gd()))
{
return FALSE;
@@ -722,32 +1026,26 @@ class CI_Image_lib {
// going to have to figure out how to determine the color
// of the alpha channel in a future release.
- $white = imagecolorallocate($src_img, 255, 255, 255);
+ $white = imagecolorallocate($src_img, 255, 255, 255);
- // Rotate it!
+ // Rotate it!
$dst_img = imagerotate($src_img, $this->rotation_angle, $white);
- // Save the Image
- if ($this->dynamic_output == TRUE)
+ // Show the image
+ if ($this->dynamic_output === TRUE)
{
$this->image_display_gd($dst_img);
}
- else
+ elseif ( ! $this->image_save_gd($dst_img)) // ... or save it
{
- // Or save it
- if ( ! $this->image_save_gd($dst_img))
- {
- return FALSE;
- }
+ return FALSE;
}
- // Kill the file handles
+ // Kill the file handles
imagedestroy($dst_img);
imagedestroy($src_img);
- // Set the file to 777
-
- @chmod($this->full_dst_path, FILE_WRITE_MODE);
+ chmod($this->full_dst_path, $this->file_permissions);
return TRUE;
}
@@ -759,10 +1057,9 @@ class CI_Image_lib {
*
* This function will flip horizontal or vertical
*
- * @access public
* @return bool
*/
- function image_mirror_gd()
+ public function image_mirror_gd()
{
if ( ! $src_img = $this->image_create_gd())
{
@@ -772,12 +1069,12 @@ class CI_Image_lib {
$width = $this->orig_width;
$height = $this->orig_height;
- if ($this->rotation_angle == 'hor')
+ if ($this->rotation_angle === 'hor')
{
for ($i = 0; $i < $height; $i++)
{
- $left = 0;
- $right = $width-1;
+ $left = 0;
+ $right = $width - 1;
while ($left < $right)
{
@@ -797,41 +1094,36 @@ class CI_Image_lib {
for ($i = 0; $i < $width; $i++)
{
$top = 0;
- $bot = $height-1;
+ $bottom = $height - 1;
- while ($top < $bot)
+ while ($top < $bottom)
{
$ct = imagecolorat($src_img, $i, $top);
- $cb = imagecolorat($src_img, $i, $bot);
+ $cb = imagecolorat($src_img, $i, $bottom);
imagesetpixel($src_img, $i, $top, $cb);
- imagesetpixel($src_img, $i, $bot, $ct);
+ imagesetpixel($src_img, $i, $bottom, $ct);
$top++;
- $bot--;
+ $bottom--;
}
}
}
- // Show the image
- if ($this->dynamic_output == TRUE)
+ // Show the image
+ if ($this->dynamic_output === TRUE)
{
$this->image_display_gd($src_img);
}
- else
+ elseif ( ! $this->image_save_gd($src_img)) // ... or save it
{
- // Or save it
- if ( ! $this->image_save_gd($src_img))
- {
- return FALSE;
- }
+ return FALSE;
}
- // Kill the file handles
+ // Kill the file handles
imagedestroy($src_img);
- // Set the file to 777
- @chmod($this->full_dst_path, FILE_WRITE_MODE);
+ chmod($this->full_dst_path, $this->file_permissions);
return TRUE;
}
@@ -844,20 +1136,11 @@ class CI_Image_lib {
* This is a wrapper function that chooses the type
* of watermarking based on the specified preference.
*
- * @access public
- * @param string
* @return bool
*/
- function watermark()
+ public function watermark()
{
- if ($this->wm_type == 'overlay')
- {
- return $this->overlay_watermark();
- }
- else
- {
- return $this->text_watermark();
- }
+ return ($this->wm_type === 'overlay') ? $this->overlay_watermark() : $this->text_watermark();
}
// --------------------------------------------------------------------
@@ -865,10 +1148,9 @@ class CI_Image_lib {
/**
* Watermark - Graphic Version
*
- * @access public
* @return bool
*/
- function overlay_watermark()
+ public function overlay_watermark()
{
if ( ! function_exists('imagecolortransparent'))
{
@@ -876,63 +1158,61 @@ class CI_Image_lib {
return FALSE;
}
- // Fetch source image properties
+ // Fetch source image properties
$this->get_image_properties();
- // Fetch watermark image properties
- $props = $this->get_image_properties($this->wm_overlay_path, TRUE);
+ // Fetch watermark image properties
+ $props = $this->get_image_properties($this->wm_overlay_path, TRUE);
$wm_img_type = $props['image_type'];
- $wm_width = $props['width'];
- $wm_height = $props['height'];
+ $wm_width = $props['width'];
+ $wm_height = $props['height'];
- // Create two image resources
+ // Create two image resources
$wm_img = $this->image_create_gd($this->wm_overlay_path, $wm_img_type);
$src_img = $this->image_create_gd($this->full_src_path);
// Reverse the offset if necessary
// When the image is positioned at the bottom
// we don't want the vertical offset to push it
- // further down. We want the reverse, so we'll
- // invert the offset. Same with the horizontal
+ // further down. We want the reverse, so we'll
+ // invert the offset. Same with the horizontal
// offset when the image is at the right
- $this->wm_vrt_alignment = strtoupper(substr($this->wm_vrt_alignment, 0, 1));
- $this->wm_hor_alignment = strtoupper(substr($this->wm_hor_alignment, 0, 1));
+ $this->wm_vrt_alignment = strtoupper($this->wm_vrt_alignment[0]);
+ $this->wm_hor_alignment = strtoupper($this->wm_hor_alignment[0]);
- if ($this->wm_vrt_alignment == 'B')
+ if ($this->wm_vrt_alignment === 'B')
$this->wm_vrt_offset = $this->wm_vrt_offset * -1;
- if ($this->wm_hor_alignment == 'R')
+ if ($this->wm_hor_alignment === 'R')
$this->wm_hor_offset = $this->wm_hor_offset * -1;
- // Set the base x and y axis values
+ // Set the base x and y axis values
$x_axis = $this->wm_hor_offset + $this->wm_padding;
$y_axis = $this->wm_vrt_offset + $this->wm_padding;
- // Set the vertical position
- switch ($this->wm_vrt_alignment)
+ // Set the vertical position
+ if ($this->wm_vrt_alignment === 'M')
{
- case 'T':
- break;
- case 'M': $y_axis += ($this->orig_height / 2) - ($wm_height / 2);
- break;
- case 'B': $y_axis += $this->orig_height - $wm_height;
- break;
+ $y_axis += ($this->orig_height / 2) - ($wm_height / 2);
+ }
+ elseif ($this->wm_vrt_alignment === 'B')
+ {
+ $y_axis += $this->orig_height - $wm_height;
}
- // Set the horizontal position
- switch ($this->wm_hor_alignment)
+ // Set the horizontal position
+ if ($this->wm_hor_alignment === 'C')
{
- case 'L':
- break;
- case 'C': $x_axis += ($this->orig_width / 2) - ($wm_width / 2);
- break;
- case 'R': $x_axis += $this->orig_width - $wm_width;
- break;
+ $x_axis += ($this->orig_width / 2) - ($wm_width / 2);
+ }
+ elseif ($this->wm_hor_alignment === 'R')
+ {
+ $x_axis += $this->orig_width - $wm_width;
}
- // Build the finalized image
- if ($wm_img_type == 3 AND function_exists('imagealphablending'))
+ // Build the finalized image
+ if ($wm_img_type === 3)
{
@imagealphablending($src_img, TRUE);
}
@@ -954,17 +1234,21 @@ class CI_Image_lib {
imagecopymerge($src_img, $wm_img, $x_axis, $y_axis, 0, 0, $wm_width, $wm_height, $this->wm_opacity);
}
- // Output the image
- if ($this->dynamic_output == TRUE)
+ // We can preserve transparency for PNG images
+ if ($this->image_type === 3)
+ {
+ imagealphablending($src_img, FALSE);
+ imagesavealpha($src_img, TRUE);
+ }
+
+ // Output the image
+ if ($this->dynamic_output === TRUE)
{
$this->image_display_gd($src_img);
}
- else
+ elseif ( ! $this->image_save_gd($src_img)) // ... or save it
{
- if ( ! $this->image_save_gd($src_img))
- {
- return FALSE;
- }
+ return FALSE;
}
imagedestroy($src_img);
@@ -978,62 +1262,63 @@ class CI_Image_lib {
/**
* Watermark - Text Version
*
- * @access public
* @return bool
*/
- function text_watermark()
+ public function text_watermark()
{
if ( ! ($src_img = $this->image_create_gd()))
{
return FALSE;
}
- if ($this->wm_use_truetype == TRUE AND ! file_exists($this->wm_font_path))
+ if ($this->wm_use_truetype === TRUE && ! file_exists($this->wm_font_path))
{
$this->set_error('imglib_missing_font');
return FALSE;
}
- // Fetch source image properties
+ // Fetch source image properties
$this->get_image_properties();
- // Set RGB values for text and shadow
- $this->wm_font_color = str_replace('#', '', $this->wm_font_color);
- $this->wm_shadow_color = str_replace('#', '', $this->wm_shadow_color);
-
- $R1 = hexdec(substr($this->wm_font_color, 0, 2));
- $G1 = hexdec(substr($this->wm_font_color, 2, 2));
- $B1 = hexdec(substr($this->wm_font_color, 4, 2));
-
- $R2 = hexdec(substr($this->wm_shadow_color, 0, 2));
- $G2 = hexdec(substr($this->wm_shadow_color, 2, 2));
- $B2 = hexdec(substr($this->wm_shadow_color, 4, 2));
-
- $txt_color = imagecolorclosest($src_img, $R1, $G1, $B1);
- $drp_color = imagecolorclosest($src_img, $R2, $G2, $B2);
-
// Reverse the vertical offset
// When the image is positioned at the bottom
// we don't want the vertical offset to push it
- // further down. We want the reverse, so we'll
- // invert the offset. Note: The horizontal
+ // further down. We want the reverse, so we'll
+ // invert the offset. Note: The horizontal
// offset flips itself automatically
- if ($this->wm_vrt_alignment == 'B')
+ if ($this->wm_vrt_alignment === 'B')
+ {
$this->wm_vrt_offset = $this->wm_vrt_offset * -1;
+ }
- if ($this->wm_hor_alignment == 'R')
+ if ($this->wm_hor_alignment === 'R')
+ {
$this->wm_hor_offset = $this->wm_hor_offset * -1;
+ }
// Set font width and height
// These are calculated differently depending on
// whether we are using the true type font or not
- if ($this->wm_use_truetype == TRUE)
+ if ($this->wm_use_truetype === TRUE)
{
- if ($this->wm_font_size == '')
- $this->wm_font_size = '17';
+ if (empty($this->wm_font_size))
+ {
+ $this->wm_font_size = 17;
+ }
+
+ if (function_exists('imagettfbbox'))
+ {
+ $temp = imagettfbbox($this->wm_font_size, 0, $this->wm_font_path, $this->wm_text);
+ $temp = $temp[2] - $temp[0];
+
+ $fontwidth = $temp / strlen($this->wm_text);
+ }
+ else
+ {
+ $fontwidth = $this->wm_font_size - ($this->wm_font_size / 4);
+ }
- $fontwidth = $this->wm_font_size-($this->wm_font_size/4);
$fontheight = $this->wm_font_size;
$this->wm_vrt_offset += $this->wm_font_size;
}
@@ -1047,59 +1332,88 @@ class CI_Image_lib {
$x_axis = $this->wm_hor_offset + $this->wm_padding;
$y_axis = $this->wm_vrt_offset + $this->wm_padding;
- // Set verticle alignment
- if ($this->wm_use_drop_shadow == FALSE)
+ if ($this->wm_use_drop_shadow === FALSE)
+ {
$this->wm_shadow_distance = 0;
+ }
- $this->wm_vrt_alignment = strtoupper(substr($this->wm_vrt_alignment, 0, 1));
- $this->wm_hor_alignment = strtoupper(substr($this->wm_hor_alignment, 0, 1));
+ $this->wm_vrt_alignment = strtoupper($this->wm_vrt_alignment[0]);
+ $this->wm_hor_alignment = strtoupper($this->wm_hor_alignment[0]);
- switch ($this->wm_vrt_alignment)
+ // Set vertical alignment
+ if ($this->wm_vrt_alignment === 'M')
{
- case "T" :
- break;
- case "M": $y_axis += ($this->orig_height/2)+($fontheight/2);
- break;
- case "B": $y_axis += ($this->orig_height - $fontheight - $this->wm_shadow_distance - ($fontheight/2));
- break;
+ $y_axis += ($this->orig_height / 2) + ($fontheight / 2);
+ }
+ elseif ($this->wm_vrt_alignment === 'B')
+ {
+ $y_axis += $this->orig_height - $fontheight - $this->wm_shadow_distance - ($fontheight / 2);
}
-
- $x_shad = $x_axis + $this->wm_shadow_distance;
- $y_shad = $y_axis + $this->wm_shadow_distance;
// Set horizontal alignment
- switch ($this->wm_hor_alignment)
+ if ($this->wm_hor_alignment === 'R')
{
- case "L":
- break;
- case "R":
- if ($this->wm_use_drop_shadow)
- $x_shad += ($this->orig_width - $fontwidth*strlen($this->wm_text));
- $x_axis += ($this->orig_width - $fontwidth*strlen($this->wm_text));
- break;
- case "C":
- if ($this->wm_use_drop_shadow)
- $x_shad += floor(($this->orig_width - $fontwidth*strlen($this->wm_text))/2);
- $x_axis += floor(($this->orig_width -$fontwidth*strlen($this->wm_text))/2);
- break;
+ $x_axis += $this->orig_width - ($fontwidth * strlen($this->wm_text)) - $this->wm_shadow_distance;
+ }
+ elseif ($this->wm_hor_alignment === 'C')
+ {
+ $x_axis += floor(($this->orig_width - ($fontwidth * strlen($this->wm_text))) / 2);
}
- // Add the text to the source image
- if ($this->wm_use_truetype)
+ if ($this->wm_use_drop_shadow)
{
- if ($this->wm_use_drop_shadow)
+ // Offset from text
+ $x_shad = $x_axis + $this->wm_shadow_distance;
+ $y_shad = $y_axis + $this->wm_shadow_distance;
+
+ /* Set RGB values for shadow
+ *
+ * First character is #, so we don't really need it.
+ * Get the rest of the string and split it into 2-length
+ * hex values:
+ */
+ $drp_color = str_split(substr($this->wm_shadow_color, 1, 6), 2);
+ $drp_color = imagecolorclosest($src_img, hexdec($drp_color[0]), hexdec($drp_color[1]), hexdec($drp_color[2]));
+
+ // Add the shadow to the source image
+ if ($this->wm_use_truetype)
+ {
imagettftext($src_img, $this->wm_font_size, 0, $x_shad, $y_shad, $drp_color, $this->wm_font_path, $this->wm_text);
- imagettftext($src_img, $this->wm_font_size, 0, $x_axis, $y_axis, $txt_color, $this->wm_font_path, $this->wm_text);
+ }
+ else
+ {
+ imagestring($src_img, $this->wm_font_size, $x_shad, $y_shad, $this->wm_text, $drp_color);
+ }
+ }
+
+ /* Set RGB values for text
+ *
+ * First character is #, so we don't really need it.
+ * Get the rest of the string and split it into 2-length
+ * hex values:
+ */
+ $txt_color = str_split(substr($this->wm_font_color, 1, 6), 2);
+ $txt_color = imagecolorclosest($src_img, hexdec($txt_color[0]), hexdec($txt_color[1]), hexdec($txt_color[2]));
+
+ // Add the text to the source image
+ if ($this->wm_use_truetype)
+ {
+ imagettftext($src_img, $this->wm_font_size, 0, $x_axis, $y_axis, $txt_color, $this->wm_font_path, $this->wm_text);
}
else
{
- if ($this->wm_use_drop_shadow)
- imagestring($src_img, $this->wm_font_size, $x_shad, $y_shad, $this->wm_text, $drp_color);
- imagestring($src_img, $this->wm_font_size, $x_axis, $y_axis, $this->wm_text, $txt_color);
+ imagestring($src_img, $this->wm_font_size, $x_axis, $y_axis, $this->wm_text, $txt_color);
}
- // Output the final image
- if ($this->dynamic_output == TRUE)
+ // We can preserve transparency for PNG images
+ if ($this->image_type === 3)
+ {
+ imagealphablending($src_img, FALSE);
+ imagesavealpha($src_img, TRUE);
+ }
+
+ // Output the final image
+ if ($this->dynamic_output === TRUE)
{
$this->image_display_gd($src_img);
}
@@ -1121,53 +1435,60 @@ class CI_Image_lib {
* This simply creates an image resource handle
* based on the type of image being processed
*
- * @access public
+ * @param string
* @param string
* @return resource
*/
- function image_create_gd($path = '', $image_type = '')
+ public function image_create_gd($path = '', $image_type = '')
{
- if ($path == '')
+ if ($path === '')
+ {
$path = $this->full_src_path;
+ }
- if ($image_type == '')
+ if ($image_type === '')
+ {
$image_type = $this->image_type;
-
+ }
switch ($image_type)
{
- case 1 :
- if ( ! function_exists('imagecreatefromgif'))
- {
- $this->set_error(array('imglib_unsupported_imagecreate', 'imglib_gif_not_supported'));
- return FALSE;
- }
+ case 1:
+ if ( ! function_exists('imagecreatefromgif'))
+ {
+ $this->set_error(array('imglib_unsupported_imagecreate', 'imglib_gif_not_supported'));
+ return FALSE;
+ }
- return imagecreatefromgif($path);
- break;
- case 2 :
- if ( ! function_exists('imagecreatefromjpeg'))
- {
- $this->set_error(array('imglib_unsupported_imagecreate', 'imglib_jpg_not_supported'));
- return FALSE;
- }
+ return imagecreatefromgif($path);
+ case 2:
+ if ( ! function_exists('imagecreatefromjpeg'))
+ {
+ $this->set_error(array('imglib_unsupported_imagecreate', 'imglib_jpg_not_supported'));
+ return FALSE;
+ }
- return imagecreatefromjpeg($path);
- break;
- case 3 :
- if ( ! function_exists('imagecreatefrompng'))
- {
- $this->set_error(array('imglib_unsupported_imagecreate', 'imglib_png_not_supported'));
- return FALSE;
- }
+ return imagecreatefromjpeg($path);
+ case 3:
+ if ( ! function_exists('imagecreatefrompng'))
+ {
+ $this->set_error(array('imglib_unsupported_imagecreate', 'imglib_png_not_supported'));
+ return FALSE;
+ }
- return imagecreatefrompng($path);
- break;
+ return imagecreatefrompng($path);
+ case 18:
+ if ( ! function_exists('imagecreatefromwebp'))
+ {
+ $this->set_error(array('imglib_unsupported_imagecreate', 'imglib_webp_not_supported'));
+ return FALSE;
+ }
+ return imagecreatefromwebp($path);
+ default:
+ $this->set_error(array('imglib_unsupported_imagecreate'));
+ return FALSE;
}
-
- $this->set_error(array('imglib_unsupported_imagecreate'));
- return FALSE;
}
// --------------------------------------------------------------------
@@ -1178,57 +1499,69 @@ class CI_Image_lib {
* Takes an image resource as input and writes the file
* to the specified destination
*
- * @access public
* @param resource
* @return bool
*/
- function image_save_gd($resource)
+ public function image_save_gd($resource)
{
switch ($this->image_type)
{
- case 1 :
- if ( ! function_exists('imagegif'))
- {
- $this->set_error(array('imglib_unsupported_imagecreate', 'imglib_gif_not_supported'));
- return FALSE;
- }
+ case 1:
+ if ( ! function_exists('imagegif'))
+ {
+ $this->set_error(array('imglib_unsupported_imagecreate', 'imglib_gif_not_supported'));
+ return FALSE;
+ }
- if ( ! @imagegif($resource, $this->full_dst_path))
- {
- $this->set_error('imglib_save_failed');
- return FALSE;
- }
- break;
- case 2 :
- if ( ! function_exists('imagejpeg'))
- {
- $this->set_error(array('imglib_unsupported_imagecreate', 'imglib_jpg_not_supported'));
- return FALSE;
- }
+ if ( ! @imagegif($resource, $this->full_dst_path))
+ {
+ $this->set_error('imglib_save_failed');
+ return FALSE;
+ }
+ break;
+ case 2:
+ if ( ! function_exists('imagejpeg'))
+ {
+ $this->set_error(array('imglib_unsupported_imagecreate', 'imglib_jpg_not_supported'));
+ return FALSE;
+ }
- if ( ! @imagejpeg($resource, $this->full_dst_path, $this->quality))
- {
- $this->set_error('imglib_save_failed');
- return FALSE;
- }
- break;
- case 3 :
- if ( ! function_exists('imagepng'))
- {
- $this->set_error(array('imglib_unsupported_imagecreate', 'imglib_png_not_supported'));
- return FALSE;
- }
+ if ( ! @imagejpeg($resource, $this->full_dst_path, $this->quality))
+ {
+ $this->set_error('imglib_save_failed');
+ return FALSE;
+ }
+ break;
+ case 3:
+ if ( ! function_exists('imagepng'))
+ {
+ $this->set_error(array('imglib_unsupported_imagecreate', 'imglib_png_not_supported'));
+ return FALSE;
+ }
- if ( ! @imagepng($resource, $this->full_dst_path))
- {
- $this->set_error('imglib_save_failed');
- return FALSE;
- }
- break;
- default :
- $this->set_error(array('imglib_unsupported_imagecreate'));
- return FALSE;
- break;
+ if ( ! @imagepng($resource, $this->full_dst_path))
+ {
+ $this->set_error('imglib_save_failed');
+ return FALSE;
+ }
+ break;
+ case 18:
+ if ( ! function_exists('imagewebp'))
+ {
+ $this->set_error(array('imglib_unsupported_imagecreate', 'imglib_webp_not_supported'));
+ return FALSE;
+ }
+
+ if ( ! @imagewebp($resource, $this->full_dst_path))
+ {
+ $this->set_error('imglib_save_failed');
+ return FALSE;
+ }
+ break;
+ default:
+ $this->set_error(array('imglib_unsupported_imagecreate'));
+ return FALSE;
+ break;
}
return TRUE;
@@ -1239,26 +1572,36 @@ class CI_Image_lib {
/**
* Dynamically outputs an image
*
- * @access public
* @param resource
* @return void
*/
- function image_display_gd($resource)
+ public function image_display_gd($resource)
{
- header("Content-Disposition: filename={$this->source_image};");
- header("Content-Type: {$this->mime_type}");
+ // RFC 6266 allows for multibyte filenames, but only in UTF-8,
+ // so we have to make it conditional ...
+ $filename = basename(empty($this->new_image) ? $this->source_image : $this->new_image);
+ $charset = strtoupper(config_item('charset'));
+ $utf8_filename = ($charset !== 'UTF-8')
+ ? get_instance()->utf8->convert_to_utf8($filename, $charset)
+ : $filename;
+ isset($utf8_filename[0]) && $utf8_filename = " filename*=UTF-8''".rawurlencode($utf8_filename);
+
+ header('Content-Disposition: filename="'.$filename.'";'.$utf8_filename);
+ header('Content-Type: '.$this->mime_type);
header('Content-Transfer-Encoding: binary');
header('Last-Modified: '.gmdate('D, d M Y H:i:s', time()).' GMT');
switch ($this->image_type)
{
- case 1 : imagegif($resource);
+ case 1 : imagegif($resource);
break;
- case 2 : imagejpeg($resource, '', $this->quality);
+ case 2 : imagejpeg($resource, NULL, $this->quality);
break;
- case 3 : imagepng($resource);
+ case 3 : imagepng($resource);
break;
- default : echo 'Unable to display the image';
+ case 18 : imagewebp($resource);
+ break;
+ default: echo 'Unable to display the image';
break;
}
}
@@ -1275,38 +1618,47 @@ class CI_Image_lib {
* This function lets us re-proportion the width/height
* if users choose to maintain the aspect ratio when resizing.
*
- * @access public
* @return void
*/
- function image_reproportion()
+ public function image_reproportion()
{
- if ( ! is_numeric($this->width) OR ! is_numeric($this->height) OR $this->width == 0 OR $this->height == 0)
- return;
-
- if ( ! is_numeric($this->orig_width) OR ! is_numeric($this->orig_height) OR $this->orig_width == 0 OR $this->orig_height == 0)
- return;
-
- $new_width = ceil($this->orig_width*$this->height/$this->orig_height);
- $new_height = ceil($this->width*$this->orig_height/$this->orig_width);
-
- $ratio = (($this->orig_height/$this->orig_width) - ($this->height/$this->width));
-
- if ($this->master_dim != 'width' AND $this->master_dim != 'height')
+ if (($this->width === 0 && $this->height === 0) OR $this->orig_width === 0 OR $this->orig_height === 0
+ OR ( ! ctype_digit((string) $this->width) && ! ctype_digit((string) $this->height))
+ OR ! ctype_digit((string) $this->orig_width) OR ! ctype_digit((string) $this->orig_height))
{
- $this->master_dim = ($ratio < 0) ? 'width' : 'height';
+ return;
}
- if (($this->width != $new_width) AND ($this->height != $new_height))
+ // Sanitize
+ $this->width = (int) $this->width;
+ $this->height = (int) $this->height;
+
+ if ($this->master_dim !== 'width' && $this->master_dim !== 'height')
{
- if ($this->master_dim == 'height')
+ if ($this->width > 0 && $this->height > 0)
{
- $this->width = $new_width;
+ $this->master_dim = ((($this->orig_height/$this->orig_width) - ($this->height/$this->width)) < 0)
+ ? 'width' : 'height';
}
else
{
- $this->height = $new_height;
+ $this->master_dim = ($this->height === 0) ? 'width' : 'height';
}
}
+ elseif (($this->master_dim === 'width' && $this->width === 0)
+ OR ($this->master_dim === 'height' && $this->height === 0))
+ {
+ return;
+ }
+
+ if ($this->master_dim === 'width')
+ {
+ $this->height = (int) ceil($this->width*$this->orig_height/$this->orig_width);
+ }
+ else
+ {
+ $this->width = (int) ceil($this->orig_width*$this->height/$this->orig_height);
+ }
}
// --------------------------------------------------------------------
@@ -1316,17 +1668,19 @@ class CI_Image_lib {
*
* A helper function that gets info about the file
*
- * @access public
* @param string
+ * @param bool
* @return mixed
*/
- function get_image_properties($path = '', $return = FALSE)
+ public function get_image_properties($path = '', $return = FALSE)
{
// For now we require GD but we should
// find a way to determine this using IM or NetPBM
- if ($path == '')
+ if ($path === '')
+ {
$path = $this->full_src_path;
+ }
if ( ! file_exists($path))
{
@@ -1334,28 +1688,32 @@ class CI_Image_lib {
return FALSE;
}
- $vals = @getimagesize($path);
+ $vals = getimagesize($path);
+ if ($vals === FALSE)
+ {
+ $this->set_error('imglib_invalid_image');
+ return FALSE;
+ }
$types = array(1 => 'gif', 2 => 'jpeg', 3 => 'png');
+ $mime = isset($types[$vals[2]]) ? 'image/'.$types[$vals[2]] : 'image/jpg';
- $mime = (isset($types[$vals['2']])) ? 'image/'.$types[$vals['2']] : 'image/jpg';
-
- if ($return == TRUE)
+ if ($return === TRUE)
{
- $v['width'] = $vals['0'];
- $v['height'] = $vals['1'];
- $v['image_type'] = $vals['2'];
- $v['size_str'] = $vals['3'];
- $v['mime_type'] = $mime;
-
- return $v;
+ return array(
+ 'width' => $vals[0],
+ 'height' => $vals[1],
+ 'image_type' => $vals[2],
+ 'size_str' => $vals[3],
+ 'mime_type' => $mime
+ );
}
- $this->orig_width = $vals['0'];
- $this->orig_height = $vals['1'];
- $this->image_type = $vals['2'];
- $this->size_str = $vals['3'];
- $this->mime_type = $mime;
+ $this->orig_width = $vals[0];
+ $this->orig_height = $vals[1];
+ $this->image_type = $vals[2];
+ $this->size_str = $vals[3];
+ $this->mime_type = $mime;
return TRUE;
}
@@ -1366,21 +1724,20 @@ class CI_Image_lib {
* Size calculator
*
* This function takes a known width x height and
- * recalculates it to a new size. Only one
+ * recalculates it to a new size. Only one
* new variable needs to be known
*
* $props = array(
- * 'width' => $width,
- * 'height' => $height,
- * 'new_width' => 40,
- * 'new_height' => ''
- * );
+ * 'width' => $width,
+ * 'height' => $height,
+ * 'new_width' => 40,
+ * 'new_height' => ''
+ * );
*
- * @access public
* @param array
* @return array
*/
- function size_calculator($vals)
+ public function size_calculator($vals)
{
if ( ! is_array($vals))
{
@@ -1391,20 +1748,22 @@ class CI_Image_lib {
foreach ($allowed as $item)
{
- if ( ! isset($vals[$item]) OR $vals[$item] == '')
+ if (empty($vals[$item]))
+ {
$vals[$item] = 0;
+ }
}
- if ($vals['width'] == 0 OR $vals['height'] == 0)
+ if ($vals['width'] === 0 OR $vals['height'] === 0)
{
return $vals;
}
- if ($vals['new_width'] == 0)
+ if ($vals['new_width'] === 0)
{
$vals['new_width'] = ceil($vals['width']*$vals['new_height']/$vals['height']);
}
- elseif ($vals['new_height'] == 0)
+ elseif ($vals['new_height'] === 0)
{
$vals['new_height'] = ceil($vals['new_width']*$vals['height']/$vals['width']);
}
@@ -1419,16 +1778,15 @@ class CI_Image_lib {
*
* This is a helper function that extracts the extension
* from the source_image. This function lets us deal with
- * source_images with multiple periods, like: my.cool.jpg
+ * source_images with multiple periods, like: my.cool.jpg
* It returns an associative array with two elements:
* $array['ext'] = '.jpg';
* $array['name'] = 'my.cool';
*
- * @access public
* @param array
* @return array
*/
- function explode_name($source_image)
+ public function explode_name($source_image)
{
$ext = strrchr($source_image, '.');
$name = ($ext === FALSE) ? $source_image : substr($source_image, 0, -strlen($ext));
@@ -1441,17 +1799,16 @@ class CI_Image_lib {
/**
* Is GD Installed?
*
- * @access public
* @return bool
*/
- function gd_loaded()
+ public function gd_loaded()
{
if ( ! extension_loaded('gd'))
{
- if ( ! dl('gd.so'))
- {
- return FALSE;
- }
+ /* As it is stated in the PHP manual, dl() is not always available
+ * and even if so - it could generate an E_WARNING message on failure
+ */
+ return (function_exists('dl') && @dl('gd.so'));
}
return TRUE;
@@ -1462,17 +1819,14 @@ class CI_Image_lib {
/**
* Get GD version
*
- * @access public
* @return mixed
*/
- function gd_version()
+ public function gd_version()
{
if (function_exists('gd_info'))
{
$gd_version = @gd_info();
- $gd_version = preg_replace("/\D/", "", $gd_version['GD Version']);
-
- return $gd_version;
+ return preg_replace('/\D/', '', $gd_version['GD Version']);
}
return FALSE;
@@ -1483,11 +1837,10 @@ class CI_Image_lib {
/**
* Set error message
*
- * @access public
* @param string
* @return void
*/
- function set_error($msg)
+ public function set_error($msg)
{
$CI =& get_instance();
$CI->lang->load('imglib');
@@ -1496,15 +1849,14 @@ class CI_Image_lib {
{
foreach ($msg as $val)
{
-
- $msg = ($CI->lang->line($val) == FALSE) ? $val : $CI->lang->line($val);
+ $msg = ($CI->lang->line($val) === FALSE) ? $val : $CI->lang->line($val);
$this->error_msg[] = $msg;
log_message('error', $msg);
}
}
else
{
- $msg = ($CI->lang->line($msg) == FALSE) ? $msg : $CI->lang->line($msg);
+ $msg = ($CI->lang->line($msg) === FALSE) ? $msg : $CI->lang->line($msg);
$this->error_msg[] = $msg;
log_message('error', $msg);
}
@@ -1515,23 +1867,13 @@ class CI_Image_lib {
/**
* Show error messages
*
- * @access public
+ * @param string
* @param string
* @return string
*/
- function display_errors($open = '<p>', $close = '</p>')
+ public function display_errors($open = '<p>', $close = '</p>')
{
- $str = '';
- foreach ($this->error_msg as $val)
- {
- $str .= $open.$val.$close;
- }
-
- return $str;
+ return (count($this->error_msg) > 0) ? $open.implode($close.$open, $this->error_msg).$close : '';
}
}
-// END Image_lib Class
-
-/* End of file Image_lib.php */
-/* Location: ./system/libraries/Image_lib.php */ \ No newline at end of file
diff --git a/system/libraries/Javascript.php b/system/libraries/Javascript.php
deleted file mode 100644
index a26bb8400..000000000
--- a/system/libraries/Javascript.php
+++ /dev/null
@@ -1,871 +0,0 @@
-<?php if ( ! defined('BASEPATH')) exit('No direct script access allowed');
-/**
- * CodeIgniter
- *
- * An open source application development framework for PHP 5.1.6 or newer
- *
- * @package CodeIgniter
- * @author ExpressionEngine Dev Team
- * @copyright Copyright (c) 2008 - 2014, EllisLab, Inc.
- * @license http://codeigniter.com/user_guide/license.html
- * @link http://codeigniter.com
- * @since Version 1.0
- * @filesource
- */
-
-// ------------------------------------------------------------------------
-
-/**
- * Javascript Class
- *
- * @package CodeIgniter
- * @subpackage Libraries
- * @category Javascript
- * @author ExpressionEngine Dev Team
- * @link http://codeigniter.com/user_guide/libraries/javascript.html
- */
-class CI_Javascript {
-
- var $_javascript_location = 'js';
-
- public function __construct($params = array())
- {
- $defaults = array('js_library_driver' => 'jquery', 'autoload' => TRUE);
-
- foreach ($defaults as $key => $val)
- {
- if (isset($params[$key]) && $params[$key] !== "")
- {
- $defaults[$key] = $params[$key];
- }
- }
-
- extract($defaults);
-
- $this->CI =& get_instance();
-
- // load the requested js library
- $this->CI->load->library('javascript/'.$js_library_driver, array('autoload' => $autoload));
- // make js to refer to current library
- $this->js =& $this->CI->$js_library_driver;
-
- log_message('debug', "Javascript Class Initialized and loaded. Driver used: $js_library_driver");
- }
-
- // --------------------------------------------------------------------
- // Event Code
- // --------------------------------------------------------------------
-
- /**
- * Blur
- *
- * Outputs a javascript library blur event
- *
- * @access public
- * @param string The element to attach the event to
- * @param string The code to execute
- * @return string
- */
- function blur($element = 'this', $js = '')
- {
- return $this->js->_blur($element, $js);
- }
-
- // --------------------------------------------------------------------
-
- /**
- * Change
- *
- * Outputs a javascript library change event
- *
- * @access public
- * @param string The element to attach the event to
- * @param string The code to execute
- * @return string
- */
- function change($element = 'this', $js = '')
- {
- return $this->js->_change($element, $js);
- }
-
- // --------------------------------------------------------------------
-
- /**
- * Click
- *
- * Outputs a javascript library click event
- *
- * @access public
- * @param string The element to attach the event to
- * @param string The code to execute
- * @param boolean whether or not to return false
- * @return string
- */
- function click($element = 'this', $js = '', $ret_false = TRUE)
- {
- return $this->js->_click($element, $js, $ret_false);
- }
-
- // --------------------------------------------------------------------
-
- /**
- * Double Click
- *
- * Outputs a javascript library dblclick event
- *
- * @access public
- * @param string The element to attach the event to
- * @param string The code to execute
- * @return string
- */
- function dblclick($element = 'this', $js = '')
- {
- return $this->js->_dblclick($element, $js);
- }
-
- // --------------------------------------------------------------------
-
- /**
- * Error
- *
- * Outputs a javascript library error event
- *
- * @access public
- * @param string The element to attach the event to
- * @param string The code to execute
- * @return string
- */
- function error($element = 'this', $js = '')
- {
- return $this->js->_error($element, $js);
- }
-
- // --------------------------------------------------------------------
-
- /**
- * Focus
- *
- * Outputs a javascript library focus event
- *
- * @access public
- * @param string The element to attach the event to
- * @param string The code to execute
- * @return string
- */
- function focus($element = 'this', $js = '')
- {
- return $this->js->__add_event($focus, $js);
- }
-
- // --------------------------------------------------------------------
-
- /**
- * Hover
- *
- * Outputs a javascript library hover event
- *
- * @access public
- * @param string - element
- * @param string - Javascript code for mouse over
- * @param string - Javascript code for mouse out
- * @return string
- */
- function hover($element = 'this', $over, $out)
- {
- return $this->js->__hover($element, $over, $out);
- }
-
- // --------------------------------------------------------------------
-
- /**
- * Keydown
- *
- * Outputs a javascript library keydown event
- *
- * @access public
- * @param string The element to attach the event to
- * @param string The code to execute
- * @return string
- */
- function keydown($element = 'this', $js = '')
- {
- return $this->js->_keydown($element, $js);
- }
-
- // --------------------------------------------------------------------
-
- /**
- * Keyup
- *
- * Outputs a javascript library keydown event
- *
- * @access public
- * @param string The element to attach the event to
- * @param string The code to execute
- * @return string
- */
- function keyup($element = 'this', $js = '')
- {
- return $this->js->_keyup($element, $js);
- }
-
- // --------------------------------------------------------------------
-
- /**
- * Load
- *
- * Outputs a javascript library load event
- *
- * @access public
- * @param string The element to attach the event to
- * @param string The code to execute
- * @return string
- */
- function load($element = 'this', $js = '')
- {
- return $this->js->_load($element, $js);
- }
-
- // --------------------------------------------------------------------
-
- /**
- * Mousedown
- *
- * Outputs a javascript library mousedown event
- *
- * @access public
- * @param string The element to attach the event to
- * @param string The code to execute
- * @return string
- */
- function mousedown($element = 'this', $js = '')
- {
- return $this->js->_mousedown($element, $js);
- }
-
- // --------------------------------------------------------------------
-
- /**
- * Mouse Out
- *
- * Outputs a javascript library mouseout event
- *
- * @access public
- * @param string The element to attach the event to
- * @param string The code to execute
- * @return string
- */
- function mouseout($element = 'this', $js = '')
- {
- return $this->js->_mouseout($element, $js);
- }
-
- // --------------------------------------------------------------------
-
- /**
- * Mouse Over
- *
- * Outputs a javascript library mouseover event
- *
- * @access public
- * @param string The element to attach the event to
- * @param string The code to execute
- * @return string
- */
- function mouseover($element = 'this', $js = '')
- {
- return $this->js->_mouseover($element, $js);
- }
-
- // --------------------------------------------------------------------
-
- /**
- * Mouseup
- *
- * Outputs a javascript library mouseup event
- *
- * @access public
- * @param string The element to attach the event to
- * @param string The code to execute
- * @return string
- */
- function mouseup($element = 'this', $js = '')
- {
- return $this->js->_mouseup($element, $js);
- }
-
- // --------------------------------------------------------------------
-
- /**
- * Output
- *
- * Outputs the called javascript to the screen
- *
- * @access public
- * @param string The code to output
- * @return string
- */
- function output($js)
- {
- return $this->js->_output($js);
- }
-
- // --------------------------------------------------------------------
-
- /**
- * Ready
- *
- * Outputs a javascript library mouseup event
- *
- * @access public
- * @param string The element to attach the event to
- * @param string The code to execute
- * @return string
- */
- function ready($js)
- {
- return $this->js->_document_ready($js);
- }
-
- // --------------------------------------------------------------------
-
- /**
- * Resize
- *
- * Outputs a javascript library resize event
- *
- * @access public
- * @param string The element to attach the event to
- * @param string The code to execute
- * @return string
- */
- function resize($element = 'this', $js = '')
- {
- return $this->js->_resize($element, $js);
- }
-
- // --------------------------------------------------------------------
-
- /**
- * Scroll
- *
- * Outputs a javascript library scroll event
- *
- * @access public
- * @param string The element to attach the event to
- * @param string The code to execute
- * @return string
- */
- function scroll($element = 'this', $js = '')
- {
- return $this->js->_scroll($element, $js);
- }
-
- // --------------------------------------------------------------------
-
- /**
- * Unload
- *
- * Outputs a javascript library unload event
- *
- * @access public
- * @param string The element to attach the event to
- * @param string The code to execute
- * @return string
- */
- function unload($element = 'this', $js = '')
- {
- return $this->js->_unload($element, $js);
- }
-
- // --------------------------------------------------------------------
- // Effects
- // --------------------------------------------------------------------
-
-
- /**
- * Add Class
- *
- * Outputs a javascript library addClass event
- *
- * @access public
- * @param string - element
- * @param string - Class to add
- * @return string
- */
- function addClass($element = 'this', $class = '')
- {
- return $this->js->_addClass($element, $class);
- }
-
- // --------------------------------------------------------------------
-
- /**
- * Animate
- *
- * Outputs a javascript library animate event
- *
- * @access public
- * @param string - element
- * @param string - One of 'slow', 'normal', 'fast', or time in milliseconds
- * @param string - Javascript callback function
- * @return string
- */
- function animate($element = 'this', $params = array(), $speed = '', $extra = '')
- {
- return $this->js->_animate($element, $params, $speed, $extra);
- }
-
- // --------------------------------------------------------------------
-
- /**
- * Fade In
- *
- * Outputs a javascript library hide event
- *
- * @access public
- * @param string - element
- * @param string - One of 'slow', 'normal', 'fast', or time in milliseconds
- * @param string - Javascript callback function
- * @return string
- */
- function fadeIn($element = 'this', $speed = '', $callback = '')
- {
- return $this->js->_fadeIn($element, $speed, $callback);
- }
-
- // --------------------------------------------------------------------
-
- /**
- * Fade Out
- *
- * Outputs a javascript library hide event
- *
- * @access public
- * @param string - element
- * @param string - One of 'slow', 'normal', 'fast', or time in milliseconds
- * @param string - Javascript callback function
- * @return string
- */
- function fadeOut($element = 'this', $speed = '', $callback = '')
- {
- return $this->js->_fadeOut($element, $speed, $callback);
- }
- // --------------------------------------------------------------------
-
- /**
- * Slide Up
- *
- * Outputs a javascript library slideUp event
- *
- * @access public
- * @param string - element
- * @param string - One of 'slow', 'normal', 'fast', or time in milliseconds
- * @param string - Javascript callback function
- * @return string
- */
- function slideUp($element = 'this', $speed = '', $callback = '')
- {
- return $this->js->_slideUp($element, $speed, $callback);
-
- }
-
- // --------------------------------------------------------------------
-
- /**
- * Remove Class
- *
- * Outputs a javascript library removeClass event
- *
- * @access public
- * @param string - element
- * @param string - Class to add
- * @return string
- */
- function removeClass($element = 'this', $class = '')
- {
- return $this->js->_removeClass($element, $class);
- }
-
- // --------------------------------------------------------------------
-
- /**
- * Slide Down
- *
- * Outputs a javascript library slideDown event
- *
- * @access public
- * @param string - element
- * @param string - One of 'slow', 'normal', 'fast', or time in milliseconds
- * @param string - Javascript callback function
- * @return string
- */
- function slideDown($element = 'this', $speed = '', $callback = '')
- {
- return $this->js->_slideDown($element, $speed, $callback);
- }
-
- // --------------------------------------------------------------------
-
- /**
- * Slide Toggle
- *
- * Outputs a javascript library slideToggle event
- *
- * @access public
- * @param string - element
- * @param string - One of 'slow', 'normal', 'fast', or time in milliseconds
- * @param string - Javascript callback function
- * @return string
- */
- function slideToggle($element = 'this', $speed = '', $callback = '')
- {
- return $this->js->_slideToggle($element, $speed, $callback);
-
- }
-
- // --------------------------------------------------------------------
-
- /**
- * Hide
- *
- * Outputs a javascript library hide action
- *
- * @access public
- * @param string - element
- * @param string - One of 'slow', 'normal', 'fast', or time in milliseconds
- * @param string - Javascript callback function
- * @return string
- */
- function hide($element = 'this', $speed = '', $callback = '')
- {
- return $this->js->_hide($element, $speed, $callback);
- }
-
- // --------------------------------------------------------------------
-
- /**
- * Toggle
- *
- * Outputs a javascript library toggle event
- *
- * @access public
- * @param string - element
- * @return string
- */
- function toggle($element = 'this')
- {
- return $this->js->_toggle($element);
-
- }
-
- // --------------------------------------------------------------------
-
- /**
- * Toggle Class
- *
- * Outputs a javascript library toggle class event
- *
- * @access public
- * @param string - element
- * @return string
- */
- function toggleClass($element = 'this', $class='')
- {
- return $this->js->_toggleClass($element, $class);
- }
-
- // --------------------------------------------------------------------
-
- /**
- * Show
- *
- * Outputs a javascript library show event
- *
- * @access public
- * @param string - element
- * @param string - One of 'slow', 'normal', 'fast', or time in milliseconds
- * @param string - Javascript callback function
- * @return string
- */
- function show($element = 'this', $speed = '', $callback = '')
- {
- return $this->js->_show($element, $speed, $callback);
- }
-
-
- // --------------------------------------------------------------------
-
- /**
- * Compile
- *
- * gather together all script needing to be output
- *
- * @access public
- * @param string The element to attach the event to
- * @return string
- */
- function compile($view_var = 'script_foot', $script_tags = TRUE)
- {
- $this->js->_compile($view_var, $script_tags);
- }
-
- /**
- * Clear Compile
- *
- * Clears any previous javascript collected for output
- *
- * @access public
- * @return void
- */
- function clear_compile()
- {
- $this->js->_clear_compile();
- }
-
- // --------------------------------------------------------------------
-
- /**
- * External
- *
- * Outputs a <script> tag with the source as an external js file
- *
- * @access public
- * @param string The element to attach the event to
- * @return string
- */
- function external($external_file = '', $relative = FALSE)
- {
- if ($external_file !== '')
- {
- $this->_javascript_location = $external_file;
- }
- else
- {
- if ($this->CI->config->item('javascript_location') != '')
- {
- $this->_javascript_location = $this->CI->config->item('javascript_location');
- }
- }
-
- if ($relative === TRUE OR strncmp($external_file, 'http://', 7) == 0 OR strncmp($external_file, 'https://', 8) == 0)
- {
- $str = $this->_open_script($external_file);
- }
- elseif (strpos($this->_javascript_location, 'http://') !== FALSE)
- {
- $str = $this->_open_script($this->_javascript_location.$external_file);
- }
- else
- {
- $str = $this->_open_script($this->CI->config->slash_item('base_url').$this->_javascript_location.$external_file);
- }
-
- $str .= $this->_close_script();
- return $str;
- }
-
- // --------------------------------------------------------------------
-
- /**
- * Inline
- *
- * Outputs a <script> tag
- *
- * @access public
- * @param string The element to attach the event to
- * @param boolean If a CDATA section should be added
- * @return string
- */
- function inline($script, $cdata = TRUE)
- {
- $str = $this->_open_script();
- $str .= ($cdata) ? "\n// <![CDATA[\n{$script}\n// ]]>\n" : "\n{$script}\n";
- $str .= $this->_close_script();
-
- return $str;
- }
-
- // --------------------------------------------------------------------
-
- /**
- * Open Script
- *
- * Outputs an opening <script>
- *
- * @access private
- * @param string
- * @return string
- */
- function _open_script($src = '')
- {
- $str = '<script type="text/javascript" charset="'.strtolower($this->CI->config->item('charset')).'"';
- $str .= ($src == '') ? '>' : ' src="'.$src.'">';
- return $str;
- }
-
- // --------------------------------------------------------------------
-
- /**
- * Close Script
- *
- * Outputs an closing </script>
- *
- * @access private
- * @param string
- * @return string
- */
- function _close_script($extra = "\n")
- {
- return "</script>$extra";
- }
-
-
- // --------------------------------------------------------------------
- // --------------------------------------------------------------------
- // AJAX-Y STUFF - still a testbed
- // --------------------------------------------------------------------
- // --------------------------------------------------------------------
-
- /**
- * Update
- *
- * Outputs a javascript library slideDown event
- *
- * @access public
- * @param string - element
- * @param string - One of 'slow', 'normal', 'fast', or time in milliseconds
- * @param string - Javascript callback function
- * @return string
- */
- function update($element = 'this', $speed = '', $callback = '')
- {
- return $this->js->_updater($element, $speed, $callback);
- }
-
- // --------------------------------------------------------------------
-
- /**
- * Generate JSON
- *
- * Can be passed a database result or associative array and returns a JSON formatted string
- *
- * @param mixed result set or array
- * @param bool match array types (defaults to objects)
- * @return string a json formatted string
- */
- function generate_json($result = NULL, $match_array_type = FALSE)
- {
- // JSON data can optionally be passed to this function
- // either as a database result object or an array, or a user supplied array
- if ( ! is_null($result))
- {
- if (is_object($result))
- {
- $json_result = $result->result_array();
- }
- elseif (is_array($result))
- {
- $json_result = $result;
- }
- else
- {
- return $this->_prep_args($result);
- }
- }
- else
- {
- return 'null';
- }
-
- $json = array();
- $_is_assoc = TRUE;
-
- if ( ! is_array($json_result) AND empty($json_result))
- {
- show_error("Generate JSON Failed - Illegal key, value pair.");
- }
- elseif ($match_array_type)
- {
- $_is_assoc = $this->_is_associative_array($json_result);
- }
-
- foreach ($json_result as $k => $v)
- {
- if ($_is_assoc)
- {
- $json[] = $this->_prep_args($k, TRUE).':'.$this->generate_json($v, $match_array_type);
- }
- else
- {
- $json[] = $this->generate_json($v, $match_array_type);
- }
- }
-
- $json = implode(',', $json);
-
- return $_is_assoc ? "{".$json."}" : "[".$json."]";
-
- }
-
- // --------------------------------------------------------------------
-
- /**
- * Is associative array
- *
- * Checks for an associative array
- *
- * @access public
- * @param type
- * @return type
- */
- function _is_associative_array($arr)
- {
- foreach (array_keys($arr) as $key => $val)
- {
- if ($key !== $val)
- {
- return TRUE;
- }
- }
-
- return FALSE;
- }
-
- // --------------------------------------------------------------------
-
- /**
- * Prep Args
- *
- * Ensures a standard json value and escapes values
- *
- * @access public
- * @param type
- * @return type
- */
- function _prep_args($result, $is_key = FALSE)
- {
- if (is_null($result))
- {
- return 'null';
- }
- elseif (is_bool($result))
- {
- return ($result === TRUE) ? 'true' : 'false';
- }
- elseif (is_string($result) OR $is_key)
- {
- return '"'.str_replace(array('\\', "\t", "\n", "\r", '"', '/'), array('\\\\', '\\t', '\\n', "\\r", '\"', '\/'), $result).'"';
- }
- elseif (is_scalar($result))
- {
- return $result;
- }
- }
-
- // --------------------------------------------------------------------
-}
-// END Javascript Class
-
-/* End of file Javascript.php */
-/* Location: ./system/libraries/Javascript.php */ \ No newline at end of file
diff --git a/system/libraries/Log.php b/system/libraries/Log.php
deleted file mode 100644
index 6d3f9094d..000000000
--- a/system/libraries/Log.php
+++ /dev/null
@@ -1,114 +0,0 @@
-<?php if ( ! defined('BASEPATH')) exit('No direct script access allowed');
-/**
- * CodeIgniter
- *
- * An open source application development framework for PHP 5.1.6 or newer
- *
- * @package CodeIgniter
- * @author ExpressionEngine Dev Team
- * @copyright Copyright (c) 2008 - 2014, EllisLab, Inc.
- * @license http://codeigniter.com/user_guide/license.html
- * @link http://codeigniter.com
- * @since Version 1.0
- * @filesource
- */
-
-// ------------------------------------------------------------------------
-
-/**
- * Logging Class
- *
- * @package CodeIgniter
- * @subpackage Libraries
- * @category Logging
- * @author ExpressionEngine Dev Team
- * @link http://codeigniter.com/user_guide/general/errors.html
- */
-class CI_Log {
-
- protected $_log_path;
- protected $_threshold = 1;
- protected $_date_fmt = 'Y-m-d H:i:s';
- protected $_enabled = TRUE;
- protected $_levels = array('ERROR' => '1', 'DEBUG' => '2', 'INFO' => '3', 'ALL' => '4');
-
- /**
- * Constructor
- */
- public function __construct()
- {
- $config =& get_config();
-
- $this->_log_path = ($config['log_path'] != '') ? $config['log_path'] : APPPATH.'logs/';
-
- if ( ! is_dir($this->_log_path) OR ! is_really_writable($this->_log_path))
- {
- $this->_enabled = FALSE;
- }
-
- if (is_numeric($config['log_threshold']))
- {
- $this->_threshold = $config['log_threshold'];
- }
-
- if ($config['log_date_format'] != '')
- {
- $this->_date_fmt = $config['log_date_format'];
- }
- }
-
- // --------------------------------------------------------------------
-
- /**
- * Write Log File
- *
- * Generally this function will be called using the global log_message() function
- *
- * @param string the error level
- * @param string the error message
- * @param bool whether the error is a native PHP error
- * @return bool
- */
- public function write_log($level = 'error', $msg, $php_error = FALSE)
- {
- if ($this->_enabled === FALSE)
- {
- return FALSE;
- }
-
- $level = strtoupper($level);
-
- if ( ! isset($this->_levels[$level]) OR ($this->_levels[$level] > $this->_threshold))
- {
- return FALSE;
- }
-
- $filepath = $this->_log_path.'log-'.date('Y-m-d').'.php';
- $message = '';
-
- if ( ! file_exists($filepath))
- {
- $message .= "<"."?php if ( ! defined('BASEPATH')) exit('No direct script access allowed'); ?".">\n\n";
- }
-
- if ( ! $fp = @fopen($filepath, FOPEN_WRITE_CREATE))
- {
- return FALSE;
- }
-
- $message .= $level.' '.(($level == 'INFO') ? ' -' : '-').' '.date($this->_date_fmt). ' --> '.$msg."\n";
-
- flock($fp, LOCK_EX);
- fwrite($fp, $message);
- flock($fp, LOCK_UN);
- fclose($fp);
-
- @chmod($filepath, FILE_WRITE_MODE);
- return TRUE;
- }
-
-}
-// END Log Class
-
-/* End of file Log.php */
-/* Location: ./system/libraries/Log.php */ \ No newline at end of file
diff --git a/system/libraries/Migration.php b/system/libraries/Migration.php
index 241ce1e59..9ee92b6e8 100644
--- a/system/libraries/Migration.php
+++ b/system/libraries/Migration.php
@@ -1,19 +1,42 @@
-<?php defined('BASEPATH') OR exit('No direct script access allowed');
+<?php
/**
* CodeIgniter
*
- * An open source application development framework for PHP 5.1.6 or newer
+ * An open source application development framework for PHP
*
- * @package CodeIgniter
- * @author EllisLab Dev Team
- * @copyright Copyright (c) 2006 - 2014, EllisLab, Inc.
- * @license http://codeigniter.com/user_guide/license.html
- * @link http://codeigniter.com
- * @since Version 1.0
+ * This content is released under the MIT License (MIT)
+ *
+ * Copyright (c) 2019 - 2022, CodeIgniter Foundation
+ *
+ * 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.
+ *
+ * @package CodeIgniter
+ * @author EllisLab Dev Team
+ * @copyright Copyright (c) 2008 - 2014, EllisLab, Inc. (https://ellislab.com/)
+ * @copyright Copyright (c) 2014 - 2019, British Columbia Institute of Technology (https://bcit.ca/)
+ * @copyright Copyright (c) 2019 - 2022, CodeIgniter Foundation (https://codeigniter.com/)
+ * @license https://opensource.org/licenses/MIT MIT License
+ * @link https://codeigniter.com
+ * @since Version 3.0.0
* @filesource
*/
-
-// ------------------------------------------------------------------------
+defined('BASEPATH') OR exit('No direct script access allowed');
/**
* Migration Class
@@ -29,26 +52,82 @@
*/
class CI_Migration {
+ /**
+ * Whether the library is enabled
+ *
+ * @var bool
+ */
protected $_migration_enabled = FALSE;
+
+ /**
+ * Migration numbering type
+ *
+ * @var bool
+ */
+ protected $_migration_type = 'sequential';
+
+ /**
+ * Path to migration classes
+ *
+ * @var string
+ */
protected $_migration_path = NULL;
+
+ /**
+ * Current migration version
+ *
+ * @var mixed
+ */
protected $_migration_version = 0;
+ /**
+ * Database table with migration info
+ *
+ * @var string
+ */
+ protected $_migration_table = 'migrations';
+
+ /**
+ * Whether to automatically run migrations
+ *
+ * @var bool
+ */
+ protected $_migration_auto_latest = FALSE;
+
+ /**
+ * Migration basename regex
+ *
+ * @var string
+ */
+ protected $_migration_regex;
+
+ /**
+ * Error message
+ *
+ * @var string
+ */
protected $_error_string = '';
+ /**
+ * Initialize Migration Class
+ *
+ * @param array $config
+ * @return void
+ */
public function __construct($config = array())
{
- # Only run this constructor on main library load
- if (get_parent_class($this) !== FALSE)
+ // Only run this constructor on main library load
+ if ( ! in_array(get_class($this), array('CI_Migration', config_item('subclass_prefix').'Migration'), TRUE))
{
return;
}
foreach ($config as $key => $val)
{
- $this->{'_' . $key} = $val;
+ $this->{'_'.$key} = $val;
}
- log_message('debug', 'Migrations class initialized');
+ log_message('info', 'Migrations Class Initialized');
// Are they trying to use migrations while it is disabled?
if ($this->_migration_enabled !== TRUE)
@@ -57,7 +136,7 @@ class CI_Migration {
}
// If not set, set it
- $this->_migration_path == '' AND $this->_migration_path = APPPATH . 'migrations/';
+ $this->_migration_path !== '' OR $this->_migration_path = APPPATH.'migrations/';
// Add trailing slash if not set
$this->_migration_path = rtrim($this->_migration_path, '/').'/';
@@ -68,16 +147,39 @@ class CI_Migration {
// They'll probably be using dbforge
$this->load->dbforge();
+ // Make sure the migration table name was set.
+ if (empty($this->_migration_table))
+ {
+ show_error('Migrations configuration file (migration.php) must have "migration_table" set.');
+ }
+
+ // Migration basename regex
+ $this->_migration_regex = ($this->_migration_type === 'timestamp')
+ ? '/^\d{14}_(\w+)$/'
+ : '/^\d{3}_(\w+)$/';
+
+ // Make sure a valid migration numbering type was set.
+ if ( ! in_array($this->_migration_type, array('sequential', 'timestamp')))
+ {
+ show_error('An invalid migration numbering type was specified: '.$this->_migration_type);
+ }
+
// If the migrations table is missing, make it
- if ( ! $this->db->table_exists('migrations'))
+ if ( ! $this->db->table_exists($this->_migration_table))
{
$this->dbforge->add_field(array(
- 'version' => array('type' => 'INT', 'constraint' => 3),
+ 'version' => array('type' => 'BIGINT', 'constraint' => 20),
));
- $this->dbforge->create_table('migrations', TRUE);
+ $this->dbforge->create_table($this->_migration_table, TRUE);
+
+ $this->db->insert($this->_migration_table, array('version' => 0));
+ }
- $this->db->insert('migrations', array('version' => 0));
+ // Do we auto migrate to the latest migration?
+ if ($this->_migration_auto_latest === TRUE && ! $this->latest())
+ {
+ show_error($this->error_string());
}
}
@@ -89,154 +191,166 @@ class CI_Migration {
* Calls each migration step required to get to the schema version of
* choice
*
- * @param int Target schema version
- * @return mixed TRUE if already latest, FALSE if failed, int if upgraded
+ * @param string $target_version Target schema version
+ * @return mixed TRUE if no migrations are found, current version string on success, FALSE on failure
*/
public function version($target_version)
{
- $start = $current_version = $this->_get_version();
- $stop = $target_version;
+ // Note: We use strings, so that timestamp versions work on 32-bit systems
+ $current_version = $this->_get_version();
- if ($target_version > $current_version)
+ if ($this->_migration_type === 'sequential')
{
- // Moving Up
- ++$start;
- ++$stop;
- $step = 1;
+ $target_version = sprintf('%03d', $target_version);
}
else
{
- // Moving Down
- $step = -1;
+ $target_version = (string) $target_version;
}
- $method = ($step === 1) ? 'up' : 'down';
- $migrations = array();
+ $migrations = $this->find_migrations();
- // We now prepare to actually DO the migrations
- // But first let's make sure that everything is the way it should be
- for ($i = $start; $i != $stop; $i += $step)
+ if ($target_version > 0 && ! isset($migrations[$target_version]))
{
- $f = glob(sprintf($this->_migration_path . '%03d_*.php', $i));
+ $this->_error_string = sprintf($this->lang->line('migration_not_found'), $target_version);
+ return FALSE;
+ }
- // Only one migration per step is permitted
- if (count($f) > 1)
- {
- $this->_error_string = sprintf($this->lang->line('migration_multiple_version'), $i);
- return FALSE;
- }
+ if ($target_version > $current_version)
+ {
+ $method = 'up';
+ }
+ elseif ($target_version < $current_version)
+ {
+ $method = 'down';
+ // We need this so that migrations are applied in reverse order
+ krsort($migrations);
+ }
+ else
+ {
+ // Well, there's nothing to migrate then ...
+ return TRUE;
+ }
- // Migration step not found
- if (count($f) == 0)
+ // Validate all available migrations within our target range.
+ //
+ // Unfortunately, we'll have to use another loop to run them
+ // in order to avoid leaving the procedure in a broken state.
+ //
+ // See https://github.com/bcit-ci/CodeIgniter/issues/4539
+ $pending = array();
+ foreach ($migrations as $number => $file)
+ {
+ // Ignore versions out of our range.
+ //
+ // Because we've previously sorted the $migrations array depending on the direction,
+ // we can safely break the loop once we reach $target_version ...
+ if ($method === 'up')
{
- // If trying to migrate up to a version greater than the last
- // existing one, migrate to the last one.
- if ($step == 1)
+ if ($number <= $current_version)
+ {
+ continue;
+ }
+ elseif ($number > $target_version)
{
break;
}
-
- // If trying to migrate down but we're missing a step,
- // something must definitely be wrong.
- $this->_error_string = sprintf($this->lang->line('migration_not_found'), $i);
- return FALSE;
}
-
- $file = basename($f[0]);
- $name = basename($f[0], '.php');
-
- // Filename validations
- if (preg_match('/^\d{3}_(\w+)$/', $name, $match))
+ else
{
- $match[1] = strtolower($match[1]);
-
- // Cannot repeat a migration at different steps
- if (in_array($match[1], $migrations))
+ if ($number > $current_version)
{
- $this->_error_string = sprintf($this->lang->line('migration_multiple_version'), $match[1]);
- return FALSE;
+ continue;
}
-
- include $f[0];
- $class = 'Migration_' . ucfirst($match[1]);
-
- if ( ! class_exists($class))
+ elseif ($number <= $target_version)
{
- $this->_error_string = sprintf($this->lang->line('migration_class_doesnt_exist'), $class);
- return FALSE;
+ break;
}
+ }
- if ( ! is_callable(array($class, $method)))
+ // Check for sequence gaps
+ if ($this->_migration_type === 'sequential')
+ {
+ if (isset($previous) && abs($number - $previous) > 1)
{
- $this->_error_string = sprintf($this->lang->line('migration_missing_'.$method.'_method'), $class);
+ $this->_error_string = sprintf($this->lang->line('migration_sequence_gap'), $number);
return FALSE;
}
- $migrations[] = $match[1];
+ $previous = $number;
}
- else
+
+ include_once($file);
+ $class = 'Migration_'.ucfirst(strtolower($this->_get_migration_name(basename($file, '.php'))));
+
+ // Validate the migration file structure
+ if ( ! class_exists($class, FALSE))
{
- $this->_error_string = sprintf($this->lang->line('migration_invalid_filename'), $file);
+ $this->_error_string = sprintf($this->lang->line('migration_class_doesnt_exist'), $class);
+ return FALSE;
+ }
+ elseif ( ! method_exists($class, $method) OR ! (new ReflectionMethod($class, $method))->isPublic())
+ {
+ $this->_error_string = sprintf($this->lang->line('migration_missing_'.$method.'_method'), $class);
return FALSE;
}
- }
-
- log_message('debug', 'Current migration: ' . $current_version);
- $version = $i + ($step == 1 ? -1 : 0);
+ $pending[$number] = array($class, $method);
+ }
- // If there is nothing to do so quit
- if ($migrations === array())
+ // Now just run the necessary migrations
+ foreach ($pending as $number => $migration)
{
- return TRUE;
- }
+ log_message('debug', 'Migrating '.$method.' from version '.$current_version.' to version '.$number);
- log_message('debug', 'Migrating from ' . $method . ' to version ' . $version);
+ $migration[0] = new $migration[0];
+ call_user_func($migration);
+ $current_version = $number;
+ $this->_update_version($current_version);
+ }
- // Loop through the migrations
- foreach ($migrations AS $migration)
+ // This is necessary when moving down, since the the last migration applied
+ // will be the down() method for the next migration up from the target
+ if ($current_version <> $target_version)
{
- // Run the migration class
- $class = 'Migration_' . ucfirst(strtolower($migration));
- call_user_func(array(new $class, $method));
-
- $current_version += $step;
+ $current_version = $target_version;
$this->_update_version($current_version);
}
log_message('debug', 'Finished migrating to '.$current_version);
-
return $current_version;
}
// --------------------------------------------------------------------
/**
- * Set's the schema to the latest migration
+ * Sets the schema to the latest migration
*
- * @return mixed true if already latest, false if failed, int if upgraded
+ * @return mixed Current version string on success, FALSE on failure
*/
public function latest()
{
- if ( ! $migrations = $this->find_migrations())
+ $migrations = $this->find_migrations();
+
+ if (empty($migrations))
{
$this->_error_string = $this->lang->line('migration_none_found');
- return false;
+ return FALSE;
}
$last_migration = basename(end($migrations));
// Calculate the last migration step from existing migration
- // filenames and procceed to the standard version migration
- return $this->version((int) substr($last_migration, 0, 3));
+ // filenames and proceed to the standard version migration
+ return $this->version($this->_get_migration_number($last_migration));
}
// --------------------------------------------------------------------
/**
- * Set's the schema to the migration version set in config
+ * Sets the schema to the migration version set in config
*
- * @return mixed true if already current, false if failed, int if upgraded
+ * @return mixed TRUE if no migrations are found, current version string on success, FALSE on failure
*/
public function current()
{
@@ -258,28 +372,66 @@ class CI_Migration {
// --------------------------------------------------------------------
/**
- * Set's the schema to the latest migration
+ * Retrieves list of available migration scripts
*
- * @return mixed true if already latest, false if failed, int if upgraded
+ * @return array list of migration file paths sorted by version
*/
- protected function find_migrations()
+ public function find_migrations()
{
- // Load all *_*.php files in the migrations path
- $files = glob($this->_migration_path . '*_*.php');
- $file_count = count($files);
+ $migrations = array();
- for ($i = 0; $i < $file_count; $i++)
+ // Load all *_*.php files in the migrations path
+ foreach (glob($this->_migration_path.'*_*.php') as $file)
{
- // Mark wrongly formatted files as false for later filtering
- $name = basename($files[$i], '.php');
- if ( ! preg_match('/^\d{3}_(\w+)$/', $name))
+ $name = basename($file, '.php');
+
+ // Filter out non-migration files
+ if (preg_match($this->_migration_regex, $name))
{
- $files[$i] = FALSE;
+ $number = $this->_get_migration_number($name);
+
+ // There cannot be duplicate migration numbers
+ if (isset($migrations[$number]))
+ {
+ $this->_error_string = sprintf($this->lang->line('migration_multiple_version'), $number);
+ show_error($this->_error_string);
+ }
+
+ $migrations[$number] = $file;
}
}
- sort($files);
- return $files;
+ ksort($migrations);
+ return $migrations;
+ }
+
+ // --------------------------------------------------------------------
+
+ /**
+ * Extracts the migration number from a filename
+ *
+ * @param string $migration
+ * @return string Numeric portion of a migration filename
+ */
+ protected function _get_migration_number($migration)
+ {
+ return sscanf($migration, '%[0-9]+', $number)
+ ? $number : '0';
+ }
+
+ // --------------------------------------------------------------------
+
+ /**
+ * Extracts the migration class name from a filename
+ *
+ * @param string $migration
+ * @return string text portion of a migration filename
+ */
+ protected function _get_migration_name($migration)
+ {
+ $parts = explode('_', $migration);
+ array_shift($parts);
+ return implode('_', $parts);
}
// --------------------------------------------------------------------
@@ -287,12 +439,12 @@ class CI_Migration {
/**
* Retrieves current schema version
*
- * @return int Current Migration
+ * @return string Current migration version
*/
protected function _get_version()
{
- $row = $this->db->get('migrations')->row();
- return $row ? $row->version : 0;
+ $row = $this->db->select('version')->get($this->_migration_table)->row();
+ return $row ? $row->version : '0';
}
// --------------------------------------------------------------------
@@ -300,13 +452,13 @@ class CI_Migration {
/**
* Stores the current schema version
*
- * @param int Migration reached
- * @return bool
+ * @param string $migration Migration reached
+ * @return void
*/
- protected function _update_version($migrations)
+ protected function _update_version($migration)
{
- return $this->db->update('migrations', array(
- 'version' => $migrations
+ $this->db->update($this->_migration_table, array(
+ 'version' => $migration
));
}
@@ -315,7 +467,7 @@ class CI_Migration {
/**
* Enable the use of CI super-global
*
- * @param mixed $var
+ * @param string $var
* @return mixed
*/
public function __get($var)
@@ -324,6 +476,3 @@ class CI_Migration {
}
}
-
-/* End of file Migration.php */
-/* Location: ./system/libraries/Migration.php */ \ No newline at end of file
diff --git a/system/libraries/Pagination.php b/system/libraries/Pagination.php
index 8b3aa8748..7d21b47b3 100644
--- a/system/libraries/Pagination.php
+++ b/system/libraries/Pagination.php
@@ -1,19 +1,42 @@
-<?php if ( ! defined('BASEPATH')) exit('No direct script access allowed');
+<?php
/**
* CodeIgniter
*
- * An open source application development framework for PHP 5.1.6 or newer
+ * An open source application development framework for PHP
*
- * @package CodeIgniter
- * @author ExpressionEngine Dev Team
- * @copyright Copyright (c) 2008 - 2014, EllisLab, Inc.
- * @license http://codeigniter.com/user_guide/license.html
- * @link http://codeigniter.com
- * @since Version 1.0
+ * This content is released under the MIT License (MIT)
+ *
+ * Copyright (c) 2019 - 2022, CodeIgniter Foundation
+ *
+ * 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.
+ *
+ * @package CodeIgniter
+ * @author EllisLab Dev Team
+ * @copyright Copyright (c) 2008 - 2014, EllisLab, Inc. (https://ellislab.com/)
+ * @copyright Copyright (c) 2014 - 2019, British Columbia Institute of Technology (https://bcit.ca/)
+ * @copyright Copyright (c) 2019 - 2022, CodeIgniter Foundation (https://codeigniter.com/)
+ * @license https://opensource.org/licenses/MIT MIT License
+ * @link https://codeigniter.com
+ * @since Version 1.0.0
* @filesource
*/
-
-// ------------------------------------------------------------------------
+defined('BASEPATH') OR exit('No direct script access allowed');
/**
* Pagination Class
@@ -21,64 +44,308 @@
* @package CodeIgniter
* @subpackage Libraries
* @category Pagination
- * @author ExpressionEngine Dev Team
- * @link http://codeigniter.com/user_guide/libraries/pagination.html
+ * @author EllisLab Dev Team
+ * @link https://codeigniter.com/userguide3/libraries/pagination.html
*/
class CI_Pagination {
- var $base_url = ''; // The page we are linking to
- var $prefix = ''; // A custom prefix added to the path.
- var $suffix = ''; // A custom suffix added to the path.
-
- var $total_rows = 0; // Total number of items (database results)
- var $per_page = 10; // Max number of items you want shown per page
- var $num_links = 2; // Number of "digit" links to show before/after the currently viewed page
- var $cur_page = 0; // The current page being viewed
- var $use_page_numbers = FALSE; // Use page number for segment instead of offset
- var $first_link = '&lsaquo; First';
- var $next_link = '&gt;';
- var $prev_link = '&lt;';
- var $last_link = 'Last &rsaquo;';
- var $uri_segment = 3;
- var $full_tag_open = '';
- var $full_tag_close = '';
- var $first_tag_open = '';
- var $first_tag_close = '&nbsp;';
- var $last_tag_open = '&nbsp;';
- var $last_tag_close = '';
- var $first_url = ''; // Alternative URL for the First Page.
- var $cur_tag_open = '&nbsp;<strong>';
- var $cur_tag_close = '</strong>';
- var $next_tag_open = '&nbsp;';
- var $next_tag_close = '&nbsp;';
- var $prev_tag_open = '&nbsp;';
- var $prev_tag_close = '';
- var $num_tag_open = '&nbsp;';
- var $num_tag_close = '';
- var $page_query_string = FALSE;
- var $query_string_segment = 'per_page';
- var $display_pages = TRUE;
- var $anchor_class = '';
+ /**
+ * Base URL
+ *
+ * The page that we're linking to
+ *
+ * @var string
+ */
+ protected $base_url = '';
+
+ /**
+ * Prefix
+ *
+ * @var string
+ */
+ protected $prefix = '';
+
+ /**
+ * Suffix
+ *
+ * @var string
+ */
+ protected $suffix = '';
+
+ /**
+ * Total number of items
+ *
+ * @var int
+ */
+ protected $total_rows = 0;
+
+ /**
+ * Number of links to show
+ *
+ * Relates to "digit" type links shown before/after
+ * the currently viewed page.
+ *
+ * @var int
+ */
+ protected $num_links = 2;
+
+ /**
+ * Items per page
+ *
+ * @var int
+ */
+ public $per_page = 10;
+
+ /**
+ * Current page
+ *
+ * @var int
+ */
+ public $cur_page = 0;
+
+ /**
+ * Use page numbers flag
+ *
+ * Whether to use actual page numbers instead of an offset
+ *
+ * @var bool
+ */
+ protected $use_page_numbers = FALSE;
+
+ /**
+ * First link
+ *
+ * @var string
+ */
+ protected $first_link = '&lsaquo; First';
+
+ /**
+ * Next link
+ *
+ * @var string
+ */
+ protected $next_link = '&gt;';
+
+ /**
+ * Previous link
+ *
+ * @var string
+ */
+ protected $prev_link = '&lt;';
+
+ /**
+ * Last link
+ *
+ * @var string
+ */
+ protected $last_link = 'Last &rsaquo;';
+
+ /**
+ * URI Segment
+ *
+ * @var int
+ */
+ protected $uri_segment = 0;
+
+ /**
+ * Full tag open
+ *
+ * @var string
+ */
+ protected $full_tag_open = '';
+
+ /**
+ * Full tag close
+ *
+ * @var string
+ */
+ protected $full_tag_close = '';
+
+ /**
+ * First tag open
+ *
+ * @var string
+ */
+ protected $first_tag_open = '';
+
+ /**
+ * First tag close
+ *
+ * @var string
+ */
+ protected $first_tag_close = '';
+
+ /**
+ * Last tag open
+ *
+ * @var string
+ */
+ protected $last_tag_open = '';
+
+ /**
+ * Last tag close
+ *
+ * @var string
+ */
+ protected $last_tag_close = '';
+
+ /**
+ * First URL
+ *
+ * An alternative URL for the first page
+ *
+ * @var string
+ */
+ protected $first_url = '';
+
+ /**
+ * Current tag open
+ *
+ * @var string
+ */
+ protected $cur_tag_open = '<strong>';
+
+ /**
+ * Current tag close
+ *
+ * @var string
+ */
+ protected $cur_tag_close = '</strong>';
+
+ /**
+ * Next tag open
+ *
+ * @var string
+ */
+ protected $next_tag_open = '';
+
+ /**
+ * Next tag close
+ *
+ * @var string
+ */
+ protected $next_tag_close = '';
+
+ /**
+ * Previous tag open
+ *
+ * @var string
+ */
+ protected $prev_tag_open = '';
+
+ /**
+ * Previous tag close
+ *
+ * @var string
+ */
+ protected $prev_tag_close = '';
+
+ /**
+ * Number tag open
+ *
+ * @var string
+ */
+ protected $num_tag_open = '';
+
+ /**
+ * Number tag close
+ *
+ * @var string
+ */
+ protected $num_tag_close = '';
+
+ /**
+ * Page query string flag
+ *
+ * @var bool
+ */
+ protected $page_query_string = FALSE;
+
+ /**
+ * Query string segment
+ *
+ * @var string
+ */
+ protected $query_string_segment = 'per_page';
+
+ /**
+ * Display pages flag
+ *
+ * @var bool
+ */
+ protected $display_pages = TRUE;
+
+ /**
+ * Attributes
+ *
+ * @var string
+ */
+ protected $_attributes = '';
+
+ /**
+ * Link types
+ *
+ * "rel" attribute
+ *
+ * @see CI_Pagination::_attr_rel()
+ * @var array
+ */
+ protected $_link_types = array();
+
+ /**
+ * Reuse query string flag
+ *
+ * @var bool
+ */
+ protected $reuse_query_string = FALSE;
+
+ /**
+ * Use global URL suffix flag
+ *
+ * @var bool
+ */
+ protected $use_global_url_suffix = FALSE;
+
+ /**
+ * Data page attribute
+ *
+ * @var string
+ */
+ protected $data_page_attr = 'data-ci-pagination-page';
+
+ /**
+ * CI Singleton
+ *
+ * @var object
+ */
+ protected $CI;
+
+ // --------------------------------------------------------------------
/**
* Constructor
*
- * @access public
- * @param array initialization parameters
+ * @param array $params Initialization parameters
+ * @return void
*/
public function __construct($params = array())
{
- if (count($params) > 0)
+ $this->CI =& get_instance();
+ $this->CI->load->language('pagination');
+ foreach (array('first_link', 'next_link', 'prev_link', 'last_link') as $key)
{
- $this->initialize($params);
+ if (($val = $this->CI->lang->line('pagination_'.$key)) !== FALSE)
+ {
+ $this->$key = $val;
+ }
}
- if ($this->anchor_class != '')
- {
- $this->anchor_class = 'class="'.$this->anchor_class.'" ';
- }
+ // _parse_attributes(), called by initialize(), needs to run at least once
+ // in order to enable "rel" attributes, and this triggers it.
+ isset($params['attributes']) OR $params['attributes'] = array();
- log_message('debug', "Pagination Class Initialized");
+ $this->initialize($params);
+ log_message('info', 'Pagination Class Initialized');
}
// --------------------------------------------------------------------
@@ -86,22 +353,36 @@ class CI_Pagination {
/**
* Initialize Preferences
*
- * @access public
- * @param array initialization parameters
- * @return void
+ * @param array $params Initialization parameters
+ * @return CI_Pagination
*/
- function initialize($params = array())
+ public function initialize(array $params = array())
{
- if (count($params) > 0)
+ if (isset($params['attributes']) && is_array($params['attributes']))
+ {
+ $this->_parse_attributes($params['attributes']);
+ unset($params['attributes']);
+ }
+
+ foreach ($params as $key => $val)
{
- foreach ($params as $key => $val)
+ if (property_exists($this, $key))
{
- if (isset($this->$key))
- {
- $this->$key = $val;
- }
+ $this->$key = $val;
}
}
+
+ if ($this->CI->config->item('enable_query_strings') === TRUE)
+ {
+ $this->page_query_string = TRUE;
+ }
+
+ if ($this->use_global_url_suffix === TRUE)
+ {
+ $this->suffix = $this->CI->config->item('url_suffix');
+ }
+
+ return $this;
}
// --------------------------------------------------------------------
@@ -109,80 +390,143 @@ class CI_Pagination {
/**
* Generate the pagination links
*
- * @access public
* @return string
*/
- function create_links()
+ public function create_links()
{
// If our item count or per-page total is zero there is no need to continue.
+ // Note: DO NOT change the operator to === here!
if ($this->total_rows == 0 OR $this->per_page == 0)
{
return '';
}
// Calculate the total number of pages
- $num_pages = ceil($this->total_rows / $this->per_page);
+ $num_pages = (int) ceil($this->total_rows / $this->per_page);
// Is there only one page? Hm... nothing more to do here then.
- if ($num_pages == 1)
+ if ($num_pages === 1)
{
return '';
}
- // Set the base page index for starting page number
- if ($this->use_page_numbers)
+ // Check the user defined number of links.
+ $this->num_links = (int) $this->num_links;
+
+ if ($this->num_links < 0)
+ {
+ show_error('Your number of links must be a non-negative number.');
+ }
+
+ // Keep any existing query string items.
+ // Note: Has nothing to do with any other query string option.
+ if ($this->reuse_query_string === TRUE)
{
- $base_page = 1;
+ $get = $this->CI->input->get();
+
+ // Unset the control, method, old-school routing options
+ unset($get['c'], $get['m'], $get[$this->query_string_segment]);
}
else
{
- $base_page = 0;
+ $get = array();
}
- // Determine the current page number.
- $CI =& get_instance();
+ // Put together our base and first URLs.
+ // Note: DO NOT append to the properties as that would break successive calls
+ $base_url = trim($this->base_url);
+ $first_url = $this->first_url;
+
+ $query_string = '';
+ $query_string_sep = (strpos($base_url, '?') === FALSE) ? '?' : '&amp;';
- if ($CI->config->item('enable_query_strings') === TRUE OR $this->page_query_string === TRUE)
+ // Are we using query strings?
+ if ($this->page_query_string === TRUE)
{
- if ($CI->input->get($this->query_string_segment) != $base_page)
+ // If a custom first_url hasn't been specified, we'll create one from
+ // the base_url, but without the page item.
+ if ($first_url === '')
{
- $this->cur_page = $CI->input->get($this->query_string_segment);
+ $first_url = $base_url;
- // Prep the current page - no funny business!
- $this->cur_page = (int) $this->cur_page;
+ // If we saved any GET items earlier, make sure they're appended.
+ if ( ! empty($get))
+ {
+ $first_url .= $query_string_sep.http_build_query($get);
+ }
}
+
+ // Add the page segment to the end of the query string, where the
+ // page number will be appended.
+ $base_url .= $query_string_sep.http_build_query(array_merge($get, array($this->query_string_segment => '')));
}
else
{
- if ($CI->uri->segment($this->uri_segment) != $base_page)
+ // Standard segment mode.
+ // Generate our saved query string to append later after the page number.
+ if ( ! empty($get))
{
- $this->cur_page = $CI->uri->segment($this->uri_segment);
+ $query_string = $query_string_sep.http_build_query($get);
+ $this->suffix .= $query_string;
+ }
- // Prep the current page - no funny business!
- $this->cur_page = (int) $this->cur_page;
+ // Does the base_url have the query string in it?
+ // If we're supposed to save it, remove it so we can append it later.
+ if ($this->reuse_query_string === TRUE && ($base_query_pos = strpos($base_url, '?')) !== FALSE)
+ {
+ $base_url = substr($base_url, 0, $base_query_pos);
}
+
+ if ($first_url === '')
+ {
+ $first_url = $base_url.$query_string;
+ }
+
+ $base_url = rtrim($base_url, '/').'/';
}
-
- // Set current page to 1 if using page numbers instead of offset
- if ($this->use_page_numbers AND $this->cur_page == 0)
+
+ // Determine the current page number.
+ $base_page = ($this->use_page_numbers) ? 1 : 0;
+
+ // Are we using query strings?
+ if ($this->page_query_string === TRUE)
{
- $this->cur_page = $base_page;
+ $this->cur_page = $this->CI->input->get($this->query_string_segment);
}
+ elseif (empty($this->cur_page))
+ {
+ // Default to the last segment number if one hasn't been defined.
+ if ($this->uri_segment === 0)
+ {
+ $this->uri_segment = count($this->CI->uri->segment_array());
+ }
- $this->num_links = (int)$this->num_links;
+ $this->cur_page = $this->CI->uri->segment($this->uri_segment);
- if ($this->num_links < 1)
+ // Remove any specified prefix/suffix from the segment.
+ if ($this->prefix !== '' OR $this->suffix !== '')
+ {
+ $this->cur_page = str_replace(array($this->prefix, $this->suffix), '', $this->cur_page);
+ }
+ }
+ else
{
- show_error('Your number of links must be a positive number.');
+ $this->cur_page = (string) $this->cur_page;
}
- if ( ! is_numeric($this->cur_page))
+ // If something isn't quite right, back to the default base page.
+ if ( ! ctype_digit($this->cur_page) OR ($this->use_page_numbers && (int) $this->cur_page === 0))
{
$this->cur_page = $base_page;
}
+ else
+ {
+ // Make sure we're using integers for comparisons later.
+ $this->cur_page = (int) $this->cur_page;
+ }
// Is the page number beyond the result range?
- // If so we show the last page
+ // If so, we show the last page.
if ($this->use_page_numbers)
{
if ($this->cur_page > $num_pages)
@@ -190,67 +534,56 @@ class CI_Pagination {
$this->cur_page = $num_pages;
}
}
- else
+ elseif ($this->cur_page > $this->total_rows)
{
- if ($this->cur_page > $this->total_rows)
- {
- $this->cur_page = ($num_pages - 1) * $this->per_page;
- }
+ $this->cur_page = ($num_pages - 1) * $this->per_page;
}
$uri_page_number = $this->cur_page;
-
+
+ // If we're using offset instead of page numbers, convert it
+ // to a page number, so we can generate the surrounding number links.
if ( ! $this->use_page_numbers)
{
- $this->cur_page = floor(($this->cur_page/$this->per_page) + 1);
+ $this->cur_page = (int) floor(($this->cur_page/$this->per_page) + 1);
}
// Calculate the start and end numbers. These determine
- // which number to start and end the digit links with
- $start = (($this->cur_page - $this->num_links) > 0) ? $this->cur_page - ($this->num_links - 1) : 1;
- $end = (($this->cur_page + $this->num_links) < $num_pages) ? $this->cur_page + $this->num_links : $num_pages;
-
- // Is pagination being used over GET or POST? If get, add a per_page query
- // string. If post, add a trailing slash to the base URL if needed
- if ($CI->config->item('enable_query_strings') === TRUE OR $this->page_query_string === TRUE)
- {
- $this->base_url = rtrim($this->base_url).'&amp;'.$this->query_string_segment.'=';
- }
- else
- {
- $this->base_url = rtrim($this->base_url, '/') .'/';
- }
+ // which number to start and end the digit links with.
+ $start = (($this->cur_page - $this->num_links) > 0) ? $this->cur_page - ($this->num_links - 1) : 1;
+ $end = (($this->cur_page + $this->num_links) < $num_pages) ? $this->cur_page + $this->num_links : $num_pages;
// And here we go...
$output = '';
- // Render the "First" link
- if ($this->first_link !== FALSE AND $this->cur_page > ($this->num_links + 1))
+ // Render the "First" link.
+ if ($this->first_link !== FALSE && $this->cur_page > ($this->num_links + 1 + ! $this->num_links))
{
- $first_url = ($this->first_url == '') ? $this->base_url : $this->first_url;
- $output .= $this->first_tag_open.'<a '.$this->anchor_class.'href="'.$first_url.'">'.$this->first_link.'</a>'.$this->first_tag_close;
+ // Take the general parameters, and squeeze this pagination-page attr in for JS frameworks.
+ $attributes = sprintf('%s %s="%d"', $this->_attributes, $this->data_page_attr, 1);
+
+ $output .= $this->first_tag_open.'<a href="'.$first_url.'"'.$attributes.$this->_attr_rel('start').'>'
+ .$this->first_link.'</a>'.$this->first_tag_close;
}
- // Render the "previous" link
- if ($this->prev_link !== FALSE AND $this->cur_page != 1)
+ // Render the "Previous" link.
+ if ($this->prev_link !== FALSE && $this->cur_page !== 1)
{
- if ($this->use_page_numbers)
- {
- $i = $uri_page_number - 1;
- }
- else
- {
- $i = $uri_page_number - $this->per_page;
- }
+ $i = ($this->use_page_numbers) ? $uri_page_number - 1 : $uri_page_number - $this->per_page;
- if ($i == 0 && $this->first_url != '')
+ $attributes = sprintf('%s %s="%d"', $this->_attributes, $this->data_page_attr, ($this->cur_page - 1));
+
+ if ($i === $base_page)
{
- $output .= $this->prev_tag_open.'<a '.$this->anchor_class.'href="'.$this->first_url.'">'.$this->prev_link.'</a>'.$this->prev_tag_close;
+ // First page
+ $output .= $this->prev_tag_open.'<a href="'.$first_url.'"'.$attributes.$this->_attr_rel('prev').'>'
+ .$this->prev_link.'</a>'.$this->prev_tag_close;
}
else
{
- $i = ($i == 0) ? '' : $this->prefix.$i.$this->suffix;
- $output .= $this->prev_tag_open.'<a '.$this->anchor_class.'href="'.$this->base_url.$i.'">'.$this->prev_link.'</a>'.$this->prev_tag_close;
+ $append = $this->prefix.$i.$this->suffix;
+ $output .= $this->prev_tag_open.'<a href="'.$base_url.$append.'"'.$attributes.$this->_attr_rel('prev').'>'
+ .$this->prev_link.'</a>'.$this->prev_tag_close;
}
}
@@ -259,82 +592,106 @@ class CI_Pagination {
if ($this->display_pages !== FALSE)
{
// Write the digit links
- for ($loop = $start -1; $loop <= $end; $loop++)
+ for ($loop = $start - 1; $loop <= $end; $loop++)
{
- if ($this->use_page_numbers)
- {
- $i = $loop;
- }
- else
- {
- $i = ($loop * $this->per_page) - $this->per_page;
- }
+ $i = ($this->use_page_numbers) ? $loop : ($loop * $this->per_page) - $this->per_page;
+
+ $attributes = sprintf('%s %s="%d"', $this->_attributes, $this->data_page_attr, $loop);
if ($i >= $base_page)
{
- if ($this->cur_page == $loop)
+ if ($this->cur_page === $loop)
+ {
+ // Current page
+ $output .= $this->cur_tag_open.$loop.$this->cur_tag_close;
+ }
+ elseif ($i === $base_page)
{
- $output .= $this->cur_tag_open.$loop.$this->cur_tag_close; // Current page
+ // First page
+ $output .= $this->num_tag_open.'<a href="'.$first_url.'"'.$attributes.$this->_attr_rel('start').'>'
+ .$loop.'</a>'.$this->num_tag_close;
}
else
{
- $n = ($i == $base_page) ? '' : $i;
-
- if ($n == '' && $this->first_url != '')
- {
- $output .= $this->num_tag_open.'<a '.$this->anchor_class.'href="'.$this->first_url.'">'.$loop.'</a>'.$this->num_tag_close;
- }
- else
- {
- $n = ($n == '') ? '' : $this->prefix.$n.$this->suffix;
-
- $output .= $this->num_tag_open.'<a '.$this->anchor_class.'href="'.$this->base_url.$n.'">'.$loop.'</a>'.$this->num_tag_close;
- }
+ $append = $this->prefix.$i.$this->suffix;
+ $output .= $this->num_tag_open.'<a href="'.$base_url.$append.'"'.$attributes.'>'
+ .$loop.'</a>'.$this->num_tag_close;
}
}
}
}
// Render the "next" link
- if ($this->next_link !== FALSE AND $this->cur_page < $num_pages)
+ if ($this->next_link !== FALSE && $this->cur_page < $num_pages)
{
- if ($this->use_page_numbers)
- {
- $i = $this->cur_page + 1;
- }
- else
- {
- $i = ($this->cur_page * $this->per_page);
- }
+ $i = ($this->use_page_numbers) ? $this->cur_page + 1 : $this->cur_page * $this->per_page;
+
+ $attributes = sprintf('%s %s="%d"', $this->_attributes, $this->data_page_attr, $this->cur_page + 1);
- $output .= $this->next_tag_open.'<a '.$this->anchor_class.'href="'.$this->base_url.$this->prefix.$i.$this->suffix.'">'.$this->next_link.'</a>'.$this->next_tag_close;
+ $output .= $this->next_tag_open.'<a href="'.$base_url.$this->prefix.$i.$this->suffix.'"'.$attributes
+ .$this->_attr_rel('next').'>'.$this->next_link.'</a>'.$this->next_tag_close;
}
// Render the "Last" link
- if ($this->last_link !== FALSE AND ($this->cur_page + $this->num_links) < $num_pages)
+ if ($this->last_link !== FALSE && ($this->cur_page + $this->num_links + ! $this->num_links) < $num_pages)
{
- if ($this->use_page_numbers)
- {
- $i = $num_pages;
- }
- else
- {
- $i = (($num_pages * $this->per_page) - $this->per_page);
- }
- $output .= $this->last_tag_open.'<a '.$this->anchor_class.'href="'.$this->base_url.$this->prefix.$i.$this->suffix.'">'.$this->last_link.'</a>'.$this->last_tag_close;
+ $i = ($this->use_page_numbers) ? $num_pages : ($num_pages * $this->per_page) - $this->per_page;
+
+ $attributes = sprintf('%s %s="%d"', $this->_attributes, $this->data_page_attr, $num_pages);
+
+ $output .= $this->last_tag_open.'<a href="'.$base_url.$this->prefix.$i.$this->suffix.'"'.$attributes.'>'
+ .$this->last_link.'</a>'.$this->last_tag_close;
}
- // Kill double slashes. Note: Sometimes we can end up with a double slash
+ // Kill double slashes. Note: Sometimes we can end up with a double slash
// in the penultimate link so we'll kill all double slashes.
- $output = preg_replace("#([^:])//+#", "\\1/", $output);
+ $output = preg_replace('#([^:"])//+#', '\\1/', $output);
// Add the wrapper HTML if exists
- $output = $this->full_tag_open.$output.$this->full_tag_close;
+ return $this->full_tag_open.$output.$this->full_tag_close;
+ }
+
+ // --------------------------------------------------------------------
- return $output;
+ /**
+ * Parse attributes
+ *
+ * @param array $attributes
+ * @return void
+ */
+ protected function _parse_attributes($attributes)
+ {
+ isset($attributes['rel']) OR $attributes['rel'] = TRUE;
+ $this->_link_types = ($attributes['rel'])
+ ? array('start' => 'start', 'prev' => 'prev', 'next' => 'next')
+ : array();
+ unset($attributes['rel']);
+
+ $this->_attributes = '';
+ foreach ($attributes as $key => $value)
+ {
+ $this->_attributes .= ' '.$key.'="'.$value.'"';
+ }
+ }
+
+ // --------------------------------------------------------------------
+
+ /**
+ * Add "rel" attribute
+ *
+ * @link https://www.w3.org/TR/html5/links.html#linkTypes
+ * @param string $type
+ * @return string
+ */
+ protected function _attr_rel($type)
+ {
+ if (isset($this->_link_types[$type]))
+ {
+ unset($this->_link_types[$type]);
+ return ' rel="'.$type.'"';
+ }
+
+ return '';
}
-}
-// END Pagination Class
-/* End of file Pagination.php */
-/* Location: ./system/libraries/Pagination.php */ \ No newline at end of file
+}
diff --git a/system/libraries/Parser.php b/system/libraries/Parser.php
index 4d31f81c7..e0adec606 100644
--- a/system/libraries/Parser.php
+++ b/system/libraries/Parser.php
@@ -1,19 +1,42 @@
-<?php if ( ! defined('BASEPATH')) exit('No direct script access allowed');
+<?php
/**
* CodeIgniter
*
- * An open source application development framework for PHP 5.1.6 or newer
+ * An open source application development framework for PHP
*
- * @package CodeIgniter
- * @author ExpressionEngine Dev Team
- * @copyright Copyright (c) 2008 - 2014, EllisLab, Inc.
- * @license http://codeigniter.com/user_guide/license.html
- * @link http://codeigniter.com
- * @since Version 1.0
+ * This content is released under the MIT License (MIT)
+ *
+ * Copyright (c) 2019 - 2022, CodeIgniter Foundation
+ *
+ * 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.
+ *
+ * @package CodeIgniter
+ * @author EllisLab Dev Team
+ * @copyright Copyright (c) 2008 - 2014, EllisLab, Inc. (https://ellislab.com/)
+ * @copyright Copyright (c) 2014 - 2019, British Columbia Institute of Technology (https://bcit.ca/)
+ * @copyright Copyright (c) 2019 - 2022, CodeIgniter Foundation (https://codeigniter.com/)
+ * @license https://opensource.org/licenses/MIT MIT License
+ * @link https://codeigniter.com
+ * @since Version 1.0.0
* @filesource
*/
-
-// ------------------------------------------------------------------------
+defined('BASEPATH') OR exit('No direct script access allowed');
/**
* Parser Class
@@ -21,22 +44,53 @@
* @package CodeIgniter
* @subpackage Libraries
* @category Parser
- * @author ExpressionEngine Dev Team
- * @link http://codeigniter.com/user_guide/libraries/parser.html
+ * @author EllisLab Dev Team
+ * @link https://codeigniter.com/userguide3/libraries/parser.html
*/
class CI_Parser {
- var $l_delim = '{';
- var $r_delim = '}';
- var $object;
+ /**
+ * Left delimiter character for pseudo vars
+ *
+ * @var string
+ */
+ public $l_delim = '{';
+
+ /**
+ * Right delimiter character for pseudo vars
+ *
+ * @var string
+ */
+ public $r_delim = '}';
+
+ /**
+ * Reference to CodeIgniter instance
+ *
+ * @var object
+ */
+ protected $CI;
+
+ // --------------------------------------------------------------------
/**
- * Parse a template
+ * Class constructor
+ *
+ * @return void
+ */
+ public function __construct()
+ {
+ $this->CI =& get_instance();
+ log_message('info', 'Parser Class Initialized');
+ }
+
+ // --------------------------------------------------------------------
+
+ /**
+ * Parse a template
*
* Parses pseudo-variables contained in the specified template view,
* replacing them with the data in the second param
*
- * @access public
* @param string
* @param array
* @param bool
@@ -44,8 +98,7 @@ class CI_Parser {
*/
public function parse($template, $data, $return = FALSE)
{
- $CI =& get_instance();
- $template = $CI->load->view($template, $data, TRUE);
+ $template = $this->CI->load->view($template, $data, TRUE);
return $this->_parse($template, $data, $return);
}
@@ -53,18 +106,17 @@ class CI_Parser {
// --------------------------------------------------------------------
/**
- * Parse a String
+ * Parse a String
*
* Parses pseudo-variables contained in the specified string,
* replacing them with the data in the second param
*
- * @access public
* @param string
* @param array
* @param bool
* @return string
*/
- function parse_string($template, $data, $return = FALSE)
+ public function parse_string($template, $data, $return = FALSE)
{
return $this->_parse($template, $data, $return);
}
@@ -72,40 +124,40 @@ class CI_Parser {
// --------------------------------------------------------------------
/**
- * Parse a template
+ * Parse a template
*
* Parses pseudo-variables contained in the specified template,
* replacing them with the data in the second param
*
- * @access public
* @param string
* @param array
* @param bool
* @return string
*/
- function _parse($template, $data, $return = FALSE)
+ protected function _parse($template, $data, $return = FALSE)
{
- if ($template == '')
+ if ($template === '')
{
return FALSE;
}
+ $replace = array();
foreach ($data as $key => $val)
{
- if (is_array($val))
- {
- $template = $this->_parse_pair($key, $val, $template);
- }
- else
- {
- $template = $this->_parse_single($key, (string)$val, $template);
- }
+ $replace = array_merge(
+ $replace,
+ is_array($val)
+ ? $this->_parse_pair($key, $val, $template)
+ : $this->_parse_single($key, (string) $val, $template)
+ );
}
- if ($return == FALSE)
+ unset($data);
+ $template = strtr($template, $replace);
+
+ if ($return === FALSE)
{
- $CI =& get_instance();
- $CI->output->append_output($template);
+ $this->CI->output->append_output($template);
}
return $template;
@@ -114,14 +166,13 @@ class CI_Parser {
// --------------------------------------------------------------------
/**
- * Set the left/right variable delimiters
+ * Set the left/right variable delimiters
*
- * @access public
* @param string
* @param string
* @return void
*/
- function set_delimiters($l = '{', $r = '}')
+ public function set_delimiters($l = '{', $r = '}')
{
$this->l_delim = $l;
$this->r_delim = $r;
@@ -130,83 +181,69 @@ class CI_Parser {
// --------------------------------------------------------------------
/**
- * Parse a single key/value
+ * Parse a single key/value
*
- * @access private
* @param string
* @param string
* @param string
* @return string
*/
- function _parse_single($key, $val, $string)
+ protected function _parse_single($key, $val, $string)
{
- return str_replace($this->l_delim.$key.$this->r_delim, $val, $string);
+ return array($this->l_delim.$key.$this->r_delim => (string) $val);
}
// --------------------------------------------------------------------
/**
- * Parse a tag pair
+ * Parse a tag pair
*
- * Parses tag pairs: {some_tag} string... {/some_tag}
+ * Parses tag pairs: {some_tag} string... {/some_tag}
*
- * @access private
* @param string
* @param array
* @param string
* @return string
*/
- function _parse_pair($variable, $data, $string)
+ protected function _parse_pair($variable, $data, $string)
{
- if (FALSE === ($match = $this->_match_pair($string, $variable)))
- {
- return $string;
- }
-
- $str = '';
- foreach ($data as $row)
+ $replace = array();
+ preg_match_all(
+ '#'.preg_quote($this->l_delim.$variable.$this->r_delim).'(.+?)'.preg_quote($this->l_delim.'/'.$variable.$this->r_delim).'#s',
+ $string,
+ $matches,
+ PREG_SET_ORDER
+ );
+
+ foreach ($matches as $match)
{
- $temp = $match['1'];
- foreach ($row as $key => $val)
+ $str = '';
+ foreach ($data as $row)
{
- if ( ! is_array($val))
- {
- $temp = $this->_parse_single($key, $val, $temp);
- }
- else
+ $temp = array();
+ foreach ($row as $key => $val)
{
- $temp = $this->_parse_pair($key, $val, $temp);
+ if (is_array($val))
+ {
+ $pair = $this->_parse_pair($key, $val, $match[1]);
+ if ( ! empty($pair))
+ {
+ $temp = array_merge($temp, $pair);
+ }
+
+ continue;
+ }
+
+ $temp[$this->l_delim.$key.$this->r_delim] = $val;
}
- }
-
- $str .= $temp;
- }
- return str_replace($match['0'], $str, $string);
- }
-
- // --------------------------------------------------------------------
+ $str .= strtr($match[1], $temp);
+ }
- /**
- * Matches a variable pair
- *
- * @access private
- * @param string
- * @param string
- * @return mixed
- */
- function _match_pair($string, $variable)
- {
- if ( ! preg_match("|" . preg_quote($this->l_delim) . $variable . preg_quote($this->r_delim) . "(.+?)". preg_quote($this->l_delim) . '/' . $variable . preg_quote($this->r_delim) . "|s", $string, $match))
- {
- return FALSE;
+ $replace[$match[0]] = $str;
}
- return $match;
+ return $replace;
}
}
-// END Parser Class
-
-/* End of file Parser.php */
-/* Location: ./system/libraries/Parser.php */
diff --git a/system/libraries/Profiler.php b/system/libraries/Profiler.php
index 2fe21db11..d423c1481 100644
--- a/system/libraries/Profiler.php
+++ b/system/libraries/Profiler.php
@@ -1,19 +1,42 @@
-<?php if ( ! defined('BASEPATH')) exit('No direct script access allowed');
+<?php
/**
* CodeIgniter
*
- * An open source application development framework for PHP 5.1.6 or newer
+ * An open source application development framework for PHP
*
- * @package CodeIgniter
- * @author ExpressionEngine Dev Team
- * @copyright Copyright (c) 2008 - 2014, EllisLab, Inc.
- * @license http://codeigniter.com/user_guide/license.html
- * @link http://codeigniter.com
- * @since Version 1.0
+ * This content is released under the MIT License (MIT)
+ *
+ * Copyright (c) 2019 - 2022, CodeIgniter Foundation
+ *
+ * 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.
+ *
+ * @package CodeIgniter
+ * @author EllisLab Dev Team
+ * @copyright Copyright (c) 2008 - 2014, EllisLab, Inc. (https://ellislab.com/)
+ * @copyright Copyright (c) 2014 - 2019, British Columbia Institute of Technology (https://bcit.ca/)
+ * @copyright Copyright (c) 2019 - 2022, CodeIgniter Foundation (https://codeigniter.com/)
+ * @license https://opensource.org/licenses/MIT MIT License
+ * @link https://codeigniter.com
+ * @since Version 1.0.0
* @filesource
*/
-
-// ------------------------------------------------------------------------
+defined('BASEPATH') OR exit('No direct script access allowed');
/**
* CodeIgniter Profiler Class
@@ -27,51 +50,68 @@
* @package CodeIgniter
* @subpackage Libraries
* @category Libraries
- * @author ExpressionEngine Dev Team
- * @link http://codeigniter.com/user_guide/general/profiling.html
+ * @author EllisLab Dev Team
+ * @link https://codeigniter.com/userguide3/general/profiling.html
*/
class CI_Profiler {
+ /**
+ * List of profiler sections available to show
+ *
+ * @var array
+ */
protected $_available_sections = array(
- 'benchmarks',
- 'get',
- 'memory_usage',
- 'post',
- 'uri_string',
- 'controller_info',
- 'queries',
- 'http_headers',
- 'session_data',
- 'config'
- );
+ 'benchmarks',
+ 'get',
+ 'memory_usage',
+ 'post',
+ 'uri_string',
+ 'controller_info',
+ 'queries',
+ 'http_headers',
+ 'session_data',
+ 'config'
+ );
+ /**
+ * Number of queries to show before making the additional queries togglable
+ *
+ * @var int
+ */
protected $_query_toggle_count = 25;
+ /**
+ * Reference to the CodeIgniter singleton
+ *
+ * @var object
+ */
protected $CI;
// --------------------------------------------------------------------
+ /**
+ * Class constructor
+ *
+ * Initialize Profiler
+ *
+ * @param array $config Parameters
+ */
public function __construct($config = array())
{
$this->CI =& get_instance();
$this->CI->load->language('profiler');
- if (isset($config['query_toggle_count']))
- {
- $this->_query_toggle_count = (int) $config['query_toggle_count'];
- unset($config['query_toggle_count']);
- }
-
// default all sections to display
foreach ($this->_available_sections as $section)
{
if ( ! isset($config[$section]))
{
- $this->_compile_{$section} = TRUE;
+ $this->{'_compile_'.$section} = TRUE;
}
}
$this->set_sections($config);
+ log_message('info', 'Profiler Class Initialized');
}
// --------------------------------------------------------------------
@@ -81,16 +121,22 @@ class CI_Profiler {
*
* Sets the private _compile_* properties to enable/disable Profiler sections
*
- * @param mixed
+ * @param mixed $config
* @return void
*/
public function set_sections($config)
{
+ if (isset($config['query_toggle_count']))
+ {
+ $this->_query_toggle_count = (int) $config['query_toggle_count'];
+ unset($config['query_toggle_count']);
+ }
+
foreach ($config as $method => $enable)
{
if (in_array($method, $this->_available_sections))
{
- $this->_compile_{$method} = ($enable !== FALSE) ? TRUE : FALSE;
+ $this->{'_compile_'.$method} = ($enable !== FALSE);
}
}
}
@@ -114,36 +160,32 @@ class CI_Profiler {
{
// We match the "end" marker so that the list ends
// up in the order that it was defined
- if (preg_match("/(.+?)_end/i", $key, $match))
+ if (preg_match('/(.+?)_end$/i', $key, $match)
+ && isset($this->CI->benchmark->marker[$match[1].'_end'], $this->CI->benchmark->marker[$match[1].'_start']))
{
- if (isset($this->CI->benchmark->marker[$match[1].'_end']) AND isset($this->CI->benchmark->marker[$match[1].'_start']))
- {
- $profile[$match[1]] = $this->CI->benchmark->elapsed_time($match[1].'_start', $key);
- }
+ $profile[$match[1]] = $this->CI->benchmark->elapsed_time($match[1].'_start', $key);
}
}
// Build a table containing the profile data.
// Note: At some point we should turn this into a template that can
- // be modified. We also might want to make this data available to be logged
+ // be modified. We also might want to make this data available to be logged
- $output = "\n\n";
- $output .= '<fieldset id="ci_profiler_benchmarks" style="border:1px solid #900;padding:6px 10px 10px 10px;margin:20px 0 20px 0;background-color:#eee">';
- $output .= "\n";
- $output .= '<legend style="color:#900;">&nbsp;&nbsp;'.$this->CI->lang->line('profiler_benchmarks').'&nbsp;&nbsp;</legend>';
- $output .= "\n";
- $output .= "\n\n<table style='width:100%'>\n";
+ $output = "\n\n"
+ .'<fieldset id="ci_profiler_benchmarks" style="border:1px solid #900;padding:6px 10px 10px 10px;margin:20px 0 20px 0;background-color:#eee;">'
+ ."\n"
+ .'<legend style="color:#900;">&nbsp;&nbsp;'.$this->CI->lang->line('profiler_benchmarks')."&nbsp;&nbsp;</legend>"
+ ."\n\n\n<table style=\"width:100%;\">\n";
foreach ($profile as $key => $val)
{
$key = ucwords(str_replace(array('_', '-'), ' ', $key));
- $output .= "<tr><td style='padding:5px;width:50%;color:#000;font-weight:bold;background-color:#ddd;'>".$key."&nbsp;&nbsp;</td><td style='padding:5px;width:50%;color:#900;font-weight:normal;background-color:#ddd;'>".$val."</td></tr>\n";
+ $output .= '<tr><td style="padding:5px;width:50%;color:#000;font-weight:bold;background-color:#ddd;">'
+ .$key.'&nbsp;&nbsp;</td><td style="padding:5px;width:50%;color:#900;font-weight:normal;background-color:#ddd;">'
+ .$val."</td></tr>\n";
}
- $output .= "</table>\n";
- $output .= "</fieldset>";
-
- return $output;
+ return $output."</table>\n</fieldset>";
}
// --------------------------------------------------------------------
@@ -158,27 +200,37 @@ class CI_Profiler {
$dbs = array();
// Let's determine which databases are currently connected to
- foreach (get_object_vars($this->CI) as $CI_object)
+ foreach (get_object_vars($this->CI) as $name => $cobject)
{
- if (is_object($CI_object) && is_subclass_of(get_class($CI_object), 'CI_DB') )
+ if (is_object($cobject))
{
- $dbs[] = $CI_object;
+ if ($cobject instanceof CI_DB)
+ {
+ $dbs[get_class($this->CI).':$'.$name] = $cobject;
+ }
+ elseif ($cobject instanceof CI_Model)
+ {
+ foreach (get_object_vars($cobject) as $mname => $mobject)
+ {
+ if ($mobject instanceof CI_DB)
+ {
+ $dbs[get_class($cobject).':$'.$mname] = $mobject;
+ }
+ }
+ }
}
}
- if (count($dbs) == 0)
+ if (count($dbs) === 0)
{
- $output = "\n\n";
- $output .= '<fieldset id="ci_profiler_queries" style="border:1px solid #0000FF;padding:6px 10px 10px 10px;margin:20px 0 20px 0;background-color:#eee">';
- $output .= "\n";
- $output .= '<legend style="color:#0000FF;">&nbsp;&nbsp;'.$this->CI->lang->line('profiler_queries').'&nbsp;&nbsp;</legend>';
- $output .= "\n";
- $output .= "\n\n<table style='border:none; width:100%;'>\n";
- $output .="<tr><td style='width:100%;color:#0000FF;font-weight:normal;background-color:#eee;padding:5px'>".$this->CI->lang->line('profiler_no_db')."</td></tr>\n";
- $output .= "</table>\n";
- $output .= "</fieldset>";
-
- return $output;
+ return "\n\n"
+ .'<fieldset id="ci_profiler_queries" style="border:1px solid #0000FF;padding:6px 10px 10px 10px;margin:20px 0 20px 0;background-color:#eee;">'
+ ."\n"
+ .'<legend style="color:#0000FF;">&nbsp;&nbsp;'.$this->CI->lang->line('profiler_queries').'&nbsp;&nbsp;</legend>'
+ ."\n\n\n<table style=\"border:none; width:100%;\">\n"
+ .'<tr><td style="width:100%;color:#0000FF;font-weight:normal;background-color:#eee;padding:5px;">'
+ .$this->CI->lang->line('profiler_no_db')
+ ."</td></tr>\n</table>\n</fieldset>";
}
// Load the text helper so we can highlight the SQL
@@ -188,58 +240,57 @@ class CI_Profiler {
$highlight = array('SELECT', 'DISTINCT', 'FROM', 'WHERE', 'AND', 'LEFT&nbsp;JOIN', 'ORDER&nbsp;BY', 'GROUP&nbsp;BY', 'LIMIT', 'INSERT', 'INTO', 'VALUES', 'UPDATE', 'OR&nbsp;', 'HAVING', 'OFFSET', 'NOT&nbsp;IN', 'IN', 'LIKE', 'NOT&nbsp;LIKE', 'COUNT', 'MAX', 'MIN', 'ON', 'AS', 'AVG', 'SUM', '(', ')');
$output = "\n\n";
-
$count = 0;
- foreach ($dbs as $db)
+ foreach ($dbs as $name => $db)
{
- $count++;
-
$hide_queries = (count($db->queries) > $this->_query_toggle_count) ? ' display:none' : '';
+ $total_time = number_format(array_sum($db->query_times), 4).' '.$this->CI->lang->line('profiler_seconds');
$show_hide_js = '(<span style="cursor: pointer;" onclick="var s=document.getElementById(\'ci_profiler_queries_db_'.$count.'\').style;s.display=s.display==\'none\'?\'\':\'none\';this.innerHTML=this.innerHTML==\''.$this->CI->lang->line('profiler_section_hide').'\'?\''.$this->CI->lang->line('profiler_section_show').'\':\''.$this->CI->lang->line('profiler_section_hide').'\';">'.$this->CI->lang->line('profiler_section_hide').'</span>)';
- if ($hide_queries != '')
+ if ($hide_queries !== '')
{
$show_hide_js = '(<span style="cursor: pointer;" onclick="var s=document.getElementById(\'ci_profiler_queries_db_'.$count.'\').style;s.display=s.display==\'none\'?\'\':\'none\';this.innerHTML=this.innerHTML==\''.$this->CI->lang->line('profiler_section_show').'\'?\''.$this->CI->lang->line('profiler_section_hide').'\':\''.$this->CI->lang->line('profiler_section_show').'\';">'.$this->CI->lang->line('profiler_section_show').'</span>)';
}
- $output .= '<fieldset style="border:1px solid #0000FF;padding:6px 10px 10px 10px;margin:20px 0 20px 0;background-color:#eee">';
- $output .= "\n";
- $output .= '<legend style="color:#0000FF;">&nbsp;&nbsp;'.$this->CI->lang->line('profiler_database').':&nbsp; '.$db->database.'&nbsp;&nbsp;&nbsp;'.$this->CI->lang->line('profiler_queries').': '.count($db->queries).'&nbsp;&nbsp;'.$show_hide_js.'</legend>';
- $output .= "\n";
- $output .= "\n\n<table style='width:100%;{$hide_queries}' id='ci_profiler_queries_db_{$count}'>\n";
+ $output .= '<fieldset style="border:1px solid #0000FF;padding:6px 10px 10px 10px;margin:20px 0 20px 0;background-color:#eee;">'
+ ."\n"
+ .'<legend style="color:#0000FF;">&nbsp;&nbsp;'.$this->CI->lang->line('profiler_database')
+ .':&nbsp; '.$db->database.' ('.$name.')&nbsp;&nbsp;&nbsp;'.$this->CI->lang->line('profiler_queries')
+ .': '.count($db->queries).' ('.$total_time.')&nbsp;&nbsp;'.$show_hide_js."</legend>\n\n\n"
+ .'<table style="width:100%;'.$hide_queries.'" id="ci_profiler_queries_db_'.$count."\">\n";
- if (count($db->queries) == 0)
+ if (count($db->queries) === 0)
{
- $output .= "<tr><td style='width:100%;color:#0000FF;font-weight:normal;background-color:#eee;padding:5px;'>".$this->CI->lang->line('profiler_no_queries')."</td></tr>\n";
+ $output .= '<tr><td style="width:100%;color:#0000FF;font-weight:normal;background-color:#eee;padding:5px;">'
+ .$this->CI->lang->line('profiler_no_queries')."</td></tr>\n";
}
else
{
foreach ($db->queries as $key => $val)
{
$time = number_format($db->query_times[$key], 4);
-
- $val = highlight_code($val, ENT_QUOTES);
+ $val = highlight_code($val);
foreach ($highlight as $bold)
{
$val = str_replace($bold, '<strong>'.$bold.'</strong>', $val);
}
- $output .= "<tr><td style='padding:5px; vertical-align: top;width:1%;color:#900;font-weight:normal;background-color:#ddd;'>".$time."&nbsp;&nbsp;</td><td style='padding:5px; color:#000;font-weight:normal;background-color:#ddd;'>".$val."</td></tr>\n";
+ $output .= '<tr><td style="padding:5px;vertical-align:top;width:1%;color:#900;font-weight:normal;background-color:#ddd;">'
+ .$time.'&nbsp;&nbsp;</td><td style="padding:5px;color:#000;font-weight:normal;background-color:#ddd;">'
+ .$val."</td></tr>\n";
}
}
- $output .= "</table>\n";
- $output .= "</fieldset>";
-
+ $output .= "</table>\n</fieldset>";
+ $count++;
}
return $output;
}
-
// --------------------------------------------------------------------
/**
@@ -249,44 +300,35 @@ class CI_Profiler {
*/
protected function _compile_get()
{
- $output = "\n\n";
- $output .= '<fieldset id="ci_profiler_get" style="border:1px solid #cd6e00;padding:6px 10px 10px 10px;margin:20px 0 20px 0;background-color:#eee">';
- $output .= "\n";
- $output .= '<legend style="color:#cd6e00;">&nbsp;&nbsp;'.$this->CI->lang->line('profiler_get_data').'&nbsp;&nbsp;</legend>';
- $output .= "\n";
+ $output = "\n\n"
+ .'<fieldset id="ci_profiler_get" style="border:1px solid #cd6e00;padding:6px 10px 10px 10px;margin:20px 0 20px 0;background-color:#eee;">'
+ ."\n"
+ .'<legend style="color:#cd6e00;">&nbsp;&nbsp;'.$this->CI->lang->line('profiler_get_data')."&nbsp;&nbsp;</legend>\n";
- if (count($_GET) == 0)
+ if (count($_GET) === 0)
{
- $output .= "<div style='color:#cd6e00;font-weight:normal;padding:4px 0 4px 0'>".$this->CI->lang->line('profiler_no_get')."</div>";
+ $output .= '<div style="color:#cd6e00;font-weight:normal;padding:4px 0 4px 0;">'.$this->CI->lang->line('profiler_no_get').'</div>';
}
else
{
- $output .= "\n\n<table style='width:100%; border:none'>\n";
+ $output .= "\n\n<table style=\"width:100%;border:none;\">\n";
foreach ($_GET as $key => $val)
{
- if ( ! is_numeric($key))
- {
- $key = "'".$key."'";
- }
-
- $output .= "<tr><td style='width:50%;color:#000;background-color:#ddd;padding:5px'>&#36;_GET[".$key."]&nbsp;&nbsp; </td><td style='width:50%;padding:5px;color:#cd6e00;font-weight:normal;background-color:#ddd;'>";
- if (is_array($val))
- {
- $output .= "<pre>" . htmlspecialchars(stripslashes(print_r($val, true))) . "</pre>";
- }
- else
- {
- $output .= htmlspecialchars(stripslashes($val));
- }
- $output .= "</td></tr>\n";
+ is_int($key) OR $key = "'".htmlspecialchars($key, ENT_QUOTES, config_item('charset'))."'";
+ $val = (is_array($val) OR is_object($val))
+ ? '<pre>'.htmlspecialchars(print_r($val, TRUE), ENT_QUOTES, config_item('charset')).'</pre>'
+ : htmlspecialchars($val, ENT_QUOTES, config_item('charset'));
+
+ $output .= '<tr><td style="width:50%;color:#000;background-color:#ddd;padding:5px;">&#36;_GET['
+ .$key.']&nbsp;&nbsp; </td><td style="width:50%;padding:5px;color:#cd6e00;font-weight:normal;background-color:#ddd;">'
+ .$val."</td></tr>\n";
}
$output .= "</table>\n";
}
- $output .= "</fieldset>";
- return $output;
+ return $output.'</fieldset>';
}
// --------------------------------------------------------------------
@@ -298,44 +340,47 @@ class CI_Profiler {
*/
protected function _compile_post()
{
- $output = "\n\n";
- $output .= '<fieldset id="ci_profiler_post" style="border:1px solid #009900;padding:6px 10px 10px 10px;margin:20px 0 20px 0;background-color:#eee">';
- $output .= "\n";
- $output .= '<legend style="color:#009900;">&nbsp;&nbsp;'.$this->CI->lang->line('profiler_post_data').'&nbsp;&nbsp;</legend>';
- $output .= "\n";
+ $output = "\n\n"
+ .'<fieldset id="ci_profiler_post" style="border:1px solid #009900;padding:6px 10px 10px 10px;margin:20px 0 20px 0;background-color:#eee;">'
+ ."\n"
+ .'<legend style="color:#009900;">&nbsp;&nbsp;'.$this->CI->lang->line('profiler_post_data')."&nbsp;&nbsp;</legend>\n";
- if (count($_POST) == 0)
+ if (count($_POST) === 0 && count($_FILES) === 0)
{
- $output .= "<div style='color:#009900;font-weight:normal;padding:4px 0 4px 0'>".$this->CI->lang->line('profiler_no_post')."</div>";
+ $output .= '<div style="color:#009900;font-weight:normal;padding:4px 0 4px 0;">'.$this->CI->lang->line('profiler_no_post').'</div>';
}
else
{
- $output .= "\n\n<table style='width:100%'>\n";
+ $output .= "\n\n<table style=\"width:100%;\">\n";
foreach ($_POST as $key => $val)
{
- if ( ! is_numeric($key))
- {
- $key = "'".$key."'";
- }
+ is_int($key) OR $key = "'".htmlspecialchars($key, ENT_QUOTES, config_item('charset'))."'";
+ $val = (is_array($val) OR is_object($val))
+ ? '<pre>'.htmlspecialchars(print_r($val, TRUE), ENT_QUOTES, config_item('charset')).'</pre>'
+ : htmlspecialchars($val, ENT_QUOTES, config_item('charset'));
+
+ $output .= '<tr><td style="width:50%;padding:5px;color:#000;background-color:#ddd;">&#36;_POST['
+ .$key.']&nbsp;&nbsp; </td><td style="width:50%;padding:5px;color:#009900;font-weight:normal;background-color:#ddd;">'
+ .$val."</td></tr>\n";
+ }
- $output .= "<tr><td style='width:50%;padding:5px;color:#000;background-color:#ddd;'>&#36;_POST[".$key."]&nbsp;&nbsp; </td><td style='width:50%;padding:5px;color:#009900;font-weight:normal;background-color:#ddd;'>";
- if (is_array($val))
- {
- $output .= "<pre>" . htmlspecialchars(stripslashes(print_r($val, TRUE))) . "</pre>";
- }
- else
- {
- $output .= htmlspecialchars(stripslashes($val));
- }
- $output .= "</td></tr>\n";
+ foreach ($_FILES as $key => $val)
+ {
+ is_int($key) OR $key = "'".htmlspecialchars($key, ENT_QUOTES, config_item('charset'))."'";
+ $val = (is_array($val) OR is_object($val))
+ ? '<pre>'.htmlspecialchars(print_r($val, TRUE), ENT_QUOTES, config_item('charset')).'</pre>'
+ : htmlspecialchars($val, ENT_QUOTES, config_item('charset'));
+
+ $output .= '<tr><td style="width:50%;padding:5px;color:#000;background-color:#ddd;">&#36;_FILES['
+ .$key.']&nbsp;&nbsp; </td><td style="width:50%;padding:5px;color:#009900;font-weight:normal;background-color:#ddd;">'
+ .$val."</td></tr>\n";
}
$output .= "</table>\n";
}
- $output .= "</fieldset>";
- return $output;
+ return $output.'</fieldset>';
}
// --------------------------------------------------------------------
@@ -347,24 +392,13 @@ class CI_Profiler {
*/
protected function _compile_uri_string()
{
- $output = "\n\n";
- $output .= '<fieldset id="ci_profiler_uri_string" style="border:1px solid #000;padding:6px 10px 10px 10px;margin:20px 0 20px 0;background-color:#eee">';
- $output .= "\n";
- $output .= '<legend style="color:#000;">&nbsp;&nbsp;'.$this->CI->lang->line('profiler_uri_string').'&nbsp;&nbsp;</legend>';
- $output .= "\n";
-
- if ($this->CI->uri->uri_string == '')
- {
- $output .= "<div style='color:#000;font-weight:normal;padding:4px 0 4px 0'>".$this->CI->lang->line('profiler_no_uri')."</div>";
- }
- else
- {
- $output .= "<div style='color:#000;font-weight:normal;padding:4px 0 4px 0'>".$this->CI->uri->uri_string."</div>";
- }
-
- $output .= "</fieldset>";
-
- return $output;
+ return "\n\n"
+ .'<fieldset id="ci_profiler_uri_string" style="border:1px solid #000;padding:6px 10px 10px 10px;margin:20px 0 20px 0;background-color:#eee;">'
+ ."\n"
+ .'<legend style="color:#000;">&nbsp;&nbsp;'.$this->CI->lang->line('profiler_uri_string')."&nbsp;&nbsp;</legend>\n"
+ .'<div style="color:#000;font-weight:normal;padding:4px 0 4px 0;">'
+ .($this->CI->uri->uri_string === '' ? $this->CI->lang->line('profiler_no_uri') : $this->CI->uri->uri_string)
+ .'</div></fieldset>';
}
// --------------------------------------------------------------------
@@ -376,17 +410,12 @@ class CI_Profiler {
*/
protected function _compile_controller_info()
{
- $output = "\n\n";
- $output .= '<fieldset id="ci_profiler_controller_info" style="border:1px solid #995300;padding:6px 10px 10px 10px;margin:20px 0 20px 0;background-color:#eee">';
- $output .= "\n";
- $output .= '<legend style="color:#995300;">&nbsp;&nbsp;'.$this->CI->lang->line('profiler_controller_info').'&nbsp;&nbsp;</legend>';
- $output .= "\n";
-
- $output .= "<div style='color:#995300;font-weight:normal;padding:4px 0 4px 0'>".$this->CI->router->fetch_class()."/".$this->CI->router->fetch_method()."</div>";
-
- $output .= "</fieldset>";
-
- return $output;
+ return "\n\n"
+ .'<fieldset id="ci_profiler_controller_info" style="border:1px solid #995300;padding:6px 10px 10px 10px;margin:20px 0 20px 0;background-color:#eee;">'
+ ."\n"
+ .'<legend style="color:#995300;">&nbsp;&nbsp;'.$this->CI->lang->line('profiler_controller_info')."&nbsp;&nbsp;</legend>\n"
+ .'<div style="color:#995300;font-weight:normal;padding:4px 0 4px 0;">'.$this->CI->router->class.'/'.$this->CI->router->method
+ .'</div></fieldset>';
}
// --------------------------------------------------------------------
@@ -400,24 +429,13 @@ class CI_Profiler {
*/
protected function _compile_memory_usage()
{
- $output = "\n\n";
- $output .= '<fieldset id="ci_profiler_memory_usage" style="border:1px solid #5a0099;padding:6px 10px 10px 10px;margin:20px 0 20px 0;background-color:#eee">';
- $output .= "\n";
- $output .= '<legend style="color:#5a0099;">&nbsp;&nbsp;'.$this->CI->lang->line('profiler_memory_usage').'&nbsp;&nbsp;</legend>';
- $output .= "\n";
-
- if (function_exists('memory_get_usage') && ($usage = memory_get_usage()) != '')
- {
- $output .= "<div style='color:#5a0099;font-weight:normal;padding:4px 0 4px 0'>".number_format($usage).' bytes</div>';
- }
- else
- {
- $output .= "<div style='color:#5a0099;font-weight:normal;padding:4px 0 4px 0'>".$this->CI->lang->line('profiler_no_memory')."</div>";
- }
-
- $output .= "</fieldset>";
-
- return $output;
+ return "\n\n"
+ .'<fieldset id="ci_profiler_memory_usage" style="border:1px solid #5a0099;padding:6px 10px 10px 10px;margin:20px 0 20px 0;background-color:#eee;">'
+ ."\n"
+ .'<legend style="color:#5a0099;">&nbsp;&nbsp;'.$this->CI->lang->line('profiler_memory_usage')."&nbsp;&nbsp;</legend>\n"
+ .'<div style="color:#5a0099;font-weight:normal;padding:4px 0 4px 0;">'
+ .(($usage = memory_get_usage()) != '' ? number_format($usage).' bytes' : $this->CI->lang->line('profiler_no_memory'))
+ .'</div></fieldset>';
}
// --------------------------------------------------------------------
@@ -431,24 +449,21 @@ class CI_Profiler {
*/
protected function _compile_http_headers()
{
- $output = "\n\n";
- $output .= '<fieldset id="ci_profiler_http_headers" style="border:1px solid #000;padding:6px 10px 10px 10px;margin:20px 0 20px 0;background-color:#eee">';
- $output .= "\n";
- $output .= '<legend style="color:#000;">&nbsp;&nbsp;'.$this->CI->lang->line('profiler_headers').'&nbsp;&nbsp;(<span style="cursor: pointer;" onclick="var s=document.getElementById(\'ci_profiler_httpheaders_table\').style;s.display=s.display==\'none\'?\'\':\'none\';this.innerHTML=this.innerHTML==\''.$this->CI->lang->line('profiler_section_show').'\'?\''.$this->CI->lang->line('profiler_section_hide').'\':\''.$this->CI->lang->line('profiler_section_show').'\';">'.$this->CI->lang->line('profiler_section_show').'</span>)</legend>';
- $output .= "\n";
-
- $output .= "\n\n<table style='width:100%;display:none' id='ci_profiler_httpheaders_table'>\n";
-
- foreach (array('HTTP_ACCEPT', 'HTTP_USER_AGENT', 'HTTP_CONNECTION', 'SERVER_PORT', 'SERVER_NAME', 'REMOTE_ADDR', 'SERVER_SOFTWARE', 'HTTP_ACCEPT_LANGUAGE', 'SCRIPT_NAME', 'REQUEST_METHOD',' HTTP_HOST', 'REMOTE_HOST', 'CONTENT_TYPE', 'SERVER_PROTOCOL', 'QUERY_STRING', 'HTTP_ACCEPT_ENCODING', 'HTTP_X_FORWARDED_FOR') as $header)
+ $output = "\n\n"
+ .'<fieldset id="ci_profiler_http_headers" style="border:1px solid #000;padding:6px 10px 10px 10px;margin:20px 0 20px 0;background-color:#eee;">'
+ ."\n"
+ .'<legend style="color:#000;">&nbsp;&nbsp;'.$this->CI->lang->line('profiler_headers')
+ .'&nbsp;&nbsp;(<span style="cursor: pointer;" onclick="var s=document.getElementById(\'ci_profiler_httpheaders_table\').style;s.display=s.display==\'none\'?\'\':\'none\';this.innerHTML=this.innerHTML==\''.$this->CI->lang->line('profiler_section_show').'\'?\''.$this->CI->lang->line('profiler_section_hide').'\':\''.$this->CI->lang->line('profiler_section_show').'\';">'.$this->CI->lang->line('profiler_section_show')."</span>)</legend>\n\n\n"
+ .'<table style="width:100%;display:none;" id="ci_profiler_httpheaders_table">'."\n";
+
+ foreach (array('HTTP_ACCEPT', 'HTTP_USER_AGENT', 'HTTP_CONNECTION', 'SERVER_PORT', 'SERVER_NAME', 'REMOTE_ADDR', 'SERVER_SOFTWARE', 'HTTP_ACCEPT_LANGUAGE', 'SCRIPT_NAME', 'REQUEST_METHOD',' HTTP_HOST', 'REMOTE_HOST', 'CONTENT_TYPE', 'SERVER_PROTOCOL', 'QUERY_STRING', 'HTTP_ACCEPT_ENCODING', 'HTTP_X_FORWARDED_FOR', 'HTTP_DNT') as $header)
{
- $val = (isset($_SERVER[$header])) ? $_SERVER[$header] : '';
- $output .= "<tr><td style='vertical-align: top;width:50%;padding:5px;color:#900;background-color:#ddd;'>".$header."&nbsp;&nbsp;</td><td style='width:50%;padding:5px;color:#000;background-color:#ddd;'>".$val."</td></tr>\n";
+ $val = isset($_SERVER[$header]) ? htmlspecialchars($_SERVER[$header], ENT_QUOTES, config_item('charset')) : '';
+ $output .= '<tr><td style="vertical-align:top;width:50%;padding:5px;color:#900;background-color:#ddd;">'
+ .$header.'&nbsp;&nbsp;</td><td style="width:50%;padding:5px;color:#000;background-color:#ddd;">'.$val."</td></tr>\n";
}
- $output .= "</table>\n";
- $output .= "</fieldset>";
-
- return $output;
+ return $output."</table>\n</fieldset>";
}
// --------------------------------------------------------------------
@@ -462,28 +477,30 @@ class CI_Profiler {
*/
protected function _compile_config()
{
- $output = "\n\n";
- $output .= '<fieldset id="ci_profiler_config" style="border:1px solid #000;padding:6px 10px 10px 10px;margin:20px 0 20px 0;background-color:#eee">';
- $output .= "\n";
- $output .= '<legend style="color:#000;">&nbsp;&nbsp;'.$this->CI->lang->line('profiler_config').'&nbsp;&nbsp;(<span style="cursor: pointer;" onclick="var s=document.getElementById(\'ci_profiler_config_table\').style;s.display=s.display==\'none\'?\'\':\'none\';this.innerHTML=this.innerHTML==\''.$this->CI->lang->line('profiler_section_show').'\'?\''.$this->CI->lang->line('profiler_section_hide').'\':\''.$this->CI->lang->line('profiler_section_show').'\';">'.$this->CI->lang->line('profiler_section_show').'</span>)</legend>';
- $output .= "\n";
+ $output = "\n\n"
+ .'<fieldset id="ci_profiler_config" style="border:1px solid #000;padding:6px 10px 10px 10px;margin:20px 0 20px 0;background-color:#eee;">'
+ ."\n"
+ .'<legend style="color:#000;">&nbsp;&nbsp;'.$this->CI->lang->line('profiler_config').'&nbsp;&nbsp;(<span style="cursor: pointer;" onclick="var s=document.getElementById(\'ci_profiler_config_table\').style;s.display=s.display==\'none\'?\'\':\'none\';this.innerHTML=this.innerHTML==\''.$this->CI->lang->line('profiler_section_show').'\'?\''.$this->CI->lang->line('profiler_section_hide').'\':\''.$this->CI->lang->line('profiler_section_show').'\';">'.$this->CI->lang->line('profiler_section_show')."</span>)</legend>\n\n\n"
+ .'<table style="width:100%;display:none;" id="ci_profiler_config_table">'."\n";
- $output .= "\n\n<table style='width:100%; display:none' id='ci_profiler_config_table'>\n";
-
- foreach ($this->CI->config->config as $config=>$val)
+ foreach ($this->CI->config->config as $config => $val)
{
- if (is_array($val))
+ $pre = '';
+ $pre_close = '';
+
+ if (is_array($val) OR is_object($val))
{
$val = print_r($val, TRUE);
+
+ $pre = '<pre>' ;
+ $pre_close = '</pre>';
}
- $output .= "<tr><td style='padding:5px; vertical-align: top;color:#900;background-color:#ddd;'>".$config."&nbsp;&nbsp;</td><td style='padding:5px; color:#000;background-color:#ddd;'>".htmlspecialchars($val)."</td></tr>\n";
+ $output .= '<tr><td style="padding:5px;vertical-align:top;color:#900;background-color:#ddd;">'
+ .$config.'&nbsp;&nbsp;</td><td style="padding:5px;color:#000;background-color:#ddd;">'.$pre.htmlspecialchars((string) $val, ENT_QUOTES, config_item('charset')).$pre_close."</td></tr>\n";
}
- $output .= "</table>\n";
- $output .= "</fieldset>";
-
- return $output;
+ return $output."</table>\n</fieldset>";
}
// --------------------------------------------------------------------
@@ -493,30 +510,35 @@ class CI_Profiler {
*
* @return string
*/
- private function _compile_session_data()
+ protected function _compile_session_data()
{
if ( ! isset($this->CI->session))
{
return;
}
- $output = '<fieldset id="ci_profiler_csession" style="border:1px solid #000;padding:6px 10px 10px 10px;margin:20px 0 20px 0;background-color:#eee">';
- $output .= '<legend style="color:#000;">&nbsp;&nbsp;'.$this->CI->lang->line('profiler_session_data').'&nbsp;&nbsp;(<span style="cursor: pointer;" onclick="var s=document.getElementById(\'ci_profiler_session_data\').style;s.display=s.display==\'none\'?\'\':\'none\';this.innerHTML=this.innerHTML==\''.$this->CI->lang->line('profiler_section_show').'\'?\''.$this->CI->lang->line('profiler_section_hide').'\':\''.$this->CI->lang->line('profiler_section_show').'\';">'.$this->CI->lang->line('profiler_section_show').'</span>)</legend>';
- $output .= "<table style='width:100%;display:none' id='ci_profiler_session_data'>";
+ $output = '<fieldset id="ci_profiler_csession" style="border:1px solid #000;padding:6px 10px 10px 10px;margin:20px 0 20px 0;background-color:#eee;">'
+ .'<legend style="color:#000;">&nbsp;&nbsp;'.$this->CI->lang->line('profiler_session_data').'&nbsp;&nbsp;(<span style="cursor: pointer;" onclick="var s=document.getElementById(\'ci_profiler_session_data\').style;s.display=s.display==\'none\'?\'\':\'none\';this.innerHTML=this.innerHTML==\''.$this->CI->lang->line('profiler_section_show').'\'?\''.$this->CI->lang->line('profiler_section_hide').'\':\''.$this->CI->lang->line('profiler_section_show').'\';">'.$this->CI->lang->line('profiler_section_show').'</span>)</legend>'
+ .'<table style="width:100%;display:none;" id="ci_profiler_session_data">';
- foreach ($this->CI->session->all_userdata() as $key => $val)
+ foreach ($this->CI->session->userdata() as $key => $val)
{
+ $pre = '';
+ $pre_close = '';
+
if (is_array($val) OR is_object($val))
{
$val = print_r($val, TRUE);
+
+ $pre = '<pre>' ;
+ $pre_close = '</pre>';
}
- $output .= "<tr><td style='padding:5px; vertical-align: top;color:#900;background-color:#ddd;'>".$key."&nbsp;&nbsp;</td><td style='padding:5px; color:#000;background-color:#ddd;'>".htmlspecialchars($val)."</td></tr>\n";
+ $output .= '<tr><td style="padding:5px;vertical-align:top;color:#900;background-color:#ddd;">'
+ .$key.'&nbsp;&nbsp;</td><td style="padding:5px;color:#000;background-color:#ddd;">'.$pre.htmlspecialchars((string) $val, ENT_QUOTES, config_item('charset')).$pre_close."</td></tr>\n";
}
- $output .= '</table>';
- $output .= "</fieldset>";
- return $output;
+ return $output."</table>\n</fieldset>";
}
// --------------------------------------------------------------------
@@ -528,31 +550,26 @@ class CI_Profiler {
*/
public function run()
{
- $output = "<div id='codeigniter_profiler' style='clear:both;background-color:#fff;padding:10px;'>";
+ $output = '<div id="codeigniter_profiler" style="clear:both;background-color:#fff;padding:10px;">';
$fields_displayed = 0;
foreach ($this->_available_sections as $section)
{
- if ($this->_compile_{$section} !== FALSE)
+ if ($this->{'_compile_'.$section} !== FALSE)
{
- $func = "_compile_{$section}";
+ $func = '_compile_'.$section;
$output .= $this->{$func}();
$fields_displayed++;
}
}
- if ($fields_displayed == 0)
+ if ($fields_displayed === 0)
{
- $output .= '<p style="border:1px solid #5a0099;padding:10px;margin:20px 0;background-color:#eee">'.$this->CI->lang->line('profiler_no_profiles').'</p>';
+ $output .= '<p style="border:1px solid #5a0099;padding:10px;margin:20px 0;background-color:#eee;">'
+ .$this->CI->lang->line('profiler_no_profiles').'</p>';
}
- $output .= '</div>';
-
- return $output;
+ return $output.'</div>';
}
-}
-
-// END CI_Profiler class
-/* End of file Profiler.php */
-/* Location: ./system/libraries/Profiler.php */ \ No newline at end of file
+}
diff --git a/system/libraries/Session.php b/system/libraries/Session.php
deleted file mode 100644
index 5f4f60547..000000000
--- a/system/libraries/Session.php
+++ /dev/null
@@ -1,793 +0,0 @@
-<?php if ( ! defined('BASEPATH')) exit('No direct script access allowed');
-/**
- * CodeIgniter
- *
- * An open source application development framework for PHP 5.1.6 or newer
- *
- * @package CodeIgniter
- * @author ExpressionEngine Dev Team
- * @copyright Copyright (c) 2008 - 2014, EllisLab, Inc.
- * @license http://codeigniter.com/user_guide/license.html
- * @link http://codeigniter.com
- * @since Version 1.0
- * @filesource
- */
-
-// ------------------------------------------------------------------------
-
-/**
- * Session Class
- *
- * @package CodeIgniter
- * @subpackage Libraries
- * @category Sessions
- * @author ExpressionEngine Dev Team
- * @link http://codeigniter.com/user_guide/libraries/sessions.html
- */
-class CI_Session {
-
- var $sess_encrypt_cookie = FALSE;
- var $sess_use_database = FALSE;
- var $sess_table_name = '';
- var $sess_expiration = 7200;
- var $sess_expire_on_close = FALSE;
- var $sess_match_ip = FALSE;
- var $sess_match_useragent = TRUE;
- var $sess_cookie_name = 'ci_session';
- var $cookie_prefix = '';
- var $cookie_path = '';
- var $cookie_domain = '';
- var $cookie_secure = FALSE;
- var $sess_time_to_update = 300;
- var $encryption_key = '';
- var $flashdata_key = 'flash';
- var $time_reference = 'time';
- var $gc_probability = 5;
- var $userdata = array();
- var $CI;
- var $now;
-
- /**
- * Session Constructor
- *
- * The constructor runs the session routines automatically
- * whenever the class is instantiated.
- */
- public function __construct($params = array())
- {
- log_message('debug', "Session Class Initialized");
-
- // Set the super object to a local variable for use throughout the class
- $this->CI =& get_instance();
-
- // Set all the session preferences, which can either be set
- // manually via the $params array above or via the config file
- foreach (array('sess_encrypt_cookie', 'sess_use_database', 'sess_table_name', 'sess_expiration', 'sess_expire_on_close', 'sess_match_ip', 'sess_match_useragent', 'sess_cookie_name', 'cookie_path', 'cookie_domain', 'cookie_secure', 'sess_time_to_update', 'time_reference', 'cookie_prefix', 'encryption_key') as $key)
- {
- $this->$key = (isset($params[$key])) ? $params[$key] : $this->CI->config->item($key);
- }
-
- if ($this->encryption_key == '')
- {
- show_error('In order to use the Session class you are required to set an encryption key in your config file.');
- }
-
- // Load the string helper so we can use the strip_slashes() function
- $this->CI->load->helper('string');
-
- // Do we need encryption? If so, load the encryption class
- if ($this->sess_encrypt_cookie == TRUE)
- {
- $this->CI->load->library('encrypt');
- }
-
- // Are we using a database? If so, load it
- if ($this->sess_use_database === TRUE AND $this->sess_table_name != '')
- {
- $this->CI->load->database();
- }
-
- // Set the "now" time. Can either be GMT or server time, based on the
- // config prefs. We use this to set the "last activity" time
- $this->now = $this->_get_time();
-
- // Set the session length. If the session expiration is
- // set to zero we'll set the expiration two years from now.
- if ($this->sess_expiration == 0)
- {
- $this->sess_expiration = (60*60*24*365*2);
- }
-
- // Set the cookie name
- $this->sess_cookie_name = $this->cookie_prefix.$this->sess_cookie_name;
-
- // Run the Session routine. If a session doesn't exist we'll
- // create a new one. If it does, we'll update it.
- if ( ! $this->sess_read())
- {
- $this->sess_create();
- }
- else
- {
- $this->sess_update();
- }
-
- // Delete 'old' flashdata (from last request)
- $this->_flashdata_sweep();
-
- // Mark all new flashdata as old (data will be deleted before next request)
- $this->_flashdata_mark();
-
- // Delete expired sessions if necessary
- $this->_sess_gc();
-
- log_message('debug', "Session routines successfully run");
- }
-
- // --------------------------------------------------------------------
-
- /**
- * Fetch the current session data if it exists
- *
- * @access public
- * @return bool
- */
- function sess_read()
- {
- // Fetch the cookie
- $session = $this->CI->input->cookie($this->sess_cookie_name);
-
- // No cookie? Goodbye cruel world!...
- if ($session === FALSE)
- {
- log_message('debug', 'A session cookie was not found.');
- return FALSE;
- }
-
- // HMAC authentication
- $len = strlen($session) - 40;
-
- if ($len <= 0)
- {
- log_message('error', 'Session: The session cookie was not signed.');
- return FALSE;
- }
-
- // Check cookie authentication
- $hmac = substr($session, $len);
- $session = substr($session, 0, $len);
-
- // Time-attack-safe comparison
- $hmac_check = hash_hmac('sha1', $session, $this->encryption_key);
- $diff = 0;
-
- for ($i = 0; $i < 40; $i++)
- {
- $xor = ord($hmac[$i]) ^ ord($hmac_check[$i]);
- $diff |= $xor;
- }
-
- if ($diff !== 0)
- {
- log_message('error', 'Session: HMAC mismatch. The session cookie data did not match what was expected.');
- $this->sess_destroy();
- return FALSE;
- }
-
- // Decrypt the cookie data
- if ($this->sess_encrypt_cookie == TRUE)
- {
- $session = $this->CI->encrypt->decode($session);
- }
-
- // Unserialize the session array
- $session = $this->_unserialize($session);
-
- // Is the session data we unserialized an array with the correct format?
- if ( ! is_array($session) OR ! isset($session['session_id']) OR ! isset($session['ip_address']) OR ! isset($session['user_agent']) OR ! isset($session['last_activity']))
- {
- $this->sess_destroy();
- return FALSE;
- }
-
- // Is the session current?
- if (($session['last_activity'] + $this->sess_expiration) < $this->now)
- {
- $this->sess_destroy();
- return FALSE;
- }
-
- // Does the IP Match?
- if ($this->sess_match_ip == TRUE AND $session['ip_address'] != $this->CI->input->ip_address())
- {
- $this->sess_destroy();
- return FALSE;
- }
-
- // Does the User Agent Match?
- if ($this->sess_match_useragent == TRUE AND trim($session['user_agent']) != trim(substr($this->CI->input->user_agent(), 0, 120)))
- {
- $this->sess_destroy();
- return FALSE;
- }
-
- // Is there a corresponding session in the DB?
- if ($this->sess_use_database === TRUE)
- {
- $this->CI->db->where('session_id', $session['session_id']);
-
- if ($this->sess_match_ip == TRUE)
- {
- $this->CI->db->where('ip_address', $session['ip_address']);
- }
-
- if ($this->sess_match_useragent == TRUE)
- {
- $this->CI->db->where('user_agent', $session['user_agent']);
- }
-
- $query = $this->CI->db->get($this->sess_table_name);
-
- // No result? Kill it!
- if ($query->num_rows() == 0)
- {
- $this->sess_destroy();
- return FALSE;
- }
-
- // Is there custom data? If so, add it to the main session array
- $row = $query->row();
- if (isset($row->user_data) AND $row->user_data != '')
- {
- $custom_data = $this->_unserialize($row->user_data);
-
- if (is_array($custom_data))
- {
- foreach ($custom_data as $key => $val)
- {
- $session[$key] = $val;
- }
- }
- }
- }
-
- // Session is valid!
- $this->userdata = $session;
- unset($session);
-
- return TRUE;
- }
-
- // --------------------------------------------------------------------
-
- /**
- * Write the session data
- *
- * @access public
- * @return void
- */
- function sess_write()
- {
- // Are we saving custom data to the DB? If not, all we do is update the cookie
- if ($this->sess_use_database === FALSE)
- {
- $this->_set_cookie();
- return;
- }
-
- // set the custom userdata, the session data we will set in a second
- $custom_userdata = $this->userdata;
- $cookie_userdata = array();
-
- // Before continuing, we need to determine if there is any custom data to deal with.
- // Let's determine this by removing the default indexes to see if there's anything left in the array
- // and set the session data while we're at it
- foreach (array('session_id','ip_address','user_agent','last_activity') as $val)
- {
- unset($custom_userdata[$val]);
- $cookie_userdata[$val] = $this->userdata[$val];
- }
-
- // Did we find any custom data? If not, we turn the empty array into a string
- // since there's no reason to serialize and store an empty array in the DB
- if (count($custom_userdata) === 0)
- {
- $custom_userdata = '';
- }
- else
- {
- // Serialize the custom data array so we can store it
- $custom_userdata = $this->_serialize($custom_userdata);
- }
-
- // Run the update query
- $this->CI->db->where('session_id', $this->userdata['session_id']);
- $this->CI->db->update($this->sess_table_name, array('last_activity' => $this->userdata['last_activity'], 'user_data' => $custom_userdata));
-
- // Write the cookie. Notice that we manually pass the cookie data array to the
- // _set_cookie() function. Normally that function will store $this->userdata, but
- // in this case that array contains custom data, which we do not want in the cookie.
- $this->_set_cookie($cookie_userdata);
- }
-
- // --------------------------------------------------------------------
-
- /**
- * Create a new session
- *
- * @access public
- * @return void
- */
- function sess_create()
- {
- $sessid = '';
- while (strlen($sessid) < 32)
- {
- $sessid .= mt_rand(0, mt_getrandmax());
- }
-
- // To make the session ID even more secure we'll combine it with the user's IP
- $sessid .= $this->CI->input->ip_address();
-
- $this->userdata = array(
- 'session_id' => md5(uniqid($sessid, TRUE)),
- 'ip_address' => $this->CI->input->ip_address(),
- 'user_agent' => substr($this->CI->input->user_agent(), 0, 120),
- 'last_activity' => $this->now,
- 'user_data' => ''
- );
-
-
- // Save the data to the DB if needed
- if ($this->sess_use_database === TRUE)
- {
- $this->CI->db->query($this->CI->db->insert_string($this->sess_table_name, $this->userdata));
- }
-
- // Write the cookie
- $this->_set_cookie();
- }
-
- // --------------------------------------------------------------------
-
- /**
- * Update an existing session
- *
- * @access public
- * @return void
- */
- function sess_update()
- {
- // We only update the session every five minutes by default
- if (($this->userdata['last_activity'] + $this->sess_time_to_update) >= $this->now)
- {
- return;
- }
-
- // Save the old session id so we know which record to
- // update in the database if we need it
- $old_sessid = $this->userdata['session_id'];
- $new_sessid = '';
- while (strlen($new_sessid) < 32)
- {
- $new_sessid .= mt_rand(0, mt_getrandmax());
- }
-
- // To make the session ID even more secure we'll combine it with the user's IP
- $new_sessid .= $this->CI->input->ip_address();
-
- // Turn it into a hash
- $new_sessid = md5(uniqid($new_sessid, TRUE));
-
- // Update the session data in the session data array
- $this->userdata['session_id'] = $new_sessid;
- $this->userdata['last_activity'] = $this->now;
-
- // _set_cookie() will handle this for us if we aren't using database sessions
- // by pushing all userdata to the cookie.
- $cookie_data = NULL;
-
- // Update the session ID and last_activity field in the DB if needed
- if ($this->sess_use_database === TRUE)
- {
- // set cookie explicitly to only have our session data
- $cookie_data = array();
- foreach (array('session_id','ip_address','user_agent','last_activity') as $val)
- {
- $cookie_data[$val] = $this->userdata[$val];
- }
-
- $this->CI->db->query($this->CI->db->update_string($this->sess_table_name, array('last_activity' => $this->now, 'session_id' => $new_sessid), array('session_id' => $old_sessid)));
- }
-
- // Write the cookie
- $this->_set_cookie($cookie_data);
- }
-
- // --------------------------------------------------------------------
-
- /**
- * Destroy the current session
- *
- * @access public
- * @return void
- */
- function sess_destroy()
- {
- // Kill the session DB row
- if ($this->sess_use_database === TRUE && isset($this->userdata['session_id']))
- {
- $this->CI->db->where('session_id', $this->userdata['session_id']);
- $this->CI->db->delete($this->sess_table_name);
- }
-
- // Kill the cookie
- setcookie(
- $this->sess_cookie_name,
- addslashes(serialize(array())),
- ($this->now - 31500000),
- $this->cookie_path,
- $this->cookie_domain,
- 0
- );
-
- // Kill session data
- $this->userdata = array();
- }
-
- // --------------------------------------------------------------------
-
- /**
- * Fetch a specific item from the session array
- *
- * @access public
- * @param string
- * @return string
- */
- function userdata($item)
- {
- return ( ! isset($this->userdata[$item])) ? FALSE : $this->userdata[$item];
- }
-
- // --------------------------------------------------------------------
-
- /**
- * Fetch all session data
- *
- * @access public
- * @return array
- */
- function all_userdata()
- {
- return $this->userdata;
- }
-
- // --------------------------------------------------------------------
-
- /**
- * Add or change data in the "userdata" array
- *
- * @access public
- * @param mixed
- * @param string
- * @return void
- */
- function set_userdata($newdata = array(), $newval = '')
- {
- if (is_string($newdata))
- {
- $newdata = array($newdata => $newval);
- }
-
- if (count($newdata) > 0)
- {
- foreach ($newdata as $key => $val)
- {
- $this->userdata[$key] = $val;
- }
- }
-
- $this->sess_write();
- }
-
- // --------------------------------------------------------------------
-
- /**
- * Delete a session variable from the "userdata" array
- *
- * @access array
- * @return void
- */
- function unset_userdata($newdata = array())
- {
- if (is_string($newdata))
- {
- $newdata = array($newdata => '');
- }
-
- if (count($newdata) > 0)
- {
- foreach ($newdata as $key => $val)
- {
- unset($this->userdata[$key]);
- }
- }
-
- $this->sess_write();
- }
-
- // ------------------------------------------------------------------------
-
- /**
- * Add or change flashdata, only available
- * until the next request
- *
- * @access public
- * @param mixed
- * @param string
- * @return void
- */
- function set_flashdata($newdata = array(), $newval = '')
- {
- if (is_string($newdata))
- {
- $newdata = array($newdata => $newval);
- }
-
- if (count($newdata) > 0)
- {
- foreach ($newdata as $key => $val)
- {
- $flashdata_key = $this->flashdata_key.':new:'.$key;
- $this->set_userdata($flashdata_key, $val);
- }
- }
- }
-
- // ------------------------------------------------------------------------
-
- /**
- * Keeps existing flashdata available to next request.
- *
- * @access public
- * @param string
- * @return void
- */
- function keep_flashdata($key)
- {
- // 'old' flashdata gets removed. Here we mark all
- // flashdata as 'new' to preserve it from _flashdata_sweep()
- // Note the function will return FALSE if the $key
- // provided cannot be found
- $old_flashdata_key = $this->flashdata_key.':old:'.$key;
- $value = $this->userdata($old_flashdata_key);
-
- $new_flashdata_key = $this->flashdata_key.':new:'.$key;
- $this->set_userdata($new_flashdata_key, $value);
- }
-
- // ------------------------------------------------------------------------
-
- /**
- * Fetch a specific flashdata item from the session array
- *
- * @access public
- * @param string
- * @return string
- */
- function flashdata($key)
- {
- $flashdata_key = $this->flashdata_key.':old:'.$key;
- return $this->userdata($flashdata_key);
- }
-
- // ------------------------------------------------------------------------
-
- /**
- * Identifies flashdata as 'old' for removal
- * when _flashdata_sweep() runs.
- *
- * @access private
- * @return void
- */
- function _flashdata_mark()
- {
- $userdata = $this->all_userdata();
- foreach ($userdata as $name => $value)
- {
- $parts = explode(':new:', $name);
- if (is_array($parts) && count($parts) === 2)
- {
- $new_name = $this->flashdata_key.':old:'.$parts[1];
- $this->set_userdata($new_name, $value);
- $this->unset_userdata($name);
- }
- }
- }
-
- // ------------------------------------------------------------------------
-
- /**
- * Removes all flashdata marked as 'old'
- *
- * @access private
- * @return void
- */
-
- function _flashdata_sweep()
- {
- $userdata = $this->all_userdata();
- foreach ($userdata as $key => $value)
- {
- if (strpos($key, ':old:'))
- {
- $this->unset_userdata($key);
- }
- }
-
- }
-
- // --------------------------------------------------------------------
-
- /**
- * Get the "now" time
- *
- * @access private
- * @return string
- */
- function _get_time()
- {
- if (strtolower($this->time_reference) == 'gmt')
- {
- $now = time();
- $time = mktime(gmdate("H", $now), gmdate("i", $now), gmdate("s", $now), gmdate("m", $now), gmdate("d", $now), gmdate("Y", $now));
- }
- else
- {
- $time = time();
- }
-
- return $time;
- }
-
- // --------------------------------------------------------------------
-
- /**
- * Write the session cookie
- *
- * @access public
- * @return void
- */
- function _set_cookie($cookie_data = NULL)
- {
- if (is_null($cookie_data))
- {
- $cookie_data = $this->userdata;
- }
-
- // Serialize the userdata for the cookie
- $cookie_data = $this->_serialize($cookie_data);
-
- if ($this->sess_encrypt_cookie == TRUE)
- {
- $cookie_data = $this->CI->encrypt->encode($cookie_data);
- }
-
- $cookie_data .= hash_hmac('sha1', $cookie_data, $this->encryption_key);
-
- $expire = ($this->sess_expire_on_close === TRUE) ? 0 : $this->sess_expiration + time();
-
- // Set the cookie
- setcookie(
- $this->sess_cookie_name,
- $cookie_data,
- $expire,
- $this->cookie_path,
- $this->cookie_domain,
- $this->cookie_secure
- );
- }
-
- // --------------------------------------------------------------------
-
- /**
- * Serialize an array
- *
- * This function first converts any slashes found in the array to a temporary
- * marker, so when it gets unserialized the slashes will be preserved
- *
- * @access private
- * @param array
- * @return string
- */
- function _serialize($data)
- {
- if (is_array($data))
- {
- foreach ($data as $key => $val)
- {
- if (is_string($val))
- {
- $data[$key] = str_replace('\\', '{{slash}}', $val);
- }
- }
- }
- else
- {
- if (is_string($data))
- {
- $data = str_replace('\\', '{{slash}}', $data);
- }
- }
-
- return serialize($data);
- }
-
- // --------------------------------------------------------------------
-
- /**
- * Unserialize
- *
- * This function unserializes a data string, then converts any
- * temporary slash markers back to actual slashes
- *
- * @access private
- * @param array
- * @return string
- */
- function _unserialize($data)
- {
- $data = @unserialize(strip_slashes($data));
-
- if (is_array($data))
- {
- foreach ($data as $key => $val)
- {
- if (is_string($val))
- {
- $data[$key] = str_replace('{{slash}}', '\\', $val);
- }
- }
-
- return $data;
- }
-
- return (is_string($data)) ? str_replace('{{slash}}', '\\', $data) : $data;
- }
-
- // --------------------------------------------------------------------
-
- /**
- * Garbage collection
- *
- * This deletes expired session rows from database
- * if the probability percentage is met
- *
- * @access public
- * @return void
- */
- function _sess_gc()
- {
- if ($this->sess_use_database != TRUE)
- {
- return;
- }
-
- srand(time());
- if ((rand() % 100) < $this->gc_probability)
- {
- $expire = $this->now - $this->sess_expiration;
-
- $this->CI->db->where("last_activity < {$expire}");
- $this->CI->db->delete($this->sess_table_name);
-
- log_message('debug', 'Session garbage collection performed.');
- }
- }
-
-
-}
-// END Session Class
-
-/* End of file Session.php */
-/* Location: ./system/libraries/Session.php */
diff --git a/system/libraries/Session/CI_Session_driver_interface.php b/system/libraries/Session/CI_Session_driver_interface.php
new file mode 100644
index 000000000..23a0dfd53
--- /dev/null
+++ b/system/libraries/Session/CI_Session_driver_interface.php
@@ -0,0 +1,60 @@
+<?php
+/**
+ * CodeIgniter
+ *
+ * An open source application development framework for PHP
+ *
+ * This content is released under the MIT License (MIT)
+ *
+ * Copyright (c) 2022, CodeIgniter Foundation
+ *
+ * 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.
+ *
+ * @package CodeIgniter
+ * @author EllisLab Dev Team
+ * @copyright Copyright (c) 2022, CodeIgniter Foundation (https://codeigniter.com/)
+ * @license https://opensource.org/licenses/MIT MIT License
+ * @link https://codeigniter.com
+ * @since Version 3.0.0
+ * @filesource
+ */
+defined('BASEPATH') OR exit('No direct script access allowed');
+
+/**
+ * CI_Session_driver_interface
+ *
+ * A compatibility typeless SessionHandlerInterface alias
+ *
+ * @package CodeIgniter
+ * @subpackage Libraries
+ * @category Sessions
+ * @author Andrey Andreev
+ * @link https://codeigniter.com/userguide3/libraries/sessions.html
+ */
+interface CI_Session_driver_interface {
+
+ public function open($save_path, $name);
+ public function close();
+ public function read($session_id);
+ public function write($session_id, $session_data);
+ public function destroy($session_id);
+ public function gc($maxlifetime);
+ public function updateTimestamp($session_id, $data);
+ public function validateId($session_id);
+}
diff --git a/system/libraries/Session/OldSessionWrapper.php b/system/libraries/Session/OldSessionWrapper.php
new file mode 100644
index 000000000..d013c777f
--- /dev/null
+++ b/system/libraries/Session/OldSessionWrapper.php
@@ -0,0 +1,98 @@
+<?php
+/**
+ * CodeIgniter
+ *
+ * An open source application development framework for PHP
+ *
+ * This content is released under the MIT License (MIT)
+ *
+ * Copyright (c) 2022, CodeIgniter Foundation
+ *
+ * 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.
+ *
+ * @package CodeIgniter
+ * @author EllisLab Dev Team
+ * @copyright Copyright (c) 2022, CodeIgniter Foundation (https://codeigniter.com/)
+ * @license https://opensource.org/licenses/MIT MIT License
+ * @link https://codeigniter.com
+ * @since Version 3.0.0
+ * @filesource
+ */
+defined('BASEPATH') OR exit('No direct script access allowed');
+
+/**
+ * OldSessionWrapper
+ *
+ * PHP 8 Session handler compatibility wrapper, pre-PHP8 version
+ *
+ * @package CodeIgniter
+ * @subpackage Libraries
+ * @category Sessions
+ * @author Andrey Andreev
+ * @link https://codeigniter.com/userguide3/libraries/sessions.html
+ */
+class CI_SessionWrapper implements SessionHandlerInterface, SessionUpdateTimestampHandlerInterface {
+
+ protected $driver;
+
+ public function __construct(CI_Session_driver_interface $driver)
+ {
+ $this->driver = $driver;
+ }
+
+ public function open($save_path, $name)
+ {
+ return $this->driver->open($save_path, $name);
+ }
+
+ public function close()
+ {
+ return $this->driver->close();
+ }
+
+ public function read($id)
+ {
+ return $this->driver->read($id);
+ }
+
+ public function write($id, $data)
+ {
+ return $this->driver->write($id, $data);
+ }
+
+ public function destroy($id)
+ {
+ return $this->driver->destroy($id);
+ }
+
+ public function gc($maxlifetime)
+ {
+ return $this->driver->gc($maxlifetime);
+ }
+
+ public function updateTimestamp($id, $data)
+ {
+ return $this->driver->updateTimestamp($id, $data);
+ }
+
+ public function validateId($id)
+ {
+ return $this->driver->validateId($id);
+ }
+}
diff --git a/system/libraries/Session/PHP8SessionWrapper.php b/system/libraries/Session/PHP8SessionWrapper.php
new file mode 100644
index 000000000..41889bc61
--- /dev/null
+++ b/system/libraries/Session/PHP8SessionWrapper.php
@@ -0,0 +1,100 @@
+<?php
+/**
+ * CodeIgniter
+ *
+ * An open source application development framework for PHP
+ *
+ * This content is released under the MIT License (MIT)
+ *
+ * Copyright (c) 2022, CodeIgniter Foundation
+ *
+ * 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.
+ *
+ * @package CodeIgniter
+ * @author EllisLab Dev Team
+ * @copyright Copyright (c) 2022, CodeIgniter Foundation (https://codeigniter.com/)
+ * @license https://opensource.org/licenses/MIT MIT License
+ * @link https://codeigniter.com
+ * @since Version 3.0.0
+ * @filesource
+ */
+defined('BASEPATH') OR exit('No direct script access allowed');
+
+/**
+ * PHP8SessionWrapper
+ *
+ * PHP 8 Session handler compatibility wrapper
+ *
+ * @package CodeIgniter
+ * @subpackage Libraries
+ * @category Sessions
+ * @author Andrey Andreev
+ * @link https://codeigniter.com/userguide3/libraries/sessions.html
+ */
+class CI_SessionWrapper implements SessionHandlerInterface, SessionUpdateTimestampHandlerInterface {
+
+ protected CI_Session_driver_interface $driver;
+
+ public function __construct(CI_Session_driver_interface $driver)
+ {
+ $this->driver = $driver;
+ }
+
+ public function open(string $save_path, string $name): bool
+ {
+ return $this->driver->open($save_path, $name);
+ }
+
+ public function close(): bool
+ {
+ return $this->driver->close();
+ }
+
+ #[\ReturnTypeWillChange]
+ public function read(string $id): mixed
+ {
+ return $this->driver->read($id);
+ }
+
+ public function write(string $id, string $data): bool
+ {
+ return $this->driver->write($id, $data);
+ }
+
+ public function destroy(string $id): bool
+ {
+ return $this->driver->destroy($id);
+ }
+
+ #[\ReturnTypeWillChange]
+ public function gc(int $maxlifetime): mixed
+ {
+ return $this->driver->gc($maxlifetime);
+ }
+
+ public function updateTimestamp(string $id, string$data): bool
+ {
+ return $this->driver->updateTimestamp($id, $data);
+ }
+
+ public function validateId(string $id): bool
+ {
+ return $this->driver->validateId($id);
+ }
+}
diff --git a/system/libraries/Session/Session.php b/system/libraries/Session/Session.php
new file mode 100644
index 000000000..2d55f822a
--- /dev/null
+++ b/system/libraries/Session/Session.php
@@ -0,0 +1,1030 @@
+<?php
+/**
+ * CodeIgniter
+ *
+ * An open source application development framework for PHP
+ *
+ * This content is released under the MIT License (MIT)
+ *
+ * Copyright (c) 2019 - 2022, CodeIgniter Foundation
+ *
+ * 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.
+ *
+ * @package CodeIgniter
+ * @author EllisLab Dev Team
+ * @copyright Copyright (c) 2008 - 2014, EllisLab, Inc. (https://ellislab.com/)
+ * @copyright Copyright (c) 2014 - 2019, British Columbia Institute of Technology (https://bcit.ca/)
+ * @copyright Copyright (c) 2019 - 2022, CodeIgniter Foundation (https://codeigniter.com/)
+ * @license https://opensource.org/licenses/MIT MIT License
+ * @link https://codeigniter.com
+ * @since Version 2.0.0
+ * @filesource
+ */
+defined('BASEPATH') OR exit('No direct script access allowed');
+
+/**
+ * CodeIgniter Session Class
+ *
+ * @package CodeIgniter
+ * @subpackage Libraries
+ * @category Sessions
+ * @author Andrey Andreev
+ * @link https://codeigniter.com/userguide3/libraries/sessions.html
+ */
+class CI_Session {
+
+ /**
+ * Userdata array
+ *
+ * Just a reference to $_SESSION, for BC purposes.
+ */
+ public $userdata;
+
+ protected $_driver = 'files';
+ protected $_config;
+ protected $_sid_regexp;
+
+ // ------------------------------------------------------------------------
+
+ /**
+ * Class constructor
+ *
+ * @param array $params Configuration parameters
+ * @return void
+ */
+ public function __construct(array $params = array())
+ {
+ // No sessions under CLI
+ if (is_cli())
+ {
+ log_message('debug', 'Session: Initialization under CLI aborted.');
+ return;
+ }
+ elseif ((bool) ini_get('session.auto_start'))
+ {
+ log_message('error', 'Session: session.auto_start is enabled in php.ini. Aborting.');
+ return;
+ }
+ elseif ( ! empty($params['driver']))
+ {
+ $this->_driver = $params['driver'];
+ unset($params['driver']);
+ }
+ elseif ($driver = config_item('sess_driver'))
+ {
+ $this->_driver = $driver;
+ }
+ // Note: BC workaround
+ elseif (config_item('sess_use_database'))
+ {
+ log_message('debug', 'Session: "sess_driver" is empty; using BC fallback to "sess_use_database".');
+ $this->_driver = 'database';
+ }
+
+ $class = $this->_ci_load_classes($this->_driver);
+
+ // Configuration ...
+ $this->_configure($params);
+ $this->_config['_sid_regexp'] = $this->_sid_regexp;
+
+ $class = new $class($this->_config);
+ $wrapper = new CI_SessionWrapper($class);
+ if (is_php('5.4'))
+ {
+ session_set_save_handler($wrapper, TRUE);
+ }
+ else
+ {
+ session_set_save_handler(
+ array($wrapper, 'open'),
+ array($wrapper, 'close'),
+ array($wrapper, 'read'),
+ array($wrapper, 'write'),
+ array($wrapper, 'destroy'),
+ array($wrapper, 'gc')
+ );
+
+ register_shutdown_function('session_write_close');
+ }
+
+ // Sanitize the cookie, because apparently PHP doesn't do that for userspace handlers
+ if (isset($_COOKIE[$this->_config['cookie_name']])
+ && (
+ ! is_string($_COOKIE[$this->_config['cookie_name']])
+ OR ! preg_match('#\A'.$this->_sid_regexp.'\z#', $_COOKIE[$this->_config['cookie_name']])
+ )
+ )
+ {
+ unset($_COOKIE[$this->_config['cookie_name']]);
+ }
+
+ session_start();
+
+ // Is session ID auto-regeneration configured? (ignoring ajax requests)
+ if ((empty($_SERVER['HTTP_X_REQUESTED_WITH']) OR strtolower($_SERVER['HTTP_X_REQUESTED_WITH']) !== 'xmlhttprequest')
+ && ($regenerate_time = config_item('sess_time_to_update')) > 0
+ )
+ {
+ if ( ! isset($_SESSION['__ci_last_regenerate']))
+ {
+ $_SESSION['__ci_last_regenerate'] = time();
+ }
+ elseif ($_SESSION['__ci_last_regenerate'] < (time() - $regenerate_time))
+ {
+ $this->sess_regenerate((bool) config_item('sess_regenerate_destroy'));
+ }
+ }
+ // Another work-around ... PHP doesn't seem to send the session cookie
+ // unless it is being currently created or regenerated
+ elseif (isset($_COOKIE[$this->_config['cookie_name']]) && $_COOKIE[$this->_config['cookie_name']] === session_id())
+ {
+ $expires = empty($this->_config['cookie_lifetime']) ? 0 : time() + $this->_config['cookie_lifetime'];
+ if (is_php('7.3'))
+ {
+ setcookie(
+ $this->_config['cookie_name'],
+ session_id(),
+ array(
+ 'expires' => $expires,
+ 'path' => $this->_config['cookie_path'],
+ 'domain' => $this->_config['cookie_domain'],
+ 'secure' => $this->_config['cookie_secure'],
+ 'httponly' => TRUE,
+ 'samesite' => $this->_config['cookie_samesite']
+ )
+ );
+ }
+ else
+ {
+ $header = 'Set-Cookie: '.$this->_config['cookie_name'].'='.session_id();
+ $header .= empty($expires) ? '' : '; Expires='.gmdate('D, d-M-Y H:i:s T', $expires).'; Max-Age='.$this->_config['cookie_lifetime'];
+ $header .= '; Path='.$this->_config['cookie_path'];
+ $header .= ($this->_config['cookie_domain'] !== '' ? '; Domain='.$this->_config['cookie_domain'] : '');
+ $header .= ($this->_config['cookie_secure'] ? '; Secure' : '').'; HttpOnly; SameSite='.$this->_config['cookie_samesite'];
+ header($header);
+ }
+
+ if ( ! $this->_config['cookie_secure'] && $this->_config['cookie_samesite'] === 'None')
+ {
+ log_message('error', "Session: '".$this->_config['cookie_name']."' cookie sent with SameSite=None, but without Secure attribute.'");
+ }
+ }
+
+ $this->_ci_init_vars();
+
+ log_message('info', "Session: Class initialized using '".$this->_driver."' driver.");
+ }
+
+ // ------------------------------------------------------------------------
+
+ /**
+ * CI Load Classes
+ *
+ * An internal method to load all possible dependency and extension
+ * classes. It kind of emulates the CI_Driver library, but is
+ * self-sufficient.
+ *
+ * @param string $driver Driver name
+ * @return string Driver class name
+ */
+ protected function _ci_load_classes($driver)
+ {
+ // PHP 7 compatibility
+ interface_exists('SessionUpdateTimestampHandlerInterface', FALSE) OR require_once(BASEPATH.'libraries/Session/SessionUpdateTimestampHandlerInterface.php');
+
+ require_once(BASEPATH.'libraries/Session/CI_Session_driver_interface.php');
+ $wrapper = is_php('8.0') ? 'PHP8SessionWrapper' : 'OldSessionWrapper';
+ require_once(BASEPATH.'libraries/Session/'.$wrapper.'.php');
+
+ $prefix = config_item('subclass_prefix');
+
+ if ( ! class_exists('CI_Session_driver', FALSE))
+ {
+ require_once(
+ file_exists(APPPATH.'libraries/Session/Session_driver.php')
+ ? APPPATH.'libraries/Session/Session_driver.php'
+ : BASEPATH.'libraries/Session/Session_driver.php'
+ );
+
+ if (file_exists($file_path = APPPATH.'libraries/Session/'.$prefix.'Session_driver.php'))
+ {
+ require_once($file_path);
+ }
+ }
+
+ $class = 'Session_'.$driver.'_driver';
+
+ // Allow custom drivers without the CI_ or MY_ prefix
+ if ( ! class_exists($class, FALSE) && file_exists($file_path = APPPATH.'libraries/Session/drivers/'.$class.'.php'))
+ {
+ require_once($file_path);
+ if (class_exists($class, FALSE))
+ {
+ return $class;
+ }
+ }
+
+ if ( ! class_exists('CI_'.$class, FALSE))
+ {
+ if (file_exists($file_path = APPPATH.'libraries/Session/drivers/'.$class.'.php') OR file_exists($file_path = BASEPATH.'libraries/Session/drivers/'.$class.'.php'))
+ {
+ require_once($file_path);
+ }
+
+ if ( ! class_exists('CI_'.$class, FALSE) && ! class_exists($class, FALSE))
+ {
+ throw new UnexpectedValueException("Session: Configured driver '".$driver."' was not found. Aborting.");
+ }
+ }
+
+ if ( ! class_exists($prefix.$class, FALSE) && file_exists($file_path = APPPATH.'libraries/Session/drivers/'.$prefix.$class.'.php'))
+ {
+ require_once($file_path);
+ if (class_exists($prefix.$class, FALSE))
+ {
+ return $prefix.$class;
+ }
+
+ log_message('debug', 'Session: '.$prefix.$class.".php found but it doesn't declare class ".$prefix.$class.'.');
+ }
+
+ return 'CI_'.$class;
+ }
+
+ // ------------------------------------------------------------------------
+
+ /**
+ * Configuration
+ *
+ * Handle input parameters and configuration defaults
+ *
+ * @param array &$params Input parameters
+ * @return void
+ */
+ protected function _configure(&$params)
+ {
+ $expiration = config_item('sess_expiration');
+
+ if (isset($params['cookie_lifetime']))
+ {
+ $params['cookie_lifetime'] = (int) $params['cookie_lifetime'];
+ }
+ else
+ {
+ $params['cookie_lifetime'] = ( ! isset($expiration) && config_item('sess_expire_on_close'))
+ ? 0 : (int) $expiration;
+ }
+
+ isset($params['cookie_name']) OR $params['cookie_name'] = config_item('sess_cookie_name');
+ if (empty($params['cookie_name']))
+ {
+ $params['cookie_name'] = ini_get('session.name');
+ }
+ else
+ {
+ ini_set('session.name', $params['cookie_name']);
+ }
+
+ isset($params['cookie_path']) OR $params['cookie_path'] = config_item('cookie_path');
+ isset($params['cookie_domain']) OR $params['cookie_domain'] = config_item('cookie_domain');
+ isset($params['cookie_secure']) OR $params['cookie_secure'] = (bool) config_item('cookie_secure');
+
+ isset($params['cookie_samesite']) OR $params['cookie_samesite'] = config_item('sess_samesite');
+ if ( ! isset($params['cookie_samesite']) && is_php('7.3'))
+ {
+ $params['cookie_samesite'] = ini_get('session.cookie_samesite');
+ }
+
+ if (isset($params['cookie_samesite']))
+ {
+ $params['cookie_samesite'] = ucfirst(strtolower($params['cookie_samesite']));
+ in_array($params['cookie_samesite'], array('Lax', 'Strict', 'None'), TRUE) OR $params['cookie_samesite'] = 'Lax';
+ }
+ else
+ {
+ $params['cookie_samesite'] = 'Lax';
+ }
+
+ if (is_php('7.3'))
+ {
+ session_set_cookie_params(array(
+ 'lifetime' => $params['cookie_lifetime'],
+ 'path' => $params['cookie_path'],
+ 'domain' => $params['cookie_domain'],
+ 'secure' => $params['cookie_secure'],
+ 'httponly' => TRUE,
+ 'samesite' => $params['cookie_samesite']
+ ));
+ }
+ else
+ {
+ session_set_cookie_params(
+ $params['cookie_lifetime'],
+ $params['cookie_path'].'; SameSite='.$params['cookie_samesite'],
+ $params['cookie_domain'],
+ $params['cookie_secure'],
+ TRUE // HttpOnly; Yes, this is intentional and not configurable for security reasons
+ );
+ }
+
+ if (empty($expiration))
+ {
+ $params['expiration'] = (int) ini_get('session.gc_maxlifetime');
+ }
+ else
+ {
+ $params['expiration'] = (int) $expiration;
+ ini_set('session.gc_maxlifetime', $expiration);
+ }
+
+ $params['match_ip'] = (bool) (isset($params['match_ip']) ? $params['match_ip'] : config_item('sess_match_ip'));
+
+ isset($params['save_path']) OR $params['save_path'] = config_item('sess_save_path');
+
+ $this->_config = $params;
+
+ // Security is king
+ ini_set('session.use_trans_sid', 0);
+ ini_set('session.use_strict_mode', 1);
+ ini_set('session.use_cookies', 1);
+ ini_set('session.use_only_cookies', 1);
+
+ $this->_configure_sid_length();
+ }
+
+ // ------------------------------------------------------------------------
+
+ /**
+ * Configure session ID length
+ *
+ * To make life easier, we used to force SHA-1 and 4 bits per
+ * character on everyone. And of course, someone was unhappy.
+ *
+ * Then PHP 7.1 broke backwards-compatibility because ext/session
+ * is such a mess that nobody wants to touch it with a pole stick,
+ * and the one guy who does, nobody has the energy to argue with.
+ *
+ * So we were forced to make changes, and OF COURSE something was
+ * going to break and now we have this pile of shit. -- Narf
+ *
+ * @return void
+ */
+ protected function _configure_sid_length()
+ {
+ if (PHP_VERSION_ID < 70100)
+ {
+ $hash_function = ini_get('session.hash_function');
+ if (ctype_digit($hash_function))
+ {
+ if ($hash_function !== '1')
+ {
+ ini_set('session.hash_function', 1);
+ }
+
+ $bits = 160;
+ }
+ elseif ( ! in_array($hash_function, hash_algos(), TRUE))
+ {
+ ini_set('session.hash_function', 1);
+ $bits = 160;
+ }
+ elseif (($bits = strlen(hash($hash_function, 'dummy', false)) * 4) < 160)
+ {
+ ini_set('session.hash_function', 1);
+ $bits = 160;
+ }
+
+ $bits_per_character = (int) ini_get('session.hash_bits_per_character');
+ $sid_length = (int) ceil($bits / $bits_per_character);
+ }
+ else
+ {
+ $bits_per_character = (int) ini_get('session.sid_bits_per_character');
+ $sid_length = (int) ini_get('session.sid_length');
+ if (($bits = $sid_length * $bits_per_character) < 160)
+ {
+ // Add as many more characters as necessary to reach at least 160 bits
+ $sid_length += (int) ceil((160 % $bits) / $bits_per_character);
+ ini_set('session.sid_length', $sid_length);
+ }
+ }
+
+ // Yes, 4,5,6 are the only known possible values as of 2016-10-27
+ switch ($bits_per_character)
+ {
+ case 4:
+ $this->_sid_regexp = '[0-9a-f]';
+ break;
+ case 5:
+ $this->_sid_regexp = '[0-9a-v]';
+ break;
+ case 6:
+ $this->_sid_regexp = '[0-9a-zA-Z,-]';
+ break;
+ }
+
+ $this->_sid_regexp .= '{'.$sid_length.'}';
+ }
+
+ // ------------------------------------------------------------------------
+
+ /**
+ * Handle temporary variables
+ *
+ * Clears old "flash" data, marks the new one for deletion and handles
+ * "temp" data deletion.
+ *
+ * @return void
+ */
+ protected function _ci_init_vars()
+ {
+ if ( ! empty($_SESSION['__ci_vars']))
+ {
+ $current_time = time();
+
+ foreach ($_SESSION['__ci_vars'] as $key => &$value)
+ {
+ if ($value === 'new')
+ {
+ $_SESSION['__ci_vars'][$key] = 'old';
+ }
+ elseif ($value === 'old' || $value < $current_time)
+ {
+ unset($_SESSION[$key], $_SESSION['__ci_vars'][$key]);
+ }
+ }
+
+ if (empty($_SESSION['__ci_vars']))
+ {
+ unset($_SESSION['__ci_vars']);
+ }
+ }
+
+ $this->userdata =& $_SESSION;
+ }
+
+ // ------------------------------------------------------------------------
+
+ /**
+ * Mark as flash
+ *
+ * @param mixed $key Session data key(s)
+ * @return bool
+ */
+ public function mark_as_flash($key)
+ {
+ if (is_array($key))
+ {
+ for ($i = 0, $c = count($key); $i < $c; $i++)
+ {
+ if ( ! isset($_SESSION[$key[$i]]))
+ {
+ return FALSE;
+ }
+ }
+
+ $new = array_fill_keys($key, 'new');
+
+ $_SESSION['__ci_vars'] = isset($_SESSION['__ci_vars'])
+ ? array_merge($_SESSION['__ci_vars'], $new)
+ : $new;
+
+ return TRUE;
+ }
+
+ if ( ! isset($_SESSION[$key]))
+ {
+ return FALSE;
+ }
+
+ $_SESSION['__ci_vars'][$key] = 'new';
+ return TRUE;
+ }
+
+ // ------------------------------------------------------------------------
+
+ /**
+ * Get flash keys
+ *
+ * @return array
+ */
+ public function get_flash_keys()
+ {
+ if ( ! isset($_SESSION['__ci_vars']))
+ {
+ return array();
+ }
+
+ $keys = array();
+ foreach (array_keys($_SESSION['__ci_vars']) as $key)
+ {
+ is_int($_SESSION['__ci_vars'][$key]) OR $keys[] = $key;
+ }
+
+ return $keys;
+ }
+
+ // ------------------------------------------------------------------------
+
+ /**
+ * Unmark flash
+ *
+ * @param mixed $key Session data key(s)
+ * @return void
+ */
+ public function unmark_flash($key)
+ {
+ if (empty($_SESSION['__ci_vars']))
+ {
+ return;
+ }
+
+ is_array($key) OR $key = array($key);
+
+ foreach ($key as $k)
+ {
+ if (isset($_SESSION['__ci_vars'][$k]) && ! is_int($_SESSION['__ci_vars'][$k]))
+ {
+ unset($_SESSION['__ci_vars'][$k]);
+ }
+ }
+
+ if (empty($_SESSION['__ci_vars']))
+ {
+ unset($_SESSION['__ci_vars']);
+ }
+ }
+
+ // ------------------------------------------------------------------------
+
+ /**
+ * Mark as temp
+ *
+ * @param mixed $key Session data key(s)
+ * @param int $ttl Time-to-live in seconds
+ * @return bool
+ */
+ public function mark_as_temp($key, $ttl = 300)
+ {
+ $ttl += time();
+
+ if (is_array($key))
+ {
+ $temp = array();
+
+ foreach ($key as $k => $v)
+ {
+ // Do we have a key => ttl pair, or just a key?
+ if (is_int($k))
+ {
+ $k = $v;
+ $v = $ttl;
+ }
+ else
+ {
+ $v += time();
+ }
+
+ if ( ! isset($_SESSION[$k]))
+ {
+ return FALSE;
+ }
+
+ $temp[$k] = $v;
+ }
+
+ $_SESSION['__ci_vars'] = isset($_SESSION['__ci_vars'])
+ ? array_merge($_SESSION['__ci_vars'], $temp)
+ : $temp;
+
+ return TRUE;
+ }
+
+ if ( ! isset($_SESSION[$key]))
+ {
+ return FALSE;
+ }
+
+ $_SESSION['__ci_vars'][$key] = $ttl;
+ return TRUE;
+ }
+
+ // ------------------------------------------------------------------------
+
+ /**
+ * Get temp keys
+ *
+ * @return array
+ */
+ public function get_temp_keys()
+ {
+ if ( ! isset($_SESSION['__ci_vars']))
+ {
+ return array();
+ }
+
+ $keys = array();
+ foreach (array_keys($_SESSION['__ci_vars']) as $key)
+ {
+ is_int($_SESSION['__ci_vars'][$key]) && $keys[] = $key;
+ }
+
+ return $keys;
+ }
+
+ // ------------------------------------------------------------------------
+
+ /**
+ * Unmark temp
+ *
+ * @param mixed $key Session data key(s)
+ * @return void
+ */
+ public function unmark_temp($key)
+ {
+ if (empty($_SESSION['__ci_vars']))
+ {
+ return;
+ }
+
+ is_array($key) OR $key = array($key);
+
+ foreach ($key as $k)
+ {
+ if (isset($_SESSION['__ci_vars'][$k]) && is_int($_SESSION['__ci_vars'][$k]))
+ {
+ unset($_SESSION['__ci_vars'][$k]);
+ }
+ }
+
+ if (empty($_SESSION['__ci_vars']))
+ {
+ unset($_SESSION['__ci_vars']);
+ }
+ }
+
+ // ------------------------------------------------------------------------
+
+ /**
+ * __get()
+ *
+ * @param string $key 'session_id' or a session data key
+ * @return mixed
+ */
+ public function __get($key)
+ {
+ // Note: Keep this order the same, just in case somebody wants to
+ // use 'session_id' as a session data key, for whatever reason
+ if (isset($_SESSION[$key]))
+ {
+ return $_SESSION[$key];
+ }
+ elseif ($key === 'session_id')
+ {
+ return session_id();
+ }
+
+ return NULL;
+ }
+
+ // ------------------------------------------------------------------------
+
+ /**
+ * __isset()
+ *
+ * @param string $key 'session_id' or a session data key
+ * @return bool
+ */
+ public function __isset($key)
+ {
+ if ($key === 'session_id')
+ {
+ return (session_status() === PHP_SESSION_ACTIVE);
+ }
+
+ return isset($_SESSION[$key]);
+ }
+
+ // ------------------------------------------------------------------------
+
+ /**
+ * __set()
+ *
+ * @param string $key Session data key
+ * @param mixed $value Session data value
+ * @return void
+ */
+ public function __set($key, $value)
+ {
+ $_SESSION[$key] = $value;
+ }
+
+ // ------------------------------------------------------------------------
+
+ /**
+ * Session destroy
+ *
+ * Legacy CI_Session compatibility method
+ *
+ * @return void
+ */
+ public function sess_destroy()
+ {
+ session_destroy();
+ }
+
+ // ------------------------------------------------------------------------
+
+ /**
+ * Session regenerate
+ *
+ * Legacy CI_Session compatibility method
+ *
+ * @param bool $destroy Destroy old session data flag
+ * @return void
+ */
+ public function sess_regenerate($destroy = FALSE)
+ {
+ $_SESSION['__ci_last_regenerate'] = time();
+ session_regenerate_id($destroy);
+ }
+
+ // ------------------------------------------------------------------------
+
+ /**
+ * Get userdata reference
+ *
+ * Legacy CI_Session compatibility method
+ *
+ * @return array
+ */
+ public function &get_userdata()
+ {
+ return $_SESSION;
+ }
+
+ // ------------------------------------------------------------------------
+
+ /**
+ * Userdata (fetch)
+ *
+ * Legacy CI_Session compatibility method
+ *
+ * @param string $key Session data key
+ * @return mixed Session data value or NULL if not found
+ */
+ public function userdata($key = NULL)
+ {
+ if (isset($key))
+ {
+ return isset($_SESSION[$key]) ? $_SESSION[$key] : NULL;
+ }
+ elseif (empty($_SESSION))
+ {
+ return array();
+ }
+
+ $userdata = array();
+ $_exclude = array_merge(
+ array('__ci_vars'),
+ $this->get_flash_keys(),
+ $this->get_temp_keys()
+ );
+
+ foreach (array_keys($_SESSION) as $key)
+ {
+ if ( ! in_array($key, $_exclude, TRUE))
+ {
+ $userdata[$key] = $_SESSION[$key];
+ }
+ }
+
+ return $userdata;
+ }
+
+ // ------------------------------------------------------------------------
+
+ /**
+ * Set userdata
+ *
+ * Legacy CI_Session compatibility method
+ *
+ * @param mixed $data Session data key or an associative array
+ * @param mixed $value Value to store
+ * @return void
+ */
+ public function set_userdata($data, $value = NULL)
+ {
+ if (is_array($data))
+ {
+ foreach ($data as $key => &$value)
+ {
+ $_SESSION[$key] = $value;
+ }
+
+ return;
+ }
+
+ $_SESSION[$data] = $value;
+ }
+
+ // ------------------------------------------------------------------------
+
+ /**
+ * Unset userdata
+ *
+ * Legacy CI_Session compatibility method
+ *
+ * @param mixed $key Session data key(s)
+ * @return void
+ */
+ public function unset_userdata($key)
+ {
+ if (is_array($key))
+ {
+ foreach ($key as $k)
+ {
+ unset($_SESSION[$k]);
+ }
+
+ return;
+ }
+
+ unset($_SESSION[$key]);
+ }
+
+ // ------------------------------------------------------------------------
+
+ /**
+ * All userdata (fetch)
+ *
+ * Legacy CI_Session compatibility method
+ *
+ * @return array $_SESSION, excluding flash data items
+ */
+ public function all_userdata()
+ {
+ return $this->userdata();
+ }
+
+ // ------------------------------------------------------------------------
+
+ /**
+ * Has userdata
+ *
+ * Legacy CI_Session compatibility method
+ *
+ * @param string $key Session data key
+ * @return bool
+ */
+ public function has_userdata($key)
+ {
+ return isset($_SESSION[$key]);
+ }
+
+ // ------------------------------------------------------------------------
+
+ /**
+ * Flashdata (fetch)
+ *
+ * Legacy CI_Session compatibility method
+ *
+ * @param string $key Session data key
+ * @return mixed Session data value or NULL if not found
+ */
+ public function flashdata($key = NULL)
+ {
+ if (isset($key))
+ {
+ return (isset($_SESSION['__ci_vars'], $_SESSION['__ci_vars'][$key], $_SESSION[$key]) && ! is_int($_SESSION['__ci_vars'][$key]))
+ ? $_SESSION[$key]
+ : NULL;
+ }
+
+ $flashdata = array();
+
+ if ( ! empty($_SESSION['__ci_vars']))
+ {
+ foreach ($_SESSION['__ci_vars'] as $key => &$value)
+ {
+ is_int($value) OR $flashdata[$key] = $_SESSION[$key];
+ }
+ }
+
+ return $flashdata;
+ }
+
+ // ------------------------------------------------------------------------
+
+ /**
+ * Set flashdata
+ *
+ * Legacy CI_Session compatibility method
+ *
+ * @param mixed $data Session data key or an associative array
+ * @param mixed $value Value to store
+ * @return void
+ */
+ public function set_flashdata($data, $value = NULL)
+ {
+ $this->set_userdata($data, $value);
+ $this->mark_as_flash(is_array($data) ? array_keys($data) : $data);
+ }
+
+ // ------------------------------------------------------------------------
+
+ /**
+ * Keep flashdata
+ *
+ * Legacy CI_Session compatibility method
+ *
+ * @param mixed $key Session data key(s)
+ * @return void
+ */
+ public function keep_flashdata($key)
+ {
+ $this->mark_as_flash($key);
+ }
+
+ // ------------------------------------------------------------------------
+
+ /**
+ * Temp data (fetch)
+ *
+ * Legacy CI_Session compatibility method
+ *
+ * @param string $key Session data key
+ * @return mixed Session data value or NULL if not found
+ */
+ public function tempdata($key = NULL)
+ {
+ if (isset($key))
+ {
+ return (isset($_SESSION['__ci_vars'], $_SESSION['__ci_vars'][$key], $_SESSION[$key]) && is_int($_SESSION['__ci_vars'][$key]))
+ ? $_SESSION[$key]
+ : NULL;
+ }
+
+ $tempdata = array();
+
+ if ( ! empty($_SESSION['__ci_vars']))
+ {
+ foreach ($_SESSION['__ci_vars'] as $key => &$value)
+ {
+ is_int($value) && $tempdata[$key] = $_SESSION[$key];
+ }
+ }
+
+ return $tempdata;
+ }
+
+ // ------------------------------------------------------------------------
+
+ /**
+ * Set tempdata
+ *
+ * Legacy CI_Session compatibility method
+ *
+ * @param mixed $data Session data key or an associative array of items
+ * @param mixed $value Value to store
+ * @param int $ttl Time-to-live in seconds
+ * @return void
+ */
+ public function set_tempdata($data, $value = NULL, $ttl = 300)
+ {
+ $this->set_userdata($data, $value);
+ $this->mark_as_temp(is_array($data) ? array_keys($data) : $data, $ttl);
+ }
+
+ // ------------------------------------------------------------------------
+
+ /**
+ * Unset tempdata
+ *
+ * Legacy CI_Session compatibility method
+ *
+ * @param mixed $data Session data key(s)
+ * @return void
+ */
+ public function unset_tempdata($key)
+ {
+ $this->unmark_temp($key);
+ }
+
+}
diff --git a/system/libraries/Session/SessionUpdateTimestampHandlerInterface.php b/system/libraries/Session/SessionUpdateTimestampHandlerInterface.php
new file mode 100644
index 000000000..fe4a321ab
--- /dev/null
+++ b/system/libraries/Session/SessionUpdateTimestampHandlerInterface.php
@@ -0,0 +1,56 @@
+<?php
+/**
+ * CodeIgniter
+ *
+ * An open source application development framework for PHP
+ *
+ * This content is released under the MIT License (MIT)
+ *
+ * Copyright (c) 2019 - 2022, CodeIgniter Foundation
+ *
+ * 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.
+ *
+ * @package CodeIgniter
+ * @author EllisLab Dev Team
+ * @copyright Copyright (c) 2008 - 2014, EllisLab, Inc. (https://ellislab.com/)
+ * @copyright Copyright (c) 2014 - 2019, British Columbia Institute of Technology (https://bcit.ca/)
+ * @copyright Copyright (c) 2019 - 2022, CodeIgniter Foundation (https://codeigniter.com/)
+ * @license https://opensource.org/licenses/MIT MIT License
+ * @link https://codeigniter.com
+ * @since Version 3.0.0
+ * @filesource
+ */
+defined('BASEPATH') OR exit('No direct script access allowed');
+
+/**
+ * SessionUpdateTimestampHandlerInterface
+ *
+ * PHP 7 compatibility interface
+ *
+ * @package CodeIgniter
+ * @subpackage Libraries
+ * @category Sessions
+ * @author Andrey Andreev
+ * @link https://codeigniter.com/userguide3/libraries/sessions.html
+ */
+interface SessionUpdateTimestampHandlerInterface {
+
+ public function updateTimestamp($session_id, $data);
+ public function validateId($session_id);
+}
diff --git a/system/libraries/Session/Session_driver.php b/system/libraries/Session/Session_driver.php
new file mode 100644
index 000000000..24b4b465e
--- /dev/null
+++ b/system/libraries/Session/Session_driver.php
@@ -0,0 +1,202 @@
+<?php
+/**
+ * CodeIgniter
+ *
+ * An open source application development framework for PHP
+ *
+ * This content is released under the MIT License (MIT)
+ *
+ * Copyright (c) 2019 - 2022, CodeIgniter Foundation
+ *
+ * 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.
+ *
+ * @package CodeIgniter
+ * @author EllisLab Dev Team
+ * @copyright Copyright (c) 2008 - 2014, EllisLab, Inc. (https://ellislab.com/)
+ * @copyright Copyright (c) 2014 - 2019, British Columbia Institute of Technology (https://bcit.ca/)
+ * @copyright Copyright (c) 2019 - 2022, CodeIgniter Foundation (https://codeigniter.com/)
+ * @license https://opensource.org/licenses/MIT MIT License
+ * @link https://codeigniter.com
+ * @since Version 3.0.0
+ * @filesource
+ */
+defined('BASEPATH') OR exit('No direct script access allowed');
+
+/**
+ * CodeIgniter Session Driver Class
+ *
+ * @package CodeIgniter
+ * @subpackage Libraries
+ * @category Sessions
+ * @author Andrey Andreev
+ * @link https://codeigniter.com/userguide3/libraries/sessions.html
+ */
+abstract class CI_Session_driver {
+
+ protected $_config;
+
+ /**
+ * Data fingerprint
+ *
+ * @var bool
+ */
+ protected $_fingerprint;
+
+ /**
+ * Lock placeholder
+ *
+ * @var mixed
+ */
+ protected $_lock = FALSE;
+
+ /**
+ * Read session ID
+ *
+ * Used to detect session_regenerate_id() calls because PHP only calls
+ * write() after regenerating the ID.
+ *
+ * @var string
+ */
+ protected $_session_id;
+
+ /**
+ * Success and failure return values
+ *
+ * Necessary due to a bug in all PHP 5 versions where return values
+ * from userspace handlers are not handled properly. PHP 7 fixes the
+ * bug, so we need to return different values depending on the version.
+ *
+ * @see https://wiki.php.net/rfc/session.user.return-value
+ * @var mixed
+ */
+ protected $_success, $_failure;
+
+ // ------------------------------------------------------------------------
+
+ /**
+ * Class constructor
+ *
+ * @param array $params Configuration parameters
+ * @return void
+ */
+ public function __construct(&$params)
+ {
+ $this->_config =& $params;
+
+ if (is_php('7'))
+ {
+ $this->_success = TRUE;
+ $this->_failure = FALSE;
+ }
+ else
+ {
+ $this->_success = 0;
+ $this->_failure = -1;
+ }
+ }
+
+ // ------------------------------------------------------------------------
+
+ /**
+ * PHP 5.x validate ID
+ *
+ * Enforces session.use_strict_mode
+ *
+ * @return void
+ */
+ public function php5_validate_id()
+ {
+ if ($this->_success === 0 && isset($_COOKIE[$this->_config['cookie_name']]) && ! $this->validateId($_COOKIE[$this->_config['cookie_name']]))
+ {
+ unset($_COOKIE[$this->_config['cookie_name']]);
+ }
+ }
+
+ // ------------------------------------------------------------------------
+
+ /**
+ * Cookie destroy
+ *
+ * Internal method to force removal of a cookie by the client
+ * when session_destroy() is called.
+ *
+ * @return bool
+ */
+ protected function _cookie_destroy()
+ {
+ if ( ! is_php('7.3'))
+ {
+ $header = 'Set-Cookie: '.$this->_config['cookie_name'].'=';
+ $header .= '; Expires='.gmdate('D, d-M-Y H:i:s T', 1).'; Max-Age=-1';
+ $header .= '; Path='.$this->_config['cookie_path'];
+ $header .= ($this->_config['cookie_domain'] !== '' ? '; Domain='.$this->_config['cookie_domain'] : '');
+ $header .= ($this->_config['cookie_secure'] ? '; Secure' : '').'; HttpOnly; SameSite='.$this->_config['cookie_samesite'];
+ header($header);
+ return;
+ }
+
+ return setcookie(
+ $this->_config['cookie_name'],
+ '',
+ array(
+ 'expires' => 1,
+ 'path' => $this->_config['cookie_path'],
+ 'domain' => $this->_config['cookie_domain'],
+ 'secure' => $this->_config['cookie_secure'],
+ 'httponly' => TRUE,
+ 'samesite' => $this->_config['cookie_samesite']
+ )
+ );
+ }
+
+ // ------------------------------------------------------------------------
+
+ /**
+ * Get lock
+ *
+ * A dummy method allowing drivers with no locking functionality
+ * (databases other than PostgreSQL and MySQL) to act as if they
+ * do acquire a lock.
+ *
+ * @param string $session_id
+ * @return bool
+ */
+ protected function _get_lock($session_id)
+ {
+ $this->_lock = TRUE;
+ return TRUE;
+ }
+
+ // ------------------------------------------------------------------------
+
+ /**
+ * Release lock
+ *
+ * @return bool
+ */
+ protected function _release_lock()
+ {
+ if ($this->_lock)
+ {
+ $this->_lock = FALSE;
+ }
+
+ return TRUE;
+ }
+}
diff --git a/system/libraries/Session/drivers/Session_database_driver.php b/system/libraries/Session/drivers/Session_database_driver.php
new file mode 100644
index 000000000..4b475364b
--- /dev/null
+++ b/system/libraries/Session/drivers/Session_database_driver.php
@@ -0,0 +1,471 @@
+<?php
+/**
+ * CodeIgniter
+ *
+ * An open source application development framework for PHP
+ *
+ * This content is released under the MIT License (MIT)
+ *
+ * Copyright (c) 2019 - 2022, CodeIgniter Foundation
+ *
+ * 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.
+ *
+ * @package CodeIgniter
+ * @author EllisLab Dev Team
+ * @copyright Copyright (c) 2008 - 2014, EllisLab, Inc. (https://ellislab.com/)
+ * @copyright Copyright (c) 2014 - 2019, British Columbia Institute of Technology (https://bcit.ca/)
+ * @copyright Copyright (c) 2019 - 2022, CodeIgniter Foundation (https://codeigniter.com/)
+ * @license https://opensource.org/licenses/MIT MIT License
+ * @link https://codeigniter.com
+ * @since Version 3.0.0
+ * @filesource
+ */
+defined('BASEPATH') OR exit('No direct script access allowed');
+
+/**
+ * CodeIgniter Session Database Driver
+ *
+ * @package CodeIgniter
+ * @subpackage Libraries
+ * @category Sessions
+ * @author Andrey Andreev
+ * @link https://codeigniter.com/userguide3/libraries/sessions.html
+ */
+class CI_Session_database_driver extends CI_Session_driver implements CI_Session_driver_interface {
+
+ /**
+ * DB object
+ *
+ * @var object
+ */
+ protected $_db;
+
+ /**
+ * Row exists flag
+ *
+ * @var bool
+ */
+ protected $_row_exists = FALSE;
+
+ /**
+ * Lock "driver" flag
+ *
+ * @var string
+ */
+ protected $_platform;
+
+ // ------------------------------------------------------------------------
+
+ /**
+ * Class constructor
+ *
+ * @param array $params Configuration parameters
+ * @return void
+ */
+ public function __construct(&$params)
+ {
+ parent::__construct($params);
+
+ $CI =& get_instance();
+ isset($CI->db) OR $CI->load->database();
+ $this->_db = $CI->db;
+
+ if ( ! $this->_db instanceof CI_DB_query_builder)
+ {
+ throw new Exception('Query Builder not enabled for the configured database. Aborting.');
+ }
+ elseif ($this->_db->pconnect)
+ {
+ throw new Exception('Configured database connection is persistent. Aborting.');
+ }
+ elseif ($this->_db->cache_on)
+ {
+ throw new Exception('Configured database connection has cache enabled. Aborting.');
+ }
+
+ $db_driver = $this->_db->dbdriver.(empty($this->_db->subdriver) ? '' : '_'.$this->_db->subdriver);
+ if (strpos($db_driver, 'mysql') !== FALSE)
+ {
+ $this->_platform = 'mysql';
+ }
+ elseif (in_array($db_driver, array('postgre', 'pdo_pgsql'), TRUE))
+ {
+ $this->_platform = 'postgre';
+ }
+
+ // Note: BC work-around for the old 'sess_table_name' setting, should be removed in the future.
+ if ( ! isset($this->_config['save_path']) && ($this->_config['save_path'] = config_item('sess_table_name')))
+ {
+ log_message('debug', 'Session: "sess_save_path" is empty; using BC fallback to "sess_table_name".');
+ }
+ }
+
+ // ------------------------------------------------------------------------
+
+ /**
+ * Open
+ *
+ * Initializes the database connection
+ *
+ * @param string $save_path Table name
+ * @param string $name Session cookie name, unused
+ * @return bool
+ */
+ public function open($save_path, $name)
+ {
+ if (empty($this->_db->conn_id) && ! $this->_db->db_connect())
+ {
+ return $this->_failure;
+ }
+
+ $this->php5_validate_id();
+
+ return $this->_success;
+ }
+
+ // ------------------------------------------------------------------------
+
+ /**
+ * Read
+ *
+ * Reads session data and acquires a lock
+ *
+ * @param string $session_id Session ID
+ * @return string Serialized session data
+ */
+ public function read($session_id)
+ {
+ if ($this->_get_lock($session_id) === FALSE)
+ {
+ return $this->_failure;
+ }
+
+ // Prevent previous QB calls from messing with our queries
+ $this->_db->reset_query();
+
+ // Needed by write() to detect session_regenerate_id() calls
+ $this->_session_id = $session_id;
+
+ $this->_db
+ ->select('data')
+ ->from($this->_config['save_path'])
+ ->where('id', $session_id);
+
+ if ($this->_config['match_ip'])
+ {
+ $this->_db->where('ip_address', $_SERVER['REMOTE_ADDR']);
+ }
+
+ if ( ! ($result = $this->_db->get()) OR ($result = $result->row()) === NULL)
+ {
+ // PHP7 will reuse the same SessionHandler object after
+ // ID regeneration, so we need to explicitly set this to
+ // FALSE instead of relying on the default ...
+ $this->_row_exists = FALSE;
+ $this->_fingerprint = md5('');
+ return '';
+ }
+
+ // PostgreSQL's variant of a BLOB datatype is Bytea, which is a
+ // PITA to work with, so we use base64-encoded data in a TEXT
+ // field instead.
+ $result = ($this->_platform === 'postgre')
+ ? base64_decode(rtrim($result->data))
+ : $result->data;
+
+ $this->_fingerprint = md5($result);
+ $this->_row_exists = TRUE;
+ return $result;
+ }
+
+ // ------------------------------------------------------------------------
+
+ /**
+ * Write
+ *
+ * Writes (create / update) session data
+ *
+ * @param string $session_id Session ID
+ * @param string $session_data Serialized session data
+ * @return bool
+ */
+ public function write($session_id, $session_data)
+ {
+ // Prevent previous QB calls from messing with our queries
+ $this->_db->reset_query();
+
+ // Was the ID regenerated?
+ if (isset($this->_session_id) && $session_id !== $this->_session_id)
+ {
+ if ( ! $this->_release_lock() OR ! $this->_get_lock($session_id))
+ {
+ return $this->_failure;
+ }
+
+ $this->_row_exists = FALSE;
+ $this->_session_id = $session_id;
+ }
+ elseif ($this->_lock === FALSE)
+ {
+ return $this->_failure;
+ }
+
+ if ($this->_row_exists === FALSE)
+ {
+ $insert_data = array(
+ 'id' => $session_id,
+ 'ip_address' => $_SERVER['REMOTE_ADDR'],
+ 'timestamp' => time(),
+ 'data' => ($this->_platform === 'postgre' ? base64_encode($session_data) : $session_data)
+ );
+
+ if ($this->_db->insert($this->_config['save_path'], $insert_data))
+ {
+ $this->_fingerprint = md5($session_data);
+ $this->_row_exists = TRUE;
+ return $this->_success;
+ }
+
+ return $this->_failure;
+ }
+
+ $this->_db->where('id', $session_id);
+ if ($this->_config['match_ip'])
+ {
+ $this->_db->where('ip_address', $_SERVER['REMOTE_ADDR']);
+ }
+
+ $update_data = array('timestamp' => time());
+ if ($this->_fingerprint !== md5($session_data))
+ {
+ $update_data['data'] = ($this->_platform === 'postgre')
+ ? base64_encode($session_data)
+ : $session_data;
+ }
+
+ if ($this->_db->update($this->_config['save_path'], $update_data))
+ {
+ $this->_fingerprint = md5($session_data);
+ return $this->_success;
+ }
+
+ return $this->_failure;
+ }
+
+ // ------------------------------------------------------------------------
+
+ /**
+ * Close
+ *
+ * Releases locks
+ *
+ * @return bool
+ */
+ public function close()
+ {
+ return ($this->_lock && ! $this->_release_lock())
+ ? $this->_failure
+ : $this->_success;
+ }
+
+ // ------------------------------------------------------------------------
+
+ /**
+ * Destroy
+ *
+ * Destroys the current session.
+ *
+ * @param string $session_id Session ID
+ * @return bool
+ */
+ public function destroy($session_id)
+ {
+ if ($this->_lock)
+ {
+ // Prevent previous QB calls from messing with our queries
+ $this->_db->reset_query();
+
+ $this->_db->where('id', $session_id);
+ if ($this->_config['match_ip'])
+ {
+ $this->_db->where('ip_address', $_SERVER['REMOTE_ADDR']);
+ }
+
+ if ( ! $this->_db->delete($this->_config['save_path']))
+ {
+ return $this->_failure;
+ }
+ }
+
+ if ($this->close() === $this->_success)
+ {
+ $this->_cookie_destroy();
+ return $this->_success;
+ }
+
+ return $this->_failure;
+ }
+
+ // ------------------------------------------------------------------------
+
+ /**
+ * Garbage Collector
+ *
+ * Deletes expired sessions
+ *
+ * @param int $maxlifetime Maximum lifetime of sessions
+ * @return bool
+ */
+ public function gc($maxlifetime)
+ {
+ // Prevent previous QB calls from messing with our queries
+ $this->_db->reset_query();
+
+ return ($this->_db->delete($this->_config['save_path'], 'timestamp < '.(time() - $maxlifetime)))
+ ? $this->_success
+ : $this->_failure;
+ }
+
+ // --------------------------------------------------------------------
+
+ /**
+ * Update Timestamp
+ *
+ * Update session timestamp without modifying data
+ *
+ * @param string $id Session ID
+ * @param string $data Unknown & unused
+ * @return bool
+ */
+ public function updateTimestamp($id, $unknown)
+ {
+ // Prevent previous QB calls from messing with our queries
+ $this->_db->reset_query();
+
+ $this->_db->where('id', $id);
+ if ($this->_config['match_ip'])
+ {
+ $this->_db->where('ip_address', $_SERVER['REMOTE_ADDR']);
+ }
+
+ return (bool) $this->_db->update($this->_config['save_path'], array('timestamp' => time()));
+ }
+
+ // --------------------------------------------------------------------
+
+ /**
+ * Validate ID
+ *
+ * Checks whether a session ID record exists server-side,
+ * to enforce session.use_strict_mode.
+ *
+ * @param string $id Session ID
+ * @return bool
+ */
+ public function validateId($id)
+ {
+ // Prevent previous QB calls from messing with our queries
+ $this->_db->reset_query();
+
+ $this->_db->select('1')->from($this->_config['save_path'])->where('id', $id);
+ empty($this->_config['match_ip']) OR $this->_db->where('ip_address', $_SERVER['REMOTE_ADDR']);
+ $result = $this->_db->get();
+ empty($result) OR $result = $result->row();
+
+ return ! empty($result);
+ }
+
+ // ------------------------------------------------------------------------
+
+ /**
+ * Get lock
+ *
+ * Acquires a lock, depending on the underlying platform.
+ *
+ * @param string $session_id Session ID
+ * @return bool
+ */
+ protected function _get_lock($session_id)
+ {
+ if ($this->_platform === 'mysql')
+ {
+ $arg = md5($session_id.($this->_config['match_ip'] ? '_'.$_SERVER['REMOTE_ADDR'] : ''));
+ if ($this->_db->query("SELECT GET_LOCK('".$arg."', 300) AS ci_session_lock")->row()->ci_session_lock)
+ {
+ $this->_lock = $arg;
+ return TRUE;
+ }
+
+ return FALSE;
+ }
+ elseif ($this->_platform === 'postgre')
+ {
+ $arg = "hashtext('".$session_id."')".($this->_config['match_ip'] ? ", hashtext('".$_SERVER['REMOTE_ADDR']."')" : '');
+ if ($this->_db->simple_query('SELECT pg_advisory_lock('.$arg.')'))
+ {
+ $this->_lock = $arg;
+ return TRUE;
+ }
+
+ return FALSE;
+ }
+
+ return parent::_get_lock($session_id);
+ }
+
+ // ------------------------------------------------------------------------
+
+ /**
+ * Release lock
+ *
+ * Releases a previously acquired lock
+ *
+ * @return bool
+ */
+ protected function _release_lock()
+ {
+ if ( ! $this->_lock)
+ {
+ return TRUE;
+ }
+
+ if ($this->_platform === 'mysql')
+ {
+ if ($this->_db->query("SELECT RELEASE_LOCK('".$this->_lock."') AS ci_session_lock")->row()->ci_session_lock)
+ {
+ $this->_lock = FALSE;
+ return TRUE;
+ }
+
+ return FALSE;
+ }
+ elseif ($this->_platform === 'postgre')
+ {
+ if ($this->_db->simple_query('SELECT pg_advisory_unlock('.$this->_lock.')'))
+ {
+ $this->_lock = FALSE;
+ return TRUE;
+ }
+
+ return FALSE;
+ }
+
+ return parent::_release_lock();
+ }
+}
diff --git a/system/libraries/Session/drivers/Session_files_driver.php b/system/libraries/Session/drivers/Session_files_driver.php
new file mode 100644
index 000000000..be0dc9ede
--- /dev/null
+++ b/system/libraries/Session/drivers/Session_files_driver.php
@@ -0,0 +1,449 @@
+<?php
+/**
+ * CodeIgniter
+ *
+ * An open source application development framework for PHP
+ *
+ * This content is released under the MIT License (MIT)
+ *
+ * Copyright (c) 2019 - 2022, CodeIgniter Foundation
+ *
+ * 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.
+ *
+ * @package CodeIgniter
+ * @author EllisLab Dev Team
+ * @copyright Copyright (c) 2008 - 2014, EllisLab, Inc. (https://ellislab.com/)
+ * @copyright Copyright (c) 2014 - 2019, British Columbia Institute of Technology (https://bcit.ca/)
+ * @copyright Copyright (c) 2019 - 2022, CodeIgniter Foundation (https://codeigniter.com/)
+ * @license https://opensource.org/licenses/MIT MIT License
+ * @link https://codeigniter.com
+ * @since Version 3.0.0
+ * @filesource
+ */
+defined('BASEPATH') OR exit('No direct script access allowed');
+
+/**
+ * CodeIgniter Session Files Driver
+ *
+ * @package CodeIgniter
+ * @subpackage Libraries
+ * @category Sessions
+ * @author Andrey Andreev
+ * @link https://codeigniter.com/userguide3/libraries/sessions.html
+ */
+class CI_Session_files_driver extends CI_Session_driver implements CI_Session_driver_interface {
+
+ /**
+ * Save path
+ *
+ * @var string
+ */
+ protected $_save_path;
+
+ /**
+ * File handle
+ *
+ * @var resource
+ */
+ protected $_file_handle;
+
+ /**
+ * File name
+ *
+ * @var resource
+ */
+ protected $_file_path;
+
+ /**
+ * File new flag
+ *
+ * @var bool
+ */
+ protected $_file_new;
+
+ /**
+ * Validate SID regular expression
+ *
+ * @var string
+ */
+ protected $_sid_regexp;
+
+ /**
+ * mbstring.func_overload flag
+ *
+ * @var bool
+ */
+ protected static $func_overload;
+
+ // ------------------------------------------------------------------------
+
+ /**
+ * Class constructor
+ *
+ * @param array $params Configuration parameters
+ * @return void
+ */
+ public function __construct(&$params)
+ {
+ parent::__construct($params);
+
+ if (isset($this->_config['save_path']))
+ {
+ $this->_config['save_path'] = rtrim($this->_config['save_path'], '/\\');
+ ini_set('session.save_path', $this->_config['save_path']);
+ }
+ else
+ {
+ log_message('debug', 'Session: "sess_save_path" is empty; using "session.save_path" value from php.ini.');
+ $this->_config['save_path'] = rtrim(ini_get('session.save_path'), '/\\');
+ }
+
+ $this->_sid_regexp = $this->_config['_sid_regexp'];
+
+ isset(self::$func_overload) OR self::$func_overload = ( ! is_php('8.0') && extension_loaded('mbstring') && @ini_get('mbstring.func_overload'));
+ }
+
+ // ------------------------------------------------------------------------
+
+ /**
+ * Open
+ *
+ * Sanitizes the save_path directory.
+ *
+ * @param string $save_path Path to session files' directory
+ * @param string $name Session cookie name
+ * @return bool
+ */
+ public function open($save_path, $name)
+ {
+ if ( ! is_dir($save_path))
+ {
+ if ( ! mkdir($save_path, 0700, TRUE))
+ {
+ log_message('error', "Session: Configured save path '".$this->_config['save_path']."' is not a directory, doesn't exist or cannot be created.");
+ return $this->_failure;
+ }
+ }
+ elseif ( ! is_writable($save_path))
+ {
+ log_message('error', "Session: Configured save path '".$this->_config['save_path']."' is not writable by the PHP process.");
+ return $this->_failure;
+ }
+
+ $this->_config['save_path'] = $save_path;
+ $this->_file_path = $this->_config['save_path'].DIRECTORY_SEPARATOR
+ .$name // we'll use the session cookie name as a prefix to avoid collisions
+ .($this->_config['match_ip'] ? md5($_SERVER['REMOTE_ADDR']) : '');
+
+ $this->php5_validate_id();
+
+ return $this->_success;
+ }
+
+ // ------------------------------------------------------------------------
+
+ /**
+ * Read
+ *
+ * Reads session data and acquires a lock
+ *
+ * @param string $session_id Session ID
+ * @return string Serialized session data
+ */
+ public function read($session_id)
+ {
+ // This might seem weird, but PHP 5.6 introduces session_reset(),
+ // which re-reads session data
+ if ($this->_file_handle === NULL)
+ {
+ $this->_file_new = ! file_exists($this->_file_path.$session_id);
+
+ if (($this->_file_handle = fopen($this->_file_path.$session_id, 'c+b')) === FALSE)
+ {
+ log_message('error', "Session: Unable to open file '".$this->_file_path.$session_id."'.");
+ return $this->_failure;
+ }
+
+ if (flock($this->_file_handle, LOCK_EX) === FALSE)
+ {
+ log_message('error', "Session: Unable to obtain lock for file '".$this->_file_path.$session_id."'.");
+ fclose($this->_file_handle);
+ $this->_file_handle = NULL;
+ return $this->_failure;
+ }
+
+ // Needed by write() to detect session_regenerate_id() calls
+ $this->_session_id = $session_id;
+
+ if ($this->_file_new)
+ {
+ chmod($this->_file_path.$session_id, 0600);
+ $this->_fingerprint = md5('');
+ return '';
+ }
+
+ // Prevent possible data corruption
+ // See https://github.com/bcit-ci/CodeIgniter/issues/5857
+ clearstatcache(TRUE, $this->_file_path.$session_id);
+ }
+ // We shouldn't need this, but apparently we do ...
+ // See https://github.com/bcit-ci/CodeIgniter/issues/4039
+ elseif ($this->_file_handle === FALSE)
+ {
+ return $this->_failure;
+ }
+ else
+ {
+ rewind($this->_file_handle);
+ }
+
+ $session_data = '';
+ for ($read = 0, $length = filesize($this->_file_path.$session_id); $read < $length; $read += self::strlen($buffer))
+ {
+ if (($buffer = fread($this->_file_handle, $length - $read)) === FALSE)
+ {
+ break;
+ }
+
+ $session_data .= $buffer;
+ }
+
+ $this->_fingerprint = md5($session_data);
+ return $session_data;
+ }
+
+ // ------------------------------------------------------------------------
+
+ /**
+ * Write
+ *
+ * Writes (create / update) session data
+ *
+ * @param string $session_id Session ID
+ * @param string $session_data Serialized session data
+ * @return bool
+ */
+ public function write($session_id, $session_data)
+ {
+ // If the two IDs don't match, we have a session_regenerate_id() call
+ // and we need to close the old handle and open a new one
+ if ($session_id !== $this->_session_id && ($this->close() === $this->_failure OR $this->read($session_id) === $this->_failure))
+ {
+ return $this->_failure;
+ }
+
+ if ( ! is_resource($this->_file_handle))
+ {
+ return $this->_failure;
+ }
+ elseif ($this->_fingerprint === md5($session_data))
+ {
+ return ( ! $this->_file_new && ! touch($this->_file_path.$session_id))
+ ? $this->_failure
+ : $this->_success;
+ }
+
+ if ( ! $this->_file_new)
+ {
+ ftruncate($this->_file_handle, 0);
+ rewind($this->_file_handle);
+ }
+
+ if (($length = strlen($session_data)) > 0)
+ {
+ for ($written = 0; $written < $length; $written += $result)
+ {
+ if (($result = fwrite($this->_file_handle, substr($session_data, $written))) === FALSE)
+ {
+ break;
+ }
+ }
+
+ if ( ! is_int($result))
+ {
+ $this->_fingerprint = md5(substr($session_data, 0, $written));
+ log_message('error', 'Session: Unable to write data.');
+ return $this->_failure;
+ }
+ }
+
+ $this->_fingerprint = md5($session_data);
+ return $this->_success;
+ }
+
+ // ------------------------------------------------------------------------
+
+ /**
+ * Close
+ *
+ * Releases locks and closes file descriptor.
+ *
+ * @return bool
+ */
+ public function close()
+ {
+ if (is_resource($this->_file_handle))
+ {
+ flock($this->_file_handle, LOCK_UN);
+ fclose($this->_file_handle);
+
+ $this->_file_handle = $this->_file_new = $this->_session_id = NULL;
+ }
+
+ return $this->_success;
+ }
+
+ // ------------------------------------------------------------------------
+
+ /**
+ * Destroy
+ *
+ * Destroys the current session.
+ *
+ * @param string $session_id Session ID
+ * @return bool
+ */
+ public function destroy($session_id)
+ {
+ if ($this->close() === $this->_success)
+ {
+ if (file_exists($this->_file_path.$session_id))
+ {
+ $this->_cookie_destroy();
+ return unlink($this->_file_path.$session_id)
+ ? $this->_success
+ : $this->_failure;
+ }
+
+ return $this->_success;
+ }
+ elseif ($this->_file_path !== NULL)
+ {
+ clearstatcache();
+ if (file_exists($this->_file_path.$session_id))
+ {
+ $this->_cookie_destroy();
+ return unlink($this->_file_path.$session_id)
+ ? $this->_success
+ : $this->_failure;
+ }
+
+ return $this->_success;
+ }
+
+ return $this->_failure;
+ }
+
+ // ------------------------------------------------------------------------
+
+ /**
+ * Garbage Collector
+ *
+ * Deletes expired sessions
+ *
+ * @param int $maxlifetime Maximum lifetime of sessions
+ * @return bool
+ */
+ public function gc($maxlifetime)
+ {
+ if ( ! is_dir($this->_config['save_path']) OR ($directory = opendir($this->_config['save_path'])) === FALSE)
+ {
+ log_message('debug', "Session: Garbage collector couldn't list files under directory '".$this->_config['save_path']."'.");
+ return $this->_failure;
+ }
+
+ $ts = time() - $maxlifetime;
+
+ $pattern = ($this->_config['match_ip'] === TRUE)
+ ? '[0-9a-f]{32}'
+ : '';
+
+ $pattern = sprintf(
+ '#\A%s'.$pattern.$this->_sid_regexp.'\z#',
+ preg_quote($this->_config['cookie_name'])
+ );
+
+ while (($file = readdir($directory)) !== FALSE)
+ {
+ // If the filename doesn't match this pattern, it's either not a session file or is not ours
+ if ( ! preg_match($pattern, $file)
+ OR ! is_file($this->_config['save_path'].DIRECTORY_SEPARATOR.$file)
+ OR ($mtime = filemtime($this->_config['save_path'].DIRECTORY_SEPARATOR.$file)) === FALSE
+ OR $mtime > $ts)
+ {
+ continue;
+ }
+
+ unlink($this->_config['save_path'].DIRECTORY_SEPARATOR.$file);
+ }
+
+ closedir($directory);
+
+ return $this->_success;
+ }
+
+ // --------------------------------------------------------------------
+
+ /**
+ * Update Timestamp
+ *
+ * Update session timestamp without modifying data
+ *
+ * @param string $id Session ID
+ * @param string $data Unknown & unused
+ * @return bool
+ */
+ public function updateTimestamp($id, $unknown)
+ {
+ return touch($this->_file_path.$id);
+ }
+
+ // --------------------------------------------------------------------
+
+ /**
+ * Validate ID
+ *
+ * Checks whether a session ID record exists server-side,
+ * to enforce session.use_strict_mode.
+ *
+ * @param string $id Session ID
+ * @return bool
+ */
+ public function validateId($id)
+ {
+ $result = is_file($this->_file_path.$id);
+ clearstatcache(TRUE, $this->_file_path.$id);
+ return $result;
+ }
+
+ // --------------------------------------------------------------------
+
+ /**
+ * Byte-safe strlen()
+ *
+ * @param string $str
+ * @return int
+ */
+ protected static function strlen($str)
+ {
+ return (self::$func_overload)
+ ? mb_strlen($str, '8bit')
+ : strlen($str);
+ }
+}
diff --git a/system/libraries/Session/drivers/Session_memcached_driver.php b/system/libraries/Session/drivers/Session_memcached_driver.php
new file mode 100644
index 000000000..d1401630d
--- /dev/null
+++ b/system/libraries/Session/drivers/Session_memcached_driver.php
@@ -0,0 +1,414 @@
+<?php
+/**
+ * CodeIgniter
+ *
+ * An open source application development framework for PHP
+ *
+ * This content is released under the MIT License (MIT)
+ *
+ * Copyright (c) 2019 - 2022, CodeIgniter Foundation
+ *
+ * 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.
+ *
+ * @package CodeIgniter
+ * @author EllisLab Dev Team
+ * @copyright Copyright (c) 2008 - 2014, EllisLab, Inc. (https://ellislab.com/)
+ * @copyright Copyright (c) 2014 - 2019, British Columbia Institute of Technology (https://bcit.ca/)
+ * @copyright Copyright (c) 2019 - 2022, CodeIgniter Foundation (https://codeigniter.com/)
+ * @license https://opensource.org/licenses/MIT MIT License
+ * @link https://codeigniter.com
+ * @since Version 3.0.0
+ * @filesource
+ */
+defined('BASEPATH') OR exit('No direct script access allowed');
+
+/**
+ * CodeIgniter Session Memcached Driver
+ *
+ * @package CodeIgniter
+ * @subpackage Libraries
+ * @category Sessions
+ * @author Andrey Andreev
+ * @link https://codeigniter.com/userguide3/libraries/sessions.html
+ */
+class CI_Session_memcached_driver extends CI_Session_driver implements CI_Session_driver_interface {
+
+ /**
+ * Memcached instance
+ *
+ * @var Memcached
+ */
+ protected $_memcached;
+
+ /**
+ * Key prefix
+ *
+ * @var string
+ */
+ protected $_key_prefix = 'ci_session:';
+
+ /**
+ * Lock key
+ *
+ * @var string
+ */
+ protected $_lock_key;
+
+ // ------------------------------------------------------------------------
+
+ /**
+ * Class constructor
+ *
+ * @param array $params Configuration parameters
+ * @return void
+ */
+ public function __construct(&$params)
+ {
+ parent::__construct($params);
+
+ if (empty($this->_config['save_path']))
+ {
+ log_message('error', 'Session: No Memcached save path configured.');
+ }
+
+ if ($this->_config['match_ip'] === TRUE)
+ {
+ $this->_key_prefix .= $_SERVER['REMOTE_ADDR'].':';
+ }
+ }
+
+ // ------------------------------------------------------------------------
+
+ /**
+ * Open
+ *
+ * Sanitizes save_path and initializes connections.
+ *
+ * @param string $save_path Server path(s)
+ * @param string $name Session cookie name, unused
+ * @return bool
+ */
+ public function open($save_path, $name)
+ {
+ $this->_memcached = new Memcached();
+ $this->_memcached->setOption(Memcached::OPT_BINARY_PROTOCOL, TRUE); // required for touch() usage
+ $server_list = array();
+ foreach ($this->_memcached->getServerList() as $server)
+ {
+ $server_list[] = $server['host'].':'.$server['port'];
+ }
+
+ if ( ! preg_match_all('#,?([^,:]+)\:(\d{1,5})(?:\:(\d+))?#', $this->_config['save_path'], $matches, PREG_SET_ORDER))
+ {
+ $this->_memcached = NULL;
+ log_message('error', 'Session: Invalid Memcached save path format: '.$this->_config['save_path']);
+ return $this->_failure;
+ }
+
+ foreach ($matches as $match)
+ {
+ // If Memcached already has this server (or if the port is invalid), skip it
+ if (in_array($match[1].':'.$match[2], $server_list, TRUE))
+ {
+ log_message('debug', 'Session: Memcached server pool already has '.$match[1].':'.$match[2]);
+ continue;
+ }
+
+ if ( ! $this->_memcached->addServer($match[1], $match[2], isset($match[3]) ? $match[3] : 0))
+ {
+ log_message('error', 'Could not add '.$match[1].':'.$match[2].' to Memcached server pool.');
+ }
+ else
+ {
+ $server_list[] = $match[1].':'.$match[2];
+ }
+ }
+
+ if (empty($server_list))
+ {
+ log_message('error', 'Session: Memcached server pool is empty.');
+ return $this->_failure;
+ }
+
+ $this->php5_validate_id();
+
+ return $this->_success;
+ }
+
+ // ------------------------------------------------------------------------
+
+ /**
+ * Read
+ *
+ * Reads session data and acquires a lock
+ *
+ * @param string $session_id Session ID
+ * @return string Serialized session data
+ */
+ public function read($session_id)
+ {
+ if (isset($this->_memcached) && $this->_get_lock($session_id))
+ {
+ // Needed by write() to detect session_regenerate_id() calls
+ $this->_session_id = $session_id;
+
+ $session_data = (string) $this->_memcached->get($this->_key_prefix.$session_id);
+ $this->_fingerprint = md5($session_data);
+ return $session_data;
+ }
+
+ return $this->_failure;
+ }
+
+ // ------------------------------------------------------------------------
+
+ /**
+ * Write
+ *
+ * Writes (create / update) session data
+ *
+ * @param string $session_id Session ID
+ * @param string $session_data Serialized session data
+ * @return bool
+ */
+ public function write($session_id, $session_data)
+ {
+ if ( ! isset($this->_memcached, $this->_lock_key))
+ {
+ return $this->_failure;
+ }
+ // Was the ID regenerated?
+ elseif ($session_id !== $this->_session_id)
+ {
+ if ( ! $this->_release_lock() OR ! $this->_get_lock($session_id))
+ {
+ return $this->_failure;
+ }
+
+ $this->_fingerprint = md5('');
+ $this->_session_id = $session_id;
+ }
+
+ $key = $this->_key_prefix.$session_id;
+
+ $this->_memcached->replace($this->_lock_key, time(), 300);
+ if ($this->_fingerprint !== ($fingerprint = md5($session_data)))
+ {
+ if ($this->_memcached->set($key, $session_data, $this->_config['expiration']))
+ {
+ $this->_fingerprint = $fingerprint;
+ return $this->_success;
+ }
+
+ return $this->_failure;
+ }
+ elseif (
+ $this->_memcached->touch($key, $this->_config['expiration'])
+ OR ($this->_memcached->getResultCode() === Memcached::RES_NOTFOUND && $this->_memcached->set($key, $session_data, $this->_config['expiration']))
+ )
+ {
+ return $this->_success;
+ }
+
+ return $this->_failure;
+ }
+
+ // ------------------------------------------------------------------------
+
+ /**
+ * Close
+ *
+ * Releases locks and closes connection.
+ *
+ * @return bool
+ */
+ public function close()
+ {
+ if (isset($this->_memcached))
+ {
+ $this->_release_lock();
+ if ( ! $this->_memcached->quit())
+ {
+ return $this->_failure;
+ }
+
+ $this->_memcached = NULL;
+ return $this->_success;
+ }
+
+ return $this->_failure;
+ }
+
+ // ------------------------------------------------------------------------
+
+ /**
+ * Destroy
+ *
+ * Destroys the current session.
+ *
+ * @param string $session_id Session ID
+ * @return bool
+ */
+ public function destroy($session_id)
+ {
+ if (isset($this->_memcached, $this->_lock_key))
+ {
+ $this->_memcached->delete($this->_key_prefix.$session_id);
+ $this->_cookie_destroy();
+ return $this->_success;
+ }
+
+ return $this->_failure;
+ }
+
+ // ------------------------------------------------------------------------
+
+ /**
+ * Garbage Collector
+ *
+ * Deletes expired sessions
+ *
+ * @param int $maxlifetime Maximum lifetime of sessions
+ * @return bool
+ */
+ public function gc($maxlifetime)
+ {
+ // Not necessary, Memcached takes care of that.
+ return $this->_success;
+ }
+
+ // --------------------------------------------------------------------
+
+ /**
+ * Update Timestamp
+ *
+ * Update session timestamp without modifying data
+ *
+ * @param string $id Session ID
+ * @param string $data Unknown & unused
+ * @return bool
+ */
+ public function updateTimestamp($id, $unknown)
+ {
+ return $this->_memcached->touch($this->_key_prefix.$id, $this->_config['expiration']);
+ }
+
+ // --------------------------------------------------------------------
+
+ /**
+ * Validate ID
+ *
+ * Checks whether a session ID record exists server-side,
+ * to enforce session.use_strict_mode.
+ *
+ * @param string $id Session ID
+ * @return bool
+ */
+ public function validateId($id)
+ {
+ $this->_memcached->get($this->_key_prefix.$id);
+ return ($this->_memcached->getResultCode() === Memcached::RES_SUCCESS);
+ }
+
+ // ------------------------------------------------------------------------
+
+ /**
+ * Get lock
+ *
+ * Acquires an (emulated) lock.
+ *
+ * @param string $session_id Session ID
+ * @return bool
+ */
+ protected function _get_lock($session_id)
+ {
+ // PHP 7 reuses the SessionHandler object on regeneration,
+ // so we need to check here if the lock key is for the
+ // correct session ID.
+ if ($this->_lock_key === $this->_key_prefix.$session_id.':lock')
+ {
+ if ( ! $this->_memcached->replace($this->_lock_key, time(), 300))
+ {
+ return ($this->_memcached->getResultCode() === Memcached::RES_NOTFOUND)
+ ? $this->_memcached->add($this->_lock_key, time(), 300)
+ : FALSE;
+ }
+
+ return TRUE;
+ }
+
+ // 30 attempts to obtain a lock, in case another request already has it
+ $lock_key = $this->_key_prefix.$session_id.':lock';
+ $attempt = 0;
+ do
+ {
+ if ($this->_memcached->get($lock_key))
+ {
+ sleep(1);
+ continue;
+ }
+
+ $method = ($this->_memcached->getResultCode() === Memcached::RES_NOTFOUND) ? 'add' : 'set';
+ if ( ! $this->_memcached->$method($lock_key, time(), 300))
+ {
+ log_message('error', 'Session: Error while trying to obtain lock for '.$this->_key_prefix.$session_id);
+ return FALSE;
+ }
+
+ $this->_lock_key = $lock_key;
+ break;
+ }
+ while (++$attempt < 30);
+
+ if ($attempt === 30)
+ {
+ log_message('error', 'Session: Unable to obtain lock for '.$this->_key_prefix.$session_id.' after 30 attempts, aborting.');
+ return FALSE;
+ }
+
+ $this->_lock = TRUE;
+ return TRUE;
+ }
+
+ // ------------------------------------------------------------------------
+
+ /**
+ * Release lock
+ *
+ * Releases a previously acquired lock
+ *
+ * @return bool
+ */
+ protected function _release_lock()
+ {
+ if (isset($this->_memcached, $this->_lock_key) && $this->_lock)
+ {
+ if ( ! $this->_memcached->delete($this->_lock_key) && $this->_memcached->getResultCode() !== Memcached::RES_NOTFOUND)
+ {
+ log_message('error', 'Session: Error while trying to free lock for '.$this->_lock_key);
+ return FALSE;
+ }
+
+ $this->_lock_key = NULL;
+ $this->_lock = FALSE;
+ }
+
+ return TRUE;
+ }
+}
diff --git a/system/libraries/Session/drivers/Session_redis_driver.php b/system/libraries/Session/drivers/Session_redis_driver.php
new file mode 100644
index 000000000..4d822d585
--- /dev/null
+++ b/system/libraries/Session/drivers/Session_redis_driver.php
@@ -0,0 +1,502 @@
+<?php
+/**
+ * CodeIgniter
+ *
+ * An open source application development framework for PHP
+ *
+ * This content is released under the MIT License (MIT)
+ *
+ * Copyright (c) 2019 - 2022, CodeIgniter Foundation
+ *
+ * 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.
+ *
+ * @package CodeIgniter
+ * @author EllisLab Dev Team
+ * @copyright Copyright (c) 2008 - 2014, EllisLab, Inc. (https://ellislab.com/)
+ * @copyright Copyright (c) 2014 - 2019, British Columbia Institute of Technology (https://bcit.ca/)
+ * @copyright Copyright (c) 2019 - 2022, CodeIgniter Foundation (https://codeigniter.com/)
+ * @license https://opensource.org/licenses/MIT MIT License
+ * @link https://codeigniter.com
+ * @since Version 3.0.0
+ * @filesource
+ */
+defined('BASEPATH') OR exit('No direct script access allowed');
+
+/**
+ * CodeIgniter Session Redis Driver
+ *
+ * @package CodeIgniter
+ * @subpackage Libraries
+ * @category Sessions
+ * @author Andrey Andreev
+ * @link https://codeigniter.com/userguide3/libraries/sessions.html
+ */
+class CI_Session_redis_driver extends CI_Session_driver implements CI_Session_driver_interface {
+
+ /**
+ * phpRedis instance
+ *
+ * @var Redis
+ */
+ protected $_redis;
+
+ /**
+ * Key prefix
+ *
+ * @var string
+ */
+ protected $_key_prefix = 'ci_session:';
+
+ /**
+ * Lock key
+ *
+ * @var string
+ */
+ protected $_lock_key;
+
+ /**
+ * Key exists flag
+ *
+ * @var bool
+ */
+ protected $_key_exists = FALSE;
+
+ /**
+ * Name of setTimeout() method in phpRedis
+ *
+ * Due to some deprecated methods in phpRedis, we need to call the
+ * specific methods depending on the version of phpRedis.
+ *
+ * @var string
+ */
+ protected $_setTimeout_name;
+
+ /**
+ * Name of delete() method in phpRedis
+ *
+ * Due to some deprecated methods in phpRedis, we need to call the
+ * specific methods depending on the version of phpRedis.
+ *
+ * @var string
+ */
+ protected $_delete_name;
+
+ /**
+ * Success return value of ping() method in phpRedis
+ *
+ * @var mixed
+ */
+ protected $_ping_success;
+
+ // ------------------------------------------------------------------------
+
+ /**
+ * Class constructor
+ *
+ * @param array $params Configuration parameters
+ * @return void
+ */
+ public function __construct(&$params)
+ {
+ parent::__construct($params);
+
+ // Detect the names of some methods in phpRedis instance
+ if (version_compare(phpversion('redis'), '5', '>='))
+ {
+ $this->_setTimeout_name = 'expire';
+ $this->_delete_name = 'del';
+ $this->_ping_success = TRUE;
+ }
+ else
+ {
+ $this->_setTimeout_name = 'setTimeout';
+ $this->_delete_name = 'delete';
+ $this->_ping_success = '+PONG';
+ }
+
+ if (empty($this->_config['save_path']))
+ {
+ log_message('error', 'Session: No Redis save path configured.');
+ }
+ elseif (preg_match('#^unix://([^\?]+)(?<options>\?.+)?$#', $this->_config['save_path'], $matches))
+ {
+ $save_path = array('path' => $matches[1]);
+ }
+ elseif (preg_match('#(?:(?:tcp|tls)://)?([^:?]+)(?:\:(\d+))?(?<options>\?.+)?#', $this->_config['save_path'], $matches))
+ {
+ $save_path = array(
+ 'host' => $matches[1],
+ 'port' => empty($matches[2]) ? NULL : $matches[2],
+ 'timeout' => 0.0 // We always pass this to Redis::connect(), so it needs to exist
+ );
+ }
+ else
+ {
+ log_message('error', 'Session: Invalid Redis save path format: '.$this->_config['save_path']);
+ }
+
+ if (isset($save_path))
+ {
+ if (isset($matches['options']))
+ {
+ $save_path['password'] = preg_match('#auth=([^\s&]+)#', $matches['options'], $match) ? $match[1] : NULL;
+ $save_path['database'] = preg_match('#database=(\d+)#', $matches['options'], $match) ? (int) $match[1] : NULL;
+ $save_path['timeout'] = preg_match('#timeout=(\d+\.\d+)#', $matches['options'], $match) ? (float) $match[1] : NULL;
+
+ preg_match('#prefix=([^\s&]+)#', $matches['options'], $match) && $this->_key_prefix = $match[1];
+ }
+
+ $this->_config['save_path'] = $save_path;
+
+ if ($this->_config['match_ip'] === TRUE)
+ {
+ $this->_key_prefix .= $_SERVER['REMOTE_ADDR'].':';
+ }
+ }
+ }
+
+ // ------------------------------------------------------------------------
+
+ /**
+ * Open
+ *
+ * Sanitizes save_path and initializes connection.
+ *
+ * @param string $save_path Server path
+ * @param string $name Session cookie name, unused
+ * @return bool
+ */
+ public function open($save_path, $name)
+ {
+ if (empty($this->_config['save_path']))
+ {
+ return $this->_failure;
+ }
+
+ $redis = new Redis();
+ $connected = isset($this->_config['save_path']['path'])
+ ? $redis->connect($this->_config['save_path']['path'])
+ : $redis->connect(
+ $this->_config['save_path']['host'],
+ $this->_config['save_path']['port'],
+ $this->_config['save_path']['timeout']
+ );
+
+ if ($connected)
+ {
+ if (isset($this->_config['save_path']['password']) && ! $redis->auth($this->_config['save_path']['password']))
+ {
+ log_message('error', 'Session: Unable to authenticate to Redis instance.');
+ }
+ elseif (isset($this->_config['save_path']['database']) && ! $redis->select($this->_config['save_path']['database']))
+ {
+ log_message('error', 'Session: Unable to select Redis database with index '.$this->_config['save_path']['database']);
+ }
+ else
+ {
+ $this->_redis = $redis;
+ $this->php5_validate_id();
+ return $this->_success;
+ }
+ }
+ else
+ {
+ $this->_redis = $redis;
+ $this->php5_validate_id();
+ return $this->_success;
+ }
+
+ return $this->_failure;
+ }
+
+ // ------------------------------------------------------------------------
+
+ /**
+ * Read
+ *
+ * Reads session data and acquires a lock
+ *
+ * @param string $session_id Session ID
+ * @return string Serialized session data
+ */
+ public function read($session_id)
+ {
+ if (isset($this->_redis) && $this->_get_lock($session_id))
+ {
+ // Needed by write() to detect session_regenerate_id() calls
+ $this->_session_id = $session_id;
+
+ $session_data = $this->_redis->get($this->_key_prefix.$session_id);
+
+ is_string($session_data)
+ ? $this->_key_exists = TRUE
+ : $session_data = '';
+
+ $this->_fingerprint = md5($session_data);
+ return $session_data;
+ }
+
+ return $this->_failure;
+ }
+
+ // ------------------------------------------------------------------------
+
+ /**
+ * Write
+ *
+ * Writes (create / update) session data
+ *
+ * @param string $session_id Session ID
+ * @param string $session_data Serialized session data
+ * @return bool
+ */
+ public function write($session_id, $session_data)
+ {
+ if ( ! isset($this->_redis, $this->_lock_key))
+ {
+ return $this->_failure;
+ }
+ // Was the ID regenerated?
+ elseif ($session_id !== $this->_session_id)
+ {
+ if ( ! $this->_release_lock() OR ! $this->_get_lock($session_id))
+ {
+ return $this->_failure;
+ }
+
+ $this->_key_exists = FALSE;
+ $this->_session_id = $session_id;
+ }
+
+ $this->_redis->{$this->_setTimeout_name}($this->_lock_key, 300);
+ if ($this->_fingerprint !== ($fingerprint = md5($session_data)) OR $this->_key_exists === FALSE)
+ {
+ if ($this->_redis->set($this->_key_prefix.$session_id, $session_data, $this->_config['expiration']))
+ {
+ $this->_fingerprint = $fingerprint;
+ $this->_key_exists = TRUE;
+ return $this->_success;
+ }
+
+ return $this->_failure;
+ }
+
+ return ($this->_redis->{$this->_setTimeout_name}($this->_key_prefix.$session_id, $this->_config['expiration']))
+ ? $this->_success
+ : $this->_failure;
+ }
+
+ // ------------------------------------------------------------------------
+
+ /**
+ * Close
+ *
+ * Releases locks and closes connection.
+ *
+ * @return bool
+ */
+ public function close()
+ {
+ if (isset($this->_redis))
+ {
+ try {
+ if ($this->_redis->ping() === $this->_ping_success)
+ {
+ $this->_release_lock();
+ if ($this->_redis->close() === FALSE)
+ {
+ return $this->_failure;
+ }
+ }
+ }
+ catch (RedisException $e)
+ {
+ log_message('error', 'Session: Got RedisException on close(): '.$e->getMessage());
+ }
+
+ $this->_redis = NULL;
+ return $this->_success;
+ }
+
+ return $this->_success;
+ }
+
+ // ------------------------------------------------------------------------
+
+ /**
+ * Destroy
+ *
+ * Destroys the current session.
+ *
+ * @param string $session_id Session ID
+ * @return bool
+ */
+ public function destroy($session_id)
+ {
+ if (isset($this->_redis, $this->_lock_key))
+ {
+ if (($result = $this->_redis->{$this->_delete_name}($this->_key_prefix.$session_id)) !== 1)
+ {
+ log_message('debug', 'Session: Redis::'.$this->_delete_name.'() expected to return 1, got '.var_export($result, TRUE).' instead.');
+ }
+
+ $this->_cookie_destroy();
+ return $this->_success;
+ }
+
+ return $this->_failure;
+ }
+
+ // ------------------------------------------------------------------------
+
+ /**
+ * Garbage Collector
+ *
+ * Deletes expired sessions
+ *
+ * @param int $maxlifetime Maximum lifetime of sessions
+ * @return bool
+ */
+ public function gc($maxlifetime)
+ {
+ // Not necessary, Redis takes care of that.
+ return $this->_success;
+ }
+
+ // --------------------------------------------------------------------
+
+ /**
+ * Update Timestamp
+ *
+ * Update session timestamp without modifying data
+ *
+ * @param string $id Session ID
+ * @param string $data Unknown & unused
+ * @return bool
+ */
+ public function updateTimestamp($id, $unknown)
+ {
+ return $this->_redis->{$this->_setTimeout_name}($this->_key_prefix.$id, $this->_config['expiration']);
+ }
+
+ // --------------------------------------------------------------------
+
+ /**
+ * Validate ID
+ *
+ * Checks whether a session ID record exists server-side,
+ * to enforce session.use_strict_mode.
+ *
+ * @param string $id Session ID
+ * @return bool
+ */
+ public function validateId($id)
+ {
+ return (bool) $this->_redis->exists($this->_key_prefix.$id);
+ }
+
+ // ------------------------------------------------------------------------
+
+ /**
+ * Get lock
+ *
+ * Acquires an (emulated) lock.
+ *
+ * @param string $session_id Session ID
+ * @return bool
+ */
+ protected function _get_lock($session_id)
+ {
+ // PHP 7 reuses the SessionHandler object on regeneration,
+ // so we need to check here if the lock key is for the
+ // correct session ID.
+ if ($this->_lock_key === $this->_key_prefix.$session_id.':lock')
+ {
+ return $this->_redis->{$this->_setTimeout_name}($this->_lock_key, 300);
+ }
+
+ // 30 attempts to obtain a lock, in case another request already has it
+ $lock_key = $this->_key_prefix.$session_id.':lock';
+ $attempt = 0;
+ do
+ {
+ if (($ttl = $this->_redis->ttl($lock_key)) > 0)
+ {
+ sleep(1);
+ continue;
+ }
+
+ if ($ttl === -2 && ! $this->_redis->set($lock_key, time(), array('nx', 'ex' => 300)))
+ {
+ // Sleep for 1s to wait for lock releases.
+ sleep(1);
+ continue;
+ }
+ elseif ( ! $this->_redis->setex($lock_key, 300, time()))
+ {
+ log_message('error', 'Session: Error while trying to obtain lock for '.$this->_key_prefix.$session_id);
+ return FALSE;
+ }
+
+ $this->_lock_key = $lock_key;
+ break;
+ }
+ while (++$attempt < 30);
+
+ if ($attempt === 30)
+ {
+ log_message('error', 'Session: Unable to obtain lock for '.$this->_key_prefix.$session_id.' after 30 attempts, aborting.');
+ return FALSE;
+ }
+ elseif ($ttl === -1)
+ {
+ log_message('debug', 'Session: Lock for '.$this->_key_prefix.$session_id.' had no TTL, overriding.');
+ }
+
+ $this->_lock = TRUE;
+ return TRUE;
+ }
+
+ // ------------------------------------------------------------------------
+
+ /**
+ * Release lock
+ *
+ * Releases a previously acquired lock
+ *
+ * @return bool
+ */
+ protected function _release_lock()
+ {
+ if (isset($this->_redis, $this->_lock_key) && $this->_lock)
+ {
+ if ( ! $this->_redis->{$this->_delete_name}($this->_lock_key))
+ {
+ log_message('error', 'Session: Error while trying to free lock for '.$this->_lock_key);
+ return FALSE;
+ }
+
+ $this->_lock_key = NULL;
+ $this->_lock = FALSE;
+ }
+
+ return TRUE;
+ }
+
+}
diff --git a/system/libraries/Session/drivers/index.html b/system/libraries/Session/drivers/index.html
new file mode 100644
index 000000000..bcb7cae34
--- /dev/null
+++ b/system/libraries/Session/drivers/index.html
@@ -0,0 +1,11 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+ <title>403 Forbidden</title>
+</head>
+<body>
+
+<p>Directory access is forbidden.</p>
+
+</body>
+</html>
diff --git a/system/libraries/Session/index.html b/system/libraries/Session/index.html
new file mode 100644
index 000000000..bcb7cae34
--- /dev/null
+++ b/system/libraries/Session/index.html
@@ -0,0 +1,11 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+ <title>403 Forbidden</title>
+</head>
+<body>
+
+<p>Directory access is forbidden.</p>
+
+</body>
+</html>
diff --git a/system/libraries/Sha1.php b/system/libraries/Sha1.php
deleted file mode 100644
index 33778f965..000000000
--- a/system/libraries/Sha1.php
+++ /dev/null
@@ -1,251 +0,0 @@
-<?php if ( ! defined('BASEPATH')) exit('No direct script access allowed');
-/**
- * CodeIgniter
- *
- * An open source application development framework for PHP 5.1.6 or newer
- *
- * @package CodeIgniter
- * @author ExpressionEngine Dev Team
- * @copyright Copyright (c) 2008 - 2014, EllisLab, Inc.
- * @license http://codeigniter.com/user_guide/license.html
- * @link http://codeigniter.com
- * @since Version 1.0
- * @filesource
- */
-
-// ------------------------------------------------------------------------
-
-/**
- * SHA1 Encoding Class
- *
- * Purpose: Provides 160 bit hashing using The Secure Hash Algorithm
- * developed at the National Institute of Standards and Technology. The 40
- * character SHA1 message hash is computationally infeasible to crack.
- *
- * This class is a fallback for servers that are not running PHP greater than
- * 4.3, or do not have the MHASH library.
- *
- * This class is based on two scripts:
- *
- * Marcus Campbell's PHP implementation (GNU license)
- * http://www.tecknik.net/sha-1/
- *
- * ...which is based on Paul Johnston's JavaScript version
- * (BSD license). http://pajhome.org.uk/
- *
- * I encapsulated the functions and wrote one additional method to fix
- * a hex conversion bug. - Rick Ellis
- *
- * @package CodeIgniter
- * @subpackage Libraries
- * @category Encryption
- * @author ExpressionEngine Dev Team
- * @link http://codeigniter.com/user_guide/general/encryption.html
- */
-class CI_SHA1 {
-
- public function __construct()
- {
- log_message('debug', "SHA1 Class Initialized");
- }
-
- /**
- * Generate the Hash
- *
- * @access public
- * @param string
- * @return string
- */
- function generate($str)
- {
- $n = ((strlen($str) + 8) >> 6) + 1;
-
- for ($i = 0; $i < $n * 16; $i++)
- {
- $x[$i] = 0;
- }
-
- for ($i = 0; $i < strlen($str); $i++)
- {
- $x[$i >> 2] |= ord(substr($str, $i, 1)) << (24 - ($i % 4) * 8);
- }
-
- $x[$i >> 2] |= 0x80 << (24 - ($i % 4) * 8);
-
- $x[$n * 16 - 1] = strlen($str) * 8;
-
- $a = 1732584193;
- $b = -271733879;
- $c = -1732584194;
- $d = 271733878;
- $e = -1009589776;
-
- for ($i = 0; $i < count($x); $i += 16)
- {
- $olda = $a;
- $oldb = $b;
- $oldc = $c;
- $oldd = $d;
- $olde = $e;
-
- for ($j = 0; $j < 80; $j++)
- {
- if ($j < 16)
- {
- $w[$j] = $x[$i + $j];
- }
- else
- {
- $w[$j] = $this->_rol($w[$j - 3] ^ $w[$j - 8] ^ $w[$j - 14] ^ $w[$j - 16], 1);
- }
-
- $t = $this->_safe_add($this->_safe_add($this->_rol($a, 5), $this->_ft($j, $b, $c, $d)), $this->_safe_add($this->_safe_add($e, $w[$j]), $this->_kt($j)));
-
- $e = $d;
- $d = $c;
- $c = $this->_rol($b, 30);
- $b = $a;
- $a = $t;
- }
-
- $a = $this->_safe_add($a, $olda);
- $b = $this->_safe_add($b, $oldb);
- $c = $this->_safe_add($c, $oldc);
- $d = $this->_safe_add($d, $oldd);
- $e = $this->_safe_add($e, $olde);
- }
-
- return $this->_hex($a).$this->_hex($b).$this->_hex($c).$this->_hex($d).$this->_hex($e);
- }
-
- // --------------------------------------------------------------------
-
- /**
- * Convert a decimal to hex
- *
- * @access private
- * @param string
- * @return string
- */
- function _hex($str)
- {
- $str = dechex($str);
-
- if (strlen($str) == 7)
- {
- $str = '0'.$str;
- }
-
- return $str;
- }
-
- // --------------------------------------------------------------------
-
- /**
- * Return result based on iteration
- *
- * @access private
- * @return string
- */
- function _ft($t, $b, $c, $d)
- {
- if ($t < 20)
- return ($b & $c) | ((~$b) & $d);
- if ($t < 40)
- return $b ^ $c ^ $d;
- if ($t < 60)
- return ($b & $c) | ($b & $d) | ($c & $d);
-
- return $b ^ $c ^ $d;
- }
-
- // --------------------------------------------------------------------
-
- /**
- * Determine the additive constant
- *
- * @access private
- * @return string
- */
- function _kt($t)
- {
- if ($t < 20)
- {
- return 1518500249;
- }
- else if ($t < 40)
- {
- return 1859775393;
- }
- else if ($t < 60)
- {
- return -1894007588;
- }
- else
- {
- return -899497514;
- }
- }
-
- // --------------------------------------------------------------------
-
- /**
- * Add integers, wrapping at 2^32
- *
- * @access private
- * @return string
- */
- function _safe_add($x, $y)
- {
- $lsw = ($x & 0xFFFF) + ($y & 0xFFFF);
- $msw = ($x >> 16) + ($y >> 16) + ($lsw >> 16);
-
- return ($msw << 16) | ($lsw & 0xFFFF);
- }
-
- // --------------------------------------------------------------------
-
- /**
- * Bitwise rotate a 32-bit number
- *
- * @access private
- * @return integer
- */
- function _rol($num, $cnt)
- {
- return ($num << $cnt) | $this->_zero_fill($num, 32 - $cnt);
- }
-
- // --------------------------------------------------------------------
-
- /**
- * Pad string with zero
- *
- * @access private
- * @return string
- */
- function _zero_fill($a, $b)
- {
- $bin = decbin($a);
-
- if (strlen($bin) < $b)
- {
- $bin = 0;
- }
- else
- {
- $bin = substr($bin, 0, strlen($bin) - $b);
- }
-
- for ($i=0; $i < $b; $i++)
- {
- $bin = "0".$bin;
- }
-
- return bindec($bin);
- }
-}
-// END CI_SHA
-
-/* End of file Sha1.php */
-/* Location: ./system/libraries/Sha1.php */ \ No newline at end of file
diff --git a/system/libraries/Table.php b/system/libraries/Table.php
index a2353d1e1..a033ced21 100644
--- a/system/libraries/Table.php
+++ b/system/libraries/Table.php
@@ -1,19 +1,42 @@
-<?php if ( ! defined('BASEPATH')) exit('No direct script access allowed');
+<?php
/**
* CodeIgniter
*
- * An open source application development framework for PHP 5.1.6 or newer
+ * An open source application development framework for PHP
*
- * @package CodeIgniter
- * @author ExpressionEngine Dev Team
- * @copyright Copyright (c) 2008 - 2014, EllisLab, Inc.
- * @license http://codeigniter.com/user_guide/license.html
- * @link http://codeigniter.com
- * @since Version 1.3.1
+ * This content is released under the MIT License (MIT)
+ *
+ * Copyright (c) 2019 - 2022, CodeIgniter Foundation
+ *
+ * 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.
+ *
+ * @package CodeIgniter
+ * @author EllisLab Dev Team
+ * @copyright Copyright (c) 2008 - 2014, EllisLab, Inc. (https://ellislab.com/)
+ * @copyright Copyright (c) 2014 - 2019, British Columbia Institute of Technology (https://bcit.ca/)
+ * @copyright Copyright (c) 2019 - 2022, CodeIgniter Foundation (https://codeigniter.com/)
+ * @license https://opensource.org/licenses/MIT MIT License
+ * @link https://codeigniter.com
+ * @since Version 1.3.1
* @filesource
*/
-
-// ------------------------------------------------------------------------
+defined('BASEPATH') OR exit('No direct script access allowed');
/**
* HTML Table Generating Class
@@ -23,23 +46,82 @@
* @package CodeIgniter
* @subpackage Libraries
* @category HTML Tables
- * @author ExpressionEngine Dev Team
- * @link http://codeigniter.com/user_guide/libraries/uri.html
+ * @author EllisLab Dev Team
+ * @link https://codeigniter.com/userguide3/libraries/table.html
*/
class CI_Table {
- var $rows = array();
- var $heading = array();
- var $auto_heading = TRUE;
- var $caption = NULL;
- var $template = NULL;
- var $newline = "\n";
- var $empty_cells = "";
- var $function = FALSE;
+ /**
+ * Data for table rows
+ *
+ * @var array
+ */
+ public $rows = array();
+
+ /**
+ * Data for table heading
+ *
+ * @var array
+ */
+ public $heading = array();
+
+ /**
+ * Whether or not to automatically create the table header
+ *
+ * @var bool
+ */
+ public $auto_heading = TRUE;
+
+ /**
+ * Table caption
+ *
+ * @var string
+ */
+ public $caption = NULL;
+
+ /**
+ * Table layout template
+ *
+ * @var array
+ */
+ public $template = NULL;
+
+ /**
+ * Newline setting
+ *
+ * @var string
+ */
+ public $newline = "\n";
+
+ /**
+ * Contents of empty cells
+ *
+ * @var string
+ */
+ public $empty_cells = '';
+
+ /**
+ * Callback for custom table layout
+ *
+ * @var function
+ */
+ public $function = NULL;
- public function __construct()
+ /**
+ * Set the template from the table config file if it exists
+ *
+ * @param array $config (default: array())
+ * @return void
+ */
+ public function __construct($config = array())
{
- log_message('debug', "Table Class Initialized");
+ // initialize config
+ foreach ($config as $key => $val)
+ {
+ $this->template[$key] = $val;
+ }
+
+ log_message('info', 'Table Class Initialized');
}
// --------------------------------------------------------------------
@@ -47,11 +129,10 @@ class CI_Table {
/**
* Set the template
*
- * @access public
- * @param array
- * @return void
+ * @param array $template
+ * @return bool
*/
- function set_template($template)
+ public function set_template($template)
{
if ( ! is_array($template))
{
@@ -59,6 +140,7 @@ class CI_Table {
}
$this->template = $template;
+ return TRUE;
}
// --------------------------------------------------------------------
@@ -68,32 +150,30 @@ class CI_Table {
*
* Can be passed as an array or discreet params
*
- * @access public
* @param mixed
- * @return void
+ * @return CI_Table
*/
- function set_heading()
+ public function set_heading($args = array())
{
- $args = func_get_args();
- $this->heading = $this->_prep_args($args);
+ $this->heading = $this->_prep_args(func_get_args());
+ return $this;
}
// --------------------------------------------------------------------
/**
- * Set columns. Takes a one-dimensional array as input and creates
+ * Set columns. Takes a one-dimensional array as input and creates
* a multi-dimensional array with a depth equal to the number of
- * columns. This allows a single array with many elements to be
+ * columns. This allows a single array with many elements to be
* displayed in a table that has a fixed column count.
*
- * @access public
- * @param array
- * @param int
- * @return void
+ * @param array $array
+ * @param int $col_limit
+ * @return array
*/
- function make_columns($array = array(), $col_limit = 0)
+ public function make_columns($array = array(), $col_limit = 0)
{
- if ( ! is_array($array) OR count($array) == 0)
+ if ( ! is_array($array) OR count($array) === 0 OR ! is_int($col_limit))
{
return FALSE;
}
@@ -102,13 +182,13 @@ class CI_Table {
// will want headings from a one-dimensional array
$this->auto_heading = FALSE;
- if ($col_limit == 0)
+ if ($col_limit === 0)
{
return $array;
}
$new = array();
- while (count($array) > 0)
+ do
{
$temp = array_splice($array, 0, $col_limit);
@@ -122,6 +202,7 @@ class CI_Table {
$new[] = $temp;
}
+ while (count($array) > 0);
return $new;
}
@@ -133,13 +214,13 @@ class CI_Table {
*
* Can be passed as an array or discreet params
*
- * @access public
- * @param mixed
- * @return void
+ * @param mixed $value
+ * @return CI_Table
*/
- function set_empty($value)
+ public function set_empty($value)
{
$this->empty_cells = $value;
+ return $this;
}
// --------------------------------------------------------------------
@@ -149,14 +230,13 @@ class CI_Table {
*
* Can be passed as an array or discreet params
*
- * @access public
* @param mixed
- * @return void
+ * @return CI_Table
*/
- function add_row()
+ public function add_row($args = array())
{
- $args = func_get_args();
- $this->rows[] = $this->_prep_args($args);
+ $this->rows[] = $this->_prep_args(func_get_args());
+ return $this;
}
// --------------------------------------------------------------------
@@ -166,42 +246,22 @@ class CI_Table {
*
* Ensures a standard associative array format for all cell data
*
- * @access public
- * @param type
- * @return type
+ * @param array
+ * @return array
*/
- function _prep_args($args)
+ protected function _prep_args($args)
{
// If there is no $args[0], skip this and treat as an associative array
// This can happen if there is only a single key, for example this is passed to table->generate
// array(array('foo'=>'bar'))
- if (isset($args[0]) AND (count($args) == 1 && is_array($args[0])))
+ if (isset($args[0]) && count($args) === 1 && is_array($args[0]) && ! isset($args[0]['data']))
{
- // args sent as indexed array
- if ( ! isset($args[0]['data']))
- {
- foreach ($args[0] as $key => $val)
- {
- if (is_array($val) && isset($val['data']))
- {
- $args[$key] = $val;
- }
- else
- {
- $args[$key] = array('data' => $val);
- }
- }
- }
+ $args = $args[0];
}
- else
+
+ foreach ($args as $key => $val)
{
- foreach ($args as $key => $val)
- {
- if ( ! is_array($val))
- {
- $args[$key] = array('data' => $val);
- }
- }
+ is_array($val) OR $args[$key] = array('data' => $val);
}
return $args;
@@ -212,13 +272,13 @@ class CI_Table {
/**
* Add a table caption
*
- * @access public
- * @param string
- * @return void
+ * @param string $caption
+ * @return CI_Table
*/
- function set_caption($caption)
+ public function set_caption($caption)
{
$this->caption = $caption;
+ return $this;
}
// --------------------------------------------------------------------
@@ -226,29 +286,27 @@ class CI_Table {
/**
* Generate the table
*
- * @access public
- * @param mixed
+ * @param mixed $table_data
* @return string
*/
- function generate($table_data = NULL)
+ public function generate($table_data = NULL)
{
// The table data can optionally be passed to this function
// either as a database result object or an array
- if ( ! is_null($table_data))
+ if ( ! empty($table_data))
{
- if (is_object($table_data))
+ if ($table_data instanceof CI_DB_result)
{
- $this->_set_from_object($table_data);
+ $this->_set_from_db_result($table_data);
}
elseif (is_array($table_data))
{
- $set_heading = (count($this->heading) == 0 AND $this->auto_heading == FALSE) ? FALSE : TRUE;
- $this->_set_from_array($table_data, $set_heading);
+ $this->_set_from_array($table_data);
}
}
- // Is there anything to display? No? Smite them!
- if (count($this->heading) == 0 AND count($this->rows) == 0)
+ // Is there anything to display? No? Smite them!
+ if (empty($this->heading) && empty($this->rows))
{
return 'Undefined table data';
}
@@ -256,29 +314,26 @@ class CI_Table {
// Compile and validate the template date
$this->_compile_template();
- // set a custom cell manipulation function to a locally scoped variable so its callable
- $function = $this->function;
+ // Validate a possibly existing custom cell manipulation function
+ if (isset($this->function) && ! is_callable($this->function))
+ {
+ $this->function = NULL;
+ }
// Build the table!
- $out = $this->template['table_open'];
- $out .= $this->newline;
+ $out = $this->template['table_open'].$this->newline;
// Add any caption here
if ($this->caption)
{
- $out .= $this->newline;
- $out .= '<caption>' . $this->caption . '</caption>';
- $out .= $this->newline;
+ $out .= '<caption>'.$this->caption.'</caption>'.$this->newline;
}
// Is there a table heading to display?
- if (count($this->heading) > 0)
+ if ( ! empty($this->heading))
{
- $out .= $this->template['thead_open'];
- $out .= $this->newline;
- $out .= $this->template['heading_row_start'];
- $out .= $this->newline;
+ $out .= $this->template['thead_open'].$this->newline.$this->template['heading_row_start'].$this->newline;
foreach ($this->heading as $heading)
{
@@ -286,28 +341,22 @@ class CI_Table {
foreach ($heading as $key => $val)
{
- if ($key != 'data')
+ if ($key !== 'data')
{
- $temp = str_replace('<th', "<th $key='$val'", $temp);
+ $temp = str_replace('<th', '<th '.$key.'="'.$val.'"', $temp);
}
}
- $out .= $temp;
- $out .= isset($heading['data']) ? $heading['data'] : '';
- $out .= $this->template['heading_cell_end'];
+ $out .= $temp.(isset($heading['data']) ? $heading['data'] : '').$this->template['heading_cell_end'];
}
- $out .= $this->template['heading_row_end'];
- $out .= $this->newline;
- $out .= $this->template['thead_close'];
- $out .= $this->newline;
+ $out .= $this->template['heading_row_end'].$this->newline.$this->template['thead_close'].$this->newline;
}
// Build the table rows
- if (count($this->rows) > 0)
+ if ( ! empty($this->rows))
{
- $out .= $this->template['tbody_open'];
- $out .= $this->newline;
+ $out .= $this->template['tbody_open'].$this->newline;
$i = 1;
foreach ($this->rows as $row)
@@ -318,10 +367,9 @@ class CI_Table {
}
// We use modulus to alternate the row colors
- $name = (fmod($i++, 2)) ? '' : 'alt_';
+ $name = fmod($i++, 2) ? '' : 'alt_';
- $out .= $this->template['row_'.$name.'start'];
- $out .= $this->newline;
+ $out .= $this->template['row_'.$name.'start'].$this->newline;
foreach ($row as $cell)
{
@@ -329,40 +377,35 @@ class CI_Table {
foreach ($cell as $key => $val)
{
- if ($key != 'data')
+ if ($key !== 'data')
{
- $temp = str_replace('<td', "<td $key='$val'", $temp);
+ $temp = str_replace('<td', '<td '.$key.'="'.$val.'"', $temp);
}
}
$cell = isset($cell['data']) ? $cell['data'] : '';
$out .= $temp;
- if ($cell === "" OR $cell === NULL)
+ if ($cell === '' OR $cell === NULL)
{
$out .= $this->empty_cells;
}
+ elseif (isset($this->function))
+ {
+ $out .= call_user_func($this->function, $cell);
+ }
else
{
- if ($function !== FALSE && is_callable($function))
- {
- $out .= call_user_func($function, $cell);
- }
- else
- {
- $out .= $cell;
- }
+ $out .= $cell;
}
$out .= $this->template['cell_'.$name.'end'];
}
- $out .= $this->template['row_'.$name.'end'];
- $out .= $this->newline;
+ $out .= $this->template['row_'.$name.'end'].$this->newline;
}
- $out .= $this->template['tbody_close'];
- $out .= $this->newline;
+ $out .= $this->template['tbody_close'].$this->newline;
}
$out .= $this->template['table_close'];
@@ -378,14 +421,15 @@ class CI_Table {
/**
* Clears the table arrays. Useful if multiple tables are being generated
*
- * @access public
- * @return void
+ * @return CI_Table
*/
- function clear()
+ public function clear()
{
- $this->rows = array();
- $this->heading = array();
- $this->auto_heading = TRUE;
+ $this->rows = array();
+ $this->heading = array();
+ $this->auto_heading = TRUE;
+ $this->caption = NULL;
+ return $this;
}
// --------------------------------------------------------------------
@@ -393,36 +437,20 @@ class CI_Table {
/**
* Set table data from a database result object
*
- * @access public
- * @param object
+ * @param CI_DB_result $object Database result object
* @return void
*/
- function _set_from_object($query)
+ protected function _set_from_db_result($object)
{
- if ( ! is_object($query))
- {
- return FALSE;
- }
-
// First generate the headings from the table column names
- if (count($this->heading) == 0)
+ if ($this->auto_heading === TRUE && empty($this->heading))
{
- if ( ! method_exists($query, 'list_fields'))
- {
- return FALSE;
- }
-
- $this->heading = $this->_prep_args($query->list_fields());
+ $this->heading = $this->_prep_args($object->list_fields());
}
- // Next blast through the result array and build out the rows
-
- if ($query->num_rows() > 0)
+ foreach ($object->result_array() as $row)
{
- foreach ($query->result_array() as $row)
- {
- $this->rows[] = $this->_prep_args($row);
- }
+ $this->rows[] = $this->_prep_args($row);
}
}
@@ -431,31 +459,19 @@ class CI_Table {
/**
* Set table data from an array
*
- * @access public
- * @param array
+ * @param array $data
* @return void
*/
- function _set_from_array($data, $set_heading = TRUE)
+ protected function _set_from_array($data)
{
- if ( ! is_array($data) OR count($data) == 0)
+ if ($this->auto_heading === TRUE && empty($this->heading))
{
- return FALSE;
+ $this->heading = $this->_prep_args(array_shift($data));
}
- $i = 0;
- foreach ($data as $row)
+ foreach ($data as &$row)
{
- // If a heading hasn't already been set we'll use the first row of the array as the heading
- if ($i == 0 AND count($data) > 1 AND count($this->heading) == 0 AND $set_heading == TRUE)
- {
- $this->heading = $this->_prep_args($row);
- }
- else
- {
- $this->rows[] = $this->_prep_args($row);
- }
-
- $i++;
+ $this->rows[] = $this->_prep_args($row);
}
}
@@ -464,12 +480,11 @@ class CI_Table {
/**
* Compile Template
*
- * @access private
* @return void
*/
- function _compile_template()
+ protected function _compile_template()
{
- if ($this->template == NULL)
+ if ($this->template === NULL)
{
$this->template = $this->_default_template();
return;
@@ -490,42 +505,36 @@ class CI_Table {
/**
* Default Template
*
- * @access private
- * @return void
+ * @return array
*/
- function _default_template()
+ protected function _default_template()
{
- return array (
- 'table_open' => '<table border="0" cellpadding="4" cellspacing="0">',
+ return array(
+ 'table_open' => '<table border="0" cellpadding="4" cellspacing="0">',
- 'thead_open' => '<thead>',
- 'thead_close' => '</thead>',
+ 'thead_open' => '<thead>',
+ 'thead_close' => '</thead>',
- 'heading_row_start' => '<tr>',
- 'heading_row_end' => '</tr>',
- 'heading_cell_start' => '<th>',
- 'heading_cell_end' => '</th>',
+ 'heading_row_start' => '<tr>',
+ 'heading_row_end' => '</tr>',
+ 'heading_cell_start' => '<th>',
+ 'heading_cell_end' => '</th>',
- 'tbody_open' => '<tbody>',
- 'tbody_close' => '</tbody>',
+ 'tbody_open' => '<tbody>',
+ 'tbody_close' => '</tbody>',
- 'row_start' => '<tr>',
- 'row_end' => '</tr>',
- 'cell_start' => '<td>',
- 'cell_end' => '</td>',
+ 'row_start' => '<tr>',
+ 'row_end' => '</tr>',
+ 'cell_start' => '<td>',
+ 'cell_end' => '</td>',
- 'row_alt_start' => '<tr>',
- 'row_alt_end' => '</tr>',
- 'cell_alt_start' => '<td>',
- 'cell_alt_end' => '</td>',
+ 'row_alt_start' => '<tr>',
+ 'row_alt_end' => '</tr>',
+ 'cell_alt_start' => '<td>',
+ 'cell_alt_end' => '</td>',
- 'table_close' => '</table>'
- );
+ 'table_close' => '</table>'
+ );
}
-
}
-
-
-/* End of file Table.php */
-/* Location: ./system/libraries/Table.php */ \ No newline at end of file
diff --git a/system/libraries/Trackback.php b/system/libraries/Trackback.php
index 898553cd1..9246ec6e9 100644
--- a/system/libraries/Trackback.php
+++ b/system/libraries/Trackback.php
@@ -1,19 +1,42 @@
-<?php if ( ! defined('BASEPATH')) exit('No direct script access allowed');
+<?php
/**
* CodeIgniter
*
- * An open source application development framework for PHP 5.1.6 or newer
+ * An open source application development framework for PHP
*
- * @package CodeIgniter
- * @author ExpressionEngine Dev Team
- * @copyright Copyright (c) 2008 - 2014, EllisLab, Inc.
- * @license http://codeigniter.com/user_guide/license.html
- * @link http://codeigniter.com
- * @since Version 1.0
+ * This content is released under the MIT License (MIT)
+ *
+ * Copyright (c) 2019 - 2022, CodeIgniter Foundation
+ *
+ * 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.
+ *
+ * @package CodeIgniter
+ * @author EllisLab Dev Team
+ * @copyright Copyright (c) 2008 - 2014, EllisLab, Inc. (https://ellislab.com/)
+ * @copyright Copyright (c) 2014 - 2019, British Columbia Institute of Technology (https://bcit.ca/)
+ * @copyright Copyright (c) 2019 - 2022, CodeIgniter Foundation (https://codeigniter.com/)
+ * @license https://opensource.org/licenses/MIT MIT License
+ * @link https://codeigniter.com
+ * @since Version 1.0.0
* @filesource
*/
-
-// ------------------------------------------------------------------------
+defined('BASEPATH') OR exit('No direct script access allowed');
/**
* Trackback Class
@@ -23,26 +46,65 @@
* @package CodeIgniter
* @subpackage Libraries
* @category Trackbacks
- * @author ExpressionEngine Dev Team
- * @link http://codeigniter.com/user_guide/libraries/trackback.html
+ * @author EllisLab Dev Team
+ * @link https://codeigniter.com/userguide3/libraries/trackback.html
*/
class CI_Trackback {
- var $time_format = 'local';
- var $charset = 'UTF-8';
- var $data = array('url' => '', 'title' => '', 'excerpt' => '', 'blog_name' => '', 'charset' => '');
- var $convert_ascii = TRUE;
- var $response = '';
- var $error_msg = array();
+ /**
+ * Character set
+ *
+ * @var string
+ */
+ public $charset = 'UTF-8';
+
+ /**
+ * Trackback data
+ *
+ * @var array
+ */
+ public $data = array(
+ 'url' => '',
+ 'title' => '',
+ 'excerpt' => '',
+ 'blog_name' => '',
+ 'charset' => ''
+ );
+
+ /**
+ * Convert ASCII flag
+ *
+ * Whether to convert high-ASCII and MS Word
+ * characters to HTML entities.
+ *
+ * @var bool
+ */
+ public $convert_ascii = TRUE;
+
+ /**
+ * Response
+ *
+ * @var string
+ */
+ public $response = '';
+
+ /**
+ * Error messages list
+ *
+ * @var string[]
+ */
+ public $error_msg = array();
+
+ // --------------------------------------------------------------------
/**
* Constructor
*
- * @access public
+ * @return void
*/
public function __construct()
{
- log_message('debug', "Trackback Class Initialized");
+ log_message('info', 'Trackback Class Initialized');
}
// --------------------------------------------------------------------
@@ -50,11 +112,10 @@ class CI_Trackback {
/**
* Send Trackback
*
- * @access public
* @param array
* @return bool
*/
- function send($tb_data)
+ public function send($tb_data)
{
if ( ! is_array($tb_data))
{
@@ -73,38 +134,32 @@ class CI_Trackback {
switch ($item)
{
- case 'ping_url' : $$item = $this->extract_urls($tb_data[$item]);
+ case 'ping_url':
+ $$item = $this->extract_urls($tb_data[$item]);
break;
- case 'excerpt' : $$item = $this->limit_characters($this->convert_xml(strip_tags(stripslashes($tb_data[$item]))));
+ case 'excerpt':
+ $$item = $this->limit_characters($this->convert_xml(strip_tags(stripslashes($tb_data[$item]))));
break;
- case 'url' : $$item = str_replace('&#45;', '-', $this->convert_xml(strip_tags(stripslashes($tb_data[$item]))));
+ case 'url':
+ $$item = str_replace('&#45;', '-', $this->convert_xml(strip_tags(stripslashes($tb_data[$item]))));
break;
- default : $$item = $this->convert_xml(strip_tags(stripslashes($tb_data[$item])));
+ default:
+ $$item = $this->convert_xml(strip_tags(stripslashes($tb_data[$item])));
break;
}
// Convert High ASCII Characters
- if ($this->convert_ascii == TRUE)
+ if ($this->convert_ascii === TRUE && in_array($item, array('excerpt', 'title', 'blog_name'), TRUE))
{
- if ($item == 'excerpt')
- {
- $$item = $this->convert_ascii($$item);
- }
- elseif ($item == 'title')
- {
- $$item = $this->convert_ascii($$item);
- }
- elseif ($item == 'blog_name')
- {
- $$item = $this->convert_ascii($$item);
- }
+ $$item = $this->convert_ascii($$item);
}
}
// Build the Trackback data string
- $charset = ( ! isset($tb_data['charset'])) ? $this->charset : $tb_data['charset'];
+ $charset = isset($tb_data['charset']) ? $tb_data['charset'] : $this->charset;
- $data = "url=".rawurlencode($url)."&title=".rawurlencode($title)."&blog_name=".rawurlencode($blog_name)."&excerpt=".rawurlencode($excerpt)."&charset=".rawurlencode($charset);
+ $data = 'url='.rawurlencode($url).'&title='.rawurlencode($title).'&blog_name='.rawurlencode($blog_name)
+ .'&excerpt='.rawurlencode($excerpt).'&charset='.rawurlencode($charset);
// Send Trackback(s)
$return = TRUE;
@@ -112,7 +167,7 @@ class CI_Trackback {
{
foreach ($ping_url as $url)
{
- if ($this->process($url, $data) == FALSE)
+ if ($this->process($url, $data) === FALSE)
{
$return = FALSE;
}
@@ -132,29 +187,35 @@ class CI_Trackback {
* If the data is valid it is set to the $this->data array
* so that it can be inserted into a database.
*
- * @access public
* @return bool
*/
- function receive()
+ public function receive()
{
foreach (array('url', 'title', 'blog_name', 'excerpt') as $val)
{
- if ( ! isset($_POST[$val]) OR $_POST[$val] == '')
+ if (empty($_POST[$val]))
{
$this->set_error('The following required POST variable is missing: '.$val);
return FALSE;
}
- $this->data['charset'] = ( ! isset($_POST['charset'])) ? 'auto' : strtoupper(trim($_POST['charset']));
+ $this->data['charset'] = isset($_POST['charset']) ? strtoupper(trim($_POST['charset'])) : 'auto';
- if ($val != 'url' && function_exists('mb_convert_encoding'))
+ if ($val !== 'url' && MB_ENABLED === TRUE)
{
- $_POST[$val] = mb_convert_encoding($_POST[$val], $this->charset, $this->data['charset']);
+ if (MB_ENABLED === TRUE)
+ {
+ $_POST[$val] = mb_convert_encoding($_POST[$val], $this->charset, $this->data['charset']);
+ }
+ elseif (ICONV_ENABLED === TRUE)
+ {
+ $_POST[$val] = @iconv($this->data['charset'], $this->charset.'//IGNORE', $_POST[$val]);
+ }
}
- $_POST[$val] = ($val != 'url') ? $this->convert_xml(strip_tags($_POST[$val])) : strip_tags($_POST[$val]);
+ $_POST[$val] = ($val !== 'url') ? $this->convert_xml(strip_tags($_POST[$val])) : strip_tags($_POST[$val]);
- if ($val == 'excerpt')
+ if ($val === 'excerpt')
{
$_POST['excerpt'] = $this->limit_characters($_POST['excerpt']);
}
@@ -170,18 +231,16 @@ class CI_Trackback {
/**
* Send Trackback Error Message
*
- * Allows custom errors to be set. By default it
+ * Allows custom errors to be set. By default it
* sends the "incomplete information" error, as that's
* the most common one.
*
- * @access public
* @param string
* @return void
*/
- function send_error($message = 'Incomplete Information')
+ public function send_error($message = 'Incomplete Information')
{
- echo "<?xml version=\"1.0\" encoding=\"utf-8\"?".">\n<response>\n<error>1</error>\n<message>".$message."</message>\n</response>";
- exit;
+ exit('<?xml version="1.0" encoding="utf-8"?'.">\n<response>\n<error>1</error>\n<message>".$message."</message>\n</response>");
}
// --------------------------------------------------------------------
@@ -192,13 +251,11 @@ class CI_Trackback {
* This should be called when a trackback has been
* successfully received and inserted.
*
- * @access public
* @return void
*/
- function send_success()
+ public function send_success()
{
- echo "<?xml version=\"1.0\" encoding=\"utf-8\"?".">\n<response>\n<error>0</error>\n</response>";
- exit;
+ exit('<?xml version="1.0" encoding="utf-8"?'.">\n<response>\n<error>0</error>\n</response>");
}
// --------------------------------------------------------------------
@@ -206,13 +263,12 @@ class CI_Trackback {
/**
* Fetch a particular item
*
- * @access public
* @param string
* @return string
*/
- function data($item)
+ public function data($item)
{
- return ( ! isset($this->data[$item])) ? '' : $this->data[$item];
+ return isset($this->data[$item]) ? $this->data[$item] : '';
}
// --------------------------------------------------------------------
@@ -221,14 +277,13 @@ class CI_Trackback {
* Process Trackback
*
* Opens a socket connection and passes the data to
- * the server. Returns TRUE on success, FALSE on failure
+ * the server. Returns TRUE on success, FALSE on failure
*
- * @access public
* @param string
* @param string
* @return bool
*/
- function process($url, $data)
+ public function process($url, $data)
{
$target = parse_url($url);
@@ -240,43 +295,37 @@ class CI_Trackback {
}
// Build the path
- $ppath = ( ! isset($target['path'])) ? $url : $target['path'];
-
- $path = (isset($target['query']) && $target['query'] != "") ? $ppath.'?'.$target['query'] : $ppath;
+ $path = isset($target['path']) ? $target['path'] : $url;
+ empty($target['query']) OR $path .= '?'.$target['query'];
// Add the Trackback ID to the data string
if ($id = $this->get_id($url))
{
- $data = "tb_id=".$id."&".$data;
+ $data = 'tb_id='.$id.'&'.$data;
}
// Transfer the data
- fputs ($fp, "POST " . $path . " HTTP/1.0\r\n" );
- fputs ($fp, "Host: " . $target['host'] . "\r\n" );
- fputs ($fp, "Content-type: application/x-www-form-urlencoded\r\n" );
- fputs ($fp, "Content-length: " . strlen($data) . "\r\n" );
- fputs ($fp, "Connection: close\r\n\r\n" );
- fputs ($fp, $data);
+ fputs($fp, 'POST '.$path." HTTP/1.0\r\n");
+ fputs($fp, 'Host: '.$target['host']."\r\n");
+ fputs($fp, "Content-type: application/x-www-form-urlencoded\r\n");
+ fputs($fp, 'Content-length: '.strlen($data)."\r\n");
+ fputs($fp, "Connection: close\r\n\r\n");
+ fputs($fp, $data);
// Was it successful?
- $this->response = "";
+ $this->response = '';
while ( ! feof($fp))
{
$this->response .= fgets($fp, 128);
}
@fclose($fp);
-
- if (stristr($this->response, '<error>0</error>') === FALSE)
+ if (stripos($this->response, '<error>0</error>') === FALSE)
{
- $message = 'An unknown error was encountered';
-
- if (preg_match("/<message>(.*?)<\/message>/is", $this->response, $match))
- {
- $message = trim($match['1']);
- }
-
+ $message = preg_match('/<message>(.*?)<\/message>/is', $this->response, $match)
+ ? trim($match[1])
+ : 'An unknown error was encountered';
$this->set_error($message);
return FALSE;
}
@@ -293,32 +342,18 @@ class CI_Trackback {
* It takes a string of URLs (separated by comma or
* space) and puts each URL into an array
*
- * @access public
* @param string
* @return string
*/
- function extract_urls($urls)
+ public function extract_urls($urls)
{
- // Remove the pesky white space and replace with a comma.
- $urls = preg_replace("/\s*(\S+)\s*/", "\\1,", $urls);
-
- // If they use commas get rid of the doubles.
- $urls = str_replace(",,", ",", $urls);
-
- // Remove any comma that might be at the end
- if (substr($urls, -1) == ",")
- {
- $urls = substr($urls, 0, -1);
- }
-
- // Break into an array via commas
- $urls = preg_split('/[,]/', $urls);
+ // Remove the pesky white space and replace with a comma, then replace doubles.
+ $urls = str_replace(',,', ',', preg_replace('/\s*(\S+)\s*/', '\\1,', $urls));
- // Removes duplicates
- $urls = array_unique($urls);
+ // Break into an array via commas and remove duplicates
+ $urls = array_unique(preg_split('/[,]/', rtrim($urls, ',')));
array_walk($urls, array($this, 'validate_url'));
-
return $urls;
}
@@ -329,17 +364,16 @@ class CI_Trackback {
*
* Simply adds "http://" if missing
*
- * @access public
* @param string
- * @return string
+ * @return void
*/
- function validate_url($url)
+ public function validate_url(&$url)
{
$url = trim($url);
- if (substr($url, 0, 4) != "http")
+ if (stripos($url, 'http') !== 0)
{
- $url = "http://".$url;
+ $url = 'http://'.$url;
}
}
@@ -348,13 +382,12 @@ class CI_Trackback {
/**
* Find the Trackback URL's ID
*
- * @access public
* @param string
* @return string
*/
- function get_id($url)
+ public function get_id($url)
{
- $tb_id = "";
+ $tb_id = '';
if (strpos($url, '?') !== FALSE)
{
@@ -378,18 +411,11 @@ class CI_Trackback {
if ( ! is_numeric($tb_id))
{
- $tb_id = $tb_array[count($tb_array)-2];
+ $tb_id = $tb_array[count($tb_array)-2];
}
}
- if ( ! preg_match ("/^([0-9]+)$/", $tb_id))
- {
- return FALSE;
- }
- else
- {
- return $tb_id;
- }
+ return ctype_digit((string) $tb_id) ? $tb_id : FALSE;
}
// --------------------------------------------------------------------
@@ -397,25 +423,20 @@ class CI_Trackback {
/**
* Convert Reserved XML characters to Entities
*
- * @access public
* @param string
* @return string
*/
- function convert_xml($str)
+ public function convert_xml($str)
{
$temp = '__TEMP_AMPERSANDS__';
- $str = preg_replace("/&#(\d+);/", "$temp\\1;", $str);
- $str = preg_replace("/&(\w+);/", "$temp\\1;", $str);
-
- $str = str_replace(array("&","<",">","\"", "'", "-"),
- array("&amp;", "&lt;", "&gt;", "&quot;", "&#39;", "&#45;"),
- $str);
+ $str = preg_replace(array('/&#(\d+);/', '/&(\w+);/'), $temp.'\\1;', $str);
- $str = preg_replace("/$temp(\d+);/","&#\\1;",$str);
- $str = preg_replace("/$temp(\w+);/","&\\1;", $str);
+ $str = str_replace(array('&', '<', '>', '"', "'", '-'),
+ array('&amp;', '&lt;', '&gt;', '&quot;', '&#39;', '&#45;'),
+ $str);
- return $str;
+ return preg_replace(array('/'.$temp.'(\d+);/', '/'.$temp.'(\w+);/'), array('&#\\1;', '&\\1;'), $str);
}
// --------------------------------------------------------------------
@@ -425,33 +446,32 @@ class CI_Trackback {
*
* Limits the string based on the character count. Will preserve complete words.
*
- * @access public
* @param string
- * @param integer
+ * @param int
* @param string
* @return string
*/
- function limit_characters($str, $n = 500, $end_char = '&#8230;')
+ public function limit_characters($str, $n = 500, $end_char = '&#8230;')
{
if (strlen($str) < $n)
{
return $str;
}
- $str = preg_replace("/\s+/", ' ', str_replace(array("\r\n", "\r", "\n"), ' ', $str));
+ $str = preg_replace('/\s+/', ' ', str_replace(array("\r\n", "\r", "\n"), ' ', $str));
if (strlen($str) <= $n)
{
return $str;
}
- $out = "";
+ $out = '';
foreach (explode(' ', trim($str)) as $val)
{
$out .= $val.' ';
if (strlen($out) >= $n)
{
- return trim($out).$end_char;
+ return rtrim($out).$end_char;
}
}
}
@@ -464,11 +484,10 @@ class CI_Trackback {
* Converts Hight ascii text and MS Word special chars
* to character entities
*
- * @access public
* @param string
* @return string
*/
- function convert_ascii($str)
+ public function convert_ascii($str)
{
$count = 1;
$out = '';
@@ -484,16 +503,18 @@ class CI_Trackback {
}
else
{
- if (count($temp) == 0)
+ if (count($temp) === 0)
{
$count = ($ordinal < 224) ? 2 : 3;
}
$temp[] = $ordinal;
- if (count($temp) == $count)
+ if (count($temp) === $count)
{
- $number = ($count == 3) ? (($temp['0'] % 16) * 4096) + (($temp['1'] % 64) * 64) + ($temp['2'] % 64) : (($temp['0'] % 32) * 64) + ($temp['1'] % 64);
+ $number = ($count === 3)
+ ? (($temp[0] % 16) * 4096) + (($temp[1] % 64) * 64) + ($temp[2] % 64)
+ : (($temp[0] % 32) * 64) + ($temp[1] % 64);
$out .= '&#'.$number.';';
$count = 1;
@@ -510,11 +531,10 @@ class CI_Trackback {
/**
* Set error message
*
- * @access public
* @param string
* @return void
*/
- function set_error($msg)
+ public function set_error($msg)
{
log_message('error', $msg);
$this->error_msg[] = $msg;
@@ -525,24 +545,13 @@ class CI_Trackback {
/**
* Show error messages
*
- * @access public
* @param string
* @param string
* @return string
*/
- function display_errors($open = '<p>', $close = '</p>')
+ public function display_errors($open = '<p>', $close = '</p>')
{
- $str = '';
- foreach ($this->error_msg as $val)
- {
- $str .= $open.$val.$close;
- }
-
- return $str;
+ return (count($this->error_msg) > 0) ? $open.implode($close.$open, $this->error_msg).$close : '';
}
}
-// END Trackback Class
-
-/* End of file Trackback.php */
-/* Location: ./system/libraries/Trackback.php */ \ No newline at end of file
diff --git a/system/libraries/Typography.php b/system/libraries/Typography.php
index b30582d8a..108bc7725 100644
--- a/system/libraries/Typography.php
+++ b/system/libraries/Typography.php
@@ -1,48 +1,95 @@
-<?php if ( ! defined('BASEPATH')) exit('No direct script access allowed');
+<?php
/**
* CodeIgniter
*
- * An open source application development framework for PHP 5.1.6 or newer
+ * An open source application development framework for PHP
*
- * @package CodeIgniter
- * @author ExpressionEngine Dev Team
- * @copyright Copyright (c) 2008 - 2014, EllisLab, Inc.
- * @license http://codeigniter.com/user_guide/license.html
- * @link http://codeigniter.com
- * @since Version 1.0
+ * This content is released under the MIT License (MIT)
+ *
+ * Copyright (c) 2019 - 2022, CodeIgniter Foundation
+ *
+ * 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.
+ *
+ * @package CodeIgniter
+ * @author EllisLab Dev Team
+ * @copyright Copyright (c) 2008 - 2014, EllisLab, Inc. (https://ellislab.com/)
+ * @copyright Copyright (c) 2014 - 2019, British Columbia Institute of Technology (https://bcit.ca/)
+ * @copyright Copyright (c) 2019 - 2022, CodeIgniter Foundation (https://codeigniter.com/)
+ * @license https://opensource.org/licenses/MIT MIT License
+ * @link https://codeigniter.com
+ * @since Version 1.0.0
* @filesource
*/
-
-// ------------------------------------------------------------------------
+defined('BASEPATH') OR exit('No direct script access allowed');
/**
* Typography Class
*
- *
- * @access private
+ * @package CodeIgniter
+ * @subpackage Libraries
* @category Helpers
- * @author ExpressionEngine Dev Team
- * @link http://codeigniter.com/user_guide/helpers/
+ * @author EllisLab Dev Team
+ * @link https://codeigniter.com/userguide3/libraries/typography.html
*/
class CI_Typography {
- // Block level elements that should not be wrapped inside <p> tags
- var $block_elements = 'address|blockquote|div|dl|fieldset|form|h\d|hr|noscript|object|ol|p|pre|script|table|ul';
+ /**
+ * Block level elements that should not be wrapped inside <p> tags
+ *
+ * @var string
+ */
+ public $block_elements = 'address|blockquote|div|dl|fieldset|form|h\d|hr|noscript|object|ol|p|pre|script|table|ul';
- // Elements that should not have <p> and <br /> tags within them.
- var $skip_elements = 'p|pre|ol|ul|dl|object|table|h\d';
+ /**
+ * Elements that should not have <p> and <br /> tags within them.
+ *
+ * @var string
+ */
+ public $skip_elements = 'p|pre|ol|ul|dl|object|table|h\d';
- // Tags we want the parser to completely ignore when splitting the string.
- var $inline_elements = 'a|abbr|acronym|b|bdo|big|br|button|cite|code|del|dfn|em|i|img|ins|input|label|map|kbd|q|samp|select|small|span|strong|sub|sup|textarea|tt|var';
+ /**
+ * Tags we want the parser to completely ignore when splitting the string.
+ *
+ * @var string
+ */
+ public $inline_elements = 'a|abbr|acronym|b|bdo|big|br|button|cite|code|del|dfn|em|i|img|ins|input|label|map|kbd|q|samp|select|small|span|strong|sub|sup|textarea|tt|var';
- // array of block level elements that require inner content to be within another block level element
- var $inner_block_required = array('blockquote');
+ /**
+ * array of block level elements that require inner content to be within another block level element
+ *
+ * @var array
+ */
+ public $inner_block_required = array('blockquote');
- // the last block element parsed
- var $last_block_element = '';
+ /**
+ * the last block element parsed
+ *
+ * @var string
+ */
+ public $last_block_element = '';
- // whether or not to protect quotes within { curly braces }
- var $protect_braced_quotes = FALSE;
+ /**
+ * whether or not to protect quotes within { curly braces }
+ *
+ * @var bool
+ */
+ public $protect_braced_quotes = FALSE;
/**
* Auto Typography
@@ -55,14 +102,13 @@ class CI_Typography {
* - Converts double dashes into em-dashes.
* - Converts two spaces into entities
*
- * @access public
* @param string
* @param bool whether to reduce more then two consecutive newlines to two
* @return string
*/
- function auto_typography($str, $reduce_linebreaks = FALSE)
+ public function auto_typography($str, $reduce_linebreaks = FALSE)
{
- if ($str == '')
+ if ($str === '')
{
return '';
}
@@ -82,15 +128,12 @@ class CI_Typography {
// HTML comment tags don't conform to patterns of normal tags, so pull them out separately, only if needed
$html_comments = array();
- if (strpos($str, '<!--') !== FALSE)
+ if (strpos($str, '<!--') !== FALSE && preg_match_all('#(<!\-\-.*?\-\->)#s', $str, $matches))
{
- if (preg_match_all("#(<!\-\-.*?\-\->)#s", $str, $matches))
+ for ($i = 0, $total = count($matches[0]); $i < $total; $i++)
{
- for ($i = 0, $total = count($matches[0]); $i < $total; $i++)
- {
- $html_comments[] = $matches[0][$i];
- $str = str_replace($matches[0][$i], '{@HC'.$i.'}', $str);
- }
+ $html_comments[] = $matches[0][$i];
+ $str = str_replace($matches[0][$i], '{@HC'.$i.'}', $str);
}
}
@@ -98,83 +141,79 @@ class CI_Typography {
// not contain <pre> tags, and it keeps the PCRE patterns below simpler and faster
if (strpos($str, '<pre') !== FALSE)
{
- $str = preg_replace_callback("#<pre.*?>.*?</pre>#si", array($this, '_protect_characters'), $str);
+ $str = preg_replace_callback('#<pre.*?>.*?</pre>#si', array($this, '_protect_characters'), $str);
}
// Convert quotes within tags to temporary markers.
- $str = preg_replace_callback("#<.+?>#si", array($this, '_protect_characters'), $str);
+ $str = preg_replace_callback('#<.+?>#si', array($this, '_protect_characters'), $str);
// Do the same with braces if necessary
if ($this->protect_braced_quotes === TRUE)
{
- $str = preg_replace_callback("#\{.+?\}#si", array($this, '_protect_characters'), $str);
+ $str = preg_replace_callback('#\{.+?\}#si', array($this, '_protect_characters'), $str);
}
// Convert "ignore" tags to temporary marker. The parser splits out the string at every tag
// it encounters. Certain inline tags, like image tags, links, span tags, etc. will be
// adversely affected if they are split out so we'll convert the opening bracket < temporarily to: {@TAG}
- $str = preg_replace("#<(/*)(".$this->inline_elements.")([ >])#i", "{@TAG}\\1\\2\\3", $str);
-
- // Split the string at every tag. This expression creates an array with this prototype:
- //
- // [array]
- // {
- // [0] = <opening tag>
- // [1] = Content...
- // [2] = <closing tag>
- // Etc...
- // }
+ $str = preg_replace('#<(/*)('.$this->inline_elements.')([ >])#i', '{@TAG}\\1\\2\\3', $str);
+
+ /* Split the string at every tag. This expression creates an array with this prototype:
+ *
+ * [array]
+ * {
+ * [0] = <opening tag>
+ * [1] = Content...
+ * [2] = <closing tag>
+ * Etc...
+ * }
+ */
$chunks = preg_split('/(<(?:[^<>]+(?:"[^"]*"|\'[^\']*\')?)+>)/', $str, -1, PREG_SPLIT_DELIM_CAPTURE|PREG_SPLIT_NO_EMPTY);
// Build our finalized string. We cycle through the array, skipping tags, and processing the contained text
$str = '';
$process = TRUE;
- $paragraph = FALSE;
- $current_chunk = 0;
- $total_chunks = count($chunks);
- foreach ($chunks as $chunk)
+ for ($i = 0, $c = count($chunks) - 1; $i <= $c; $i++)
{
- $current_chunk++;
-
// Are we dealing with a tag? If so, we'll skip the processing for this cycle.
// Well also set the "process" flag which allows us to skip <pre> tags and a few other things.
- if (preg_match("#<(/*)(".$this->block_elements.").*?>#", $chunk, $match))
+ if (preg_match('#<(/*)('.$this->block_elements.').*?>#', $chunks[$i], $match))
{
- if (preg_match("#".$this->skip_elements."#", $match[2]))
+ if (preg_match('#'.$this->skip_elements.'#', $match[2]))
{
- $process = ($match[1] == '/') ? TRUE : FALSE;
+ $process = ($match[1] === '/');
}
- if ($match[1] == '')
+ if ($match[1] === '')
{
$this->last_block_element = $match[2];
}
- $str .= $chunk;
+ $str .= $chunks[$i];
continue;
}
- if ($process == FALSE)
+ if ($process === FALSE)
{
- $str .= $chunk;
+ $str .= $chunks[$i];
continue;
}
// Force a newline to make sure end tags get processed by _format_newlines()
- if ($current_chunk == $total_chunks)
+ if ($i === $c)
{
- $chunk .= "\n";
+ $chunks[$i] .= "\n";
}
// Convert Newlines into <p> and <br /> tags
- $str .= $this->_format_newlines($chunk);
+ $str .= $this->_format_newlines($chunks[$i]);
}
- // No opening block level tag? Add it if needed.
- if ( ! preg_match("/^\s*<(?:".$this->block_elements.")/i", $str))
+ // No opening block level tag? Add it if needed.
+ if ( ! preg_match('/^\s*<(?:'.$this->block_elements.')/i', $str))
{
- $str = preg_replace("/^(.*?)<(".$this->block_elements.")/i", '<p>$1</p><$2', $str);
+ $str = preg_replace('/^(.*?)<('.$this->block_elements.')/i', '<p>$1</p><$2', $str);
}
// Convert quotes, elipsis, em-dashes, non-breaking spaces, and ampersands
@@ -203,7 +242,7 @@ class CI_Typography {
// Clean up stray paragraph tags that appear before block level elements
'#<p></p><('.$this->block_elements.')#' => '<$1',
- // Clean up stray non-breaking spaces preceeding block elements
+ // Clean up stray non-breaking spaces preceding block elements
'#(&nbsp;\s*)+<('.$this->block_elements.')#' => ' <$2',
// Replace the temporary markers we added earlier
@@ -221,7 +260,7 @@ class CI_Typography {
// Similarly, there might be cases where a closing </block> will follow
// a closing </p> tag, so we'll correct it by adding a newline in between
- "#</p></#" => "</p>\n</"
+ '#</p></#' => "</p>\n</"
);
// Do we need to reduce empty lines?
@@ -249,11 +288,10 @@ class CI_Typography {
* to curly entities, but it also converts em-dashes,
* double spaces, and ampersands
*
- * @access public
* @param string
* @return string
*/
- function format_characters($str)
+ public function format_characters($str)
{
static $table;
@@ -313,18 +351,12 @@ class CI_Typography {
*
* Converts newline characters into either <p> tags or <br />
*
- * @access public
* @param string
* @return string
*/
- function _format_newlines($str)
+ protected function _format_newlines($str)
{
- if ($str == '')
- {
- return $str;
- }
-
- if (strpos($str, "\n") === FALSE && ! in_array($this->last_block_element, $this->inner_block_required))
+ if ($str === '' OR (strpos($str, "\n") === FALSE && ! in_array($this->last_block_element, $this->inner_block_required)))
{
return $str;
}
@@ -333,10 +365,10 @@ class CI_Typography {
$str = str_replace("\n\n", "</p>\n\n<p>", $str);
// Convert single spaces to <br /> tags
- $str = preg_replace("/([^\n])(\n)([^\n])/", "\\1<br />\\2\\3", $str);
+ $str = preg_replace("/([^\n])(\n)([^\n])/", '\\1<br />\\2\\3', $str);
// Wrap the whole enchilada in enclosing paragraphs
- if ($str != "\n")
+ if ($str !== "\n")
{
// We trim off the right-side new line so that the closing </p> tag
// will be positioned immediately following the string, matching
@@ -346,9 +378,7 @@ class CI_Typography {
// Remove empty paragraphs if they are on the first line, as this
// is a potential unintended consequence of the previous code
- $str = preg_replace("/<p><\/p>(.*)/", "\\1", $str, 1);
-
- return $str;
+ return preg_replace('/<p><\/p>(.*)/', '\\1', $str, 1);
}
// ------------------------------------------------------------------------
@@ -361,11 +391,10 @@ class CI_Typography {
* and we don't want double dashes converted to emdash entities, so they are marked with {@DD}
* likewise double spaces are converted to {@NBS} to prevent entity conversion
*
- * @access public
* @param array
* @return string
*/
- function _protect_characters($match)
+ protected function _protect_characters($match)
{
return str_replace(array("'",'"','--',' '), array('{@SQ}', '{@DQ}', '{@DD}', '{@NBS}'), $match[0]);
}
@@ -375,36 +404,22 @@ class CI_Typography {
/**
* Convert newlines to HTML line breaks except within PRE tags
*
- * @access public
* @param string
* @return string
*/
- function nl2br_except_pre($str)
+ public function nl2br_except_pre($str)
{
- $ex = explode("pre>",$str);
- $ct = count($ex);
-
- $newstr = "";
- for ($i = 0; $i < $ct; $i++)
+ $newstr = '';
+ for ($ex = explode('pre>', $str), $ct = count($ex), $i = 0; $i < $ct; $i++)
{
- if (($i % 2) == 0)
- {
- $newstr .= nl2br($ex[$i]);
- }
- else
+ $newstr .= (($i % 2) === 0) ? nl2br($ex[$i]) : $ex[$i];
+ if ($ct - 1 !== $i)
{
- $newstr .= $ex[$i];
+ $newstr .= 'pre>';
}
-
- if ($ct - 1 != $i)
- $newstr .= "pre>";
}
return $newstr;
}
}
-// END Typography Class
-
-/* End of file Typography.php */
-/* Location: ./system/libraries/Typography.php */ \ No newline at end of file
diff --git a/system/libraries/Unit_test.php b/system/libraries/Unit_test.php
index b8919e1e5..e1b94f0a8 100644
--- a/system/libraries/Unit_test.php
+++ b/system/libraries/Unit_test.php
@@ -1,19 +1,42 @@
-<?php if ( ! defined('BASEPATH')) exit('No direct script access allowed');
+<?php
/**
* CodeIgniter
*
- * An open source application development framework for PHP 5.1.6 or newer
+ * An open source application development framework for PHP
*
- * @package CodeIgniter
- * @author ExpressionEngine Dev Team
- * @copyright Copyright (c) 2008 - 2014, EllisLab, Inc.
- * @license http://codeigniter.com/user_guide/license.html
- * @link http://codeigniter.com
- * @since Version 1.3.1
+ * This content is released under the MIT License (MIT)
+ *
+ * Copyright (c) 2019 - 2022, CodeIgniter Foundation
+ *
+ * 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.
+ *
+ * @package CodeIgniter
+ * @author EllisLab Dev Team
+ * @copyright Copyright (c) 2008 - 2014, EllisLab, Inc. (https://ellislab.com/)
+ * @copyright Copyright (c) 2014 - 2019, British Columbia Institute of Technology (https://bcit.ca/)
+ * @copyright Copyright (c) 2019 - 2022, CodeIgniter Foundation (https://codeigniter.com/)
+ * @license https://opensource.org/licenses/MIT MIT License
+ * @link https://codeigniter.com
+ * @since Version 1.3.1
* @filesource
*/
-
-// ------------------------------------------------------------------------
+defined('BASEPATH') OR exit('No direct script access allowed');
/**
* Unit Testing Class
@@ -23,32 +46,73 @@
* @package CodeIgniter
* @subpackage Libraries
* @category UnitTesting
- * @author ExpressionEngine Dev Team
- * @link http://codeigniter.com/user_guide/libraries/uri.html
+ * @author EllisLab Dev Team
+ * @link https://codeigniter.com/userguide3/libraries/unit_testing.html
*/
class CI_Unit_test {
- var $active = TRUE;
- var $results = array();
- var $strict = FALSE;
- var $_template = NULL;
- var $_template_rows = NULL;
- var $_test_items_visible = array();
+ /**
+ * Active flag
+ *
+ * @var bool
+ */
+ public $active = TRUE;
+
+ /**
+ * Test results
+ *
+ * @var array
+ */
+ public $results = array();
+
+ /**
+ * Strict comparison flag
+ *
+ * Whether to use === or == when comparing
+ *
+ * @var bool
+ */
+ public $strict = FALSE;
+
+ /**
+ * Template
+ *
+ * @var string
+ */
+ protected $_template = NULL;
+
+ /**
+ * Template rows
+ *
+ * @var string
+ */
+ protected $_template_rows = NULL;
+
+ /**
+ * List of visible test items
+ *
+ * @var array
+ */
+ protected $_test_items_visible = array(
+ 'test_name',
+ 'test_datatype',
+ 'res_datatype',
+ 'result',
+ 'file',
+ 'line',
+ 'notes'
+ );
+
+ // --------------------------------------------------------------------
+ /**
+ * Constructor
+ *
+ * @return void
+ */
public function __construct()
{
- // These are the default items visible when a test is run.
- $this->_test_items_visible = array (
- 'test_name',
- 'test_datatype',
- 'res_datatype',
- 'result',
- 'file',
- 'line',
- 'notes'
- );
-
- log_message('debug', "Unit Testing Class Initialized");
+ log_message('info', 'Unit Testing Class Initialized');
}
// --------------------------------------------------------------------
@@ -58,13 +122,12 @@ class CI_Unit_test {
*
* Runs the supplied tests
*
- * @access public
- * @param array
+ * @param array $items
* @return void
*/
- function set_test_items($items = array())
+ public function set_test_items($items)
{
- if ( ! empty($items) AND is_array($items))
+ if ( ! empty($items) && is_array($items))
{
$this->_test_items_visible = $items;
}
@@ -77,50 +140,45 @@ class CI_Unit_test {
*
* Runs the supplied tests
*
- * @access public
- * @param mixed
- * @param mixed
- * @param string
+ * @param mixed $test
+ * @param mixed $expected
+ * @param string $test_name
+ * @param string $notes
* @return string
*/
- function run($test, $expected = TRUE, $test_name = 'undefined', $notes = '')
+ public function run($test, $expected = TRUE, $test_name = 'undefined', $notes = '')
{
- if ($this->active == FALSE)
+ if ($this->active === FALSE)
{
return FALSE;
}
- if (in_array($expected, array('is_object', 'is_string', 'is_bool', 'is_true', 'is_false', 'is_int', 'is_numeric', 'is_float', 'is_double', 'is_array', 'is_null'), TRUE))
+ if (in_array($expected, array('is_object', 'is_string', 'is_bool', 'is_true', 'is_false', 'is_int', 'is_numeric', 'is_float', 'is_double', 'is_array', 'is_null', 'is_resource'), TRUE))
{
- $expected = str_replace('is_float', 'is_double', $expected);
- $result = ($expected($test)) ? TRUE : FALSE;
+ $result = $expected($test);
$extype = str_replace(array('true', 'false'), 'bool', str_replace('is_', '', $expected));
}
else
{
- if ($this->strict == TRUE)
- $result = ($test === $expected) ? TRUE : FALSE;
- else
- $result = ($test == $expected) ? TRUE : FALSE;
-
+ $result = ($this->strict === TRUE) ? ($test === $expected) : ($test == $expected);
$extype = gettype($expected);
}
$back = $this->_backtrace();
- $report[] = array (
- 'test_name' => $test_name,
- 'test_datatype' => gettype($test),
- 'res_datatype' => $extype,
- 'result' => ($result === TRUE) ? 'passed' : 'failed',
- 'file' => $back['file'],
- 'line' => $back['line'],
- 'notes' => $notes
- );
+ $report = array (
+ 'test_name' => $test_name,
+ 'test_datatype' => gettype($test),
+ 'res_datatype' => $extype,
+ 'result' => ($result === TRUE) ? 'passed' : 'failed',
+ 'file' => $back['file'],
+ 'line' => $back['line'],
+ 'notes' => $notes
+ );
$this->results[] = $report;
- return($this->report($this->result($report)));
+ return $this->report($this->result(array($report)));
}
// --------------------------------------------------------------------
@@ -130,12 +188,12 @@ class CI_Unit_test {
*
* Displays a table with the test data
*
- * @access public
+ * @param array $result
* @return string
*/
- function report($result = array())
+ public function report($result = array())
{
- if (count($result) == 0)
+ if (count($result) === 0)
{
$result = $this->result();
}
@@ -152,22 +210,19 @@ class CI_Unit_test {
foreach ($res as $key => $val)
{
- if ($key == $CI->lang->line('ut_result'))
+ if ($key === $CI->lang->line('ut_result'))
{
- if ($val == $CI->lang->line('ut_passed'))
+ if ($val === $CI->lang->line('ut_passed'))
{
$val = '<span style="color: #0C0;">'.$val.'</span>';
}
- elseif ($val == $CI->lang->line('ut_failed'))
+ elseif ($val === $CI->lang->line('ut_failed'))
{
$val = '<span style="color: #C00;">'.$val.'</span>';
}
}
- $temp = $this->_template_rows;
- $temp = str_replace('{item}', $key, $temp);
- $temp = str_replace('{result}', $val, $temp);
- $table .= $temp;
+ $table .= str_replace(array('{item}', '{result}'), array($key, $val), $this->_template_rows);
}
$r .= str_replace('{rows}', $table, $this->_template);
@@ -183,13 +238,12 @@ class CI_Unit_test {
*
* Causes the evaluation to use === rather than ==
*
- * @access public
- * @param bool
- * @return null
+ * @param bool $state
+ * @return void
*/
- function use_strict($state = TRUE)
+ public function use_strict($state = TRUE)
{
- $this->strict = ($state == FALSE) ? FALSE : TRUE;
+ $this->strict = (bool) $state;
}
// --------------------------------------------------------------------
@@ -199,13 +253,12 @@ class CI_Unit_test {
*
* Enables/disables unit testing
*
- * @access public
* @param bool
- * @return null
+ * @return void
*/
- function active($state = TRUE)
+ public function active($state = TRUE)
{
- $this->active = ($state == FALSE) ? FALSE : TRUE;
+ $this->active = (bool) $state;
}
// --------------------------------------------------------------------
@@ -215,15 +268,15 @@ class CI_Unit_test {
*
* Returns the raw result data
*
- * @access public
+ * @param array $results
* @return array
*/
- function result($results = array())
+ public function result($results = array())
{
$CI =& get_instance();
$CI->load->language('unit_test');
- if (count($results) == 0)
+ if (count($results) === 0)
{
$results = $this->results;
}
@@ -238,26 +291,15 @@ class CI_Unit_test {
{
continue;
}
-
- if (is_array($val))
- {
- foreach ($val as $k => $v)
- {
- if (FALSE !== ($line = $CI->lang->line(strtolower('ut_'.$v))))
- {
- $v = $line;
- }
- $temp[$CI->lang->line('ut_'.$k)] = $v;
- }
- }
- else
+ elseif (in_array($key, array('test_name', 'test_datatype', 'res_datatype', 'result'), TRUE))
{
- if (FALSE !== ($line = $CI->lang->line(strtolower('ut_'.$val))))
+ if (FALSE !== ($line = $CI->lang->line(strtolower('ut_'.$val), FALSE)))
{
$val = $line;
}
- $temp[$CI->lang->line('ut_'.$key)] = $val;
}
+
+ $temp[$CI->lang->line('ut_'.$key, FALSE)] = $val;
}
$retval[] = $temp;
@@ -273,11 +315,10 @@ class CI_Unit_test {
*
* This lets us set the template to be used to display results
*
- * @access public
* @param string
* @return void
*/
- function set_template($template)
+ public function set_template($template)
{
$this->_template = $template;
}
@@ -289,21 +330,15 @@ class CI_Unit_test {
*
* This lets us show file names and line numbers
*
- * @access private
* @return array
*/
- function _backtrace()
+ protected function _backtrace()
{
- if (function_exists('debug_backtrace'))
- {
- $back = debug_backtrace();
-
- $file = ( ! isset($back['1']['file'])) ? '' : $back['1']['file'];
- $line = ( ! isset($back['1']['line'])) ? '' : $back['1']['line'];
-
- return array('file' => $file, 'line' => $line);
- }
- return array('file' => 'Unknown', 'line' => 'Unknown');
+ $back = debug_backtrace();
+ return array(
+ 'file' => (isset($back[1]['file']) ? $back[1]['file'] : ''),
+ 'line' => (isset($back[1]['line']) ? $back[1]['line'] : '')
+ );
}
// --------------------------------------------------------------------
@@ -311,19 +346,14 @@ class CI_Unit_test {
/**
* Get Default Template
*
- * @access private
* @return string
*/
- function _default_template()
+ protected function _default_template()
{
- $this->_template = "\n".'<table style="width:100%; font-size:small; margin:10px 0; border-collapse:collapse; border:1px solid #CCC;">';
- $this->_template .= '{rows}';
- $this->_template .= "\n".'</table>';
-
- $this->_template_rows = "\n\t".'<tr>';
- $this->_template_rows .= "\n\t\t".'<th style="text-align: left; border-bottom:1px solid #CCC;">{item}</th>';
- $this->_template_rows .= "\n\t\t".'<td style="border-bottom:1px solid #CCC;">{result}</td>';
- $this->_template_rows .= "\n\t".'</tr>';
+ $this->_template = "\n".'<table style="width:100%; font-size:small; margin:10px 0; border-collapse:collapse; border:1px solid #CCC;">{rows}'."\n</table>";
+
+ $this->_template_rows = "\n\t<tr>\n\t\t".'<th style="text-align: left; border-bottom:1px solid #CCC;">{item}</th>'
+ ."\n\t\t".'<td style="border-bottom:1px solid #CCC;">{result}</td>'."\n\t</tr>";
}
// --------------------------------------------------------------------
@@ -333,51 +363,45 @@ class CI_Unit_test {
*
* Harvests the data within the template {pseudo-variables}
*
- * @access private
* @return void
*/
- function _parse_template()
+ protected function _parse_template()
{
- if ( ! is_null($this->_template_rows))
- {
- return;
- }
-
- if (is_null($this->_template))
+ if ($this->_template_rows !== NULL)
{
- $this->_default_template();
return;
}
- if ( ! preg_match("/\{rows\}(.*?)\{\/rows\}/si", $this->_template, $match))
+ if ($this->_template === NULL OR ! preg_match('/\{rows\}(.*?)\{\/rows\}/si', $this->_template, $match))
{
$this->_default_template();
return;
}
- $this->_template_rows = $match['1'];
- $this->_template = str_replace($match['0'], '{rows}', $this->_template);
+ $this->_template_rows = $match[1];
+ $this->_template = str_replace($match[0], '{rows}', $this->_template);
}
}
-// END Unit_test Class
/**
- * Helper functions to test boolean true/false
+ * Helper function to test boolean TRUE
*
- *
- * @access private
+ * @param mixed $test
* @return bool
*/
function is_true($test)
{
- return (is_bool($test) AND $test === TRUE) ? TRUE : FALSE;
+ return ($test === TRUE);
}
+
+/**
+ * Helper function to test boolean FALSE
+ *
+ * @param mixed $test
+ * @return bool
+ */
function is_false($test)
{
- return (is_bool($test) AND $test === FALSE) ? TRUE : FALSE;
+ return ($test === FALSE);
}
-
-
-/* End of file Unit_test.php */
-/* Location: ./system/libraries/Unit_test.php */ \ No newline at end of file
diff --git a/system/libraries/Upload.php b/system/libraries/Upload.php
index c188c39bc..168211a91 100644
--- a/system/libraries/Upload.php
+++ b/system/libraries/Upload.php
@@ -1,19 +1,42 @@
-<?php if ( ! defined('BASEPATH')) exit('No direct script access allowed');
+<?php
/**
* CodeIgniter
*
- * An open source application development framework for PHP 5.1.6 or newer
+ * An open source application development framework for PHP
*
- * @package CodeIgniter
- * @author ExpressionEngine Dev Team
- * @copyright Copyright (c) 2008 - 2014, EllisLab, Inc.
- * @license http://codeigniter.com/user_guide/license.html
- * @link http://codeigniter.com
- * @since Version 1.0
+ * This content is released under the MIT License (MIT)
+ *
+ * Copyright (c) 2019 - 2022, CodeIgniter Foundation
+ *
+ * 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.
+ *
+ * @package CodeIgniter
+ * @author EllisLab Dev Team
+ * @copyright Copyright (c) 2008 - 2014, EllisLab, Inc. (https://ellislab.com/)
+ * @copyright Copyright (c) 2014 - 2019, British Columbia Institute of Technology (https://bcit.ca/)
+ * @copyright Copyright (c) 2019 - 2022, CodeIgniter Foundation (https://codeigniter.com/)
+ * @license https://opensource.org/licenses/MIT MIT License
+ * @link https://codeigniter.com
+ * @since Version 1.0.0
* @filesource
*/
-
-// ------------------------------------------------------------------------
+defined('BASEPATH') OR exit('No direct script access allowed');
/**
* File Uploading Class
@@ -21,52 +44,260 @@
* @package CodeIgniter
* @subpackage Libraries
* @category Uploads
- * @author ExpressionEngine Dev Team
- * @link http://codeigniter.com/user_guide/libraries/file_uploading.html
+ * @author EllisLab Dev Team
+ * @link https://codeigniter.com/userguide3/libraries/file_uploading.html
*/
class CI_Upload {
- public $max_size = 0;
- public $max_width = 0;
- public $max_height = 0;
- public $max_filename = 0;
- public $allowed_types = "";
- public $file_temp = "";
- public $file_name = "";
- public $orig_name = "";
- public $file_type = "";
- public $file_size = "";
- public $file_ext = "";
- public $upload_path = "";
- public $overwrite = FALSE;
- public $encrypt_name = FALSE;
- public $is_image = FALSE;
- public $image_width = '';
- public $image_height = '';
- public $image_type = '';
- public $image_size_str = '';
- public $error_msg = array();
- public $mimes = array();
- public $remove_spaces = TRUE;
- public $xss_clean = FALSE;
- public $temp_prefix = "temp_file_";
- public $client_name = '';
-
- protected $_file_name_override = '';
+ /**
+ * Maximum file size
+ *
+ * @var int
+ */
+ public $max_size = 0;
+
+ /**
+ * Maximum image width
+ *
+ * @var int
+ */
+ public $max_width = 0;
+
+ /**
+ * Maximum image height
+ *
+ * @var int
+ */
+ public $max_height = 0;
+
+ /**
+ * Minimum image width
+ *
+ * @var int
+ */
+ public $min_width = 0;
+
+ /**
+ * Minimum image height
+ *
+ * @var int
+ */
+ public $min_height = 0;
+
+ /**
+ * Maximum filename length
+ *
+ * @var int
+ */
+ public $max_filename = 0;
+
+ /**
+ * Maximum duplicate filename increment ID
+ *
+ * @var int
+ */
+ public $max_filename_increment = 100;
+
+ /**
+ * Allowed file types
+ *
+ * @var string
+ */
+ public $allowed_types = '';
+
+ /**
+ * Temporary filename
+ *
+ * @var string
+ */
+ public $file_temp = '';
+
+ /**
+ * Filename
+ *
+ * @var string
+ */
+ public $file_name = '';
+
+ /**
+ * Original filename
+ *
+ * @var string
+ */
+ public $orig_name = '';
+
+ /**
+ * File type
+ *
+ * @var string
+ */
+ public $file_type = '';
+
+ /**
+ * File size
+ *
+ * @var int
+ */
+ public $file_size = NULL;
+
+ /**
+ * Filename extension
+ *
+ * @var string
+ */
+ public $file_ext = '';
+
+ /**
+ * Force filename extension to lowercase
+ *
+ * @var string
+ */
+ public $file_ext_tolower = FALSE;
+
+ /**
+ * Upload path
+ *
+ * @var string
+ */
+ public $upload_path = '';
+
+ /**
+ * Overwrite flag
+ *
+ * @var bool
+ */
+ public $overwrite = FALSE;
+
+ /**
+ * Obfuscate filename flag
+ *
+ * @var bool
+ */
+ public $encrypt_name = FALSE;
+
+ /**
+ * Is image flag
+ *
+ * @var bool
+ */
+ public $is_image = FALSE;
+
+ /**
+ * Image width
+ *
+ * @var int
+ */
+ public $image_width = NULL;
+
+ /**
+ * Image height
+ *
+ * @var int
+ */
+ public $image_height = NULL;
+
+ /**
+ * Image type
+ *
+ * @var string
+ */
+ public $image_type = '';
+
+ /**
+ * Image size string
+ *
+ * @var string
+ */
+ public $image_size_str = '';
+
+ /**
+ * Error messages list
+ *
+ * @var array
+ */
+ public $error_msg = array();
+
+ /**
+ * Remove spaces flag
+ *
+ * @var bool
+ */
+ public $remove_spaces = TRUE;
+
+ /**
+ * MIME detection flag
+ *
+ * @var bool
+ */
+ public $detect_mime = TRUE;
+
+ /**
+ * XSS filter flag
+ *
+ * @var bool
+ */
+ public $xss_clean = FALSE;
+
+ /**
+ * Apache mod_mime fix flag
+ *
+ * @var bool
+ */
+ public $mod_mime_fix = TRUE;
+
+ /**
+ * Temporary filename prefix
+ *
+ * @var string
+ */
+ public $temp_prefix = 'temp_file_';
+
+ /**
+ * Filename sent by the client
+ *
+ * @var bool
+ */
+ public $client_name = '';
+
+ // --------------------------------------------------------------------
+
+ /**
+ * Filename override
+ *
+ * @var string
+ */
+ protected $_file_name_override = '';
+
+ /**
+ * MIME types list
+ *
+ * @var array
+ */
+ protected $_mimes = array();
+
+ /**
+ * CI Singleton
+ *
+ * @var object
+ */
+ protected $_CI;
+
+ // --------------------------------------------------------------------
/**
* Constructor
*
- * @access public
+ * @param array $config
+ * @return void
*/
- public function __construct($props = array())
+ public function __construct($config = array())
{
- if (count($props) > 0)
- {
- $this->initialize($props);
- }
+ empty($config) OR $this->initialize($config, FALSE);
+
+ $this->_mimes =& get_mimes();
+ $this->_CI =& get_instance();
- log_message('debug', "Upload Class Initialized");
+ log_message('info', 'Upload Class Initialized');
}
// --------------------------------------------------------------------
@@ -74,63 +305,63 @@ class CI_Upload {
/**
* Initialize preferences
*
- * @param array
- * @return void
+ * @param array $config
+ * @param bool $reset
+ * @return CI_Upload
*/
- public function initialize($config = array())
+ public function initialize(array $config = array(), $reset = TRUE)
{
- $defaults = array(
- 'max_size' => 0,
- 'max_width' => 0,
- 'max_height' => 0,
- 'max_filename' => 0,
- 'allowed_types' => "",
- 'file_temp' => "",
- 'file_name' => "",
- 'orig_name' => "",
- 'file_type' => "",
- 'file_size' => "",
- 'file_ext' => "",
- 'upload_path' => "",
- 'overwrite' => FALSE,
- 'encrypt_name' => FALSE,
- 'is_image' => FALSE,
- 'image_width' => '',
- 'image_height' => '',
- 'image_type' => '',
- 'image_size_str' => '',
- 'error_msg' => array(),
- 'mimes' => array(),
- 'remove_spaces' => TRUE,
- 'xss_clean' => FALSE,
- 'temp_prefix' => "temp_file_",
- 'client_name' => ''
- );
-
-
- foreach ($defaults as $key => $val)
+ $reflection = new ReflectionClass($this);
+
+ if ($reset === TRUE)
{
- if (isset($config[$key]))
+ $defaults = $reflection->getDefaultProperties();
+ foreach (array_keys($defaults) as $key)
{
- $method = 'set_'.$key;
- if (method_exists($this, $method))
+ if ($key[0] === '_')
+ {
+ continue;
+ }
+
+ if (isset($config[$key]))
{
- $this->$method($config[$key]);
+ if ($reflection->hasMethod('set_'.$key))
+ {
+ $this->{'set_'.$key}($config[$key]);
+ }
+ else
+ {
+ $this->$key = $config[$key];
+ }
}
else
{
- $this->$key = $config[$key];
+ $this->$key = $defaults[$key];
}
}
- else
+ }
+ else
+ {
+ foreach ($config as $key => &$value)
{
- $this->$key = $val;
+ if ($key[0] !== '_' && $reflection->hasProperty($key))
+ {
+ if ($reflection->hasMethod('set_'.$key))
+ {
+ $this->{'set_'.$key}($value);
+ }
+ else
+ {
+ $this->$key = $value;
+ }
+ }
}
}
// if a file_name was provided in the config, use it instead of the user input
// supplied file name for all uploads until initialized again
$this->_file_name_override = $this->file_name;
+ return $this;
}
// --------------------------------------------------------------------
@@ -138,15 +369,36 @@ class CI_Upload {
/**
* Perform the file upload
*
+ * @param string $field
* @return bool
*/
public function do_upload($field = 'userfile')
{
+ // Is $_FILES[$field] set? If not, no reason to continue.
+ if (isset($_FILES[$field]))
+ {
+ $_file = $_FILES[$field];
+ }
+ // Does the field name contain array notation?
+ elseif (($c = preg_match_all('/(?:^[^\[]+)|\[[^]]*\]/', $field, $matches)) > 1)
+ {
+ $_file = $_FILES;
+ for ($i = 0; $i < $c; $i++)
+ {
+ // We can't track numeric iterations, only full field names are accepted
+ if (($field = trim($matches[0][$i], '[]')) === '' OR ! isset($_file[$field]))
+ {
+ $_file = NULL;
+ break;
+ }
+
+ $_file = $_file[$field];
+ }
+ }
- // Is $_FILES[$field] set? If not, no reason to continue.
- if ( ! isset($_FILES[$field]))
+ if ( ! isset($_file))
{
- $this->set_error('upload_no_file_selected');
+ $this->set_error('upload_no_file_selected', 'debug');
return FALSE;
}
@@ -158,60 +410,66 @@ class CI_Upload {
}
// Was the file able to be uploaded? If not, determine the reason why.
- if ( ! is_uploaded_file($_FILES[$field]['tmp_name']))
+ if ( ! is_uploaded_file($_file['tmp_name']))
{
- $error = ( ! isset($_FILES[$field]['error'])) ? 4 : $_FILES[$field]['error'];
+ $error = isset($_file['error']) ? $_file['error'] : 4;
- switch($error)
+ switch ($error)
{
- case 1: // UPLOAD_ERR_INI_SIZE
- $this->set_error('upload_file_exceeds_limit');
+ case UPLOAD_ERR_INI_SIZE:
+ $this->set_error('upload_file_exceeds_limit', 'info');
break;
- case 2: // UPLOAD_ERR_FORM_SIZE
- $this->set_error('upload_file_exceeds_form_limit');
+ case UPLOAD_ERR_FORM_SIZE:
+ $this->set_error('upload_file_exceeds_form_limit', 'info');
break;
- case 3: // UPLOAD_ERR_PARTIAL
- $this->set_error('upload_file_partial');
+ case UPLOAD_ERR_PARTIAL:
+ $this->set_error('upload_file_partial', 'debug');
break;
- case 4: // UPLOAD_ERR_NO_FILE
- $this->set_error('upload_no_file_selected');
+ case UPLOAD_ERR_NO_FILE:
+ $this->set_error('upload_no_file_selected', 'debug');
break;
- case 6: // UPLOAD_ERR_NO_TMP_DIR
- $this->set_error('upload_no_temp_directory');
+ case UPLOAD_ERR_NO_TMP_DIR:
+ $this->set_error('upload_no_temp_directory', 'error');
break;
- case 7: // UPLOAD_ERR_CANT_WRITE
- $this->set_error('upload_unable_to_write_file');
+ case UPLOAD_ERR_CANT_WRITE:
+ $this->set_error('upload_unable_to_write_file', 'error');
break;
- case 8: // UPLOAD_ERR_EXTENSION
- $this->set_error('upload_stopped_by_extension');
+ case UPLOAD_ERR_EXTENSION:
+ $this->set_error('upload_stopped_by_extension', 'debug');
break;
- default : $this->set_error('upload_no_file_selected');
+ default:
+ $this->set_error('upload_no_file_selected', 'debug');
break;
}
return FALSE;
}
-
// Set the uploaded data as class variables
- $this->file_temp = $_FILES[$field]['tmp_name'];
- $this->file_size = $_FILES[$field]['size'];
- $this->_file_mime_type($_FILES[$field]);
- $this->file_type = preg_replace("/^(.+?);.*$/", "\\1", $this->file_type);
+ $this->file_temp = $_file['tmp_name'];
+ $this->file_size = $_file['size'];
+
+ // Skip MIME type detection?
+ if ($this->detect_mime !== FALSE)
+ {
+ $this->_file_mime_type($_file);
+ }
+
+ $this->file_type = preg_replace('/^(.+?);.*$/', '\\1', $this->file_type);
$this->file_type = strtolower(trim(stripslashes($this->file_type), '"'));
- $this->file_name = $this->_prep_filename($_FILES[$field]['name']);
+ $this->file_name = $this->_prep_filename($_file['name']);
$this->file_ext = $this->get_extension($this->file_name);
$this->client_name = $this->file_name;
// Is the file type allowed to be uploaded?
if ( ! $this->is_allowed_filetype())
{
- $this->set_error('upload_invalid_filetype');
+ $this->set_error('upload_invalid_filetype', 'debug');
return FALSE;
}
// if we're overriding, let's now make sure the new name and type is allowed
- if ($this->_file_name_override != '')
+ if ($this->_file_name_override !== '')
{
$this->file_name = $this->_prep_filename($this->_file_name_override);
@@ -220,16 +478,15 @@ class CI_Upload {
{
$this->file_name .= $this->file_ext;
}
-
- // An extension was provided, lets have it!
else
{
- $this->file_ext = $this->get_extension($this->_file_name_override);
+ // An extension was provided, let's have it!
+ $this->file_ext = $this->get_extension($this->_file_name_override);
}
if ( ! $this->is_allowed_filetype(TRUE))
{
- $this->set_error('upload_invalid_filetype');
+ $this->set_error('upload_invalid_filetype', 'debug');
return FALSE;
}
}
@@ -243,20 +500,20 @@ class CI_Upload {
// Is the file size within the allowed maximum?
if ( ! $this->is_allowed_filesize())
{
- $this->set_error('upload_invalid_filesize');
+ $this->set_error('upload_invalid_filesize', 'info');
return FALSE;
}
// Are the image dimensions within the allowed size?
- // Note: This can fail if the server has an open_basdir restriction.
+ // Note: This can fail if the server has an open_basedir restriction.
if ( ! $this->is_allowed_dimensions())
{
- $this->set_error('upload_invalid_dimensions');
+ $this->set_error('upload_invalid_dimensions', 'info');
return FALSE;
}
// Sanitize the file name for security
- $this->file_name = $this->clean_file_name($this->file_name);
+ $this->file_name = $this->_CI->security->sanitize_filename($this->file_name);
// Truncate the file name if it's too long
if ($this->max_filename > 0)
@@ -265,9 +522,15 @@ class CI_Upload {
}
// Remove white spaces in the name
- if ($this->remove_spaces == TRUE)
+ if ($this->remove_spaces === TRUE)
{
- $this->file_name = preg_replace("/\s+/", "_", $this->file_name);
+ $this->file_name = preg_replace('/\s+/', '_', $this->file_name);
+ }
+
+ if ($this->file_ext_tolower && ($ext_length = strlen($this->file_ext)))
+ {
+ // file_ext was previously lower-cased by a get_extension() call
+ $this->file_name = substr($this->file_name, 0, -$ext_length).$this->file_ext;
}
/*
@@ -277,44 +540,35 @@ class CI_Upload {
* If it returns false there was a problem.
*/
$this->orig_name = $this->file_name;
-
- if ($this->overwrite == FALSE)
+ if (FALSE === ($this->file_name = $this->set_filename($this->upload_path, $this->file_name)))
{
- $this->file_name = $this->set_filename($this->upload_path, $this->file_name);
-
- if ($this->file_name === FALSE)
- {
- return FALSE;
- }
+ return FALSE;
}
/*
* Run the file through the XSS hacking filter
* This helps prevent malicious code from being
- * embedded within a file. Scripts can easily
+ * embedded within a file. Scripts can easily
* be disguised as images or other file types.
*/
- if ($this->xss_clean)
+ if ($this->xss_clean && $this->do_xss_clean() === FALSE)
{
- if ($this->do_xss_clean() === FALSE)
- {
- $this->set_error('upload_unable_to_write_file');
- return FALSE;
- }
+ $this->set_error('upload_unable_to_write_file', 'error');
+ return FALSE;
}
/*
* Move the file to the final destination
* To deal with different server configurations
- * we'll attempt to use copy() first. If that fails
- * we'll use move_uploaded_file(). One of the two should
+ * we'll attempt to use copy() first. If that fails
+ * we'll use move_uploaded_file(). One of the two should
* reliably work in most environments
*/
if ( ! @copy($this->file_temp, $this->upload_path.$this->file_name))
{
if ( ! @move_uploaded_file($this->file_temp, $this->upload_path.$this->file_name))
{
- $this->set_error('upload_destination_error');
+ $this->set_error('upload_destination_error', 'error');
return FALSE;
}
}
@@ -322,7 +576,7 @@ class CI_Upload {
/*
* Set the finalized image dimensions
* This sets the image width/height (assuming the
- * file was an image). We use this information
+ * file was an image). We use this information
* in the "data" function.
*/
$this->set_image_properties($this->upload_path.$this->file_name);
@@ -338,26 +592,34 @@ class CI_Upload {
* Returns an associative array containing all of the information
* related to the upload, allowing the developer easy access in one array.
*
- * @return array
+ * @param string $index
+ * @return mixed
*/
- public function data()
+ public function data($index = NULL)
{
- return array (
- 'file_name' => $this->file_name,
- 'file_type' => $this->file_type,
- 'file_path' => $this->upload_path,
- 'full_path' => $this->upload_path.$this->file_name,
- 'raw_name' => str_replace($this->file_ext, '', $this->file_name),
- 'orig_name' => $this->orig_name,
- 'client_name' => $this->client_name,
- 'file_ext' => $this->file_ext,
- 'file_size' => $this->file_size,
- 'is_image' => $this->is_image(),
- 'image_width' => $this->image_width,
- 'image_height' => $this->image_height,
- 'image_type' => $this->image_type,
- 'image_size_str' => $this->image_size_str,
- );
+ $data = array(
+ 'file_name' => $this->file_name,
+ 'file_type' => $this->file_type,
+ 'file_path' => $this->upload_path,
+ 'full_path' => $this->upload_path.$this->file_name,
+ 'raw_name' => substr($this->file_name, 0, -strlen($this->file_ext)),
+ 'orig_name' => $this->orig_name,
+ 'client_name' => $this->client_name,
+ 'file_ext' => $this->file_ext,
+ 'file_size' => $this->file_size,
+ 'is_image' => $this->is_image(),
+ 'image_width' => $this->image_width,
+ 'image_height' => $this->image_height,
+ 'image_type' => $this->image_type,
+ 'image_size_str' => $this->image_size_str,
+ );
+
+ if ( ! empty($index))
+ {
+ return isset($data[$index]) ? $data[$index] : NULL;
+ }
+
+ return $data;
}
// --------------------------------------------------------------------
@@ -365,13 +627,14 @@ class CI_Upload {
/**
* Set Upload Path
*
- * @param string
- * @return void
+ * @param string $path
+ * @return CI_Upload
*/
public function set_upload_path($path)
{
// Make sure it has a trailing slash
$this->upload_path = rtrim($path, '/').'/';
+ return $this;
}
// --------------------------------------------------------------------
@@ -383,19 +646,18 @@ class CI_Upload {
* existence of a file with the same name. If found, it will append a
* number to the end of the filename to avoid overwriting a pre-existing file.
*
- * @param string
- * @param string
+ * @param string $path
+ * @param string $filename
* @return string
*/
public function set_filename($path, $filename)
{
- if ($this->encrypt_name == TRUE)
+ if ($this->encrypt_name === TRUE)
{
- mt_srand();
$filename = md5(uniqid(mt_rand())).$this->file_ext;
}
- if ( ! file_exists($path.$filename))
+ if ($this->overwrite === TRUE OR ! file_exists($path.$filename))
{
return $filename;
}
@@ -403,7 +665,7 @@ class CI_Upload {
$filename = str_replace($this->file_ext, '', $filename);
$new_filename = '';
- for ($i = 1; $i < 100; $i++)
+ for ($i = 1; $i < $this->max_filename_increment; $i++)
{
if ( ! file_exists($path.$filename.$i.$this->file_ext))
{
@@ -412,15 +674,13 @@ class CI_Upload {
}
}
- if ($new_filename == '')
+ if ($new_filename === '')
{
- $this->set_error('upload_bad_filename');
+ $this->set_error('upload_bad_filename', 'debug');
return FALSE;
}
- else
- {
- return $new_filename;
- }
+
+ return $new_filename;
}
// --------------------------------------------------------------------
@@ -428,12 +688,29 @@ class CI_Upload {
/**
* Set Maximum File Size
*
- * @param integer
- * @return void
+ * @param int $n
+ * @return CI_Upload
*/
public function set_max_filesize($n)
{
- $this->max_size = ((int) $n < 0) ? 0: (int) $n;
+ $this->max_size = ($n < 0) ? 0 : (int) $n;
+ return $this;
+ }
+
+ // --------------------------------------------------------------------
+
+ /**
+ * Set Maximum File Size
+ *
+ * An internal alias to set_max_filesize() to help with configuration
+ * as initialize() will look for a set_<property_name>() method ...
+ *
+ * @param int $n
+ * @return CI_Upload
+ */
+ protected function set_max_size($n)
+ {
+ return $this->set_max_filesize($n);
}
// --------------------------------------------------------------------
@@ -441,12 +718,13 @@ class CI_Upload {
/**
* Set Maximum File Name Length
*
- * @param integer
- * @return void
+ * @param int $n
+ * @return CI_Upload
*/
public function set_max_filename($n)
{
- $this->max_filename = ((int) $n < 0) ? 0: (int) $n;
+ $this->max_filename = ($n < 0) ? 0 : (int) $n;
+ return $this;
}
// --------------------------------------------------------------------
@@ -454,12 +732,13 @@ class CI_Upload {
/**
* Set Maximum Image Width
*
- * @param integer
- * @return void
+ * @param int $n
+ * @return CI_Upload
*/
public function set_max_width($n)
{
- $this->max_width = ((int) $n < 0) ? 0: (int) $n;
+ $this->max_width = ($n < 0) ? 0 : (int) $n;
+ return $this;
}
// --------------------------------------------------------------------
@@ -467,12 +746,41 @@ class CI_Upload {
/**
* Set Maximum Image Height
*
- * @param integer
- * @return void
+ * @param int $n
+ * @return CI_Upload
*/
public function set_max_height($n)
{
- $this->max_height = ((int) $n < 0) ? 0: (int) $n;
+ $this->max_height = ($n < 0) ? 0 : (int) $n;
+ return $this;
+ }
+
+ // --------------------------------------------------------------------
+
+ /**
+ * Set minimum image width
+ *
+ * @param int $n
+ * @return CI_Upload
+ */
+ public function set_min_width($n)
+ {
+ $this->min_width = ($n < 0) ? 0 : (int) $n;
+ return $this;
+ }
+
+ // --------------------------------------------------------------------
+
+ /**
+ * Set minimum image height
+ *
+ * @param int $n
+ * @return CI_Upload
+ */
+ public function set_min_height($n)
+ {
+ $this->min_height = ($n < 0) ? 0 : (int) $n;
+ return $this;
}
// --------------------------------------------------------------------
@@ -480,17 +788,15 @@ class CI_Upload {
/**
* Set Allowed File Types
*
- * @param string
- * @return void
+ * @param mixed $types
+ * @return CI_Upload
*/
public function set_allowed_types($types)
{
- if ( ! is_array($types) && $types == '*')
- {
- $this->allowed_types = '*';
- return;
- }
- $this->allowed_types = explode('|', $types);
+ $this->allowed_types = (is_array($types) OR $types === '*')
+ ? $types
+ : explode('|', $types);
+ return $this;
}
// --------------------------------------------------------------------
@@ -500,28 +806,25 @@ class CI_Upload {
*
* Uses GD to determine the width/height/type of image
*
- * @param string
- * @return void
+ * @param string $path
+ * @return CI_Upload
*/
public function set_image_properties($path = '')
{
- if ( ! $this->is_image())
- {
- return;
- }
-
- if (function_exists('getimagesize'))
+ if ($this->is_image() && function_exists('getimagesize'))
{
if (FALSE !== ($D = @getimagesize($path)))
{
$types = array(1 => 'gif', 2 => 'jpeg', 3 => 'png');
- $this->image_width = $D['0'];
- $this->image_height = $D['1'];
- $this->image_type = ( ! isset($types[$D['2']])) ? 'unknown' : $types[$D['2']];
- $this->image_size_str = $D['3']; // string containing height and width
+ $this->image_width = $D[0];
+ $this->image_height = $D[1];
+ $this->image_type = isset($types[$D[2]]) ? $types[$D[2]] : 'unknown';
+ $this->image_size_str = $D[3]; // string containing height and width
}
}
+
+ return $this;
}
// --------------------------------------------------------------------
@@ -532,12 +835,13 @@ class CI_Upload {
* Enables the XSS flag so that the file that was uploaded
* will be run through the XSS filter.
*
- * @param bool
- * @return void
+ * @param bool $flag
+ * @return CI_Upload
*/
public function set_xss_clean($flag = FALSE)
{
- $this->xss_clean = ($flag == TRUE) ? TRUE : FALSE;
+ $this->xss_clean = ($flag === TRUE);
+ return $this;
}
// --------------------------------------------------------------------
@@ -559,19 +863,14 @@ class CI_Upload {
{
$this->file_type = 'image/png';
}
-
- if (in_array($this->file_type, $jpeg_mimes))
+ elseif (in_array($this->file_type, $jpeg_mimes))
{
$this->file_type = 'image/jpeg';
}
- $img_mimes = array(
- 'image/gif',
- 'image/jpeg',
- 'image/png',
- );
+ $img_mimes = array('image/gif', 'image/jpeg', 'image/png', 'image/webp');
- return (in_array($this->file_type, $img_mimes, TRUE)) ? TRUE : FALSE;
+ return in_array($this->file_type, $img_mimes, TRUE);
}
// --------------------------------------------------------------------
@@ -579,37 +878,33 @@ class CI_Upload {
/**
* Verify that the filetype is allowed
*
+ * @param bool $ignore_mime
* @return bool
*/
public function is_allowed_filetype($ignore_mime = FALSE)
{
- if ($this->allowed_types == '*')
+ if ($this->allowed_types === '*')
{
return TRUE;
}
- if (count($this->allowed_types) == 0 OR ! is_array($this->allowed_types))
+ if (empty($this->allowed_types) OR ! is_array($this->allowed_types))
{
- $this->set_error('upload_no_file_types');
+ $this->set_error('upload_no_file_types', 'debug');
return FALSE;
}
$ext = strtolower(ltrim($this->file_ext, '.'));
- if ( ! in_array($ext, $this->allowed_types))
+ if ( ! in_array($ext, $this->allowed_types, TRUE))
{
return FALSE;
}
// Images get some additional checks
- $image_types = array('gif', 'jpg', 'jpeg', 'png', 'jpe');
-
- if (in_array($ext, $image_types))
+ if (in_array($ext, array('gif', 'jpg', 'jpeg', 'jpe', 'png', 'webp'), TRUE) && @getimagesize($this->file_temp) === FALSE)
{
- if (getimagesize($this->file_temp) === FALSE)
- {
- return FALSE;
- }
+ return FALSE;
}
if ($ignore_mime === TRUE)
@@ -617,18 +912,11 @@ class CI_Upload {
return TRUE;
}
- $mime = $this->mimes_types($ext);
-
- if (is_array($mime))
+ if (isset($this->_mimes[$ext]))
{
- if (in_array($this->file_type, $mime, TRUE))
- {
- return TRUE;
- }
- }
- elseif ($mime == $this->file_type)
- {
- return TRUE;
+ return is_array($this->_mimes[$ext])
+ ? in_array($this->file_type, $this->_mimes[$ext], TRUE)
+ : ($this->_mimes[$ext] === $this->file_type);
}
return FALSE;
@@ -643,14 +931,7 @@ class CI_Upload {
*/
public function is_allowed_filesize()
{
- if ($this->max_size != 0 AND $this->file_size > $this->max_size)
- {
- return FALSE;
- }
- else
- {
- return TRUE;
- }
+ return ($this->max_size === 0 OR $this->max_size > $this->file_size);
}
// --------------------------------------------------------------------
@@ -671,17 +952,25 @@ class CI_Upload {
{
$D = @getimagesize($this->file_temp);
- if ($this->max_width > 0 AND $D['0'] > $this->max_width)
+ if ($this->max_width > 0 && $D[0] > $this->max_width)
{
return FALSE;
}
- if ($this->max_height > 0 AND $D['1'] > $this->max_height)
+ if ($this->max_height > 0 && $D[1] > $this->max_height)
{
return FALSE;
}
- return TRUE;
+ if ($this->min_width > 0 && $D[0] < $this->min_width)
+ {
+ return FALSE;
+ }
+
+ if ($this->min_height > 0 && $D[1] < $this->min_height)
+ {
+ return FALSE;
+ }
}
return TRUE;
@@ -694,35 +983,34 @@ class CI_Upload {
*
* Verifies that it is a valid upload path with proper permissions.
*
- *
* @return bool
*/
public function validate_upload_path()
{
- if ($this->upload_path == '')
+ if ($this->upload_path === '')
{
- $this->set_error('upload_no_filepath');
+ $this->set_error('upload_no_filepath', 'error');
return FALSE;
}
- if (function_exists('realpath') AND @realpath($this->upload_path) !== FALSE)
+ if (realpath($this->upload_path) !== FALSE)
{
- $this->upload_path = str_replace("\\", "/", realpath($this->upload_path));
+ $this->upload_path = str_replace('\\', '/', realpath($this->upload_path));
}
- if ( ! @is_dir($this->upload_path))
+ if ( ! is_dir($this->upload_path))
{
- $this->set_error('upload_no_filepath');
+ $this->set_error('upload_no_filepath', 'error');
return FALSE;
}
if ( ! is_really_writable($this->upload_path))
{
- $this->set_error('upload_not_writable');
+ $this->set_error('upload_not_writable', 'error');
return FALSE;
}
- $this->upload_path = preg_replace("/(.+?)\/*$/", "\\1/", $this->upload_path);
+ $this->upload_path = preg_replace('/(.+?)\/*$/', '\\1/', $this->upload_path);
return TRUE;
}
@@ -731,57 +1019,20 @@ class CI_Upload {
/**
* Extract the file extension
*
- * @param string
+ * @param string $filename
* @return string
*/
public function get_extension($filename)
{
$x = explode('.', $filename);
- return '.'.end($x);
- }
- // --------------------------------------------------------------------
+ if (count($x) === 1)
+ {
+ return '';
+ }
- /**
- * Clean the file name for security
- *
- * @param string
- * @return string
- */
- public function clean_file_name($filename)
- {
- $bad = array(
- "<!--",
- "-->",
- "'",
- "<",
- ">",
- '"',
- '&',
- '$',
- '=',
- ';',
- '?',
- '/',
- "%20",
- "%22",
- "%3c", // <
- "%253c", // <
- "%3e", // >
- "%0e", // >
- "%28", // (
- "%29", // )
- "%2528", // (
- "%26", // &
- "%24", // $
- "%3f", // ?
- "%3b", // ;
- "%3d" // =
- );
-
- $filename = str_replace($bad, '', $filename);
-
- return stripslashes($filename);
+ $ext = ($this->file_ext_tolower) ? strtolower(end($x)) : end($x);
+ return '.'.$ext;
}
// --------------------------------------------------------------------
@@ -789,7 +1040,8 @@ class CI_Upload {
/**
* Limit the File Name Length
*
- * @param string
+ * @param string $filename
+ * @param int $length
* @return string
*/
public function limit_filename_length($filename, $length)
@@ -819,7 +1071,7 @@ class CI_Upload {
* I'm not sure that it won't negatively affect certain files in unexpected ways,
* but so far I haven't found that it causes trouble.
*
- * @return void
+ * @return string
*/
public function do_xss_clean()
{
@@ -830,23 +1082,34 @@ class CI_Upload {
return FALSE;
}
- if (function_exists('memory_get_usage') && memory_get_usage() && ini_get('memory_limit') != '')
+ if (memory_get_usage() && ($memory_limit = ini_get('memory_limit')) > 0)
{
- $current = ini_get('memory_limit') * 1024 * 1024;
-
- // There was a bug/behavioural change in PHP 5.2, where numbers over one million get output
- // into scientific notation. number_format() ensures this number is an integer
- // http://bugs.php.net/bug.php?id=43053
-
- $new_memory = number_format(ceil(filesize($file) + $current), 0, '.', '');
+ $memory_limit = str_split($memory_limit, strspn($memory_limit, '1234567890'));
+ if ( ! empty($memory_limit[1]))
+ {
+ switch ($memory_limit[1][0])
+ {
+ case 'g':
+ case 'G':
+ $memory_limit[0] *= 1024 * 1024 * 1024;
+ break;
+ case 'm':
+ case 'M':
+ $memory_limit[0] *= 1024 * 1024;
+ break;
+ default:
+ break;
+ }
+ }
- ini_set('memory_limit', $new_memory); // When an integer is used, the value is measured in bytes. - PHP.net
+ $memory_limit = (int) ceil(filesize($file) + $memory_limit[0]);
+ ini_set('memory_limit', $memory_limit); // When an integer is used, the value is measured in bytes. - PHP.net
}
// If the file being uploaded is an image, then we should have no problem with XSS attacks (in theory), but
// IE can be fooled into mime-type detecting a malformed image as an html file, thus executing an XSS attack on anyone
- // using IE who looks at the image. It does this by inspecting the first 255 bytes of an image. To get around this
- // CI will itself look at the first 255 bytes of an image to determine its relative safety. This can save a lot of
+ // using IE who looks at the image. It does this by inspecting the first 255 bytes of an image. To get around this
+ // CI will itself look at the first 255 bytes of an image to determine its relative safety. This can save a lot of
// processor power and time if it is actually a clean image, as it will be in nearly all instances _except_ an
// attempted XSS attack.
@@ -864,14 +1127,8 @@ class CI_Upload {
// <a, <body, <head, <html, <img, <plaintext, <pre, <script, <table, <title
// title is basically just in SVG, but we filter it anyhow
- if ( ! preg_match('/<(a|body|head|html|img|plaintext|pre|script|table|title)[\s>]/i', $opening_bytes))
- {
- return TRUE; // its an image, no "triggers" detected in the first 256 bytes, we're good
- }
- else
- {
- return FALSE;
- }
+ // if it's an image or no "triggers" detected in the first 256 bytes - we're good
+ return ! preg_match('/<(a|body|head|html|img|plaintext|pre|script|table|title)[\s>]/i', $opening_bytes);
}
if (($data = @file_get_contents($file)) === FALSE)
@@ -879,8 +1136,7 @@ class CI_Upload {
return FALSE;
}
- $CI =& get_instance();
- return $CI->security->xss_clean($data, TRUE);
+ return $this->_CI->security->xss_clean($data, TRUE);
}
// --------------------------------------------------------------------
@@ -888,29 +1144,22 @@ class CI_Upload {
/**
* Set an error message
*
- * @param string
- * @return void
+ * @param string $msg
+ * @return CI_Upload
*/
- public function set_error($msg)
+ public function set_error($msg, $log_level = 'error')
{
- $CI =& get_instance();
- $CI->lang->load('upload');
+ $this->_CI->lang->load('upload');
- if (is_array($msg))
+ is_array($msg) OR $msg = array($msg);
+ foreach ($msg as $val)
{
- foreach ($msg as $val)
- {
- $msg = ($CI->lang->line($val) == FALSE) ? $val : $CI->lang->line($val);
- $this->error_msg[] = $msg;
- log_message('error', $msg);
- }
- }
- else
- {
- $msg = ($CI->lang->line($msg) == FALSE) ? $msg : $CI->lang->line($msg);
+ $msg = ($this->_CI->lang->line($val) === FALSE) ? $val : $this->_CI->lang->line($val);
$this->error_msg[] = $msg;
- log_message('error', $msg);
+ log_message($log_level, $msg);
}
+
+ return $this;
}
// --------------------------------------------------------------------
@@ -918,56 +1167,13 @@ class CI_Upload {
/**
* Display the error message
*
- * @param string
- * @param string
+ * @param string $open
+ * @param string $close
* @return string
*/
public function display_errors($open = '<p>', $close = '</p>')
{
- $str = '';
- foreach ($this->error_msg as $val)
- {
- $str .= $open.$val.$close;
- }
-
- return $str;
- }
-
- // --------------------------------------------------------------------
-
- /**
- * List of Mime Types
- *
- * This is a list of mime types. We use it to validate
- * the "allowed types" set by the developer
- *
- * @param string
- * @return string
- */
- public function mimes_types($mime)
- {
- global $mimes;
-
- if (count($this->mimes) == 0)
- {
- if (defined('ENVIRONMENT') AND is_file(APPPATH.'config/'.ENVIRONMENT.'/mimes.php'))
- {
- include(APPPATH.'config/'.ENVIRONMENT.'/mimes.php');
- }
- elseif (is_file(APPPATH.'config/mimes.php'))
- {
- include(APPPATH.'config//mimes.php');
- }
- else
- {
- return FALSE;
- }
-
- $this->mimes = $mimes;
- unset($mimes);
- }
-
- return ( ! isset($this->mimes[$mime])) ? FALSE : $this->mimes[$mime];
+ return (count($this->error_msg) > 0) ? $open.implode($close.$open, $this->error_msg).$close : '';
}
// --------------------------------------------------------------------
@@ -975,38 +1181,24 @@ class CI_Upload {
/**
* Prep Filename
*
- * Prevents possible script execution from Apache's handling of files multiple extensions
- * http://httpd.apache.org/docs/1.3/mod/mod_mime.html#multipleext
+ * Prevents possible script execution from Apache's handling
+ * of files' multiple extensions.
+ *
+ * @link https://httpd.apache.org/docs/1.3/mod/mod_mime.html#multipleext
*
- * @param string
+ * @param string $filename
* @return string
*/
protected function _prep_filename($filename)
{
- if (strpos($filename, '.') === FALSE OR $this->allowed_types == '*')
+ if ($this->mod_mime_fix === FALSE OR $this->allowed_types === '*' OR ($ext_pos = strrpos($filename, '.')) === FALSE)
{
return $filename;
}
- $parts = explode('.', $filename);
- $ext = array_pop($parts);
- $filename = array_shift($parts);
-
- foreach ($parts as $part)
- {
- if ( ! in_array(strtolower($part), $this->allowed_types) OR $this->mimes_types(strtolower($part)) === FALSE)
- {
- $filename .= '.'.$part.'_';
- }
- else
- {
- $filename .= '.'.$part;
- }
- }
-
- $filename .= '.'.$ext;
-
- return $filename;
+ $ext = substr($filename, $ext_pos);
+ $filename = substr($filename, 0, $ext_pos);
+ return str_replace('.', '_', $filename).$ext;
}
// --------------------------------------------------------------------
@@ -1017,7 +1209,7 @@ class CI_Upload {
* Detects the (actual) MIME type of the uploaded file, if possible.
* The input array is expected to be $_FILES[$field]
*
- * @param array
+ * @param array $file
* @return void
*/
protected function _file_mime_type($file)
@@ -1025,15 +1217,18 @@ class CI_Upload {
// We'll need this to validate the MIME info string (e.g. text/plain; charset=us-ascii)
$regexp = '/^([a-z\-]+\/[a-z0-9\-\.\+]+)(;\s.+)?$/';
- /* Fileinfo extension - most reliable method
+ /**
+ * Fileinfo extension - most reliable method
*
- * Unfortunately, prior to PHP 5.3 - it's only available as a PECL extension and the
- * more convenient FILEINFO_MIME_TYPE flag doesn't exist.
+ * Apparently XAMPP, CentOS, cPanel and who knows what
+ * other PHP distribution channels EXPLICITLY DISABLE
+ * ext/fileinfo, which is otherwise enabled by default
+ * since PHP 5.3 ...
*/
if (function_exists('finfo_file'))
{
- $finfo = finfo_open(FILEINFO_MIME);
- if (is_resource($finfo)) // It is possible that a FALSE value is returned, if there is no magic MIME database file found on the system
+ $finfo = @finfo_open(FILEINFO_MIME);
+ if ($finfo !== FALSE) // It is possible that a FALSE value is returned, if there is no magic MIME database file found on the system
{
$mime = @finfo_file($finfo, $file['tmp_name']);
finfo_close($finfo);
@@ -1059,16 +1254,16 @@ class CI_Upload {
* Notes:
* - the DIRECTORY_SEPARATOR comparison ensures that we're not on a Windows system
* - many system admins would disable the exec(), shell_exec(), popen() and similar functions
- * due to security concerns, hence the function_exists() checks
+ * due to security concerns, hence the function_usable() checks
*/
if (DIRECTORY_SEPARATOR !== '\\')
{
- $cmd = 'file --brief --mime ' . escapeshellarg($file['tmp_name']) . ' 2>&1';
+ $cmd = 'file --brief --mime '.escapeshellarg($file['tmp_name']).' 2>&1';
- if (function_exists('exec'))
+ if (function_usable('exec'))
{
/* This might look confusing, as $mime is being populated with all of the output when set in the second parameter.
- * However, we only neeed the last line, which is the actual return value of exec(), and as such - it overwrites
+ * However, we only need the last line, which is the actual return value of exec(), and as such - it overwrites
* anything that could already be set for $mime previously. This effectively makes the second parameter a dummy
* value, which is only put to allow us to get the return status code.
*/
@@ -1080,7 +1275,7 @@ class CI_Upload {
}
}
- if ( (bool) @ini_get('safe_mode') === FALSE && function_exists('shell_exec'))
+ if (function_usable('shell_exec'))
{
$mime = @shell_exec($cmd);
if (strlen($mime) > 0)
@@ -1094,7 +1289,7 @@ class CI_Upload {
}
}
- if (function_exists('popen'))
+ if (function_usable('popen'))
{
$proc = @popen($cmd, 'r');
if (is_resource($proc))
@@ -1114,7 +1309,7 @@ class CI_Upload {
}
}
- // Fall back to the deprecated mime_content_type(), if available (still better than $_FILES[$field]['type'])
+ // Fall back to mime_content_type(), if available (still better than $_FILES[$field]['type'])
if (function_exists('mime_content_type'))
{
$this->file_type = @mime_content_type($file['tmp_name']);
@@ -1127,10 +1322,4 @@ class CI_Upload {
$this->file_type = $file['type'];
}
- // --------------------------------------------------------------------
-
}
-// END Upload Class
-
-/* End of file Upload.php */
-/* Location: ./system/libraries/Upload.php */
diff --git a/system/libraries/User_agent.php b/system/libraries/User_agent.php
index 9b0d87134..6dfabda2e 100644
--- a/system/libraries/User_agent.php
+++ b/system/libraries/User_agent.php
@@ -1,77 +1,188 @@
-<?php if ( ! defined('BASEPATH')) exit('No direct script access allowed');
+<?php
/**
* CodeIgniter
*
- * An open source application development framework for PHP 5.1.6 or newer
+ * An open source application development framework for PHP
*
- * @package CodeIgniter
- * @author ExpressionEngine Dev Team
- * @copyright Copyright (c) 2008 - 2014, EllisLab, Inc.
- * @license http://codeigniter.com/user_guide/license.html
- * @link http://codeigniter.com
- * @since Version 1.0
+ * This content is released under the MIT License (MIT)
+ *
+ * Copyright (c) 2019 - 2022, CodeIgniter Foundation
+ *
+ * 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.
+ *
+ * @package CodeIgniter
+ * @author EllisLab Dev Team
+ * @copyright Copyright (c) 2008 - 2014, EllisLab, Inc. (https://ellislab.com/)
+ * @copyright Copyright (c) 2014 - 2019, British Columbia Institute of Technology (https://bcit.ca/)
+ * @copyright Copyright (c) 2019 - 2022, CodeIgniter Foundation (https://codeigniter.com/)
+ * @license https://opensource.org/licenses/MIT MIT License
+ * @link https://codeigniter.com
+ * @since Version 1.0.0
* @filesource
*/
-
-// ------------------------------------------------------------------------
+defined('BASEPATH') OR exit('No direct script access allowed');
/**
* User Agent Class
*
- * Identifies the platform, browser, robot, or mobile devise of the browsing agent
+ * Identifies the platform, browser, robot, or mobile device of the browsing agent
*
* @package CodeIgniter
* @subpackage Libraries
* @category User Agent
- * @author ExpressionEngine Dev Team
- * @link http://codeigniter.com/user_guide/libraries/user_agent.html
+ * @author EllisLab Dev Team
+ * @link https://codeigniter.com/userguide3/libraries/user_agent.html
*/
class CI_User_agent {
- var $agent = NULL;
+ /**
+ * Current user-agent
+ *
+ * @var string
+ */
+ public $agent = NULL;
+
+ /**
+ * Flag for if the user-agent belongs to a browser
+ *
+ * @var bool
+ */
+ public $is_browser = FALSE;
+
+ /**
+ * Flag for if the user-agent is a robot
+ *
+ * @var bool
+ */
+ public $is_robot = FALSE;
+
+ /**
+ * Flag for if the user-agent is a mobile browser
+ *
+ * @var bool
+ */
+ public $is_mobile = FALSE;
+
+ /**
+ * Languages accepted by the current user agent
+ *
+ * @var array
+ */
+ public $languages = array();
+
+ /**
+ * Character sets accepted by the current user agent
+ *
+ * @var array
+ */
+ public $charsets = array();
- var $is_browser = FALSE;
- var $is_robot = FALSE;
- var $is_mobile = FALSE;
+ /**
+ * List of platforms to compare against current user agent
+ *
+ * @var array
+ */
+ public $platforms = array();
- var $languages = array();
- var $charsets = array();
+ /**
+ * List of browsers to compare against current user agent
+ *
+ * @var array
+ */
+ public $browsers = array();
- var $platforms = array();
- var $browsers = array();
- var $mobiles = array();
- var $robots = array();
+ /**
+ * List of mobile browsers to compare against current user agent
+ *
+ * @var array
+ */
+ public $mobiles = array();
- var $platform = '';
- var $browser = '';
- var $version = '';
- var $mobile = '';
- var $robot = '';
+ /**
+ * List of robots to compare against current user agent
+ *
+ * @var array
+ */
+ public $robots = array();
+
+ /**
+ * Current user-agent platform
+ *
+ * @var string
+ */
+ public $platform = '';
+
+ /**
+ * Current user-agent browser
+ *
+ * @var string
+ */
+ public $browser = '';
+
+ /**
+ * Current user-agent version
+ *
+ * @var string
+ */
+ public $version = '';
+
+ /**
+ * Current user-agent mobile name
+ *
+ * @var string
+ */
+ public $mobile = '';
+
+ /**
+ * Current user-agent robot name
+ *
+ * @var string
+ */
+ public $robot = '';
+
+ /**
+ * HTTP Referer
+ *
+ * @var mixed
+ */
+ public $referer;
+
+ // --------------------------------------------------------------------
/**
* Constructor
*
* Sets the User Agent and runs the compilation routine
*
- * @access public
* @return void
*/
public function __construct()
{
+ $this->_load_agent_file();
+
if (isset($_SERVER['HTTP_USER_AGENT']))
{
$this->agent = trim($_SERVER['HTTP_USER_AGENT']);
+ $this->_compile_data();
}
- if ( ! is_null($this->agent))
- {
- if ($this->_load_agent_file())
- {
- $this->_compile_data();
- }
- }
-
- log_message('debug', "User Agent Class Initialized");
+ log_message('info', 'User Agent Class Initialized');
}
// --------------------------------------------------------------------
@@ -79,20 +190,22 @@ class CI_User_agent {
/**
* Compile the User Agent Data
*
- * @access private
* @return bool
*/
- private function _load_agent_file()
+ protected function _load_agent_file()
{
- if (defined('ENVIRONMENT') AND is_file(APPPATH.'config/'.ENVIRONMENT.'/user_agents.php'))
+ if (($found = file_exists(APPPATH.'config/user_agents.php')))
{
- include(APPPATH.'config/'.ENVIRONMENT.'/user_agents.php');
+ include(APPPATH.'config/user_agents.php');
}
- elseif (is_file(APPPATH.'config/user_agents.php'))
+
+ if (file_exists(APPPATH.'config/'.ENVIRONMENT.'/user_agents.php'))
{
- include(APPPATH.'config/user_agents.php');
+ include(APPPATH.'config/'.ENVIRONMENT.'/user_agents.php');
+ $found = TRUE;
}
- else
+
+ if ($found !== TRUE)
{
return FALSE;
}
@@ -135,10 +248,9 @@ class CI_User_agent {
/**
* Compile the User Agent Data
*
- * @access private
* @return bool
*/
- private function _compile_data()
+ protected function _compile_data()
{
$this->_set_platform();
@@ -156,23 +268,24 @@ class CI_User_agent {
/**
* Set the Platform
*
- * @access private
- * @return mixed
+ * @return bool
*/
- private function _set_platform()
+ protected function _set_platform()
{
- if (is_array($this->platforms) AND count($this->platforms) > 0)
+ if (is_array($this->platforms) && count($this->platforms) > 0)
{
foreach ($this->platforms as $key => $val)
{
- if (preg_match("|".preg_quote($key)."|i", $this->agent))
+ if (preg_match('|'.preg_quote($key).'|i', $this->agent))
{
$this->platform = $val;
return TRUE;
}
}
}
+
$this->platform = 'Unknown Platform';
+ return FALSE;
}
// --------------------------------------------------------------------
@@ -180,16 +293,15 @@ class CI_User_agent {
/**
* Set the Browser
*
- * @access private
* @return bool
*/
- private function _set_browser()
+ protected function _set_browser()
{
- if (is_array($this->browsers) AND count($this->browsers) > 0)
+ if (is_array($this->browsers) && count($this->browsers) > 0)
{
foreach ($this->browsers as $key => $val)
{
- if (preg_match("|".preg_quote($key).".*?([0-9\.]+)|i", $this->agent, $match))
+ if (preg_match('|'.$key.'.*?([0-9\.]+)|i', $this->agent, $match))
{
$this->is_browser = TRUE;
$this->version = $match[1];
@@ -199,6 +311,7 @@ class CI_User_agent {
}
}
}
+
return FALSE;
}
@@ -207,23 +320,24 @@ class CI_User_agent {
/**
* Set the Robot
*
- * @access private
* @return bool
*/
- private function _set_robot()
+ protected function _set_robot()
{
- if (is_array($this->robots) AND count($this->robots) > 0)
+ if (is_array($this->robots) && count($this->robots) > 0)
{
foreach ($this->robots as $key => $val)
{
- if (preg_match("|".preg_quote($key)."|i", $this->agent))
+ if (preg_match('|'.preg_quote($key).'|i', $this->agent))
{
$this->is_robot = TRUE;
$this->robot = $val;
+ $this->_set_mobile();
return TRUE;
}
}
}
+
return FALSE;
}
@@ -232,16 +346,15 @@ class CI_User_agent {
/**
* Set the Mobile Device
*
- * @access private
* @return bool
*/
- private function _set_mobile()
+ protected function _set_mobile()
{
- if (is_array($this->mobiles) AND count($this->mobiles) > 0)
+ if (is_array($this->mobiles) && count($this->mobiles) > 0)
{
foreach ($this->mobiles as $key => $val)
{
- if (FALSE !== (strpos(strtolower($this->agent), $key)))
+ if (FALSE !== (stripos($this->agent, $key)))
{
$this->is_mobile = TRUE;
$this->mobile = $val;
@@ -249,6 +362,7 @@ class CI_User_agent {
}
}
}
+
return FALSE;
}
@@ -257,19 +371,16 @@ class CI_User_agent {
/**
* Set the accepted languages
*
- * @access private
* @return void
*/
- private function _set_languages()
+ protected function _set_languages()
{
- if ((count($this->languages) == 0) AND isset($_SERVER['HTTP_ACCEPT_LANGUAGE']) AND $_SERVER['HTTP_ACCEPT_LANGUAGE'] != '')
+ if ((count($this->languages) === 0) && ! empty($_SERVER['HTTP_ACCEPT_LANGUAGE']))
{
- $languages = preg_replace('/(;q=[0-9\.]+)/i', '', strtolower(trim($_SERVER['HTTP_ACCEPT_LANGUAGE'])));
-
- $this->languages = explode(',', $languages);
+ $this->languages = explode(',', preg_replace('/(;\s?q=[0-9\.]+)|\s/i', '', strtolower(trim($_SERVER['HTTP_ACCEPT_LANGUAGE']))));
}
- if (count($this->languages) == 0)
+ if (count($this->languages) === 0)
{
$this->languages = array('Undefined');
}
@@ -280,19 +391,16 @@ class CI_User_agent {
/**
* Set the accepted character sets
*
- * @access private
* @return void
*/
- private function _set_charsets()
+ protected function _set_charsets()
{
- if ((count($this->charsets) == 0) AND isset($_SERVER['HTTP_ACCEPT_CHARSET']) AND $_SERVER['HTTP_ACCEPT_CHARSET'] != '')
+ if ((count($this->charsets) === 0) && ! empty($_SERVER['HTTP_ACCEPT_CHARSET']))
{
- $charsets = preg_replace('/(;q=.+)/i', '', strtolower(trim($_SERVER['HTTP_ACCEPT_CHARSET'])));
-
- $this->charsets = explode(',', $charsets);
+ $this->charsets = explode(',', preg_replace('/(;\s?q=.+)|\s/i', '', strtolower(trim($_SERVER['HTTP_ACCEPT_CHARSET']))));
}
- if (count($this->charsets) == 0)
+ if (count($this->charsets) === 0)
{
$this->charsets = array('Undefined');
}
@@ -303,7 +411,7 @@ class CI_User_agent {
/**
* Is Browser
*
- * @access public
+ * @param string $key
* @return bool
*/
public function is_browser($key = NULL)
@@ -320,7 +428,7 @@ class CI_User_agent {
}
// Check for a specific browser
- return array_key_exists($key, $this->browsers) AND $this->browser === $this->browsers[$key];
+ return (isset($this->browsers[$key]) && $this->browser === $this->browsers[$key]);
}
// --------------------------------------------------------------------
@@ -328,7 +436,7 @@ class CI_User_agent {
/**
* Is Robot
*
- * @access public
+ * @param string $key
* @return bool
*/
public function is_robot($key = NULL)
@@ -345,7 +453,7 @@ class CI_User_agent {
}
// Check for a specific robot
- return array_key_exists($key, $this->robots) AND $this->robot === $this->robots[$key];
+ return (isset($this->robots[$key]) && $this->robot === $this->robots[$key]);
}
// --------------------------------------------------------------------
@@ -353,7 +461,7 @@ class CI_User_agent {
/**
* Is Mobile
*
- * @access public
+ * @param string $key
* @return bool
*/
public function is_mobile($key = NULL)
@@ -370,7 +478,7 @@ class CI_User_agent {
}
// Check for a specific robot
- return array_key_exists($key, $this->mobiles) AND $this->mobile === $this->mobiles[$key];
+ return (isset($this->mobiles[$key]) && $this->mobile === $this->mobiles[$key]);
}
// --------------------------------------------------------------------
@@ -378,16 +486,26 @@ class CI_User_agent {
/**
* Is this a referral from another site?
*
- * @access public
* @return bool
*/
public function is_referral()
{
- if ( ! isset($_SERVER['HTTP_REFERER']) OR $_SERVER['HTTP_REFERER'] == '')
+ if ( ! isset($this->referer))
{
- return FALSE;
+ if (empty($_SERVER['HTTP_REFERER']))
+ {
+ $this->referer = FALSE;
+ }
+ else
+ {
+ $referer_host = @parse_url($_SERVER['HTTP_REFERER'], PHP_URL_HOST);
+ $own_host = parse_url((string) config_item('base_url'), PHP_URL_HOST);
+
+ $this->referer = ($referer_host && $referer_host !== $own_host);
+ }
}
- return TRUE;
+
+ return $this->referer;
}
// --------------------------------------------------------------------
@@ -395,7 +513,6 @@ class CI_User_agent {
/**
* Agent String
*
- * @access public
* @return string
*/
public function agent_string()
@@ -408,7 +525,6 @@ class CI_User_agent {
/**
* Get Platform
*
- * @access public
* @return string
*/
public function platform()
@@ -421,7 +537,6 @@ class CI_User_agent {
/**
* Get Browser Name
*
- * @access public
* @return string
*/
public function browser()
@@ -434,7 +549,6 @@ class CI_User_agent {
/**
* Get the Browser Version
*
- * @access public
* @return string
*/
public function version()
@@ -447,7 +561,6 @@ class CI_User_agent {
/**
* Get The Robot Name
*
- * @access public
* @return string
*/
public function robot()
@@ -459,7 +572,6 @@ class CI_User_agent {
/**
* Get the Mobile Device
*
- * @access public
* @return string
*/
public function mobile()
@@ -472,12 +584,11 @@ class CI_User_agent {
/**
* Get the referrer
*
- * @access public
* @return bool
*/
public function referrer()
{
- return ( ! isset($_SERVER['HTTP_REFERER']) OR $_SERVER['HTTP_REFERER'] == '') ? '' : trim($_SERVER['HTTP_REFERER']);
+ return empty($_SERVER['HTTP_REFERER']) ? '' : trim($_SERVER['HTTP_REFERER']);
}
// --------------------------------------------------------------------
@@ -485,12 +596,11 @@ class CI_User_agent {
/**
* Get the accepted languages
*
- * @access public
* @return array
*/
public function languages()
{
- if (count($this->languages) == 0)
+ if (count($this->languages) === 0)
{
$this->_set_languages();
}
@@ -503,12 +613,11 @@ class CI_User_agent {
/**
* Get the accepted Character Sets
*
- * @access public
* @return array
*/
public function charsets()
{
- if (count($this->charsets) == 0)
+ if (count($this->charsets) === 0)
{
$this->_set_charsets();
}
@@ -521,12 +630,12 @@ class CI_User_agent {
/**
* Test for a particular language
*
- * @access public
+ * @param string $lang
* @return bool
*/
public function accept_lang($lang = 'en')
{
- return (in_array(strtolower($lang), $this->languages(), TRUE));
+ return in_array(strtolower($lang), $this->languages(), TRUE);
}
// --------------------------------------------------------------------
@@ -534,16 +643,40 @@ class CI_User_agent {
/**
* Test for a particular character set
*
- * @access public
+ * @param string $charset
* @return bool
*/
public function accept_charset($charset = 'utf-8')
{
- return (in_array(strtolower($charset), $this->charsets(), TRUE));
+ return in_array(strtolower($charset), $this->charsets(), TRUE);
}
-}
+ // --------------------------------------------------------------------
+ /**
+ * Parse a custom user-agent string
+ *
+ * @param string $string
+ * @return void
+ */
+ public function parse($string)
+ {
+ // Reset values
+ $this->is_browser = FALSE;
+ $this->is_robot = FALSE;
+ $this->is_mobile = FALSE;
+ $this->browser = '';
+ $this->version = '';
+ $this->mobile = '';
+ $this->robot = '';
+
+ // Set the new user-agent string and parse it, unless empty
+ $this->agent = $string;
+
+ if ( ! empty($string))
+ {
+ $this->_compile_data();
+ }
+ }
-/* End of file User_agent.php */
-/* Location: ./system/libraries/User_agent.php */ \ No newline at end of file
+}
diff --git a/system/libraries/Xmlrpc.php b/system/libraries/Xmlrpc.php
index f0f53cefe..8587015e8 100644
--- a/system/libraries/Xmlrpc.php
+++ b/system/libraries/Xmlrpc.php
@@ -1,24 +1,48 @@
-<?php if ( ! defined('BASEPATH')) exit('No direct script access allowed');
+<?php
/**
* CodeIgniter
*
- * An open source application development framework for PHP 5.1.6 or newer
+ * An open source application development framework for PHP
*
- * @package CodeIgniter
- * @author ExpressionEngine Dev Team
- * @copyright Copyright (c) 2008 - 2014, EllisLab, Inc.
- * @license http://codeigniter.com/user_guide/license.html
- * @link http://codeigniter.com
- * @since Version 1.0
+ * This content is released under the MIT License (MIT)
+ *
+ * Copyright (c) 2019 - 2022, CodeIgniter Foundation
+ *
+ * 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.
+ *
+ * @package CodeIgniter
+ * @author EllisLab Dev Team
+ * @copyright Copyright (c) 2008 - 2014, EllisLab, Inc. (https://ellislab.com/)
+ * @copyright Copyright (c) 2014 - 2019, British Columbia Institute of Technology (https://bcit.ca/)
+ * @copyright Copyright (c) 2019 - 2022, CodeIgniter Foundation (https://codeigniter.com/)
+ * @license https://opensource.org/licenses/MIT MIT License
+ * @link https://codeigniter.com
+ * @since Version 1.0.0
* @filesource
*/
+defined('BASEPATH') OR exit('No direct script access allowed');
if ( ! function_exists('xml_parser_create'))
{
show_error('Your PHP installation does not support XML');
}
-
// ------------------------------------------------------------------------
/**
@@ -27,51 +51,219 @@ if ( ! function_exists('xml_parser_create'))
* @package CodeIgniter
* @subpackage Libraries
* @category XML-RPC
- * @author ExpressionEngine Dev Team
- * @link http://codeigniter.com/user_guide/libraries/xmlrpc.html
+ * @author EllisLab Dev Team
+ * @link https://codeigniter.com/userguide3/libraries/xmlrpc.html
*/
class CI_Xmlrpc {
- var $debug = FALSE; // Debugging on or off
- var $xmlrpcI4 = 'i4';
- var $xmlrpcInt = 'int';
- var $xmlrpcBoolean = 'boolean';
- var $xmlrpcDouble = 'double';
- var $xmlrpcString = 'string';
- var $xmlrpcDateTime = 'dateTime.iso8601';
- var $xmlrpcBase64 = 'base64';
- var $xmlrpcArray = 'array';
- var $xmlrpcStruct = 'struct';
-
- var $xmlrpcTypes = array();
- var $valid_parents = array();
- var $xmlrpcerr = array(); // Response numbers
- var $xmlrpcstr = array(); // Response strings
-
- var $xmlrpc_defencoding = 'UTF-8';
- var $xmlrpcName = 'XML-RPC for CodeIgniter';
- var $xmlrpcVersion = '1.1';
- var $xmlrpcerruser = 800; // Start of user errors
- var $xmlrpcerrxml = 100; // Start of XML Parse errors
- var $xmlrpc_backslash = ''; // formulate backslashes for escaping regexp
-
- var $client;
- var $method;
- var $data;
- var $message = '';
- var $error = ''; // Error string for request
- var $result;
- var $response = array(); // Response from remote server
-
- var $xss_clean = TRUE;
-
- //-------------------------------------
- // VALUES THAT MULTIPLE CLASSES NEED
- //-------------------------------------
-
+ /**
+ * Debug flag
+ *
+ * @var bool
+ */
+ public $debug = FALSE;
+
+ /**
+ * I4 data type
+ *
+ * @var string
+ */
+ public $xmlrpcI4 = 'i4';
+
+ /**
+ * Integer data type
+ *
+ * @var string
+ */
+ public $xmlrpcInt = 'int';
+
+ /**
+ * Boolean data type
+ *
+ * @var string
+ */
+ public $xmlrpcBoolean = 'boolean';
+
+ /**
+ * Double data type
+ *
+ * @var string
+ */
+ public $xmlrpcDouble = 'double';
+
+ /**
+ * String data type
+ *
+ * @var string
+ */
+ public $xmlrpcString = 'string';
+
+ /**
+ * DateTime format
+ *
+ * @var string
+ */
+ public $xmlrpcDateTime = 'dateTime.iso8601';
+
+ /**
+ * Base64 data type
+ *
+ * @var string
+ */
+ public $xmlrpcBase64 = 'base64';
+
+ /**
+ * Array data type
+ *
+ * @var string
+ */
+ public $xmlrpcArray = 'array';
+
+ /**
+ * Struct data type
+ *
+ * @var string
+ */
+ public $xmlrpcStruct = 'struct';
+
+ /**
+ * Data types list
+ *
+ * @var array
+ */
+ public $xmlrpcTypes = array();
+
+ /**
+ * Valid parents list
+ *
+ * @var array
+ */
+ public $valid_parents = array();
+
+ /**
+ * Response error numbers list
+ *
+ * @var array
+ */
+ public $xmlrpcerr = array();
+
+ /**
+ * Response error messages list
+ *
+ * @var string[]
+ */
+ public $xmlrpcstr = array();
+
+ /**
+ * Encoding charset
+ *
+ * @var string
+ */
+ public $xmlrpc_defencoding = 'UTF-8';
+
+ /**
+ * XML-RPC client name
+ *
+ * @var string
+ */
+ public $xmlrpcName = 'XML-RPC for CodeIgniter';
+
+ /**
+ * XML-RPC version
+ *
+ * @var string
+ */
+ public $xmlrpcVersion = '1.1';
+
+ /**
+ * Start of user errors
+ *
+ * @var int
+ */
+ public $xmlrpcerruser = 800;
+
+ /**
+ * Start of XML parse errors
+ *
+ * @var int
+ */
+ public $xmlrpcerrxml = 100;
+
+ /**
+ * Backslash replacement value
+ *
+ * @var string
+ */
+ public $xmlrpc_backslash = '';
+
+ /**
+ * XML-RPC Client object
+ *
+ * @var object
+ */
+ public $client;
+
+ /**
+ * XML-RPC Method name
+ *
+ * @var string
+ */
+ public $method;
+
+ /**
+ * XML-RPC Data
+ *
+ * @var array
+ */
+ public $data;
+
+ /**
+ * XML-RPC Message
+ *
+ * @var string
+ */
+ public $message = '';
+
+ /**
+ * Request error message
+ *
+ * @var string
+ */
+ public $error = '';
+
+ /**
+ * XML-RPC result object
+ *
+ * @var object
+ */
+ public $result;
+
+ /**
+ * XML-RPC Response
+ *
+ * @var array
+ */
+ public $response = array(); // Response from remote server
+
+ /**
+ * XSS Filter flag
+ *
+ * @var bool
+ */
+ public $xss_clean = TRUE;
+
+ // --------------------------------------------------------------------
+
+ /**
+ * Constructor
+ *
+ * Initializes property default values
+ *
+ * @param array $config
+ * @return void
+ */
public function __construct($config = array())
{
- $this->xmlrpcName = $this->xmlrpcName;
$this->xmlrpc_backslash = chr(92).chr(92);
// Types for info sent back and forth
@@ -85,54 +277,56 @@ class CI_Xmlrpc {
$this->xmlrpcBase64 => '1',
$this->xmlrpcArray => '2',
$this->xmlrpcStruct => '3'
- );
+ );
// Array of Valid Parents for Various XML-RPC elements
- $this->valid_parents = array('BOOLEAN' => array('VALUE'),
- 'I4' => array('VALUE'),
- 'INT' => array('VALUE'),
- 'STRING' => array('VALUE'),
- 'DOUBLE' => array('VALUE'),
- 'DATETIME.ISO8601' => array('VALUE'),
- 'BASE64' => array('VALUE'),
- 'ARRAY' => array('VALUE'),
- 'STRUCT' => array('VALUE'),
- 'PARAM' => array('PARAMS'),
- 'METHODNAME' => array('METHODCALL'),
- 'PARAMS' => array('METHODCALL', 'METHODRESPONSE'),
- 'MEMBER' => array('STRUCT'),
- 'NAME' => array('MEMBER'),
- 'DATA' => array('ARRAY'),
- 'FAULT' => array('METHODRESPONSE'),
- 'VALUE' => array('MEMBER', 'DATA', 'PARAM', 'FAULT')
- );
-
+ $this->valid_parents = array('BOOLEAN' => array('VALUE'),
+ 'I4' => array('VALUE'),
+ 'INT' => array('VALUE'),
+ 'STRING' => array('VALUE'),
+ 'DOUBLE' => array('VALUE'),
+ 'DATETIME.ISO8601' => array('VALUE'),
+ 'BASE64' => array('VALUE'),
+ 'ARRAY' => array('VALUE'),
+ 'STRUCT' => array('VALUE'),
+ 'PARAM' => array('PARAMS'),
+ 'METHODNAME' => array('METHODCALL'),
+ 'PARAMS' => array('METHODCALL', 'METHODRESPONSE'),
+ 'MEMBER' => array('STRUCT'),
+ 'NAME' => array('MEMBER'),
+ 'DATA' => array('ARRAY'),
+ 'FAULT' => array('METHODRESPONSE'),
+ 'VALUE' => array('MEMBER', 'DATA', 'PARAM', 'FAULT')
+ );
// XML-RPC Responses
$this->xmlrpcerr['unknown_method'] = '1';
$this->xmlrpcstr['unknown_method'] = 'This is not a known method for this XML-RPC Server';
$this->xmlrpcerr['invalid_return'] = '2';
- $this->xmlrpcstr['invalid_return'] = 'The XML data received was either invalid or not in the correct form for XML-RPC. Turn on debugging to examine the XML data further.';
+ $this->xmlrpcstr['invalid_return'] = 'The XML data received was either invalid or not in the correct form for XML-RPC. Turn on debugging to examine the XML data further.';
$this->xmlrpcerr['incorrect_params'] = '3';
$this->xmlrpcstr['incorrect_params'] = 'Incorrect parameters were passed to method';
$this->xmlrpcerr['introspect_unknown'] = '4';
- $this->xmlrpcstr['introspect_unknown'] = "Cannot inspect signature for request: method unknown";
+ $this->xmlrpcstr['introspect_unknown'] = 'Cannot inspect signature for request: method unknown';
$this->xmlrpcerr['http_error'] = '5';
$this->xmlrpcstr['http_error'] = "Did not receive a '200 OK' response from remote server.";
$this->xmlrpcerr['no_data'] = '6';
- $this->xmlrpcstr['no_data'] ='No data received from server.';
+ $this->xmlrpcstr['no_data'] = 'No data received from server.';
$this->initialize($config);
- log_message('debug', "XML-RPC Class Initialized");
+ log_message('info', 'XML-RPC Class Initialized');
}
+ // --------------------------------------------------------------------
- //-------------------------------------
- // Initialize Prefs
- //-------------------------------------
-
- function initialize($config = array())
+ /**
+ * Initialize
+ *
+ * @param array $config
+ * @return void
+ */
+ public function initialize($config = array())
{
if (count($config) > 0)
{
@@ -145,64 +339,85 @@ class CI_Xmlrpc {
}
}
}
- // END
- //-------------------------------------
- // Take URL and parse it
- //-------------------------------------
-
- function server($url, $port=80)
+ // --------------------------------------------------------------------
+
+ /**
+ * Parse server URL
+ *
+ * @param string $url
+ * @param int $port
+ * @param string $proxy
+ * @param int $proxy_port
+ * @return void
+ */
+ public function server($url, $port = 80, $proxy = FALSE, $proxy_port = 8080)
{
- if (substr($url, 0, 4) != "http")
+ if (stripos($url, 'http') !== 0)
{
- $url = "http://".$url;
+ $url = 'http://'.$url;
}
$parts = parse_url($url);
- $path = ( ! isset($parts['path'])) ? '/' : $parts['path'];
+ if (isset($parts['user'], $parts['pass']))
+ {
+ $parts['host'] = $parts['user'].':'.$parts['pass'].'@'.$parts['host'];
+ }
+
+ $path = isset($parts['path']) ? $parts['path'] : '/';
- if (isset($parts['query']) && $parts['query'] != '')
+ if ( ! empty($parts['query']))
{
$path .= '?'.$parts['query'];
}
- $this->client = new XML_RPC_Client($path, $parts['host'], $port);
+ $this->client = new XML_RPC_Client($path, $parts['host'], $port, $proxy, $proxy_port);
}
- // END
- //-------------------------------------
- // Set Timeout
- //-------------------------------------
+ // --------------------------------------------------------------------
- function timeout($seconds=5)
+ /**
+ * Set Timeout
+ *
+ * @param int $seconds
+ * @return void
+ */
+ public function timeout($seconds = 5)
{
- if ( ! is_null($this->client) && is_int($seconds))
+ if ($this->client !== NULL && is_int($seconds))
{
$this->client->timeout = $seconds;
}
}
- // END
- //-------------------------------------
- // Set Methods
- //-------------------------------------
+ // --------------------------------------------------------------------
- function method($function)
+ /**
+ * Set Methods
+ *
+ * @param string $function Method name
+ * @return void
+ */
+ public function method($function)
{
$this->method = $function;
}
- // END
- //-------------------------------------
- // Take Array of Data and Create Objects
- //-------------------------------------
+ // --------------------------------------------------------------------
- function request($incoming)
+ /**
+ * Take Array of Data and Create Objects
+ *
+ * @param array $incoming
+ * @return void
+ */
+ public function request($incoming)
{
if ( ! is_array($incoming))
{
// Send Error
+ return;
}
$this->data = array();
@@ -212,49 +427,47 @@ class CI_Xmlrpc {
$this->data[$key] = $this->values_parsing($value);
}
}
- // END
+ // --------------------------------------------------------------------
- //-------------------------------------
- // Set Debug
- //-------------------------------------
-
- function set_debug($flag = TRUE)
+ /**
+ * Set Debug
+ *
+ * @param bool $flag
+ * @return void
+ */
+ public function set_debug($flag = TRUE)
{
- $this->debug = ($flag == TRUE) ? TRUE : FALSE;
+ $this->debug = ($flag === TRUE);
}
- //-------------------------------------
- // Values Parsing
- //-------------------------------------
+ // --------------------------------------------------------------------
- function values_parsing($value, $return = FALSE)
+ /**
+ * Values Parsing
+ *
+ * @param mixed $value
+ * @return object
+ */
+ public function values_parsing($value)
{
if (is_array($value) && array_key_exists(0, $value))
{
- if ( ! isset($value['1']) OR ( ! isset($this->xmlrpcTypes[$value['1']])))
+ if ( ! isset($value[1], $this->xmlrpcTypes[$value[1]]))
{
- if (is_array($value[0]))
- {
- $temp = new XML_RPC_Values($value['0'], 'array');
- }
- else
- {
- $temp = new XML_RPC_Values($value['0'], 'string');
- }
+ $temp = new XML_RPC_Values($value[0], (is_array($value[0]) ? 'array' : 'string'));
}
- elseif (is_array($value['0']) && ($value['1'] == 'struct' OR $value['1'] == 'array'))
+ else
{
- while (list($k) = each($value['0']))
+ if (is_array($value[0]) && ($value[1] === 'struct' OR $value[1] === 'array'))
{
- $value['0'][$k] = $this->values_parsing($value['0'][$k], TRUE);
+ foreach (array_keys($value[0]) as $k)
+ {
+ $value[0][$k] = $this->values_parsing($value[0][$k]);
+ }
}
- $temp = new XML_RPC_Values($value['0'], $value['1']);
- }
- else
- {
- $temp = new XML_RPC_Values($value['0'], $value['1']);
+ $temp = new XML_RPC_Values($value[0], $value[1]);
}
}
else
@@ -264,132 +477,248 @@ class CI_Xmlrpc {
return $temp;
}
- // END
+ // --------------------------------------------------------------------
- //-------------------------------------
- // Sends XML-RPC Request
- //-------------------------------------
-
- function send_request()
+ /**
+ * Sends XML-RPC Request
+ *
+ * @return bool
+ */
+ public function send_request()
{
- $this->message = new XML_RPC_Message($this->method,$this->data);
+ $this->message = new XML_RPC_Message($this->method, $this->data);
$this->message->debug = $this->debug;
- if ( ! $this->result = $this->client->send($this->message))
- {
- $this->error = $this->result->errstr;
- return FALSE;
- }
- elseif ( ! is_object($this->result->val))
+ if ( ! $this->result = $this->client->send($this->message) OR ! is_object($this->result->val))
{
$this->error = $this->result->errstr;
return FALSE;
}
$this->response = $this->result->decode();
-
return TRUE;
}
- // END
- //-------------------------------------
- // Returns Error
- //-------------------------------------
+ // --------------------------------------------------------------------
- function display_error()
+ /**
+ * Returns Error
+ *
+ * @return string
+ */
+ public function display_error()
{
return $this->error;
}
- // END
- //-------------------------------------
- // Returns Remote Server Response
- //-------------------------------------
+ // --------------------------------------------------------------------
- function display_response()
+ /**
+ * Returns Remote Server Response
+ *
+ * @return string
+ */
+ public function display_response()
{
return $this->response;
}
- // END
- //-------------------------------------
- // Sends an Error Message for Server Request
- //-------------------------------------
+ // --------------------------------------------------------------------
- function send_error_message($number, $message)
+ /**
+ * Sends an Error Message for Server Request
+ *
+ * @param int $number
+ * @param string $message
+ * @return object
+ */
+ public function send_error_message($number, $message)
{
- return new XML_RPC_Response('0',$number, $message);
+ return new XML_RPC_Response(0, $number, $message);
}
- // END
-
- //-------------------------------------
- // Send Response for Server Request
- //-------------------------------------
+ // --------------------------------------------------------------------
- function send_response($response)
+ /**
+ * Send Response for Server Request
+ *
+ * @param array $response
+ * @return object
+ */
+ public function send_response($response)
{
// $response should be array of values, which will be parsed
// based on their data and type into a valid group of XML-RPC values
-
- $response = $this->values_parsing($response);
-
- return new XML_RPC_Response($response);
+ return new XML_RPC_Response($this->values_parsing($response));
}
- // END
} // END XML_RPC Class
-
-
/**
* XML-RPC Client class
*
* @category XML-RPC
- * @author ExpressionEngine Dev Team
- * @link http://codeigniter.com/user_guide/libraries/xmlrpc.html
+ * @author EllisLab Dev Team
+ * @link https://codeigniter.com/userguide3/libraries/xmlrpc.html
*/
class XML_RPC_Client extends CI_Xmlrpc
{
- var $path = '';
- var $server = '';
- var $port = 80;
- var $errno = '';
- var $errstring = '';
- var $timeout = 5;
- var $no_multicall = FALSE;
-
- public function __construct($path, $server, $port=80)
+ /**
+ * Path
+ *
+ * @var string
+ */
+ public $path = '';
+
+ /**
+ * Server hostname
+ *
+ * @var string
+ */
+ public $server = '';
+
+ /**
+ * Server port
+ *
+ * @var int
+ */
+ public $port = 80;
+
+ /**
+ *
+ * Server username
+ *
+ * @var string
+ */
+ public $username;
+
+ /**
+ * Server password
+ *
+ * @var string
+ */
+ public $password;
+
+ /**
+ * Proxy hostname
+ *
+ * @var string
+ */
+ public $proxy = FALSE;
+
+ /**
+ * Proxy port
+ *
+ * @var int
+ */
+ public $proxy_port = 8080;
+
+ /**
+ * Error number
+ *
+ * @var string
+ */
+ public $errno = '';
+
+ /**
+ * Error message
+ *
+ * @var string
+ */
+ public $errstring = '';
+
+ /**
+ * Timeout in seconds
+ *
+ * @var int
+ */
+ public $timeout = 5;
+
+ /**
+ * No Multicall flag
+ *
+ * @var bool
+ */
+ public $no_multicall = FALSE;
+
+ // --------------------------------------------------------------------
+
+ /**
+ * Constructor
+ *
+ * @param string $path
+ * @param object $server
+ * @param int $port
+ * @param string $proxy
+ * @param int $proxy_port
+ * @return void
+ */
+ public function __construct($path, $server, $port = 80, $proxy = FALSE, $proxy_port = 8080)
{
parent::__construct();
+ $url = parse_url('http://'.$server);
+
+ if (isset($url['user'], $url['pass']))
+ {
+ $this->username = $url['user'];
+ $this->password = $url['pass'];
+ }
+
$this->port = $port;
- $this->server = $server;
+ $this->server = $url['host'];
$this->path = $path;
+ $this->proxy = $proxy;
+ $this->proxy_port = $proxy_port;
}
- function send($msg)
+ // --------------------------------------------------------------------
+
+ /**
+ * Send message
+ *
+ * @param mixed $msg
+ * @return object
+ */
+ public function send($msg)
{
if (is_array($msg))
{
// Multi-call disabled
- $r = new XML_RPC_Response(0, $this->xmlrpcerr['multicall_recursion'],$this->xmlrpcstr['multicall_recursion']);
- return $r;
+ return new XML_RPC_Response(0, $this->xmlrpcerr['multicall_recursion'], $this->xmlrpcstr['multicall_recursion']);
}
return $this->sendPayload($msg);
}
- function sendPayload($msg)
+ // --------------------------------------------------------------------
+
+ /**
+ * Send payload
+ *
+ * @param object $msg
+ * @return object
+ */
+ public function sendPayload($msg)
{
- $fp = @fsockopen($this->server, $this->port,$this->errno, $this->errstr, $this->timeout);
+ if ($this->proxy === FALSE)
+ {
+ $server = $this->server;
+ $port = $this->port;
+ }
+ else
+ {
+ $server = $this->proxy;
+ $port = $this->proxy_port;
+ }
+
+ $fp = @fsockopen($server, $port, $this->errno, $this->errstring, $this->timeout);
if ( ! is_resource($fp))
{
error_log($this->xmlrpcstr['http_error']);
- $r = new XML_RPC_Response(0, $this->xmlrpcerr['http_error'],$this->xmlrpcstr['http_error']);
- return $r;
+ return new XML_RPC_Response(0, $this->xmlrpcerr['http_error'], $this->xmlrpcstr['http_error']);
}
if (empty($msg->payload))
@@ -399,55 +728,121 @@ class XML_RPC_Client extends CI_Xmlrpc
}
$r = "\r\n";
- $op = "POST {$this->path} HTTP/1.0$r";
- $op .= "Host: {$this->server}$r";
- $op .= "Content-Type: text/xml$r";
- $op .= "User-Agent: {$this->xmlrpcName}$r";
- $op .= "Content-Length: ".strlen($msg->payload). "$r$r";
- $op .= $msg->payload;
+ $op = 'POST '.$this->path.' HTTP/1.0'.$r
+ .'Host: '.$this->server.$r
+ .'Content-Type: text/xml'.$r
+ .(isset($this->username, $this->password) ? 'Authorization: Basic '.base64_encode($this->username.':'.$this->password).$r : '')
+ .'User-Agent: '.$this->xmlrpcName.$r
+ .'Content-Length: '.strlen($msg->payload).$r.$r
+ .$msg->payload;
+
+ stream_set_timeout($fp, $this->timeout); // set timeout for subsequent operations
+ for ($written = $timestamp = 0, $length = strlen($op); $written < $length; $written += $result)
+ {
+ if (($result = fwrite($fp, substr($op, $written))) === FALSE)
+ {
+ break;
+ }
+ // See https://bugs.php.net/bug.php?id=39598 and https://secure.php.net/manual/en/function.fwrite.php#96951
+ elseif ($result === 0)
+ {
+ if ($timestamp === 0)
+ {
+ $timestamp = time();
+ }
+ elseif ($timestamp < (time() - $this->timeout))
+ {
+ $result = FALSE;
+ break;
+ }
+ }
+ else
+ {
+ $timestamp = 0;
+ }
+ }
- if ( ! fputs($fp, $op, strlen($op)))
+ if ($result === FALSE)
{
error_log($this->xmlrpcstr['http_error']);
- $r = new XML_RPC_Response(0, $this->xmlrpcerr['http_error'], $this->xmlrpcstr['http_error']);
- return $r;
+ return new XML_RPC_Response(0, $this->xmlrpcerr['http_error'], $this->xmlrpcstr['http_error']);
}
+
$resp = $msg->parseResponse($fp);
fclose($fp);
return $resp;
}
-} // end class XML_RPC_Client
-
+} // END XML_RPC_Client Class
/**
* XML-RPC Response class
*
* @category XML-RPC
- * @author ExpressionEngine Dev Team
- * @link http://codeigniter.com/user_guide/libraries/xmlrpc.html
+ * @author EllisLab Dev Team
+ * @link https://codeigniter.com/userguide3/libraries/xmlrpc.html
*/
class XML_RPC_Response
{
- var $val = 0;
- var $errno = 0;
- var $errstr = '';
- var $headers = array();
- var $xss_clean = TRUE;
+ /**
+ * Value
+ *
+ * @var mixed
+ */
+ public $val = 0;
+
+ /**
+ * Error number
+ *
+ * @var int
+ */
+ public $errno = 0;
+
+ /**
+ * Error message
+ *
+ * @var string
+ */
+ public $errstr = '';
+
+ /**
+ * Headers list
+ *
+ * @var array
+ */
+ public $headers = array();
+
+ /**
+ * XSS Filter flag
+ *
+ * @var bool
+ */
+ public $xss_clean = TRUE;
+
+ // --------------------------------------------------------------------
+
+ /**
+ * Constructor
+ *
+ * @param mixed $val
+ * @param int $code
+ * @param string $fstr
+ * @return void
+ */
public function __construct($val, $code = 0, $fstr = '')
{
- if ($code != 0)
+ if ($code !== 0)
{
// error
$this->errno = $code;
- $this->errstr = htmlentities($fstr);
+ $this->errstr = htmlspecialchars($fstr, ENT_XML1 | ENT_NOQUOTES, 'UTF-8');
}
- else if ( ! is_object($val))
+ elseif ( ! is_object($val))
{
// programmer error, not an object
- error_log("Invalid type '" . gettype($val) . "' (value: $val) passed to XML_RPC_Response. Defaulting to empty value.");
+ error_log("Invalid type '".gettype($val)."' (value: ".$val.') passed to XML_RPC_Response. Defaulting to empty value.');
$this->val = new XML_RPC_Values();
}
else
@@ -456,172 +851,234 @@ class XML_RPC_Response
}
}
- function faultCode()
+ // --------------------------------------------------------------------
+
+ /**
+ * Fault code
+ *
+ * @return int
+ */
+ public function faultCode()
{
return $this->errno;
}
- function faultString()
+ // --------------------------------------------------------------------
+
+ /**
+ * Fault string
+ *
+ * @return string
+ */
+ public function faultString()
{
return $this->errstr;
}
- function value()
+ // --------------------------------------------------------------------
+
+ /**
+ * Value
+ *
+ * @return mixed
+ */
+ public function value()
{
return $this->val;
}
- function prepare_response()
+ // --------------------------------------------------------------------
+
+ /**
+ * Prepare response
+ *
+ * @return string xml
+ */
+ public function prepare_response()
{
- $result = "<methodResponse>\n";
- if ($this->errno)
- {
- $result .= '<fault>
+ return "<methodResponse>\n"
+ .($this->errno
+ ? '<fault>
<value>
<struct>
<member>
<name>faultCode</name>
- <value><int>' . $this->errno . '</int></value>
+ <value><int>'.$this->errno.'</int></value>
</member>
<member>
<name>faultString</name>
- <value><string>' . $this->errstr . '</string></value>
+ <value><string>'.$this->errstr.'</string></value>
</member>
</struct>
</value>
-</fault>';
- }
- else
- {
- $result .= "<params>\n<param>\n" .
- $this->val->serialize_class() .
- "</param>\n</params>";
- }
- $result .= "\n</methodResponse>";
- return $result;
+</fault>'
+ : "<params>\n<param>\n".$this->val->serialize_class()."</param>\n</params>")
+ ."\n</methodResponse>";
}
- function decode($array=FALSE)
+ // --------------------------------------------------------------------
+
+ /**
+ * Decode
+ *
+ * @param mixed $array
+ * @return array
+ */
+ public function decode($array = NULL)
{
$CI =& get_instance();
-
- if ($array !== FALSE && is_array($array))
+
+ if (is_array($array))
{
- while (list($key) = each($array))
+ foreach ($array as $key => &$value)
{
- if (is_array($array[$key]))
+ if (is_array($value))
{
- $array[$key] = $this->decode($array[$key]);
+ $array[$key] = $this->decode($value);
}
- else
+ elseif ($this->xss_clean)
{
- $array[$key] = ($this->xss_clean) ? $CI->security->xss_clean($array[$key]) : $array[$key];
+ $array[$key] = $CI->security->xss_clean($value);
}
}
- $result = $array;
+ return $array;
}
- else
- {
- $result = $this->xmlrpc_decoder($this->val);
- if (is_array($result))
- {
- $result = $this->decode($result);
- }
- else
- {
- $result = ($this->xss_clean) ? $CI->security->xss_clean($result) : $result;
- }
+ $result = $this->xmlrpc_decoder($this->val);
+
+ if (is_array($result))
+ {
+ $result = $this->decode($result);
+ }
+ elseif ($this->xss_clean)
+ {
+ $result = $CI->security->xss_clean($result);
}
return $result;
}
+ // --------------------------------------------------------------------
-
- //-------------------------------------
- // XML-RPC Object to PHP Types
- //-------------------------------------
-
- function xmlrpc_decoder($xmlrpc_val)
+ /**
+ * XML-RPC Object to PHP Types
+ *
+ * @param object
+ * @return array
+ */
+ public function xmlrpc_decoder($xmlrpc_val)
{
$kind = $xmlrpc_val->kindOf();
- if ($kind == 'scalar')
+ if ($kind === 'scalar')
{
return $xmlrpc_val->scalarval();
}
- elseif ($kind == 'array')
+ elseif ($kind === 'array')
{
reset($xmlrpc_val->me);
- list($a,$b) = each($xmlrpc_val->me);
- $size = count($b);
-
+ $b = current($xmlrpc_val->me);
$arr = array();
- for ($i = 0; $i < $size; $i++)
+ for ($i = 0, $size = count($b); $i < $size; $i++)
{
$arr[] = $this->xmlrpc_decoder($xmlrpc_val->me['array'][$i]);
}
return $arr;
}
- elseif ($kind == 'struct')
+ elseif ($kind === 'struct')
{
reset($xmlrpc_val->me['struct']);
$arr = array();
- while (list($key,$value) = each($xmlrpc_val->me['struct']))
+ foreach ($xmlrpc_val->me['struct'] as $key => &$value)
{
$arr[$key] = $this->xmlrpc_decoder($value);
}
+
return $arr;
}
}
+ // --------------------------------------------------------------------
- //-------------------------------------
- // ISO-8601 time to server or UTC time
- //-------------------------------------
-
- function iso8601_decode($time, $utc=0)
+ /**
+ * ISO-8601 time to server or UTC time
+ *
+ * @param string
+ * @param bool
+ * @return int unix timestamp
+ */
+ public function iso8601_decode($time, $utc = FALSE)
{
- // return a timet in the localtime, or UTC
+ // Return a time in the localtime, or UTC
$t = 0;
if (preg_match('/([0-9]{4})([0-9]{2})([0-9]{2})T([0-9]{2}):([0-9]{2}):([0-9]{2})/', $time, $regs))
{
- $fnc = ($utc == 1) ? 'gmmktime' : 'mktime';
+ $fnc = ($utc === TRUE) ? 'gmmktime' : 'mktime';
$t = $fnc($regs[4], $regs[5], $regs[6], $regs[2], $regs[3], $regs[1]);
}
return $t;
}
-} // End Response Class
-
-
+} // END XML_RPC_Response Class
/**
* XML-RPC Message class
*
* @category XML-RPC
- * @author ExpressionEngine Dev Team
- * @link http://codeigniter.com/user_guide/libraries/xmlrpc.html
+ * @author EllisLab Dev Team
+ * @link https://codeigniter.com/userguide3/libraries/xmlrpc.html
*/
class XML_RPC_Message extends CI_Xmlrpc
{
- var $payload;
- var $method_name;
- var $params = array();
- var $xh = array();
- public function __construct($method, $pars=0)
+ /**
+ * Payload
+ *
+ * @var string
+ */
+ public $payload;
+
+ /**
+ * Method name
+ *
+ * @var string
+ */
+ public $method_name;
+
+ /**
+ * Parameter list
+ *
+ * @var array
+ */
+ public $params = array();
+
+ /**
+ * XH?
+ *
+ * @var array
+ */
+ public $xh = array();
+
+ // --------------------------------------------------------------------
+
+ /**
+ * Constructor
+ *
+ * @param string $method
+ * @param array $pars
+ * @return void
+ */
+ public function __construct($method, $pars = FALSE)
{
parent::__construct();
$this->method_name = $method;
if (is_array($pars) && count($pars) > 0)
{
- for ($i=0; $i<count($pars); $i++)
+ for ($i = 0, $c = count($pars); $i < $c; $i++)
{
// $pars[$i] = XML_RPC_Values
$this->params[] = $pars[$i];
@@ -629,17 +1086,20 @@ class XML_RPC_Message extends CI_Xmlrpc
}
}
- //-------------------------------------
- // Create Payload to Send
- //-------------------------------------
+ // --------------------------------------------------------------------
- function createPayload()
+ /**
+ * Create Payload to Send
+ *
+ * @return void
+ */
+ public function createPayload()
{
- $this->payload = "<?xml version=\"1.0\"?".">\r\n<methodCall>\r\n";
- $this->payload .= '<methodName>' . $this->method_name . "</methodName>\r\n";
- $this->payload .= "<params>\r\n";
+ $this->payload = '<?xml version="1.0"?'.">\r\n<methodCall>\r\n"
+ .'<methodName>'.$this->method_name."</methodName>\r\n"
+ ."<params>\r\n";
- for ($i=0; $i<count($this->params); $i++)
+ for ($i = 0, $c = count($this->params); $i < $c; $i++)
{
// $p = XML_RPC_Values
$p = $this->params[$i];
@@ -649,11 +1109,15 @@ class XML_RPC_Message extends CI_Xmlrpc
$this->payload .= "</params>\r\n</methodCall>\r\n";
}
- //-------------------------------------
- // Parse External XML-RPC Server's Response
- //-------------------------------------
+ // --------------------------------------------------------------------
- function parseResponse($fp)
+ /**
+ * Parse External XML-RPC Server's Response
+ *
+ * @param resource
+ * @return object
+ */
+ public function parseResponse($fp)
{
$data = '';
@@ -662,65 +1126,48 @@ class XML_RPC_Message extends CI_Xmlrpc
$data .= $datum;
}
- //-------------------------------------
- // DISPLAY HTTP CONTENT for DEBUGGING
- //-------------------------------------
-
+ // Display HTTP content for debugging
if ($this->debug === TRUE)
{
- echo "<pre>";
- echo "---DATA---\n" . htmlspecialchars($data) . "\n---END DATA---\n\n";
- echo "</pre>";
+ echo "<pre>---DATA---\n".htmlspecialchars($data)."\n---END DATA---\n\n</pre>";
}
- //-------------------------------------
- // Check for data
- //-------------------------------------
-
- if ($data == "")
+ // Check for data
+ if ($data === '')
{
error_log($this->xmlrpcstr['no_data']);
- $r = new XML_RPC_Response(0, $this->xmlrpcerr['no_data'], $this->xmlrpcstr['no_data']);
- return $r;
+ return new XML_RPC_Response(0, $this->xmlrpcerr['no_data'], $this->xmlrpcstr['no_data']);
}
-
- //-------------------------------------
- // Check for HTTP 200 Response
- //-------------------------------------
-
- if (strncmp($data, 'HTTP', 4) == 0 && ! preg_match('/^HTTP\/[0-9\.]+ 200 /', $data))
+ // Check for HTTP 200 Response
+ if (strpos($data, 'HTTP') === 0 && ! preg_match('/^HTTP\/[0-9\.]+ 200 /', $data))
{
- $errstr= substr($data, 0, strpos($data, "\n")-1);
- $r = new XML_RPC_Response(0, $this->xmlrpcerr['http_error'], $this->xmlrpcstr['http_error']. ' (' . $errstr . ')');
- return $r;
+ $errstr = substr($data, 0, strpos($data, "\n")-1);
+ return new XML_RPC_Response(0, $this->xmlrpcerr['http_error'], $this->xmlrpcstr['http_error'].' ('.$errstr.')');
}
//-------------------------------------
- // Create and Set Up XML Parser
+ // Create and Set Up XML Parser
//-------------------------------------
$parser = xml_parser_create($this->xmlrpc_defencoding);
-
- $this->xh[$parser] = array();
- $this->xh[$parser]['isf'] = 0;
- $this->xh[$parser]['ac'] = '';
- $this->xh[$parser]['headers'] = array();
- $this->xh[$parser]['stack'] = array();
- $this->xh[$parser]['valuestack'] = array();
- $this->xh[$parser]['isf_reason'] = 0;
+ $pname = (string) $parser;
+ $this->xh[$pname] = array(
+ 'isf' => 0,
+ 'ac' => '',
+ 'headers' => array(),
+ 'stack' => array(),
+ 'valuestack' => array(),
+ 'isf_reason' => 0
+ );
xml_set_object($parser, $this);
- xml_parser_set_option($parser, XML_OPTION_CASE_FOLDING, true);
+ xml_parser_set_option($parser, XML_OPTION_CASE_FOLDING, TRUE);
xml_set_element_handler($parser, 'open_tag', 'closing_tag');
xml_set_character_data_handler($parser, 'character_data');
//xml_set_default_handler($parser, 'default_handler');
-
- //-------------------------------------
- // GET HEADERS
- //-------------------------------------
-
+ // Get headers
$lines = explode("\r\n", $data);
while (($line = array_shift($lines)))
{
@@ -728,87 +1175,67 @@ class XML_RPC_Message extends CI_Xmlrpc
{
break;
}
- $this->xh[$parser]['headers'][] = $line;
+ $this->xh[$pname]['headers'][] = $line;
}
$data = implode("\r\n", $lines);
-
- //-------------------------------------
- // PARSE XML DATA
- //-------------------------------------
-
- if ( ! xml_parse($parser, $data, count($data)))
+ // Parse XML data
+ if ( ! xml_parse($parser, $data, TRUE))
{
$errstr = sprintf('XML error: %s at line %d',
- xml_error_string(xml_get_error_code($parser)),
- xml_get_current_line_number($parser));
- //error_log($errstr);
+ xml_error_string(xml_get_error_code($parser)),
+ xml_get_current_line_number($parser));
+
$r = new XML_RPC_Response(0, $this->xmlrpcerr['invalid_return'], $this->xmlrpcstr['invalid_return']);
xml_parser_free($parser);
return $r;
}
xml_parser_free($parser);
- // ---------------------------------------
- // Got Ourselves Some Badness, It Seems
- // ---------------------------------------
-
- if ($this->xh[$parser]['isf'] > 1)
+ // Got ourselves some badness, it seems
+ if ($this->xh[$pname]['isf'] > 1)
{
if ($this->debug === TRUE)
{
- echo "---Invalid Return---\n";
- echo $this->xh[$parser]['isf_reason'];
- echo "---Invalid Return---\n\n";
+ echo "---Invalid Return---\n".$this->xh[$pname]['isf_reason']."---Invalid Return---\n\n";
}
- $r = new XML_RPC_Response(0, $this->xmlrpcerr['invalid_return'],$this->xmlrpcstr['invalid_return'].' '.$this->xh[$parser]['isf_reason']);
- return $r;
+ return new XML_RPC_Response(0, $this->xmlrpcerr['invalid_return'], $this->xmlrpcstr['invalid_return'].' '.$this->xh[$pname]['isf_reason']);
}
- elseif ( ! is_object($this->xh[$parser]['value']))
+ elseif ( ! is_object($this->xh[$pname]['value']))
{
- $r = new XML_RPC_Response(0, $this->xmlrpcerr['invalid_return'],$this->xmlrpcstr['invalid_return'].' '.$this->xh[$parser]['isf_reason']);
- return $r;
+ return new XML_RPC_Response(0, $this->xmlrpcerr['invalid_return'], $this->xmlrpcstr['invalid_return'].' '.$this->xh[$pname]['isf_reason']);
}
- //-------------------------------------
- // DISPLAY XML CONTENT for DEBUGGING
- //-------------------------------------
-
+ // Display XML content for debugging
if ($this->debug === TRUE)
{
- echo "<pre>";
+ echo '<pre>';
- if (count($this->xh[$parser]['headers'] > 0))
+ if (count($this->xh[$pname]['headers']) > 0)
{
echo "---HEADERS---\n";
- foreach ($this->xh[$parser]['headers'] as $header)
+ foreach ($this->xh[$pname]['headers'] as $header)
{
- echo "$header\n";
+ echo $header."\n";
}
echo "---END HEADERS---\n\n";
}
- echo "---DATA---\n" . htmlspecialchars($data) . "\n---END DATA---\n\n";
-
- echo "---PARSED---\n" ;
- var_dump($this->xh[$parser]['value']);
+ echo "---DATA---\n".htmlspecialchars($data)."\n---END DATA---\n\n---PARSED---\n";
+ var_dump($this->xh[$pname]['value']);
echo "\n---END PARSED---</pre>";
}
- //-------------------------------------
- // SEND RESPONSE
- //-------------------------------------
-
- $v = $this->xh[$parser]['value'];
-
- if ($this->xh[$parser]['isf'])
+ // Send response
+ $v = $this->xh[$pname]['value'];
+ if ($this->xh[$pname]['isf'])
{
$errno_v = $v->me['struct']['faultCode'];
$errstr_v = $v->me['struct']['faultString'];
$errno = $errno_v->scalarval();
- if ($errno == 0)
+ if ($errno === 0)
{
// FAULT returned, errno needs to reflect that
$errno = -1;
@@ -821,10 +1248,12 @@ class XML_RPC_Message extends CI_Xmlrpc
$r = new XML_RPC_Response($v);
}
- $r->headers = $this->xh[$parser]['headers'];
+ $r->headers = $this->xh[$pname]['headers'];
return $r;
}
+ // --------------------------------------------------------------------
+
// ------------------------------------
// Begin Return Message Parsing section
// ------------------------------------
@@ -839,63 +1268,63 @@ class XML_RPC_Message extends CI_Xmlrpc
// stack - array with parent tree of the xml element,
// used to validate the nesting of elements
- //-------------------------------------
- // Start Element Handler
- //-------------------------------------
+ // --------------------------------------------------------------------
- function open_tag($the_parser, $name, $attrs)
+ /**
+ * Start Element Handler
+ *
+ * @param string
+ * @param string
+ * @return void
+ */
+ public function open_tag($the_parser, $name)
{
+ $the_parser = (string) $the_parser;
+
// If invalid nesting, then return
if ($this->xh[$the_parser]['isf'] > 1) return;
// Evaluate and check for correct nesting of XML elements
-
- if (count($this->xh[$the_parser]['stack']) == 0)
+ if (count($this->xh[$the_parser]['stack']) === 0)
{
- if ($name != 'METHODRESPONSE' && $name != 'METHODCALL')
+ if ($name !== 'METHODRESPONSE' && $name !== 'METHODCALL')
{
$this->xh[$the_parser]['isf'] = 2;
$this->xh[$the_parser]['isf_reason'] = 'Top level XML-RPC element is missing';
return;
}
}
- else
+ // not top level element: see if parent is OK
+ elseif ( ! in_array($this->xh[$the_parser]['stack'][0], $this->valid_parents[$name], TRUE))
{
- // not top level element: see if parent is OK
- if ( ! in_array($this->xh[$the_parser]['stack'][0], $this->valid_parents[$name], TRUE))
- {
- $this->xh[$the_parser]['isf'] = 2;
- $this->xh[$the_parser]['isf_reason'] = "XML-RPC element $name cannot be child of ".$this->xh[$the_parser]['stack'][0];
- return;
- }
+ $this->xh[$the_parser]['isf'] = 2;
+ $this->xh[$the_parser]['isf_reason'] = 'XML-RPC element '.$name.' cannot be child of '.$this->xh[$the_parser]['stack'][0];
+ return;
}
- switch($name)
+ switch ($name)
{
case 'STRUCT':
case 'ARRAY':
// Creates array for child elements
-
- $cur_val = array('value' => array(),
- 'type' => $name);
-
+ $cur_val = array('value' => array(), 'type' => $name);
array_unshift($this->xh[$the_parser]['valuestack'], $cur_val);
- break;
+ break;
case 'METHODNAME':
case 'NAME':
$this->xh[$the_parser]['ac'] = '';
- break;
+ break;
case 'FAULT':
$this->xh[$the_parser]['isf'] = 1;
- break;
+ break;
case 'PARAM':
$this->xh[$the_parser]['value'] = NULL;
- break;
+ break;
case 'VALUE':
$this->xh[$the_parser]['vt'] = 'value';
$this->xh[$the_parser]['ac'] = '';
$this->xh[$the_parser]['lv'] = 1;
- break;
+ break;
case 'I4':
case 'INT':
case 'STRING':
@@ -903,70 +1332,76 @@ class XML_RPC_Message extends CI_Xmlrpc
case 'DOUBLE':
case 'DATETIME.ISO8601':
case 'BASE64':
- if ($this->xh[$the_parser]['vt'] != 'value')
+ if ($this->xh[$the_parser]['vt'] !== 'value')
{
//two data elements inside a value: an error occurred!
$this->xh[$the_parser]['isf'] = 2;
- $this->xh[$the_parser]['isf_reason'] = "'Twas a $name element following a ".$this->xh[$the_parser]['vt']." element inside a single value";
+ $this->xh[$the_parser]['isf_reason'] = 'There is a '.$name.' element following a '
+ .$this->xh[$the_parser]['vt'].' element inside a single value';
return;
}
$this->xh[$the_parser]['ac'] = '';
- break;
+ break;
case 'MEMBER':
// Set name of <member> to nothing to prevent errors later if no <name> is found
$this->xh[$the_parser]['valuestack'][0]['name'] = '';
// Set NULL value to check to see if value passed for this param/member
$this->xh[$the_parser]['value'] = NULL;
- break;
+ break;
case 'DATA':
case 'METHODCALL':
case 'METHODRESPONSE':
case 'PARAMS':
// valid elements that add little to processing
- break;
+ break;
default:
/// An Invalid Element is Found, so we have trouble
$this->xh[$the_parser]['isf'] = 2;
- $this->xh[$the_parser]['isf_reason'] = "Invalid XML-RPC element found: $name";
- break;
+ $this->xh[$the_parser]['isf_reason'] = 'Invalid XML-RPC element found: '.$name;
+ break;
}
// Add current element name to stack, to allow validation of nesting
array_unshift($this->xh[$the_parser]['stack'], $name);
- if ($name != 'VALUE') $this->xh[$the_parser]['lv'] = 0;
+ $name === 'VALUE' OR $this->xh[$the_parser]['lv'] = 0;
}
- // END
+ // --------------------------------------------------------------------
- //-------------------------------------
- // End Element Handler
- //-------------------------------------
-
- function closing_tag($the_parser, $name)
+ /**
+ * End Element Handler
+ *
+ * @param string
+ * @param string
+ * @return void
+ */
+ public function closing_tag($the_parser, $name)
{
+ $the_parser = (string) $the_parser;
+
if ($this->xh[$the_parser]['isf'] > 1) return;
// Remove current element from stack and set variable
// NOTE: If the XML validates, then we do not have to worry about
- // the opening and closing of elements. Nesting is checked on the opening
+ // the opening and closing of elements. Nesting is checked on the opening
// tag so we be safe there as well.
$curr_elem = array_shift($this->xh[$the_parser]['stack']);
- switch($name)
+ switch ($name)
{
case 'STRUCT':
case 'ARRAY':
$cur_val = array_shift($this->xh[$the_parser]['valuestack']);
- $this->xh[$the_parser]['value'] = ( ! isset($cur_val['values'])) ? array() : $cur_val['values'];
+ $this->xh[$the_parser]['value'] = isset($cur_val['values']) ? $cur_val['values'] : array();
$this->xh[$the_parser]['vt'] = strtolower($name);
- break;
+ break;
case 'NAME':
$this->xh[$the_parser]['valuestack'][0]['name'] = $this->xh[$the_parser]['ac'];
- break;
+ break;
case 'BOOLEAN':
case 'I4':
case 'INT':
@@ -976,63 +1411,46 @@ class XML_RPC_Message extends CI_Xmlrpc
case 'BASE64':
$this->xh[$the_parser]['vt'] = strtolower($name);
- if ($name == 'STRING')
+ if ($name === 'STRING')
{
$this->xh[$the_parser]['value'] = $this->xh[$the_parser]['ac'];
}
- elseif ($name=='DATETIME.ISO8601')
+ elseif ($name === 'DATETIME.ISO8601')
{
$this->xh[$the_parser]['vt'] = $this->xmlrpcDateTime;
$this->xh[$the_parser]['value'] = $this->xh[$the_parser]['ac'];
}
- elseif ($name=='BASE64')
+ elseif ($name === 'BASE64')
{
$this->xh[$the_parser]['value'] = base64_decode($this->xh[$the_parser]['ac']);
}
- elseif ($name=='BOOLEAN')
+ elseif ($name === 'BOOLEAN')
{
// Translated BOOLEAN values to TRUE AND FALSE
- if ($this->xh[$the_parser]['ac'] == '1')
- {
- $this->xh[$the_parser]['value'] = TRUE;
- }
- else
- {
- $this->xh[$the_parser]['value'] = FALSE;
- }
+ $this->xh[$the_parser]['value'] = (bool) $this->xh[$the_parser]['ac'];
}
elseif ($name=='DOUBLE')
{
// we have a DOUBLE
// we must check that only 0123456789-.<space> are characters here
- if ( ! preg_match('/^[+-]?[eE0-9\t \.]+$/', $this->xh[$the_parser]['ac']))
- {
- $this->xh[$the_parser]['value'] = 'ERROR_NON_NUMERIC_FOUND';
- }
- else
- {
- $this->xh[$the_parser]['value'] = (double)$this->xh[$the_parser]['ac'];
- }
+ $this->xh[$the_parser]['value'] = preg_match('/^[+-]?[eE0-9\t \.]+$/', $this->xh[$the_parser]['ac'])
+ ? (float) $this->xh[$the_parser]['ac']
+ : 'ERROR_NON_NUMERIC_FOUND';
}
else
{
// we have an I4/INT
// we must check that only 0123456789-<space> are characters here
- if ( ! preg_match('/^[+-]?[0-9\t ]+$/', $this->xh[$the_parser]['ac']))
- {
- $this->xh[$the_parser]['value'] = 'ERROR_NON_NUMERIC_FOUND';
- }
- else
- {
- $this->xh[$the_parser]['value'] = (int)$this->xh[$the_parser]['ac'];
- }
+ $this->xh[$the_parser]['value'] = preg_match('/^[+-]?[0-9\t ]+$/', $this->xh[$the_parser]['ac'])
+ ? (int) $this->xh[$the_parser]['ac']
+ : 'ERROR_NON_NUMERIC_FOUND';
}
$this->xh[$the_parser]['ac'] = '';
$this->xh[$the_parser]['lv'] = 3; // indicate we've found a value
- break;
+ break;
case 'VALUE':
// This if() detects if no scalar was inside <VALUE></VALUE>
- if ($this->xh[$the_parser]['vt']=='value')
+ if ($this->xh[$the_parser]['vt'] == 'value')
{
$this->xh[$the_parser]['value'] = $this->xh[$the_parser]['ac'];
$this->xh[$the_parser]['vt'] = $this->xmlrpcString;
@@ -1041,7 +1459,7 @@ class XML_RPC_Message extends CI_Xmlrpc
// build the XML-RPC value out of the data received, and substitute it
$temp = new XML_RPC_Values($this->xh[$the_parser]['value'], $this->xh[$the_parser]['vt']);
- if (count($this->xh[$the_parser]['valuestack']) && $this->xh[$the_parser]['valuestack'][0]['type'] == 'ARRAY')
+ if (count($this->xh[$the_parser]['valuestack']) && $this->xh[$the_parser]['valuestack'][0]['type'] === 'ARRAY')
{
// Array
$this->xh[$the_parser]['valuestack'][0]['values'][] = $temp;
@@ -1051,57 +1469,64 @@ class XML_RPC_Message extends CI_Xmlrpc
// Struct
$this->xh[$the_parser]['value'] = $temp;
}
- break;
+ break;
case 'MEMBER':
- $this->xh[$the_parser]['ac']='';
+ $this->xh[$the_parser]['ac'] = '';
// If value add to array in the stack for the last element built
if ($this->xh[$the_parser]['value'])
{
$this->xh[$the_parser]['valuestack'][0]['values'][$this->xh[$the_parser]['valuestack'][0]['name']] = $this->xh[$the_parser]['value'];
}
- break;
+ break;
case 'DATA':
- $this->xh[$the_parser]['ac']='';
- break;
+ $this->xh[$the_parser]['ac'] = '';
+ break;
case 'PARAM':
if ($this->xh[$the_parser]['value'])
{
$this->xh[$the_parser]['params'][] = $this->xh[$the_parser]['value'];
}
- break;
+ break;
case 'METHODNAME':
$this->xh[$the_parser]['method'] = ltrim($this->xh[$the_parser]['ac']);
- break;
+ break;
case 'PARAMS':
case 'FAULT':
case 'METHODCALL':
case 'METHORESPONSE':
// We're all good kids with nuthin' to do
- break;
+ break;
default:
- // End of an Invalid Element. Taken care of during the opening tag though
- break;
+ // End of an Invalid Element. Taken care of during the opening tag though
+ break;
}
}
- //-------------------------------------
- // Parses Character Data
- //-------------------------------------
+ // --------------------------------------------------------------------
- function character_data($the_parser, $data)
+ /**
+ * Parse character data
+ *
+ * @param string
+ * @param string
+ * @return void
+ */
+ public function character_data($the_parser, $data)
{
+ $the_parser = (string) $the_parser;
+
if ($this->xh[$the_parser]['isf'] > 1) return; // XML Fault found already
// If a value has not been found
- if ($this->xh[$the_parser]['lv'] != 3)
+ if ($this->xh[$the_parser]['lv'] !== 3)
{
- if ($this->xh[$the_parser]['lv'] == 1)
+ if ($this->xh[$the_parser]['lv'] === 1)
{
$this->xh[$the_parser]['lv'] = 2; // Found a value
}
- if ( ! @isset($this->xh[$the_parser]['ac']))
+ if ( ! isset($this->xh[$the_parser]['ac']))
{
$this->xh[$the_parser]['ac'] = '';
}
@@ -1110,83 +1535,104 @@ class XML_RPC_Message extends CI_Xmlrpc
}
}
+ // --------------------------------------------------------------------
+
+ /**
+ * Add parameter
+ *
+ * @param mixed
+ * @return void
+ */
+ public function addParam($par)
+ {
+ $this->params[] = $par;
+ }
- function addParam($par) { $this->params[]=$par; }
+ // --------------------------------------------------------------------
- function output_parameters($array=FALSE)
+ /**
+ * Output parameters
+ *
+ * @param array $array
+ * @return array
+ */
+ public function output_parameters(array $array = array())
{
$CI =& get_instance();
-
- if ($array !== FALSE && is_array($array))
+
+ if ( ! empty($array))
{
- while (list($key) = each($array))
+ foreach ($array as $key => &$value)
{
- if (is_array($array[$key]))
+ if (is_array($value))
{
- $array[$key] = $this->output_parameters($array[$key]);
+ $array[$key] = $this->output_parameters($value);
}
- else
+ elseif ($key !== 'bits' && $this->xss_clean)
{
// 'bits' is for the MetaWeblog API image bits
// @todo - this needs to be made more general purpose
- $array[$key] = ($key == 'bits' OR $this->xss_clean == FALSE) ? $array[$key] : $CI->security->xss_clean($array[$key]);
+ $array[$key] = $CI->security->xss_clean($value);
}
}
- $parameters = $array;
+ return $array;
}
- else
+
+ $parameters = array();
+
+ for ($i = 0, $c = count($this->params); $i < $c; $i++)
{
- $parameters = array();
+ $a_param = $this->decode_message($this->params[$i]);
- for ($i = 0; $i < count($this->params); $i++)
+ if (is_array($a_param))
{
- $a_param = $this->decode_message($this->params[$i]);
-
- if (is_array($a_param))
- {
- $parameters[] = $this->output_parameters($a_param);
- }
- else
- {
- $parameters[] = ($this->xss_clean) ? $CI->security->xss_clean($a_param) : $a_param;
- }
+ $parameters[] = $this->output_parameters($a_param);
+ }
+ else
+ {
+ $parameters[] = ($this->xss_clean) ? $CI->security->xss_clean($a_param) : $a_param;
}
}
return $parameters;
}
+ // --------------------------------------------------------------------
- function decode_message($param)
+ /**
+ * Decode message
+ *
+ * @param object
+ * @return mixed
+ */
+ public function decode_message($param)
{
$kind = $param->kindOf();
- if ($kind == 'scalar')
+ if ($kind === 'scalar')
{
return $param->scalarval();
}
- elseif ($kind == 'array')
+ elseif ($kind === 'array')
{
reset($param->me);
- list($a,$b) = each($param->me);
-
+ $b = current($param->me);
$arr = array();
- for($i = 0; $i < count($b); $i++)
+ for ($i = 0, $c = count($b); $i < $c; $i++)
{
$arr[] = $this->decode_message($param->me['array'][$i]);
}
return $arr;
}
- elseif ($kind == 'struct')
+ elseif ($kind === 'struct')
{
reset($param->me['struct']);
-
$arr = array();
- while (list($key,$value) = each($param->me['struct']))
+ foreach ($param->me['struct'] as $key => &$value)
{
$arr[$key] = $this->decode_message($value);
}
@@ -1195,33 +1641,51 @@ class XML_RPC_Message extends CI_Xmlrpc
}
}
-} // End XML_RPC_Messages class
-
-
+} // END XML_RPC_Message Class
/**
* XML-RPC Values class
*
* @category XML-RPC
- * @author ExpressionEngine Dev Team
- * @link http://codeigniter.com/user_guide/libraries/xmlrpc.html
+ * @author EllisLab Dev Team
+ * @link https://codeigniter.com/userguide3/libraries/xmlrpc.html
*/
class XML_RPC_Values extends CI_Xmlrpc
{
- var $me = array();
- var $mytype = 0;
-
- public function __construct($val=-1, $type='')
+ /**
+ * Value data
+ *
+ * @var array
+ */
+ public $me = array();
+
+ /**
+ * Value type
+ *
+ * @var int
+ */
+ public $mytype = 0;
+
+ // --------------------------------------------------------------------
+
+ /**
+ * Constructor
+ *
+ * @param mixed $val
+ * @param string $type
+ * @return void
+ */
+ public function __construct($val = -1, $type = '')
{
parent::__construct();
- if ($val != -1 OR $type != '')
+ if ($val !== -1 OR $type !== '')
{
- $type = $type == '' ? 'string' : $type;
+ $type = $type === '' ? 'string' : $type;
if ($this->xmlrpcTypes[$type] == 1)
{
- $this->addScalar($val,$type);
+ $this->addScalar($val, $type);
}
elseif ($this->xmlrpcTypes[$type] == 2)
{
@@ -1234,11 +1698,20 @@ class XML_RPC_Values extends CI_Xmlrpc
}
}
- function addScalar($val, $type='string')
+ // --------------------------------------------------------------------
+
+ /**
+ * Add scalar value
+ *
+ * @param scalar
+ * @param string
+ * @return int
+ */
+ public function addScalar($val, $type = 'string')
{
$typeof = $this->xmlrpcTypes[$type];
- if ($this->mytype==1)
+ if ($this->mytype === 1)
{
echo '<strong>XML_RPC_Values</strong>: scalar can have only one value<br />';
return 0;
@@ -1246,23 +1719,16 @@ class XML_RPC_Values extends CI_Xmlrpc
if ($typeof != 1)
{
- echo '<strong>XML_RPC_Values</strong>: not a scalar type (${typeof})<br />';
+ echo "<strong>XML_RPC_Values</strong>: not a scalar type ($typeof)<br />";
return 0;
}
- if ($type == $this->xmlrpcBoolean)
+ if ($type === $this->xmlrpcBoolean)
{
- if (strcasecmp($val,'true')==0 OR $val==1 OR ($val==true && strcasecmp($val,'false')))
- {
- $val = 1;
- }
- else
- {
- $val=0;
- }
+ $val = (int) (strcasecmp($val, 'true') === 0 OR $val === 1 OR ($val === TRUE && strcasecmp($val, 'false')));
}
- if ($this->mytype == 2)
+ if ($this->mytype === 2)
{
// adding to an array here
$ar = $this->me['array'];
@@ -1275,14 +1741,23 @@ class XML_RPC_Values extends CI_Xmlrpc
$this->me[$type] = $val;
$this->mytype = $typeof;
}
+
return 1;
}
- function addArray($vals)
+ // --------------------------------------------------------------------
+
+ /**
+ * Add array value
+ *
+ * @param array
+ * @return int
+ */
+ public function addArray($vals)
{
- if ($this->mytype != 0)
+ if ($this->mytype !== 0)
{
- echo '<strong>XML_RPC_Values</strong>: already initialized as a [' . $this->kindOf() . ']<br />';
+ echo '<strong>XML_RPC_Values</strong>: already initialized as a ['.$this->kindOf().']<br />';
return 0;
}
@@ -1291,11 +1766,19 @@ class XML_RPC_Values extends CI_Xmlrpc
return 1;
}
- function addStruct($vals)
+ // --------------------------------------------------------------------
+
+ /**
+ * Add struct value
+ *
+ * @param object
+ * @return int
+ */
+ public function addStruct($vals)
{
- if ($this->mytype != 0)
+ if ($this->mytype !== 0)
{
- echo '<strong>XML_RPC_Values</strong>: already initialized as a [' . $this->kindOf() . ']<br />';
+ echo '<strong>XML_RPC_Values</strong>: already initialized as a ['.$this->kindOf().']<br />';
return 0;
}
$this->mytype = $this->xmlrpcTypes['struct'];
@@ -1303,121 +1786,134 @@ class XML_RPC_Values extends CI_Xmlrpc
return 1;
}
- function kindOf()
+ // --------------------------------------------------------------------
+
+ /**
+ * Get value type
+ *
+ * @return string
+ */
+ public function kindOf()
{
- switch($this->mytype)
+ switch ($this->mytype)
{
- case 3:
- return 'struct';
- break;
- case 2:
- return 'array';
- break;
- case 1:
- return 'scalar';
- break;
- default:
- return 'undef';
+ case 3: return 'struct';
+ case 2: return 'array';
+ case 1: return 'scalar';
+ default: return 'undef';
}
}
- function serializedata($typ, $val)
+ // --------------------------------------------------------------------
+
+ /**
+ * Serialize data
+ *
+ * @param string
+ * @param mixed
+ * @return string
+ */
+ public function serializedata($typ, $val)
{
$rs = '';
- switch($this->xmlrpcTypes[$typ])
+ switch ($this->xmlrpcTypes[$typ])
{
case 3:
// struct
$rs .= "<struct>\n";
reset($val);
- while (list($key2, $val2) = each($val))
+ foreach ($val as $key2 => &$val2)
{
- $rs .= "<member>\n<name>{$key2}</name>\n";
- $rs .= $this->serializeval($val2);
- $rs .= "</member>\n";
+ $rs .= "<member>\n<name>{$key2}</name>\n".$this->serializeval($val2)."</member>\n";
}
$rs .= '</struct>';
- break;
+ break;
case 2:
// array
$rs .= "<array>\n<data>\n";
- for($i=0; $i < count($val); $i++)
+ for ($i = 0, $c = count($val); $i < $c; $i++)
{
$rs .= $this->serializeval($val[$i]);
}
- $rs.="</data>\n</array>\n";
+ $rs .= "</data>\n</array>\n";
break;
case 1:
// others
switch ($typ)
{
case $this->xmlrpcBase64:
- $rs .= "<{$typ}>" . base64_encode((string)$val) . "</{$typ}>\n";
- break;
+ $rs .= '<'.$typ.'>'.base64_encode( (string) $val).'</'.$typ.">\n";
+ break;
case $this->xmlrpcBoolean:
- $rs .= "<{$typ}>" . ((bool)$val ? '1' : '0') . "</{$typ}>\n";
- break;
+ $rs .= '<'.$typ.'>'.( (bool) $val ? '1' : '0').'</'.$typ.">\n";
+ break;
case $this->xmlrpcString:
- $rs .= "<{$typ}>" . htmlspecialchars((string)$val). "</{$typ}>\n";
- break;
+ $rs .= '<'.$typ.'>'.htmlspecialchars( (string) $val).'</'.$typ.">\n";
+ break;
default:
- $rs .= "<{$typ}>{$val}</{$typ}>\n";
- break;
+ $rs .= '<'.$typ.'>'.$val.'</'.$typ.">\n";
+ break;
}
default:
- break;
+ break;
}
+
return $rs;
}
- function serialize_class()
+ // --------------------------------------------------------------------
+
+ /**
+ * Serialize class
+ *
+ * @return string
+ */
+ public function serialize_class()
{
return $this->serializeval($this);
}
- function serializeval($o)
- {
- $ar = $o->me;
- reset($ar);
-
- list($typ, $val) = each($ar);
- $rs = "<value>\n".$this->serializedata($typ, $val)."</value>\n";
- return $rs;
- }
+ // --------------------------------------------------------------------
- function scalarval()
+ /**
+ * Serialize value
+ *
+ * @param object
+ * @return string
+ */
+ public function serializeval($o)
{
- reset($this->me);
- list($a,$b) = each($this->me);
- return $b;
+ $array = $o->me;
+ list($value, $type) = array(reset($array), key($array));
+ return "<value>\n".$this->serializedata($type, $value)."</value>\n";
}
+ // --------------------------------------------------------------------
- //-------------------------------------
- // Encode time in ISO-8601 form.
- //-------------------------------------
-
- // Useful for sending time in XML-RPC
-
- function iso8601_encode($time, $utc=0)
+ /**
+ * Scalar value
+ *
+ * @return mixed
+ */
+ public function scalarval()
{
- if ($utc == 1)
- {
- $t = strftime("%Y%m%dT%H:%i:%s", $time);
- }
- else
- {
- if (function_exists('gmstrftime'))
- $t = gmstrftime("%Y%m%dT%H:%i:%s", $time);
- else
- $t = strftime("%Y%m%dT%H:%i:%s", $time - date('Z'));
- }
- return $t;
+ return reset($this->me);
}
-}
-// END XML_RPC_Values Class
+ // --------------------------------------------------------------------
+
+ /**
+ * Encode time in ISO-8601 form.
+ * Useful for sending time in XML-RPC
+ *
+ * @param int unix timestamp
+ * @param bool
+ * @return string
+ */
+ public function iso8601_encode($time, $utc = FALSE)
+ {
+ return ($utc) ? date('Ymd\TH:i:s', $time) : gmdate('Ymd\TH:i:s', $time);
+ }
-/* End of file Xmlrpc.php */
-/* Location: ./system/libraries/Xmlrpc.php */ \ No newline at end of file
+} // END XML_RPC_Values Class
diff --git a/system/libraries/Xmlrpcs.php b/system/libraries/Xmlrpcs.php
index d9d53c8a1..eb5a24c49 100644
--- a/system/libraries/Xmlrpcs.php
+++ b/system/libraries/Xmlrpcs.php
@@ -1,24 +1,49 @@
-<?php if ( ! defined('BASEPATH')) exit('No direct script access allowed');
+<?php
/**
* CodeIgniter
*
- * An open source application development framework for PHP 5.1.6 or newer
+ * An open source application development framework for PHP
*
- * @package CodeIgniter
- * @author ExpressionEngine Dev Team
- * @copyright Copyright (c) 2008 - 2014, EllisLab, Inc.
- * @license http://codeigniter.com/user_guide/license.html
- * @link http://codeigniter.com
- * @since Version 1.0
+ * This content is released under the MIT License (MIT)
+ *
+ * Copyright (c) 2019 - 2022, CodeIgniter Foundation
+ *
+ * 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.
+ *
+ * @package CodeIgniter
+ * @author EllisLab Dev Team
+ * @copyright Copyright (c) 2008 - 2014, EllisLab, Inc. (https://ellislab.com/)
+ * @copyright Copyright (c) 2014 - 2019, British Columbia Institute of Technology (https://bcit.ca/)
+ * @copyright Copyright (c) 2019 - 2022, CodeIgniter Foundation (https://codeigniter.com/)
+ * @license https://opensource.org/licenses/MIT MIT License
+ * @link https://codeigniter.com
+ * @since Version 1.0.0
* @filesource
*/
+defined('BASEPATH') OR exit('No direct script access allowed');
if ( ! function_exists('xml_parser_create'))
{
show_error('Your PHP installation does not support XML');
}
-if ( ! class_exists('CI_Xmlrpc'))
+if ( ! class_exists('CI_Xmlrpc', FALSE))
{
show_error('You must load the Xmlrpc class before loading the Xmlrpcs class in order to create a server.');
}
@@ -31,22 +56,46 @@ if ( ! class_exists('CI_Xmlrpc'))
* @package CodeIgniter
* @subpackage Libraries
* @category XML-RPC
- * @author ExpressionEngine Dev Team
- * @link http://codeigniter.com/user_guide/libraries/xmlrpc.html
+ * @author EllisLab Dev Team
+ * @link https://codeigniter.com/userguide3/libraries/xmlrpc.html
*/
-class CI_Xmlrpcs extends CI_Xmlrpc
-{
- var $methods = array(); //array of methods mapped to function names and signatures
- var $debug_msg = ''; // Debug Message
- var $system_methods = array(); // XML RPC Server methods
- var $controller_obj;
+class CI_Xmlrpcs extends CI_Xmlrpc {
+
+ /**
+ * Array of methods mapped to function names and signatures
+ *
+ * @var array
+ */
+ public $methods = array();
+
+ /**
+ * Debug Message
+ *
+ * @var string
+ */
+ public $debug_msg = '';
+
+ /**
+ * XML RPC Server methods
+ *
+ * @var array
+ */
+ public $system_methods = array();
- var $object = FALSE;
+ /**
+ * Configuration object
+ *
+ * @var object
+ */
+ public $object = FALSE;
/**
- * Constructor
+ * Initialize XMLRPC class
+ *
+ * @param array $config
+ * @return void
*/
- public function __construct($config=array())
+ public function __construct($config = array())
{
parent::__construct();
$this->set_system_methods();
@@ -56,7 +105,7 @@ class CI_Xmlrpcs extends CI_Xmlrpc
$this->methods = array_merge($this->methods, $config['functions']);
}
- log_message('debug', "XML-RPC Server Class Initialized");
+ log_message('info', 'XML-RPC Server Class Initialized');
}
// --------------------------------------------------------------------
@@ -64,11 +113,10 @@ class CI_Xmlrpcs extends CI_Xmlrpc
/**
* Initialize Prefs and Serve
*
- * @access public
* @param mixed
* @return void
*/
- function initialize($config=array())
+ public function initialize($config = array())
{
if (isset($config['functions']) && is_array($config['functions']))
{
@@ -96,29 +144,28 @@ class CI_Xmlrpcs extends CI_Xmlrpc
/**
* Setting of System Methods
*
- * @access public
* @return void
*/
- function set_system_methods()
+ public function set_system_methods()
{
$this->methods = array(
'system.listMethods' => array(
- 'function' => 'this.listMethods',
- 'signature' => array(array($this->xmlrpcArray, $this->xmlrpcString), array($this->xmlrpcArray)),
- 'docstring' => 'Returns an array of available methods on this server'),
- 'system.methodHelp' => array(
- 'function' => 'this.methodHelp',
- 'signature' => array(array($this->xmlrpcString, $this->xmlrpcString)),
- 'docstring' => 'Returns a documentation string for the specified method'),
+ 'function' => 'this.listMethods',
+ 'signature' => array(array($this->xmlrpcArray, $this->xmlrpcString), array($this->xmlrpcArray)),
+ 'docstring' => 'Returns an array of available methods on this server'),
+ 'system.methodHelp' => array(
+ 'function' => 'this.methodHelp',
+ 'signature' => array(array($this->xmlrpcString, $this->xmlrpcString)),
+ 'docstring' => 'Returns a documentation string for the specified method'),
'system.methodSignature' => array(
- 'function' => 'this.methodSignature',
- 'signature' => array(array($this->xmlrpcArray, $this->xmlrpcString)),
- 'docstring' => 'Returns an array describing the return type and required parameters of a method'),
- 'system.multicall' => array(
- 'function' => 'this.multicall',
- 'signature' => array(array($this->xmlrpcArray, $this->xmlrpcArray)),
- 'docstring' => 'Combine multiple RPC calls in one request. See http://www.xmlrpc.com/discuss/msgReader$1208 for details')
- );
+ 'function' => 'this.methodSignature',
+ 'signature' => array(array($this->xmlrpcArray, $this->xmlrpcString)),
+ 'docstring' => 'Returns an array describing the return type and required parameters of a method'),
+ 'system.multicall' => array(
+ 'function' => 'this.multicall',
+ 'signature' => array(array($this->xmlrpcArray, $this->xmlrpcArray)),
+ 'docstring' => 'Combine multiple RPC calls in one request. See http://www.xmlrpc.com/discuss/msgReader$1208 for details')
+ );
}
// --------------------------------------------------------------------
@@ -126,18 +173,15 @@ class CI_Xmlrpcs extends CI_Xmlrpc
/**
* Main Server Function
*
- * @access public
* @return void
*/
- function serve()
+ public function serve()
{
$r = $this->parseRequest();
- $payload = '<?xml version="1.0" encoding="'.$this->xmlrpc_defencoding.'"?'.'>'."\n";
- $payload .= $this->debug_msg;
- $payload .= $r->prepare_response();
+ $payload = '<?xml version="1.0" encoding="'.$this->xmlrpc_defencoding.'"?'.'>'."\n".$this->debug_msg.$r->prepare_response();
- header("Content-Type: text/xml");
- header("Content-Length: ".strlen($payload));
+ header('Content-Type: text/xml');
+ header('Content-Length: '.strlen($payload));
exit($payload);
}
@@ -146,19 +190,18 @@ class CI_Xmlrpcs extends CI_Xmlrpc
/**
* Add Method to Class
*
- * @access public
* @param string method name
* @param string function
* @param string signature
* @param string docstring
* @return void
*/
- function add_to_map($methodname, $function, $sig, $doc)
+ public function add_to_map($methodname, $function, $sig, $doc)
{
$this->methods[$methodname] = array(
- 'function' => $function,
- 'signature' => $sig,
- 'docstring' => $doc
+ 'function' => $function,
+ 'signature' => $sig,
+ 'docstring' => $doc
);
}
@@ -167,21 +210,22 @@ class CI_Xmlrpcs extends CI_Xmlrpc
/**
* Parse Server Request
*
- * @access public
* @param string data
* @return object xmlrpc response
*/
- function parseRequest($data='')
+ public function parseRequest($data = '')
{
- global $HTTP_RAW_POST_DATA;
-
//-------------------------------------
// Get Data
//-------------------------------------
- if ($data == '')
+ if ($data === '')
{
- $data = $HTTP_RAW_POST_DATA;
+ $CI =& get_instance();
+ if ($CI->input->method() === 'post')
+ {
+ $data = $CI->input->raw_input_stream;
+ }
}
//-------------------------------------
@@ -189,38 +233,39 @@ class CI_Xmlrpcs extends CI_Xmlrpc
//-------------------------------------
$parser = xml_parser_create($this->xmlrpc_defencoding);
- $parser_object = new XML_RPC_Message("filler");
-
- $parser_object->xh[$parser] = array();
- $parser_object->xh[$parser]['isf'] = 0;
- $parser_object->xh[$parser]['isf_reason'] = '';
- $parser_object->xh[$parser]['params'] = array();
- $parser_object->xh[$parser]['stack'] = array();
- $parser_object->xh[$parser]['valuestack'] = array();
- $parser_object->xh[$parser]['method'] = '';
+ $parser_object = new XML_RPC_Message('filler');
+ $pname = (string) $parser;
+
+ $parser_object->xh[$pname] = array(
+ 'isf' => 0,
+ 'isf_reason' => '',
+ 'params' => array(),
+ 'stack' => array(),
+ 'valuestack' => array(),
+ 'method' => ''
+ );
xml_set_object($parser, $parser_object);
- xml_parser_set_option($parser, XML_OPTION_CASE_FOLDING, true);
+ xml_parser_set_option($parser, XML_OPTION_CASE_FOLDING, TRUE);
xml_set_element_handler($parser, 'open_tag', 'closing_tag');
xml_set_character_data_handler($parser, 'character_data');
//xml_set_default_handler($parser, 'default_handler');
-
//-------------------------------------
- // PARSE + PROCESS XML DATA
+ // PARSE + PROCESS XML DATA
//-------------------------------------
if ( ! xml_parse($parser, $data, 1))
{
- // return XML error as a faultCode
+ // Return XML error as a faultCode
$r = new XML_RPC_Response(0,
- $this->xmlrpcerrxml + xml_get_error_code($parser),
- sprintf('XML error: %s at line %d',
+ $this->xmlrpcerrxml + xml_get_error_code($parser),
+ sprintf('XML error: %s at line %d',
xml_error_string(xml_get_error_code($parser)),
xml_get_current_line_number($parser)));
xml_parser_free($parser);
}
- elseif ($parser_object->xh[$parser]['isf'])
+ elseif ($parser_object->xh[$pname]['isf'])
{
return new XML_RPC_Response(0, $this->xmlrpcerr['invalid_return'], $this->xmlrpcstr['invalid_return']);
}
@@ -228,31 +273,29 @@ class CI_Xmlrpcs extends CI_Xmlrpc
{
xml_parser_free($parser);
- $m = new XML_RPC_Message($parser_object->xh[$parser]['method']);
- $plist='';
+ $m = new XML_RPC_Message($parser_object->xh[$pname]['method']);
+ $plist = '';
- for ($i=0; $i < count($parser_object->xh[$parser]['params']); $i++)
+ for ($i = 0, $c = count($parser_object->xh[$pname]['params']); $i < $c; $i++)
{
if ($this->debug === TRUE)
{
- $plist .= "$i - " . print_r(get_object_vars($parser_object->xh[$parser]['params'][$i]), TRUE). ";\n";
+ $plist .= $i.' - '.print_r(get_object_vars($parser_object->xh[$pname]['params'][$i]), TRUE).";\n";
}
- $m->addParam($parser_object->xh[$parser]['params'][$i]);
+ $m->addParam($parser_object->xh[$pname]['params'][$i]);
}
if ($this->debug === TRUE)
{
- echo "<pre>";
- echo "---PLIST---\n" . $plist . "\n---PLIST END---\n\n";
- echo "</pre>";
+ echo "<pre>---PLIST---\n".$plist."\n---PLIST END---\n\n</pre>";
}
$r = $this->_execute($m);
}
//-------------------------------------
- // SET DEBUGGING MESSAGE
+ // SET DEBUGGING MESSAGE
//-------------------------------------
if ($this->debug === TRUE)
@@ -268,24 +311,23 @@ class CI_Xmlrpcs extends CI_Xmlrpc
/**
* Executes the Method
*
- * @access protected
* @param object
* @return mixed
*/
- function _execute($m)
+ protected function _execute($m)
{
$methName = $m->method_name;
// Check to see if it is a system call
- $system_call = (strncmp($methName, 'system', 5) == 0) ? TRUE : FALSE;
+ $system_call = (strpos($methName, 'system') === 0);
- if ($this->xss_clean == FALSE)
+ if ($this->xss_clean === FALSE)
{
$m->xss_clean = FALSE;
}
//-------------------------------------
- // Valid Method
+ // Valid Method
//-------------------------------------
if ( ! isset($this->methods[$methName]['function']))
@@ -294,50 +336,45 @@ class CI_Xmlrpcs extends CI_Xmlrpc
}
//-------------------------------------
- // Check for Method (and Object)
+ // Check for Method (and Object)
//-------------------------------------
- $method_parts = explode(".", $this->methods[$methName]['function']);
- $objectCall = (isset($method_parts['1']) && $method_parts['1'] != "") ? TRUE : FALSE;
+ $method_parts = explode('.', $this->methods[$methName]['function']);
+ $objectCall = ! empty($method_parts[1]);
if ($system_call === TRUE)
{
- if ( ! is_callable(array($this,$method_parts['1'])))
+ if ( ! is_callable(array($this, $method_parts[1])))
{
return new XML_RPC_Response(0, $this->xmlrpcerr['unknown_method'], $this->xmlrpcstr['unknown_method']);
}
}
- else
+ elseif (($objectCall && ( ! method_exists($method_parts[0], $method_parts[1]) OR ! (new ReflectionMethod($method_parts[0], $method_parts[1]))->isPublic()))
+ OR ( ! $objectCall && ! is_callable($this->methods[$methName]['function']))
+ )
{
- if ($objectCall && ! is_callable(array($method_parts['0'],$method_parts['1'])))
- {
- return new XML_RPC_Response(0, $this->xmlrpcerr['unknown_method'], $this->xmlrpcstr['unknown_method']);
- }
- elseif ( ! $objectCall && ! is_callable($this->methods[$methName]['function']))
- {
- return new XML_RPC_Response(0, $this->xmlrpcerr['unknown_method'], $this->xmlrpcstr['unknown_method']);
- }
+ return new XML_RPC_Response(0, $this->xmlrpcerr['unknown_method'], $this->xmlrpcstr['unknown_method']);
}
//-------------------------------------
- // Checking Methods Signature
+ // Checking Methods Signature
//-------------------------------------
if (isset($this->methods[$methName]['signature']))
{
$sig = $this->methods[$methName]['signature'];
- for ($i=0; $i<count($sig); $i++)
+ for ($i = 0, $c = count($sig); $i < $c; $i++)
{
$current_sig = $sig[$i];
- if (count($current_sig) == count($m->params)+1)
+ if (count($current_sig) === count($m->params)+1)
{
- for ($n=0; $n < count($m->params); $n++)
+ for ($n = 0, $mc = count($m->params); $n < $mc; $n++)
{
$p = $m->params[$n];
- $pt = ($p->kindOf() == 'scalar') ? $p->scalarval() : $p->kindOf();
+ $pt = ($p->kindOf() === 'scalar') ? $p->scalarval() : $p->kindOf();
- if ($pt != $current_sig[$n+1])
+ if ($pt !== $current_sig[$n+1])
{
$pno = $n+1;
$wanted = $current_sig[$n+1];
@@ -345,7 +382,7 @@ class CI_Xmlrpcs extends CI_Xmlrpc
return new XML_RPC_Response(0,
$this->xmlrpcerr['incorrect_params'],
$this->xmlrpcstr['incorrect_params'] .
- ": Wanted {$wanted}, got {$pt} at param {$pno})");
+ ': Wanted '.$wanted.', got '.$pt.' at param '.$pno.')');
}
}
}
@@ -353,45 +390,35 @@ class CI_Xmlrpcs extends CI_Xmlrpc
}
//-------------------------------------
- // Calls the Function
+ // Calls the Function
//-------------------------------------
if ($objectCall === TRUE)
{
- if ($method_parts[0] == "this" && $system_call == TRUE)
+ if ($method_parts[0] === 'this' && $system_call === TRUE)
{
return call_user_func(array($this, $method_parts[1]), $m);
}
- else
+ elseif ($this->object === FALSE)
{
- if ($this->object === FALSE)
- {
- $CI =& get_instance();
- return $CI->$method_parts['1']($m);
- }
- else
- {
- return $this->object->$method_parts['1']($m);
- //return call_user_func(array(&$method_parts['0'],$method_parts['1']), $m);
- }
+ return get_instance()->{$method_parts[1]}($m);
}
+
+ return $this->object->{$method_parts[1]}($m);
}
- else
- {
- return call_user_func($this->methods[$methName]['function'], $m);
- }
+
+ return call_user_func($this->methods[$methName]['function'], $m);
}
-
+
// --------------------------------------------------------------------
/**
- * Server Function: List Methods
+ * Server Function: List Methods
*
- * @access public
* @param mixed
* @return object
*/
- function listMethods($m)
+ public function listMethods($m)
{
$v = new XML_RPC_Values();
$output = array();
@@ -403,23 +430,22 @@ class CI_Xmlrpcs extends CI_Xmlrpc
foreach ($this->system_methods as $key => $value)
{
- $output[]= new XML_RPC_Values($key, 'string');
+ $output[] = new XML_RPC_Values($key, 'string');
}
$v->addArray($output);
return new XML_RPC_Response($v);
}
-
+
// --------------------------------------------------------------------
/**
- * Server Function: Return Signature for Method
+ * Server Function: Return Signature for Method
*
- * @access public
* @param mixed
* @return object
*/
- function methodSignature($m)
+ public function methodSignature($m)
{
$parameters = $m->output_parameters();
$method_name = $parameters[0];
@@ -431,40 +457,35 @@ class CI_Xmlrpcs extends CI_Xmlrpc
$sigs = array();
$signature = $this->methods[$method_name]['signature'];
- for ($i=0; $i < count($signature); $i++)
+ for ($i = 0, $c = count($signature); $i < $c; $i++)
{
$cursig = array();
$inSig = $signature[$i];
- for ($j=0; $j<count($inSig); $j++)
+ for ($j = 0, $jc = count($inSig); $j < $jc; $j++)
{
$cursig[]= new XML_RPC_Values($inSig[$j], 'string');
}
- $sigs[]= new XML_RPC_Values($cursig, 'array');
+ $sigs[] = new XML_RPC_Values($cursig, 'array');
}
- $r = new XML_RPC_Response(new XML_RPC_Values($sigs, 'array'));
- }
- else
- {
- $r = new XML_RPC_Response(new XML_RPC_Values('undef', 'string'));
+
+ return new XML_RPC_Response(new XML_RPC_Values($sigs, 'array'));
}
+
+ return new XML_RPC_Response(new XML_RPC_Values('undef', 'string'));
}
- else
- {
- $r = new XML_RPC_Response(0,$this->xmlrpcerr['introspect_unknown'], $this->xmlrpcstr['introspect_unknown']);
- }
- return $r;
+
+ return new XML_RPC_Response(0, $this->xmlrpcerr['introspect_unknown'], $this->xmlrpcstr['introspect_unknown']);
}
// --------------------------------------------------------------------
/**
- * Server Function: Doc String for Method
+ * Server Function: Doc String for Method
*
- * @access public
* @param mixed
* @return object
*/
- function methodHelp($m)
+ public function methodHelp($m)
{
$parameters = $m->output_parameters();
$method_name = $parameters[0];
@@ -475,22 +496,19 @@ class CI_Xmlrpcs extends CI_Xmlrpc
return new XML_RPC_Response(new XML_RPC_Values($docstring, 'string'));
}
- else
- {
- return new XML_RPC_Response(0, $this->xmlrpcerr['introspect_unknown'], $this->xmlrpcstr['introspect_unknown']);
- }
+
+ return new XML_RPC_Response(0, $this->xmlrpcerr['introspect_unknown'], $this->xmlrpcstr['introspect_unknown']);
}
-
+
// --------------------------------------------------------------------
/**
- * Server Function: Multi-call
+ * Server Function: Multi-call
*
- * @access public
* @param mixed
* @return object
*/
- function multicall($m)
+ public function multicall($m)
{
// Disabled
return new XML_RPC_Response(0, $this->xmlrpcerr['unknown_method'], $this->xmlrpcstr['unknown_method']);
@@ -502,19 +520,17 @@ class CI_Xmlrpcs extends CI_Xmlrpc
foreach ($calls as $value)
{
- //$attempt = $this->_execute(new XML_RPC_Message($value[0], $value[1]));
-
$m = new XML_RPC_Message($value[0]);
- $plist='';
+ $plist = '';
- for ($i=0; $i < count($value[1]); $i++)
+ for ($i = 0, $c = count($value[1]); $i < $c; $i++)
{
$m->addParam(new XML_RPC_Values($value[1][$i], 'string'));
}
$attempt = $this->_execute($m);
- if ($attempt->faultCode() != 0)
+ if ($attempt->faultCode() !== 0)
{
return $attempt;
}
@@ -528,16 +544,15 @@ class CI_Xmlrpcs extends CI_Xmlrpc
// --------------------------------------------------------------------
/**
- * Multi-call Function: Error Handling
+ * Multi-call Function: Error Handling
*
- * @access public
* @param mixed
* @return object
*/
- function multicall_error($err)
+ public function multicall_error($err)
{
- $str = is_string($err) ? $this->xmlrpcstr["multicall_${err}"] : $err->faultString();
- $code = is_string($err) ? $this->xmlrpcerr["multicall_${err}"] : $err->faultCode();
+ $str = is_string($err) ? $this->xmlrpcstr["multicall_$err"] : $err->faultString();
+ $code = is_string($err) ? $this->xmlrpcerr["multicall_$err"] : $err->faultCode();
$struct['faultCode'] = new XML_RPC_Values($code, 'int');
$struct['faultString'] = new XML_RPC_Values($str, 'string');
@@ -548,15 +563,14 @@ class CI_Xmlrpcs extends CI_Xmlrpc
// --------------------------------------------------------------------
/**
- * Multi-call Function: Processes method
+ * Multi-call Function: Processes method
*
- * @access public
* @param mixed
* @return object
*/
- function do_multicall($call)
+ public function do_multicall($call)
{
- if ($call->kindOf() != 'struct')
+ if ($call->kindOf() !== 'struct')
{
return $this->multicall_error('notstruct');
}
@@ -565,14 +579,14 @@ class CI_Xmlrpcs extends CI_Xmlrpc
return $this->multicall_error('nomethod');
}
- list($scalar_type,$scalar_value)=each($methName->me);
- $scalar_type = $scalar_type == $this->xmlrpcI4 ? $this->xmlrpcInt : $scalar_type;
+ list($scalar_value, $scalar_type) = array(reset($methName->me), key($methName->me));
+ $scalar_type = $scalar_type === $this->xmlrpcI4 ? $this->xmlrpcInt : $scalar_type;
- if ($methName->kindOf() != 'scalar' OR $scalar_type != 'string')
+ if ($methName->kindOf() !== 'scalar' OR $scalar_type !== 'string')
{
return $this->multicall_error('notstring');
}
- elseif ($scalar_value == 'system.multicall')
+ elseif ($scalar_value === 'system.multicall')
{
return $this->multicall_error('recursion');
}
@@ -580,23 +594,22 @@ class CI_Xmlrpcs extends CI_Xmlrpc
{
return $this->multicall_error('noparams');
}
- elseif ($params->kindOf() != 'array')
+ elseif ($params->kindOf() !== 'array')
{
return $this->multicall_error('notarray');
}
- list($a,$b)=each($params->me);
- $numParams = count($b);
+ list($b, $a) = array(reset($params->me), key($params->me));
$msg = new XML_RPC_Message($scalar_value);
- for ($i = 0; $i < $numParams; $i++)
+ for ($i = 0, $numParams = count($b); $i < $numParams; $i++)
{
$msg->params[] = $params->me['array'][$i];
}
$result = $this->_execute($msg);
- if ($result->faultCode() != 0)
+ if ($result->faultCode() !== 0)
{
return $this->multicall_error($result);
}
@@ -605,8 +618,3 @@ class CI_Xmlrpcs extends CI_Xmlrpc
}
}
-// END XML_RPC_Server class
-
-
-/* End of file Xmlrpcs.php */
-/* Location: ./system/libraries/Xmlrpcs.php */ \ No newline at end of file
diff --git a/system/libraries/Zip.php b/system/libraries/Zip.php
index ffff3f340..6b9b1816b 100644
--- a/system/libraries/Zip.php
+++ b/system/libraries/Zip.php
@@ -1,25 +1,48 @@
-<?php if ( ! defined('BASEPATH')) exit('No direct script access allowed');
+<?php
/**
* CodeIgniter
*
- * An open source application development framework for PHP 5.1.6 or newer
+ * An open source application development framework for PHP
*
- * @package CodeIgniter
- * @author ExpressionEngine Dev Team
- * @copyright Copyright (c) 2008 - 2014, EllisLab, Inc.
- * @license http://codeigniter.com/user_guide/license.html
- * @link http://codeigniter.com
- * @since Version 1.0
+ * This content is released under the MIT License (MIT)
+ *
+ * Copyright (c) 2019 - 2022, CodeIgniter Foundation
+ *
+ * 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.
+ *
+ * @package CodeIgniter
+ * @author EllisLab Dev Team
+ * @copyright Copyright (c) 2008 - 2014, EllisLab, Inc. (https://ellislab.com/)
+ * @copyright Copyright (c) 2014 - 2019, British Columbia Institute of Technology (https://bcit.ca/)
+ * @copyright Copyright (c) 2019 - 2022, CodeIgniter Foundation (https://codeigniter.com/)
+ * @license https://opensource.org/licenses/MIT MIT License
+ * @link https://codeigniter.com
+ * @since Version 1.0.0
* @filesource
*/
-
-// ------------------------------------------------------------------------
+defined('BASEPATH') OR exit('No direct script access allowed');
/**
* Zip Compression Class
*
* This class is based on a library I found at Zend:
- * http://www.zend.com/codex.php?id=696&single=1
+ * https://www.zend.com/codex.php?id=696&single=1
*
* The original library is a little rough around the edges so I
* refactored it and added several additional methods -- Rick Ellis
@@ -27,26 +50,80 @@
* @package CodeIgniter
* @subpackage Libraries
* @category Encryption
- * @author ExpressionEngine Dev Team
- * @link http://codeigniter.com/user_guide/libraries/zip.html
+ * @author EllisLab Dev Team
+ * @link https://codeigniter.com/userguide3/libraries/zip.html
*/
-class CI_Zip {
+class CI_Zip {
+
+ /**
+ * Zip data in string form
+ *
+ * @var string
+ */
+ public $zipdata = '';
+
+ /**
+ * Zip data for a directory in string form
+ *
+ * @var string
+ */
+ public $directory = '';
+
+ /**
+ * Number of files/folder in zip file
+ *
+ * @var int
+ */
+ public $entries = 0;
- var $zipdata = '';
- var $directory = '';
- var $entries = 0;
- var $file_num = 0;
- var $offset = 0;
- var $now;
+ /**
+ * Number of files in zip
+ *
+ * @var int
+ */
+ public $file_num = 0;
/**
- * Constructor
+ * relative offset of local header
+ *
+ * @var int
+ */
+ public $offset = 0;
+
+ /**
+ * Reference to time at init
+ *
+ * @var int
+ */
+ public $now;
+
+ /**
+ * The level of compression
+ *
+ * Ranges from 0 to 9, with 9 being the highest level.
+ *
+ * @var int
+ */
+ public $compression_level = 2;
+
+ /**
+ * mbstring.func_overload flag
+ *
+ * @var bool
+ */
+ protected static $func_overload;
+
+ /**
+ * Initialize zip compression class
+ *
+ * @return void
*/
public function __construct()
{
- log_message('debug', "Zip Compression Class Initialized");
+ isset(self::$func_overload) OR self::$func_overload = ( ! is_php('8.0') && extension_loaded('mbstring') && @ini_get('mbstring.func_overload'));
$this->now = time();
+ log_message('info', 'Zip Compression Class Initialized');
}
// --------------------------------------------------------------------
@@ -56,21 +133,19 @@ class CI_Zip {
*
* Lets you add a virtual directory into which you can place files.
*
- * @access public
- * @param mixed the directory name. Can be string or array
+ * @param mixed $directory the directory name. Can be string or array
* @return void
*/
- function add_dir($directory)
+ public function add_dir($directory)
{
- foreach ((array)$directory as $dir)
+ foreach ((array) $directory as $dir)
{
- if ( ! preg_match("|.+/$|", $dir))
+ if ( ! preg_match('|.+/$|', $dir))
{
$dir .= '/';
}
$dir_time = $this->_get_mod_time($dir);
-
$this->_add_dir($dir, $dir_time['file_mtime'], $dir_time['file_mdate']);
}
}
@@ -78,22 +153,22 @@ class CI_Zip {
// --------------------------------------------------------------------
/**
- * Get file/directory modification time
+ * Get file/directory modification time
*
- * If this is a newly created file/dir, we will set the time to 'now'
+ * If this is a newly created file/dir, we will set the time to 'now'
*
- * @param string path to file
- * @return array filemtime/filemdate
+ * @param string $dir path to file
+ * @return array filemtime/filemdate
*/
- function _get_mod_time($dir)
+ protected function _get_mod_time($dir)
{
- // filemtime() will return false, but it does raise an error.
- $date = (@filemtime($dir)) ? filemtime($dir) : getdate($this->now);
+ // filemtime() may return false, but raises an error for non-existing files
+ $date = file_exists($dir) ? getdate(filemtime($dir)) : getdate($this->now);
- $time['file_mtime'] = ($date['hours'] << 11) + ($date['minutes'] << 5) + $date['seconds'] / 2;
- $time['file_mdate'] = (($date['year'] - 1980) << 9) + ($date['mon'] << 5) + $date['mday'];
-
- return $time;
+ return array(
+ 'file_mtime' => ($date['hours'] << 11) + ($date['minutes'] << 5) + $date['seconds'] / 2,
+ 'file_mdate' => (($date['year'] - 1980) << 9) + ($date['mon'] << 5) + $date['mday']
+ );
}
// --------------------------------------------------------------------
@@ -101,13 +176,14 @@ class CI_Zip {
/**
* Add Directory
*
- * @access private
- * @param string the directory name
+ * @param string $dir the directory name
+ * @param int $file_mtime
+ * @param int $file_mdate
* @return void
*/
- function _add_dir($dir, $file_mtime, $file_mdate)
+ protected function _add_dir($dir, $file_mtime, $file_mdate)
{
- $dir = str_replace("\\", "/", $dir);
+ $dir = str_replace('\\', '/', $dir);
$this->zipdata .=
"\x50\x4b\x03\x04\x0a\x00\x00\x00\x00\x00"
@@ -116,7 +192,7 @@ class CI_Zip {
.pack('V', 0) // crc32
.pack('V', 0) // compressed filesize
.pack('V', 0) // uncompressed filesize
- .pack('v', strlen($dir)) // length of pathname
+ .pack('v', self::strlen($dir)) // length of pathname
.pack('v', 0) // extra field length
.$dir
// below is "data descriptor" segment
@@ -131,7 +207,7 @@ class CI_Zip {
.pack('V',0) // crc32
.pack('V',0) // compressed filesize
.pack('V',0) // uncompressed filesize
- .pack('v', strlen($dir)) // length of pathname
+ .pack('v', self::strlen($dir)) // length of pathname
.pack('v', 0) // extra field length
.pack('v', 0) // file comment length
.pack('v', 0) // disk number start
@@ -140,7 +216,7 @@ class CI_Zip {
.pack('V', $this->offset) // relative offset of local header
.$dir;
- $this->offset = strlen($this->zipdata);
+ $this->offset = self::strlen($this->zipdata);
$this->entries++;
}
@@ -150,29 +226,26 @@ class CI_Zip {
* Add Data to Zip
*
* Lets you add files to the archive. If the path is included
- * in the filename it will be placed within a directory. Make
+ * in the filename it will be placed within a directory. Make
* sure you use add_dir() first to create the folder.
*
- * @access public
- * @param mixed
- * @param string
+ * @param mixed $filepath A single filepath or an array of file => data pairs
+ * @param string $data Single file contents
* @return void
*/
- function add_data($filepath, $data = NULL)
+ public function add_data($filepath, $data = NULL)
{
if (is_array($filepath))
{
foreach ($filepath as $path => $data)
{
$file_data = $this->_get_mod_time($path);
-
$this->_add_data($path, $data, $file_data['file_mtime'], $file_data['file_mdate']);
}
}
else
{
$file_data = $this->_get_mod_time($filepath);
-
$this->_add_data($filepath, $data, $file_data['file_mtime'], $file_data['file_mdate']);
}
}
@@ -182,21 +255,20 @@ class CI_Zip {
/**
* Add Data to Zip
*
- * @access private
- * @param string the file name/path
- * @param string the data to be encoded
+ * @param string $filepath the file name/path
+ * @param string $data the data to be encoded
+ * @param int $file_mtime
+ * @param int $file_mdate
* @return void
*/
- function _add_data($filepath, $data, $file_mtime, $file_mdate)
+ protected function _add_data($filepath, $data, $file_mtime, $file_mdate)
{
- $filepath = str_replace("\\", "/", $filepath);
+ $filepath = str_replace('\\', '/', $filepath);
- $uncompressed_size = strlen($data);
+ $uncompressed_size = self::strlen($data);
$crc32 = crc32($data);
-
- $gzdata = gzcompress($data);
- $gzdata = substr($gzdata, 2, -4);
- $compressed_size = strlen($gzdata);
+ $gzdata = self::substr(gzcompress($data, $this->compression_level), 2, -4);
+ $compressed_size = self::strlen($gzdata);
$this->zipdata .=
"\x50\x4b\x03\x04\x14\x00\x00\x00\x08\x00"
@@ -205,7 +277,7 @@ class CI_Zip {
.pack('V', $crc32)
.pack('V', $compressed_size)
.pack('V', $uncompressed_size)
- .pack('v', strlen($filepath)) // length of filename
+ .pack('v', self::strlen($filepath)) // length of filename
.pack('v', 0) // extra field length
.$filepath
.$gzdata; // "file data" segment
@@ -217,7 +289,7 @@ class CI_Zip {
.pack('V', $crc32)
.pack('V', $compressed_size)
.pack('V', $uncompressed_size)
- .pack('v', strlen($filepath)) // length of filename
+ .pack('v', self::strlen($filepath)) // length of filename
.pack('v', 0) // extra field length
.pack('v', 0) // file comment length
.pack('v', 0) // disk number start
@@ -226,7 +298,7 @@ class CI_Zip {
.pack('V', $this->offset) // relative offset of local header
.$filepath;
- $this->offset = strlen($this->zipdata);
+ $this->offset = self::strlen($this->zipdata);
$this->entries++;
$this->file_num++;
}
@@ -236,28 +308,32 @@ class CI_Zip {
/**
* Read the contents of a file and add it to the zip
*
- * @access public
+ * @param string $path
+ * @param bool $archive_filepath
* @return bool
*/
- function read_file($path, $preserve_filepath = FALSE)
+ public function read_file($path, $archive_filepath = FALSE)
{
- if ( ! file_exists($path))
- {
- return FALSE;
- }
-
- if (FALSE !== ($data = file_get_contents($path)))
+ if (file_exists($path) && FALSE !== ($data = file_get_contents($path)))
{
- $name = str_replace("\\", "/", $path);
-
- if ($preserve_filepath === FALSE)
+ if (is_string($archive_filepath))
+ {
+ $name = str_replace('\\', '/', $archive_filepath);
+ }
+ else
{
- $name = preg_replace("|.*/(.+)|", "\\1", $name);
+ $name = str_replace('\\', '/', $path);
+
+ if ($archive_filepath === FALSE)
+ {
+ $name = preg_replace('|.*/(.+)|', '\\1', $name);
+ }
}
$this->add_data($name, $data);
return TRUE;
}
+
return FALSE;
}
@@ -267,15 +343,17 @@ class CI_Zip {
* Read a directory and add it to the zip.
*
* This function recursively reads a folder and everything it contains (including
- * sub-folders) and creates a zip based on it. Whatever directory structure
+ * sub-folders) and creates a zip based on it. Whatever directory structure
* is in the original file path will be recreated in the zip file.
*
- * @access public
- * @param string path to source
+ * @param string $path path to source directory
+ * @param bool $preserve_filepath
+ * @param string $root_path
* @return bool
*/
- function read_dir($path, $preserve_filepath = TRUE, $root_path = NULL)
+ public function read_dir($path, $preserve_filepath = TRUE, $root_path = NULL)
{
+ $path = rtrim($path, '/\\').DIRECTORY_SEPARATOR;
if ( ! $fp = @opendir($path))
{
return FALSE;
@@ -284,36 +362,33 @@ class CI_Zip {
// Set the original directory root for child dir's to use as relative
if ($root_path === NULL)
{
- $root_path = dirname($path).'/';
+ $root_path = str_replace(array('\\', '/'), DIRECTORY_SEPARATOR, dirname($path)).DIRECTORY_SEPARATOR;
}
while (FALSE !== ($file = readdir($fp)))
{
- if (substr($file, 0, 1) == '.')
+ if ($file === '.' OR $file === '..')
{
continue;
}
- if (@is_dir($path.$file))
+ if (is_dir($path.$file))
{
- $this->read_dir($path.$file."/", $preserve_filepath, $root_path);
+ $this->read_dir($path.$file.DIRECTORY_SEPARATOR, $preserve_filepath, $root_path);
}
- else
+ elseif (FALSE !== ($data = file_get_contents($path.$file)))
{
- if (FALSE !== ($data = file_get_contents($path.$file)))
+ $name = str_replace(array('\\', '/'), DIRECTORY_SEPARATOR, $path);
+ if ($preserve_filepath === FALSE)
{
- $name = str_replace("\\", "/", $path);
-
- if ($preserve_filepath === FALSE)
- {
- $name = str_replace($root_path, '', $name);
- }
-
- $this->add_data($name.$file, $data);
+ $name = str_replace($root_path, '', $name);
}
+
+ $this->add_data($name.$file, $data);
}
}
+ closedir($fp);
return TRUE;
}
@@ -322,26 +397,24 @@ class CI_Zip {
/**
* Get the Zip file
*
- * @access public
- * @return binary string
+ * @return string (binary encoded)
*/
- function get_zip()
+ public function get_zip()
{
// Is there any data to return?
- if ($this->entries == 0)
+ if ($this->entries === 0)
{
return FALSE;
}
- $zip_data = $this->zipdata;
- $zip_data .= $this->directory."\x50\x4b\x05\x06\x00\x00\x00\x00";
- $zip_data .= pack('v', $this->entries); // total # of entries "on this disk"
- $zip_data .= pack('v', $this->entries); // total # of entries overall
- $zip_data .= pack('V', strlen($this->directory)); // size of central dir
- $zip_data .= pack('V', strlen($this->zipdata)); // offset to start of central dir
- $zip_data .= "\x00\x00"; // .zip file comment length
-
- return $zip_data;
+ // @see https://github.com/bcit-ci/CodeIgniter/issues/5864
+ $footer = $this->directory."\x50\x4b\x05\x06\x00\x00\x00\x00"
+ .pack('v', $this->entries) // total # of entries "on this disk"
+ .pack('v', $this->entries) // total # of entries overall
+ .pack('V', self::strlen($this->directory)) // size of central dir
+ .pack('V', self::strlen($this->zipdata)) // offset to start of central dir
+ ."\x00\x00"; // .zip file comment length
+ return $this->zipdata.$footer;
}
// --------------------------------------------------------------------
@@ -351,23 +424,30 @@ class CI_Zip {
*
* Lets you write a file
*
- * @access public
- * @param string the file name
+ * @param string $filepath the file name
* @return bool
*/
- function archive($filepath)
+ public function archive($filepath)
{
- if ( ! ($fp = @fopen($filepath, FOPEN_WRITE_CREATE_DESTRUCTIVE)))
+ if ( ! ($fp = @fopen($filepath, 'w+b')))
{
return FALSE;
}
flock($fp, LOCK_EX);
- fwrite($fp, $this->get_zip());
+
+ for ($result = $written = 0, $data = $this->get_zip(), $length = self::strlen($data); $written < $length; $written += $result)
+ {
+ if (($result = fwrite($fp, self::substr($data, $written))) === FALSE)
+ {
+ break;
+ }
+ }
+
flock($fp, LOCK_UN);
fclose($fp);
- return TRUE;
+ return is_int($result);
}
// --------------------------------------------------------------------
@@ -375,23 +455,18 @@ class CI_Zip {
/**
* Download
*
- * @access public
- * @param string the file name
- * @param string the data to be encoded
- * @return bool
+ * @param string $filename the file name
+ * @return void
*/
- function download($filename = 'backup.zip')
+ public function download($filename = 'backup.zip')
{
- if ( ! preg_match("|.+?\.zip$|", $filename))
+ if ( ! preg_match('|.+?\.zip$|', $filename))
{
$filename .= '.zip';
}
- $CI =& get_instance();
- $CI->load->helper('download');
-
+ get_instance()->load->helper('download');
$get_zip = $this->get_zip();
-
$zip_content =& $get_zip;
force_download($filename, $zip_content);
@@ -402,22 +477,55 @@ class CI_Zip {
/**
* Initialize Data
*
- * Lets you clear current zip data. Useful if you need to create
+ * Lets you clear current zip data. Useful if you need to create
* multiple zips with different data.
*
- * @access public
- * @return void
+ * @return CI_Zip
*/
- function clear_data()
+ public function clear_data()
{
- $this->zipdata = '';
- $this->directory = '';
- $this->entries = 0;
- $this->file_num = 0;
- $this->offset = 0;
+ $this->zipdata = '';
+ $this->directory = '';
+ $this->entries = 0;
+ $this->file_num = 0;
+ $this->offset = 0;
+ return $this;
}
-}
+ // --------------------------------------------------------------------
+
+ /**
+ * Byte-safe strlen()
+ *
+ * @param string $str
+ * @return int
+ */
+ protected static function strlen($str)
+ {
+ return (self::$func_overload)
+ ? mb_strlen($str, '8bit')
+ : strlen($str);
+ }
-/* End of file Zip.php */
-/* Location: ./system/libraries/Zip.php */ \ No newline at end of file
+ // --------------------------------------------------------------------
+
+ /**
+ * Byte-safe substr()
+ *
+ * @param string $str
+ * @param int $start
+ * @param int $length
+ * @return string
+ */
+ protected static function substr($str, $start, $length = NULL)
+ {
+ if (self::$func_overload)
+ {
+ return mb_substr($str, $start, $length, '8bit');
+ }
+
+ return isset($length)
+ ? substr($str, $start, $length)
+ : substr($str, $start);
+ }
+}
diff --git a/system/libraries/index.html b/system/libraries/index.html
index c942a79ce..bcb7cae34 100644
--- a/system/libraries/index.html
+++ b/system/libraries/index.html
@@ -1,4 +1,5 @@
-<html>
+<!DOCTYPE html>
+<html lang="en">
<head>
<title>403 Forbidden</title>
</head>
@@ -7,4 +8,4 @@
<p>Directory access is forbidden.</p>
</body>
-</html> \ No newline at end of file
+</html>
diff --git a/system/libraries/javascript/Jquery.php b/system/libraries/javascript/Jquery.php
deleted file mode 100644
index 48d8b3e57..000000000
--- a/system/libraries/javascript/Jquery.php
+++ /dev/null
@@ -1,1071 +0,0 @@
-<?php if ( ! defined('BASEPATH')) exit('No direct script access allowed');
-
-/**
- * CodeIgniter
- *
- * An open source application development framework for PHP 5.1.6 or newer
- *
- * @package CodeIgniter
- * @author ExpressionEngine Dev Team
- * @copyright Copyright (c) 2008 - 2011, EllisLab, Inc.
- * @license http://www.codeigniter.com/user_guide/license.html
- * @link http://www.codeigniter.com
- * @since Version 1.0
- * @filesource
- */
-
-/**
- * Jquery Class
- *
- * @package CodeIgniter
- * @subpackage Libraries
- * @author ExpressionEngine Dev Team
- * @category Loader
- * @link http://www.codeigniter.com/user_guide/libraries/javascript.html
- */
-
-class CI_Jquery extends CI_Javascript {
-
- var $_javascript_folder = 'js';
- var $jquery_code_for_load = array();
- var $jquery_code_for_compile = array();
- var $jquery_corner_active = FALSE;
- var $jquery_table_sorter_active = FALSE;
- var $jquery_table_sorter_pager_active = FALSE;
- var $jquery_ajax_img = '';
-
- public function __construct($params)
- {
- $this->CI =& get_instance();
- extract($params);
-
- if ($autoload === TRUE)
- {
- $this->script();
- }
-
- log_message('debug', "Jquery Class Initialized");
- }
-
- // --------------------------------------------------------------------
- // Event Code
- // --------------------------------------------------------------------
-
- /**
- * Blur
- *
- * Outputs a jQuery blur event
- *
- * @access private
- * @param string The element to attach the event to
- * @param string The code to execute
- * @return string
- */
- function _blur($element = 'this', $js = '')
- {
- return $this->_add_event($element, $js, 'blur');
- }
-
- // --------------------------------------------------------------------
-
- /**
- * Change
- *
- * Outputs a jQuery change event
- *
- * @access private
- * @param string The element to attach the event to
- * @param string The code to execute
- * @return string
- */
- function _change($element = 'this', $js = '')
- {
- return $this->_add_event($element, $js, 'change');
- }
-
- // --------------------------------------------------------------------
-
- /**
- * Click
- *
- * Outputs a jQuery click event
- *
- * @access private
- * @param string The element to attach the event to
- * @param string The code to execute
- * @param boolean whether or not to return false
- * @return string
- */
- function _click($element = 'this', $js = '', $ret_false = TRUE)
- {
- if ( ! is_array($js))
- {
- $js = array($js);
- }
-
- if ($ret_false)
- {
- $js[] = "return false;";
- }
-
- return $this->_add_event($element, $js, 'click');
- }
-
- // --------------------------------------------------------------------
-
- /**
- * Double Click
- *
- * Outputs a jQuery dblclick event
- *
- * @access private
- * @param string The element to attach the event to
- * @param string The code to execute
- * @return string
- */
- function _dblclick($element = 'this', $js = '')
- {
- return $this->_add_event($element, $js, 'dblclick');
- }
-
- // --------------------------------------------------------------------
-
- /**
- * Error
- *
- * Outputs a jQuery error event
- *
- * @access private
- * @param string The element to attach the event to
- * @param string The code to execute
- * @return string
- */
- function _error($element = 'this', $js = '')
- {
- return $this->_add_event($element, $js, 'error');
- }
-
- // --------------------------------------------------------------------
-
- /**
- * Focus
- *
- * Outputs a jQuery focus event
- *
- * @access private
- * @param string The element to attach the event to
- * @param string The code to execute
- * @return string
- */
- function _focus($element = 'this', $js = '')
- {
- return $this->_add_event($element, $js, 'focus');
- }
-
- // --------------------------------------------------------------------
-
- /**
- * Hover
- *
- * Outputs a jQuery hover event
- *
- * @access private
- * @param string - element
- * @param string - Javascript code for mouse over
- * @param string - Javascript code for mouse out
- * @return string
- */
- function _hover($element = 'this', $over, $out)
- {
- $event = "\n\t$(" . $this->_prep_element($element) . ").hover(\n\t\tfunction()\n\t\t{\n\t\t\t{$over}\n\t\t}, \n\t\tfunction()\n\t\t{\n\t\t\t{$out}\n\t\t});\n";
-
- $this->jquery_code_for_compile[] = $event;
-
- return $event;
- }
-
- // --------------------------------------------------------------------
-
- /**
- * Keydown
- *
- * Outputs a jQuery keydown event
- *
- * @access private
- * @param string The element to attach the event to
- * @param string The code to execute
- * @return string
- */
- function _keydown($element = 'this', $js = '')
- {
- return $this->_add_event($element, $js, 'keydown');
- }
-
- // --------------------------------------------------------------------
-
- /**
- * Keyup
- *
- * Outputs a jQuery keydown event
- *
- * @access private
- * @param string The element to attach the event to
- * @param string The code to execute
- * @return string
- */
- function _keyup($element = 'this', $js = '')
- {
- return $this->_add_event($element, $js, 'keyup');
- }
-
- // --------------------------------------------------------------------
-
- /**
- * Load
- *
- * Outputs a jQuery load event
- *
- * @access private
- * @param string The element to attach the event to
- * @param string The code to execute
- * @return string
- */
- function _load($element = 'this', $js = '')
- {
- return $this->_add_event($element, $js, 'load');
- }
-
- // --------------------------------------------------------------------
-
- /**
- * Mousedown
- *
- * Outputs a jQuery mousedown event
- *
- * @access private
- * @param string The element to attach the event to
- * @param string The code to execute
- * @return string
- */
- function _mousedown($element = 'this', $js = '')
- {
- return $this->_add_event($element, $js, 'mousedown');
- }
-
- // --------------------------------------------------------------------
-
- /**
- * Mouse Out
- *
- * Outputs a jQuery mouseout event
- *
- * @access private
- * @param string The element to attach the event to
- * @param string The code to execute
- * @return string
- */
- function _mouseout($element = 'this', $js = '')
- {
- return $this->_add_event($element, $js, 'mouseout');
- }
-
- // --------------------------------------------------------------------
-
- /**
- * Mouse Over
- *
- * Outputs a jQuery mouseover event
- *
- * @access private
- * @param string The element to attach the event to
- * @param string The code to execute
- * @return string
- */
- function _mouseover($element = 'this', $js = '')
- {
- return $this->_add_event($element, $js, 'mouseover');
- }
-
- // --------------------------------------------------------------------
-
- /**
- * Mouseup
- *
- * Outputs a jQuery mouseup event
- *
- * @access private
- * @param string The element to attach the event to
- * @param string The code to execute
- * @return string
- */
- function _mouseup($element = 'this', $js = '')
- {
- return $this->_add_event($element, $js, 'mouseup');
- }
-
- // --------------------------------------------------------------------
-
- /**
- * Output
- *
- * Outputs script directly
- *
- * @access private
- * @param string The element to attach the event to
- * @param string The code to execute
- * @return string
- */
- function _output($array_js = '')
- {
- if ( ! is_array($array_js))
- {
- $array_js = array($array_js);
- }
-
- foreach ($array_js as $js)
- {
- $this->jquery_code_for_compile[] = "\t$js\n";
- }
- }
-
- // --------------------------------------------------------------------
-
- /**
- * Resize
- *
- * Outputs a jQuery resize event
- *
- * @access private
- * @param string The element to attach the event to
- * @param string The code to execute
- * @return string
- */
- function _resize($element = 'this', $js = '')
- {
- return $this->_add_event($element, $js, 'resize');
- }
-
- // --------------------------------------------------------------------
-
- /**
- * Scroll
- *
- * Outputs a jQuery scroll event
- *
- * @access private
- * @param string The element to attach the event to
- * @param string The code to execute
- * @return string
- */
- function _scroll($element = 'this', $js = '')
- {
- return $this->_add_event($element, $js, 'scroll');
- }
-
- // --------------------------------------------------------------------
-
- /**
- * Unload
- *
- * Outputs a jQuery unload event
- *
- * @access private
- * @param string The element to attach the event to
- * @param string The code to execute
- * @return string
- */
- function _unload($element = 'this', $js = '')
- {
- return $this->_add_event($element, $js, 'unload');
- }
-
- // --------------------------------------------------------------------
- // Effects
- // --------------------------------------------------------------------
-
- /**
- * Add Class
- *
- * Outputs a jQuery addClass event
- *
- * @access private
- * @param string - element
- * @return string
- */
- function _addClass($element = 'this', $class='')
- {
- $element = $this->_prep_element($element);
- $str = "$({$element}).addClass(\"$class\");";
- return $str;
- }
-
- // --------------------------------------------------------------------
-
- /**
- * Animate
- *
- * Outputs a jQuery animate event
- *
- * @access private
- * @param string - element
- * @param string - One of 'slow', 'normal', 'fast', or time in milliseconds
- * @param string - Javascript callback function
- * @return string
- */
- function _animate($element = 'this', $params = array(), $speed = '', $extra = '')
- {
- $element = $this->_prep_element($element);
- $speed = $this->_validate_speed($speed);
-
- $animations = "\t\t\t";
-
- foreach ($params as $param=>$value)
- {
- $animations .= $param.': \''.$value.'\', ';
- }
-
- $animations = substr($animations, 0, -2); // remove the last ", "
-
- if ($speed != '')
- {
- $speed = ', '.$speed;
- }
-
- if ($extra != '')
- {
- $extra = ', '.$extra;
- }
-
- $str = "$({$element}).animate({\n$animations\n\t\t}".$speed.$extra.");";
-
- return $str;
- }
-
- // --------------------------------------------------------------------
-
- /**
- * Fade In
- *
- * Outputs a jQuery hide event
- *
- * @access private
- * @param string - element
- * @param string - One of 'slow', 'normal', 'fast', or time in milliseconds
- * @param string - Javascript callback function
- * @return string
- */
- function _fadeIn($element = 'this', $speed = '', $callback = '')
- {
- $element = $this->_prep_element($element);
- $speed = $this->_validate_speed($speed);
-
- if ($callback != '')
- {
- $callback = ", function(){\n{$callback}\n}";
- }
-
- $str = "$({$element}).fadeIn({$speed}{$callback});";
-
- return $str;
- }
-
- // --------------------------------------------------------------------
-
- /**
- * Fade Out
- *
- * Outputs a jQuery hide event
- *
- * @access private
- * @param string - element
- * @param string - One of 'slow', 'normal', 'fast', or time in milliseconds
- * @param string - Javascript callback function
- * @return string
- */
- function _fadeOut($element = 'this', $speed = '', $callback = '')
- {
- $element = $this->_prep_element($element);
- $speed = $this->_validate_speed($speed);
-
- if ($callback != '')
- {
- $callback = ", function(){\n{$callback}\n}";
- }
-
- $str = "$({$element}).fadeOut({$speed}{$callback});";
-
- return $str;
- }
-
- // --------------------------------------------------------------------
-
- /**
- * Hide
- *
- * Outputs a jQuery hide action
- *
- * @access private
- * @param string - element
- * @param string - One of 'slow', 'normal', 'fast', or time in milliseconds
- * @param string - Javascript callback function
- * @return string
- */
- function _hide($element = 'this', $speed = '', $callback = '')
- {
- $element = $this->_prep_element($element);
- $speed = $this->_validate_speed($speed);
-
- if ($callback != '')
- {
- $callback = ", function(){\n{$callback}\n}";
- }
-
- $str = "$({$element}).hide({$speed}{$callback});";
-
- return $str;
- }
-
- // --------------------------------------------------------------------
-
- /**
- * Remove Class
- *
- * Outputs a jQuery remove class event
- *
- * @access private
- * @param string - element
- * @return string
- */
- function _removeClass($element = 'this', $class='')
- {
- $element = $this->_prep_element($element);
- $str = "$({$element}).removeClass(\"$class\");";
- return $str;
- }
-
- // --------------------------------------------------------------------
-
- /**
- * Slide Up
- *
- * Outputs a jQuery slideUp event
- *
- * @access private
- * @param string - element
- * @param string - One of 'slow', 'normal', 'fast', or time in milliseconds
- * @param string - Javascript callback function
- * @return string
- */
- function _slideUp($element = 'this', $speed = '', $callback = '')
- {
- $element = $this->_prep_element($element);
- $speed = $this->_validate_speed($speed);
-
- if ($callback != '')
- {
- $callback = ", function(){\n{$callback}\n}";
- }
-
- $str = "$({$element}).slideUp({$speed}{$callback});";
-
- return $str;
- }
-
- // --------------------------------------------------------------------
-
- /**
- * Slide Down
- *
- * Outputs a jQuery slideDown event
- *
- * @access private
- * @param string - element
- * @param string - One of 'slow', 'normal', 'fast', or time in milliseconds
- * @param string - Javascript callback function
- * @return string
- */
- function _slideDown($element = 'this', $speed = '', $callback = '')
- {
- $element = $this->_prep_element($element);
- $speed = $this->_validate_speed($speed);
-
- if ($callback != '')
- {
- $callback = ", function(){\n{$callback}\n}";
- }
-
- $str = "$({$element}).slideDown({$speed}{$callback});";
-
- return $str;
- }
-
- // --------------------------------------------------------------------
-
- /**
- * Slide Toggle
- *
- * Outputs a jQuery slideToggle event
- *
- * @access public
- * @param string - element
- * @param string - One of 'slow', 'normal', 'fast', or time in milliseconds
- * @param string - Javascript callback function
- * @return string
- */
- function _slideToggle($element = 'this', $speed = '', $callback = '')
- {
- $element = $this->_prep_element($element);
- $speed = $this->_validate_speed($speed);
-
- if ($callback != '')
- {
- $callback = ", function(){\n{$callback}\n}";
- }
-
- $str = "$({$element}).slideToggle({$speed}{$callback});";
-
- return $str;
- }
-
- // --------------------------------------------------------------------
-
- /**
- * Toggle
- *
- * Outputs a jQuery toggle event
- *
- * @access private
- * @param string - element
- * @return string
- */
- function _toggle($element = 'this')
- {
- $element = $this->_prep_element($element);
- $str = "$({$element}).toggle();";
- return $str;
- }
-
- // --------------------------------------------------------------------
-
- /**
- * Toggle Class
- *
- * Outputs a jQuery toggle class event
- *
- * @access private
- * @param string - element
- * @return string
- */
- function _toggleClass($element = 'this', $class='')
- {
- $element = $this->_prep_element($element);
- $str = "$({$element}).toggleClass(\"$class\");";
- return $str;
- }
-
- // --------------------------------------------------------------------
-
- /**
- * Show
- *
- * Outputs a jQuery show event
- *
- * @access private
- * @param string - element
- * @param string - One of 'slow', 'normal', 'fast', or time in milliseconds
- * @param string - Javascript callback function
- * @return string
- */
- function _show($element = 'this', $speed = '', $callback = '')
- {
- $element = $this->_prep_element($element);
- $speed = $this->_validate_speed($speed);
-
- if ($callback != '')
- {
- $callback = ", function(){\n{$callback}\n}";
- }
-
- $str = "$({$element}).show({$speed}{$callback});";
-
- return $str;
- }
-
- // --------------------------------------------------------------------
-
- /**
- * Updater
- *
- * An Ajax call that populates the designated DOM node with
- * returned content
- *
- * @access private
- * @param string The element to attach the event to
- * @param string the controller to run the call against
- * @param string optional parameters
- * @return string
- */
-
- function _updater($container = 'this', $controller, $options = '')
- {
- $container = $this->_prep_element($container);
-
- $controller = (strpos('://', $controller) === FALSE) ? $controller : $this->CI->config->site_url($controller);
-
- // ajaxStart and ajaxStop are better choices here... but this is a stop gap
- if ($this->CI->config->item('javascript_ajax_img') == '')
- {
- $loading_notifier = "Loading...";
- }
- else
- {
- $loading_notifier = '<img src=\'' . $this->CI->config->slash_item('base_url') . $this->CI->config->item('javascript_ajax_img') . '\' alt=\'Loading\' />';
- }
-
- $updater = "$($container).empty();\n"; // anything that was in... get it out
- $updater .= "\t\t$($container).prepend(\"$loading_notifier\");\n"; // to replace with an image
-
- $request_options = '';
- if ($options != '')
- {
- $request_options .= ", {";
- $request_options .= (is_array($options)) ? "'".implode("', '", $options)."'" : "'".str_replace(":", "':'", $options)."'";
- $request_options .= "}";
- }
-
- $updater .= "\t\t$($container).load('$controller'$request_options);";
- return $updater;
- }
-
-
- // --------------------------------------------------------------------
- // Pre-written handy stuff
- // --------------------------------------------------------------------
-
- /**
- * Zebra tables
- *
- * @access private
- * @param string table name
- * @param string plugin location
- * @return string
- */
- function _zebraTables($class = '', $odd = 'odd', $hover = '')
- {
- $class = ($class != '') ? '.'.$class : '';
-
- $zebra = "\t\$(\"table{$class} tbody tr:nth-child(even)\").addClass(\"{$odd}\");";
-
- $this->jquery_code_for_compile[] = $zebra;
-
- if ($hover != '')
- {
- $hover = $this->hover("table{$class} tbody tr", "$(this).addClass('hover');", "$(this).removeClass('hover');");
- }
-
- return $zebra;
- }
-
-
-
- // --------------------------------------------------------------------
- // Plugins
- // --------------------------------------------------------------------
-
- /**
- * Corner Plugin
- *
- * http://www.malsup.com/jquery/corner/
- *
- * @access public
- * @param string target
- * @return string
- */
- function corner($element = '', $corner_style = '')
- {
- // may want to make this configurable down the road
- $corner_location = '/plugins/jquery.corner.js';
-
- if ($corner_style != '')
- {
- $corner_style = '"'.$corner_style.'"';
- }
-
- return "$(" . $this->_prep_element($element) . ").corner(".$corner_style.");";
- }
-
- // --------------------------------------------------------------------
-
- /**
- * modal window
- *
- * Load a thickbox modal window
- *
- * @access public
- * @return void
- */
- function modal($src, $relative = FALSE)
- {
- $this->jquery_code_for_load[] = $this->external($src, $relative);
- }
-
- // --------------------------------------------------------------------
-
- /**
- * Effect
- *
- * Load an Effect library
- *
- * @access public
- * @return void
- */
- function effect($src, $relative = FALSE)
- {
- $this->jquery_code_for_load[] = $this->external($src, $relative);
- }
-
- // --------------------------------------------------------------------
-
- /**
- * Plugin
- *
- * Load a plugin library
- *
- * @access public
- * @return void
- */
- function plugin($src, $relative = FALSE)
- {
- $this->jquery_code_for_load[] = $this->external($src, $relative);
- }
-
- // --------------------------------------------------------------------
-
- /**
- * UI
- *
- * Load a user interface library
- *
- * @access public
- * @return void
- */
- function ui($src, $relative = FALSE)
- {
- $this->jquery_code_for_load[] = $this->external($src, $relative);
- }
- // --------------------------------------------------------------------
-
- /**
- * Sortable
- *
- * Creates a jQuery sortable
- *
- * @access public
- * @return void
- */
- function sortable($element, $options = array())
- {
-
- if (count($options) > 0)
- {
- $sort_options = array();
- foreach ($options as $k=>$v)
- {
- $sort_options[] = "\n\t\t".$k.': '.$v."";
- }
- $sort_options = implode(",", $sort_options);
- }
- else
- {
- $sort_options = '';
- }
-
- return "$(" . $this->_prep_element($element) . ").sortable({".$sort_options."\n\t});";
- }
-
- // --------------------------------------------------------------------
-
- /**
- * Table Sorter Plugin
- *
- * @access public
- * @param string table name
- * @param string plugin location
- * @return string
- */
- function tablesorter($table = '', $options = '')
- {
- $this->jquery_code_for_compile[] = "\t$(" . $this->_prep_element($table) . ").tablesorter($options);\n";
- }
-
- // --------------------------------------------------------------------
- // Class functions
- // --------------------------------------------------------------------
-
- /**
- * Add Event
- *
- * Constructs the syntax for an event, and adds to into the array for compilation
- *
- * @access private
- * @param string The element to attach the event to
- * @param string The code to execute
- * @param string The event to pass
- * @return string
- */
- function _add_event($element, $js, $event)
- {
- if (is_array($js))
- {
- $js = implode("\n\t\t", $js);
-
- }
-
- $event = "\n\t$(" . $this->_prep_element($element) . ").{$event}(function(){\n\t\t{$js}\n\t});\n";
- $this->jquery_code_for_compile[] = $event;
- return $event;
- }
-
- // --------------------------------------------------------------------
-
- /**
- * Compile
- *
- * As events are specified, they are stored in an array
- * This funciton compiles them all for output on a page
- *
- * @access private
- * @return string
- */
- function _compile($view_var = 'script_foot', $script_tags = TRUE)
- {
- // External references
- $external_scripts = implode('', $this->jquery_code_for_load);
- $this->CI->load->vars(array('library_src' => $external_scripts));
-
- if (count($this->jquery_code_for_compile) == 0 )
- {
- // no inline references, let's just return
- return;
- }
-
- // Inline references
- $script = '$(document).ready(function() {' . "\n";
- $script .= implode('', $this->jquery_code_for_compile);
- $script .= '});';
-
- $output = ($script_tags === FALSE) ? $script : $this->inline($script);
-
- $this->CI->load->vars(array($view_var => $output));
-
- }
-
- // --------------------------------------------------------------------
-
- /**
- * Clear Compile
- *
- * Clears the array of script events collected for output
- *
- * @access public
- * @return void
- */
- function _clear_compile()
- {
- $this->jquery_code_for_compile = array();
- }
-
- // --------------------------------------------------------------------
-
- /**
- * Document Ready
- *
- * A wrapper for writing document.ready()
- *
- * @access private
- * @return string
- */
- function _document_ready($js)
- {
- if ( ! is_array($js))
- {
- $js = array ($js);
-
- }
-
- foreach ($js as $script)
- {
- $this->jquery_code_for_compile[] = $script;
- }
- }
-
- // --------------------------------------------------------------------
-
- /**
- * Script Tag
- *
- * Outputs the script tag that loads the jquery.js file into an HTML document
- *
- * @access public
- * @param string
- * @return string
- */
- function script($library_src = '', $relative = FALSE)
- {
- $library_src = $this->external($library_src, $relative);
- $this->jquery_code_for_load[] = $library_src;
- return $library_src;
- }
-
- // --------------------------------------------------------------------
-
- /**
- * Prep Element
- *
- * Puts HTML element in quotes for use in jQuery code
- * unless the supplied element is the Javascript 'this'
- * object, in which case no quotes are added
- *
- * @access public
- * @param string
- * @return string
- */
- function _prep_element($element)
- {
- if ($element != 'this')
- {
- $element = '"'.$element.'"';
- }
-
- return $element;
- }
-
- // --------------------------------------------------------------------
-
- /**
- * Validate Speed
- *
- * Ensures the speed parameter is valid for jQuery
- *
- * @access private
- * @param string
- * @return string
- */
- function _validate_speed($speed)
- {
- if (in_array($speed, array('slow', 'normal', 'fast')))
- {
- $speed = '"'.$speed.'"';
- }
- elseif (preg_match("/[^0-9]/", $speed))
- {
- $speed = '';
- }
-
- return $speed;
- }
-
-}
-
-/* End of file Jquery.php */
-/* Location: ./system/libraries/Jquery.php */ \ No newline at end of file