rubyception 0.0.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/MIT-LICENSE +20 -0
- data/README.md +30 -0
- data/Rakefile +42 -0
- data/app/assets/javascripts/rubyception/app.coffee +4 -0
- data/app/assets/javascripts/rubyception/application.coffee +17 -0
- data/app/assets/javascripts/rubyception/application.js +15 -0
- data/app/assets/javascripts/rubyception/collections/backtrace_lines.coffee +4 -0
- data/app/assets/javascripts/rubyception/collections/entries.coffee +3 -0
- data/app/assets/javascripts/rubyception/collections/lines.coffee +3 -0
- data/app/assets/javascripts/rubyception/init.coffee +10 -0
- data/app/assets/javascripts/rubyception/lib/backbone.js +1431 -0
- data/app/assets/javascripts/rubyception/lib/inflections.js +659 -0
- data/app/assets/javascripts/rubyception/lib/jenny.coffee +262 -0
- data/app/assets/javascripts/rubyception/lib/milk.coffee +261 -0
- data/app/assets/javascripts/rubyception/lib/mousetrap.js +7 -0
- data/app/assets/javascripts/rubyception/lib/underscore-min.js +32 -0
- data/app/assets/javascripts/rubyception/models/backtrace_line.coffee +2 -0
- data/app/assets/javascripts/rubyception/models/entry.coffee +2 -0
- data/app/assets/javascripts/rubyception/models/line.coffee +2 -0
- data/app/assets/javascripts/rubyception/routers/log.coffee +45 -0
- data/app/assets/javascripts/rubyception/shBrushSql.js +66 -0
- data/app/assets/javascripts/rubyception/shCore.js +17 -0
- data/app/assets/javascripts/rubyception/template.js +1 -0
- data/app/assets/javascripts/rubyception/views/backtrace_lines/backtrace_line.coffee +8 -0
- data/app/assets/javascripts/rubyception/views/backtrace_lines/index.coffee +16 -0
- data/app/assets/javascripts/rubyception/views/entries/entry.coffee +49 -0
- data/app/assets/javascripts/rubyception/views/entries/index.coffee +79 -0
- data/app/assets/javascripts/rubyception/views/lines/index.coffee +5 -0
- data/app/assets/javascripts/rubyception/views/lines/line.coffee +10 -0
- data/app/assets/javascripts/rubyception/views/logs/show.coffee +8 -0
- data/app/assets/stylesheets/rubyception/application.css +13 -0
- data/app/assets/stylesheets/rubyception/application.sass +12 -0
- data/app/assets/stylesheets/rubyception/backtrace_lines.sass +66 -0
- data/app/assets/stylesheets/rubyception/default.sass +35 -0
- data/app/assets/stylesheets/rubyception/entries.sass +57 -0
- data/app/assets/stylesheets/rubyception/lines.sass +41 -0
- data/app/assets/stylesheets/rubyception/shCore.css +226 -0
- data/app/assets/stylesheets/rubyception/shThemeDefault.css +117 -0
- data/app/controllers/rubyception/application_controller.rb +4 -0
- data/app/controllers/rubyception/templating_controller.rb +22 -0
- data/app/helpers/rubyception/application_helper.rb +9 -0
- data/app/models/rubyception/entry.rb +108 -0
- data/app/models/rubyception/line.rb +55 -0
- data/app/views/layouts/rubyception/application.haml +15 -0
- data/app/views/rubyception/application/index.haml +0 -0
- data/app/views/rubyception/backtrace_lines/_backtrace_line.haml +5 -0
- data/app/views/rubyception/backtrace_lines/_index.haml +7 -0
- data/app/views/rubyception/entries/_entry.haml +12 -0
- data/app/views/rubyception/entries/_index.haml +1 -0
- data/app/views/rubyception/lines/_index.haml +3 -0
- data/app/views/rubyception/lines/_line.haml +2 -0
- data/app/views/rubyception/lines/action_controller/_exist_fragment.haml +0 -0
- data/app/views/rubyception/lines/action_controller/_expire_fragment.haml +0 -0
- data/app/views/rubyception/lines/action_controller/_expire_page.haml +0 -0
- data/app/views/rubyception/lines/action_controller/_halted_callback.haml +0 -0
- data/app/views/rubyception/lines/action_controller/_process_action.haml +0 -0
- data/app/views/rubyception/lines/action_controller/_read_fragment.haml +0 -0
- data/app/views/rubyception/lines/action_controller/_redirect_to.haml +0 -0
- data/app/views/rubyception/lines/action_controller/_send_data.haml +0 -0
- data/app/views/rubyception/lines/action_controller/_send_file.haml +0 -0
- data/app/views/rubyception/lines/action_controller/_start_processing.haml +0 -0
- data/app/views/rubyception/lines/action_controller/_write_fragment.haml +0 -0
- data/app/views/rubyception/lines/action_controller/_write_page.haml +0 -0
- data/app/views/rubyception/lines/action_mailer/_deliver.haml +0 -0
- data/app/views/rubyception/lines/action_mailer/_receive.haml +0 -0
- data/app/views/rubyception/lines/action_view/_render_partial.haml +3 -0
- data/app/views/rubyception/lines/action_view/_render_template.haml +6 -0
- data/app/views/rubyception/lines/active_record/_identity.haml +3 -0
- data/app/views/rubyception/lines/active_record/_sql.haml +4 -0
- data/app/views/rubyception/lines/active_resource/_request.haml +0 -0
- data/app/views/rubyception/lines/active_support/_cache_delete.haml +0 -0
- data/app/views/rubyception/lines/active_support/_cache_exist.haml +0 -0
- data/app/views/rubyception/lines/active_support/_cache_fetch_hit.haml +0 -0
- data/app/views/rubyception/lines/active_support/_cache_generate.haml +0 -0
- data/app/views/rubyception/lines/active_support/_cache_read.haml +0 -0
- data/app/views/rubyception/lines/active_support/_cache_write.haml +0 -0
- data/config/initializers/sass.rb +4 -0
- data/config/initializers/websocket_server.rb +23 -0
- data/config/routes.rb +3 -0
- data/config/templating.yml +39 -0
- data/db/development.sqlite3 +0 -0
- data/lib/rubyception.rb +6 -0
- data/lib/rubyception/catcher.rb +12 -0
- data/lib/rubyception/engine.rb +5 -0
- data/lib/rubyception/subscriber.rb +30 -0
- data/lib/rubyception/templating.rb +48 -0
- data/lib/rubyception/version.rb +3 -0
- data/lib/rubyception/websocket_server.rb +30 -0
- data/lib/tasks/rubyception_tasks.rake +4 -0
- data/lib/tasks/templates.rake +7 -0
- data/test/dummy/README.rdoc +261 -0
- data/test/dummy/Rakefile +7 -0
- data/test/dummy/app/assets/javascripts/application.js +15 -0
- data/test/dummy/app/assets/stylesheets/application.css +13 -0
- data/test/dummy/app/controllers/application_controller.rb +3 -0
- data/test/dummy/app/controllers/tasks_controller.rb +4 -0
- data/test/dummy/app/helpers/application_helper.rb +2 -0
- data/test/dummy/app/views/layouts/application.html.erb +14 -0
- data/test/dummy/app/views/tasks/index.html.erb +1 -0
- data/test/dummy/config.ru +4 -0
- data/test/dummy/config/application.rb +59 -0
- data/test/dummy/config/boot.rb +10 -0
- data/test/dummy/config/database.yml +25 -0
- data/test/dummy/config/environment.rb +5 -0
- data/test/dummy/config/environments/development.rb +37 -0
- data/test/dummy/config/environments/production.rb +67 -0
- data/test/dummy/config/environments/test.rb +37 -0
- data/test/dummy/config/initializers/backtrace_silencers.rb +7 -0
- data/test/dummy/config/initializers/inflections.rb +15 -0
- data/test/dummy/config/initializers/mime_types.rb +5 -0
- data/test/dummy/config/initializers/secret_token.rb +7 -0
- data/test/dummy/config/initializers/session_store.rb +8 -0
- data/test/dummy/config/initializers/wrap_parameters.rb +14 -0
- data/test/dummy/config/locales/en.yml +5 -0
- data/test/dummy/config/routes.rb +5 -0
- data/test/dummy/db/development.sqlite3 +0 -0
- data/test/dummy/log/development.log +183 -0
- data/test/dummy/public/404.html +26 -0
- data/test/dummy/public/422.html +26 -0
- data/test/dummy/public/500.html +25 -0
- data/test/dummy/public/favicon.ico +0 -0
- data/test/dummy/script/rails +6 -0
- data/test/dummy/tmp/cache/assets/C2A/A50/sprockets%2F502789294f7d708459013948aa30c91c +0 -0
- data/test/dummy/tmp/cache/assets/C76/720/sprockets%2F610e52016b025a1ce3828a39a7e93981 +0 -0
- data/test/dummy/tmp/cache/assets/C7E/740/sprockets%2F8703235e74ff13492a454a492b24b7c5 +0 -0
- data/test/dummy/tmp/cache/assets/C86/920/sprockets%2Fc4933e5c2861e44ab07485567259fa32 +0 -0
- data/test/dummy/tmp/cache/assets/C9C/F10/sprockets%2F33864d14794ad865eed8829535780ad9 +0 -0
- data/test/dummy/tmp/cache/assets/CA1/8B0/sprockets%2F2fb362180bfba307350968110ec6f454 +0 -0
- data/test/dummy/tmp/cache/assets/CAD/770/sprockets%2Fc5914c960063cf7cdcd50130a7637485 +0 -0
- data/test/dummy/tmp/cache/assets/CAD/DB0/sprockets%2Fe276db0373d02afa09f069436349328d +0 -0
- data/test/dummy/tmp/cache/assets/CAE/140/sprockets%2F6350953af3f774482a51481f8cd0a3c3 +0 -0
- data/test/dummy/tmp/cache/assets/CB0/010/sprockets%2F1c103104469484b76897daa3d04c6ee9 +0 -0
- data/test/dummy/tmp/cache/assets/CB2/E10/sprockets%2F7b063ac8c2e913114d098d89e60228e5 +0 -0
- data/test/dummy/tmp/cache/assets/CB5/EA0/sprockets%2Fe98d20291409af69a7316a86c45da651 +0 -0
- data/test/dummy/tmp/cache/assets/CB9/B80/sprockets%2F07c32ba22a6489f062e48df8830667d8 +0 -0
- data/test/dummy/tmp/cache/assets/CBE/630/sprockets%2F476aa283807fdd8203e98d3137686eb5 +0 -0
- data/test/dummy/tmp/cache/assets/CBE/A10/sprockets%2F7f66283cb01647a7c4cf954b62919c29 +0 -0
- data/test/dummy/tmp/cache/assets/CC8/DC0/sprockets%2F302790cf97013f80dc311cf2ad0a6342 +0 -0
- data/test/dummy/tmp/cache/assets/CD1/8E0/sprockets%2Fe898f78743c913b3855e1786cc58ec61 +0 -0
- data/test/dummy/tmp/cache/assets/CD2/510/sprockets%2F32995b414c804ddbb00f5504841f5e1b +0 -0
- data/test/dummy/tmp/cache/assets/CD6/EE0/sprockets%2F8666c05da70453c6e7cda433411192af +0 -0
- data/test/dummy/tmp/cache/assets/CD8/370/sprockets%2F357970feca3ac29060c1e3861e2c0953 +0 -0
- data/test/dummy/tmp/cache/assets/CE4/F70/sprockets%2F538e295f0a0d9311c8259bf029ec680f +0 -0
- data/test/dummy/tmp/cache/assets/CE5/3E0/sprockets%2F7b916f5404e1ee4a943e921d6523a97b +0 -0
- data/test/dummy/tmp/cache/assets/CE9/D20/sprockets%2F9dd235d6e21764622b79bf65cf15349a +0 -0
- data/test/dummy/tmp/cache/assets/CF2/2F0/sprockets%2F314b4a0885d2efea526a19f539987e96 +0 -0
- data/test/dummy/tmp/cache/assets/CFB/750/sprockets%2F42c8014c71d022dd4c60ed1e963b076a +0 -0
- data/test/dummy/tmp/cache/assets/D03/BA0/sprockets%2F7c98f37658deed89f88b13d429262a75 +0 -0
- data/test/dummy/tmp/cache/assets/D07/3B0/sprockets%2F2ca3210b7cff57c3299aea8507803f32 +0 -0
- data/test/dummy/tmp/cache/assets/D08/7E0/sprockets%2F47ed6467d629e22254ca2b0d13c863bb +0 -0
- data/test/dummy/tmp/cache/assets/D0F/970/sprockets%2F0461a7c825c4bb945e2f5daf00974f64 +0 -0
- data/test/dummy/tmp/cache/assets/D10/E40/sprockets%2Fa39042b3a568331554c57725fedfcf3e +0 -0
- data/test/dummy/tmp/cache/assets/D13/740/sprockets%2F98a98bb8bc60c8b170c451195e44e42d +0 -0
- data/test/dummy/tmp/cache/assets/D14/BE0/sprockets%2Faa69f1166262379365d818afcf4fa3b3 +0 -0
- data/test/dummy/tmp/cache/assets/D15/C40/sprockets%2F8fe3d124a7582df0a47c44743f3e3d85 +0 -0
- data/test/dummy/tmp/cache/assets/D1B/210/sprockets%2F567244d0d8f2733c6d1826ee82c76dce +0 -0
- data/test/dummy/tmp/cache/assets/D2C/BD0/sprockets%2F17c8f4a03e52cd0d6b85605b0abd7413 +0 -0
- data/test/dummy/tmp/cache/assets/D2D/BE0/sprockets%2Fcdcde17900d6ad0c72c14390808a35b3 +0 -0
- data/test/dummy/tmp/cache/assets/D30/800/sprockets%2F6ad46c52755a4a6c0b81250fc72ae17c +0 -0
- data/test/dummy/tmp/cache/assets/D32/A10/sprockets%2F13fe41fee1fe35b49d145bcc06610705 +0 -0
- data/test/dummy/tmp/cache/assets/D34/E20/sprockets%2Fc81175b2bb38464d022abb2d9b18bf87 +0 -0
- data/test/dummy/tmp/cache/assets/D38/420/sprockets%2Fc893136e3c5e0cb051358b8c01fc6b9c +0 -0
- data/test/dummy/tmp/cache/assets/D3C/C50/sprockets%2F52be58b70628a1836b3cea8bd096ee22 +0 -0
- data/test/dummy/tmp/cache/assets/D3E/980/sprockets%2F5e82819b1fe416d230b10d76abd948df +0 -0
- data/test/dummy/tmp/cache/assets/D3F/A80/sprockets%2F5e2ee04b0c2b6417f9ab728692a738cd +0 -0
- data/test/dummy/tmp/cache/assets/D41/340/sprockets%2Ffdc1e8bf346373a650e6d082f522a59e +0 -0
- data/test/dummy/tmp/cache/assets/D41/920/sprockets%2F64c94c28c60bbb70d9a3b9e972ae3057 +0 -0
- data/test/dummy/tmp/cache/assets/D47/BB0/sprockets%2F40929a27da40e05aa0b4aa0173f1ebb1 +0 -0
- data/test/dummy/tmp/cache/assets/D49/740/sprockets%2Fbcb0e6511f88b9af6d70380c86a57b79 +0 -0
- data/test/dummy/tmp/cache/assets/D4E/1B0/sprockets%2Ff7cbd26ba1d28d48de824f0e94586655 +0 -0
- data/test/dummy/tmp/cache/assets/D50/940/sprockets%2Ff601e768c1f96abd3d019e7f7564e3c9 +0 -0
- data/test/dummy/tmp/cache/assets/D53/460/sprockets%2F64e051e79ebdaa56bd989d67831f941d +0 -0
- data/test/dummy/tmp/cache/assets/D58/150/sprockets%2F70d598d088bce2f8078c80866be9cff0 +0 -0
- data/test/dummy/tmp/cache/assets/D5A/EA0/sprockets%2Fd771ace226fc8215a3572e0aa35bb0d6 +0 -0
- data/test/dummy/tmp/cache/assets/D66/E40/sprockets%2F0cd4ea4f30e313f641b96be80a13cf98 +0 -0
- data/test/dummy/tmp/cache/assets/D69/5A0/sprockets%2F8a630d58f110eeeffad123986912bae1 +0 -0
- data/test/dummy/tmp/cache/assets/D69/E10/sprockets%2F3d4d98cb1ad46a0b7635b75993c2ac0d +0 -0
- data/test/dummy/tmp/cache/assets/D6A/920/sprockets%2F9f1c0c3ad8aae418e1004d5cf54f5479 +0 -0
- data/test/dummy/tmp/cache/assets/D6B/A70/sprockets%2Fff1bd1557a03e82f5c2c45d58b661a4f +0 -0
- data/test/dummy/tmp/cache/assets/D6F/6B0/sprockets%2F1e2a322de4879106937ede70ffa2da6e +0 -0
- data/test/dummy/tmp/cache/assets/D70/780/sprockets%2F6c6a33bccf82b144dbd7982f0854f8b0 +0 -0
- data/test/dummy/tmp/cache/assets/D73/C90/sprockets%2F9752db727fd681d97becb91a17ad302d +0 -0
- data/test/dummy/tmp/cache/assets/D74/A50/sprockets%2Fe50162da67fdab880a08590f618dfc8e +0 -0
- data/test/dummy/tmp/cache/assets/D75/270/sprockets%2Fef0b31d47f8f9acc69f0234019b737ee +0 -0
- data/test/dummy/tmp/cache/assets/D77/320/sprockets%2F0c96ad8de5e6a049f5df53415baa4697 +0 -0
- data/test/dummy/tmp/cache/assets/D7A/CF0/sprockets%2Fb7e1aa09c7c42e2b98895e16dbc839b6 +0 -0
- data/test/dummy/tmp/cache/assets/D7B/110/sprockets%2F55184d1c2bc87fc49369d69e10be7adf +0 -0
- data/test/dummy/tmp/cache/assets/D81/A40/sprockets%2Ff886784e31a152dc32fdf5cf782d6bd9 +0 -0
- data/test/dummy/tmp/cache/assets/D91/AE0/sprockets%2Fab00df33bbe0ef2939b4d43e042e4d77 +0 -0
- data/test/dummy/tmp/cache/assets/D9E/1A0/sprockets%2F69bf83cb71b257071ba6db37bdea78f1 +0 -0
- data/test/dummy/tmp/cache/assets/D9E/840/sprockets%2Fc8da1010ea3b0a24bd101a2dd319dc4c +0 -0
- data/test/dummy/tmp/cache/assets/DA0/880/sprockets%2F89c46cad03e38a94663bfaac1b8d95d0 +0 -0
- data/test/dummy/tmp/cache/assets/DA1/950/sprockets%2F1ea6768fec37a8ee2ad758d4152ae1d2 +0 -0
- data/test/dummy/tmp/cache/assets/DA1/D40/sprockets%2Ffd605d648ad0a46b3e3ef9cf089a51b6 +0 -0
- data/test/dummy/tmp/cache/assets/DA1/FB0/sprockets%2Fe8cba462fd2940b4be7bf2d294fd7209 +0 -0
- data/test/dummy/tmp/cache/assets/DA9/140/sprockets%2Fa9dc7b7a71d4a92925c654f09d17fefa +0 -0
- data/test/dummy/tmp/cache/assets/DAA/960/sprockets%2F486294de7d016fa0ec65f0f9efa2e19f +0 -0
- data/test/dummy/tmp/cache/assets/DAB/9E0/sprockets%2Fa425c97bf53c87ea907267ce66ad5deb +0 -0
- data/test/dummy/tmp/cache/assets/DB3/1D0/sprockets%2F095c6e852a938bbfb9fc98a9ea105ee8 +0 -0
- data/test/dummy/tmp/cache/assets/DC3/C60/sprockets%2F0c49e61f99da10cf7acc0cac7736b23a +0 -0
- data/test/dummy/tmp/cache/assets/DC7/E70/sprockets%2Fdae539d9ce457212fabb2abf298e112d +0 -0
- data/test/dummy/tmp/cache/assets/DC8/C90/sprockets%2F880a4acdf2b3dec03f783e63ed10e6e3 +0 -0
- data/test/dummy/tmp/cache/assets/DD0/DE0/sprockets%2Fdd9bcfaa02c63a17f16d84fb523fd886 +0 -0
- data/test/dummy/tmp/cache/assets/DD0/E10/sprockets%2F7ff09d9b55239e81c67ac0bd1cd60cfb +0 -0
- data/test/dummy/tmp/cache/assets/DD3/140/sprockets%2Fdcdc4960e9f9381cbaec40b2f26c8f53 +0 -0
- data/test/dummy/tmp/cache/assets/DD4/340/sprockets%2Ff2cff1054cf9ff3d947f1372db9b6aa2 +0 -0
- data/test/dummy/tmp/cache/assets/DD7/F90/sprockets%2F3cbb2ecff5ff45927078c4490a93bfeb +0 -0
- data/test/dummy/tmp/cache/assets/DDC/400/sprockets%2Fcffd775d018f68ce5dba1ee0d951a994 +0 -0
- data/test/dummy/tmp/cache/assets/DE2/3D0/sprockets%2F503d37fdad31b11af5bd20b78ee3eaa0 +0 -0
- data/test/dummy/tmp/cache/assets/DF5/440/sprockets%2Fadaab579f5cf709a6c31b2fe40404ede +0 -0
- data/test/dummy/tmp/cache/assets/DFC/4A0/sprockets%2Fb2e7beb7a4bdf292c1b627bf0f84d9f0 +0 -0
- data/test/dummy/tmp/cache/assets/E02/C10/sprockets%2Fdd42eb4f61a99477f901cc31ddda7cfd +0 -0
- data/test/dummy/tmp/cache/assets/E04/610/sprockets%2Fd7d96ebe1affa231845c03c78ff6eaa7 +0 -0
- data/test/dummy/tmp/cache/assets/E04/890/sprockets%2F2f5173deea6c795b8fdde723bb4b63af +0 -0
- data/test/dummy/tmp/cache/assets/E14/060/sprockets%2F7d7d19b9786dbc0f535478bcdcdc8fce +0 -0
- data/test/dummy/tmp/cache/assets/E27/F00/sprockets%2F8abb689fab00eaa7301adf5d198f4dce +0 -0
- data/test/dummy/tmp/cache/assets/E2B/870/sprockets%2F22736cce6575f0dd0fcedb11ece9cc8a +0 -0
- data/test/dummy/tmp/cache/assets/E31/FB0/sprockets%2Fa8bb4c8b0dc7cc0affd698f7e2a27d72 +0 -0
- data/test/dummy/tmp/cache/assets/E3B/E00/sprockets%2F4e3fffccff7a681b16a92b6caf8ef761 +0 -0
- data/test/dummy/tmp/cache/assets/E57/9C0/sprockets%2Fb3ca68a8872ea6d2cc2bcbfe01a7fde6 +0 -0
- data/test/dummy/tmp/cache/assets/E69/200/sprockets%2Ff2e92a9a60ccdd889dd57cd5bdfed0f2 +0 -0
- data/test/dummy/tmp/cache/assets/E6B/860/sprockets%2Ffde862d9eef7ace776b5e3ebc470bcb4 +0 -0
- data/test/dummy/tmp/cache/assets/F4A/1F0/sprockets%2Ffceff42fa3bdfb2eafbcad5a77ad38ee +0 -0
- data/test/integration/navigation_test.rb +10 -0
- data/test/rubyception_test.rb +7 -0
- data/test/test_helper.rb +15 -0
- metadata +495 -0
|
@@ -0,0 +1,262 @@
|
|
|
1
|
+
$.ajaxSetup
|
|
2
|
+
beforeSend:(xhr)->
|
|
3
|
+
xhr.setRequestHeader 'Accept', 'application/json'
|
|
4
|
+
cache: false
|
|
5
|
+
|
|
6
|
+
ViewHelpers=
|
|
7
|
+
el_template:(path)->
|
|
8
|
+
attrs = {}
|
|
9
|
+
attrs = @model.attrs() if @model
|
|
10
|
+
html = @template path, attrs
|
|
11
|
+
$(@el).html html
|
|
12
|
+
fire:(event)->
|
|
13
|
+
$(@el).trigger event
|
|
14
|
+
bind_render:->
|
|
15
|
+
_.bindAll @ , 'render'
|
|
16
|
+
@collection.bind 'add' , @add
|
|
17
|
+
@collection.bind 'reset', @render
|
|
18
|
+
validate:->
|
|
19
|
+
Backbone.Validation.bind @,
|
|
20
|
+
invalid: (view, attr, error, selector)->
|
|
21
|
+
l = $("label[for=document_#{attr}]").html()
|
|
22
|
+
$('.errors').addClass 'on'
|
|
23
|
+
$('.errors').append "<tr class='error'><td class='name'>#{l}</td><td>#{error}</td></tr>"
|
|
24
|
+
|
|
25
|
+
ModelHelpers=
|
|
26
|
+
get:(attr)->
|
|
27
|
+
old_attr = @attributes[attr]
|
|
28
|
+
is_function = typeof @[attr] is 'function'
|
|
29
|
+
return @[attr](old_attr) if is_function
|
|
30
|
+
old_attr
|
|
31
|
+
attrs:->
|
|
32
|
+
attrs = {}
|
|
33
|
+
self = @
|
|
34
|
+
attrs = @toJSON()
|
|
35
|
+
attrs['id'] = @id if @id
|
|
36
|
+
_.each attrs, (value,attr)=>
|
|
37
|
+
attrs[attr] = @get attr
|
|
38
|
+
attrs
|
|
39
|
+
CollectionHelpers =
|
|
40
|
+
# @collection.collect
|
|
41
|
+
# ---------------
|
|
42
|
+
#
|
|
43
|
+
# This method is best used with the BackboneHelpers.collect
|
|
44
|
+
# In your view that has a collection you can call collect on
|
|
45
|
+
# the collection and it will render the the individual models
|
|
46
|
+
# if you used BackboneHelpers.collect to render the current
|
|
47
|
+
# view all you have to do now is:
|
|
48
|
+
#
|
|
49
|
+
# (assuming @collect 'projects' was called)
|
|
50
|
+
# @collection.collect
|
|
51
|
+
#
|
|
52
|
+
# This would render the template 'projects/index' and attach
|
|
53
|
+
# it the '.projects'
|
|
54
|
+
#
|
|
55
|
+
# if you don't want to render a template because it already exists
|
|
56
|
+
# you can just set has_template_index to false
|
|
57
|
+
collect:->
|
|
58
|
+
e = $ @context.el
|
|
59
|
+
find = arguments[0]
|
|
60
|
+
has_find = find isnt undefined
|
|
61
|
+
options = arguments[1]
|
|
62
|
+
options ||= {}
|
|
63
|
+
|
|
64
|
+
ti = options.template_index
|
|
65
|
+
delete options.template_index
|
|
66
|
+
template_index = ti
|
|
67
|
+
template_index = "#{@name}/index" if ti is undefined
|
|
68
|
+
e
|
|
69
|
+
html = @context.template template_index, options.args || {}
|
|
70
|
+
|
|
71
|
+
hti = options.has_template_index
|
|
72
|
+
delete options.has_template_index
|
|
73
|
+
e.html html if hti != false
|
|
74
|
+
|
|
75
|
+
t = options.template
|
|
76
|
+
delete options.template
|
|
77
|
+
template = "#{@name}/#{@name.singularize()}"
|
|
78
|
+
template = t if t isnt undefined
|
|
79
|
+
selector = e
|
|
80
|
+
selector = e.find(find) if has_find
|
|
81
|
+
primer = options.primer || template_index
|
|
82
|
+
selector.html ''
|
|
83
|
+
|
|
84
|
+
p = options.primer
|
|
85
|
+
delete options.primer
|
|
86
|
+
if @length == 0 && p
|
|
87
|
+
html = @context.template "#{primer}_primer", {}
|
|
88
|
+
e.html html
|
|
89
|
+
else
|
|
90
|
+
for model in @context.collection.models
|
|
91
|
+
options['model'] = model
|
|
92
|
+
e = @context.partial null, template, options
|
|
93
|
+
selector.append e.el
|
|
94
|
+
BackboneHelpers =
|
|
95
|
+
redirect_on_browse:->
|
|
96
|
+
if @browsing()
|
|
97
|
+
return window.location.pathname = '/signup'
|
|
98
|
+
else
|
|
99
|
+
false
|
|
100
|
+
browsing:->
|
|
101
|
+
$('body').hasClass('logged_out')
|
|
102
|
+
hash:(i=null)->
|
|
103
|
+
e = window.location.hash.replace /#/, ''
|
|
104
|
+
e = e.replace /\?.+$/, ''
|
|
105
|
+
e = e.split '/'
|
|
106
|
+
if i then e[i] else e
|
|
107
|
+
template: (path,data)->
|
|
108
|
+
template = null
|
|
109
|
+
eval "template = Template.#{path.replace(/\//g,'.')}"
|
|
110
|
+
throw "template is undefined: Template.#{path.replace(/\//g,'.')}" if template is undefined
|
|
111
|
+
Milk.render template, data
|
|
112
|
+
|
|
113
|
+
# collect
|
|
114
|
+
# ---------------
|
|
115
|
+
# This method makes it easy to quickly render a collection.
|
|
116
|
+
# all you need to do is pass in the name of the collection you
|
|
117
|
+
# want to be rendered eg.
|
|
118
|
+
#
|
|
119
|
+
# collect 'projects'
|
|
120
|
+
#
|
|
121
|
+
# If your dealing with an association:
|
|
122
|
+
#
|
|
123
|
+
# collect 'projects/tasks', id: 5
|
|
124
|
+
#
|
|
125
|
+
# It will create a collection in this case App.Collections.Projects
|
|
126
|
+
# with the model being App.Models.Project and an url with '/projects'.
|
|
127
|
+
# The collection will be assigned to the @collection instance variable
|
|
128
|
+
# so you can reference it in your view. It will not invoke fetch().
|
|
129
|
+
#
|
|
130
|
+
# It will also called @partial '.projects', 'projects/index' and
|
|
131
|
+
# attach the collection and whatever options you pass to to the
|
|
132
|
+
# collect method.
|
|
133
|
+
#
|
|
134
|
+
# If you want to override the el of the partial you just need to specify
|
|
135
|
+
# the el the option. eg.
|
|
136
|
+
#
|
|
137
|
+
# @collect 'users', el: '.people'
|
|
138
|
+
#
|
|
139
|
+
# If you want to override the partial path thats being used in the partial you just
|
|
140
|
+
# need to specify the template option (probably should not be called template since
|
|
141
|
+
# that might confuse people with the actual template being render) eg.
|
|
142
|
+
#
|
|
143
|
+
# #collect 'users', template: 'awesome_people/index'
|
|
144
|
+
#
|
|
145
|
+
# If you want to override the model being use you need to pass just the
|
|
146
|
+
# model name to the model option eg.
|
|
147
|
+
#
|
|
148
|
+
# @collect 'groups', model: 'community'
|
|
149
|
+
#
|
|
150
|
+
collect:->
|
|
151
|
+
name = arguments[0]
|
|
152
|
+
options = arguments[1]
|
|
153
|
+
options ||= {}
|
|
154
|
+
|
|
155
|
+
url = options.url if options.url
|
|
156
|
+
|
|
157
|
+
url ||= if name.match(/\//)
|
|
158
|
+
e = name.split '/'
|
|
159
|
+
context = e[0]
|
|
160
|
+
name = e[1]
|
|
161
|
+
reg = new RegExp "#{context}\\/(\\d+)"
|
|
162
|
+
id = options.id
|
|
163
|
+
"/api/#{context}/#{id}/#{name}"
|
|
164
|
+
else
|
|
165
|
+
"/api/#{name}"
|
|
166
|
+
klass = name.camelize()
|
|
167
|
+
has_model = options['model'] isnt undefined
|
|
168
|
+
model_name = if has_model
|
|
169
|
+
model_name = options['model']
|
|
170
|
+
delete options['model']
|
|
171
|
+
model_name.camelize()
|
|
172
|
+
else if @model_name
|
|
173
|
+
model_name = @model_name
|
|
174
|
+
model_name.camelize()
|
|
175
|
+
else
|
|
176
|
+
klass.singularize()
|
|
177
|
+
@collection = if @options.collection isnt undefined
|
|
178
|
+
collection = options['collection']
|
|
179
|
+
delete options['collection']
|
|
180
|
+
collection
|
|
181
|
+
else
|
|
182
|
+
new App.Collections[klass]
|
|
183
|
+
result_model = @result_model || "#{model_name}Result"
|
|
184
|
+
if App.Models[result_model]
|
|
185
|
+
@collection.results_model = new App.Models[result_model]()
|
|
186
|
+
@collection.results_model.collection = @collection
|
|
187
|
+
@collection.url = url
|
|
188
|
+
@collection.model = App.Models[model_name]
|
|
189
|
+
@collection.name = name
|
|
190
|
+
options ||= {}
|
|
191
|
+
options.collection = @collection
|
|
192
|
+
|
|
193
|
+
selector = ".#{name}"
|
|
194
|
+
if options['el'] isnt undefined
|
|
195
|
+
selector = options['el']
|
|
196
|
+
delete options['el']
|
|
197
|
+
|
|
198
|
+
template = "#{name}/index"
|
|
199
|
+
if options['template'] isnt undefined
|
|
200
|
+
template = options['template']
|
|
201
|
+
delete options['template']
|
|
202
|
+
e = @partial selector, template, options
|
|
203
|
+
@collection.context = e
|
|
204
|
+
@collection
|
|
205
|
+
partial:->
|
|
206
|
+
el = arguments[0]
|
|
207
|
+
name = arguments[1]
|
|
208
|
+
options = arguments[2]
|
|
209
|
+
scope = 'App'
|
|
210
|
+
if name.match /admin/
|
|
211
|
+
scope = 'Admin'
|
|
212
|
+
name = name.replace /admin\//, ''
|
|
213
|
+
e = name.split '/'
|
|
214
|
+
options ||= {}
|
|
215
|
+
options['el'] = el if el != null
|
|
216
|
+
p = (item.camelize() for item in e)
|
|
217
|
+
eval "var p = new #{scope}.Views.#{p.join('.')}(options)"
|
|
218
|
+
instance_var = e.join('_')
|
|
219
|
+
@[instance_var] = p
|
|
220
|
+
post:(url,success,data)->
|
|
221
|
+
token = $("meta[name='csrf-token']").attr('content')
|
|
222
|
+
data['authenticity_token'] = token
|
|
223
|
+
$.ajax
|
|
224
|
+
type : 'post'
|
|
225
|
+
url : url,
|
|
226
|
+
data : data
|
|
227
|
+
dataType : 'json'
|
|
228
|
+
context : this
|
|
229
|
+
success : success
|
|
230
|
+
get:(url,success,data)->
|
|
231
|
+
$.ajax
|
|
232
|
+
url : url,
|
|
233
|
+
data : data
|
|
234
|
+
dataType : 'json'
|
|
235
|
+
context : this
|
|
236
|
+
success : success
|
|
237
|
+
resize:->
|
|
238
|
+
App.Helpers.Base.resize_panels()
|
|
239
|
+
_.extend Backbone.Router.prototype , BackboneHelpers
|
|
240
|
+
_.extend Backbone.View.prototype , BackboneHelpers
|
|
241
|
+
_.extend Backbone.View.prototype , ViewHelpers
|
|
242
|
+
_.extend Backbone.Model.prototype , ModelHelpers
|
|
243
|
+
_.extend Backbone.Collection.prototype, CollectionHelpers
|
|
244
|
+
|
|
245
|
+
|
|
246
|
+
|
|
247
|
+
cx_backbone_common =
|
|
248
|
+
sync: (method, model, options) ->
|
|
249
|
+
# Changed attributes will be available here if model.saveChanges was called instead of model.save
|
|
250
|
+
if method == 'update' && model.changedAttributes()
|
|
251
|
+
options.data = JSON.stringify(model.changedAttributes())
|
|
252
|
+
options.contentType = 'application/json';
|
|
253
|
+
Backbone.sync.call(this, method, model, options)
|
|
254
|
+
|
|
255
|
+
cx_backbone_model =
|
|
256
|
+
# Calling this method instead of set will force sync to only send changed attributes
|
|
257
|
+
# Changed event will not be triggered until after the model is synced
|
|
258
|
+
saveChanges: (attrs) ->
|
|
259
|
+
@save(attrs, {wait: true})
|
|
260
|
+
|
|
261
|
+
_.extend(Backbone.Model.prototype, cx_backbone_common, cx_backbone_model)
|
|
262
|
+
_.extend(Backbone.Collection.prototype, cx_backbone_common)
|
|
@@ -0,0 +1,261 @@
|
|
|
1
|
+
# Milk is a simple, fast way to get more Mustache into your CoffeeScript and
|
|
2
|
+
# Javascript.
|
|
3
|
+
#
|
|
4
|
+
# Mustache templates are reasonably simple -- plain text templates are
|
|
5
|
+
# sprinkled with "tags", which are (by default) a pair of curly braces
|
|
6
|
+
# surrounding some bit of content. A good resource for Mustache can be found
|
|
7
|
+
# [here](mustache.github.com).
|
|
8
|
+
TemplateCache = {}
|
|
9
|
+
|
|
10
|
+
# Tags used for working with data get their data by looking up a name in a
|
|
11
|
+
# context stack. This name corresponds to a key in a hash, and the stack is
|
|
12
|
+
# searched top to bottom for an object with given key. Dots in names are
|
|
13
|
+
# special: a single dot ('.') is "top of stack", and dotted names like 'a.b.c'
|
|
14
|
+
# do a chained lookups.
|
|
15
|
+
Find = (name, stack, value = null) ->
|
|
16
|
+
return stack[stack.length - 1] if name == '.'
|
|
17
|
+
[name, parts...] = name.split(/\./)
|
|
18
|
+
for i in [stack.length - 1...-1]
|
|
19
|
+
continue unless stack[i]?
|
|
20
|
+
continue unless typeof stack[i] == 'object' and name of (ctx = stack[i])
|
|
21
|
+
value = ctx[name]
|
|
22
|
+
break
|
|
23
|
+
|
|
24
|
+
value = Find(part, [value]) for part in parts
|
|
25
|
+
|
|
26
|
+
# If we find a function in the stack, we'll treat it as a method, and call it
|
|
27
|
+
# with `this` bound to the element it came from. If a method returns a
|
|
28
|
+
# function, we treat it as a lambda, which doesn't have a bound `this`.
|
|
29
|
+
if value instanceof Function
|
|
30
|
+
value = do (value) -> ->
|
|
31
|
+
val = value.apply(ctx, arguments)
|
|
32
|
+
return (val instanceof Function) and val.apply(null, arguments) or val
|
|
33
|
+
|
|
34
|
+
# Null values will be coerced to the empty string.
|
|
35
|
+
return value
|
|
36
|
+
|
|
37
|
+
# Parsed templates are expanded by simply calling each function in turn.
|
|
38
|
+
Expand = (obj, tmpl, args...) -> (f.call(obj, args...) for f in tmpl).join('')
|
|
39
|
+
|
|
40
|
+
# For parsing, we'll basically need a template string to parse. We do need to
|
|
41
|
+
# remember to take the tag delimiters into account for the cache -- different
|
|
42
|
+
# parse trees can exist for the same template string!
|
|
43
|
+
Parse = (template, delimiters = ['{{','}}'], section = null) ->
|
|
44
|
+
cache = (TemplateCache[delimiters.join(' ')] ||= {})
|
|
45
|
+
return cache[template] if template of cache
|
|
46
|
+
|
|
47
|
+
buffer = []
|
|
48
|
+
|
|
49
|
+
# We'll use a regular expression to handle tag discovery. A proper parser
|
|
50
|
+
# might be faster, but this is simpler, and certainly fast enough for now.
|
|
51
|
+
# Since the tag delimiters may change over time, we'll want to rebuild the
|
|
52
|
+
# regex when they change.
|
|
53
|
+
BuildRegex = ->
|
|
54
|
+
[tagOpen, tagClose] = delimiters
|
|
55
|
+
return ///
|
|
56
|
+
([\s\S]*?) # Capture the pre-tag content
|
|
57
|
+
([#{' '}\t]*) # Capture the pre-tag whitespace
|
|
58
|
+
(?: #{tagOpen} \s* # Match the opening tag
|
|
59
|
+
(?:
|
|
60
|
+
(!) \s* ([\s\S]+?) | # Comments
|
|
61
|
+
(=) \s* ([\s\S]+?) \s* = | # Set Delimiters
|
|
62
|
+
({) \s* (\w[\S]*?) \s* } | # Triple Mustaches
|
|
63
|
+
([^0-9a-zA-Z._!={]?) \s* ([\w.][\S]*?) # Everything else
|
|
64
|
+
)
|
|
65
|
+
\s* #{tagClose} ) # Match the closing tag
|
|
66
|
+
///gm
|
|
67
|
+
|
|
68
|
+
tagPattern = BuildRegex()
|
|
69
|
+
tagPattern.lastIndex = pos = (section || { start: 0 }).start
|
|
70
|
+
|
|
71
|
+
# Useful errors should always be prefered - we should compile as much
|
|
72
|
+
# relevant information as possible.
|
|
73
|
+
parseError = (pos, msg) ->
|
|
74
|
+
(endOfLine = /$/gm).lastIndex = pos
|
|
75
|
+
endOfLine.exec(template)
|
|
76
|
+
|
|
77
|
+
parsedLines = template.substr(0, pos).split('\n')
|
|
78
|
+
lineNo = parsedLines.length
|
|
79
|
+
lastLine = parsedLines[lineNo - 1]
|
|
80
|
+
tagStart = contentEnd + whitespace.length
|
|
81
|
+
lastTag = template.substr(tagStart + 1, pos - tagStart - 1)
|
|
82
|
+
|
|
83
|
+
indent = new Array(lastLine.length - lastTag.length + 1).join(' ')
|
|
84
|
+
carets = new Array(lastTag.length + 1).join('^')
|
|
85
|
+
lastLine = lastLine + template.substr(pos, endOfLine.lastIndex - pos)
|
|
86
|
+
|
|
87
|
+
error = new Error()
|
|
88
|
+
error[key] = e[key] for key of e =
|
|
89
|
+
"message": "#{msg}\n\nLine #{lineNo}:\n#{lastLine}\n#{indent}#{carets}"
|
|
90
|
+
"error": msg, "line": lineNo, "char": indent.length, "tag": lastTag
|
|
91
|
+
return error
|
|
92
|
+
|
|
93
|
+
# As we start matching things, let's pull out our captures and build indices.
|
|
94
|
+
while match = tagPattern.exec(template)
|
|
95
|
+
[content, whitespace] = match[1..2]
|
|
96
|
+
type = match[3] || match[5] || match[7] || match[9]
|
|
97
|
+
tag = match[4] || match[6] || match[8] || match[10]
|
|
98
|
+
|
|
99
|
+
contentEnd = (pos + content.length) - 1
|
|
100
|
+
pos = tagPattern.lastIndex
|
|
101
|
+
|
|
102
|
+
# Standalone tags are tags on lines without any non-whitespace characters.
|
|
103
|
+
isStandalone = (contentEnd == -1 or template.charAt(contentEnd) == '\n') &&
|
|
104
|
+
template.charAt(pos) in [ undefined, '', '\r', '\n' ]
|
|
105
|
+
|
|
106
|
+
# We should just add static content to the buffer.
|
|
107
|
+
buffer.push(do (content) -> -> content) if content
|
|
108
|
+
|
|
109
|
+
# If we're dealing with a standalone tag that's not interpolation, we
|
|
110
|
+
# should consume the newline immediately following the tag. If we're not,
|
|
111
|
+
# we need to buffer the whitespace we captured earlier.
|
|
112
|
+
if isStandalone and type not in ['', '&', '{']
|
|
113
|
+
pos += 1 if template.charAt(pos) == '\r'
|
|
114
|
+
pos += 1 if template.charAt(pos) == '\n'
|
|
115
|
+
else if whitespace
|
|
116
|
+
buffer.push(do (whitespace) -> -> whitespace)
|
|
117
|
+
contentEnd += whitespace.length
|
|
118
|
+
whitespace = ''
|
|
119
|
+
|
|
120
|
+
# Now we'll handle the tag itself:
|
|
121
|
+
switch type
|
|
122
|
+
|
|
123
|
+
# Comment tags should simply be ignored.
|
|
124
|
+
when '!' then break
|
|
125
|
+
|
|
126
|
+
# Interpolations are handled by finding the value in the context stack,
|
|
127
|
+
# calling and rendering lambdas, and escaping the value if appropriate.
|
|
128
|
+
when '', '&', '{'
|
|
129
|
+
buildInterpolationTag = (name, is_unescaped) ->
|
|
130
|
+
return (context) ->
|
|
131
|
+
if (value = Find(name, context) ? '') instanceof Function
|
|
132
|
+
value = Expand(this, Parse("#{value()}"), arguments...)
|
|
133
|
+
value = @escape("#{value}") unless is_unescaped
|
|
134
|
+
return "#{value}"
|
|
135
|
+
buffer.push(buildInterpolationTag(tag, type))
|
|
136
|
+
|
|
137
|
+
# Partial data is looked up lazily by the given function, indented as
|
|
138
|
+
# appropriate, and then rendered.
|
|
139
|
+
when '>'
|
|
140
|
+
buildPartialTag = (name, indentation) ->
|
|
141
|
+
return (context, partials) ->
|
|
142
|
+
partial = partials(name).toString()
|
|
143
|
+
partial = partial.replace(/^(?=.)/gm, indentation) if indentation
|
|
144
|
+
return Expand(this, Parse(partial), arguments...)
|
|
145
|
+
buffer.push(buildPartialTag(tag, whitespace))
|
|
146
|
+
|
|
147
|
+
# Sections and Inverted Sections make a recursive parsing pass, allowing
|
|
148
|
+
# us to use the call stack to handle section parsing. This will go until
|
|
149
|
+
# it reaches the matching End Section tag, when it will return the
|
|
150
|
+
# (cached!) template it parsed, along with the index it stopped at.
|
|
151
|
+
when '#', '^'
|
|
152
|
+
sectionInfo =
|
|
153
|
+
name: tag, start: pos
|
|
154
|
+
error: parseError(tagPattern.lastIndex, "Unclosed section '#{tag}'!")
|
|
155
|
+
[tmpl, pos] = Parse(template, delimiters, sectionInfo)
|
|
156
|
+
|
|
157
|
+
# Sections are rendered by finding the value in the context stack,
|
|
158
|
+
# coercing it into an array (unless the value is falsey), and rendering
|
|
159
|
+
# the template with each element of the array taking a turn atop the
|
|
160
|
+
# context stack. If the value was a function, the template is filtered
|
|
161
|
+
# through it before rendering.
|
|
162
|
+
sectionInfo['#'] = buildSectionTag = (name, delims, raw) ->
|
|
163
|
+
return (context) ->
|
|
164
|
+
value = Find(name, context) || []
|
|
165
|
+
tmpl = if value instanceof Function then value(raw) else raw
|
|
166
|
+
value = [value] unless value instanceof Array
|
|
167
|
+
parsed = Parse(tmpl || '', delims)
|
|
168
|
+
|
|
169
|
+
context.push(value)
|
|
170
|
+
result = for v in value
|
|
171
|
+
context[context.length - 1] = v
|
|
172
|
+
Expand(this, parsed, arguments...)
|
|
173
|
+
context.pop()
|
|
174
|
+
|
|
175
|
+
return result.join('')
|
|
176
|
+
|
|
177
|
+
# Inverted Sections render under almost opposite conditions: their
|
|
178
|
+
# contents will only be rendered when the retrieved value is either
|
|
179
|
+
# falsey or an empty array.
|
|
180
|
+
sectionInfo['^'] = buildInvertedSectionTag = (name, delims, raw) ->
|
|
181
|
+
return (context) ->
|
|
182
|
+
value = Find(name, context) || []
|
|
183
|
+
value = [1] unless value instanceof Array
|
|
184
|
+
value = if value.length is 0 then Parse(raw, delims) else []
|
|
185
|
+
return Expand(this, value, arguments...)
|
|
186
|
+
|
|
187
|
+
buffer.push(sectionInfo[type](tag, delimiters, tmpl))
|
|
188
|
+
|
|
189
|
+
# When the parser encounters an End Section tag, it runs a couple of
|
|
190
|
+
# quick sanity checks, then returns control back to its caller.
|
|
191
|
+
when '/'
|
|
192
|
+
unless section?
|
|
193
|
+
error = "End Section tag '#{tag}' found, but not in section!"
|
|
194
|
+
else if tag != (name = section.name)
|
|
195
|
+
error = "End Section tag closes '#{tag}'; expected '#{name}'!"
|
|
196
|
+
throw parseError(tagPattern.lastIndex, error) if error
|
|
197
|
+
|
|
198
|
+
template = template[section.start..contentEnd]
|
|
199
|
+
cache[template] = buffer
|
|
200
|
+
return [template, pos]
|
|
201
|
+
|
|
202
|
+
# The Set Delimiters tag needs to update the delimiters after some error
|
|
203
|
+
# checking, and rebuild the regular expression we're using to match tags.
|
|
204
|
+
when '='
|
|
205
|
+
unless (delimiters = tag.split(/\s+/)).length == 2
|
|
206
|
+
error = "Set Delimiters tags should have two and only two values!"
|
|
207
|
+
throw parseError(tagPattern.lastIndex, error) if error
|
|
208
|
+
|
|
209
|
+
escape = /[-[\]{}()*+?.,\\^$|#]/g
|
|
210
|
+
delimiters = (d.replace(escape, "\\$&") for d in delimiters)
|
|
211
|
+
tagPattern = BuildRegex()
|
|
212
|
+
|
|
213
|
+
# Any other tag type is probably a typo.
|
|
214
|
+
else
|
|
215
|
+
throw parseError(tagPattern.lastIndex, "Unknown tag type -- #{type}")
|
|
216
|
+
|
|
217
|
+
# Now that we've finished with this tag, we prepare to parse the next one!
|
|
218
|
+
tagPattern.lastIndex = if pos? then pos else template.length
|
|
219
|
+
|
|
220
|
+
# At this point, we've parsed all the tags. If we've still got a `section`,
|
|
221
|
+
# someone left a section tag open.
|
|
222
|
+
throw section.error if section?
|
|
223
|
+
|
|
224
|
+
# All the tags is not all the content; if there's anything left over, append
|
|
225
|
+
# it to the buffer. Then we'll cache the buffer and return it!
|
|
226
|
+
buffer.push(-> template[pos..]) unless template.length == pos
|
|
227
|
+
return cache[template] = buffer
|
|
228
|
+
|
|
229
|
+
# ### Public API
|
|
230
|
+
|
|
231
|
+
# The exported object (globally `Milk` in browsers) forms Milk's public API:
|
|
232
|
+
Milk =
|
|
233
|
+
VERSION: '1.2.0'
|
|
234
|
+
# Helpers are a form of context, implicitly on the bottom of the stack. This
|
|
235
|
+
# is a global value, and may be either an object or an array.
|
|
236
|
+
helpers: []
|
|
237
|
+
# Partials may also be provided globally.
|
|
238
|
+
partials: null
|
|
239
|
+
# The `escape` method performs basic content escaping, and may be either
|
|
240
|
+
# called or overridden with an alternate escaping mechanism.
|
|
241
|
+
escape: (value) ->
|
|
242
|
+
entities = { '&': 'amp', '"': 'quot', '<': 'lt', '>': 'gt' }
|
|
243
|
+
return value.replace(/[&"<>]/g, (ch) -> "&#{ entities[ch] };")
|
|
244
|
+
# Rendering is simple: given a template and some data, it populates the
|
|
245
|
+
# template. If your template uses Partial Tags, you may also supply a hash or
|
|
246
|
+
# a function, or simply override `Milk.partials`. There is no Step Three.
|
|
247
|
+
render: (template, data, partials = null) ->
|
|
248
|
+
unless (partials ||= @partials || {}) instanceof Function
|
|
249
|
+
partials = do (partials) -> (name) ->
|
|
250
|
+
throw "Unknown partial '#{name}'!" unless name of partials
|
|
251
|
+
return Find(name, [partials])
|
|
252
|
+
|
|
253
|
+
context = if @helpers instanceof Array then @helpers else [@helpers]
|
|
254
|
+
return Expand(this, Parse(template), context.concat([data]), partials)
|
|
255
|
+
|
|
256
|
+
# Happy hacking!
|
|
257
|
+
if exports?
|
|
258
|
+
exports[key] = Milk[key] for key of Milk
|
|
259
|
+
else
|
|
260
|
+
this.Milk = Milk
|
|
261
|
+
|