pax_global_header00006660000000000000000000000064146554741010014521gustar00rootroot0000000000000052 comment=8aaaec0707ef193ff6f6a369c77668d16825c91a camping-3.2.6/000077500000000000000000000000001465547410100131475ustar00rootroot00000000000000camping-3.2.6/.github/000077500000000000000000000000001465547410100145075ustar00rootroot00000000000000camping-3.2.6/.github/dependabot.yml000066400000000000000000000001661465547410100173420ustar00rootroot00000000000000version: 2 updates: - package-ecosystem: "github-actions" directory: "/" schedule: interval: "weekly" camping-3.2.6/.github/pull_request_template.md000066400000000000000000000016001465547410100214450ustar00rootroot00000000000000Get your PR ready! - [ ] Name your PR something Snazzy. - [ ] Make certain that your PR passes the tests. - [ ] If you made any changes to `camping.rb`, or `camping-unabridged.rb` then make sure they are in sync. - [ ] If this is a Release PR, make sure that you updated the Camping version in `camping.gemspec`, `lib/version.rb`, and the `CHANGELOG` - [ ] Add a nice description of your changes in the CHANGELOG. - [ ] Delete any unnecessary commented out code that you thought you might need while working on the thing. - [ ] Rebase from main: `git checkout main; git pull upstream main, git checkout my_awesome_branch, git rebase main`. - [ ] Add a Description of your PR here. - [ ] Add a bulleted list with your changes. When all that's done, Ask for a review from Karl. ## Example This is my great PR! I added kittens. lot's of Kittens. Changes: * Added Kittens. * Removed non Kittens. camping-3.2.6/.github/workflows/000077500000000000000000000000001465547410100165445ustar00rootroot00000000000000camping-3.2.6/.github/workflows/camping.yml000066400000000000000000000016161465547410100207110ustar00rootroot00000000000000# This workflow uses actions that are not certified by GitHub. # They are provided by a third-party and are governed by # separate terms of service, privacy policy, and support # documentation. # This workflow will download a prebuilt Ruby version, install dependencies and run tests with Rake # For more information see: https://github.com/marketplace/actions/setup-ruby-jruby-and-truffleruby name: Camping on: [push, pull_request] jobs: build: runs-on: ${{ matrix.os }} strategy: matrix: os: [ 'ubuntu-latest', 'macos-latest' ] # 'windows-latest' ruby: ['3.3', '3.2', '3.1'] #, 'ruby-head'] exclude: - os: windows-latest ruby: jruby-head steps: - uses: actions/checkout@v4 - uses: ruby/setup-ruby@v1 with: ruby-version: ${{ matrix.ruby }} bundler-cache: true - name: Run tests run: bundle exec rake camping-3.2.6/.github/workflows/integration.yml000066400000000000000000000022031465547410100216070ustar00rootroot00000000000000# This workflow uses actions that are not certified by GitHub. # They are provided by a third-party and are governed by # separate terms of service, privacy policy, and support # documentation. # This workflow will download a prebuilt Ruby version, install dependencies and run tests with Rake # For more information see: https://github.com/marketplace/actions/setup-ruby-jruby-and-truffleruby name: Integration on: [push, pull_request] jobs: build: runs-on: ${{ matrix.os }} strategy: matrix: os: [ 'ubuntu-latest', 'macos-latest' ] # 'windows-latest' ruby: ['3.3', '3.2', '3.1'] #, 'ruby-head'] exclude: - os: windows-latest ruby: jruby-head steps: - uses: actions/checkout@v4 - uses: ruby/setup-ruby@v1 with: ruby-version: ${{ matrix.ruby }} bundler-cache: false - name: Build Camping run: gem build -o camping.gem - name: Move to test folder run: cd test/integration - name: Install all gems run: bundle install - name: Remove all gems run: bundle clean --force - name: Run tests run: bundle exec rake camping-3.2.6/.gitignore000066400000000000000000000001151465547410100151340ustar00rootroot00000000000000pkg doc camping-*.gem node_modules camping.gem test/integration/Gemfile.lock camping-3.2.6/CHANGELOG000066400000000000000000000227241465547410100143700ustar00rootroot00000000000000= 3.2.6 09 Aug, 2024 * Server now loads everything from the kindling directory. To start your fire. * Move Camping Gear to their own directory to play nicer with Zeitwerk. * Add some testing for Camping::Server. = 3.2.5 == 13 June, 2024 * Fix bug where create method is never called. * Update KDL dependency to 1.0.6. = 3.2.4 == 10 June, 2024 * Add new integration test. * Pack the new Firewatch Logging Gear wrapper by default in Camping apps. = 3.2.3 == 09 June, 2024 * Remove dead code make a derelict file in the generator. * Correct some Ruby warnings in Camping.rb, CampingTools, and Camping Gear. * Hopefully correct some Gemfile errors causing tests to fail. * Add MIT License to camping.gemspec. * Correct error where Minitest was misspelled in generated test helper. = 3.2.2 == 09 June, 2024 * Fix wrong version of dependency. = 3.2.1 == 09 June, 2024 * Start work on integration testing. * Change some gem requirements in Gemfile and gemspec. * Add XSendfile to deployment middleware. = 3.2.0 == 05 June, 2024 * Add default logging middleware to Camping. * Some Bugfixes for Nancy. = 3.1.3 === 24 June, 2023 * Fix a loader error where the loader would assume that the `/apps` directory exists, instead of checking first. = 3.1.1 === 23 June, 2023 * Fix an error that caused the loader not to load because we renamed it Loader from Relaoder. * Add new dependencies to gemspec. = 3.1.0 === 22 June, 2023 * Rewrote Camping's Reloader/Loader to work with the new nested /apps directory. * Reopening and redefining controllers, views, and helpers is possible by placing them in the /apps directory. * Added Zeitwerk autoloading, watched directories are /apps and /lib. * Reloader now calls make camp upon each request in development mode. * Reloader also now listens for changes in the /apps and /lib directory and eager reloads as needed. * /apps and /lib are now eager loaded. * Camping new command now adds a test_helper. * Started work on CampGuide, an exception handler that provides context aware resolutions to common Camping Problems. = 3.0.2 === 18th March, 2023 * App names supplied to the camping new command are now CamelCased. = 3.0.1 === 18th March, 2023 * Fixed error with Camping new that would generate a faulty camp.rb. * App names supplied to the camping new command are now capitlized. = 3.0 === 18th March, 2023 * Camping Now uses the latest versions of Rack and Rackup under the hood. * New Routing Spec gives Sinatra like Routing helpers. * Inherit from other Controllers without carrying their urls. * You can now inherit from another Controller, AND set custom urls. * ActiveRecord now removed in favor of a more flexible database architecture. * Builtin Gear added for Filtering, and Sinatra style routing. * Added Camping Tools, small utilities that come with Camping. * Camping now has a new project generator. run `camping new` from your shell. * _meta data set in an App's options hash. * Session secrets are now much longer. * Camping settings can now be set in a kdl config doc. * Gems are now loaded from your Gemfile upon startup. * Add Inspection Camping Gear. Utilities for Camp inspection. * Camping now looks for a `camp.rb` file to start with. Put your apps there. = 2.3 === 28th July, 2022 * New routes command line helper command. * plugin support added via the gear method. * Restored support for mounting multiple apps. * Add url_prefix option for mounting apps at different urls. * Increase camping test limit to 5120 bytes. We needed just a bit more space. * Add a book stub about Models. * Add a chapter about middleware and how it works. * Camping a '/' is now forcibly terminating each route. May revert this back if we run into trouble. = 2.2 === Never Released * Updated ActiveRecord migrations class to reference version 6.1 * Removed deprecated gemfile definitions * Rename deprecated methods * Get tests to pass = 2.1 === 19th Aug, 2010 (whyday) * Helpers#R now calls to_param on any object it passes in * Fix route generation issue with routes including "." (#22) * Improved tests * Improved 1.9 support * Camping::Server is now built upon Rack::Server * Add support for ERB, Haml etc through Tilt * Introducing Camping.options and Camping#set * Camping::Server only loads ActiveRecord when needed = 2.0 === 9th Apr, 2010 * Speed-up of Camping::Mab (thanks zimbatm!) * @state is now an alias of @env['rack.session'] * Camping.use injects a Rack middleware. * Update Flipbook to RDoc 2.4 * Removed old examples. * Updated examples/blog.rb * Camping::Apps returns! * Session-cookies now timeout naturally (thanks jenna!) * You can now `throw :halt` to halt the response in a helper. * Camping::H#u is gone (was an alias to merge!) * Camping::Session now uses session-cookies. The AR-backend is gone for now. * camping/db.rb has been renamed to camping/ar.rb. * Camping now uses Rack internally. Every app responds to #call. = 1.6 === Never released * Camping::Apps removed, it wasn't reliable. * bin/camping server kinds splitted in various files. * NotFound and ServerError controllers changed to methods : r404 : called when a controller was not found r500 : called on uncaught exception r501 : called on undefined method All of those can be overridden at your taste. * Markaby no longer required. Like AR, is it autoloaded on (Mab) usage. * Camping::H is now inheriting from Hash instead of HashWithIndifferentAccess. * Which made possible to remove the last strict dependency : active_support * #errors_for removed, it wasn't really used * Bug fixes ! = 1.5 === 3rd Oct, 2006 * Camping::Apps stores an array of classes for all loaded apps. * bin/camping can be given a directory. Like: camping examples/ * Console mode -- thank zimbatm. Use: camping -C yourapp.rb * Call controllers with Camping.method_missing. Tepee.get(:Index) #=> (Response) Blog.post(:Delete, id) #=> (Response) Blog.post(:Login, :input => {'username' => 'admin', 'password' => 'camping'}) #=> # Blog.get(:Info, :env => {:HTTP_HOST => 'wagon'}) #=> #'wagon'} ...> * Using \r\n instead of \n on output. FastCGI has these needs. * ActiveRecord no longer required or installed. * If you refer to Models::Base, however, ActiveRecord will be loaded with autoload. (see lib/camping/db.rb) * new Camping::FastCGI.serve which will serve a whole directory of apps (see http://code.whytheluckystiff.net/camping/wiki/TheCampingServer) * ~/.campingrc can contain database connection info if you want your default to be something other than SQLite. database: adapter: mysql username: camping socket: /tmp/mysql.sock password: NOFORESTFIRES database: camping * controllers are now *ordered*. uses the inherited hook to keep track of all classes created with R. those classes are scanned, in order, when a request is made. any other controllers are handled first. so if you plan on overriding the urls method, be sure to subclass from R(). * Console mode will load .irbrc in the working directory, if present. (for example, in my ~/git/balloon directory, i have this in the .irbrc: include Balloon::Models when camping -C balloon.rb gets run, the models all get included in main.) * And, of course, many other bugfixes from myself and the loyal+kind zimbatm... * Markaby updated to 0.5. (See its CHANGELOG.) = 1.4.2 === 18th May, 2006 * Efficient file uploads for multipart/form-data POSTs. * Camping tool now uses Mongrel, if available. If not, sticks with WEBrick. * Multiple apps can be loaded with the camping tool, each mounted according to their file name. = 1.4.1 === 3rd May, 2006 * Streaming HTTP support. If body is IO, will simply pass to the controller. Mongrel, in particular, supports this nicely. = 1.4 === 11th April, 2006 * Moved Camping::Controllers::Base to Camping::Base. * Moved Camping::Controllers::R to Camping::R. * New session library (lib/camping/session.rb). * WEBrick handler (lib/camping/webrick.rb) and Mongrel handler (lib/camping/mongrel.rb). * Helpers#URL, builds a complete URL for a route. Returns a URI object. This way relative links could just return self.URL.path. * Base#initialize takes over some of Base#service's duties. * ENV now available as @env in controllers and views. * Beautiful multi-page docs without frames! = 1.3 === 28th January, 2006 * bin/camping: an application launcher. * Camping.run(request, response) now changed to controller = Camping.run(request, env) * This means outputting the response is the wrapper/server's job. See bin/camping, you can do a controller.to_s at the least. * Controllers::Base.env is the new thread-safe home for ENV. * The input hash now works more like Rails params. You can call keys like methods or with symbols or strings. * Queries are now parsed more like PHP/Rails, in that you can denote structure with brackets: post[user]=_why;post[id]=2 * Auto-prefix table names, to help prevent name clash. * Helpers.errors_for simple validation. * Lots of empty :href and :action attributes, a bug. * New single-page flipbook RDoc template. = 1.2 === 23rd January, 2006 * Camping.goes allows fresh modules build from all Camping parts. * File uploads now supported (multipart/form-data). * Helpers.R can rebuild routes. * Helpers./ for tracing paths from the root. = 1.1 === 19th January, 2006 * Allowed request and response streams to be passed in, to allow WEBrick and FastCGI support. = 1.0 === 17th January, 2006 * Initial checkin, see announcement at http://redhanded.hobix.com/bits/campingAMicroframework.html. camping-3.2.6/COPYING000066400000000000000000000020301465547410100141750ustar00rootroot00000000000000Copyright (c) 2006 why the lucky stiff 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 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. camping-3.2.6/Gemfile000066400000000000000000000007731465547410100144510ustar00rootroot00000000000000# frozen_string_literal: true source 'https://rubygems.org' gem 'rake' gem 'rack' gem 'rack-session' gem 'rackup' gem 'mab' gem 'kdl', '~> 1.0.6' gem 'zeitwerk' gem 'listen', '~> 3.9.0' gem 'dry-logger', '~> 1.0.4' gem 'bundler' gem 'tilt' group :development do gem 'parser' gem 'unparser' gem 'pp' end group :test do gem 'minitest', '~> 5.0' gem 'minitest-reporters' gem 'rack-test' gem 'ruby_parser' gem 'minitest-hooks' gem "minitest-global_expectations" gem "minitest-sprint" end camping-3.2.6/Gemfile.lock000066400000000000000000000036321465547410100153750ustar00rootroot00000000000000GEM remote: https://rubygems.org/ specs: ansi (1.5.0) ast (2.4.2) base64 (0.2.0) bigdecimal (3.1.8) builder (3.3.0) diff-lcs (1.5.1) dry-logger (1.0.4) ffi (1.17.0) ffi (1.17.0-arm64-darwin) ffi (1.17.0-x86_64-darwin) ffi (1.17.0-x86_64-linux-gnu) kdl (1.0.6) base64 (~> 0.2.0) bigdecimal (~> 3.1.6) racc (~> 1.5) simpleidn (~> 0.2.1) listen (3.9.0) rb-fsevent (~> 0.10, >= 0.10.3) rb-inotify (~> 0.9, >= 0.9.10) mab (0.0.3) minitest (5.23.1) minitest-global_expectations (1.0.1) minitest (> 5) minitest-hooks (1.5.1) minitest (> 5.3) minitest-reporters (1.6.1) ansi builder minitest (>= 5.0) ruby-progressbar minitest-sprint (1.2.2) path_expander (~> 1.1) parser (3.3.3.0) ast (~> 2.4.1) racc path_expander (1.1.2) pp (0.5.0) prettyprint prettyprint (0.2.0) racc (1.8.0) rack (3.1.3) rack-session (2.0.0) rack (>= 3.0.0) rack-test (2.1.0) rack (>= 1.3) rackup (2.1.0) rack (>= 3) webrick (~> 1.8) rake (13.2.1) rb-fsevent (0.11.2) rb-inotify (0.11.1) ffi (~> 1.0) ruby-progressbar (1.13.0) ruby_parser (3.21.0) racc (~> 1.5) sexp_processor (~> 4.16) sexp_processor (4.17.1) simpleidn (0.2.3) tilt (2.3.0) unparser (0.6.13) diff-lcs (~> 1.3) parser (>= 3.3.0) webrick (1.8.1) zeitwerk (2.6.15) PLATFORMS aarch64-darwin arm64-darwin arm64-darwin-22 arm64-darwin-23 x86_64-darwin-20 x86_64-linux DEPENDENCIES bundler dry-logger (~> 1.0.4) kdl (~> 1.0.6) listen (~> 3.9.0) mab minitest (~> 5.0) minitest-global_expectations minitest-hooks minitest-reporters minitest-sprint parser pp rack rack-session rack-test rackup rake ruby_parser tilt unparser zeitwerk BUNDLED WITH 2.5.11 camping-3.2.6/README.md000066400000000000000000000113301465547410100144240ustar00rootroot00000000000000![Build Status](https://github.com/camping/camping/actions/workflows/camping.yml/badge.svg) ![Build Status](https://github.com/camping/camping/actions/workflows/integration.yml/badge.svg) # ⛺️ Camping, a Microframework Camping is a micro web framework which stays as small as possible. You can probably view the complete source code on a single page. But, you know, it's so small that, if you think about it, what can it really do? Apparently it can do a lot. It's pretty swell. The idea here is to store a complete fledgling web application in a single file like many small CGIs. But to organize it as a Model-View-Controller application. And with time, you can move your Models, Views, and Controllers into other files as your app grows. Camping supports multiple *apps*, capsuled code that runs together. Each app can have independent models, routes, and controllers. Pack your gear when you go Camping! With a simple plugin system, Camping is easily extensible. Add all sorts of useful and silly things. ## A Camping Skeleton A skeletal Camping blog could look like this: ```ruby require 'camping' Camping.goes :Blog module Blog::Models class Post < Base; belongs_to :user; end class Comment < Base; belongs_to :user; end class User < Base; end end module Blog::Controllers class Index def get @posts = Post.find :all render :index end end end module Blog::Views def layout html do head { title "My Blog" } body do h1 "My Blog" self << yield end end end def index @posts.each do |post| h1 post.title end end end ``` ## Installation Interested yet? Luckily it's quite easy to install Camping. We'll be using a tool called RubyGems, and Bundler, so if you don't have that installed yet, go grab it! Once that's sorted out, open up a Terminal or Command Line and enter: ``` gem install camping ``` ~~Even better, install the Camping Omnibus, a full package of recommended libs:~~ Camping Omnibus will return for summer vacation. Now make a new directory filled with your camp essentials using the `camping new` command: ``` camping new Donuts # You can replace Donuts with whatever but CamelCased. ``` Move to your new directory, then use bundler to install all of your camp's dependencies: ``` cd donuts; bundle install ``` You can now run camping using the `camping` command. We recommend running camping in development mode locally. Make certain to prefix the camping command with `bundle exec` to run your app with the gems you've installed just for your camp: ``` bundle exec camping -e development ``` ## Learning First of all, you should read [the first chapters](/book/01_introduction.md) of The Camping Book. It should hopefully get you started pretty quick. While you're doing that, you should be aware of the _reference_ which contains documentation for all the different parts of Camping. [The wiki](https://github.com/camping/camping/wiki) is the place for all tiny, useful tricks that we've collected over the years. Don't be afraid to share your own discoveries; the more, the better! We have a Discord channel over at the Ruby Punks community, so if you feel like chatting with us, you should join [#camping](https://discord.gg/JSmPBsWgFt). Once our chattin' leads to action, we open up an issue on Github and move the conversation over there. ## Contributing Camping is under active development, and we'd love some help!. Our current focus is bug fixes, documentation, and tests. We have a [number of issues](https://github.com/camping/camping/issues?q=is%3Aissue+is%3Aopen+label%3A%22Great+for+new+Contributors%22) open for new contributors to get crackin! To begin contributing, [Fork Camping](https://github.com/camping/camping/fork), Then make a new branch, (`git checkout -b my_branch_name`) with your changes. When you're ready to bring all that hard work on over, open a draft PR and ping Karl to take a look, He'll help you fix any issues and help you get it inside of old Camping. Have a suggestion? Open an issue with what you think we ought to be doing with our lives, and we'll talk about it. Don't be shy. ## Running Tests Tests should be run using bundler and rake: `bundle exec rake`. ## Minting Releases We use Ruby Gems to distribute versions of Camping. When you're ready to mint a release, run: `gem build -o camping.gem`, then `gem push camping.gem`. ## Authors Camping was originally crafted by [why the lucky stiff](http://en.wikipedia.org/wiki/Why_the_lucky_stiff), but is now maintained by the _community_. This simply means that if we like your patch, it will be applied. Everything is managed through this Github repo, so just [open an issue](https://github.com/camping/camping/issues/new) and you can instantly take part in shaping Camping. camping-3.2.6/Rakefile000066400000000000000000000107601465547410100146200ustar00rootroot00000000000000$:.unshift 'extras' begin require 'rake/dsl_definition' require 'rake/alt_system' rescue LoadError else begin if defined?(Rake::DeprecatedObjectDSL) Rake::DeprecatedObjectDSL.class_eval do private_instance_methods(false).each do |meth| remove_method meth end end end rescue Exception end end require "bundler/gem_tasks" require 'rake' require 'rake/clean' require 'rake/testtask' require 'tempfile' require 'open3' # require File.expand_path('../constants', __FILE__) CLEAN.include ['**/.*.sw?', '*.gem', '.config', 'test/test.log', '.*.pt'] task :default => :check # New Docs ## context for the docs sections: we're going to move to using Rdoc and yard. ## With some extras. The Docs folder will probably go away. ## New docs directly serve a website like a boss. desc "Serves the docs locally" task :serve do sh "ruby -run -e httpd docs -p 8000" end ## RDoc begin gem 'rdoc', '~>3.9.0' rescue LoadError task :docs do puts "** Camping needs RDoc 3.9 in order to use the Flipbook template." end else require 'rdoc/task' RDoc::Task.new(:docs) do |rdoc| # We have a recent version of RDoc, so let's use flipbook. require 'rdoc/generator/singledarkfish' rdoc.options += ['-f', 'singledarkfish', *RDOC_OPTS] rdoc.template = "flipbook" rdoc.rdoc_dir = 'doc' rdoc.title = "Camping, a Microframework" rdoc.rdoc_files.add ['README', 'lib/camping-unabridged.rb', 'lib/camping/**/*.rb', 'book/*'] end end task :rubygems_docs do require 'rubygems/doc_manager' def spec.installation_path; '.' end def spec.full_gem_path; '.' end manager = Gem::DocManager.new(spec) manager.generate_rdoc end desc "Packages Camping." task :package => :clean ## Tests namespace :test do Rake::TestTask.new(:camping) do |t| t.libs << "test" t.test_files = FileList['test/app_*.rb'] end Rake::TestTask.new(:gear) do |t| t.libs << "test" t.test_files = FileList['test/gear/gear_*.rb'] end ## Reloader Tests Rake::TestTask.new(:reloader) do |t| t.libs << "test" t.test_files = FileList['test/reload_*.rb'] end ## Config Reloader Tests Rake::TestTask.new(:configreloader) do |t| t.libs << "test" t.test_files = FileList['test/config_*.rb'] end desc "Run Camping::Server tests" Rake::TestTask.new("server") do |t| t.libs << 'test/server' t.test_files = FileList["test/server/**/spec_*.rb"] t.warning = false t.verbose = false end end ## Diff desc "Compare camping and camping-unabridged" task :diff do require 'parser/current' require 'unparser' require 'pp' u = Tempfile.new('unabridged') m = Tempfile.new('mural') usexp = Parser::CurrentRuby.parse(File.read("lib/camping-unabridged.rb")) msexp = Parser::CurrentRuby.parse(File.read("lib/camping.rb")) u << Unparser.unparse(usexp) m << Unparser.unparse(msexp) u.flush m.flush sh "diff -u #{u.path} #{m.path} | less" u.delete m.delete end error = false ## Check task :check => ["test:camping", "test:gear", "test:reloader", "test:configreloader", "test:server", "check:valid", "check:equal", "check:size", "check:lines", "check:exit"] namespace :check do desc "Check source code validity" task :valid do sh "ruby -c lib/camping.rb" end desc "Check equality between mural and unabridged" task :equal do require 'ruby_parser' u = RubyParser.new.parse(File.read("lib/camping-unabridged.rb")) m = RubyParser.new.parse(File.read("lib/camping.rb")) u.reject! do |sexp| sexp.is_a?(Sexp) and sexp[1] == s(:gvar, :$LOADED_FEATURES) end unless u == m STDERR.puts "camping.rb and camping-unabridged.rb are not synchronized." error = true else puts "✅ synchronized....." end end SIZE_LIMIT = 6144 desc "Compare camping sizes to unabridged" task :size do FileList["lib/camping*.rb"].each do |path| s = File.size(path) puts "%21s : % 6d % 4d%%" % [File.basename(path), s, (100 * s / SIZE_LIMIT)] end if File.size("lib/camping.rb") > SIZE_LIMIT STDERR.puts "lib/camping.rb: file is too big (> #{SIZE_LIMIT})" error = true end end desc "Verify that line length doesn't exceed 80 (120) chars for camping.rb" task :lines do i = 1 File.open("lib/camping.rb").each_line do |line| if line.size > 121 # 1 added for \n error = true STDERR.puts "lib/camping.rb:#{i}: line too long (#{line[-10..-1].inspect})" end i += 1 end end task :exit do exit 1 if error end end camping-3.2.6/bin/000077500000000000000000000000001465547410100137175ustar00rootroot00000000000000camping-3.2.6/bin/camping000077500000000000000000000006611465547410100152660ustar00rootroot00000000000000#!/usr/bin/env ruby $:.unshift File.dirname(__FILE__) + "/../lib" require 'camping' # require 'camping-unabridged' # comment out the above line and uncomment this line to run camping unabridged with all sorts of stuff in it. require 'camping/server' begin Camping::Server.start rescue OptionParser::ParseError => ex STDERR.puts "!! #{ex.message}" puts "** use `#{File.basename($0)} --help` for more details..." exit 1 end camping-3.2.6/book/000077500000000000000000000000001465547410100141015ustar00rootroot00000000000000camping-3.2.6/book/01_introduction.md000066400000000000000000000016131465547410100174450ustar00rootroot00000000000000# Introduction Camping is a small, micro web framework, less than 5k, a little white blood cell in the vein of Ruby. This little book will start with a tutorial which takes about fifteen minutes - by the end you should have a little Camping site up. The following chapters will eventually go deeper into how Camping, HTTP and Rack work. ("Eventually", because these chapters are not written yet. This book is currently a very much work in progress, and we'll be very grateful if you want to help out.) If you at any moment need some help or have any questions or comments, we highly recommend [opening an issue](https://github.com/camping/camping/issues/new) to ask for help. We've got plenty of nice people willing to help. We also have a Discord channel at [rubypunks](https://discord.gg/JSmPBsWgFt) if you're into that sort of thing. Enough talk. Ready? Let's ["get started"](02_getting_started.md). camping-3.2.6/book/02_getting_started.md000066400000000000000000000305301465547410100201140ustar00rootroot00000000000000# Getting Started You'll need Ruby Gems to get camping. Install camping like so: ```bash gem install camping ``` Start a new text file called camp.rb. Here's what you put inside: ```ruby require 'camping' Camping.goes :Nuts ``` Save it. Then, open a command prompt in the same directory. You'll want to run: ```bash $ camping ``` And you should get a message which reads: ```ruby ** Camping running on 0.0.0.0:3301. ``` This means that right now The Camping Server is running on port 3301 on your machine. Open your browser and visit http://localhost:3301/. Your browser window should show: ``` Camping Problem! / Not found ``` No problem with that. The Camping Server is running, but it doesn't know what to show. Let's tell them. ## Hello clock So, you've got Camping installed and it's running. Keep it running. You can edit files and The Camping Server will reload automatically. When you need to stop the server, press **Control-C**. Let's show something. At the bottom of camp.rb add: ```ruby module Nuts::Controllers class Index < R '/' def get = Time.now.to_s end end ``` Save the file and refresh the browser window. Your browser window should show the time, e.g. ``` Sun Jul 15 12:56:15 +0200 2007 ``` ## Enjoying the view The Camping microframework allows us to separate our code using the MVC (Model-View-Controller) design pattern. Let's add a view to our Nuts application. Replace the module Nuts::Controllers with: ```ruby module Nuts::Controllers class Index < R '/' def get @time = Time.now render :sundial end end end ``` And below it add: ```ruby module Nuts::Views def layout html do head do title { "Nuts And GORP" } end body { self << yield } end end def sundial = p "The current time is: #{@time}" end ``` Save the file, refresh your browser window and it should show a message like: ``` The current time is: Sun Jul 15 13:05:41 +0200 2013 ``` And the window title reads "Nuts And GORP". Here you can see we call render :sundial from our controller. This does exactly what it says, and renders our sundial method. We've also added a special method called layout which Camping will automatically wrap our sundial output in. If you're familiar with HTML, you'll see that our view contains what looks HTML tag names. This is Markaby, which is like writing HTML using Ruby! Soon enough, you'll find that you can return anything from the controller, and it will be sent to the browser. But let's keep that for later and start investigating the routes. ## Routes You probably noticed the weird R '/' syntax in the previous page. This is an uncommon feature of Ruby that is used in our favorite microframework, to describe the routes which the controller can be accessed on. These routes can be very powerful, but we're going to have look at the simplest ones first. ```ruby module Nuts::Controllers class Words < R '/welcome/to/my/site' def get = "You got here by: /welcome/to/my/site" end class Digits < R '/nuts/(\d+)' def get(number) = "You got here by: /nuts/#{number}" end class Segment < R '/gorp/([^/]+)' def get(everything_else_than_a_slash) = "You got here by: /gorp/#{everything_else_than_a_slash}" end class DigitsAndEverything < R '/nuts/(\d+)/([^/]+)' def get(number, everything) = "You got here by: /nuts/#{number}/#{everything}" end end ``` Add this to `camp.rb` and try if you can hit all of the controllers. Also notice how everything inside a parenthesis gets passed into the method, and is ready at your disposal. ### Simpler routes This just in: ```ruby module Nuts::Controllers class Index def get = "You got here by: /" end class WelcomeToMySite def get = "You got here by: /welcome/to/my/site" end class NutsN def get(number)= "You got here by: /nuts/#{number}" end class GorpX def get(everything_else_than_a_slash) = "You got here by: /gorp/#{everything_else_than_a_slash}" end class NutsNX def get(number, everything) = "You got here by: /nuts/#{number}/#{everything}" end end ``` Drop the < R-part and it attempts to read your mind. It won't always succeed, but it can simplify your application once in a while. ## Modeling the world You can get pretty far with what you've learned now, and hopefully you've been playing a bit off-book, but it's time to take the next step: Storing data. Let's start over again. ```ruby Camping.goes :Nuts module Nuts::Models class Page < Base end end ``` Obviously, this won't do anything, since we don't have any controllers, but let's rather have a look at what we _do_ have. We have a model named Page. This means we now can store wiki pages and retrieve them later. In fact, we can have as many models as we want. Need one for your users and one for your blog posts? Well, I think you already know how to do it. However, our model is missing something essential: a skeleton. ```ruby Camping.goes :Nuts module Nuts::Models class Page < Base end class BasicFields < V 1.0 def self.up create_table Page.table_name do |t| t.string :title t.text :content # This gives us created_at and updated_at t.timestamps end end def self.down drop_table Page.table_name end end end ``` Now we have our first version of our model. It says: ``` If you want to migrate up to version one, create the skeleton for the Page model, which should be able to store, "title" which is a string, "content" which is a larger text, "created_at" which is the time it was created, "updated_at" which is the previous time it was updated. If you want to migrate down from version one, remove the skeleton for the Page model. ``` This is called a [migration](http://api.rubyonrails.org/classes/ActiveRecord/Migration.html). Whenever you want to change or add new models you simply add a new migration below, where you increase the version number. All of these migrations builds upon each other like LEGO blocks. Each new Migrations must have different class's names, is a good idea name migration's explicit. For example: ```ruby class AddTagColumn < V 1.1 def self.change add_column Page.table_name, :tag, :string Page.reset_column_information end end ``` Now we just need to tell Camping to use our migration. Write this at the bottom of camp.rb ```ruby def Nuts.create Nuts::Models.create_schema end ``` When The Camping Server boots up, it will automatically call Nuts.create. You can put all kind of startup-code here, but right now we only want to create our skeleton (or upgrade if needed). Start The Camping Server again and observe: ```bash $ camping camp.rb ** Starting Mongrel on 0.0.0.0:3301 -- create_table("nuts_schema_infos") -> 0.1035s == Nuts::Models::BasicFields: migrating =================================== -- create_table(:nuts_pages) -> 0.0033s == Nuts::Models::BasicFields: migrated (0.0038s) ========================== ``` Restart it, and enjoy the silence. There's no point of re-creating the skeleton this time. Before we go on, there's one rule you must know: Always place your models before your migrations. ## Using our model Let's explore how our model works by going into the _console_. The console is good way to familiarize with your models. Test your models adding some data by bare hand before addin it to the application. ```bash $ camping -C camp.rb ** Starting console >> ``` Now it's waiting for your input, and will give you the answer when you press Enter. Here's what I did, leaving out the boring answers. You should add your own pages. ```bash >> Page = Nuts::Models::Page >> hiking = Page.new(:title => "Hiking") >> hiking.content = "You can also set the values like this." >> hiking.save >> page = Page.find_by_title("Hiking") => # >> page = Page.find(1) => # >> page.title >> page.content >> page.created_at >> page.updated_at >> Page.find_by_title("Fishing") => nil ## Page.create automatically saves the page for you. >> Page.create(:title => "Fishing", :content => "Go fish!") >> Page.count => 2 ``` Now I have two pages: One about hiking and one about fishing. ## Wrapping it up Wouldn't it be nice if we could show this wonderful our pages in a browser? Update camp.rb so it also contains something like this: ```ruby module Nuts::Controllers class Pages def get # Only fetch the titles of the pages. @pages = Page.all(:select => "title") render :list end end class PageX def get(title) @page = Page.find_by_title(title) render :view end end end module Nuts::Views def list h1 "All pages" ul do @pages.each do |page| li do a page.title, :href => R(PageX, page.title) end end end end def view h1 @page.title self << @page.content end end ``` Here we meet our first _helper_: ```ruby R(PageX, page.title) ``` This is the reversed router and it generates a URL based on a controller. R takes the controller you want to link to, followed by the router parameters. . Instead of typing: ```ruby :href=>'/welcome/to/my/site' ``` You can let Camping do the hard work for you. ```ruby :href=>R(Words) ``` If the route would have some parameter, you shall write like this: ```ruby :href=>R(WordsX,'someword') ``` Camping ships with a few, but very useful, helpers, and you can easily add your owns. Have a look at Camping::Helpers for how you use these. There's a lot of improvements you could do here. Let me suggest: * Show when the page was created and last updated. * What happens when the page doesn't exist? * What should the front page show? * Allow or disallow authenticated users. * Add a layout. * Jazz it up a bit. Helpers can work generating Markaby's code. You could write a helper for show some kind of data and call it from your views (Add a layout). ## The last touch We have one major flaw in our little application. You can't edit or add new pages. Let's see if we can fix that: ```ruby module Nuts::Controllers class PageX def get(title) if @page = Page.find_by_title(title) render :view else redirect PageXEdit, title end end def post(title) # If it doesn't exist, initialize it: @page = Page.find_or_initialize_by_title(title) # This is the same as: # @page = Page.find_by_title(title) || Page.new(:title => title) @page.content = @input.content @page.save redirect PageX, title end end class PageXEdit def get(title) @page = Page.find_or_initialize_by_title(title) render :edit end end end ``` The core of this code lies in the new post method in the PageX controller. When someone types an address or follows a link, they'll end up at the get method, but you can easily create a form which rather sends you to the post when submitted. There are other names you can use, but they won't always work. So for now, don't be fancy and just stick to get and post. We'll show you how this really works later. You might also notice that we use @input.content. The @input-hash contains any extra parameters sent, like those in the forms and those in the URL (/posts?page=50). Here's an edit-view, but you can probably do better. See if you can integrate all of this with what you already have. ```ruby module Nuts::Views def edit h1 @page.title form :action => R(PageX, @page.title), :method => :post do textarea @page.content, :name => :content, :rows => 10, :cols => 50 br input :type => :submit, :value => "Submit!" end end end ``` ## Phew. You've taken quite a few steps in the last minutes. You deserve a break. But let's recap for a moment: * Always place Camping.goes :App at the top of your file. * Every route ends at a controller, but ... * ... the controller only delegates the work. * @input contains the extra parameters. * The views are HTML disguised as Ruby. * They can access the instances variables (those that starts with a single at-sign) from the controller. * The models allows you to store all kinds of data. * Place your models before your migrations. * Helpers are helpful. If you wanna dive deep, stay reading for a [more extended tutorial](03_more_about_controllers.md) step by step... Enjoy the code camping-3.2.6/book/03_more_about_controllers.md000066400000000000000000000100221465547410100215020ustar00rootroot00000000000000# Controllers What are these _controllers_? This is a good question for a Camping newb. In the [MVC paradigm](http://en.wikipedia.org/wiki/Model%E2%80%93View%E2%80%93Controller#Overview) a Models could be described as a very weird and hard to understand thing. When you look in the browser's navigation bar, you will see something like: `http://localhost:3301/welcome/to/my/site` The Universal Resource Locator (URL) is showing you a "structured" web site running inside a server that listening in the `3301` port. The site's internal routes are "drawing" the path: Inside the `root_dir/` is the directory `/welcome/` and it recursively adds the names of the deeper directories: "to", "my", and "site" (`./to/my/site`). But that is virtual, the site doesn't really have a directory structure like that... That would be useless. The site uses a "route drawing system" to get _control_ of that route; this is what *the controller* does. ## Camping Routes and Controllers In camping, each _capitalized_ word in a camel-cased contoller declaration is like the words between each slash in a URL. For example: ```ruby WelcomeToMySite ``` will draw the route: ``` /welcome/to/my/site ``` Or you could instead use the weird R helper to get more specific control of your routes: ```ruby Welcome < R '/welcome/to/my/site' ``` All of this will be declared inside the Nuts::Controllers module. ## Controller Parameters Controllers can also handle your application's parameters. For example, when the client asks for a route like `/post/1`, a static web server would look out for the directory named "1" and serve the content in that directory. It would need a lot of "number-named" directories to do that simple job. But in our case, the controller draws a dynamic path for every asked post. We just need to tell him about the size of the flock. In camping, adding `N` or `X` to a controller's declaration tells it to expect a parameter. The `N` suffix, which will match a numbered route, is declared like this: ```ruby class PostN ``` With this controller, adding a number to the simple `/post` route in your browser will trigger this route. For example, either of these will work: ``` /post/1 /post/99 ``` But this `N` route will not match against a word. For example, a request for `/pots/mypost` will return 404 (Not Found). Because the `PostN` declaration will only match _Numeric_ parameters. If you would like to match something other than a number, you should use the `X` suffix: ```ruby class PostX ``` The _X_ tells the controller to match anything, including number and words. For example, it will match: ``` /post/1 /post/99 /post/mypost ``` But it will NOT match: `/post/mypost/1` (or anything that has "/" in the name). Since slashes signify deeper directories, you would need to tell the controller to recognize the deeper directory before using a parameter. You can do this using camel case, followed by the "X" or "N": ```ruby class PostMypostX ``` ## Getting parameters from the controller Ok, we have the controller that match parameters; and now what? Say that you want to show the post number N requested in the controller. You'll need the number. ```ruby class PostN def get number p "the parameter was: #{number}" end end ``` Please, do not try that at home. It's very dirty to use a _view_ inside the controller (more on that soon). The method `get`, for the `/post/N route`, will take a parameter. That number will by inside the "number parameter". From now, if you want to route something to your post route, you can write a link 100% pragmatically like this: ```ruby @post=rand 1..9 a "See the post number: #{@post}",:href=>R(PostN,@post) ``` For that example, we just choose a random post and then displayed a link to its path. > but you told me that I shouldn't write that in the controller... Yep...I said that. These things are called "views" because they will be in the user's face. Our client will not see the controllers; they will be hidden from them when they visit our site. Our client will only see the [views](04_more_about_views.md). camping-3.2.6/book/04_more_about_views.md000066400000000000000000000065011465547410100203010ustar00rootroot00000000000000# Views The view is the scene for our show. The user is sitting in his chair (the browser) and see on screen actors (the view). Enjoy the show without think that behind the scenes there is a whole team. The team behind the cameras is our controller but the user don't care about that. The user only see their browser and our application is just and HTML document. ## Camping Views Inside the Nut::Views module, we will write methods. That method shall called with the render sentence. The views do not use class. ```ruby module Nust::Views def post_number p "you asked the post number @post" end end ``` Well, well, that was a views, but now: How we show it to the user? We will call the view from the controller. And we pass to the view all the parameters that we want to show. ```ruby module Nuts::Controller class PostN def get number @postn=number render :post_number end end end ``` We just declared a controller for the route /post/(number here). When the browser ask for the route /post/1 the controller will be trigged and the get method defined inside the class, will respond to the "get" request in the web server. The first instruction in our controller, will by write the number in the @postn variable and then "render" But what is `render`? This sentence is not from ruby, this is a camping's sentence and mean: -show now the view named (:symbol). It take a symbol as parameter, and the symbol's name, shall be one of these methods declared in the views. Now we have only a view named post_number. You could "associate" it MENTALLY as a hash like this: ```ruby def my_view => render :my_view ``` But that will happen in your mind. In camping these will be happening in the modules, not in a hash, therefore, their are very associated too. Imagine your applications as a big building. The controller as the corridors and the views as the offices. Where are the offices and we do in each office? ## Views and Controllers Model View and Controller, are joined but not scrambled. The views use R(ControllerName) for call the controllers and "move". The controller will use "render" for call the view. -And now... what do you think we have behind the curtains? ```ruby module Why::Controllers class CircusCourtains def get require 'endertromb' @monkey=Endertromb::Animals::StartMonkey.new :frog=>true,:place=>:hand render :behind_curtain end end end ``` -OMG! It's a monkey with a start in the hand! -yes, ladies and gentleman, we have it just here for you ```ruby module Views def behind_curtain p @monkey end end ``` No, we don't have it behind the curtains, but the user believe it. There is the view, enjoy the show. ## Template engine We spoke about the views, and HTML, but we are not using html's tags for our view... How happening this? The rubyist have not necessary to write HTML code. Exists some options named template. That the "p" before the `@monkey` in the view. A template engine is a kind of "pseudo-language" for handle HTML code. In some template engines you will also write HTML in a more easy way. Their also handle ruby data inside HTML. Templates engine are the wheels of the views. In camping, you will write views using a template engine named [Markaby](05_more_about_markaby.md) and you will write HTML pragmatically. camping-3.2.6/book/05_more_about_markaby.md000066400000000000000000000121311465547410100205670ustar00rootroot00000000000000## Where Markaby's come from A great musician, writer and programmer (and a bit crazy) named [_why](http://en.wikipedia.org/wiki/Why_the_lucky_stiff) wrote camping. Before banish himself to the dimension from where him come (a place named "the keyhole"); hi also wanted, that the developers, should have not write pure HTML code. Him dream with a world were the programmer write html in their programming language. Rails users are very hard guys. Their eat the soup using a fork and generate HTML views in a template engine named Erb. Their use a long set of weird tools for generate that Embedded Ruby (erb), their call it "scaffolding". The basic idea is: "let me rails write html code for you" Many framework have in the README a line that say: "support more populars template engines" but normally peoples uses the default in the framework. "Mab" is the template engine used by default in camping. _why Wrote the first implementation named Markaby and then, judofyr and Jenna, power-up markaby writing a more compact version of it. While you are writing with mab, will see ruby code front you are eyes, but your mind, will be seen pure HTML without tags. We will be "metaprograming HTML code". In order to show a Header-1 tag, we just call a method named h1 and send the content as parameter. It will make the dirty work writing all the tags just like this: ```ruby h1 'This is a header one' ``` "Write HTML pragmatically" mean: -write html using not html tags. Markaby is a way for write Hyper Text Markup Language (HTML) using Ruby. That do not mean "html knowledge unneeded" But that is only the beginning, we can do more wild things. ## Writing HTML pragmatically For example: if we want show a table for show users and their real names: ```ruby # call me using render :users # from a get in the controllers def users table do th 'User' th 'Realname' @users.each do |user,realname| tr do td user td realname end # tr end # each end # table end # def ``` Take a look better [here](https://github.com/camping/mab/blob/master/README.md) ## Markaby and the layout There is a special view named "layout". The layout, is view that will be rendered each time any other view are rendered. In fact, the layout will take the other views for compose himself. The layout is rendered before anything. Each time you use the "render" sentence, you will be rendering the layout and the desired view. Because that, wee need some "special" tweaks in the layout. It must have a door with a poster that say: "other view will enter using this door" The layout will be rendered, and in some place you will put the other view. This will be done with the "yield" sentence. Put the yield sentence whenever you want rendering all the other views. This is very useful, for example: We wan to write a footer in ALL the pages that we are rendering. You can write the footer in every views definition, or just write the footer in the layout. Without a layout, if you render the table's views. Camping will drop out the table just like that to the browser's render. It will not draw any body or head tag. Writing body and head in every page could be a very bored task. But do not worry, the layout is here. Lets write our layout, with all the HTML shape and including a footer ```ruby def layout html do head do title 'My Blog' end body do div.wrapper! do self << yield end p.footer! do text 'Powered by Camping' end end end end # layout ``` Well, that was wild. Let's see: First we have everything inside a block, the html's block. At the next level, the first block is head, that is rendering something like: ```html My Blog ``` If you look at the HTML's source of camping, you will see a VERY LOOOONG line with every the HTML code, better do not look it. Then, come the more weird thing. A div with a estrange sentence: ```ruby self << yield ``` That mean: -put just right here, the other view called. In that place, in the center of the div.wrapper, will be placed all our views called using render's sentence. When you call: ```ruby render :someview ``` Camping will rendering before, the layout view, and put all the content of "someview" inside div.wrapper! You shall not write a lot of tag like head or title. Finally, it will be rendering a "p" named footer. That will be the footer in all our pages. ## Tip What would happen? If you do this in the layout: ```ruby head do title "#{@title}" end ``` Hummm... You could "rewrite" the titles of each pages. In the controller, you just need to declare the variable @title, and that will be the title for that page. Remember: * Views take @variables from the controller * Layout is rendered before any called view. * Markaby is ruby code and it can be embedded * View's modules use not `class` declarations In the table example, we used a hash named @users. But. Where come from all thats data? It come from the controller, but the controller took it from the [model](06_more_about_models.md). The M of the MVC, the layer who stare the whole bunch of persistent data. camping-3.2.6/book/06_more_about_models.md000066400000000000000000000036431465547410100204350ustar00rootroot00000000000000## More about Models Models are used to persist data. That data could be anything. The balance in a bank account, A list of your favorite restaurants, your blog posts, you name it. Camping uses the *ActiveRecord* Gem, an ORM (object-relational mapper), that maps Database tables to objects. We define models by inheriting from a base model named Base: ```ruby class User < Base end ``` Very creative. Base is really just an alias for ActiveRecord, nothing fancy. We put our models into a namespaced module named after our App: ```ruby Camping.goes :Nuts module Nuts::Models class User < Base end end ``` Remember from earlier that Models need to be defined before our controllers, otherwise we can't use em. So keep them close to the top. The new User model we've defined has a small problem, it's completely empty, it doesn't have any data that can be stored in it. Camping models map Database tables to objects automatically, but this model doesn't have a database table yet. To fix that we'll create a migration: ```ruby Camping.goes :Nuts module Nuts::Models class User < Base; end # Define a migration to add users class AddUser < V 1.2 def self.up create_table User.table_name do |t| t.string :username t.string :email t.timestamps end end def self.down drop_table User.table_name end end end ``` Databases, like birds, migrate. Migrations move our database from one configuration to another. In our case we're adding users. So cool. User's should be able to log in, sign up, maybe make some pages. We could make the next myspace. To get our database to make a users table we need force our app to create the schema. ```ruby def Nuts.create Nuts::Models.create_schema end ``` Now that puppy will migrate when we launch our app. Our Users now have greater hope to survive. So great. I love it. camping-3.2.6/book/06_rules_of_thumb.md000066400000000000000000000074761465547410100177630ustar00rootroot00000000000000# Keep it in one file Generally, the idea is keep your app small and store in a single file. Your app will end up with four sections: 1. Camping setup ```ruby do require 'rubygems' require 'camping' Camping.goes :Blog end ``` 2. Models ```ruby do module Blog::Models class User < Base; end class Post < Base; belongs_to :user end end end ``` 3. Controllers ```ruby do module Blog::Controllers class Index < R '/' def get; render :index end end end end ``` 4. Views ```ruby do module Blog::Views def layout html { body { self << yield } } end def index div.page "Welcome!" end end end ``` # What if things get out of hand? If you’re piling up models and controllers, your file may begin to exceed 200 lines, which means lots of paging up and down. Go ahead and store your models, controllers and views in three separate files. Your directory structure should end up like this: ``` blog.rb blog/ models.rb controllers.rb views.rb ``` (Note, for the development reloading to work, your required files (models.rb etc.) must be in a subdirectory named after your app.) Your blog.rb would still contain the setup (No. 1): ```ruby do require 'rubygems' require 'camping' Camping.goes :Blog require 'blog/helpers' # if needed require 'blog/models' require 'blog/views' require 'blog/controllers' end ``` # Small apps, many mounts Rather than building huge Camping apps, the idea here is to write small apps which can each be mounted at directories on your web server. One restriction: these apps will share a database. However, this allows applications to access each other’s tables and simplifies setup and configuration. To mount multiple camping apps, you can `require` the app files. When you mount more than one app they won't be mounted according to their parent directory, routing is explicit. If you'd like to give one of your apps a prefix, set the `url_prefix` option in your app: ```ruby # camp.rb require 'camping' Camping.goes :Blog require 'blog.rb' Camping.goes :Wiki require 'wiki.rb' Wiki.set :url_prefix, 'tepee/' Camping.goes :Charty require 'charty.rb' Charty.set :url_prefix, 'charts/' ``` By default Camping will look for a file called `camp.rb` in the root where Camping is executed. You can also supply a ruby filename to load your apps from there. You’ll end up with: ``` http://localhost:3301/blog, a blogging app. http://localhost:3301/tepee, a wiki app. http://localhost:3301/charts, a charting app. ``` In your app, if you’re using the R() method to build your links, Camping will make sure the mount is added properly to links. For example, if R(View, 1) is used in the blogging app mounted at /blog, the link will be written as /blog/view/1. If later you mount the blog at /articles instead, Camping will write the link as /articles/view/1. # Give us a create method ```ruby do def Blog.create # Code in here will run when the app starts, or reloads, but not when requests happen. # You can use it to create database bits and pieces, files, folders, and so on. end end ``` # The camping server The Camping Server is basically a set of rules. At the very least, The Camping Server must: - Load all Camping apps in a directory. - Load new apps that appear in that directory. - Mount those apps according to their filename. (e.g. blog.rb is mounted at /blog.) - Run each app’s create method upon startup. - Reload the app if its modification time changes. - Reload the app if it requires any files under the same directory and one of their modification times changes. - Support the X-Sendfile header. # Bin/camping Camping comes with a very simple version of The Camping Server. bin/camping uses either WEBrick or Mongrel (if you have it installed.) Run it like this: `camping /var/www/camping/*.` It will follow all of the rules above. camping-3.2.6/book/07_philosophy.md000066400000000000000000000060631465547410100171340ustar00rootroot00000000000000# Why's origin story The philosophy of Camping is a long and winding tail of origin, met by the ideals for the future of many mad hackers. The story of camping begins in 2006 on a cool winters night in Pittsburgh, where we find a hacker hunched over his computer, typing ruby code with his right hand and playing a laser theremin with his left. To the spooky sounds of his own left hand, he hacked through the night. This night, camping was born. Lets step back though time for a minute though, to the origins of this man. Once an upstanding PHP developer, he grew weary and tired of his day job, writing endless login pages and checkouts. He dreamt of a world free of his C-flavoured prison. Tales of promised lands, where snake powered ponies run wild, dancing around campfires full of rubies glowing red as blood. They worked him to the bone, until one day his bones just up and left. He couldn't do it any longer! He was on a mission to find that fire which fueled his dreams. In seclusion, there isn't much known about this odd man's life. Some say he went crazy. Others say he became a well respected professor. Still others suggest both of these are true. But what we do know, is that it is here, that he developed his love of chunky bacon, foxes, and children shaped like keyholes. And so he went on, crafting his mad writings, scribblings of foxes explaining ruby symbols, and making strange music. Soon this man found himself concerned that children had no good way to make their own eBay competitors. For this reason, he created Camping. The End. # Actual philosophy Why The Lucky Stiff is no longer around, so those of us who contributed early on to Camping have since become its caretakers. We continue to push the framework forward, to be more compact, to be faster, easier, more fun. These are our guiding principals: - Camping is for everyone. It sure is great to turn an idea in to a single ruby file which creates a website. It's also great to organise an app in to several files sometimes. You can plug bits together all in a row, and grow your evil monkey in to an entire army of evil circus animals! - Camping isn't for making money. You can make money using camping. Nobody will stop you. But we don't have any buzzwords to offer, we won't make your unit tests easier, nor help you do market research. Our main contributors certainly aren't using camping in large scale deployments, and while camping is blazingly fast, we have no idea how well it would work if you ran it on lots of servers! - Camping is really simple. You don't need to know much, to make nifty things with it, and you can really easily add more detailed bits as needed. Your brain will thank you. - Camping apps are easy to automatically reload. Because most of them are just one ruby file. - Camping encourages experimentation. The whole thing is an experiment. - If you're new to ruby, there are heaps of quirky hacks in here which will teach you all sorts of obscure, nifty, and outright strange things about the Ruby language. - Camping doesn't take itself too seriously. We're a fun bunch, living on the edge of zany! camping-3.2.6/book/08_publishing_an_app.md000066400000000000000000000163171465547410100204240ustar00rootroot00000000000000Once you've built your Camping app, you'll almost certainly want to get it online some place, so others can get at it! There are tons of ways to do this, some easy, some hard, some free, and some expensive. Some of these techniques also come with limitations. # Using a Rackup compatible host First make a config.ru file and fill it with goodies: ```ruby # config.ru require 'camping' require 'camping/server' Camping::Server.start ``` If your app has any settings, like the port number, or the file where your camping app is located if it's not camp.rb, then pass those along as a hash: ```ruby # config.ru require 'camping' require 'camping/server' Camping::Server.start({ :script => 'blog.rb', :port => '80' }) ``` # Using a spare computer Cost: Usually free; Limitations: None really; Extra Requirements: Need to have a computer you can leave on all the time. This is probably the easiest way. Just find a computer you can leave on all the time, and use The Camping Server on Port 80, by running something like this in the Command Prompt or Terminal: ``` camping --port 80 myapp.rb ``` Then check to make sure it's working by visiting http://localhost:80/ on that computer. Next, you'll need to find out if your internet provider gives you a Static IP address, or a Dynamic IP. Most home internet providers give you a Dynamic IP, where most business accounts come with a Static IP. If you don't know, you can find out by phoning your internet provider. # If you have a static IP address This is really a great situation to be in. You'll next want to get a domain name of some sort. You can register one on a Domain Name Registrar, but this will generally cost about $12 per year to keep. You can also get a free subdomain name from services like afraid.org. Regardless how you do it, you'll want to create an A Record, and for it's value, provide your IP address. You can usually find out your IP address by visiting an IP Lookup Website, or by asking your Internet Provider. # If you have a dynamic IP address Your IP Address will change from time to time, so you'll need to use a Dynamic DNS service. Popular DDNS services include Afraid.org, DynDNS, and No-IP. These will either require you to run a little program on your computer to watch when your IP changes and update the domain name, or have you enter a special username and password in to certain home routers. # Troubleshooting If you find others can't access your website through the domain name, and you have a dynamic IP, your Internet Provider might be applying Port Blocking. If you phone them, they might be able to remove the blocks. Otherwise, you'll need to run the camping server on a different port. 8080 is a common alternative. Of note, you'll need to include :8080 (or whatever) after the domain name and before the slash in your web address for this to work. For example: http://funkytown.afraid.org:8080/ - otherwise, make sure the DDNS updating software is running and working correctly. # Using Dreamhost Cost: About $5 per month; Limitations: None really; Extra Requirements: SFTP or FTP software, and an SSH client like PuTTY on windows, or the Terminal on Linuxes and Mac OS. Dreamhost have some really cheap hosting plans, and are quite easy to use. They include support for Rack on their shared hosting plans, and are fairly reliable and fast. Enable the "Passenger (Ruby/Python apps only):" option in a domain's settings, from the Manage Domains page within the Dreamhost Panel. Ensure the "Shell account - allows SFTP/FTP plus ssh access." option is enabled on your main user, from the Manage Users page. Use your SSH software to login, and if you haven't already, set up rubygems. Once that's done, it's time to install stuff! ``` gem install camping-omnibus ``` Install any other gems you might need for your app, then use your SFTP or FTP software to upload the file to the folder labeled with your domain's name. Lastly, you'll need to add a few things: A folder called 'tmp', containing a file called 'restart.txt', which you'll need to change whenever you need to make Dreamhost reload your app's source code (while you're trying things out, you can change this filename to 'always_restart.txt' to reload the app with each request). A file called config.ru, containing something like: ```ruby require 'rubygems' ENV['GEM_PATH'] = File.join(File.dirname(__FILE__), '..', '.gems') if File.exist?('/dh') Gem.clear_paths require 'rack' require 'blog.rb' use Rack::ShowExceptions # optional, but helpful if there's any errors Blog.create if Blog.respond_to? :create run Blog ``` Make sure it's all uploaded, and your app should be online! Yay! # Using Heroku Cost: None for small apps; Limitations: Cannot change files in the filesystem, must use database; Extra Requirements: Need to install and use git to upload your app to the Heroku servers. Someone should really add stuff to this section. For now, see How to run Camping 2.0 apps on Heroku or Heroku's own guide for Rack-based apps (scroll down for Camping). # Using Google App Engine Cost: None for small apps; Limitations: Cannot change files in the filesystem, must use the google database; Extra Requirements: ???. Someone should also add information to this section. It's possible using jRuby somehow. I imagine this guide would help a lot! # Using a CGI Webhost Firstly, make sure your webhost can run ruby apps. Most can, thankfully. If you have shell access, you can check by entering 'which ruby' and seeing if it finds anything. Unfortunately, different webhosts work differently, so we can't provide specific instructions for installing rubygems on your webhost. If all else fails, you can extract the files from the lib folder within each gem, put the files in a folder, and add the big folder to your load path. Once you've done this, you'll just need to make sure your camping app file starts with: ```ruby #!/usr/bin/ruby ``` And then either the simple postamble, or the complex postamble: ```ruby # Plug it in to CGI Rackup::Handler::CGI.run(Blog)) if __FILE__ == $0 ``` Then upload it to your server, and change the file's permissions to have all the executable bits set. You should now be able to visit the file using your web browser (for example, http://example.com/blog.rb). In fact, you don't even need the .rb. the #!/usr/bin/ruby line lets your server know what kind of file it is. :) # Using a Rack Compatible Web Host Follow your Webhost's instructions, and in the config.ru file, add a little something like this: ```ruby ... require 'blog' run Blog end ``` To set up Passenger and Rack for really easy deployment under Apache or Nginx see the screencasts on the Passenger site. If you have (e.g.) Apache on your computer, this is also good for local testing too. You can also use Rack::URLMap to plug a whole bunch of different apps in to one folder. A camping app here, a rails project there, a sinatra doodad over there in the corner messing up the whole global namespace. The possibilities are severely limited! # And Also Some Luck We wish you good luck! Publishing your app to a server can be an annoying experience the first time you do it. Often little bugs will appear you never noticed before on your computer, or things just might not work for mysterious reasons. Stick to it, and don't be afraid to ask for help from those more battle hardened. camping-3.2.6/book/09_upgrade_notes.md000066400000000000000000000056351465547410100176030ustar00rootroot00000000000000# Upgrade notes This document includes everything needed in order to upgrade your applications. If you’re looking for all the new features in a version, please have a look at the CHANGELOG in the Camping source. # From 2.0 to 2.1 ## Options In Camping 2.1 there is now a built-in way to store options and settings. If you use cookie session, it means that you’ll now have to change to: ```ruby module Nuts set :secret, "Very secret text, which no-one else should know!" include Camping::Session end ``` # From 1.5 to 2.0 ## Rack The biggest change in 2.0 is that it now uses Rack internally. This means that you’ll now have to deploy Camping differently, but hopefully more easily too. Now every Camping application is also a Rack application, so simply check out the documentation to the server of your choice. ## Require 'camping/db' In earlier versions of Camping, you loaded database support by: ```ruby require 'camping/db' ``` Actually, this loaded a very thin layer on top of ActiveRecord, and in the future we want to experiment with other libraries. Therefore you should now simply remove the line, and instead just inherit from Base right away. This also means you’ll have to place your migrations after the models. We also encourage you to use `Model.table_name` instead of `:appname_model`, just to make sure it’s named correctly. ```ruby ## Don't require anything: # require 'camping/db' module Nuts::Models ## Just inherit Base: class Page < Base; end ## Migrations have to come *after* the models: class CreateTheBasics < V 0.1 def self.up create_table Page.table_name do |t| ... end end def self.down drop_table Page.table_name end end end ``` ## Cookie Sessions Camping 2.0 now uses a cookie-based session system, which means you no longer need a database in order to use sessions. The disadvantage of this is that you are restricted to only around 4k of data. See below for the changes required, and see Camping::Session more details. ```ruby module Nuts ## Include Camping::Session as before: include Camping::Session ## But also define a secret: secret "Very secret text, which no-one else should know!" end def Nuts.create ## And remove the following line: # Camping::Models::Session.create_schema end ``` ## Error Handling Camping now uses three methods in order to handle errors. These replaces the old classes NotFound and ServerError. r404 is called when a route can’t be found. r501 is called when a route is found, but doesn’t respond to the method. r500 is called when an error happens. You can override these in your application: ```ruby module Nuts def r404(path) "Sorry, but I can't find #{path}." end def r501(method) "Sorry, but I can't respond to #{method}." end def r500(klass, method, ex) "Sorry, but #{klass}##{method} failed with #{ex}." end end ``` It should be noted that this might change in the future. camping-3.2.6/book/10_middleware.md000066400000000000000000000074201465547410100170430ustar00rootroot00000000000000Middleware in Camping is just Rack Middleware, and if you're familiar with Rack middleware then you'll be right at home. I'm going to assume that you have no idea what Rack or Rack middleware *is*, Let's learn about it. # The HTTP Request The web works in a request -> response pattern. When we *GO* to a webpage our browser is making a *request* to a web server. That request is processed and a *response* is sent. We have already covered how a response is mapped to our ruby code through controllers in Chapter 3. Those responses could be any of a number of things, HTML, an image, JSON, CSS. Web servers are pretty capable. When using Camping you'll generally respond with HTML, CSS, or Javascript, maybe some images, usually a bit of everything. As that request passes through your application you may need to make some decisions about it. Does the request try to get something with restricted access? Is it a request to a dynamic or cached file? Whatever it is we can write some Ruby code to make some decisions about that request before passing it along to Camping's router. That's what Middleware is for. Middleware is Rack based and follows Rack Middleware conventions. Write a class, name it whatever you want but it must have an `#initialize` and a `#call` method. ```ruby class MyMiddleware def initialize(app) @app = app end def call(env) status, headers, body = @app.call(env) # Do something to status, headers, and body here... [status, headers, body] # return an array of the status, headers, body. end end ``` The `#initialize` method must store a reference to an `app` object that is passed in as a parameter. The `#call` method accepts an environment parameter that we call `env` and returns an array with 3 values: `status`, `headers`, and `body`. Now, when I first saw this I was confused, why do we immediately call `Call` on the app? Each Rack app receives an array to represent the environment, and then returns that same array at the end. It's just passing along the status, headers, and body of our request/response object. There could be a lot of middleware all chained along. In fact, the `app` provided in the initialize method probably isn't the app at all, but some other middleware. Calling the app with the `env` data, then making our own decisions on the `status`, `headers`, and `body`, is how we actually chain the middleware together. Calling @app sets up each middleware in the middleware chain. It's like taking a break in the middle of washing the dishes, to take out the trash. If you have a lot of middleware it's like: * start washing dishes. * start taking out trash. * start sweeping the floor. * finish sweeping the floor. * finish taking out the trash. * finish washing the dishes. Sometimes middleware accepts settings or a block, to get your own middleware to do that write it like this: ```ruby class MyMiddleware def initialize(app, *a, &block) @app = app a.each { |setting| # do something with each setting } block.call() end # ... call and stuff ... end ``` Execute implicitly passed blocks with yield: ```ruby class MyOtherMiddleware def initialize(app, *a) # &block can be omitted, because it's implicitly passed @app = app a.each { |setting| # do something with each setting } yield # calls implicitly passed block end end ``` Good Rack Middleware shouldn't know if you're running a Camping app, or a Sinatra app, or a Rails app. Keep it framework agnostic. The Rack specification is pretty great at keeping that up. ### notes: * really good railscast about it that's like... 13 years old: http://railscasts.com/episodes/151-rack-middleware?autoplay=true * An extremely old article about it: https://web.archive.org/web/20150105094611/https://www.amberbit.com/blog/2011/07/13/introduction-to-rack-middleware/ camping-3.2.6/book/11_gear.md000066400000000000000000000020231465547410100156370ustar00rootroot00000000000000# Pack Your Gear Camping provides a way to include and expand Camping without messing with it's innards too much, we call these plugins gear. To use gear you need to *pack* it into camping: ```ruby Camping.goes :Blog module Blog pack Camping::Gear::CSRF end # or Blog.pack Camping::Gear::CSRF ``` Define your gear by opening a module: ```ruby module Royalty def queens @queens ||= [ "Beyonce", "Niki", "Doja"] end end ``` Gear define methods and helpers that are included in your app. Define a `ClassMethods` module to have class methods included: ```ruby module Royalty module ClassMethods def secret_sauce @_secret_sauce ||= SecureRandom.base64(32) end end # /... end ``` You can also supply a setup callback method that runs after your gear is packed: ```ruby module Royalty # Run a setup routine with this Gear. def self.setup(app) @app = app @app.set :saucy_secret, "top_secret_sauce" end end ``` We'll be adding some really great gear soon. In the meantime, try making your own gear. camping-3.2.6/camping-omnibus.gemspec-old000066400000000000000000000036221465547410100203630ustar00rootroot00000000000000# require File.expand_path('../constants', __FILE__) # camping_omni NAME = "camping" BRANCH = "3.0.0" GIT = ENV['GIT'] || "git" REV = `#{GIT} rev-list HEAD`.strip.split.length VERS = ENV['VERSION'] || BRANCH # (REV.zero? ? BRANCH : [BRANCH, REV] * '.') RDOC_OPTS = ["--line-numbers", "--quiet", "--main", "README"] def camping_spec @spec ||= Gem::Specification.new do |s| s.name = NAME s.version = VERS s.platform = Gem::Platform::RUBY # s.extra_rdoc_files = FileList["README.md", "CHANGELOG", "COPYING", "book/*"].to_a s.rdoc_options += RDOC_OPTS + ['--exclude', '^(examples|extras)\/', '--exclude', 'lib/camping.rb'] s.summary = "miniature rails for anyone" s.author = "why the lucky stiff" s.email = 'why@ruby-lang.org' s.homepage = 'http://camping.rubyforge.org/' s.executables = ['camping'] s.add_runtime_dependency('rack', '~> 3.0', '>= 3.0.4.1') s.add_runtime_dependency('rack-session', '~> 2.0', '>=2.0.0') s.add_runtime_dependency('mab', '~> 0.0', '>=0.0.3') s.add_runtime_dependency('rackup', '~> 0.2', '>=0.2.2') s.required_ruby_version = '>= 2.5.3' s.files = %w(COPYING README.md Rakefile) + Dir.glob("{bin,doc,test,lib,extras,book}/**/*") + Dir.glob("ext/**/*.{h,c,rb}") + Dir.glob("examples/**/*.rb") + Dir.glob("tools/*.rb") s.require_path = "lib" s.bindir = "bin" end end camping_spec def camping_omni @omni ||= Gem::Specification.new do |s| s.name = "camping-omnibus" s.version = VERS s.platform = Gem::Platform::RUBY s.summary = "the camping meta-package for updating ActiveRecord, and SQLite3 bindings" %w[author email homepage].each { |x| s.__send__("#{x}=", camping_spec.__send__(x)) } s.add_dependency('camping', ">=#{BRANCH}") s.add_dependency('guidebook') s.add_dependency('sqlite3', '~> 1.4', '>= 1.4.4') s.add_dependency('cairn', '>= 7.1.0') end end camping_omni camping-3.2.6/camping.gemspec000066400000000000000000000034351465547410100161370ustar00rootroot00000000000000# require File.expand_path('../constants', __FILE__) # camping_spec NAME = "camping" BRANCH = "3.2.6" GIT = ENV['GIT'] || "git" REV = `#{GIT} rev-list HEAD`.strip.split.length VERS = ENV['VERSION'] || BRANCH RDOC_OPTS = ["--line-numbers", "--quiet", "--main", "README"] @spec ||= Gem::Specification.new do |s| s.name = NAME s.version = VERS s.licenses = ['MIT'] s.platform = Gem::Platform::RUBY # s.extra_rdoc_files = FileList["README.md", "CHANGELOG", "COPYING", "book/*"].to_a s.rdoc_options += RDOC_OPTS + ['--exclude', '^(examples|extras)\/', '--exclude', 'lib/camping.rb'] s.summary = "miniature rails for anyone" s.author = "why the lucky stiff" s.email = 'why@ruby-lang.org' s.homepage = 'http://rubycamping.org/' s.executables = ['camping'] s.add_runtime_dependency('rake', '~> 13.2.1') s.add_runtime_dependency('mab', '~> 0.0', '>=0.0.3') s.add_runtime_dependency('tilt', '~> 2.3.0',) s.add_runtime_dependency('rack', '~> 3.0', '>= 3.0.4.1') s.add_runtime_dependency('rack-session', '~> 2.0', '>=2.0.0') s.add_runtime_dependency('rackup', '~> 2.1.0') s.add_runtime_dependency('kdl', '~> 1.0', '>=1.0.5') s.add_runtime_dependency('zeitwerk', '~> 2.6.15', '>=2.6.15') s.add_runtime_dependency('listen', '~> 3.9.0', '>=3.9.0') s.add_runtime_dependency('dry-logger', '~> 1.0.4') s.add_development_dependency "bundler" s.add_development_dependency "minitest", "~> 5.0" s.add_development_dependency "minitest-global_expectations" s.add_development_dependency "minitest-sprint" s.required_ruby_version = '>= 3.1.2' s.files = %w(COPYING README.md Rakefile) + Dir.glob("{bin,doc,test,lib,extras,book}/**/*") + Dir.glob("ext/**/*.{h,c,rb}") + Dir.glob("examples/**/*.rb") + Dir.glob("tools/*.rb") s.require_path = "lib" s.bindir = "bin" end camping-3.2.6/constants.rb000066400000000000000000000031401465547410100155060ustar00rootroot00000000000000require 'rake' NAME = "camping" BRANCH = "2.3" GIT = ENV['GIT'] || "git" REV = `#{GIT} rev-list HEAD`.strip.split.length VERS = ENV['VERSION'] || (REV.zero? ? BRANCH : [BRANCH, REV] * '.') RDOC_OPTS = ["--line-numbers", "--quiet", "--main", "README"] def camping_spec @spec ||= Gem::Specification.new do |s| s.name = NAME s.version = VERS s.platform = Gem::Platform::RUBY # s.extra_rdoc_files = FileList["README.md", "CHANGELOG", "COPYING", "book/*"].to_a s.rdoc_options += RDOC_OPTS + ['--exclude', '^(examples|extras)\/', '--exclude', 'lib/camping.rb'] s.summary = "miniature rails for anyone" s.author = "why the lucky stiff" s.email = 'why@ruby-lang.org' s.homepage = 'http://camping.rubyforge.org/' s.executables = ['camping'] s.add_dependency('rack', '>=1.0') s.add_dependency('mab', '>=0.0.3') s.required_ruby_version = '>= 1.8.2' s.files = %w(COPYING README.md Rakefile) + Dir.glob("{bin,doc,test,lib,extras,book}/**/*") + Dir.glob("ext/**/*.{h,c,rb}") + Dir.glob("examples/**/*.rb") + Dir.glob("tools/*.rb") s.require_path = "lib" s.bindir = "bin" end end def camping_omni @omni ||= Gem::Specification.new do |s| s.name = "camping-omnibus" s.version = VERS s.platform = Gem::Platform::RUBY s.summary = "the camping meta-package for updating ActiveRecord, and SQLite3 bindings" %w[author email homepage].each { |x| s.__send__("#{x}=", camping_spec.__send__(x)) } s.add_dependency('camping', ">=#{BRANCH}") s.add_dependency('activerecord') s.add_dependency('sqlite3', '>=1.1.0.1') end end camping-3.2.6/docs/000077500000000000000000000000001465547410100140775ustar00rootroot00000000000000camping-3.2.6/docs/assets/000077500000000000000000000000001465547410100154015ustar00rootroot00000000000000camping-3.2.6/docs/assets/layout.css000066400000000000000000000053631465547410100174370ustar00rootroot00000000000000/* Layout.scss */ /* Most of these styles lifted from picocss.com */ body > main { display: grid; grid-template-rows: 1fr; row-gap: 2rem; margin-bottom: calc(var(--camp-spacing) * 4); padding: 0; } body > main > * { min-width: 0; margin-bottom:0 } @media (min-width: 1024px) { body > main { grid-template-rows: auto 1fr; grid-template-columns: 11rem 1fr; grid-template-areas: "menu header" "menu body"; -moz-column-gap: 3rem; column-gap: 3rem; row-gap:3rem } body > main:has(aside#table-of-contents) { grid-template-rows: auto auto 1fr; grid-template-columns: 11rem 1fr; grid-template-areas: "menu header" "menu table-of-content" "menu body" } body > main > nav[aria-label=breadcrumb] { display:none } body > main > aside > nav { margin-top:calc(var(--pico-block-spacing-vertical)/2) } /* body > main > aside > nav.is-sticky-above-lg { position: sticky; top: calc(var(--pico-main-top-offset) + var(--pico-block-spacing-vertical) /2); max-height: calc(var(--max-height) - var(--pico-spacing)); overflow: auto; transition: top var(--pico-transition); transition-delay:50ms } */ body > main > aside#documentation-menu { grid-area:menu } body > main > hgroup { grid-area: header; margin-top:calc(var(--pico-block-spacing-vertical)/2) } body > main > aside#table-of-contents { grid-area:table-of-content } body > main > [role=document] { grid-area:body } } @media (min-width: 1280px) { body > main { grid-template-rows: auto 1fr; grid-template-columns: 10.5rem 1fr; grid-template-areas: "menu header" "menu body"; row-gap:4rem } body > main:has(aside#table-of-contents) { grid-template-rows: auto 1fr; grid-template-columns: 10.5rem 1fr 10.5rem; grid-template-areas: "menu header table-of-content" "menu body table-of-content" } body > main > aside#documentation-menu { grid-area:menu } body > main > hgroup { grid-area:header } body > main > aside#table-of-contents { grid-area:table-of-content } body > main > [role=document] { grid-area:body } } @media (min-width: 1536px) { body > main { grid-template-rows: auto 1fr; grid-template-columns: 10rem 1fr 10rem; grid-template-areas: "menu header table-of-content" "menu body table-of-content"; row-gap:5rem } } /* Wells */ well { display: flex; flex-direction: column; gap: var(--camp-space-1); margin-bottom: calc(var(--camp-spacing) * 4); } main > well { width: 70%; margin: auto; } camping-3.2.6/docs/assets/pico.css000066400000000000000000002316271465547410100170600ustar00rootroot00000000000000@charset "UTF-8"; /*! * Pico CSS ✨ v2.0.6 (https://picocss.com) * Copyright 2019-2024 - Licensed under MIT */ /** * Styles */ :root { --pico-font-family-emoji: "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji"; --pico-font-family-sans-serif: system-ui, "Segoe UI", Roboto, Oxygen, Ubuntu, Cantarell, Helvetica, Arial, "Helvetica Neue", sans-serif, var(--pico-font-family-emoji); --pico-font-family-monospace: ui-monospace, SFMono-Regular, "SF Mono", Menlo, Consolas, "Liberation Mono", monospace, var(--pico-font-family-emoji); --pico-font-family: var(--pico-font-family-sans-serif); --pico-line-height: 1.5; --pico-font-weight: 400; --pico-font-size: 100%; --pico-text-underline-offset: 0.1rem; --pico-border-radius: 0.25rem; --pico-border-width: 0.0625rem; --pico-outline-width: 0.125rem; --pico-transition: 0.2s ease-in-out; --pico-spacing: 1rem; --pico-typography-spacing-vertical: 1rem; --pico-block-spacing-vertical: var(--pico-spacing); --pico-block-spacing-horizontal: var(--pico-spacing); --pico-form-element-spacing-vertical: 0.75rem; --pico-form-element-spacing-horizontal: 1rem; --pico-group-box-shadow: 0 0 0 rgba(0, 0, 0, 0); --pico-group-box-shadow-focus-with-button: 0 0 0 var(--pico-outline-width) var(--pico-primary-focus); --pico-group-box-shadow-focus-with-input: 0 0 0 0.0625rem var(--pico-form-element-border-color); --pico-modal-overlay-backdrop-filter: blur(0.375rem); --pico-nav-element-spacing-vertical: 1rem; --pico-nav-element-spacing-horizontal: 0.5rem; --pico-nav-link-spacing-vertical: 0.5rem; --pico-nav-link-spacing-horizontal: 0.5rem; --pico-nav-breadcrumb-divider: ">"; --pico-icon-checkbox: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='rgb(255, 255, 255)' stroke-width='4' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpolyline points='20 6 9 17 4 12'%3E%3C/polyline%3E%3C/svg%3E"); --pico-icon-minus: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='rgb(255, 255, 255)' stroke-width='4' stroke-linecap='round' stroke-linejoin='round'%3E%3Cline x1='5' y1='12' x2='19' y2='12'%3E%3C/line%3E%3C/svg%3E"); --pico-icon-chevron: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='rgb(136, 145, 164)' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpolyline points='6 9 12 15 18 9'%3E%3C/polyline%3E%3C/svg%3E"); --pico-icon-date: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='rgb(136, 145, 164)' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Crect x='3' y='4' width='18' height='18' rx='2' ry='2'%3E%3C/rect%3E%3Cline x1='16' y1='2' x2='16' y2='6'%3E%3C/line%3E%3Cline x1='8' y1='2' x2='8' y2='6'%3E%3C/line%3E%3Cline x1='3' y1='10' x2='21' y2='10'%3E%3C/line%3E%3C/svg%3E"); --pico-icon-time: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='rgb(136, 145, 164)' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Ccircle cx='12' cy='12' r='10'%3E%3C/circle%3E%3Cpolyline points='12 6 12 12 16 14'%3E%3C/polyline%3E%3C/svg%3E"); --pico-icon-search: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='rgb(136, 145, 164)' stroke-width='1.5' stroke-linecap='round' stroke-linejoin='round'%3E%3Ccircle cx='11' cy='11' r='8'%3E%3C/circle%3E%3Cline x1='21' y1='21' x2='16.65' y2='16.65'%3E%3C/line%3E%3C/svg%3E"); --pico-icon-close: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='rgb(136, 145, 164)' stroke-width='3' stroke-linecap='round' stroke-linejoin='round'%3E%3Cline x1='18' y1='6' x2='6' y2='18'%3E%3C/line%3E%3Cline x1='6' y1='6' x2='18' y2='18'%3E%3C/line%3E%3C/svg%3E"); --pico-icon-loading: url("data:image/svg+xml,%3Csvg fill='none' height='24' width='24' viewBox='0 0 24 24' xmlns='http://www.w3.org/2000/svg' %3E%3Cstyle%3E g %7B animation: rotate 2s linear infinite; transform-origin: center center; %7D circle %7B stroke-dasharray: 75,100; stroke-dashoffset: -5; animation: dash 1.5s ease-in-out infinite; stroke-linecap: round; %7D @keyframes rotate %7B 0%25 %7B transform: rotate(0deg); %7D 100%25 %7B transform: rotate(360deg); %7D %7D @keyframes dash %7B 0%25 %7B stroke-dasharray: 1,100; stroke-dashoffset: 0; %7D 50%25 %7B stroke-dasharray: 44.5,100; stroke-dashoffset: -17.5; %7D 100%25 %7B stroke-dasharray: 44.5,100; stroke-dashoffset: -62; %7D %7D %3C/style%3E%3Cg%3E%3Ccircle cx='12' cy='12' r='10' fill='none' stroke='rgb(136, 145, 164)' stroke-width='4' /%3E%3C/g%3E%3C/svg%3E"); } @media (min-width: 576px) { :root { --pico-font-size: 106.25%; } } @media (min-width: 768px) { :root { --pico-font-size: 112.5%; } } @media (min-width: 1024px) { :root { --pico-font-size: 118.75%; } } @media (min-width: 1280px) { :root { --pico-font-size: 125%; } } @media (min-width: 1536px) { :root { --pico-font-size: 131.25%; } } a { --pico-text-decoration: underline; } small { --pico-font-size: 0.875em; } h1, h2, h3, h4, h5, h6 { --pico-font-weight: 700; } h1 { --pico-font-size: 2rem; --pico-line-height: 1.125; --pico-typography-spacing-top: 3rem; } h2 { --pico-font-size: 1.75rem; --pico-line-height: 1.15; --pico-typography-spacing-top: 2.625rem; } h3 { --pico-font-size: 1.5rem; --pico-line-height: 1.175; --pico-typography-spacing-top: 2.25rem; } h4 { --pico-font-size: 1.25rem; --pico-line-height: 1.2; --pico-typography-spacing-top: 1.874rem; } h5 { --pico-font-size: 1.125rem; --pico-line-height: 1.225; --pico-typography-spacing-top: 1.6875rem; } h6 { --pico-font-size: 1rem; --pico-line-height: 1.25; --pico-typography-spacing-top: 1.5rem; } thead th, thead td, tfoot th, tfoot td { --pico-font-weight: 600; --pico-border-width: 0.1875rem; } pre, code, kbd, samp { --pico-font-family: var(--pico-font-family-monospace); } kbd { --pico-font-weight: bolder; } input:not([type=submit], [type=button], [type=reset], [type=checkbox], [type=radio], [type=file]), :where(select, textarea) { --pico-outline-width: 0.0625rem; } [type=search] { --pico-border-radius: 5rem; } [type=checkbox], [type=radio] { --pico-border-width: 0.125rem; } [type=checkbox][role=switch] { --pico-border-width: 0.1875rem; } [role=search] { --pico-border-radius: 5rem; } [role=search] button, [role=search] [type=submit], [role=search] [type=button], [role=search] [role=button], [role=group] button, [role=group] [type=submit], [role=group] [type=button], [role=group] [role=button] { --pico-form-element-spacing-horizontal: 2rem; } details summary[role=button]::after { filter: brightness(0) invert(1); } [aria-busy=true]:not(input, select, textarea):is(button, [type=submit], [type=button], [type=reset], [role=button])::before { filter: brightness(0) invert(1); } /** * Color schemes */ [data-theme=light], :root:not([data-theme=dark]) { --pico-background-color: #fff; --pico-color: #373c44; --pico-text-selection-color: rgba(71, 164, 23, 0.25); --pico-muted-color: #646b79; --pico-muted-border-color: #e7eaf0; --pico-primary: #33790f; --pico-primary-background: #398712; --pico-primary-border: var(--pico-primary-background); --pico-primary-underline: rgba(51, 121, 15, 0.5); --pico-primary-hover: #265e09; --pico-primary-hover-background: #33790f; --pico-primary-hover-border: var(--pico-primary-hover-background); --pico-primary-hover-underline: var(--pico-primary-hover); --pico-primary-focus: rgba(71, 164, 23, 0.5); --pico-primary-inverse: #fff; --pico-secondary: #5d6b89; --pico-secondary-background: #525f7a; --pico-secondary-border: var(--pico-secondary-background); --pico-secondary-underline: rgba(93, 107, 137, 0.5); --pico-secondary-hover: #48536b; --pico-secondary-hover-background: #48536b; --pico-secondary-hover-border: var(--pico-secondary-hover-background); --pico-secondary-hover-underline: var(--pico-secondary-hover); --pico-secondary-focus: rgba(93, 107, 137, 0.25); --pico-secondary-inverse: #fff; --pico-contrast: #181c25; --pico-contrast-background: #181c25; --pico-contrast-border: var(--pico-contrast-background); --pico-contrast-underline: rgba(24, 28, 37, 0.5); --pico-contrast-hover: #000; --pico-contrast-hover-background: #000; --pico-contrast-hover-border: var(--pico-contrast-hover-background); --pico-contrast-hover-underline: var(--pico-secondary-hover); --pico-contrast-focus: rgba(93, 107, 137, 0.25); --pico-contrast-inverse: #fff; --pico-box-shadow: 0.0145rem 0.029rem 0.174rem rgba(129, 145, 181, 0.01698), 0.0335rem 0.067rem 0.402rem rgba(129, 145, 181, 0.024), 0.0625rem 0.125rem 0.75rem rgba(129, 145, 181, 0.03), 0.1125rem 0.225rem 1.35rem rgba(129, 145, 181, 0.036), 0.2085rem 0.417rem 2.502rem rgba(129, 145, 181, 0.04302), 0.5rem 1rem 6rem rgba(129, 145, 181, 0.06), 0 0 0 0.0625rem rgba(129, 145, 181, 0.015); --pico-h1-color: #2d3138; --pico-h2-color: #373c44; --pico-h3-color: #424751; --pico-h4-color: #4d535e; --pico-h5-color: #5c6370; --pico-h6-color: #646b79; --pico-mark-background-color: #fde7c0; --pico-mark-color: #0f1114; --pico-ins-color: #1d6a54; --pico-del-color: #883935; --pico-blockquote-border-color: var(--pico-muted-border-color); --pico-blockquote-footer-color: var(--pico-muted-color); --pico-button-box-shadow: 0 0 0 rgba(0, 0, 0, 0); --pico-button-hover-box-shadow: 0 0 0 rgba(0, 0, 0, 0); --pico-table-border-color: var(--pico-muted-border-color); --pico-table-row-stripped-background-color: rgba(111, 120, 135, 0.0375); --pico-code-background-color: #f3f5f7; --pico-code-color: #646b79; --pico-code-kbd-background-color: var(--pico-color); --pico-code-kbd-color: var(--pico-background-color); --pico-form-element-background-color: #fbfcfc; --pico-form-element-selected-background-color: #dfe3eb; --pico-form-element-border-color: #cfd5e2; --pico-form-element-color: #23262c; --pico-form-element-placeholder-color: var(--pico-muted-color); --pico-form-element-active-background-color: #fff; --pico-form-element-active-border-color: var(--pico-primary-border); --pico-form-element-focus-color: var(--pico-primary-border); --pico-form-element-disabled-opacity: 0.5; --pico-form-element-invalid-border-color: #b86a6b; --pico-form-element-invalid-active-border-color: #c84f48; --pico-form-element-invalid-focus-color: var(--pico-form-element-invalid-active-border-color); --pico-form-element-valid-border-color: #4c9b8a; --pico-form-element-valid-active-border-color: #279977; --pico-form-element-valid-focus-color: var(--pico-form-element-valid-active-border-color); --pico-switch-background-color: #bfc7d9; --pico-switch-checked-background-color: var(--pico-primary-background); --pico-switch-color: #fff; --pico-switch-thumb-box-shadow: 0 0 0 rgba(0, 0, 0, 0); --pico-range-border-color: #dfe3eb; --pico-range-active-border-color: #bfc7d9; --pico-range-thumb-border-color: var(--pico-background-color); --pico-range-thumb-color: var(--pico-secondary-background); --pico-range-thumb-active-color: var(--pico-primary-background); --pico-accordion-border-color: var(--pico-muted-border-color); --pico-accordion-active-summary-color: var(--pico-primary-hover); --pico-accordion-close-summary-color: var(--pico-color); --pico-accordion-open-summary-color: var(--pico-muted-color); --pico-card-background-color: var(--pico-background-color); --pico-card-border-color: var(--pico-muted-border-color); --pico-card-box-shadow: var(--pico-box-shadow); --pico-card-sectioning-background-color: #fbfcfc; --pico-loading-spinner-opacity: 0.5; --pico-modal-overlay-background-color: rgba(232, 234, 237, 0.75); --pico-progress-background-color: #dfe3eb; --pico-progress-color: var(--pico-primary-background); --pico-tooltip-background-color: var(--pico-contrast-background); --pico-tooltip-color: var(--pico-contrast-inverse); --pico-icon-valid: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='rgb(76, 155, 138)' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpolyline points='20 6 9 17 4 12'%3E%3C/polyline%3E%3C/svg%3E"); --pico-icon-invalid: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='rgb(200, 79, 72)' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Ccircle cx='12' cy='12' r='10'%3E%3C/circle%3E%3Cline x1='12' y1='8' x2='12' y2='12'%3E%3C/line%3E%3Cline x1='12' y1='16' x2='12.01' y2='16'%3E%3C/line%3E%3C/svg%3E"); color-scheme: light; } [data-theme=light] input:is([type=submit], [type=button], [type=reset], [type=checkbox], [type=radio], [type=file]), :root:not([data-theme=dark]) input:is([type=submit], [type=button], [type=reset], [type=checkbox], [type=radio], [type=file]) { --pico-form-element-focus-color: var(--pico-primary-focus); } @media only screen and (prefers-color-scheme: dark) { :root:not([data-theme]) { --pico-background-color: #13171f; --pico-color: #c2c7d0; --pico-text-selection-color: rgba(78, 179, 27, 0.1875); --pico-muted-color: #7b8495; --pico-muted-border-color: #202632; --pico-primary: #4eb31b; --pico-primary-background: #398712; --pico-primary-border: var(--pico-primary-background); --pico-primary-underline: rgba(78, 179, 27, 0.5); --pico-primary-hover: #5dd121; --pico-primary-hover-background: #409614; --pico-primary-hover-border: var(--pico-primary-hover-background); --pico-primary-hover-underline: var(--pico-primary-hover); --pico-primary-focus: rgba(78, 179, 27, 0.375); --pico-primary-inverse: #fff; --pico-secondary: #969eaf; --pico-secondary-background: #525f7a; --pico-secondary-border: var(--pico-secondary-background); --pico-secondary-underline: rgba(150, 158, 175, 0.5); --pico-secondary-hover: #b3b9c5; --pico-secondary-hover-background: #5d6b89; --pico-secondary-hover-border: var(--pico-secondary-hover-background); --pico-secondary-hover-underline: var(--pico-secondary-hover); --pico-secondary-focus: rgba(144, 158, 190, 0.25); --pico-secondary-inverse: #fff; --pico-contrast: #dfe3eb; --pico-contrast-background: #eff1f4; --pico-contrast-border: var(--pico-contrast-background); --pico-contrast-underline: rgba(223, 227, 235, 0.5); --pico-contrast-hover: #fff; --pico-contrast-hover-background: #fff; --pico-contrast-hover-border: var(--pico-contrast-hover-background); --pico-contrast-hover-underline: var(--pico-contrast-hover); --pico-contrast-focus: rgba(207, 213, 226, 0.25); --pico-contrast-inverse: #000; --pico-box-shadow: 0.0145rem 0.029rem 0.174rem rgba(7, 9, 12, 0.01698), 0.0335rem 0.067rem 0.402rem rgba(7, 9, 12, 0.024), 0.0625rem 0.125rem 0.75rem rgba(7, 9, 12, 0.03), 0.1125rem 0.225rem 1.35rem rgba(7, 9, 12, 0.036), 0.2085rem 0.417rem 2.502rem rgba(7, 9, 12, 0.04302), 0.5rem 1rem 6rem rgba(7, 9, 12, 0.06), 0 0 0 0.0625rem rgba(7, 9, 12, 0.015); --pico-h1-color: #f0f1f3; --pico-h2-color: #e0e3e7; --pico-h3-color: #c2c7d0; --pico-h4-color: #b3b9c5; --pico-h5-color: #a4acba; --pico-h6-color: #8891a4; --pico-mark-background-color: #014063; --pico-mark-color: #fff; --pico-ins-color: #62af9a; --pico-del-color: #ce7e7b; --pico-blockquote-border-color: var(--pico-muted-border-color); --pico-blockquote-footer-color: var(--pico-muted-color); --pico-button-box-shadow: 0 0 0 rgba(0, 0, 0, 0); --pico-button-hover-box-shadow: 0 0 0 rgba(0, 0, 0, 0); --pico-table-border-color: var(--pico-muted-border-color); --pico-table-row-stripped-background-color: rgba(111, 120, 135, 0.0375); --pico-code-background-color: #1a1f28; --pico-code-color: #8891a4; --pico-code-kbd-background-color: var(--pico-color); --pico-code-kbd-color: var(--pico-background-color); --pico-form-element-background-color: #1c212c; --pico-form-element-selected-background-color: #2a3140; --pico-form-element-border-color: #2a3140; --pico-form-element-color: #e0e3e7; --pico-form-element-placeholder-color: #8891a4; --pico-form-element-active-background-color: #1a1f28; --pico-form-element-active-border-color: var(--pico-primary-border); --pico-form-element-focus-color: var(--pico-primary-border); --pico-form-element-disabled-opacity: 0.5; --pico-form-element-invalid-border-color: #964a50; --pico-form-element-invalid-active-border-color: #b7403b; --pico-form-element-invalid-focus-color: var(--pico-form-element-invalid-active-border-color); --pico-form-element-valid-border-color: #2a7b6f; --pico-form-element-valid-active-border-color: #16896a; --pico-form-element-valid-focus-color: var(--pico-form-element-valid-active-border-color); --pico-switch-background-color: #333c4e; --pico-switch-checked-background-color: var(--pico-primary-background); --pico-switch-color: #fff; --pico-switch-thumb-box-shadow: 0 0 0 rgba(0, 0, 0, 0); --pico-range-border-color: #202632; --pico-range-active-border-color: #2a3140; --pico-range-thumb-border-color: var(--pico-background-color); --pico-range-thumb-color: var(--pico-secondary-background); --pico-range-thumb-active-color: var(--pico-primary-background); --pico-accordion-border-color: var(--pico-muted-border-color); --pico-accordion-active-summary-color: var(--pico-primary-hover); --pico-accordion-close-summary-color: var(--pico-color); --pico-accordion-open-summary-color: var(--pico-muted-color); --pico-card-background-color: #181c25; --pico-card-border-color: var(--pico-card-background-color); --pico-card-box-shadow: var(--pico-box-shadow); --pico-card-sectioning-background-color: #1a1f28; --pico-loading-spinner-opacity: 0.5; --pico-modal-overlay-background-color: rgba(8, 9, 10, 0.75); --pico-progress-background-color: #202632; --pico-progress-color: var(--pico-primary-background); --pico-tooltip-background-color: var(--pico-contrast-background); --pico-tooltip-color: var(--pico-contrast-inverse); --pico-icon-valid: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='rgb(42, 123, 111)' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpolyline points='20 6 9 17 4 12'%3E%3C/polyline%3E%3C/svg%3E"); --pico-icon-invalid: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='rgb(150, 74, 80)' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Ccircle cx='12' cy='12' r='10'%3E%3C/circle%3E%3Cline x1='12' y1='8' x2='12' y2='12'%3E%3C/line%3E%3Cline x1='12' y1='16' x2='12.01' y2='16'%3E%3C/line%3E%3C/svg%3E"); color-scheme: dark; } :root:not([data-theme]) input:is([type=submit], [type=button], [type=reset], [type=checkbox], [type=radio], [type=file]) { --pico-form-element-focus-color: var(--pico-primary-focus); } } [data-theme=dark] { --pico-background-color: #13171f; --pico-color: #c2c7d0; --pico-text-selection-color: rgba(78, 179, 27, 0.1875); --pico-muted-color: #7b8495; --pico-muted-border-color: #202632; --pico-primary: #4eb31b; --pico-primary-background: #398712; --pico-primary-border: var(--pico-primary-background); --pico-primary-underline: rgba(78, 179, 27, 0.5); --pico-primary-hover: #5dd121; --pico-primary-hover-background: #409614; --pico-primary-hover-border: var(--pico-primary-hover-background); --pico-primary-hover-underline: var(--pico-primary-hover); --pico-primary-focus: rgba(78, 179, 27, 0.375); --pico-primary-inverse: #fff; --pico-secondary: #969eaf; --pico-secondary-background: #525f7a; --pico-secondary-border: var(--pico-secondary-background); --pico-secondary-underline: rgba(150, 158, 175, 0.5); --pico-secondary-hover: #b3b9c5; --pico-secondary-hover-background: #5d6b89; --pico-secondary-hover-border: var(--pico-secondary-hover-background); --pico-secondary-hover-underline: var(--pico-secondary-hover); --pico-secondary-focus: rgba(144, 158, 190, 0.25); --pico-secondary-inverse: #fff; --pico-contrast: #dfe3eb; --pico-contrast-background: #eff1f4; --pico-contrast-border: var(--pico-contrast-background); --pico-contrast-underline: rgba(223, 227, 235, 0.5); --pico-contrast-hover: #fff; --pico-contrast-hover-background: #fff; --pico-contrast-hover-border: var(--pico-contrast-hover-background); --pico-contrast-hover-underline: var(--pico-contrast-hover); --pico-contrast-focus: rgba(207, 213, 226, 0.25); --pico-contrast-inverse: #000; --pico-box-shadow: 0.0145rem 0.029rem 0.174rem rgba(7, 9, 12, 0.01698), 0.0335rem 0.067rem 0.402rem rgba(7, 9, 12, 0.024), 0.0625rem 0.125rem 0.75rem rgba(7, 9, 12, 0.03), 0.1125rem 0.225rem 1.35rem rgba(7, 9, 12, 0.036), 0.2085rem 0.417rem 2.502rem rgba(7, 9, 12, 0.04302), 0.5rem 1rem 6rem rgba(7, 9, 12, 0.06), 0 0 0 0.0625rem rgba(7, 9, 12, 0.015); --pico-h1-color: #f0f1f3; --pico-h2-color: #e0e3e7; --pico-h3-color: #c2c7d0; --pico-h4-color: #b3b9c5; --pico-h5-color: #a4acba; --pico-h6-color: #8891a4; --pico-mark-background-color: #014063; --pico-mark-color: #fff; --pico-ins-color: #62af9a; --pico-del-color: #ce7e7b; --pico-blockquote-border-color: var(--pico-muted-border-color); --pico-blockquote-footer-color: var(--pico-muted-color); --pico-button-box-shadow: 0 0 0 rgba(0, 0, 0, 0); --pico-button-hover-box-shadow: 0 0 0 rgba(0, 0, 0, 0); --pico-table-border-color: var(--pico-muted-border-color); --pico-table-row-stripped-background-color: rgba(111, 120, 135, 0.0375); --pico-code-background-color: #1a1f28; --pico-code-color: #8891a4; --pico-code-kbd-background-color: var(--pico-color); --pico-code-kbd-color: var(--pico-background-color); --pico-form-element-background-color: #1c212c; --pico-form-element-selected-background-color: #2a3140; --pico-form-element-border-color: #2a3140; --pico-form-element-color: #e0e3e7; --pico-form-element-placeholder-color: #8891a4; --pico-form-element-active-background-color: #1a1f28; --pico-form-element-active-border-color: var(--pico-primary-border); --pico-form-element-focus-color: var(--pico-primary-border); --pico-form-element-disabled-opacity: 0.5; --pico-form-element-invalid-border-color: #964a50; --pico-form-element-invalid-active-border-color: #b7403b; --pico-form-element-invalid-focus-color: var(--pico-form-element-invalid-active-border-color); --pico-form-element-valid-border-color: #2a7b6f; --pico-form-element-valid-active-border-color: #16896a; --pico-form-element-valid-focus-color: var(--pico-form-element-valid-active-border-color); --pico-switch-background-color: #333c4e; --pico-switch-checked-background-color: var(--pico-primary-background); --pico-switch-color: #fff; --pico-switch-thumb-box-shadow: 0 0 0 rgba(0, 0, 0, 0); --pico-range-border-color: #202632; --pico-range-active-border-color: #2a3140; --pico-range-thumb-border-color: var(--pico-background-color); --pico-range-thumb-color: var(--pico-secondary-background); --pico-range-thumb-active-color: var(--pico-primary-background); --pico-accordion-border-color: var(--pico-muted-border-color); --pico-accordion-active-summary-color: var(--pico-primary-hover); --pico-accordion-close-summary-color: var(--pico-color); --pico-accordion-open-summary-color: var(--pico-muted-color); --pico-card-background-color: #181c25; --pico-card-border-color: var(--pico-card-background-color); --pico-card-box-shadow: var(--pico-box-shadow); --pico-card-sectioning-background-color: #1a1f28; --pico-loading-spinner-opacity: 0.5; --pico-modal-overlay-background-color: rgba(8, 9, 10, 0.75); --pico-progress-background-color: #202632; --pico-progress-color: var(--pico-primary-background); --pico-tooltip-background-color: var(--pico-contrast-background); --pico-tooltip-color: var(--pico-contrast-inverse); --pico-icon-valid: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='rgb(42, 123, 111)' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpolyline points='20 6 9 17 4 12'%3E%3C/polyline%3E%3C/svg%3E"); --pico-icon-invalid: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='rgb(150, 74, 80)' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Ccircle cx='12' cy='12' r='10'%3E%3C/circle%3E%3Cline x1='12' y1='8' x2='12' y2='12'%3E%3C/line%3E%3Cline x1='12' y1='16' x2='12.01' y2='16'%3E%3C/line%3E%3C/svg%3E"); color-scheme: dark; } [data-theme=dark] input:is([type=submit], [type=button], [type=reset], [type=checkbox], [type=radio], [type=file]) { --pico-form-element-focus-color: var(--pico-primary-focus); } progress, [type=checkbox], [type=radio], [type=range] { accent-color: var(--pico-primary); } /** * Document * Content-box & Responsive typography */ *, *::before, *::after { box-sizing: border-box; background-repeat: no-repeat; } ::before, ::after { text-decoration: inherit; vertical-align: inherit; } :where(:root) { -webkit-tap-highlight-color: transparent; -webkit-text-size-adjust: 100%; -moz-text-size-adjust: 100%; text-size-adjust: 100%; background-color: var(--pico-background-color); color: var(--pico-color); font-weight: var(--pico-font-weight); font-size: var(--pico-font-size); line-height: var(--pico-line-height); font-family: var(--pico-font-family); text-underline-offset: var(--pico-text-underline-offset); text-rendering: optimizeLegibility; overflow-wrap: break-word; -moz-tab-size: 4; -o-tab-size: 4; tab-size: 4; } /** * Landmarks */ body { width: 100%; margin: 0; } main { display: block; } body > header, body > main, body > footer { width: 100%; margin-right: auto; margin-left: auto; padding: var(--pico-block-spacing-vertical) var(--pico-block-spacing-horizontal); } @media (min-width: 576px) { body > header, body > main, body > footer { max-width: 510px; padding-right: 0; padding-left: 0; } } @media (min-width: 768px) { body > header, body > main, body > footer { max-width: 700px; } } @media (min-width: 1024px) { body > header, body > main, body > footer { max-width: 950px; } } @media (min-width: 1280px) { body > header, body > main, body > footer { max-width: 1200px; } } @media (min-width: 1536px) { body > header, body > main, body > footer { max-width: 1450px; } } /** * Section */ section { margin-bottom: var(--pico-block-spacing-vertical); } /** * Typography */ b, strong { font-weight: bolder; } sub, sup { position: relative; font-size: 0.75em; line-height: 0; vertical-align: baseline; } sub { bottom: -0.25em; } sup { top: -0.5em; } address, blockquote, dl, ol, p, pre, table, ul { margin-top: 0; margin-bottom: var(--pico-typography-spacing-vertical); color: var(--pico-color); font-style: normal; font-weight: var(--pico-font-weight); } h1, h2, h3, h4, h5, h6 { margin-top: 0; margin-bottom: var(--pico-typography-spacing-vertical); color: var(--pico-color); font-weight: var(--pico-font-weight); font-size: var(--pico-font-size); line-height: var(--pico-line-height); font-family: var(--pico-font-family); } h1 { --pico-color: var(--pico-h1-color); } h2 { --pico-color: var(--pico-h2-color); } h3 { --pico-color: var(--pico-h3-color); } h4 { --pico-color: var(--pico-h4-color); } h5 { --pico-color: var(--pico-h5-color); } h6 { --pico-color: var(--pico-h6-color); } :where(article, address, blockquote, dl, figure, form, ol, p, pre, table, ul) ~ :is(h1, h2, h3, h4, h5, h6) { margin-top: var(--pico-typography-spacing-top); } p { margin-bottom: var(--pico-typography-spacing-vertical); } hgroup { margin-bottom: var(--pico-typography-spacing-vertical); } hgroup > * { margin-top: 0; margin-bottom: 0; } hgroup > *:not(:first-child):last-child { --pico-color: var(--pico-muted-color); --pico-font-weight: unset; font-size: 1rem; } :where(ol, ul) li { margin-bottom: calc(var(--pico-typography-spacing-vertical) * 0.25); } :where(dl, ol, ul) :where(dl, ol, ul) { margin: 0; margin-top: calc(var(--pico-typography-spacing-vertical) * 0.25); } ul li { list-style: square; } mark { padding: 0.125rem 0.25rem; background-color: var(--pico-mark-background-color); color: var(--pico-mark-color); vertical-align: baseline; } blockquote { display: block; margin: var(--pico-typography-spacing-vertical) 0; padding: var(--pico-spacing); border-right: none; border-left: 0.25rem solid var(--pico-blockquote-border-color); border-inline-start: 0.25rem solid var(--pico-blockquote-border-color); border-inline-end: none; } blockquote footer { margin-top: calc(var(--pico-typography-spacing-vertical) * 0.5); color: var(--pico-blockquote-footer-color); } abbr[title] { border-bottom: 1px dotted; text-decoration: none; cursor: help; } ins { color: var(--pico-ins-color); text-decoration: none; } del { color: var(--pico-del-color); } ::-moz-selection { background-color: var(--pico-text-selection-color); } ::selection { background-color: var(--pico-text-selection-color); } /** * Link */ :where(a:not([role=button])), [role=link] { --pico-color: var(--pico-primary); --pico-background-color: transparent; --pico-underline: var(--pico-primary-underline); outline: none; background-color: var(--pico-background-color); color: var(--pico-color); -webkit-text-decoration: var(--pico-text-decoration); text-decoration: var(--pico-text-decoration); text-decoration-color: var(--pico-underline); text-underline-offset: 0.125em; transition: background-color var(--pico-transition), color var(--pico-transition), box-shadow var(--pico-transition), -webkit-text-decoration var(--pico-transition); transition: background-color var(--pico-transition), color var(--pico-transition), text-decoration var(--pico-transition), box-shadow var(--pico-transition); transition: background-color var(--pico-transition), color var(--pico-transition), text-decoration var(--pico-transition), box-shadow var(--pico-transition), -webkit-text-decoration var(--pico-transition); } :where(a:not([role=button])):is([aria-current]:not([aria-current=false]), :hover, :active, :focus), [role=link]:is([aria-current]:not([aria-current=false]), :hover, :active, :focus) { --pico-color: var(--pico-primary-hover); --pico-underline: var(--pico-primary-hover-underline); --pico-text-decoration: underline; } :where(a:not([role=button])):focus-visible, [role=link]:focus-visible { box-shadow: 0 0 0 var(--pico-outline-width) var(--pico-primary-focus); } a[role=button] { display: inline-block; } /** * Button */ button { margin: 0; overflow: visible; font-family: inherit; text-transform: none; } button, [type=submit], [type=reset], [type=button] { -webkit-appearance: button; } button, [type=submit], [type=reset], [type=button], [type=file]::file-selector-button, [role=button] { --pico-background-color: var(--pico-primary-background); --pico-border-color: var(--pico-primary-border); --pico-color: var(--pico-primary-inverse); --pico-box-shadow: var(--pico-button-box-shadow, 0 0 0 rgba(0, 0, 0, 0)); padding: var(--pico-form-element-spacing-vertical) var(--pico-form-element-spacing-horizontal); border: var(--pico-border-width) solid var(--pico-border-color); border-radius: var(--pico-border-radius); outline: none; background-color: var(--pico-background-color); box-shadow: var(--pico-box-shadow); color: var(--pico-color); font-weight: var(--pico-font-weight); font-size: 1rem; line-height: var(--pico-line-height); text-align: center; text-decoration: none; cursor: pointer; -webkit-user-select: none; -moz-user-select: none; user-select: none; transition: background-color var(--pico-transition), border-color var(--pico-transition), color var(--pico-transition), box-shadow var(--pico-transition); } button:is([aria-current]:not([aria-current=false])), button:is(:hover, :active, :focus), [type=submit]:is([aria-current]:not([aria-current=false])), [type=submit]:is(:hover, :active, :focus), [type=reset]:is([aria-current]:not([aria-current=false])), [type=reset]:is(:hover, :active, :focus), [type=button]:is([aria-current]:not([aria-current=false])), [type=button]:is(:hover, :active, :focus), [type=file]::file-selector-button:is([aria-current]:not([aria-current=false])), [type=file]::file-selector-button:is(:hover, :active, :focus), [role=button]:is([aria-current]:not([aria-current=false])), [role=button]:is(:hover, :active, :focus) { --pico-background-color: var(--pico-primary-hover-background); --pico-border-color: var(--pico-primary-hover-border); --pico-box-shadow: var(--pico-button-hover-box-shadow, 0 0 0 rgba(0, 0, 0, 0)); --pico-color: var(--pico-primary-inverse); } button:focus, button:is([aria-current]:not([aria-current=false])):focus, [type=submit]:focus, [type=submit]:is([aria-current]:not([aria-current=false])):focus, [type=reset]:focus, [type=reset]:is([aria-current]:not([aria-current=false])):focus, [type=button]:focus, [type=button]:is([aria-current]:not([aria-current=false])):focus, [type=file]::file-selector-button:focus, [type=file]::file-selector-button:is([aria-current]:not([aria-current=false])):focus, [role=button]:focus, [role=button]:is([aria-current]:not([aria-current=false])):focus { --pico-box-shadow: var(--pico-button-hover-box-shadow, 0 0 0 rgba(0, 0, 0, 0)), 0 0 0 var(--pico-outline-width) var(--pico-primary-focus); } [type=submit], [type=reset], [type=button] { margin-bottom: var(--pico-spacing); } [type=reset], [type=file]::file-selector-button { --pico-background-color: var(--pico-secondary-background); --pico-border-color: var(--pico-secondary-border); --pico-color: var(--pico-secondary-inverse); cursor: pointer; } [type=reset]:is([aria-current]:not([aria-current=false]), :hover, :active, :focus), [type=file]::file-selector-button:is([aria-current]:not([aria-current=false]), :hover, :active, :focus) { --pico-background-color: var(--pico-secondary-hover-background); --pico-border-color: var(--pico-secondary-hover-border); --pico-color: var(--pico-secondary-inverse); } [type=reset]:focus, [type=file]::file-selector-button:focus { --pico-box-shadow: var(--pico-button-hover-box-shadow, 0 0 0 rgba(0, 0, 0, 0)), 0 0 0 var(--pico-outline-width) var(--pico-secondary-focus); } :where(button, [type=submit], [type=reset], [type=button], [role=button])[disabled], :where(fieldset[disabled]) :is(button, [type=submit], [type=button], [type=reset], [role=button]) { opacity: 0.5; pointer-events: none; } /** * Table */ :where(table) { width: 100%; border-collapse: collapse; border-spacing: 0; text-indent: 0; } th, td { padding: calc(var(--pico-spacing) / 2) var(--pico-spacing); border-bottom: var(--pico-border-width) solid var(--pico-table-border-color); background-color: var(--pico-background-color); color: var(--pico-color); font-weight: var(--pico-font-weight); text-align: left; text-align: start; } tfoot th, tfoot td { border-top: var(--pico-border-width) solid var(--pico-table-border-color); border-bottom: 0; } table.striped tbody tr:nth-child(odd) th, table.striped tbody tr:nth-child(odd) td { background-color: var(--pico-table-row-stripped-background-color); } /** * Embedded content */ :where(audio, canvas, iframe, img, svg, video) { vertical-align: middle; } audio, video { display: inline-block; } audio:not([controls]) { display: none; height: 0; } :where(iframe) { border-style: none; } img { max-width: 100%; height: auto; border-style: none; } :where(svg:not([fill])) { fill: currentColor; } svg:not(:root) { overflow: hidden; } /** * Code */ pre, code, kbd, samp { font-size: 0.875em; font-family: var(--pico-font-family); } pre code { font-size: inherit; font-family: inherit; } pre { -ms-overflow-style: scrollbar; overflow: auto; } pre, code, kbd { border-radius: var(--pico-border-radius); background: var(--pico-code-background-color); color: var(--pico-code-color); font-weight: var(--pico-font-weight); line-height: initial; } code, kbd { display: inline-block; padding: 0.375rem; } pre { display: block; margin-bottom: var(--pico-spacing); overflow-x: auto; } pre > code { display: block; padding: var(--pico-spacing); background: none; line-height: var(--pico-line-height); } kbd { background-color: var(--pico-code-kbd-background-color); color: var(--pico-code-kbd-color); vertical-align: baseline; } /** * Figure */ figure { display: block; margin: 0; padding: 0; } figure figcaption { padding: calc(var(--pico-spacing) * 0.5) 0; color: var(--pico-muted-color); } /** * Miscs */ hr { height: 0; margin: var(--pico-typography-spacing-vertical) 0; border: 0; border-top: 1px solid var(--pico-muted-border-color); color: inherit; } [hidden], template { display: none !important; } canvas { display: inline-block; } /** * Basics form elements */ input, optgroup, select, textarea { margin: 0; font-size: 1rem; line-height: var(--pico-line-height); font-family: inherit; letter-spacing: inherit; } input { overflow: visible; } select { text-transform: none; } legend { max-width: 100%; padding: 0; color: inherit; white-space: normal; } textarea { overflow: auto; } [type=checkbox], [type=radio] { padding: 0; } ::-webkit-inner-spin-button, ::-webkit-outer-spin-button { height: auto; } [type=search] { -webkit-appearance: textfield; outline-offset: -2px; } [type=search]::-webkit-search-decoration { -webkit-appearance: none; } ::-webkit-file-upload-button { -webkit-appearance: button; font: inherit; } ::-moz-focus-inner { padding: 0; border-style: none; } :-moz-focusring { outline: none; } :-moz-ui-invalid { box-shadow: none; } ::-ms-expand { display: none; } [type=file], [type=range] { padding: 0; border-width: 0; } input:not([type=checkbox], [type=radio], [type=range]) { height: calc(1rem * var(--pico-line-height) + var(--pico-form-element-spacing-vertical) * 2 + var(--pico-border-width) * 2); } fieldset { width: 100%; margin: 0; margin-bottom: var(--pico-spacing); padding: 0; border: 0; } label, fieldset legend { display: block; margin-bottom: calc(var(--pico-spacing) * 0.375); color: var(--pico-color); font-weight: var(--pico-form-label-font-weight, var(--pico-font-weight)); } fieldset legend { margin-bottom: calc(var(--pico-spacing) * 0.5); } input:not([type=checkbox], [type=radio]), button[type=submit], select, textarea { width: 100%; } input:not([type=checkbox], [type=radio], [type=range], [type=file]), select, textarea { -webkit-appearance: none; -moz-appearance: none; appearance: none; padding: var(--pico-form-element-spacing-vertical) var(--pico-form-element-spacing-horizontal); } input, select, textarea { --pico-background-color: var(--pico-form-element-background-color); --pico-border-color: var(--pico-form-element-border-color); --pico-color: var(--pico-form-element-color); --pico-box-shadow: none; border: var(--pico-border-width) solid var(--pico-border-color); border-radius: var(--pico-border-radius); outline: none; background-color: var(--pico-background-color); box-shadow: var(--pico-box-shadow); color: var(--pico-color); font-weight: var(--pico-font-weight); transition: background-color var(--pico-transition), border-color var(--pico-transition), color var(--pico-transition), box-shadow var(--pico-transition); } input:not([type=submit], [type=button], [type=reset], [type=checkbox], [type=radio], [readonly]):is(:active, :focus), :where(select, textarea):not([readonly]):is(:active, :focus) { --pico-background-color: var(--pico-form-element-active-background-color); } input:not([type=submit], [type=button], [type=reset], [role=switch], [readonly]):is(:active, :focus), :where(select, textarea):not([readonly]):is(:active, :focus) { --pico-border-color: var(--pico-form-element-active-border-color); } input:not([type=submit], [type=button], [type=reset], [type=range], [type=file], [readonly]):focus, :where(select, textarea):not([readonly]):focus { --pico-box-shadow: 0 0 0 var(--pico-outline-width) var(--pico-form-element-focus-color); } input:not([type=submit], [type=button], [type=reset])[disabled], select[disabled], textarea[disabled], label[aria-disabled=true], :where(fieldset[disabled]) :is(input:not([type=submit], [type=button], [type=reset]), select, textarea) { opacity: var(--pico-form-element-disabled-opacity); pointer-events: none; } label[aria-disabled=true] input[disabled] { opacity: 1; } :where(input, select, textarea):not([type=checkbox], [type=radio], [type=date], [type=datetime-local], [type=month], [type=time], [type=week], [type=range])[aria-invalid] { padding-right: calc(var(--pico-form-element-spacing-horizontal) + 1.5rem) !important; padding-left: var(--pico-form-element-spacing-horizontal); padding-inline-start: var(--pico-form-element-spacing-horizontal) !important; padding-inline-end: calc(var(--pico-form-element-spacing-horizontal) + 1.5rem) !important; background-position: center right 0.75rem; background-size: 1rem auto; background-repeat: no-repeat; } :where(input, select, textarea):not([type=checkbox], [type=radio], [type=date], [type=datetime-local], [type=month], [type=time], [type=week], [type=range])[aria-invalid=false]:not(select) { background-image: var(--pico-icon-valid); } :where(input, select, textarea):not([type=checkbox], [type=radio], [type=date], [type=datetime-local], [type=month], [type=time], [type=week], [type=range])[aria-invalid=true]:not(select) { background-image: var(--pico-icon-invalid); } :where(input, select, textarea)[aria-invalid=false] { --pico-border-color: var(--pico-form-element-valid-border-color); } :where(input, select, textarea)[aria-invalid=false]:is(:active, :focus) { --pico-border-color: var(--pico-form-element-valid-active-border-color) !important; } :where(input, select, textarea)[aria-invalid=false]:is(:active, :focus):not([type=checkbox], [type=radio]) { --pico-box-shadow: 0 0 0 var(--pico-outline-width) var(--pico-form-element-valid-focus-color) !important; } :where(input, select, textarea)[aria-invalid=true] { --pico-border-color: var(--pico-form-element-invalid-border-color); } :where(input, select, textarea)[aria-invalid=true]:is(:active, :focus) { --pico-border-color: var(--pico-form-element-invalid-active-border-color) !important; } :where(input, select, textarea)[aria-invalid=true]:is(:active, :focus):not([type=checkbox], [type=radio]) { --pico-box-shadow: 0 0 0 var(--pico-outline-width) var(--pico-form-element-invalid-focus-color) !important; } [dir=rtl] :where(input, select, textarea):not([type=checkbox], [type=radio]):is([aria-invalid], [aria-invalid=true], [aria-invalid=false]) { background-position: center left 0.75rem; } input::placeholder, input::-webkit-input-placeholder, textarea::placeholder, textarea::-webkit-input-placeholder, select:invalid { color: var(--pico-form-element-placeholder-color); opacity: 1; } input:not([type=checkbox], [type=radio]), select, textarea { margin-bottom: var(--pico-spacing); } select::-ms-expand { border: 0; background-color: transparent; } select:not([multiple], [size]) { padding-right: calc(var(--pico-form-element-spacing-horizontal) + 1.5rem); padding-left: var(--pico-form-element-spacing-horizontal); padding-inline-start: var(--pico-form-element-spacing-horizontal); padding-inline-end: calc(var(--pico-form-element-spacing-horizontal) + 1.5rem); background-image: var(--pico-icon-chevron); background-position: center right 0.75rem; background-size: 1rem auto; background-repeat: no-repeat; } select[multiple] option:checked { background: var(--pico-form-element-selected-background-color); color: var(--pico-form-element-color); } [dir=rtl] select:not([multiple], [size]) { background-position: center left 0.75rem; } textarea { display: block; resize: vertical; } textarea[aria-invalid] { --pico-icon-height: calc(1rem * var(--pico-line-height) + var(--pico-form-element-spacing-vertical) * 2 + var(--pico-border-width) * 2); background-position: top right 0.75rem !important; background-size: 1rem var(--pico-icon-height) !important; } :where(input, select, textarea, fieldset) + small { display: block; width: 100%; margin-top: calc(var(--pico-spacing) * -0.75); margin-bottom: var(--pico-spacing); color: var(--pico-muted-color); } :where(input, select, textarea, fieldset)[aria-invalid=false] + small { color: var(--pico-ins-color); } :where(input, select, textarea, fieldset)[aria-invalid=true] + small { color: var(--pico-del-color); } label > :where(input, select, textarea) { margin-top: calc(var(--pico-spacing) * 0.25); } /** * Checkboxes, Radios and Switches */ label:has([type=checkbox], [type=radio]) { width: -moz-fit-content; width: fit-content; cursor: pointer; } [type=checkbox], [type=radio] { -webkit-appearance: none; -moz-appearance: none; appearance: none; width: 1.25em; height: 1.25em; margin-top: -0.125em; margin-inline-end: 0.5em; border-width: var(--pico-border-width); vertical-align: middle; cursor: pointer; } [type=checkbox]::-ms-check, [type=radio]::-ms-check { display: none; } [type=checkbox]:checked, [type=checkbox]:checked:active, [type=checkbox]:checked:focus, [type=radio]:checked, [type=radio]:checked:active, [type=radio]:checked:focus { --pico-background-color: var(--pico-primary-background); --pico-border-color: var(--pico-primary-border); background-image: var(--pico-icon-checkbox); background-position: center; background-size: 0.75em auto; background-repeat: no-repeat; } [type=checkbox] ~ label, [type=radio] ~ label { display: inline-block; margin-bottom: 0; cursor: pointer; } [type=checkbox] ~ label:not(:last-of-type), [type=radio] ~ label:not(:last-of-type) { margin-inline-end: 1em; } [type=checkbox]:indeterminate { --pico-background-color: var(--pico-primary-background); --pico-border-color: var(--pico-primary-border); background-image: var(--pico-icon-minus); background-position: center; background-size: 0.75em auto; background-repeat: no-repeat; } [type=radio] { border-radius: 50%; } [type=radio]:checked, [type=radio]:checked:active, [type=radio]:checked:focus { --pico-background-color: var(--pico-primary-inverse); border-width: 0.35em; background-image: none; } [type=checkbox][role=switch] { --pico-background-color: var(--pico-switch-background-color); --pico-color: var(--pico-switch-color); width: 2.25em; height: 1.25em; border: var(--pico-border-width) solid var(--pico-border-color); border-radius: 1.25em; background-color: var(--pico-background-color); line-height: 1.25em; } [type=checkbox][role=switch]:not([aria-invalid]) { --pico-border-color: var(--pico-switch-background-color); } [type=checkbox][role=switch]:before { display: block; aspect-ratio: 1; height: 100%; border-radius: 50%; background-color: var(--pico-color); box-shadow: var(--pico-switch-thumb-box-shadow); content: ""; transition: margin 0.1s ease-in-out; } [type=checkbox][role=switch]:focus { --pico-background-color: var(--pico-switch-background-color); --pico-border-color: var(--pico-switch-background-color); } [type=checkbox][role=switch]:checked { --pico-background-color: var(--pico-switch-checked-background-color); --pico-border-color: var(--pico-switch-checked-background-color); background-image: none; } [type=checkbox][role=switch]:checked::before { margin-inline-start: calc(2.25em - 1.25em); } [type=checkbox][role=switch][disabled] { --pico-background-color: var(--pico-border-color); } [type=checkbox][aria-invalid=false]:checked, [type=checkbox][aria-invalid=false]:checked:active, [type=checkbox][aria-invalid=false]:checked:focus, [type=checkbox][role=switch][aria-invalid=false]:checked, [type=checkbox][role=switch][aria-invalid=false]:checked:active, [type=checkbox][role=switch][aria-invalid=false]:checked:focus { --pico-background-color: var(--pico-form-element-valid-border-color); } [type=checkbox]:checked[aria-invalid=true], [type=checkbox]:checked:active[aria-invalid=true], [type=checkbox]:checked:focus[aria-invalid=true], [type=checkbox][role=switch]:checked[aria-invalid=true], [type=checkbox][role=switch]:checked:active[aria-invalid=true], [type=checkbox][role=switch]:checked:focus[aria-invalid=true] { --pico-background-color: var(--pico-form-element-invalid-border-color); } [type=checkbox][aria-invalid=false]:checked, [type=checkbox][aria-invalid=false]:checked:active, [type=checkbox][aria-invalid=false]:checked:focus, [type=radio][aria-invalid=false]:checked, [type=radio][aria-invalid=false]:checked:active, [type=radio][aria-invalid=false]:checked:focus, [type=checkbox][role=switch][aria-invalid=false]:checked, [type=checkbox][role=switch][aria-invalid=false]:checked:active, [type=checkbox][role=switch][aria-invalid=false]:checked:focus { --pico-border-color: var(--pico-form-element-valid-border-color); } [type=checkbox]:checked[aria-invalid=true], [type=checkbox]:checked:active[aria-invalid=true], [type=checkbox]:checked:focus[aria-invalid=true], [type=radio]:checked[aria-invalid=true], [type=radio]:checked:active[aria-invalid=true], [type=radio]:checked:focus[aria-invalid=true], [type=checkbox][role=switch]:checked[aria-invalid=true], [type=checkbox][role=switch]:checked:active[aria-invalid=true], [type=checkbox][role=switch]:checked:focus[aria-invalid=true] { --pico-border-color: var(--pico-form-element-invalid-border-color); } /** * Input type color */ [type=color]::-webkit-color-swatch-wrapper { padding: 0; } [type=color]::-moz-focus-inner { padding: 0; } [type=color]::-webkit-color-swatch { border: 0; border-radius: calc(var(--pico-border-radius) * 0.5); } [type=color]::-moz-color-swatch { border: 0; border-radius: calc(var(--pico-border-radius) * 0.5); } /** * Input type datetime */ input:not([type=checkbox], [type=radio], [type=range], [type=file]):is([type=date], [type=datetime-local], [type=month], [type=time], [type=week]) { --pico-icon-position: 0.75rem; --pico-icon-width: 1rem; padding-right: calc(var(--pico-icon-width) + var(--pico-icon-position)); background-image: var(--pico-icon-date); background-position: center right var(--pico-icon-position); background-size: var(--pico-icon-width) auto; background-repeat: no-repeat; } input:not([type=checkbox], [type=radio], [type=range], [type=file])[type=time] { background-image: var(--pico-icon-time); } [type=date]::-webkit-calendar-picker-indicator, [type=datetime-local]::-webkit-calendar-picker-indicator, [type=month]::-webkit-calendar-picker-indicator, [type=time]::-webkit-calendar-picker-indicator, [type=week]::-webkit-calendar-picker-indicator { width: var(--pico-icon-width); margin-right: calc(var(--pico-icon-width) * -1); margin-left: var(--pico-icon-position); opacity: 0; } @-moz-document url-prefix() { [type=date], [type=datetime-local], [type=month], [type=time], [type=week] { padding-right: var(--pico-form-element-spacing-horizontal) !important; background-image: none !important; } } [dir=rtl] :is([type=date], [type=datetime-local], [type=month], [type=time], [type=week]) { text-align: right; } /** * Input type file */ [type=file] { --pico-color: var(--pico-muted-color); margin-left: calc(var(--pico-outline-width) * -1); padding: calc(var(--pico-form-element-spacing-vertical) * 0.5) 0; padding-left: var(--pico-outline-width); border: 0; border-radius: 0; background: none; } [type=file]::file-selector-button { margin-right: calc(var(--pico-spacing) / 2); padding: calc(var(--pico-form-element-spacing-vertical) * 0.5) var(--pico-form-element-spacing-horizontal); } [type=file]:is(:hover, :active, :focus)::file-selector-button { --pico-background-color: var(--pico-secondary-hover-background); --pico-border-color: var(--pico-secondary-hover-border); } [type=file]:focus::file-selector-button { --pico-box-shadow: var(--pico-button-hover-box-shadow, 0 0 0 rgba(0, 0, 0, 0)), 0 0 0 var(--pico-outline-width) var(--pico-secondary-focus); } /** * Input type range */ [type=range] { -webkit-appearance: none; -moz-appearance: none; appearance: none; width: 100%; height: 1.25rem; background: none; } [type=range]::-webkit-slider-runnable-track { width: 100%; height: 0.375rem; border-radius: var(--pico-border-radius); background-color: var(--pico-range-border-color); -webkit-transition: background-color var(--pico-transition), box-shadow var(--pico-transition); transition: background-color var(--pico-transition), box-shadow var(--pico-transition); } [type=range]::-moz-range-track { width: 100%; height: 0.375rem; border-radius: var(--pico-border-radius); background-color: var(--pico-range-border-color); -moz-transition: background-color var(--pico-transition), box-shadow var(--pico-transition); transition: background-color var(--pico-transition), box-shadow var(--pico-transition); } [type=range]::-ms-track { width: 100%; height: 0.375rem; border-radius: var(--pico-border-radius); background-color: var(--pico-range-border-color); -ms-transition: background-color var(--pico-transition), box-shadow var(--pico-transition); transition: background-color var(--pico-transition), box-shadow var(--pico-transition); } [type=range]::-webkit-slider-thumb { -webkit-appearance: none; width: 1.25rem; height: 1.25rem; margin-top: -0.4375rem; border: 2px solid var(--pico-range-thumb-border-color); border-radius: 50%; background-color: var(--pico-range-thumb-color); cursor: pointer; -webkit-transition: background-color var(--pico-transition), transform var(--pico-transition); transition: background-color var(--pico-transition), transform var(--pico-transition); } [type=range]::-moz-range-thumb { -webkit-appearance: none; width: 1.25rem; height: 1.25rem; margin-top: -0.4375rem; border: 2px solid var(--pico-range-thumb-border-color); border-radius: 50%; background-color: var(--pico-range-thumb-color); cursor: pointer; -moz-transition: background-color var(--pico-transition), transform var(--pico-transition); transition: background-color var(--pico-transition), transform var(--pico-transition); } [type=range]::-ms-thumb { -webkit-appearance: none; width: 1.25rem; height: 1.25rem; margin-top: -0.4375rem; border: 2px solid var(--pico-range-thumb-border-color); border-radius: 50%; background-color: var(--pico-range-thumb-color); cursor: pointer; -ms-transition: background-color var(--pico-transition), transform var(--pico-transition); transition: background-color var(--pico-transition), transform var(--pico-transition); } [type=range]:active, [type=range]:focus-within { --pico-range-border-color: var(--pico-range-active-border-color); --pico-range-thumb-color: var(--pico-range-thumb-active-color); } [type=range]:active::-webkit-slider-thumb { transform: scale(1.25); } [type=range]:active::-moz-range-thumb { transform: scale(1.25); } [type=range]:active::-ms-thumb { transform: scale(1.25); } /** * Input type search */ input:not([type=checkbox], [type=radio], [type=range], [type=file])[type=search] { padding-inline-start: calc(var(--pico-form-element-spacing-horizontal) + 1.75rem); background-image: var(--pico-icon-search); background-position: center left calc(var(--pico-form-element-spacing-horizontal) + 0.125rem); background-size: 1rem auto; background-repeat: no-repeat; } input:not([type=checkbox], [type=radio], [type=range], [type=file])[type=search][aria-invalid] { padding-inline-start: calc(var(--pico-form-element-spacing-horizontal) + 1.75rem) !important; background-position: center left 1.125rem, center right 0.75rem; } input:not([type=checkbox], [type=radio], [type=range], [type=file])[type=search][aria-invalid=false] { background-image: var(--pico-icon-search), var(--pico-icon-valid); } input:not([type=checkbox], [type=radio], [type=range], [type=file])[type=search][aria-invalid=true] { background-image: var(--pico-icon-search), var(--pico-icon-invalid); } [dir=rtl] :where(input):not([type=checkbox], [type=radio], [type=range], [type=file])[type=search] { background-position: center right 1.125rem; } [dir=rtl] :where(input):not([type=checkbox], [type=radio], [type=range], [type=file])[type=search][aria-invalid] { background-position: center right 1.125rem, center left 0.75rem; } /** * Accordion (
) */ details { display: block; margin-bottom: var(--pico-spacing); } details summary { line-height: 1rem; list-style-type: none; cursor: pointer; transition: color var(--pico-transition); } details summary:not([role]) { color: var(--pico-accordion-close-summary-color); } details summary::-webkit-details-marker { display: none; } details summary::marker { display: none; } details summary::-moz-list-bullet { list-style-type: none; } details summary::after { display: block; width: 1rem; height: 1rem; margin-inline-start: calc(var(--pico-spacing, 1rem) * 0.5); float: right; transform: rotate(-90deg); background-image: var(--pico-icon-chevron); background-position: right center; background-size: 1rem auto; background-repeat: no-repeat; content: ""; transition: transform var(--pico-transition); } details summary:focus { outline: none; } details summary:focus:not([role]) { color: var(--pico-accordion-active-summary-color); } details summary:focus-visible:not([role]) { outline: var(--pico-outline-width) solid var(--pico-primary-focus); outline-offset: calc(var(--pico-spacing, 1rem) * 0.5); color: var(--pico-primary); } details summary[role=button] { width: 100%; text-align: left; } details summary[role=button]::after { height: calc(1rem * var(--pico-line-height, 1.5)); } details[open] > summary { margin-bottom: var(--pico-spacing); } details[open] > summary:not([role]):not(:focus) { color: var(--pico-accordion-open-summary-color); } details[open] > summary::after { transform: rotate(0); } [dir=rtl] details summary { text-align: right; } [dir=rtl] details summary::after { float: left; background-position: left center; } /** * Card (
) */ article { margin-bottom: var(--pico-block-spacing-vertical); padding: var(--pico-block-spacing-vertical) var(--pico-block-spacing-horizontal); border-radius: var(--pico-border-radius); background: var(--pico-card-background-color); box-shadow: var(--pico-card-box-shadow); } article > header, article > footer { margin-right: calc(var(--pico-block-spacing-horizontal) * -1); margin-left: calc(var(--pico-block-spacing-horizontal) * -1); padding: calc(var(--pico-block-spacing-vertical) * 0.66) var(--pico-block-spacing-horizontal); background-color: var(--pico-card-sectioning-background-color); } article > header { margin-top: calc(var(--pico-block-spacing-vertical) * -1); margin-bottom: var(--pico-block-spacing-vertical); border-bottom: var(--pico-border-width) solid var(--pico-card-border-color); border-top-right-radius: var(--pico-border-radius); border-top-left-radius: var(--pico-border-radius); } article > footer { margin-top: var(--pico-block-spacing-vertical); margin-bottom: calc(var(--pico-block-spacing-vertical) * -1); border-top: var(--pico-border-width) solid var(--pico-card-border-color); border-bottom-right-radius: var(--pico-border-radius); border-bottom-left-radius: var(--pico-border-radius); } /** * Group ([role="group"], [role="search"]) */ [role=search], [role=group] { display: inline-flex; position: relative; width: 100%; margin-bottom: var(--pico-spacing); border-radius: var(--pico-border-radius); box-shadow: var(--pico-group-box-shadow, 0 0 0 rgba(0, 0, 0, 0)); vertical-align: middle; transition: box-shadow var(--pico-transition); } [role=search] > *, [role=search] input:not([type=checkbox], [type=radio]), [role=search] select, [role=group] > *, [role=group] input:not([type=checkbox], [type=radio]), [role=group] select { position: relative; flex: 1 1 auto; margin-bottom: 0; } [role=search] > *:not(:first-child), [role=search] input:not([type=checkbox], [type=radio]):not(:first-child), [role=search] select:not(:first-child), [role=group] > *:not(:first-child), [role=group] input:not([type=checkbox], [type=radio]):not(:first-child), [role=group] select:not(:first-child) { margin-left: 0; border-top-left-radius: 0; border-bottom-left-radius: 0; } [role=search] > *:not(:last-child), [role=search] input:not([type=checkbox], [type=radio]):not(:last-child), [role=search] select:not(:last-child), [role=group] > *:not(:last-child), [role=group] input:not([type=checkbox], [type=radio]):not(:last-child), [role=group] select:not(:last-child) { border-top-right-radius: 0; border-bottom-right-radius: 0; } [role=search] > *:focus, [role=search] input:not([type=checkbox], [type=radio]):focus, [role=search] select:focus, [role=group] > *:focus, [role=group] input:not([type=checkbox], [type=radio]):focus, [role=group] select:focus { z-index: 2; } [role=search] button:not(:first-child), [role=search] [type=submit]:not(:first-child), [role=search] [type=reset]:not(:first-child), [role=search] [type=button]:not(:first-child), [role=search] [role=button]:not(:first-child), [role=search] input:not([type=checkbox], [type=radio]):not(:first-child), [role=search] select:not(:first-child), [role=group] button:not(:first-child), [role=group] [type=submit]:not(:first-child), [role=group] [type=reset]:not(:first-child), [role=group] [type=button]:not(:first-child), [role=group] [role=button]:not(:first-child), [role=group] input:not([type=checkbox], [type=radio]):not(:first-child), [role=group] select:not(:first-child) { margin-left: calc(var(--pico-border-width) * -1); } [role=search] button, [role=search] [type=submit], [role=search] [type=reset], [role=search] [type=button], [role=search] [role=button], [role=group] button, [role=group] [type=submit], [role=group] [type=reset], [role=group] [type=button], [role=group] [role=button] { width: auto; } @supports selector(:has(*)) { [role=search]:has(button:focus, [type=submit]:focus, [type=button]:focus, [role=button]:focus), [role=group]:has(button:focus, [type=submit]:focus, [type=button]:focus, [role=button]:focus) { --pico-group-box-shadow: var(--pico-group-box-shadow-focus-with-button); } [role=search]:has(button:focus, [type=submit]:focus, [type=button]:focus, [role=button]:focus) input:not([type=checkbox], [type=radio]), [role=search]:has(button:focus, [type=submit]:focus, [type=button]:focus, [role=button]:focus) select, [role=group]:has(button:focus, [type=submit]:focus, [type=button]:focus, [role=button]:focus) input:not([type=checkbox], [type=radio]), [role=group]:has(button:focus, [type=submit]:focus, [type=button]:focus, [role=button]:focus) select { border-color: transparent; } [role=search]:has(input:not([type=submit], [type=button]):focus, select:focus), [role=group]:has(input:not([type=submit], [type=button]):focus, select:focus) { --pico-group-box-shadow: var(--pico-group-box-shadow-focus-with-input); } [role=search]:has(input:not([type=submit], [type=button]):focus, select:focus) button, [role=search]:has(input:not([type=submit], [type=button]):focus, select:focus) [type=submit], [role=search]:has(input:not([type=submit], [type=button]):focus, select:focus) [type=button], [role=search]:has(input:not([type=submit], [type=button]):focus, select:focus) [role=button], [role=group]:has(input:not([type=submit], [type=button]):focus, select:focus) button, [role=group]:has(input:not([type=submit], [type=button]):focus, select:focus) [type=submit], [role=group]:has(input:not([type=submit], [type=button]):focus, select:focus) [type=button], [role=group]:has(input:not([type=submit], [type=button]):focus, select:focus) [role=button] { --pico-button-box-shadow: 0 0 0 var(--pico-border-width) var(--pico-primary-border); --pico-button-hover-box-shadow: 0 0 0 var(--pico-border-width) var(--pico-primary-hover-border); } [role=search] button:focus, [role=search] [type=submit]:focus, [role=search] [type=reset]:focus, [role=search] [type=button]:focus, [role=search] [role=button]:focus, [role=group] button:focus, [role=group] [type=submit]:focus, [role=group] [type=reset]:focus, [role=group] [type=button]:focus, [role=group] [role=button]:focus { box-shadow: none; } } [role=search] > *:first-child { border-top-left-radius: 5rem; border-bottom-left-radius: 5rem; } [role=search] > *:last-child { border-top-right-radius: 5rem; border-bottom-right-radius: 5rem; } /** * Loading ([aria-busy=true]) */ [aria-busy=true]:not(input, select, textarea, html) { white-space: nowrap; } [aria-busy=true]:not(input, select, textarea, html)::before { display: inline-block; width: 1em; height: 1em; background-image: var(--pico-icon-loading); background-size: 1em auto; background-repeat: no-repeat; content: ""; vertical-align: -0.125em; } [aria-busy=true]:not(input, select, textarea, html):not(:empty)::before { margin-inline-end: calc(var(--pico-spacing) * 0.5); } [aria-busy=true]:not(input, select, textarea, html):empty { text-align: center; } button[aria-busy=true], [type=submit][aria-busy=true], [type=button][aria-busy=true], [type=reset][aria-busy=true], [role=button][aria-busy=true], a[aria-busy=true] { pointer-events: none; } /** * Modal () */ :root { --pico-scrollbar-width: 0px; } dialog { display: flex; z-index: 999; position: fixed; top: 0; right: 0; bottom: 0; left: 0; align-items: center; justify-content: center; width: inherit; min-width: 100%; height: inherit; min-height: 100%; padding: 0; border: 0; -webkit-backdrop-filter: var(--pico-modal-overlay-backdrop-filter); backdrop-filter: var(--pico-modal-overlay-backdrop-filter); background-color: var(--pico-modal-overlay-background-color); color: var(--pico-color); } dialog article { width: 100%; max-height: calc(100vh - var(--pico-spacing) * 2); margin: var(--pico-spacing); overflow: auto; } @media (min-width: 576px) { dialog article { max-width: 510px; } } @media (min-width: 768px) { dialog article { max-width: 700px; } } dialog article > header > * { margin-bottom: 0; } dialog article > header :is(a, button)[rel=prev] { margin: 0; margin-left: var(--pico-spacing); padding: 0; float: right; } dialog article > footer { text-align: right; } dialog article > footer button, dialog article > footer [role=button] { margin-bottom: 0; } dialog article > footer button:not(:first-of-type), dialog article > footer [role=button]:not(:first-of-type) { margin-left: calc(var(--pico-spacing) * 0.5); } dialog article :is(a, button)[rel=prev] { display: block; width: 1rem; height: 1rem; margin-top: calc(var(--pico-spacing) * -1); margin-bottom: var(--pico-spacing); margin-left: auto; border: none; background-image: var(--pico-icon-close); background-position: center; background-size: auto 1rem; background-repeat: no-repeat; background-color: transparent; opacity: 0.5; transition: opacity var(--pico-transition); } dialog article :is(a, button)[rel=prev]:is([aria-current]:not([aria-current=false]), :hover, :active, :focus) { opacity: 1; } dialog:not([open]), dialog[open=false] { display: none; } /** * Nav */ :where(nav li)::before { float: left; content: "​"; } nav, nav ul { display: flex; } nav { justify-content: space-between; overflow: visible; } nav ol, nav ul { align-items: center; margin-bottom: 0; padding: 0; list-style: none; } nav ol:first-of-type, nav ul:first-of-type { margin-left: calc(var(--pico-nav-element-spacing-horizontal) * -1); } nav ol:last-of-type, nav ul:last-of-type { margin-right: calc(var(--pico-nav-element-spacing-horizontal) * -1); } nav li { display: inline-block; margin: 0; padding: var(--pico-nav-element-spacing-vertical) var(--pico-nav-element-spacing-horizontal); } nav li :where(a, [role=link]) { display: inline-block; margin: calc(var(--pico-nav-link-spacing-vertical) * -1) calc(var(--pico-nav-link-spacing-horizontal) * -1); padding: var(--pico-nav-link-spacing-vertical) var(--pico-nav-link-spacing-horizontal); border-radius: var(--pico-border-radius); } nav li :where(a, [role=link]):not(:hover) { text-decoration: none; } nav li button, nav li [role=button], nav li [type=button], nav li input:not([type=checkbox], [type=radio], [type=range], [type=file]), nav li select { height: auto; margin-right: inherit; margin-bottom: 0; margin-left: inherit; padding: calc(var(--pico-nav-link-spacing-vertical) - var(--pico-border-width) * 2) var(--pico-nav-link-spacing-horizontal); } nav[aria-label=breadcrumb] { align-items: center; justify-content: start; } nav[aria-label=breadcrumb] ul li:not(:first-child) { margin-inline-start: var(--pico-nav-link-spacing-horizontal); } nav[aria-label=breadcrumb] ul li a { margin: calc(var(--pico-nav-link-spacing-vertical) * -1) 0; margin-inline-start: calc(var(--pico-nav-link-spacing-horizontal) * -1); } nav[aria-label=breadcrumb] ul li:not(:last-child)::after { display: inline-block; position: absolute; width: calc(var(--pico-nav-link-spacing-horizontal) * 4); margin: 0 calc(var(--pico-nav-link-spacing-horizontal) * -1); content: var(--pico-nav-breadcrumb-divider); color: var(--pico-muted-color); text-align: center; text-decoration: none; white-space: nowrap; } nav[aria-label=breadcrumb] a[aria-current]:not([aria-current=false]) { background-color: transparent; color: inherit; text-decoration: none; pointer-events: none; } aside nav, aside ol, aside ul, aside li { display: block; } aside li { padding: calc(var(--pico-nav-element-spacing-vertical) * 0.5) var(--pico-nav-element-spacing-horizontal); } aside li a { display: block; } aside li [role=button] { margin: inherit; } [dir=rtl] nav[aria-label=breadcrumb] ul li:not(:last-child) ::after { content: "\\"; } /** * Progress */ progress { display: inline-block; vertical-align: baseline; } progress { -webkit-appearance: none; -moz-appearance: none; display: inline-block; appearance: none; width: 100%; height: 0.5rem; margin-bottom: calc(var(--pico-spacing) * 0.5); overflow: hidden; border: 0; border-radius: var(--pico-border-radius); background-color: var(--pico-progress-background-color); color: var(--pico-progress-color); } progress::-webkit-progress-bar { border-radius: var(--pico-border-radius); background: none; } progress[value]::-webkit-progress-value { background-color: var(--pico-progress-color); -webkit-transition: inline-size var(--pico-transition); transition: inline-size var(--pico-transition); } progress::-moz-progress-bar { background-color: var(--pico-progress-color); } @media (prefers-reduced-motion: no-preference) { progress:indeterminate { background: var(--pico-progress-background-color) linear-gradient(to right, var(--pico-progress-color) 30%, var(--pico-progress-background-color) 30%) top left/150% 150% no-repeat; animation: progress-indeterminate 1s linear infinite; } progress:indeterminate[value]::-webkit-progress-value { background-color: transparent; } progress:indeterminate::-moz-progress-bar { background-color: transparent; } } @media (prefers-reduced-motion: no-preference) { [dir=rtl] progress:indeterminate { animation-direction: reverse; } } @keyframes progress-indeterminate { 0% { background-position: 200% 0; } 100% { background-position: -200% 0; } } /** * Tooltip ([data-tooltip]) */ [data-tooltip] { position: relative; } [data-tooltip]:not(a, button, input) { border-bottom: 1px dotted; text-decoration: none; cursor: help; } [data-tooltip][data-placement=top]::before, [data-tooltip][data-placement=top]::after, [data-tooltip]::before, [data-tooltip]::after { display: block; z-index: 99; position: absolute; bottom: 100%; left: 50%; padding: 0.25rem 0.5rem; overflow: hidden; transform: translate(-50%, -0.25rem); border-radius: var(--pico-border-radius); background: var(--pico-tooltip-background-color); content: attr(data-tooltip); color: var(--pico-tooltip-color); font-style: normal; font-weight: var(--pico-font-weight); font-size: 0.875rem; text-decoration: none; text-overflow: ellipsis; white-space: nowrap; opacity: 0; pointer-events: none; } [data-tooltip][data-placement=top]::after, [data-tooltip]::after { padding: 0; transform: translate(-50%, 0rem); border-top: 0.3rem solid; border-right: 0.3rem solid transparent; border-left: 0.3rem solid transparent; border-radius: 0; background-color: transparent; content: ""; color: var(--pico-tooltip-background-color); } [data-tooltip][data-placement=bottom]::before, [data-tooltip][data-placement=bottom]::after { top: 100%; bottom: auto; transform: translate(-50%, 0.25rem); } [data-tooltip][data-placement=bottom]:after { transform: translate(-50%, -0.3rem); border: 0.3rem solid transparent; border-bottom: 0.3rem solid; } [data-tooltip][data-placement=left]::before, [data-tooltip][data-placement=left]::after { top: 50%; right: 100%; bottom: auto; left: auto; transform: translate(-0.25rem, -50%); } [data-tooltip][data-placement=left]:after { transform: translate(0.3rem, -50%); border: 0.3rem solid transparent; border-left: 0.3rem solid; } [data-tooltip][data-placement=right]::before, [data-tooltip][data-placement=right]::after { top: 50%; right: auto; bottom: auto; left: 100%; transform: translate(0.25rem, -50%); } [data-tooltip][data-placement=right]:after { transform: translate(-0.3rem, -50%); border: 0.3rem solid transparent; border-right: 0.3rem solid; } [data-tooltip]:focus::before, [data-tooltip]:focus::after, [data-tooltip]:hover::before, [data-tooltip]:hover::after { opacity: 1; } @media (hover: hover) and (pointer: fine) { [data-tooltip]:focus::before, [data-tooltip]:focus::after, [data-tooltip]:hover::before, [data-tooltip]:hover::after { --pico-tooltip-slide-to: translate(-50%, -0.25rem); transform: translate(-50%, 0.75rem); animation-duration: 0.2s; animation-fill-mode: forwards; animation-name: tooltip-slide; opacity: 0; } [data-tooltip]:focus::after, [data-tooltip]:hover::after { --pico-tooltip-caret-slide-to: translate(-50%, 0rem); transform: translate(-50%, -0.25rem); animation-name: tooltip-caret-slide; } [data-tooltip][data-placement=bottom]:focus::before, [data-tooltip][data-placement=bottom]:focus::after, [data-tooltip][data-placement=bottom]:hover::before, [data-tooltip][data-placement=bottom]:hover::after { --pico-tooltip-slide-to: translate(-50%, 0.25rem); transform: translate(-50%, -0.75rem); animation-name: tooltip-slide; } [data-tooltip][data-placement=bottom]:focus::after, [data-tooltip][data-placement=bottom]:hover::after { --pico-tooltip-caret-slide-to: translate(-50%, -0.3rem); transform: translate(-50%, -0.5rem); animation-name: tooltip-caret-slide; } [data-tooltip][data-placement=left]:focus::before, [data-tooltip][data-placement=left]:focus::after, [data-tooltip][data-placement=left]:hover::before, [data-tooltip][data-placement=left]:hover::after { --pico-tooltip-slide-to: translate(-0.25rem, -50%); transform: translate(0.75rem, -50%); animation-name: tooltip-slide; } [data-tooltip][data-placement=left]:focus::after, [data-tooltip][data-placement=left]:hover::after { --pico-tooltip-caret-slide-to: translate(0.3rem, -50%); transform: translate(0.05rem, -50%); animation-name: tooltip-caret-slide; } [data-tooltip][data-placement=right]:focus::before, [data-tooltip][data-placement=right]:focus::after, [data-tooltip][data-placement=right]:hover::before, [data-tooltip][data-placement=right]:hover::after { --pico-tooltip-slide-to: translate(0.25rem, -50%); transform: translate(-0.75rem, -50%); animation-name: tooltip-slide; } [data-tooltip][data-placement=right]:focus::after, [data-tooltip][data-placement=right]:hover::after { --pico-tooltip-caret-slide-to: translate(-0.3rem, -50%); transform: translate(-0.05rem, -50%); animation-name: tooltip-caret-slide; } } @keyframes tooltip-slide { to { transform: var(--pico-tooltip-slide-to); opacity: 1; } } @keyframes tooltip-caret-slide { 50% { opacity: 0; } to { transform: var(--pico-tooltip-caret-slide-to); opacity: 1; } } /** * Accessibility & User interaction */ [aria-controls] { cursor: pointer; } [aria-disabled=true], [disabled] { cursor: not-allowed; } [aria-hidden=false][hidden] { display: initial; } [aria-hidden=false][hidden]:not(:focus) { clip: rect(0, 0, 0, 0); position: absolute; } a, area, button, input, label, select, summary, textarea, [tabindex] { -ms-touch-action: manipulation; } [dir=rtl] { direction: rtl; } /** * Reduce Motion Features */ @media (prefers-reduced-motion: reduce) { *:not([aria-busy=true]), :not([aria-busy=true])::before, :not([aria-busy=true])::after { background-attachment: initial !important; animation-duration: 1ms !important; animation-delay: -1ms !important; animation-iteration-count: 1 !important; scroll-behavior: auto !important; transition-delay: 0s !important; transition-duration: 0s !important; } } camping-3.2.6/docs/assets/styles.css000066400000000000000000000006601465547410100174400ustar00rootroot00000000000000@import url("pico.css"); /* Variables */ :root { /* Spacing */ --camp-spacing: 0.25rem; /* 4px */ --cs-xs: calc(var(--camp-spacing) * 1); /* 4px */ --cs-s: calc(var(--camp-spacing) * 2); /* 8px */ --cs-m: calc(var(--camp-spacing) * 4); /* 16px */ --cs-l: calc(var(--camp-spacing) * 8); /* 32px */ --cs-xl: calc(var(--camp-spacing) * 16); /* 64px */ --cs-xxl: calc(var(--camp-spacing) * 32); /* 128px */ } camping-3.2.6/docs/assets/typography.css000066400000000000000000000005201465547410100203160ustar00rootroot00000000000000/* typography.css */ /* modifiers */ /* text-alignment */ .ta-l { text-align: left; } .ta-c { text-align: center; } .ta-r { text-align: right; } /* font-weight */ .fw-400 { font-weight: 400; } .fw-500 { font-weight: 500; } .fw-600 { font-weight: 600; } .fw-700 { font-weight: 700; } .fw-800 { font-weight: 800; } camping-3.2.6/docs/campingtrip.md000066400000000000000000000271031465547410100167410ustar00rootroot00000000000000# How does Camping work? This is an academic document written to help people, but mostly me, understand what Camping is doing and in what sequence. Why? Because I want to make camping better, but how do you make it better unless you understand what you've got? # Start Camping starts off with some simple code you type: `camping nuts.rb` into your terminal and the camping gem is loaded and executed. This assumes that you've installed camping via ruby gems: `gem install camping`. Gems are then given a binary command you can use on the command line, in our case **camping**. The camping command accepts a file as an argument and maybe some options. This command calls a *binary* that's found the camping gem, this is what it looks like: ```ruby #!/usr/bin/env ruby $:.unshift File.dirname(__FILE__) + "/../lib" require 'camping' require 'camping/server' begin Camping::Server.start rescue OptionParser::ParseError => ex STDERR.puts "!! #{ex.message}" puts "** use `#{File.basename($0)} --help` for more details..." exit 1 end ``` First wee see `$:.unshift File.dirname(__FILE__) + "/../lib"`, `$:`, is a global variable, that contains the loadpath for scripts. Every ruby file is a script, you may be more familiar with `$LOAD_PATH` which is an alias for the `$:` global. Next `unshift` is an array method that prepends an item to the beginning of an array. `File` is a builtin ruby class that lets you work with files. `File.dirname(file)` returns a string of the complete file path of the file given, except for the file's name. In our case we're using `__FILE__` to return the current file name, which is the binary file in the camping gem. the last portion: `+ "/../lib"` appends the string to the directory path that we just got. All of this is done to ensure that the `lib` folder where all of camping's code resides is added to the script load path. Next we require camping: ```ruby require 'camping' require 'camping/server' ``` Which loads camping and it's server code in to the current script context. ```ruby Camping::Server.start ``` The above code finally Starts the camping server. So let's take a look at the server: ```ruby require 'irb' require 'erb' require 'rack' require 'camping/reloader' require 'camping/commands' # == The Camping Server (for development) # # Camping includes a pretty nifty server which is built for development. # It follows these rules: # # * Load all Camping apps in a file. # * Mount those apps according to their name. (e.g. Blog is mounted at /blog.) # * Run each app's create method upon startup. # * Reload the app if its modification time changes. # * Reload the app if it requires any files under the same directory and one # of their modification times changes. # * Support the X-Sendfile header. # # Run it like this: # # camping blog.rb # Mounts Blog at / # # And visit http://localhost:3301/ in your browser. module Camping class Server < Rack::Server class Options if home = ENV['HOME'] # POSIX DB = File.join(home, '.camping.db') RC = File.join(home, '.campingrc') elsif home = ENV['APPDATA'] # MSWIN DB = File.join(home, 'Camping.db') RC = File.join(home, 'Campingrc') else DB = nil RC = nil end HOME = File.expand_path(home) + '/' def parse!(args) args = args.dup options = {} opt_parser = OptionParser.new("", 24, ' ') do |opts| opts.banner = "Usage: camping my-camping-app.rb" opts.define_head "#{File.basename($0)}, the microframework ON-button for ruby #{RUBY_VERSION} (#{RUBY_RELEASE_DATE}) [#{RUBY_PLATFORM}]" opts.separator "" opts.separator "Specific options:" opts.on("-h", "--host HOSTNAME", "Host for web server to bind to (default is all IPs)") { |v| options[:Host] = v } opts.on("-p", "--port NUM", "Port for web server (defaults to 3301)") { |v| options[:Port] = v } db = DB.sub(HOME, '~/') if DB opts.on("-d", "--database FILE", "SQLite3 database path (defaults to #{db ? db : ''})") { |db_path| options[:database] = db_path } opts.on("-C", "--console", "Run in console mode with IRB") { options[:server] = "console" } server_list = ["thin", "webrick", "console"] opts.on("-s", "--server NAME", "Server to force (#{server_list.join(', ')})") { |v| options[:server] = v } opts.separator "" opts.separator "Common options:" # No argument, shows at tail. This will print an options summary. # Try it and see! opts.on("-?", "--help", "Show this message") do puts opts exit end # Another typical switch to print the version. opts.on("-m", "--mounting", "Shows Mounting Guide") do puts "Mounting Guide" puts "" puts "To mount your horse, hop up on the side and put it." exit end # Another typical switch to print the version. opts.on("-v", "--version", "Show version") do puts Gem.loaded_specs['camping'].version exit end end opt_parser.parse!(args) # If no Arguments were called. if args.empty? args << "cabin.rb" # adds cabin.rb as a default camping entrance file end # Parses the first argument as the script to load into the server. options[:script] = args.shift options end end def initialize(*) super @reloader = Camping::Reloader.new(options[:script]) do |app| if !app.options.has_key?(:dynamic_templates) app.options[:dynamic_templates] = true end if !Camping::Models.autoload?(:Base) && options[:database] Camping::Models::Base.establish_connection( :adapter => 'sqlite3', :database => options[:database] ) end end end def opt_parser Options.new end def default_options super.merge({ :Port => 3301, :database => Options::DB }) end def middleware h = super h["development"] << [XSendfile] h end def start if options[:server] == "console" puts "** Starting console" @reloader.reload! r = @reloader eval("self", TOPLEVEL_BINDING).meta_def(:reload!) { r.reload!; nil } ARGV.clear IRB.start exit else name = server.name[/\w+$/] puts "** Starting #{name} on #{options[:Host]}:#{options[:Port]}" super end end # defines the public directory to be /public def public_dir File.expand_path('../public', @reloader.file) end # add the public directory as a Rack app serving files first, then the # current value of self, which is our camping apps, as an app. def app Rack::Cascade.new([Rack::Files.new(public_dir), self], [405, 404, 403]) end # path_matches? # accepts a regular expression string # in our case our apps and controllers def path_matches?(path, *reg) reg.each do |r| return true if Regexp.new(r).match? path end false end # call(env) res # == How routing works # # The first app added using Camping.goes is set at the root, we walk through # the defined routes of the first app to see if there is a match. # With no match we then walk through every other defined app. # Each subsequent app defined is loaded at a directory named after them: # # camping.goes :Nuts # Mounts Nuts at / # camping.goes :Auth # Mounts Auth at /auth/ # camping.goes :Blog # Mounts Blog at /blog/ # def call(env) @reloader.reload apps = @reloader.apps # our switch statement iterates through possible app outcomes, no apps # loaded, one app loaded, or multiple apps loaded. case apps.length when 0 [200, {'Content-Type' => 'text/html'}, ["I'm sorry but no apps were found."]] when 1 apps.values.first.call(env) # When we have one else # 2 and up get special treatment count = 0 apps.each do |name, app| if count == 0 app.routes.each do |r| if (path_matches?(env['PATH_INFO'], r)) next end return app.call(env) unless !(path_matches?(env['PATH_INFO'], r)) end else mount = name.to_s.downcase case env["PATH_INFO"] when %r{^/#{mount}} env["SCRIPT_NAME"] = env["SCRIPT_NAME"] + $& env["PATH_INFO"] = $' return app.call(env) when %r{^/code/#{mount}} return [200, {'Content-Type' => 'text/plain', 'X-Sendfile' => @reloader.file}, []] end end count += 1 end # Just return the first app if we didn't find a match. return apps.values.first.call(env) end end class XSendfile def initialize(app) @app = app end def call(env) status, headers, body = @app.call(env) if key = headers.keys.grep(/c-sendfile/i).first filename = headers[key] content = open(filename,'rb') { | io | io.read} headers['content-length'] = size(content).to_s body = [content] end return status, headers, body end if "".respond_to?(:bytesize) def size(str) str.bytesize end else def size(str) str.size end end end end end ``` The beginning of Server loads the required code to get Camping started, and then opens the `Camping` module: ```ruby module Camping class Server < Rack::Server end end ``` `Server` inherits from `Rack::Server`. Camping is Rack based to give ourselves a predictable interface for our web server code. Consequentally a lot of utilities useful for webservers are just baked into Rack, It gives Camping the chance to do what it does best, magic! The first class declared in `Server` is called `Options`. It's in charge of parsing the command line options supplied to camping, and then supplying those options as a hash for the program further down the line. This class is declared inside of the `Server` class so that we can encapsulate the behaviour of options within the server. Which is pretty nice. Next is initialize: ```ruby def initialize(*) super @reloader = Camping::Reloader.new(options[:script]) do |app| if !app.options.has_key?(:dynamic_templates) app.options[:dynamic_templates] = true end if !Camping::Models.autoload?(:Base) && options[:database] Camping::Models::Base.establish_connection( :adapter => 'sqlite3', :database => options[:database] ) end end end ``` `initialize` is the method called whenever you instantiate a new object of a class. You'll notice that the line of code in the method is a call to `super`. Because `Camping::Server` is a subclass of `Rack::Server`, and to get things rolling we first call `Rack::Server`'s initialize. Afterwards we setup the reloader, and optionally include the database. When you call super naked like that it passes along whatever arguments were sent to the method that called super. In our case It's a splat: `initialize(*)`, so everything is sent along. Remember earlier from the Camping binary where we start the server? : `Camping::Server.start`, You may notice that this is a call to a class method `start`, but we don't declare any class methods in `Camping::Server` only instance methods. camping-3.2.6/docs/index.html000066400000000000000000000006171465547410100161000ustar00rootroot00000000000000 Hello world!

Camping

The best fucking thing ever

camping-3.2.6/examples/000077500000000000000000000000001465547410100147655ustar00rootroot00000000000000camping-3.2.6/examples/README000066400000000000000000000003571465547410100156520ustar00rootroot00000000000000If you've got camping installed, why not run all these examples using TheCampingServer? From this directory, run: `camping blog.rb` for example. NOTE: You'll need the active_record and acts_as_versioned gems installed for some of these. camping-3.2.6/examples/blog.rb000077500000000000000000000164621465547410100162510ustar00rootroot00000000000000#!/usr/bin/env ruby $:.unshift File.dirname(__FILE__) + '/../lib' require 'camping' require 'camping/ar' require 'camping/session' require 'redcloth' Camping.goes :Blog module Blog include Camping::Session module Models class Post < Base belongs_to :user before_save do |record| cloth = RedCloth.new(record.body) cloth.hard_breaks = false record.html_body = cloth.to_html end end class Comment < Base; belongs_to :user; end class User < Base; end class BasicFields < V 1.1 def self.up create_table :blog_posts, :force => true do |t| t.integer :user_id, :null => false t.string :title, :limit => 255 t.text :body, :html_body t.timestamps end create_table :blog_users, :force => true do |t| t.string :username, :password end create_table :blog_comments, :force => true do |t| t.integer :post_id, :null => false t.string :username t.text :body, :html_body t.timestamps end User.create :username => 'admin', :password => 'camping' end def self.down drop_table :blog_posts drop_table :blog_users drop_table :blog_comments end end end module Controllers class Index def get @posts = Post.all(:order => 'updated_at DESC') render :index end end class PostNew def get require_login! @post = Post.new render :add end def post require_login! post = Post.create(:title => input.post_title, :body => input.post_body, :user_id => @state.user_id) redirect PostN, post end end class PostN def get(post_id) @post = Post.find(post_id) render :view end end class Edit < R '/post/(\d+)/edit' def get(post_id) require_login! @post = Post.find(post_id) render :edit end def post(post_id) require_login! @post = Post.find(post_id) @post.update :title => input.post_title, :body => input.post_body redirect PostN, @post end end class Login def get render :login end def post @user = User.find_by_username_and_password(input.username, input.password) if @user @state.user_id = @user.id redirect R(Index) else @info = 'Wrong username or password.' end render :login end end class Logout def get @state.user_id = nil redirect Index end end class Style < R '/styles\.css' STYLE = File.read(__FILE__).gsub(/.*__END__/m, '') def get @headers['content-type'] = 'text/css; charset=utf-8' STYLE end end end module Helpers def logged_in? !!@state.user_id end def require_login! unless logged_in? redirect Controllers::Login throw :halt end end end module Views def layout html do head do title 'My Blog' link :rel => 'stylesheet', :type => 'text/css', :href => '/styles.css', :media => 'screen' end body do h1 { a 'My Blog', :href => R(Index) } div.wrapper! do self << yield end hr p.footer! do if logged_in? _admin_menu else a 'Login', :href => R(Login) text ' to the adminpanel' end text! ' – Powered by ' a 'Camping', :href => 'http://camping.rubyforge.org/' end end end end def index if @posts.empty? h2 'No posts' p do text 'Could not find any posts. Feel free to ' a 'add one', :href => R(PostNew) text ' yourself. ' end else @posts.each do |post| _post(post) end end end def login h2 'Login' p.info @info if @info form :action => R(Login), :method => 'post' do input :name => 'to', :type => 'hidden', :value => @to if @to label 'Username', :for => 'username' input :name => 'username', :id => 'username', :type => 'text' label 'Password', :for => 'password' input :name => 'password', :id => 'password', :type => 'password' input :type => 'submit', :class => 'submit', :value => 'Login' end end def add _form(@post, :action => R(PostNew)) end def edit _form(@post, :action => R(Edit, @post)) end def view _post(@post) end # partials def _admin_menu text! [['Log out', R(Logout)], ['New', R(PostNew)]].map { |name, to| mab { a name, :href => to} }.join(' – ') end def _post(post) h2 { a post.title, :href => R(PostN, post) } p.info do text! "Written by #{post.user.username} " text post.updated_at.strftime('%B %M, %Y @ %H:%M ') _post_menu(post) end text! post.html_body end def _post_menu(post) if logged_in? a '(edit)', :href => R(Edit, post) end end def _form(post, opts) form({:method => 'post'}.merge(opts)) do label 'Title', :for => 'post_title' input :name => 'post_title', :id => 'post_title', :type => 'text', :value => post.title label 'Body', :for => 'post_body' textarea post.body, :name => 'post_body', :id => 'post_body' input :type => 'hidden', :name => 'post_id', :value => post.id input :type => 'submit', :class => 'submit', :value => 'Submit' end end end end def Blog.create Blog::Models.create_schema :assume => (Blog::Models::Post.table_exists? ? 1.0 : 0.0) end __END__ * { margin: 0; padding: 0; } body { font: normal 14px Arial, 'Bitstream Vera Sans', Helvetica, sans-serif; line-height: 1.5; } h1, h2, h3, h4 { font-family: Georgia, serif; font-weight: normal; } h1 { background-color: #EEE; border-bottom: 5px solid #6F812D; outline: 5px solid #9CB441; font-weight: normal; font-size: 3em; padding: 0.5em 0; text-align: center; } h2 { margin-top: 1em; font-size: 2em; } h1 a { color: #143D55; text-decoration: none } h1 a:hover { color: #143D55; text-decoration: underline } h2 a { color: #287AA9; text-decoration: none } h2 a:hover { color: #287AA9; text-decoration: underline } #wrapper { margin: 3em auto; width: 700px; } p { margin-bottom: 1em; } p.info, p#footer { color: #999; margin-left: 1em; } p.info a, p#footer a { color: #999; } p.info a:hover, p#footer a:hover { text-decoration: none; } a { color: #6F812D; } a:hover { color: #9CB441; } hr { border-width: 5px 0; border-style: solid; border-color: #9CB441; border-bottom-color: #6F812D; height: 0; } p#footer { font-size: 0.9em; margin: 0; padding: 1em; text-align: center; } label { display: block; width: 100%; } input, textarea { padding: 5px; margin-bottom: 1em; margin-right: 490px; width: 200px; } input.submit { width: auto; } textarea { font: normal 14px Arial, 'Bitstream Vera Sans', Helvetica, sans-serif; height: 300px; width: 400px; } camping-3.2.6/extras/000077500000000000000000000000001465547410100144555ustar00rootroot00000000000000camping-3.2.6/extras/images/000077500000000000000000000000001465547410100157225ustar00rootroot00000000000000camping-3.2.6/extras/images/badge.gif000066400000000000000000000247411465547410100174630ustar00rootroot00000000000000GIF89a3f3333f333ff3fffff3f3f̙3f3333f3333333333f3333333f3f33ff3f3f3f3333f3333333f3̙333333f333ff3ffffff3f33f3ff3f3f3ffff3fffffffffff3fffffff3fff̙ffff3fffff3f̙3333f33̙3ff3ffff̙f3f̙3f̙̙3f̙3f3333f333ff3fffff̙̙3̙f̙̙̙3f̙3f3f3333f333ff3fffff3f3f̙3fM!, H*\ȰÇ#JHŋ3jȱǏ? TG$L@\2:{`~g+5|BIM, H = d ўR 4ui =4yB2L3ZaʊCΚ Ī"lʓO)RLMbJV R 1TM*K{3?eRn-hN m[hp@Y-&\4ĩ"=Zkd%fpQ^ر%{7VȝG[57y·֭t]g=U!Wy֜'x%('\pvU^T_,&X{x\3`7`%X"ȕZbjDvVy65,8)W\rX%qФVuML |\) +g\Msz]eZF,b^j`p2:5W d M [[{<Te_iy'䊄h̀Ҝ يuWUkY9"˽9%4Zq=byN:nRɊJحRYa]ZԲG|Ι颜!VLzSIxXضT+b+&ն)cECUr( dYY{@wp{ rma RWx |tqX<k Mʘ/iLR=zwc^l+2A]-0C3u 4D1,Wl=),_ \)}ԹGmrl{E3 R4М}^\1tWHxX}dsJxyy[y .M 4 ^,93D92'2Mr0ⴰwUOHka^Qx1]_9 dK9 8ʆ?ͬfWV\P1ݮTEYL 2MZp_Pb#w6,4laZ\9ax`=4=R)(X! 9|ADSu@,S[v<غVr_$ 1<-Ӹ3(TUCE1Myz. 30 ix?%kC~^[d5agH3GIx/b\J2f'<p Ke$׶2RE" :q6t*AP'1aA\<~kVo:Otu/"\_m IAIK:q20l*Y({" kyTekd̨m.s)/%E Fh%*JT~gY/|Dmebqn 1UIu Y*[1CΠ6|7+8@ݰ/lj:xs5C5bg4SS-Dw_KIXSD{4NٙZfx7?9ռԌP͙ zh!Mp3E$pPNM+xIӞ. qی8E~ӤiydB 'k e uy=BWW8gh꾉O Kw 2PS 1}b/"u:Z v S _mf6D11"P'k@ 3 d&5׃ȲQ%>1q;E" Pe~U'=ߢ06@]S-<1*w;%R +qS!b>tc&#"{XDoh}Q}+/!,Wp XW%Ȳ~!r lStrMN!?EGP1"kq xw! 7,pNӐ Ry7 1UyqȦ/"B [.I:+%}"i!W C)'#q+P+MvVUBʐw)lsVӎB&PQJɱt!W!7H A$k}[Y ҃zɓ!G)D j ywǕjl`+1, ;=ezUWY9!FqֱW@VcDOyA K8<ɆB9} E2'XO &DͶriU!ҕjz4=hR),]\]]`1Y}UrQaw9}_w;5bvQd1%w=hEtnT-vh7xq^wLwyC2\0٢+O`[#S " >T/rVڄpCJQv~8hWRфh!yaé/1i21/| vM1($΂ʡ0B. d oF  "q;@!eaUИp\90\ I-!,P`P0TA' gau%)"J P!gTJiD$,y!%b61ࢋR!+P5%2{UyELאؘV,4AWQxNd5|]VOeda N$UǮ\m<܍ܩpI˴Zfm+,X$ЩVӨ RynTrc*WhT*]\  I R<,Kř,ɍy<";qv p+2bY}ʝ:lí3ZV!\ǩk0j[l7XK1];|1DI!"ݫR Jtu- q}Y1UNP1<X|$2ɟ]l=lmq5C #cUV JՃ,cڏ[-Kzgz=ɱ ĻΏYL]ØÂx)\+eP1)`]{,@L՜>dmNVinZZMnPP:>ɝ>ɾ>M~ͬu+U".QT=,ɻ^+$pI&=,+@vZ=Φlp4;0m+Z,O Q! QJ^)<Ėlӎ;eDm>ʟE( H߿߭LY2ݔ-+P$PIn q!r!'G 5O@o+T S~/1CoT{Y<%n5 a,!"pm+RwiM %|N_hM/ ckJl^5 nN;{1OP$O1. } )`2PØ~ANU'/+N bJA+2p  A++ Xd0FD+XX +(qࡂ81"'1|Rb*K,kXat^|إ v G+,d!PrYU^e.e<&jY (|C-`Mv;MӢ&Lۉ?T('=@(zD9b" (V,%2 ;p),4V:(%) ChZ7Vj" :zС$K=ZJDQJӯ"V@YM.`.0RaHУ=#'*z(kR*($vB" 2q& (22!SNQ=dH@T/Qղh*i'+B*PEVRSM{&O {H .b,`9 TFز4h1l=*z)1T+V)Nk7L0hRkJK+X9v-$hAl* bZ /sIH9:¾hp'= 8dͪ@&PD*hfA@VLXLY֎\Į@ܒ,FGl k""SE!B 4i pbU@*%K15۬{:H$Wg 8i3Pb4o 踘x*֣@c(wdžokBd(h"Vb(FkA^C4=M+2I{&(%$! ÚApXaENB:"͂Pp֖!ѰBnR(y觕V+~UA"3M;j!ka$F0 Q#†ltT*K% O#^c7<_lcC\!h8vY`P"P M;jb:eS3a8≌X!c{C)&z@,1(E 2J#, dZujEE2Ce/yip2c\36ǂVr$Qr*V|OI QL D4䰀WO,X eA<d"p1NJDYA9ل $#hP: j$B&~*A"sHldW5bVXFD ˏ4X%@䲊* ˾hd$j:R){PV̢*}M30cvI&k*(<$aˋh Mh]iV$>&&RPj2Ft8WQqh8S.H rtLȝ,2bg($9*V(@t" a 9\GV RRQ~WlAfE͊VBOjAL¼(!y\(#ǫV qD[U!d@50"gM0d6:r 5/T2ѠgxRxN Q[)yҢjT61X8Gn:O|W? Ԟ^h_<,$-7J#1j7+!…'+deRC_qg 'jkega-Y*IZ3 * E0 )qrB؂ Ѐ 4D)N:Nh@#ǂFX Ys`aXt 2B;|I\CV( eLߧ9yE\'D=3JѐFo~JZa )NV./ ސ&$*Jºc)H=LE'vTPz*Jq)tnؚ:MdN֭.O A?"U\d)..-3&wpbwU[vK zݩkU5!RgE {]VA {/IGX'^"]m>7QN ua b`~mIQ%(`Mɓqp>Ai[жxBV06DNjE(/ZΊP,,ejdz1Ƚe^ ~TPs~pC-+4cW{>lck3m>896p.IyX x+⃂< hSC֨@l! 0 (˾! ¸+?q (@49`/:pMb<A)كQj =Ïh8ؗ!@5e5$$:#@ 8*9=@2۔Z<1U5,2tY< \ȉwZ!鉠=2 E##y|A8zQRH;Y4cDȄ Ӽ,يCAF\'Pq) PE^ ۉ#2(L "PA؍1:@dA*;љP U,+>4YL9((G U܏B>qpDEZ39_dܙ+>%ix 8V $ DE'xX#+ bMA O9QI+@2F+=.P2I+z;SPS"xA=Њ4>ͻUBJ =@G -UF|?H!1,JbbM6kݝO>۸<$X12Ph; \EX+VxSM :V׀<) SX ۛ?#@'֡K 2R⓶М*5R YPMxA=Lx'Vİ̡\K#! Ra )\[A3<LU0&j4 ya͐aD+`:TkHpqCQ ndP_AO ƿILt |V`=xъk܍żx@婇~<&H8O 6Gm0 S8SӐՊ( [ٸ@՟U X|[y VHXU Q MҒW,) =@ůLH$鳍ZsƑ4S;+ħE$yNI5[x]-sSg-`ҌÝ#zOL\SA_y[M)f!@Uf:}AUU[߫URA܅QŒX#tVL糘+ɒZC ߠjcm:_T~/p.+Rɡ (jj>::ʫЋnk`LLȄkNl^lnlR;camping-3.2.6/extras/images/boys-life.png000066400000000000000000000107711465547410100203270ustar00rootroot00000000000000PNG  IHDRYIJ]gAMA7tEXtSoftwareAdobe ImageReadyqe<PLTEUUUPPPPQ<mIDATx읉 @y&qM[g5Ҧ"PJVUJVUJVUJVUJVUJ m ͹ж%4eyNgq+"M6h=1lcFwC6Y>Yag&d1y&ߡo_D<"'\ YYdv)Ȣ_YC5f(JgG@t{6,BpfQQ6:onjd-lItdqf ؎΢LD&odgY|~ Z{E\my#mbOpsg,8WUJVUJVUJVUJVUJVK})&:R*W%UAUV%/ʬ:5Mi'0WFn"_Il*\BqI0Jdgm-cjgek^|fZ֟LA*_NbṞdq5O"ыn?X0q6W9[,v/ vY5l}ŽJ #'k *0vyk ٫mF!dQW1X7ZKP>,}0_A֖̬gi1do'U CNlr Kg .=IТ3aC;, %r)d- ]H(cPBqA2AF-YJ5ŭX1t , QM*ӥ]ۍ1XI)2okO隑)%).#.K&q{ Fb%~׭K kdrY1\".-SvC;n'hDmn5 φ)X!VMmY95Ff ߍH܁ 4er)ƅ,p Tc@ onT3wkUUZUZ-k_h,@V d-l0 S{-? `{mBP.Wմ.x%+ׄMB+D AO<7PJ p4lىzoDk?A.Ďiv7E5$hBIU1e 6GŵMlwX'vڊ,$d蜯mȂ G0VYVוog JԄl}(7d5Rc+%y%;gqJ,906H{ƛSA6]xc aj1ژd :ȮƋu#.Dݾ"Bd Z:m̐-\ 3U!}A5Z3HQBB`5UZm:`ndaH0^sV&űdD{Mk"!ҀV(.: Ɵ4 Jed/2Fətvn5,^ n#)<f3ȃLjjQJV9dԜɂ,4J6^foLdsdi9˜eܘF:4)L"}"IUZUZUZUZUZUZ?`aw17IENDB`camping-3.2.6/extras/images/deerputer.png000066400000000000000000000114001465547410100204230ustar00rootroot00000000000000PNG  IHDR[f gAMA7tEXtSoftwareAdobe ImageReadyqe<PLTE U2t+Z3ɨ1$Z-fس^k3g&p. W/'b*a22pR\qX.[1^-.ƌXx$lz$ķ~@m%ί¨Y.Ks#uV+)E|XĞ,٩ }oO4+W-~xV3Ty5,$1.ZGt4ӵ< 4h8ɥ Y%&d9ޱ:a#["ɳT22Uh]8\7\/]02~.ŝT% Y+`2T%ҬǰGe+TX*U;2^)b$5q&P+ ^*9qDK6W8 Q,۸ ɫ]{ [1Y/ߓIDATx[ضhqb?(FTi۱)Sj-vNŃL_kht̜]5ksBppp%C__>S;L0dftzO^<1>`<><<8?@oeNÃə?`y^|s8_?:awunzEnG*,stt{tqso|o7_MmF3A4Λ4rLٔ$M$k櫑/37cX ̳uhm" (z癞i^W07GG4fl\.EIo@4Eϧþf>BגsYC,>F)dc!u0EDQ.q|qË>ǛG%Ҿ?FLht'yz5Poڠe6-1%ITjB"<z<}:D]iQGc_z-[iv- $OtSXQ1ǞF{.^?͞ u;li/^\"*ǎ o`!_åLO8-*7m`kZLb7 'q`>s rJ5]huE򎭝'{%0Y-XEX.@h|Eq0v{%Z̀ѬeN[KW?>f{~naۇ%7~40ۖ,+`l)~O\^Ů~ew&ٍC8< }<_Ỳ-5ܲXa,ÜlegК9SGz\ph{/=ٯj JSgc?IlBK*4TCr陫c@ۅa#`'D!q}ab)uEzĊSy]ܓ=2=Y ̆&9]ί{S5J,\؇Ǎ qq@pK'0pV [6b` W%\|;f>T"]|]/<ړD 2 D0ʒ$[u,Şir= cvOKBDZR>ڗg0s)K\|ff$D>N\fxzy*f8k`* e"s9;B\bX[MIg{$6ƞfwl9A {ؿĜbg e8np^̞铽e̶q( Vtq>}|'KK[*θ  \췷+mj=.+*1fi)]b%rikLհP]0@o c9ڥI p-9ᮖ:TvxяډOB8}CYd ɩb ̞21L?G釤k N#/E5қ=uO}7Rؘ%s2o`䄙GEKyU|?޵ݴkykZ%.q v:3 WqHB㰞75p1L Ыu_~fl9y,\!{ru5oB6Rw|O|źakv%f 21вZ7^7/C,MN@XZ6pnXT+v]W~;gyW>t,+iF=ߚ^ ZMԵ*27CuR>wKl¯ f]NJ tg.%%\r\,YM[{"6dx.u^#k;;|lhߏ=C4mǃI\2r%S|(quX !D$X.>|4Wn7(_HmO}+(`Z!48AI4m)`-Yc5ivUfc?O_\-؎ ;Bd ZnP+a$˗>7LU.<ޭCMw!": kZ xO9/;<l3f1g+yPudT(Z7UgK}ړ==7hZӦ)-~Ŕʬ!A=B"<"b;ۄ稺аtEXw#ŽR{6B]w؆M3*C]=UBnͳFt($ח:<;x~u24eՏGgoz ;]ϜVɑy{egݼ Rs=ݹxջ^. {lC99ƻv`ǃ2m5mTLo Ш5$0AC;ȑ( GH$|gExE {aEP24saϣ !azcw9R2Z hz Gx ܓ|{ J2Ix*lz0[.'Up/ژ_Up;ai% E5y&p߮')_Q6Sj1Mk>drñD|i#IX͞ sCd.1U2X%\z'ʥb2a4DH"tϨJzV ri1µ5}WU/pQ8U[lpI)PU%(*0!*Wr Uɦdpt!9b f$RIj:t J&P2  Ոك} s>+ڙD+JTM)Rj}9nНIaD" `D(,wTpG>j_mfQ8\OsT2ICL"r+ !=Cq-NDB!t+нj4U 9K=Fdq u9˨B R VelTw (x8\f b-D`Б5DN칕b¾ [@ɵc-7$ b+eHZl"? ܲ2@޺)A1pk;p LT6 Su')-HXo_ 1ab"Tj "ɦ4L|RT'nC/[̀(0+ 5(j(m Ѝݣ3t[^Y(C;kgbDnW[ńΨY2m@J`{z!.TP\WdabyJ!E7|c&&G90:9p[h#WS *`WIENDB`camping-3.2.6/extras/images/diagram.png000066400000000000000000001056621465547410100200460ustar00rootroot00000000000000PNG  IHDRgAMA7tEXtSoftwareAdobe ImageReadyqe<PLTE""Tvh,)&&rrrs@$MKQQQqldžȲoIF%ұٯY4L㸸Г4 {W4Ɩ`;o=;S-Q,`[)qJ&ϛL-9'?݈{d>[4xݺ K}˥ $a܏]N_艧ݕv؏YzVF]!^݂VGڳ1+ o_[$1\ n?nz3<Yyxi^ :_$ gwV YB罅 :;.vw6 ]plyމy~9/FLv3}o+QO;=?BwX3~/- -zminwΖˇ zr ?KqsT=4|>!jƿ9St=o&3򍩲8zIy=_do1%?ٺ x??g\ynj\qv띙mhM{"qxxj )2;x9p;#; /ΘONuM6ukj~͍wCԟ]q膠> q[i^" Yׅs^]wy%6n w'Е Yt}ya5CБŝD/&\$x5Nb}Dw6v+;',\h3b𬹐DegX1g 0x>wnYA΁!醞qN{6/t8|sZf(Lu>h DL gg ;XP .iB^ӱai.r{vdTHvwO$!UNDHǗ %/ wxQnyA>xaʎ͞Bbc t K/z d6 ›Gyiܧz_Wo$;ٵJtttq, z[;,Vz>M;F~aVc/X<39TU}V{ly&\1aݑCA喕wv zۍ~G /Mm 2wh?W4Vxf\"4zƙѫݕͻg;uCؿ0m8#OHA84.uwYλ]O_sa ldo& x'g+n@wp6vK4wVv+gf ?+ osmBo߿Hl{ڵx.wu1sFߞf,Dad{|}gnH8Xx/`W;3%*/=]E vz13p#/Ox>IXţUgy$ [7XK<=4l ?n$6J'$lodlz}gY'r v$ۄ&+.-U߳[㜟IO,/lB6^]yxyҞ}:ހ;/̦.lHO|,4W&Mm^gx UoK+6Ͽ;aoޛf)0lo8 OjW{r(.uپ/gHk]tqL/Ϡm7g'7z"ʹb2PɯW(m:@<5l:>u z0NY'6l4߁/e›ؗm?yy'ۯ]^=8;Mk^*QG6$ZX/pcr6%^rv{r9n;?'(pf^vec uY7~wK{ߏڞ~)Ý$'Y%~i>.7UK^s/]N=qii~Rsc-?o?6ҷghҗZ9;c ]csxeu͝'Om~{O RCo45{o -S_#/ w;EM_ml ve5arsu[|saug7ymB _\1vjo?Lx!ow瘭!ZbV^#?ǖ;A7ɗhɤ񕦗ZɤLFUZ˺&ӗh*˪x|n5YǃϧdM'x6scQN_}XmcKB@4#i%$ 5KA-1wNO͋uzc>l7ނ_ R$~sgCf1j:}p'! I;:(3Pcl:cGt F b3.揢}F`eN/σ?Qt_pIӳHViIѻ?[;|n绕~Ϡg~lO %n#юqE{ 8~4u 3W=o[&kv h膓'j=ېww?po_ B1>ξrFxiA.o- .v4%{p L=\:FG({F?~+v?XstO#.RLTzfmg G'o vNF8YcI5Oڞ"=SM-nI q~//uo_Q`%]KRWNLJN>\3`DzY7sIヴx2gcIT{2mPdt>LxiMx}^ i gyfm~XC>j%~{m ēWդJ¨k F]R5ԴAbt9]SwC+܋cXy >Bks3;]i=ӓs'o_Nø+r6^x\B}:J|>" 5z+ff\PEfl:)3fUFJ\/@~ν|&Px/b5MOav90~mFwt"Ƣu Xu& n_S GλO_ !U+#{&!Id0gd912O &F^0OI5ljQ\|nR` <_l|foMO}n|ΛM;xF"GӪ1<; :vPV*TUJZe2)|-{uC_ ّ0b}"?rPKfX+̯҅Xڤ26.+f(?y'<($4Qx)+FYC7I_W&#%Aȉ8Aݦ(#!1sb*؋yz2r͚#>"_Bo|Ҟ}tNqp bOy\# MkIx};嚘N[#')2 sa0_֍NգX m!%1Ad^W9WL&|쯦&ԫcPI!j\خ)IuƧц؛^]fJO8caIA e02IO0aRIp Iu aXȪN|^j-WC0v=d^?]6U/Ƴ])4Fo- $_Lӳ襆^v ;1,g7![ޅ:'xC.. ?,6]9btɒ8$pVO%L2[b|k! "몛s)gӊ b5$lluc=4#=$N4rP6I"ozY/}aMzjp#? /Qk' ܪ _$f*}).`1oWHL4TcgI؇Л~LS;N:djN(IFV+tD6I\2(zV’sjWo 5|Xw"1`7^TI?$ۀ){+)d39Y4dL*RMUHHa| [!XȴjccŸ`R5X+HL!mp<ܙ]慙Gtt39玬,yEQe$n16#s X 50dKe̘R M6FR(c6|FΔeKHQa EζY+D#sg']E5Cc/[,,/jtAHbhf?=ĽM+gr,E[ YYI؂[Y@=;;}k|.Տ}+ٵ]oHÆ:4/ K`> &nq#!+dDPs(*_-p&c$p17F/1-k$$#Ve,ޠTby!4s7  ^II׊^Jzx{m7 ~Zȶ6KjFv؟`0RweEQe(R.g*e؞S!d(CĂՔ2.V(~%VFtHl\Ve:. ȭ7TI&rу^ۏv=)?m{xtXG8+frsW27%$Cےx<مooK펰MϬ `"x\ЕZ!VI JZ͔>A@)fTAUTJR,:9Rρ":g-|BVGM!~޶`߫?{?y/OHbD/z+?Bu&䆭o8e&xW:[e ;r39wsx(K e.e?;%'(sMcL@5 _ f 3H)T]DEqO31W[/|G6}>xO~Yxo\"xlje'-q7Jn4^5ӵ{<$KظzmtZJ4~WJ:P9[༬s5bND$`O^.@/"^q`QTK l+9&/?!{?ȵ'p{70btq#RrIl߲^ZqEN``K&S5K>!ϖlZ~W傯HzsY۟}B* id\@*@uAOe &c;mub Q#; iC瓯t&{ǕV0@o5.GT0R8+QLDD^aeAW<Ų٘I:m@ &- 'ϟPd2OY7c]X.?9}t7,i0A tAbKpXl=b΀?W1.A&MQeEm?6 ^1x}l|m<ٟL_ |4ΩUsI}<(Pw?>xt8*K8N)Bh1U-ѓnBmm\TSq.ovlEPX dS7ۻrQpozNEl-eU1anTɗdiLE-~x@N;9$tsWݝMʤ)@g=̃!WVҨxB8(_mo@~:.~({7Z<}`tϯɳs\7P`\]vܖ+?T5P {P沁@*>06ڍ0y6˅Du`NtsaR Xˎ6ݧi&@aF"8S*#c]d-S9L $A/PS^wˊ[v25=j4g0rXA/%^-GqթT"[7P*ciM}WfʲVu{I఍=Y7<'< r{-;XӻаdgV ;>!zWsQRYz*E׳ ' SGx~V9"r~wcdZA6T@'FNr+dޛШXڶ}ԢeLL4ږ*[k-]d:ϰȳ{Oa<%dƃs,RDknfԈGȥGd 7Tfꈎ⯨ۻhwLѹ.\F(jБ Fx;IVքx獌2cB?jڈqs9*yFq^L%ayzuN~'l%S?2gY秤;=(tzXr#&>OLG݄_ެomzn w soz~ VT UK*TkiMܱBM :D '5,B: MP(r6F~fz|NMv_8Jx̹޶_~aINcWyjIwnaS)yaO,!%MJ;YBS\]!fjl|j}fdooHOWWC, T5TR q UCMtNpLP V 䮫W; 9 O:)=,KtZKJҤ2seQ a;sp~})r^ט3p.i1?AH dsBGGsGg)+Qv.}j7oW8 qJsp~oC5ݧ":2* ~mIt5`n$#) U67ϣI/F߹#TTQD4eL'ĬRhxmbTxZG'sF+uf VOpOuL Tx54:͑W-8D6 V͂[!1"ɯGr8yt^= =c6*X"A)cľ<ϵJd }!WF^ rC]$Pǥ4UԿLӫOf~x33vp14{rϏ 冴#v֫{yڻGYGFnv߾ýܝې8&CǐNHf25EԥUT+%K3j9;tRR9$u%i՗nšH!E_Øjsskg"$]?ؙ)㥏&Zl~ <2@ 57B]d+p3l*LMgbWZKU*䆃F-I K9L _P'qbݱ$r9eU&S|*"4"2=݄Gg^&M^dŷlnrR!C:ηmrXzEyvIs|UP4Sű8rbYht\#hFF3bh@3d]IH tk2ؓ *+2ڤ ;?.T ݍU7|0HJޒ#W^t Z74,,T_df*玎oۻͭox/DNNG4}5֣0B.td__ r;bu^vTzɤʓ \~,MnD] *Pݲ|d&`q~ܲOyIb#PkU2V!OT=t{$~ާĒJ69DbwY񏥯WW 0!o<7ƟϟG*3-EFEI>MGaK)Zxu>^K"C L)%yV\җTJ ]*d+ 谛aFgrhm[LڑfxgWYIc-;.H H sKq6:PG墓s;*M>L>ʘf)!&Q'_t\eEk5%'^}!AvHsFlyYdPx»“a0fDMދIF6ڧ`;_KǧQ8kN)/=_;=]nb`8 MO^[Qh:+HgPgogWKh.TtF^)heϢZ W VކSd|4jq?E??";JRvlM?fW_i#YO_5Yn7d0&^4E &;8wHXR^5rUɂ\FU*7'_f 6ǖz 5H+`lF>k rrmďyejﳡfmGof|ekooK$ڬ#GB6dGY"K~1FC9_ͫ?6W˒Tܗ,NQ\Z DчR aT*$^[\gl*DJ-S)ܩꓱ,S<'#hLv^Fwϙy__ R,juHSwx)۝wJ`\UQ5 +,&Nr*t&eĪ/]AܴZhHީpbubERP 2J0*R@Jb2kZ|?UI (@qZHgC1gaΥ7o(_A6t* _#cʺ[?%~ 'p(k1Q #Rt(5, IBFC@)'Ǎf5nޕLGqD JF)v*G ^BQDB2zmɁЏi臱 ۫o7or}º+x|4 >W]XW [ɻ V0[*$h^1eIX+'Ҙ2ii-K=: f[zӜ/P(yms\]Z|=&6ߎB>ݖəA Y*w֗޶j#KEXJ$<-HT{ǫ#|;ʖAmL&HJcxQBLVdt F1۞L2Rø|:Ǡ+j-Ã}\=@k)zs˱w{LGEbomޞɹ!i[ ){+I߼"Ib]̋sW79 t1br&V)QlZU4rH3}A:𥆉ر Gu>=JQ { uxKQ 7Xhr`( D4/mOP O>ə9sO?q1 ;)h*pWBj7j _9`?&v$r;Hk:_ ̨Vy 1*eld˔[xPRt=`0$ j r$<;MNƳl5|sz^kc=Q'}}wo'^_9Ǯ7Squº&)HxSD#Qh)N oۢ$o(:^Qڹܻ$p(CeXYA)z;"tPjQ ˡ]7m.d0:,5Tq[[066*8$29z8rj Ң3mG&]g›J gb)=m{3y1'g|pwy'~~ۈ6D4WƗovUj 5h%z#4>+٥{[wB&&p$3Z*) d P6j&+t"c †`4$j_"9N55d8?vfcL/\79ls_ZJ,%Nh.5]J4ֵZ 'ĭŨk8w&x+f8k~4`%<.2&PbE&LC!JL$3FM+~Ti>ʣ<ƶ%5:xH>C5[x j<@>~ @s3er=};)dn4HɅsc |lH[-oOR^oO r% $/ғb<"wOe\*ԥOEA-sԅGQ5p0K+5]=Nنw#*\0WCaZ= y6@f~dr29K%[8l7m-`ƨeCܸb+f1djFZ-5%{m1uUwsHu$X $Jiz F) 6LAJݩbؓ D07~l3{xoZKBaZ!X4RWAB 83WoថY298.یh>xU~ 2 Uw&rKDӛvT!Oԣ-܊=O\¸Vv/Ƌw),L" Ik12JAk5_1zL+԰"F&fn<1ˤ U\: !d72^r5ə!3)ox/~lc ԪT'FFa|&gs̐&¯\jo$>xq+* j$zMvF⌭De3\+1zPQ ;UDNޚȹ*͊NQnb&'F&'RCٕJ/v8Oc39d1q:125^6G9[Kҋ"T =QtVb7w^E xx\= LʨrϋgȗQ uq{j˰dsz!F5oZ{+Q[!ag2*fb.Vq9w#X~BTx&gG.+:B*P!qMUs~ ձn6^e_DWxgs%{**DqZT`!vfE:.dL:dvdwAw/V~= {w,;Ud @cBx䬝Fs;=Y_^?&d Ωh{PY\ kW(hjxK^[x 2ۄY)+$;SH_S,VK>]N'UyAlb1Tgͳ$DZL ,<75a۞4<]Puf 8-kc渘GWCAM,\s=ZOˑ!"{-ơyz&:>F)OpEV`*'}dŰʫ7E^6p8Qc{vw^bfrA .Ι oS)>vN&fa+A aUj\.}때 j^y ͏5r_]= $pɱf:6P T.IHpa;$E[Wx̺6$e"՝VnUTGI 2QK(n=-)>ô+t+1+y3 Fy%Vb{_ÚrV"\ԦÑM\-y[Z-sք{~?h_󓋶:9rɅsJ`IlVorUB4[D>50t[p~N/6B.~~K^t*}TyՆNz 냵R@"k< rըQ"צ]+Ra6܅I<_L}_$v7.ۚIm{*+X3Da" xep|o^a9`XC⺾!櫾s='Z h6 @<xsepQdD3j\Y?ƩiW1;VϾ*7nW\ 8UgfS[Z弌yT\pl~ ؘi睊&ӕmU=rj< Y\CF`e_8%`TGS-Ë_{Kyܐ ܩbhLCp"E{X;r[Cdq"wpnh[[Vlq5+f<4M|64VCEQ;KV{Y޺G:oSgGwLg׫zj蜋av5%Gp8c:f?hrVv~O1O}lHO,Z0uJ.q 7g^lt|nnp[ 'q?{RzsSO40+ JRC&)({~Yyp'tY¹zCk y3LTGi1+KzR^ _+Bx]=v~ {VhI5]YN"FaRkxV_wI _'GIit1ItLQwg]S_,@0F&<I)=?>;tr<:;SW~#5\$O)SĹ _<[ݜ.>qz=R }uugN$\w=}ywOLܧkFFj,=|փI\rsƹ |φ ׾C471Sq? ^w ~t .H}N^y|3YFsq>d皒ilKx_r֝aOoܼໍExJRB~YRt$yK\JP~!NIqzѡfuxZ?u#axouG;S5%Yr :NK~: Oa7x;tKR ;fsK.iMCO'sIj]zm_g2DN+ NUwkӣ&'LУ goC^o9<9/N)E6;vk|:]qmRCj9{ow2xkoZxM{ːNMRT=]I 'ɶ7ț9=[S$mM8wm%>pZrz\:ܖ$g o8ۍvSwoCϟ#߇we -t2d;F}#OsRK&0#p:Hu tyvLzѪC;F]s\ѵFޏxd[3.oKNȷkH Nzwb +~KLD;1'}_lޝ[BpjQ6R`O;*owtK DFC,QX'pڮkXť jPl*Qf<'z,CRt}Ȭސp;:EyO>Y\lISXa_8bf0BjQT^@JED/QxN$do%֍smcې}[w#K c@Ge<2hᏅzxsy VD"wt}iZx4] /ރtD÷R惧R4u~v_Ñ92nK'Rhz1AuIc!m,6)x8B՛=k~d)bA[jZuS=x|!lMrbfxoK,N.x?C^/TۢG܏ W=w%i?ﱫv{{yg=1mF>ҷm%>?D6<6;K>]v0+{|xn9ϠyWrE\Rېni/1/OVJ8x-l`lQ(vzagӞhkkWI_&'*2]?"]n)Hz]n/]˜A]kݒa$N"oG&t|q\7[=ۇ {Vgþw;ݾ_J|votwy:z5u1Fq*9u=|?N_9$wݏ\q;zvkȼz~7̓WYrvq;.)/LS-9vI:OdUw:uo;fgsN}$k,К-1~?w :1oP0mwԿq\~=bfl-ҳp3Vi7ZFBH?RKjoџ;"dO\~k)Ue?)`t Ks65ؽ[ㆃv}萞ZҚt~m;uoxm =a|i똈imʾdon6/u=If{>FkI#%ґ%ꄻK%ajw/ĹVcao#o ~NN?{mQ l!DE !#J)OeDl/>\f&$s9zٙ |o=͈ٺ>hYVC Y2}Zncxle60on h]7Ie:U,KʕrbxlXrxewlwEXu|(ΙϽ(fi}P%CbrvD<ݧԝ,q{[=r19U L:({!sg[' $v{-*!5]gכ~R%Ep'k)o>bSҾ=YW늒mo^D lЂ%bhk-=ݼ3l/X[Z4# <@/IJrgloƋHR b$+/pw!%GϏGHw|S3GGʒIhѢ>nß=č(o<3]۩qU9սyc 7SN>;&~fDbh wqNe @teUp:689B;f^wg x;Kp7xhO+۪vPf.qY<27]XMx#x`EB}PpuktgH,,5n3l ~I%O&m*usޙku >@> `y{^=PBo1pOI\-˪,NL[JiEXW_X{N%j˻ˑa4/dvaoqn}t&p+W{6氭dWHe`%TpEoUNy cIdTæ-ܷwEkJ<Ouwz/K2G\c/xvc}h\7Wa+fntV7:w7ݒw|Nd^ƳCVg,d]9@!Ŝڮ|3|ÌwsnHf k>9clO{?H_}_U -*k)&Ef8"KAG_"`\̾f@Sv!ɉ4;/>ճod|=xd\47'&IL6Yfۈ.Q7T[PIb=BbG~%QG\Gz{;Sm$ gj[qD-,EO1`oQfPSڵƏjK}oӾFDP<xڄ<ї))?y_߲WҘEq& # eo"~>&آZgP47;艎:NDUA8y>Xs#i^d{zڤNV >y30,WS hn 2~>2rV_Vo˸w<̐IpΎ7-`EGM] lmUm5;.$+itoǣt+=w+wEw ɟ~HЙڮrnUsXk,͊j[ TD4kXH!ܞ`;l+yVW8O<Ȝ3kWyHV߯mĎ}S]+g*mEqE4nrMY7 Z4ОY-o"bj*i L 0?WN#.:_aqG[k+p' n-^c*}B{77m YN΄؟!OL/(`mWp|=ݘdN(j4cϹJ=jbb'.I>^y^AUdeUYI1)l^7Y]Ğsϯb+SMU &!RVOcM}fgX}[UUxme[vUѶ6S~LaҌ%,khvU0[ \cm6ifkR!DI.Y[sB2e|mQbP_Cñ/K_X}!ߟ]߯\,0,V2ז Uī<&U$el%ٺl8]S~S?!?wݬo(- l ܮf"2(vKEnsYv}B[ߔoΑkz 2kKt]$CNF#ޖkZTe`)3IuB*h^;xIѣR7A o[!5хN#%5U(516 W2I#%)*wgYW~s\l=)#l^Hj56щX\+)h՞-H/uV4-͢׬/¥GM.ߦQ_^}-NfA&idmoc F6mβ¦ F?ZK0Խ:IߺW\!덶9w&|14궫'j+QtRzzI뵧::?~U:4{Kë{A0 p;!Ξ٧toF_hG^A()2Dۍ40vKі\ʤqIwkliwymiL'm ,p'Bӕؙ] $F?D<6-9\|4^^;Iݭ[9?{ .1!<󋼶W{4bEol~c*.;(w ucf,sB~sB:ܘY%,=,egzQ~?nPX:jf2yL`DNXyW<זP{:oΑp'b & :'ihs}@K}a& RrGH_[dہ) Ft*  f<?fXw)=1>-I㖦@ NiNўtP܀' $|y as| ?xrOny y_1{#¦>l0={^A0+#d ͡PNM, Hȓ㨎ԣ3y+f-l &t"<3 6J^Z(s| m.9,4yLmFR9(~?Â\q7mߢ)?䉝<`&@i.6j.$3&ʎ P6ltX>i9!?8mZv |߁Csj7)qttip6ay ME a)loIq{myۤv\_~yG7R{OӋɯtk Wj`n#~j=dhf dBswYij>C |mu3LW{tmUH,+9b 7<8x;=])V^#QWag$16#wa&2y4A1k!=>;G+qS(W Dꟿ$6 1zuVA { V#HO]NX[FVo{Rc 9v=ڽ?M&Y˩ID{CrZZ*&JQe ΜNL-\e9u}8nTA_A+<ƭ:[FL)|,;s X Գ$j0 6Dj^N8(wYw1})r/ݣ`_śJQʧ貭*΍ʜXL;^*T;ےgo'yCKo €!1=QDw^?{Y'0}w\鯏p5fڧpΙviFSzg]Vºpğ2Eti&p>p#1>sߛa XorT1yǘ $rh"0 xl~ hz4[X\gs]3r5r9zAA7ɜO~%>w 6&0A"OfY}c\J _>-'4,{yE.[**$[ ̬-1(8̷ËRL}rř]$(cv1qPGw[G$P? ӂgTӅQRΌ>ǠQ$ c}UE}^'r|彬|JZ qn3^ 8)[39j[HǴ{@0 h \߀q'AG1Ҹ1^2Œ{ϲȚۻ~n.WҍI15?#gR@4=gWhu:[Wiљ!'t٤anաpJs9?FXcfڷSo!Ih"8;jRN b.5fEʪW ^:MP8F^l>Dx'^@>ltj3ّ'90=aцCC]D !N{ Z%L]o|m6#9]O5^:m IbCmJA8{ -}t:AQx'sBGuuk Ow^-rs = '] 3Pv`` `# <|t, P {b_'p`T [)F #49(\>':sT݌}oMZ< c.d|އ'IwP0^o.09 Le <"PyD8 F8NNG,19.=RV AA2af`l6I_ΉPEm< ÆzZ=ϣ"$eeAox4ޚ\vJĚ:{|5;47v%yl|X6P'}:]U14}[xdj dܡu.l}mGx'qrjm\03a M;n2?U 6 ۰Inos̃MV!99w;C,; \.݂EВj|V.2U/X0G + `:Wb@ * Yb^`wqط@>%q +sv.>u]T1lKwj}ћ\WBXܶl3bctMy%|;PDNȲI)=;l]svBZ@O0O[,RNob17ayyo(>ĵf&4 yW yLĐ1 ב]%OmLoB3R:Xʜ;A6vHl 'yQ`&D?<6om7]P*ĶsjZiotn9uR)gR2NKy'.yFAc[ ,2%Hzح\``ϰu7x5V .hEr!U/9T=Xȷ̒8jwAiCM:O^Jscς`y}|h gm;ZD Gљ zo!\;~E',ς>7òWym=G9mk/_}3hy(`c w:ë '+ޅfyO}xuR)U'CKxG" 2*t?=m|}w?Dԏ@F<2l̃]ɿEM#m̔1M0+d g$9Y/UK'! "eWb2$&}b wCdjὦyO/Sr89̇ÒaVjȢՑXw܃浝`nb֥`.%f9fe?R?xdG IՓeE=u^B:i3 `kn*CF|H c ] 6'ƴ6nItQtk0tG{ɾc/BDzTՈV+ ΥJ ɱVrO5DX`/[ Q; о`%ŋk9씜c„ 8<r'H̪w䚩.`>+ 6`JF4QDo~ǯ/?HQ:ty^x%t3bElM'9 [yrKǩ߱<˸E\R@Q`B8CN$ȈNB~Pss%˃K ̬@yL_UЍHߏ |O1<4a\<;i%<17^q% _ Y$lv5IAl~9'̖RD3Qs} ԶʄhV喎8Y(K,LgS0@c?=+KK*>VJ;\e f 0M56` bÏsݛff3RsOt[x8Ulͳ.ٹذ`m(/)H?ՋHi\o{+ j5^s5PDn@{^sP"1^P(@ZcmLY'[l15>К\zk ѓ^AjVCk.y$T-,BceT"Kax2 1tng&;]"=w5 |)PDod7T,G3vQyb)G?-_v?,K 48נY#bSvZD?l$襭^z±*B.L|nc"΂UN1hQS4ko5C P(P.j]84x~BOY)+pKi8U]N oA[z]G&芖,8GB'j0% [-G_ [ 2yh_o=xXN`aoQR Tj09^Z #1]KHzK %u^[$WW[IQS9mzc`Ci7OS05U/~A񒗢s;&,?uPl-aK|QѪ<t-3V]c<$h^ۣ{&_rYȵ1gZXM΀=ZN zc(\4z'{Ymn[qΘp} cU9:k&@1(<UcnϤʤ+5:ҁ1Ө"ȑxk{[_:ٙm&1h{og,sjW!mvo3T°zӠYu T&|lIM]Z] s;jEPilx"kף)=iZ Q<2ZHҧe&۹DIA!Te% 7-WVo(}V+Ոt'Q臡#:7<MPmw@vYE\{ ^K`XŤ[ X-\njIP:QuA we{w`6ץKaY--i_58><cA5<@Hvɾ4kf_%jG &\6|4(~y ۡVžF|?gʼZ>`x;z\,+m }ЌKvͨsnwQƚC~3сz(gOuD7Go;]d%aͨfpqT ֤_+ ?ՐQTqvMO59>)c}Ν;K ;r@ALzz۷v2D6$ѣULÇ3lqF///U9+W<~1c_# !]Md;%׫W13!nnn:}ѣGt]4iwAPa\ie˖ƩSKE׬Y97d|DH$&&޸q*e޼y{500]낵-Z#F  >"$\]]ǎ0y5-Z$;DU@$gXXX(J=]3~Ȥl$*tLtI~~h#,Ѥ(̽{Cφ Fe.FŋVzʕEi)pc}zllg͚իWpwvvvLwr?GNя'=ed<4ݺuS)0FI= #Vi&xX:KȖUDuJn~)w #ٳgFܹsT[.**JonӚa+EIE_V*jsh)콠+M6_r%a- .=w73j(]K_i:dY~S7,y۲=rc`rQ~lL雍>55ՕU6ab߻wU꺤عs']p]֭[)]4Vl>O;X"OHH6ȕRq|lu¡C؝hooϫy:}4M䈈?ߴi9Ű.$ZnApϥKy\lQOO `g 'ܕgNN?oD6}H)2 )>H3#E(sI`9.]b74f͚d5!҈O>A( V}+LiԿY]7nܠ9jkk[lY-'Ipŋϴ]#ȑ#iii" VjժuDm259KH!HsV3Y 믿nܸIȱc24iRR%'''+++J~Eܗ{RE6 Ƚ\n4k ЬO H\]HlYC\P/!$XU100Wׯ߾}CQP۰aæMhzyy;vAp#ٹs'&K. J Wq2Yʑ֗HE"RbHnb fe ۄۛ7oe$>T50ϲ`'sqFA;"/cG{FDFFF\\W QG6 _~E8Ꚉ5&Zn/[\ Xڔ'3O&F!0ދUyK-((?yyyT冁***4ǭPc(mR1~HP5͵kN:6]} g?l]^ׯ_{zzJ;xS?~ Wp_>_H:"NHP5͛7`tj90j(a r}ٲex wQidȐw[))]ܖ, PAVڒ%K ru~UF(ɇtW^2Kl0C0D 1{lw |xk@Ⱦ|&L9`)3(|NN31X(}Y_W)PI⶛ 딚)SFkѣ)SHtss8q)Ç?_dITGu Ϟ=|*Ut9s`h Q)/_8Nג3RFJ3ѸMvioodKO:/^J&CCCϟ?mZf|aЄ'@W >HAM[.]4sL5E ̘1#55ɓ?V3Bm۶i.d$! !c}||:*d?s/AGĉhY-[⬨(}'PF͛O>v׮];w n݂7oԽyɐ[^Ç W$%B6T_O|$<{D60Jn*58`<*;9?WTw^-bZ ~HtS޽suuZ>|Sm+++cc6m۷OPFFƍ7`U/{IIIR;aΝ;vž۷o뇙}v0'>}ZZ"Dy~3ԚԖʜp2%VVz˜DSP͛7:99RJR;alo!]vzw͚5‚B*1|rAQ-^S>BZнweތ9gVUC꺪fë0L朌o=De謀\x%5kM(}(5jЏL^n,aaaX^B%fKyIwelh"HC.M0 d}9d d˗|e󍜜9//`ʕrCJ1ߟ c*UZn$<ܻJ$(**R4i&u'&vbP[[[;;;$/^7o߾żLvhT_|Y/&. |E$D3gرCr[6R-8 &ćulXXN+Е+W?z~CxR~4J? ߅Yϯlٲ pGG)u5[uz+UjtEGԥJ*L:T?ޮ];sׯʕ 8 rO$G*_U};t0zhEX1cK&$CNӈ˶6+mv Վ!@V(}^=rh1{P)X=z?+ o߾AI:111YnH!ooorTѹ'??ϒ;*Wɔ)SP( (QQQRBbb"Emذ$9;;ZCg>,G;K?W%3gŋIG.ٓ}޽?EAɓ'Ç sќ cJ\%VjkDQgPr{֭`JAl}E@@vA.FJݼUA4H+j:$yN#5GMFGK,$ trr*L566*Xڐ=p]3e(#定B"Bw#Rzeƌ׮]17Ǐ/]$H~ч!C+Wjժ:\uv߾} о}{&QF(qI4u7[-'`СٰC%CuLɂxb1 _iFX@+Z܇HC mF :1f ߥKudv~\\\_N`2%os):$d7^_NA( Ǝ{-\.))Z$ V-X/>_ *PӶ_'C0SQ2e@_~I<#֭KiBJJ^B><xj:ҏ @UN:Y,Cptt$]փG0C0PD:H*W^e}.Id233S(,,SYGG=ǧ^z?>zݛ5oݺ,R"8HwjjjTT|gB J?=ʕzF.@J_.[ϨV乕-$^U /Y#bddD:f[hd O>HIϞ=֭K:6F;$$$HN"î^z%70d &\@G}`2  XıұI.k.}g0ɩ%KrCe1{K'IZZWQ/_5iΜ9[n%۷C$yyy\IN@+ 7_tiss_ΆXd*:TBYfGm[ZZcRRRRSSmmm]6֭/}XXdѣG'N0`ÇWx9ۇOϟ4hN:Q>} 9%8կ.r{1|SNJIe{… 5x: lC q1bB>rHsɩSB9Yx/_ʖ-v)A g苸k_`/dooo8qu`bx^| Sz. )f L`9R|Ԃ"a7f!> ?VsN'#i`&2>>ѣG M!ifm'@_7 S4FEuovҼys^`҉Y{nAXjj*fvw*(EØ'NOOOII Sϫ41xڴiKaĠA}6u_bbbex񂷷"ۆC}rG{dORp6!C$?K9 wj5j ǀE݄U0b)SZSKf>B4A$)677?{,jGDD(H+,,\v-7N:a^!GСCLMMNdK:Q3'^sܷc'o߾]pm}-&@ȕ~.=d_ *@}vڿDUZxċot"/_|ذaԺB>z@ 'mڴgݿ?>>H?$; $;1;;͛7*E 9I~ NիNt'-ZJ;$$$11Q$n›5kҥKUQ,8:D6BCCCM9̀'= 6l D9|P2ATL̪UR "2y/\vUG1x`s___%.5kL 57oޔ/_,7#uL1Gi۶mǎ=<<4h" Vx=hK.- LJKTAJfZZZ6?{l>LL`N~~~aa!ww'O>~'/ ׿ 'R!^B{QAvȫA*:Km6}*~@۵k'HkUuC_D 7y;7hNj o˖-dmZ{WBСCP{]QfggS;㍌519ٳg4-EEE_~@m~^$4XY"H{qY۷ϟ?Q,RL MSL]jXK߿_n)R#!,_$=vX)|QQQqqqAAAJݨۙ|d%C 0%@>}&:Ξ= 1l4lؐ8B 0O?}4`M65m33Сԭ~=DGG'''szH9P VԘ!K*Ezd0gLhf$[^|٩S'ɪܘ/_,_~!k[:ȑ#]GIKKK[5%Dw<==a@?~̤t辤,Yя =*G/ѣ߯R4@O?AcuL٤IhNFZs׮]Gf,H%<<|GG1cDyCgmP3}i?dP{rrr$.]z7n$e~ȑ3fX|#rG K?Mׯ߷o_EG]\\+͛`Çm3D}=tD[`nn^zu}vvvGMMMMCihԈ9ssC7";8TR+WVtJLLSOt6A20hmM)2`/ C#u_vZ={v>}dRU)aRECK, Bu>s]tN䠼܎X%lٲJ=z46w"zgdda ױ^:77[bڵk;_ZP=zЇy /jbuH  ijM PUٳ˶rb6ip/ܽ{wuuuu}^Hy)oܸq߾}aڵJü~ZvbPPPy漼> 0pGR]abbYfMկ_vׯcƌz@RRRɒ%GI%C}Dݻ۷ooʔ)+W0XCjFX7f߾}Tmzn:^%dTG-\0$$|Smڴ)[iv>":DWZUo^;:88JFDDd39DBRɠ<QTCEYf3gH֭[5j$1VVV'NСCP(4X"M0A̱{h >^a/*T0z@9ydh(%s$?-aH- e!!!NNNXD;P@߿v H<Ȓ~5jJVWs"NuJN ,,,ݠA|Ј MHHؽ{7+W` ?uޗ/_ ee{}ead|J_ >3PK.ڵGh8/^iӦZja b޸qc O>J@aa!m>&_2 h޼͉'ʔ)CIOOə0aT+ԩkH?s=>Lj`א7G@+++}555mԨ>9{  cǎa6Ϝ9l󤤤(~bĜ9sdɒJ233d>G[\imm}vEsHoݺ+C5g2XjԚvJC0 ުU#go!˗ǏvUT 3gμvf)ӥK0V0+P9)SFjO z%O4tmll0?͛7}}}\bllydxxxFF|dn߾kNt~NbϡC^jݻРJ?=޽{Ok׮BI+Znf|(#ԩ~Fdǎr O>h#L#~_XX"-Ç}||0KHLL ?Wa{N,YK;#ѴiSNsR̜9S^+9#1|sssRE Tg/Y)))MyRRիGQaÆ>7//Pp8qlٲsp͛ɓ'1 J?ˎ &l2??=KIiڴif3f1YVAG@RSS)Wnՠ|<'ψ]v.]P yڷo:88Pu7>!(8Zj%Fo;Nccc,YsCGԥQFfffO/_<~sR>|ŋ{ʖ-ȷtB&L0sL~n8q"8s 󳼽Сۘ,m۶I6|e̘1SLy  $-Z{Ԑ4iwD=.\@YJz *Owر4a0'yKfff`` 5555,,Zj߿_tCุ8 KÔC2!"(йsgN8?ӝ;wClܽ{IT_kԨ+6rOJ~k׮}ńʕ+=>|1b]voD)ڽ{7R C~nX"۔ăF >4תU 6@VZӃrrr0K7}b_dddDEEAC2e.\%U'O{l4lؐ& p0M4o 0'%%|΃Trk-垞8KA vj߾=~Pu"O~MR)و' ؘ6 ˋ& RNn\ewH@%&&ROx-}|nn.è?_v ˖-CG8'N$xz{{{رc\MS 'Nسg"hkk566fqzF K Kh䀀jժCP/N d(Z[=uR{СSCPGތoo${yy) j(SN4iB_4nܘ4^cǎ\z2 ߾}c!ر~eJ (@`"s]L>pҥ/_?ӧOXj>2_g^&4wW=#33[|yVVֆ ʔ) A f"x-xݩSի}vLD)ׯ7008rfүK9I&G焅z֡C>}N:5..!( wP}ٵ3888`fꐢ"O@;η}||ʕ+wA44iӦGGG8^f椀 ؕ+Wb#@G#HJqPu,?YYYZ[JŐ!C3V? O<܂!ѱiӦdgDDR OCgGRRҜ9sOp~~ѿ0Yct"L|C M_eȑ3g3AYMd%J=ta1a&Qeddc"PΝ;n8},d>A$ ?`/Yٳ3/<үm8`jj %4lo??9K<==Ɇ 011X"RrƍUVرv\~֭}C?ea dCA|4E>}R 4po/ii߾өzy>Lќeܻwoܹ`#('?A1.*vvÀA[hAƤUgO}ԌaܸqzF5,Yڵk===_AuTGGG ?z(f;~ff&|b9_)gϞ-Zhذ!Yb̘1ZzȀ{`D%&&bfjA~*J*YJ"w@fff!%r%̼U ˗c#(mXXX۶m?~lKmf48>˖-<үKN\qk%qtҡCRݻUFRCDGFFB>S*OmTTy#(ŝ5k. 61 4;s…^z5oޜoV?333ڨݻwA>үϘvժNt钰+RCBB+4IՊ~@탃;e}tI_K.R{BCC&J266fXUŧ8}7J1ONN5k:^ZX7Nz=:qD&аlٲ_n'eu!W`;w?NIIaT@?ަM8Y$&&tr<\r6<,~~~|Ço۶ 6w3fpppz4)钓j;ߩǍ +Zo߾fff켸8::ϋJLLw^TTTzzz /_>dȐcBcAAA--- xRB(ŚY.\Hm9r?%KS~.]D9s@ᯛ<СC6jHRmmmWXE]W:I<:6n^`jj Fqtt4S 0Y}dd,ٳgϟ?=S|/zxx4iƍaZPkٳFQPQ 55؞2eH( ŋ;w\V-.ZPOA;6-{u!N%޽0} !}du)zQ|y,ųzA]!] (:`ŊժUӧ-R#?qСHݨs"={yRBɉڮ[lf͚&e->y$ Fvvvm7#??K||("##߇F>}D豷R pSX>X*կJ.mnn<#E~UpzjQFF7%11q oZϜ9ESR%=z4 ~q2eʐmC1Tk 5FIrvvֽPREzOjj[_KK 888i&** +W8YԉZnA=bT:E2$#F7of ps/Ԝ * f)5mthc7nׯ_ӧO%Pt:;;8p )) ,ƍ׮]{̙˗/[ѣ_zEW;QG3av'aaae߻wo͚57nܸrJ2+[N3E٭,bر#(,,]K/-n^ݻw߲e_թS'sŊPws8iKᶵGn$ZkAs̡)QƍWT w͚5 Œ ֧L4[nŸ,_Arg? E:w\FLcrH?zԨQqqqnɔX8ŒP`EU4i$11_0K5Ǘ/_\";rM6Æ Sߋ S~};w(4uTOQ@7o,?55LFih`MIIR 2< R@QQ'$C 'NHT l=z`c&_~̌/_y @(Y m7o"-{ %ݻw'VR'O޺uK9/ 0rHS?g(:f-Z3<%++래 P%|all|~->>~W.]4U=Sbvȑ#Db_/^=ٳʕ+cNrd&|\O8Y^~<CC0?~QI%{gzn߾!D722[~ׅ b'GP`?&hfϞݴiS~rriibSԟy~`bbbiiҸq㨨( ӧvڵ0e",՛[TT5]dpP9880r_Ν;~-|H~QR7nLd+1"dQFk׮VZ֭[Ík׮$?t-Ȇ (f͚`)=GN|rѣG"9 @Ք++4ؐ @ ߰aH<|te_~}֭X8pe˖ 'N ҟI=I u^^?'Po@jj*ՌC.JFK!ꊐర0!D~4i(5\v[/@(SR*QZl޼~ϟ/)԰aiiLvN>׮]SꇢL -m|a 4>ay9sV^M!RҬE8*NR),`G]]V8~ץTɢFbN:k֬yz~֭nܸL^:L]#G̜9Sͷ\O!PBNJIE #5j899qu+V dKD%ɓkXljկA>}J6Zjfr\\K" YǼyH`>ej^v rIREs:*,ebrX 1+W| gꊺ\eȓy%JUeJƠedx9 Jn# W繰Kʕ;y䴴4j'&2Dʑ\jkkD h޼9_jU 8p"߿_PngllWN,-888>>*m< R.%r7)wCCw![(T,Vv'`r)4iwһwK.0%d%_wޱ^zj>_f{K/O֭ၪWm۾#sT@L.?WQR> :(.ëg}0F dAV?ihԨsݳgim׮^bәX,Ϗ5jԩhūi2kӖ҆}.|7o8p`˖-" a њ>nݺN:֭06m/]ٳ,Nk=""GohhvRWXdI~~ʕ+MMMQYEH5&@wfjG >3gP:66Id-Z;d`Ν-AГ=NNN߾}c0 ?m> 7д(mRR M a,70[v3\ > TYDT^|֭wu֭J*MO'1:E 5kְ5##g0{ezj0Fz^4nzٲePP43}sRJI ˗/[|hsXX8yfuw8f᜙Ofw—G\%~iBU5WԵ y!E K. 2XiJ߾}i߾}{ƍUx>̄/; &hzsq*j맷8/${-ɝ7nܰc݇g'/)Y$#y(;;{֭߶mJn~ J4ʕ޽{wPPp<^5H,ZHiErROAgD_jz).O>2ky|aҥ pŰ׭[gϞa޽{wA<}@ܹ('^~M5D }PP|۶m u#;;q;hefCuriu2ǁDDD,\p׮]eʔ)W\1}}}FV͡&7k,{uOOO?~8!!֭[үh_TT5' +gDw`mm 0o޼@x**URڌenTŶq-Zx9ŋo޼133޽ٳ9cĐmx]t WzXϟ?_xQ$vNJJJNN&Yl2tP###0*TX83`2#A*%Ă%&]r6}~}]hsss ɝΝc5k$;v2A> FAbV6 n[tKBL x@@"m?fO>={v֬YժU#;MO-hrΒ%K޿?sL[ű&x~" dKLBrڣ~ “~!QQQ/HMMz?cgg m.ɼUArRWK-1obxܑN(}.D.EI-[PӅYӤ>|xРA999GQsufh9<&Ni* L9LiMËzH_3®b16gNNNJ:wĉ---1ODC[?B&8"""bbbJ,_\A ;wtqq -Z?{vo!BE"={>͛7ǂ!^^^ܹsݺu1[D􇇇_z6wԩ9 +v[3nի!>E2dرcϟ?5 )D;__M環uS4]D)< >J 7 .r׃J||B1۷b8eP%ètT$C 4H)u mʍSmiږJ\QJM:귳 G֤@)==}ԩ(m `ZY$ Q<40tH~VRɿ1C/941vް-'NNNu z FsJP) Kdgq}“~UPPPvvvZի1<;w7+W1bfauDUR f>HVVkXX۷oɞ01`QDD1C-WAOa?5y- .z*h~S;wã}6l@K_A*&&xnR٢Nj&ҽN:Dz//>r{{΂tcǎElYV֜ܨ._EpxS=!k::yV&@5PpG?}TJ|Yp%KZZZh}o`ދ+W(C_*y_ҕoU\vaN '] 0`ӦMM6ŒC,|KaOСCϝ;usss㏷oFGG'''شmVp+Dnnnf } 2ԔIH4ܻw1>9ڹsgaO0!$$d۶mرΝ;8p:k陙 999wޅ=O>-_SN% vv/_1 ~<++>Lxx Ο?ʕjqr娿?.**sTA/·uֵlWEDߡCGEFFrsPPЙ3gn߾ 1K7nܸtҋ/&ϟO011ٱclt…ؑ >HJ֭{],ճׯd"޾}-|\y>5))ĉ߇/YAA j>\`φ޽{8p&ߟ;w۷oo\\`+V TPbyED?<<|4O4DDCf͚mbIEDݻw~eBBBzz`bԹivZlQ1Ϟ=ԁ6mvZ,ϛ7ʕ+?~d]B*U@K.dɒ 4i҄#00pʕ7o߿?NAM>>7o|'hٳg]tV^:o޼UbYDD;**ׯ_[a[pŋq  /6 Hsrr233wڅ6).?|ϟ?wuu\ !+S$^]fw~,,, h:pnAP9۷0>|o  E矓'Oٺu p. Am7oh;ٳgyɳfͺy&/_~ٔ}yՕ+W ƫl={vaa%׀Dˊ+"##5w5=9s'OD?3!/;w͛79_qWvQyiP| bBIǏAw Vt~rrn;?i\el8 ,sYaVCH@-$ ,!2# hh-A5¬VfECc޻n{۽9s>{7n_ K+W]t: rFdWFx?z׸߿-Y'f=j}LLLvv7=ZT:u|Juӎ?600p &MZ`Ajj@Fu9***?N=t#K?QX"߹'NXd^g@dΜ92 ɚ}@Rݹs `VBwؑظgׯ_T3ooo?? џ9sc Å ~+|͛f{iTyٳgGe?p@||۷u!$][[N3k&sᵵ=jxYYYd$@HH 333.]:|Y?]U*/]%ŅYYY~~~|mZt:]WW׽{0P7[[[LuVÇ@+F Ųe${WJBBB||͛7G.p?l^{u_V,^x޽}~ٳ߿/..Bm AbjGyx{=* &={F>u;jԨM6peDRZZ@/0/_TTl\@ϟ߹sȑ#~ #FJ䓳oBk]իӦMٳuuuD"000%%۷oOYZ)$aAGZ'22244A2O<٪CZ]YYIA_[ '"%x,wtn X+Ю82k׺m222ӧΝ6b:50+mUPӪw|ѪM\\P477Ϝ9pO}8 ҂Fu?KR̊F}e~?07C2s0q$7mrt dأ~lܸ"&L`t:u W+#,]91bk FgU?~&:::,,S }5E]O<ݻ AAAB777@hX>'KKtX XhQK?A3gѣ&''oذ{ĐZc[ uPgl|SHRñ)$vG؊X.3&..ϏiTm۶AD.[UA$O,h`)Grcc)&f-MثR Y>ij= `4%H5 ێM> ,Za ]]]EEE3f̀kH?X `\mܹf[K5K 5Vٚ! tn O>MhOoDQgU>&MIt C1y :Cf[6id >8ۛT]]ڊaoӴP]Zc3^b5;,O DaweeeqK[RR.1 q3k>񧞩9Zb^ P }㴆2T Ys^Y!<Ӟ17߾}?qE bG Z. K m}"n"Sfg~0s:f~ǡ+w Jڵ -c03iͲ^Z ۦn-V5p_@áe xV!744])(R*= 6>^6$+Td` vaj8@9x^Y_Ii`΍OdqKՙG.cMte4 /jaăt RAcgЃ!?V'! ۿ {qh*LCS$ ˿<ʋp $~P3JGe#~"~ n(yg  $DBHvo ~H8 xL`^~"~ّBhuD^CP91 Ro‚%Oss0<9PJy"ͬ&ADDXDs qB[XCO`P3qI%x -"삨=\B]2~p B t&XQGaAmT]ka~p {F b /"Jj}4OhH_ %~~qFF_r:_v: @C1H\@ۢC[?/3 @|ЩdE%` h ;,F5!IENDB`camping-3.2.6/extras/images/latl.png000066400000000000000000001171161465547410100173730ustar00rootroot00000000000000PNG  IHDR3tgAMA7tEXtSoftwareAdobe ImageReadyqe<PLTEqqquuuiii++*mmmfffZZZaaa]]]MMMIIIQQQ999AAA===444ð111 շ---!!!%%%TTTFFF{~~~ͦzzzxxx|||WWWDDDktѯddc⍍Ȟr}ٯĖcl`TIDATx]SLWDrX$r؂vy)`E)〥wMy[*9HX Ǭj>s c|19s c|19s c|19s c|19s c|19s :?|gCk=Lcf~͇|g Z! #,egO|/,ώ<}geTX2nsfG/m1g><8̈mH[c`GbRr,p̀}x1 p$D #'ѹasvTǗx tٰ}PtW)׆S4W]Icrr@SrMkܚ ѺǃZH*9'WHhs$gCE-_6w^~uެ9 {JBEгJb,[J4s G껒@'b=(t{w=o1쏆jlS l!s ghTON}g@dZ7pfFrٗfw gW^]ḧۃ,*b˖ÙP>;Gs gf%e@RE/4s gd֗SX11a3<;G5pFFZq\L`$ZΪ ]3pvFàD7"P(˹ֈkEk944Ag0D}ŇU ?:Ls gilhz(wEQ0?eHdSɊ=acG!0kdg@,0Z\_r67/Gz[G\Mu@4ļ[md3ÿ< W;`bNih9'H*1em3 ڰ5$9yD ׮ceXJ뫎SW/l#&PNwG3h * aDfc{(IM 1YQ"[/9Dn5bOm_~F.HpȔ؊F<3,?V>^ } ˯f]}*_#w7ܨ|ukj'#f> ?>cx( 3f 17똅/f 2ĦQlWvV_VcxXcv y5~d`pҲLgޛ3Uq)ZVMi4>˖5b#]_s{c14>Ҍv:j ꚩ}*̋Р) =KWIGL£h͉5(~OGD=܅ czQ"s&6uchy*WNa,:?$HP(߆#}qA0k,+AL0pMDqI{_i 9XEXyظIlA0v-iCuw!NW | E>+pW>/:|Թ?W@_g"J@> fG 0L/L"L :sJFۥI-!FVaDJ;n; 0hJw7`K0l^FW0z\2ҷr7rBR=ċ̱Iy8cIu_w7AZ))4'0S=ƭh7 \Sc8k,W)~Gs gmTiE4pF5I&z԰U+wa%ٮ(a߃PpT=1o48lye^fؠ'`IKR ْSTQwVBN)%F Q Jԁ8S[ ^e C@Hts'wQH6qGVg*g ͆PPb5.nwRD>cP"D7j!{cP7!G$TbSBH:WL$%<"i^z W&2%A=) MtfgbPoPQE) eaWQ3 l1T<& ;/0K?ŰpEn;~48 rnŰcP"|TPfF !*$,G*w? Æz=ŰKFSaاCwBnp5ҡ[V;zh%4B thpFНUaܱ@=CdT3b%XW1TA:H o bE%MGFC9.䈇:!Zr4C:`aP%Q9DJ3yrG"Izy10?Bs+ha7NYݍN4hJRy,'} ,fP0JԁJ jC=M=F#9mFIiZ'g?O'!p97 1E9381pKg#97b~_»!Ե]ly\ ̀[pRCNKg9n!UՁVCP 0lpW` ahs(;a ;?`EA~?G0H"{^`hw :z '1\PKwDIV:obhZ;T c@(CĀ^hE Z en"7t_6q xHȄ96UFkግp -'Ùc8SMCXJګ91p@ttۃ}}, ;wa2E6¥j2X11j+oO:GQ?N7b)@::՚->;c8|%ڎȦmba ҡi~}VokdBʙrNᨃHwGx ; &eaQ_rۑaT alL Q^N7m|æVwQ1ܛéAiOԜv%@B841: A@v@b-I as ;9 rέH_>bwta5Vx=+BP]|yZ:i@RO{ }X\ڌ`% P/ &: !YI6[- hǹ@L1Tls&0IJ 8fC=/qB $?o$7V_yEgCƋx.ǞG_pJ+e9=?-[:j3@rv xߢߚ9 [$|Ǹ{8fߺ1xFFߞz MЏ5DYZ7֐Kt41NaO="pڇEp#~~ytA17GP2E^+2_ JS|n0lDTYsGul2E^. %+-uߩ& I@ll'r}W N>Z f8WMKk0~84aU7?vw/%d}I (gI(QBiсlk0$c<*S]n(ІZQofeۈw)Q5ZqU*7C#UR+5e@ns}0<1O z?FF!S`L+>@Wke HX`9,k$h<@f=nɤUİ1LH;}3! BYC!)M16Ձ`m1K"&[.ΐv K "LQ؞^3M5oJOdGEsSZRh +i HsqF3db޵"wj>QǬv̗Z#3$WOmQHk5}V9@yLWߒظʁ6/:Vf%񗶗2ٜw܎AWx+KbS'(ٺVŮ5H[M;0(ʏnl {bE15{@dI\jYgˣ&8z~gab|J&:U䔓@C\d`[?kY5k$/b" 0QinRt,.3MX;1u,u)0)@lJ]>toFZk! ȘU,|'w[ S\qV`SՄUs,*V^:"U) h0<+Ş;I!:H %8DX@z,ĪCXZsFsp2U"sH.Q8TφcNJؠ2*!AJ~V*Kpz Zc w&@Rtn(;t-]! 1tab/~map;eCINX2S-NůTgBz m.yj$*_\ܑ7Ə][$95~ %&S'u|q;q(C + %4 f@^i L! J_nqDlVEKⲞhݖtbXs$mƆ؜=n24>xT(ږϕ?0t653 A -%D}cG쨊tjT)2gXGaX:=|0:; #~T۱?f ?ò ^J]\^-T-,c! 9Ka%ˑ2xX !7N)K6ز5zݯqJبˤBz+h[s/ojZ!d2$Ctk*"7o''@8v*h`bGtWQYf6pyɅ:ƾ e("[.k|R+U鬕 6QJtLcT- 'b\r@&r`^8< G?72Jz4wV3P}년oYs=Kӝ&oՒ+b~MŦBNE2cPHKE}d:1hL@鰳@L☕p^ZXII&Q1il( nPR_n2A2CRxoƒd:;Abd[̛4uн=7?Y1zC!MӜE:}o9LI'yIK2;m:2)eJ^bg?;Y>cŪuqdy)Tsӯ@ / ߏ9_{Yd]ۿ[@Q/e35eXpE(n\Hamp>26'~65cHaPj0+0l>%O$y]831 4> ^@ЊE'*٘|K]^+@lv1HY  U? \iĕjӅ!m]0pC[Y '+~d|?%B ɣ.'kJ @MVS Ep; mhBs,BdDy^BPөPǼAN;O05`fozw9 *{,D 4>AZpUl"sf#'y1]+zxXˢt5d(ԕh[2$L 9y\/^"x(ZVPEZ /!}hd G @Vca^ yPI?`3|qEb[#r*+=t~U Zhѫ`xI+,i.nӃ⹳k*gF\Qa8c5 iM_!bQlqt9:K0lf,-[ZIRe,S _,*\  RGy]j6gwGյ9m1 s)zF՗ϯW'aPh4v{9${,&uiS nD ( ahЦNk&P0P4 9:o,-$peW:* ?{aH +=<Ǹ)<7Hi.U5 D 3n^aBT|knLin@J6tXд#vDѥh+!$1S=2'DTөG=:#,cȲXiy |ػZMN/P LP]BCȄ)PP0a#na Y}MO}D?i"`C@L Vlq{ӂ>D# ѱqϽ}UƒDS]քe E7$ h:î6ޤ{e?.|D߱EI;ŷh)\ uY;dɺƧȹ$p=+gB"s  nȴxG-vi|U<,p)õu1WϏM8_ddpa_Ie2scIOc$uJ'Ưl"1ҰuɋhzZ ZI5p5Q]14'/CP~}jŶ-ʥڞCte<)ҁ ȯR$Tȑjޠ ;ZlAVD&yʥg*fa%G oDwjR _o_Q7,l:N:ʲf݃;n6d*k JVbT)4ڽ$@Ov_ 7*dq?Ģ'x% af9nyvcu=rb#<{zj_LWEQ_cyք sb!nB*h%EN>bI*J;[ӉJ%H I==@Wm-IN9VXJaQ{1|Ni$Dh^ U3XjuېOq:aխ 1ͤCzGԪN#iZH5Uo0 0MXiC2ݒFn*{?72=M tOCܬi,zENkZV[e M8MgKm!4Y2~^myjт#}?T%nLyUe:NO2U<4ٞ7CvTķ-c(\%;J+%UmS_À(HN3sSIRDRS<8ZHts@`.$ Nu%( JOPDJpM84 Ŏw@ %TtQL0V6ZJa,=kKAÐ>pB35u3i8״ɮ, O:t̏^!JOayS1(Nq6h;!YnK+K@Og XU Rӛ'Ćy6=){Tu3ÒuE<g:d\Zڗ/=(-DrC){CYDf6ȤM)vaub%l'a K@&g7- 5sh` A1)0=򈓋,C~6Ї!-7󧪿r @H<2Rk AoBz0nPkOŵVBwu |E{\󘜧=}^sVGPf/v x@=i͕v1a$'ёPFLFȐˠ,o^L\+xb0,TɯwW defr.0ӇSeQWƦծ{ܙDn0]@\?3 Vu[uCfYH9 >v%⮘(PMǮ ZHj؆h[yV5~ɷԄ{I(uUfz ]J;MCA2F Q<bBނ9+!NP%fŽpC-kQd1/D<7xqpCW,UD?Y !lF 7"o1qG|'Y`S Z\ $EN)<|}QKO rS}(ۤ/5lHj!0^RI6e(%'OLf}y0t`(|WԜ-VEAmׯPS̸[z½LEAlRvA3a( DqT_I8}k6ze0L1|KϿTQޙ~ R}&RfaG,i!1f!% ~wfĽ <ȰҽKsĦ zI 2+mέpF6 V /Zn!g*+$W?wɆ^/d`e+VL*X^Hœ{u*ݫ'Jë)܆4'C `#}@Z=St3Ke -HR0.8ޗM>TӜޏ2T sZ*w^+26LMՀo6SF^Zi0l~Ծ3:qI .{4_vlN&n} ܈k<04țɫ^\& Wr~d}x[ >ƥ$WnCo0EUșM#K1rU\fC&W-5ѭYI=ht\4eVu2O]2ip'v Y`f^0+z~AiBLW P#ڔgRaЫ]\iv(ነ,x"2ormX,IVfؚ" Fu#LYY} d&$hpF1WCt6 3b,|Zޛ:mlJ!2{e{9˝2Erzm,d6o=1^'Au"Ȍ+tѰs}nuͨ,2m5'tFFc9J{]sL M3K+0ܓ~l4Sؼ * .Wl +,72ގjc Fm\dKeu}K-oHN)=.V)oW;{L{OKj+KM+UEJP{BHO,Z+Ì9O Ã| I:!4y&~{9 tVgiG򩵬Զ,"~?SJY\AՖESVT"FˑgR2P{b>? IWawV_!iRS1ЧІg"NТ :ݝLvNqþ~`PϏ݂`C4:c{fJ0>)72 Kk&?v.SG8vuU(Vf/6?w'5ZEr-5~wTAJ[2 kOCGר _A%`3V% vyF)`Hd҅?ƺۄ?+|b:0jhQBDdhƲܗzC;W^{5z(؅ IVQ ߵǫnwA}Rm6r NK1Txq0"@dQ.C%#-=  VDW;VoOqqt=5B{ˁ`hvr|#<֔NJ]_0 C:&>mȔDW- ' RQ(9s1)tȠ\ x`R o/#OeI*Y8]=PEݬq%c*h *o2>NYd?*l9 j $;]SEw4tG'^]5 ^su]k"J=D{e&p8? ӟP@l2'ĺhI91-3ꢔ_$b  &k"Qw\&ͭ|ZZ[zq+ >a*MfSX0یR'`.#p'$ jʉBG1۞?5cM-K:!D "x! qtH]7I=yiƧg̕V==eӂ,D؜K2}0qYP֞2C;?՗PCG'c腙mtGk⥍ iH=Z6AjbEVaOd8ߤ)ɑ~IxoKxS^zn(3﷏P\{ a_a9S&YA追o{!^zH@}8<|8rl!)bTMA<&: 1-״Q, zI{_tsu4tNJ_r r~{N?;b3t+>S~<'iD("b7PݠC=ΰ'gS6p`8*GDF5^>[¶ܴEӑxA ~3+j+aaFh˫c+!:, ՘~B "RZGC9 sVEIra@E %a15oz{RL|:#qOɏJPgmx1/p6̶Z <) x|/C`E0Ԉ!4N=0&=,a9^ֲ.$,\ &[.R[ӊoDK6j֣mǻB1LYY '>K*!~t18ab'ٸkԁ BvP,-mf5} 3g{D=# )gnba8v&Ed.]=Y9Snp%0lu5f ֌RzljZX݄TVD554wŮ|);J\դeďf& 9o-Xd~zxkDr|D]_^O2!Zخ+)]o3Nѵfe5XVf=+Y[%H;ld}vʞ"dc c0 /VuI1Oy~FSAjr9dLa(벂~ϩrȑ}EŤKT K<@Na*^CP0qJ ?9dP"\k4k'0})vgq nśBJ f^hb@/Y0Tvi2=/j}atN]W4ž"#n;2/Č2$c+,qZ4ۛ:wɦѶQ ${ܢi7Kfͬ¡'-84\Vt4/!2ˌ<ЎvPcs;PRc4Vmk+r*shC?#!0 d\g!~$$VEcOQ4e.ހC@6eb(Sí]ӹ\4܍*>FDZ1.8uK'$&8gR=EedJtt9yw"M՞rg_?v|}q)tFeb̮rͱ5FDj@R0׶?`"TLmSO?'Һ E|SrJ)^FjC#)Bq 5pA6 JH.Tqu;f\m%ylZ^$u7qr7jVXw9N0\k93˿^X2)B4;FVQSHSZV^UX-$;>>H}#EXivWnEub ЛjȖJon"E)0 qbߙX_`)Fɱ5 GP:|6J0S_3.7e<,J56{kv 06w.r(BMDi׷ dbHX@H(7 N﹠U콝yp$ћV8ϗg?M8C7o7[^Cك8Bk+~Qsp' )'i@a1w!CSHm- @D@cQ'&kA/2 W> #$mx^CzF?X!c0ch/UniBe4( s_dդ|j-"t(7.yL=?gu1 /h WY8J"Q+W)(#;PZ Ch jዤHOwWD;z %N*1vl-aγ.Wj|;eNDL !-  |0TøvOU5bpZ'Jh$bZu:f@R it(0_B0ʢWmFű6-H,væ$QZhK.L-X=7 K/` #+S-{='> 4Ȑ˥NWs JWĭ'bٜЈ(E U~ɾ:8te2C[b4>8L@[{2?(%J%e՘o@óltNXq[jh[}Vy:(T)%u[gAzwTt&ም~'6K]JMQ6? tg牡)77?W4Nn;k~CIٗ1EnXU({ENϤܚ}frŴ溄Lm}\8ռԱUWb 5 BLp#0]Zg}wXgnA5WfrN rGgpJ,,1٬XɫPW]P,KЭ4 a¨.=SDn젷"77A3K SLvp(;(0XI08̎}E"?X=e#v!!s-1D" -E5%۾/{iU9[U= !3qCzw[)֝% |[,N /MLBF?Kuax1XE mȮ}q X!Y3rAjS,J =Z -4OV  dBD)Q>êhygo2OhQՎ[F*uiyAI'Yh!9^z̙ Ӡ%S^+T ="w0l ,#љH~RoLg KU>0SiXR=qi.&>Qm$L~w 09@e᷃q, ggUZY2*g90S<ٓ?䊘a!|mLP `D>Frr<<*L| UYtOm"5cdG7,'Y 8%)4ߺW@Y."Kx'W5S"{tH5d@\? ɾmu0nݯavaŭ8E}p ɋD`ark! 5d!<d1QiNs[jw>a pU;u퉭^a$BԔ `Y_;j r,2Fa:Sp̓Ā3{cw7}-%zҕzfw25%*~&Fs?a+UM֞qnW1ljWxWش<ќC WapJ9zO :l0%Ժ=,$%LG? χ`FʡU%qi@۱9fc6=Q3uhlDiB~YBQ]lt((,I".#MȖyx**A )Äm'k;ɸV*ػM2DI,`S ROJ{wTy=5w5n*jGy b's>Ut9/C16G<ăN,&MFl{z,g ܃zWb;1j<9To D coGE% -SBb*4Mz{7R}]:hBa^fE{%d)Zh~E(Nvyy,@Æ𵍚'#c{0h%/K>jL X7.S2U -# 7Rt4ƭ`4E"uqs;dQwO:@ #qE`atxE5:jzݸyi \i6EOP;ApES Ţsv eP)ullhltF]+ek%}f\mPZ>G3#NU:xdT~U24Y[ A _ \TCSviF?66Tqcxܦ跈<&M ,ob%m(cO*'q饾2"3 a\4_W`sݠ&BVRԵfC"+Glkg=_B5j( ΍ai+\ KaAQ@V`Ц|S9{9ҳ>Ks%BHDM>L{Dzv3a H2i^ id;[=ӻH+hn C{mXϲkIͽG"WWmWmT/H^Z*o3 Be;n 0U@ $610 ;Ԑ)hX96b/5X NEbL \ ޱP2Z@M;9&bBōx1kDY#JXI1{"kK7#CM\(Vk{NQ!nۢሪt ԂKpǶnyFգhdUx2 DT(󱼋Yh99C2@G}D D֣v1$lFζ$ 3X֌du>.0QBR#)=]GT, ;]t y:8soA?&3 ^٦i4u)/؜=Úp7 hL'ILg|lRCMg)ޖ|C]겖*b MIHx|Gҡ/mmiFrDӠ%$+V:Ho$6klVGh-ڎҺ ^ttM,!ߦf i7_.2!hNXGxZ2r5f@(é(e-8Ea|[ "T"_YNQv2u)hNW&+U5I ,M$X\: v,pVU*)"JIi1Fpj'Ƣ 8\JG6K vzGMh318蠞?F|Թvy12GS8Oh?RiÎm+HjxqFiYZG yyH8ה3e:8 _?Ъ'X:0( _|dQ+sa~t]Fb:ݳ":cA:AVO̿tSj6byK^1` X ޓXz3,?ա"]%I@g>@yoi )&ՂZʛEf( ra@|UJ4uqH9Lz+/QoijVAZpC9Z-ۭѮ@3+$KQXadZc'ZyJi3oa6b7G!HQ Ԃ pHm;֖+?lu+?\.q8mkddx:k}FobxtDdAxeYʹE(JRB379~ {+3,-k@&m9/a-s;S%-2kU/h-ޤ!E$UZֶ3ZxPH4ؕvӠwkBPUDNsR"32̦q%ʋM>>n4gt#jQN/aѵvtb(#$)gVu;D&7~ X Afbo8f@[ygxCV2\ Vljn%xojk.#2|֙"X Uj v~,qu}l{nJu~% )c {RE'? !U0(+)(Eړ ,i@!Cv-D}m 6j ss\*jMΜ7R:Eq8+M'2k̔i/UR8m~vVlcf+wjLudhrd/,n28Pk?Hm+󘾕1QZSwHqopx5\D4\}hg7AK=EZhY*x@X@%Y(d:Dtv`H UN5p*[GdʴR[7U;bRCoTtd=㧊LKjgs %+_'JHuNz`VyQ38j$PĮSj> jY [jG~MD%-l{ӛ7$ZFK 2uMLI-8ךHb%WO GI=a(_2 oχ3DA!U%'$e#Ԇ.ܐʅ}@&K4=4E}|ɢm#IJxB9Cy]8^,J szWU/ HW J@⭾'9)1uw5 39xoϺP*Hvj>\Biccm Rʊxe(L]gtW6ifS ~.㎦+ .U $w=P=zk>h4KL٢t "s* ^Gr4"r/ݛ' Aϯhl+*el]ENLEL4.G|o]OC-,iAi=-RlΥgBJq0*r (nJu5+k5޹ @\Afd6`<ɹֳeۉYQb-YxCJd_{Ee-r~`ov3?agKŃr+)#hJ/ j`vǦiN'5ET7e1X reBo`__s@F9qUrh\=Z$JpO R6ME~X3ؑv=ʘJq\y5E7`z8/?Ϩ=ϖtKV+OlNC׆>r%tߛQdNX -FH@xӎM\_#)\8,+X;ڰ b5rqg6*ˇ1JGGo$;?cآq;) ;D_}j/U׆Tqh׽y;\4{\'ϧ \t!j5TnW^.`+$(cN/NuWdMheKǛ&Msg/) 5SKi@2e|( Mz0l OC%*t MTxȇⶾ.S%'d֛ ,yQ tDD9KN$(gkG$MYٳ  (5Q3%n74Y(R^dE>ox J]?g %D "fɇ<>koA% 7̥ V[$/k<EQr *J109 Ñf2h [n{8r,C= vP&uLz[/_" ,ɶ42`캩ԩ pfS U^AlyH\n:MxbۺBH9)\Pfmg @DR I-$kK||Ÿdu͜ܓ ]}al+&:j@R~O.|O(Ȏx 7'h$eZoSdfV.d;3!&x5#宱rB3JCqyFrxE]ĭfKaE=3S'P# ++MD+fOZ̓ "VTkrݮMIuŜ=1H Ѡ>h{rkAR"+'3l`EE5 2OaAþ2[yDwxEZ O8c(DY`ɜsٞMƿU X9Y*Q*fTGɄ1@zIOe#2oM"_[\I>chO_iޓ&XA^iM '羹[MB) )r$6ǝgM悹E٬U '=cX NB;P 'ۮ o`| Xy&!a  <Mix2q3o%c"eBZG:&ϙ ̶#:ƕ7fktR'C̠Ot?w@=K%n 怩gqRWq2* ^_pqѕH@y M5ALM*=ɨΊw!VJV`c%X{x-=ul[ĕQտb^,Ԉ/%!AWR^&K]{͞)ʐ*%!NmRw$b͟<:,X?RѫkmċlSRon[pqZ@[nɣPas)ctψjl@Ȋ>COma@oF/"r7י 7x+ϔR7b; ՆJ@Q ueI/$+Ypx^,gnۥӍ:a>ʊ]meiu|B4l>2Gi`s[6 $$n( T-aðl={E%G<TruڎV,5bB%b8oc֍쨥Nh:̰@'Fe5'ʕJ5xAhYoCN=lW)a hÒW:Ѵ1)} BB,)$;؞,t)i`R^6-ƙ8QB`uy_Knk/bX+;ʮVó4|.-$'{[[F8}#RJX1朔ҵHMI= 9kM[ڪX+_--1)yoaD{o'6=f .>ڤ5o]QY+go%TCyՑFN07 1pj[0rG/J(EVR4c*SٲN:^g3n~Rwަȕ8%>wQؑV/7 NE)GZ ne?L )4oȏvV\8bW*罼o a6;G'uKOxN51 _ mo*U ܱwur>r(o液O'w!)J܏Z szOܢD!H?)C }bu`߯]҅=c(k*oK/C,N;g{&dHּcLmżaxcn ױyZ<ǝs\(dwudm ϯC~[x01dC0r%K h{ǰXHqᎹ|_&׸x‘~y8Qc\yf. ĕr!6D9軥e%z(i[d{ت)L"8#k#Y/2Cn3?kcXq;V ioԛ{ȍ;/YtOzm1)V2">.qoG홎*eW1<AL5FRZ5/{ra5JN=W1 h9vxx D#סf7$'n:"YtA0V/*Y/=?hTݫ7w$C^żb֨{~ĨB2L%s8mBS0O& x{ FM.|_00*ku8LzŞdk+u}GC^QGc>{t{DG070,YT P1;5y/}Fw'C^w;k7WwMDн<*eF&ϰb 24 m5΢h)l-bNQbw2a^':hvTU 1N\Z{*Rv@Cf;0 ?"1TÉh!wl+R9; 񋈝 J<wKaR~NI*Xv'H ]l CLa b#Q_IC{>'E+j,>gjQ LƱ#7?D{?]3,2u^y#21\dԣYjPSe@  D5OBr^oZb]$иHJ͉z`V0Pu\U1(Yk(O cp:cdwRGJrh\fY0%D¼dհ)0.ߓAǪz_M;ZIoP\J k_ǩ" spB^_9*Uh=4WpT`f)OYYhBhsIJ::dxk L _R:si;&T|[b)9r:l!o-r㍳O~qZ+s57(pN7=!@3gbJ̋˯w+ l"SK<,Uwޥ!OecYirll;|(Z隺R:#8&h8<UY-.ro%U|(0jT*RT-@Zr2B^Mn"reT;&BHM{ 6 V1˜V䰿u`ǕKL>L)}rcS*OQ֕_I{6=k,ZUuB\叼7o#kYn! 1օĝlPIN1NlFIL4T:ͳw|EE7ӔiS!3Ov$5LkUfb bg4"dB[VPg77ʭKeP2doGrY(zWyy*%!2 Yh/#Eove*=Ʃ3bmɉ'$l ֔;ڒ/C _\K/C#-:5(I8h{ "Ns12ZU\SPfQkTZ S8STA j`52Z>n(<.3r Ne*K}I{ ; ON8."c<#z8.A(NaNn-bԺB gv;oJ}ZFЂoT'݈ É<ZL;i !q°닣{l):W!~ٵbpHKvN!OߐPMn%0)Nݱw+x?kt֧~*1FHwEʜ,op >(E4[ K2զփNBDU%\NS8F+˱0 }x}k:{=A?EsWt0.g09k;ʘO}>qs=F{yv+l6r=qQV'je=a8^bqYuϨן0ob?{iTO|{0. s<*Є*e:9ɒ*XNu:c8@͹UrvqqCpXtmИ ?}}<W_^G+??bݫ>??}z~ϻ5^1μg~GCB_{ŧO އHB'sbdw˶rLK3q5qf˘cH(ýa=TJl1.זX_ذ.=/Qi JIt7G`x;ÿ?ÿGP>,}Q/@orx}C>gY҅{}}7C|p@[5 Qy$*N)-d(OGk \EB4ɔ|L07"Dj^be Hj8|qOV\Ӵ v۽`zM͢7҇h_"]@WJZ!Nj^·%{_G.//s7WDc!˪9ڸDo=١yRldR NʥBQf,$'}67i|0#\LY hU.ȺIQb@%g+y{Յk\Ͱ~䝛cw_1Nʡ;$_b?:D k/Yr//` v1īY {nt# 52jԾ>54. "֧ P:ڈV*M4XB,<O(:Ì&)OrA4|p i~?+$bחɼW/1|  Y 6G #*m~ti>,ak×H~ )3e\ hH;^# gjʂ4ͺ+  .G$r??Gf;tiG~ƗS;t#cEa>zïxs i U NʅցcN&)嬴D|*2VI(7hB(;n޶@>Y3ܴG_?%om|m}wʗga Fiחz/eYv},!y cMiuۄ`oW`u<VUh9γ-QCC:FFBq _j!?;l t;.9FڽJ0󬽅A:v⤙j2J$$ gU`q9 bՔҥd0'x4 G!Nt QjU1| \/_~~[?TO6{#M<^**Pmm9qLY/~f=wvnTG]sN$k5(_K_{+E08&Fs|IKmo4-nV\^51ǸE\9D)ɥ7~ T P ))DxeeD14>"oԒ{iöhq$\x2,e|6MR SCRJc-({a.4'8k:l 'l"zG `um-m͌MxO$/=;"hڸ@ĭMx//[IJFɐ VW~G11-An660Fqf~g~YISւ1E@<(B~[΄JɻϷ1_&f6'-$@GȆդb&HKjE4NX{ZBZjۜ˶iTDPAϪӉ 0N*UUeȴњ} h`f`R[[bh P#(jϲBL-qD81QUJ*Lm=ʒr26c2)6NX(BQRC?2v{ ߽jSH_q./^8s>{߾}}:<&B{spg;&.{и燳"à )8rQe2Be0?yDD@yatC<\(y(?*.𜞟NAidX̜bOLwQzh_4+k~,}_~bݟc .>~G.R|+_v~\{|^]\pͶe^8M9Q,][{5/O^|G ^D5 խ݅*-y. j![-)ɲSCƛRNI$ͦX rbQƋT=")dHEgZVϒ3ɒvy^WE=tko}H 4} K _ӏkbO;r;cn~A(h,fY@3O G^'BOڨ>$v4ٔj ¬rV~xk0g3B%Dx7q;RW1|f8Vb쇽+>z`Y~ṛ/hmc'͝1BPHf?'B(O.eT/G2FyJm Rhp\fe= ySkiؑBDŮBNS :,>i2T/1g- ~ _ . I/8%F}F4jcqhad Oڶ&9r?Ú|Ve~J¶U-NM[>uaSq'gXVjPiWlSΕZt2LqҀ&bRdo*KY[d@>엥_)K},>`F{# +NO goo **qm1K<6+Y,p©W8-jZBL?4K'yÏ_'Fc6DIWaOٿF"Ϝn!jJ|+Ȣ0ѷ vP眨Y 1\0R5ŨyNS1U)^YfBH1i7D5!e* q^N挗ة7f#ٔcRmg ѝzr鵬@ B֣tva^l@%D61n'za{1w1_8kl_z>|9!k>#ֿx~LO]alUHz]B-RVF-Yܦ;c;ԤVЧq&ڜ2[3~-4m}vI?݋}~A ߟ92v=;4EԺҷJj,L9'#cMr.z#!(!,ǯ~|L w[ڥ/ |^]]2sg&>o]yxx:_H8_Z\W[Y>a" lGy8\>//L1RSxV;lxKʽgpf:*}2 j7X#0rVV}90,U$g╓ Rm2%v ZmoTnuFt0qs&_ Qf6= vo`Un}Rzw~ vCz1 }?^S9`0s? `?? w;_wB=IENDB`camping-3.2.6/extras/images/little-wheels.png000066400000000000000000000420101465547410100212070ustar00rootroot00000000000000PNG  IHDR3ͨgAMA7tEXtSoftwareAdobe ImageReadyqe<PLTE  fffTTTzzz:::CCC̲T Z^vvZZŘXhӲɦ" _ZXs[qq%%VVvu=1xy|ȳ*EH22󟟟l%%0 ?@w^^^p52;aaﻻSttkyc޺xzhg~BE7b9:+1tttƵW)))ӷ2KI||Ӫ|WWmmmLLLX@P/*5>"ð111u|BBIDATx읋ƑZHBOĶ78رv=gזWrv&r<dsjnI $0B)K,K) M5VEn  'ƅțoWb?}*{Ǟ'*9(W #ne tjדTcȅ=kA5= <;e_TJ%RN,Kтg,KzS-_(K~g¿1l?38e<.).VvV!yb0)>,&qǘ@K8}:Urpb9W9dJU;Oy|AIMgJv:Nf.yR%O,nſ=iy+ g׼4p(9ڠ乏<Q\ſjwlFr9Q\!qO(ebƙW)63 e&ĄXnsMB,/ZyrV,VE踊1Ǣ?Nu`I[A^(WeaA?gze"4E20بm; .t83 Ï Rd<^eVKbPyP,nqywQ}Sz5ʧ~aUktp ܸ:Σ6*t ܐ:+#pȅY$E.]e]9$ 9Z$hT Ozuz]UU_]'I1 Ϧrol9oWl֫5XsQ8dS MU]6 YӬj"U^g^ #Y~M8i<'|18)MX{1x :svh!C~5)F C²}|HV.W*=$6C?? ;GRKQ5<8Q6M"nƼ>_ۡE4E=!Jc QDa*{*o[l(OfvqО 6 N$ƚع ~^S/MgU?Gv*Wئ \edu66EYJ+ItůgĿRՔɴ|?\9LEGL%3,$-9Q%5犢>qtk4j)u]{HrGɖq9+npW7Ȉ=sF'bny\L|RۓϞ}k5ƦyyMߞȽ+pq*Aכw~h+XU=$j&}|Mt$bu9ΣANM#\|D80ՁQ㒪ʩ¢S+6YtĞ$B7wLB96[M椫 e9ej,IStBy|u8l iRrZom)C)у=-;0j>&<*5 ?d tb{;@7@OQc(Z[xh~h'~f#uRCIwǚ=yqIa?siA2z{Jzh b}7Tgt$LLCOQO2n#Oґ"4ҼbM|Wfoh074:S-ܩR1DKT+ҟu#/d2> p$*MagǐlyczrX3[<洏J[x׏f`AJzV=l PV^N๷h b B1Qݟi\# VJ + ϓ·!,[İdm8t:Y 67dԁ00@TeGL.M&Fm pyU܆bG0|qtLvRp ţ3ַ67/,B,+M+RWDu\([>!z/{ d۾&Ɋǧ ~; R#VU_}pJ"-H  VUW!'qNVKrekPc}- yӁi_>Odg{KW<=V=fHIHAͯs4n-ˏqjV2U @ded/SicOFE^X$U$@\gp12>_h&@q|\}qȞI gf+ԑb.s@N"L5`?=41Q ǃ jܷ"]aKJÄk-C-P<']${5Ap)ꓜCplUcx[WCN@hO05}.h&E֎#69P9PTppq]{CIhh@ lW͙lΐ'b~{ L"4\KF6rሱl @^yh:Lf'z.}ui^КRZZ4VG! ß$h FSzدDO<hi. :d}A~N)91܊wF ͓ ph-Lƍ=~c}HMp8 CaS\yRPe^r@˞fo ޛdGAzh0؈?)y44#2Gy+5IbU8?Y .ox+ qsKȁ@:Q̷(hom+=zS%vD˚*o=iw'2d,TAnѼ^Mn8>Df)="iEKJ9xj8Pjb~11[wzVow< z13,GvC~*zz@Xnh{ߒq.6P.~E݊uit5'E"^[+1b߹ zhO r? <35t` ^mk2_ y1b"\ .Kk8u^f.W :äVhpRΙ+ X >Z|ѿ5tAoWN;#$f}DKPA%$z/UQZ 3݅:k=#5iV C?dJ>p3ʿY'0X d/<=åy~ʹ{9T1gY%%yd+gcOΣn>6t;&vʒT+ᒅoWQo׺-ftK$"dg sIڠ]eAT`2G&_nGݎY$˚sTnEL(VF3z6xm{[G; ͿT-!lek:!!"yb)SGs;Mfr1 q[*٣[2ܛ7/'Yz(9\V&m:,MoHVo3xEaSВ'58 8j9udLK;6EQlqT_2g:7F/2 r(b̔VU>SI2pfv+`DHb j$O|q?/Wo~* jHu;p8ǽaxH@:zӷnHn<:m>OV}WVagЫz{xD1ۋk5-rgԌ$tJhOj(sUO荐1,1}S43V!dhGhheQ ƐTň'ϧnO>'0)^/I=x~xIc09ya7&7ߔWZ^nǚP FCh ^mFI^Ukڸ֞CٜESh>w|K¤1yJ[h&xt-Ts_m?v $9z( ?> \=E==G@? !WGݶ+!#BGDA7#|s}pA,eS>}_=*_=Ec"ԕSrI{? ҴB^I )0 ye()eQ\TJ%RJs%q~u<8Ldn g7Ώg"KO8 5<_zsjM(S~2bnm$3 GRG{;NIQv>ӓ,MAq >D69~@>y䲬ָ e0NV8ȹ9͍>=/zm\PmH4WRQoma*^>,V\kF8إ?w6a{5hEUQrdI`v睦]QD"^97)jYmȑHt*b畚քKX%[s 3NJҚw$/}YfH?ucO@חє͙Ne6mq=GIAq)x:/p6 qS{<ę[6Ldx^'i=g@W0AlͿz=0,iOunT;(3orR;3\2$h^2(YB;ykxTnݟ%N3or} u5C;:*|;W_EmeWO>K;vTFAt85Ҹ-QJD){0v~ϬDάV2`Ɵbk¿hӣV7( [idۋ'B"k)޼I]ϱ-8Ⳙtdװ~ Ϧ7l[.U Ϙ'rbi7C2cfB3liے6Gl9g~  'ixS=- GyjAD^6M}]X5GxR^S;N4ۋ\&.7E3$yf@j`dRYx}<7|_uw30u6~},s:21=A"!.VyDz5|bdeKv5}#鱸Q$w%u~ E@+<7=B8Pmsj_ț?e:zU'Ro++ ynxT!$7RxWw_2nFAa# \;>uCs o3T鈖~pZ0NiHSz ]Ϝ\O r_64@8/b`(p"*$ka̫AG z .'SV+2( =^?'J=F~@i]5@ȰlP\v6 lt~ )TM FDжuIrlIcM׌eE4Kቔt [3qZ8td0?~'SAObȓ p=j.Wzs|V\ ϕ~6G(>S\͑`/i|GR`8_y8F0Ra0VKn3 xy\l<.ƭ{j :-(lltô-2 9QyZٵ1TO`s \1els$2)pmb GY,ڶ*H=4;4$>Hh`2b!ږk)1ڤ|. ;=&Hi+P[ez vLh/MbPɷ4t2qO0ը$o`8AEyc^F.z1!:Y{~J7"D fOmu剔Cv >| jXz@OT6g3jSÈgk+0u`"c |ZJɯ_O/y*w%!gi֘}INZoەd@% oryn]Utw2o$'k^AQ 'zē{O,*--/W "͘[U]82Lh|XDדie*Q~_&pvY+oTd(M $9MweJyi d6^ *y~U ^xbYVȨj  A򤱐z5 S ۢ)%EŸFȽeG`8e_'nu{XOpƄ *d!&;e(1<7<0bY&Ca(H3A[&ሾ+TEԷ/v$ ^%C!58{k. ?jzL9F(l˽F ZkGf?#biDxO96),ˆU\MJaT_e/XpOD?e r vUo>3 O*y&yvW,"qBL/çx_,"/lPq !>(fvݾ~v|/w-k:v2'JñOӑ8jy*P$#1Nin~XryɥxD4)q<'q(I![8}C!DYƨLIܲxi3a*tQTYuHfa!*wN<eGGC2[W ƢA'IeJ"Qw 8TYrx)#LŴ2Ra5(y_ϯ3ѯ9Ԫpt"FaX;e$ ^6ONqh`Jh.x//H]#"<IBE4ҳ+B XoOZ>=Ϥa~x(Fϝ>CGK2@MEeϹ#uѡ ?oMZ,R_fL An9t޶Otp~ hē?)\:fҥ: [>eh4> Mؔ=$zFjLtYfU?q;!XiZJޖ *yfk@+n"dTE7zWwTMT>ծ`s.m7z\炥hd~ΜsyFhk/|1f/Sr̃?on]-$pcp&x9 Xk'O'j5f'ָg"(y ZPj`9r:$;{Rζ{v81[dyiM}8Ŧ@>AQts"Է?2+3Q)z島eЉJm;Zdzc쀫&6_3<.3>3cНJ};,]bt0I.{Z$}++jӜo?f;[N3k$9tOi +I0 Z\(O\7ͪoU֚ͩ+)OB_3F[m q0MG1qg=fVs,H /'Als$Ɋt|`üxP't:C`Μ;F(|XsL WN)Sv:Qi` 9|B҆?!a_{{q{kez!JcmwLJLöx뾨^S)=h,uga}'Q.aq ?,ꀫ"Q].kSṗyAT*"3 _Q6ea!H9ZszA0Mk%ɍ9=iZzտ1ÃMKbl#J>K}R <%%mC)gdHN[{4Kxճ{2ӻ7*L`&.O^!g*> EwqL͉\xgܼD 'DXժ%OL$7,P Um/>oo+Z_/xs oJY% yކ?xA :Bc1Q"AD͇B$68}n??1o ~0o[A?7?|}jKa?Ғ-::7kZ~#|8 y~Mfě؅$>y/>mc?0G>jw!+~_z{>b8pޗ:}lK;CH??cIm/ˎ\l~ђ;l;NS,6Xn-$yA-=qL5QfNIcqG]Uנ6Uq[O:f>ٴjcF\P~)2)YJɳg)%ϒg)%ύьEjގ:(myİJ ]Q8VS97$(;S\njnxI;ၢzAf)+Sif͗7pY nS=yWOo{O}xa;O9^ܐ?$Eq߹y"̓ɾ fΏ)۹Rq4˼ڹfp8R6Ff6Tm{C<[lyh/fvEۉgax2x<=ŧ4'̬~CyETܗgBf8,v8`िɮ&ZFňr>Os(i6VPo)'kȻMExz%P7@N'S$" Vk83UdExf"-ɣm̆Jͮ_4N;4sޡ"<[~5yn1t…f{G9ճLRU2'ss ɬ4zxyw''x5('8KNVQ&x+,Ѣs/smnm?s weyk. 7UR6+ gwc3Ij# 2_۠\ X:u8|ç*B6lI^_)Ԗ7-g/O3c3SYs百ڊ\f6l)sᙸ#]og!x22*s百 ìx.OaK$`Qi4(?*:+ˠgU3&uL 4a <7|uKJ_g1H= gf||`yn`D/hiʙxƧ߲0?#axj5㽂ЮDz5-ȧ:U~ڎ&l([I=VR ܨDي *97*gKҙ픻hU2~kqRyns-yeXYO\WX=Vx!@W/{Zٖw-^1z\X>1eSm]_-ή9 ֆsx&D7T̈ DHLDDfcSBBRpDDTԣYMRZ)5 NㄤUWd DK3~RU?>fvՌv] D5|Zk>U2%|"Oc*J)ia5ZK)tADe1ujf]ʹ6)M̥x:^?8sRbvTOںx1niM1e1tꇮ֒Rrs9fVhjz}~93SJ`*Zmj, "\s2s1s>$]S,<&DK4M3G?,֘h9xce\r?&iZu)s2# 3RJ Tr^VjaLQ]^^^v=^)uϭ>Ǟ"TLJnn^<xxϞ>{sBD~ysGȈyV2/TJ !,BUU%&0PU@ b&Ub\}k9D&B"3er*MZڹp8:k!,>Yy9z8PKafRVuUUE5#Zj΋ 3 КA9'"sN>8v HMA ɀ[+ΑhCdmy155v\%jOff9罈DZkg>>XUb)uW__}wkK~`GYdL`u/樈s{˙*CRejʎ漯%j!&S@^2HQDT_ Wf&sY f,{"މ9VD"j1կ7~W_DTɟ'tUU2QU2@MQT$DT͇@/"Dx̜cDj1GB1R٠{Zk"9FBԱcEU !FJ.Uj;;쭵3^\ 9`d0#FfBDk)8N;__߳mwfeJ}?g7gΣJ򗢡#3zo~?/\)hcC]odmD<$s=7BV=fq__3DZV>D LV3%0T4k-"62jnOe2- ˸,b٪" *6Gng4d.s;H1 j}|ٳ+p'?wﵩ 3 CSmFj񗞌ejsQnUJ .Л:Gy}M+W*<~y]nk*[]\}ӆ18/]|ՒbhKYdJtQHР톋^3i*}jeO ȂiݭLE82{ntT+9eM/Yϧ?l(|lǏǽO"=M .1S7 dYҜz-UDkS $7 Ly{~L_PARb\?Ç4SUgavT5i3g~:8}40Go=y.az*,]<9Va=Dt8(Dj780~p2k +\]-xcVpY@pqOxwDxdXtScr~9Kz'YaJRp t== ]]mS846!`]>nn8w6ó>><=߉ԛO|zC?ôFmg_ʗw<=wFΛԚcpw`hwz8o0wTDa94@|+R1*_W{ֵ]G?Ho=MK 8zC]tJ裣. <:Vu>hMǻ6λPk}z+ R7Knus|8b2-.xh?Iq3i"0?n6_·epzGu\V[e Y^|\'m鋗?C>ۏ!w!nCriy~wXxz=ƇϾ|28=(2:ps:MT؅XTn޼e>5hx{:݌rR4r'/-# *Xڥ4^.❗_ܕiy*&u@!Oq^輏{F{٫U$V'l; Ϲ?ؙ7SV*Y"ܴa:3]Jm80n,xwnm9Ou8,MDSƻS=f(0N`~aGS=FPQve.|\Nr<餭yr,gЙS{͙oS==힚>Om.{d:-~|qU۹ugj, O'ǢYۢDZ(Ә[6)Ps[.2ԩa%Y% b,2 0֥JsV4NIQ3BqʇlYƻͲ2801׹-HZjb"N \ER"]__}/O"}":aO[NTM $`Va֥_Swva-3YZ.uDբe<i8 }#j$ \O3qTЯK.㲄[= JԤ x%/S SˍrV/Ux ZJ; +.F•2D$![us(5{_΋*2,uؤvC8x׻ 5U+a{bUZJn{ڙin ˂V 7seܟuS~08pqswK*uz${,N!BEd Uk/RJ~\ POl@M96:z΅Nk1IDpEI~0x5L[z|S-A6Mu8KU~]xqzѓeEqnz=2.iKȌo֯kBթeRѯ(:*pX/rZJC -xE6,qn֣ 5JVä WO)w}jC_q^?If6yEU6C |pZc7tcu_ŞS.ī5Oecxw,B>DEh^s>88tr.[E,>[L:(5{w?/I3«|f6?/)qICKĈA֭{0y6n J@v+!.duV":6Ko,>ZLjɓ+LiKUhC>! O$]2(Kʲt,Ǵ}tE&R쯮(6`,j5yм.RA溈Vb 5npW0yv[7 Pʉ??\ ۂ V2Ax0wc #i[ ۋՓb.v֢۫Jq>ZQߚf(B玧E]\q>nzWc?nVntD%Tߍǣu\ֱ޽#}t25AK!`QeYR~\0J=֬7vXfsKJ} A7Dn/Cŕ ֏j~}v>_/CPwh֫UK4'fBrH,EO R ']@ݔ~^+0x󊃍1hU\y5D\V?܄RveӘvu6mK )}!xJU0z}x̫>/c^>z2OaગSs/U7g;,]>y~gCwNu _#!C{o6abKqw )x/^3V)2Pwi]qZ^;j79T\4q\JMq3>l_qp"RNS׭jQ0vUBKpwFvm8IեR_Dg#ry. Dduw/} 'oGᄐVn>|{w`|s"SZ&ڬ۫Uڮ(y}e1A;:)֦y>7]R jlп<og~]RN7f TNł\זy&7}iy,Knд)@?~7~ >;{5۝SPGZcQ8Ӻ{x7[7“ 1}V`eޤxѹQo&n_&Rr딠1K4:Gj!h9·= R/fñ3 sbhZAa^%3.RJmCSYqNkEmڹ4M V7t,KCp9kpC`ZSu\]҉n^Q,oY'e^ڰ1r8+Rג88>]n`:q^9z~y.4Jv+YEj(͛k!s_-d4mye^~p#>mmb*":w}+ _s?iK -|7/o #Bi+ E~I[7NxiA+vw7 Ap᪷Ӽ߭}MX\ (vP\&K"OsU-s%/)agIȴTp)~|"d)xy)TVA"D&5jHqEeߪ.Kryѻi5P[Ay1F|Ws>ֶlCx86wG69B?%;rutz܅t)$ly2eA-fU `jRQ"·}|W&b By} .e^4_/,EX@ 2nKJH $S IDAT&qr3"Fʥ*#V1-{~/mL~lḨM_I͖y! {}yCHsvnۥ/{/VPFFSfOu::Ҝ-UdXo)!>2-o.VhY Ze ZAE4iͤl~G,cQh"|iY3jCgكw,ma`ad]Jᾯ:GT1#\QE}028b'b59\_mٯ(K{uX S\u[\AMlʨD. }׮.b~u:Xf\9̙ڔKMŁ:WĪ:lVi *!GL ؅k+􃇮5P9Fn1 hrwىGB&G T ` PJw .D|ث4D]_}uTʐb:m [Խ Vn#VRT\Uׯ_E('O J L\?եx]'Xq<0:B6N n@p}~+bمZ.vWp10Jb^S670࢈4-9ê)Ft[rVɡJιO4DDԱ*5BSȎV 4btNRLbށ"[ cC\'sfFðN10dz?z8(0b1UJj,%jv ZZHBѺ 2V8P B)xiԼ+8`#jIYqɠ lm,"U2S/هA \B]_}=?{8޿)UׅO]yØ3ffZCfZ;ڔ.Ld+Ks"ח_}ڻwmy*%3B4q@$:+z0[E.uԻ`&4>51AujTXiR+zQ@-3 8Hj6sjh8d՜k"bBZA(]^[B(ą}isQY "QUgpZMJ"1ZkJ`dqCWH,%jX$9DUI=G XD 10w 7HGm!γALɡU?mw>}z֏>כmMh@ U Z8IDV$ў>t֩R ̦6!je^o3 dM- *9SXa"zj89Ri%ԻA||) ȦKT} iCxGq:̑])[}|H-bF̢1כۗ/nn[~t'=%Ua;S`<7u)mpL]7/9DfU3dg6bZD#w"UsMp.a T"MqX$zdr4&T&SCe^@#`e{21X*Պ4XJ-=xg9g5SfH@TU# QBTР:B-%k fy.I{c4PUJֵͥd1E#[J VPZ[ Pm9)9yq3A"S3@Nsۺ VQ,'G_g!YԁX꼉BqlfВo !ib͂"b&"peL?t|Տ00 &C)lժg@fRD۲";o_?yBM"PudꝧTkEv1Qk-DrL҅ B{Ykkyk&BdPB`]ul1YV1UPf*֒ qT]Gh& :ɇ!  )8ua.KnE =6BHs]H {^IAa5/ 90sC EiC$Qe914|/ONbr.8 AQ<{ fT-W#Zfڮ~UiuV"zآ.o]֑#3R빒9ȯJi̎iYG')g$4`!FbN)!&HU{yZݛw(rMTCd&P3aK>)+Msk|HqUU"65 @Ťy,ad˒'S*(\9 2ܒ*N` DZ6 >:ڪ9ժML0b*)b͸L5IUUKEEbKăas@.E R-&c8Rn[Ecd"Jm[vϾrSǪ؝Zm6<9%UKE&Q2(i)v⬭HV8VCzlP!pnTsKi.DU +lZRA`4͑rj\JS] ѷȑ/ {XåbHvnRfͶduV9jБDYtramU$ ۝fNEY3b5H\sػ.N:-L;(w )ҎSZ8ho;x.ퟳ20 mkޝ.UNMlFJA]0ܝj)M?Y[jV(b$2Lp} uvݗx̛ g~zDŽ$ֺ̙fPuEè@1}9!K,Cn+R*FDA-aɦH1 ¸M;9'X9e9Бu)nX1Î1EìGf1q;-ͿћSYR_<+UzcNVS&1~ǿo>~h anOM\ϱ}6TM}7n>9k*PS\)0b[CrZ~=~;OeYRD,FD\Ϗ!Nջ;E?:$Dq=튀fǾ 扖\ O9ș])W2+KNEXP,uGRCJ yK2ڠ \N}7ݶx_$9%dZ'`"|F")y+$MjΜhߺʼnf8" ^ڞpXK]01DI}jFT^vV9O.aLZ(G)Ipty϶ܺʊN1?\ۇx~ٟ^n}I@Cx!:tnOW H) hzJY{ȩXضzk[;z[ a`Ӓ(Nr6zgx]+spnf&Lc6P؞O>řXtYYg7:){kcQۻ53SE bojh@ԏFMg^1'ޏjJzE/e3@x=?~wo TnGT8`]1 H9Xf] ljT0krbb6j-"NỸ0iqHH8_1)&h:e{`缮"BW{9ю(cOV/ ~o=!ʘ})Xp!K-HBȘ:}>SYm=r*3%KuI~Ǜ SqW sw]!@k[ $zyyq1 &>=ח#n|9܌|48p Jr@ǡ9a&XķJթj K)e5$wOW37@gE $f~:uIkP0zFfL>€Y&*IǘJk*@#8r^xT!L։ YLI OJ0|Y=m#z7+Fx\_|s2'_´<圗j5"aA™TM#3I(.BDBAԄ)ɢW|xy|: 6O S_ B:^[MW{6@\SHv. }LD:t>a P)Y]t?9ɺo]٧{0ETzY)j)5*O//Ooy秒o/3K^L2u):[}0c y;ltp br#>2Co=[M}clVy@HHbT$zt#5cNiLNEnWHCoE7+7?[:Rb1ajew?9&qz]^鳕U}w<1R03!`u@0].{?,a&f-V ;L9✓qN}gVP|?/E a`la bDNC 1<>交-R]h<yUU< Ic0&ȜpDݳ/OW_BURmjʆVj"\AX ה!/F=LIDi,nqOxl$5GnpDj$y6HJ0[Ǻ1%-gz~KWOz<{ c½o3*Q/8WďlSa*&ǔT{ O M% b%%7JWGgP&0y`:?sM;;Քw=cNDZpB07 Ӳj sIaZz1\r.x9qKP gp,joU2mN@"v9Hȑ'04ۏ`@rǑ}_cG42X"%-5ÜR}N @!kYEtLE7{dF4d#`pň5u:FTTpG:Gܭ4[HRq ctRLތϿzvxz9p P̀co>+Z s)p@>ztw7X̽ݚ#q$:wXӕ ?q!.9%\JB桀nZ쀀'}D?)9 ZpְOsUR噐a`z~y!R0G cC'#C"hhjHtrv! ׃1n7my4/'N LI k: юC3!3"y!&!WBr yeI{ f\^)M8xE$=LH+z_巷O>t83Qմv%@ݝ~?7_uC{mS똮~T kbqӄ*!܎P j x]- `Ʊ܀#Bݣ8HLq)"hn9RkpBakm4"RNzǔ51{wYnf=1FL$3:xkt 5gN0xEE y*N+eDRkIy}{>e N|8_A.ՕWĉ!Tjzb[SxŐ!38"䜘{ů0SWXs^:NDs"vŶ]Hǜ SfB8)Bra%j6>f7kBn뚀cO>]oG9o9A=_㷿11LF>=3,bB>U"4F`yZݭ~ө $SM7uk_^F;dY1V}+sɔ?$O͉Q۱Hʩ22M`y97}: yk3r,TΉzC 9IzEVR#0D ̩(ITK.Irk3H ̬R ?k-0\.!!1FD`J„)9O޳1R%"Z)Q9RA6t 4pb.40©ājMRĿ_쯟?|/ܽ)m9z~oGU?Sm;P;fB(i?TU1D)P7)uEӈi3Ht%11aB|N,%鳎^cZj0}2VO0~vGxr­ϮEgpk+f!eɽڄguKke/)ԱͣlxߎsYOfOeɜ aYAl;y~{N,ڻ$Op'1ODv';`2,G] )Tv^˂hb# yAuCmz^^yj1m̩co+<>] \D)v%k9ZRWx#sZ"XEhQsv0w=-Ąq48O_󲤔p(N38!;JLc L^jt}؉.SmDrEο-r6>ȗ2&nC9>~$_xo<|u]>sWR q9hݪvhᾮxYۜTБWh3TSJݺ_[.}$N0ѯt9jm=sByjPs|.M̲$cҽ'M? #p]y%K]H8 t#t4L %Lg4zB}mIB;mL_N7Gm+ɏ8TS {3ߙ1g{pt(<R 1tyn<>_n/ހlZ:|}}y dLh6m1Eѧp8.>37'g`(8uW(|/f(ac`xu# SWRZmף=eJ\O?~i;Z PT 0w=>]Avۆ">正0;5)lfj6hj{EPaU DÔCDoRcKݪ%Vǭq]+(KsR$2M?iAOwF 5"fH SUv Hhc%RiL,'ʔdnfLX.D߬,Jx%rD>17&g?}]N{Sfm\ Y YHbf)"R|y_dp)ӡ‘.HE|ZMJ>;DL99Wޓ8ne$A 8f{|<2ԥ>yx!F=a5Ku$Ǿ*2Q.KԼ39((R N2S9\3h^s 5LZ}ѷba"$:`yiۥlKQ@WJ~MmpE<-s!Lrrc̔0iU9G:Nf,ᖘZ!z<_G?,\t:rY }1KD\LJ۷hqeL: RzWJe!өV15Ĕs{XJnw|!tfArn{ZU2$cE@*Șn6; ֵP iIR6g?ӿН";1('p 5RR"b$* sI,|:UIԒSrI)-՗^$Vr!@/uTZ\6Zo* ޲Kw8x>tC`d34@ΙHU֓ [돷O&02IvTK[y:eL/ @Ĺ#g1v\/[")a,^tp@ Z+؍1b6m}6}܎_%Rᜡ滒O)qm(9Ղit2qٵe(pu`mHJd7HԲTH$99/*Kql[*B=X0Zv1T=)]e9<2'Gk3#s8I8!HFiMKf><Nh]qc#ѥV ?m>/ytM5y8 I tLL) 'I93' ~ͧҲH?Zk[f)ֶclJ%RgxW243p 8ߝKNNu빦$6RS3W1F{d(8nm N]gJ6;(]c?PRR#Cqm.[gyff@#Z?>s}٫Ȱr߼*SECtd`@h9},?^Ә6"mU(SϹ Э5 K@NX0ҧ-@v;2~>%}r3WΗ\z81s_W_/0'\^[9q]@Kk.%0Ra""3Hۏ$ZJ(InvDVYCt(dR-l)nho' P3` H,өZR,QL;;ZLfzai]e[y)_^>ƴ9݀IbG f!Nךr/90#:d!98’hM5h4#$ 3O嘞׼ @Î>j^EՈ%>F*9>ݯ:ˉJx,y]Qˆ6G")1P9)k ºp%f{S0fcD$sDLA< hzxMOĘ6uL9-iA@$,1Usx{[K:m49 LRʐӌ>lh8nuz |m}}ƈr}Bwx:C&awW33Gp|<;v{1s``C &$2pkh/On! }9$?j~[~m{Q\rF؃L9:6r8%,į0| ?!?iIs)8^˘c[m9$R6w,Yjm-*$)iI"9C{oM(ej,EJIМ p"n/?/|4](`dis-']_)$r=.?_SD.:52Q ̈́{ۏ#3"L/߼szX RK9Q<:;iα,Kܭ#2O r5h¹Ǎh'I(r>/ XaoK]z]r>Ē/?xi;,ˆH]͇?u)ϩ%9IYdIעM_x:U46EJ2JYed dDd^! t,~ʩ_<_ZR78ā~ p=%v/sϵ?t!;nā~¨VDXʞ2|]󘑲FoHb\p,,}ڣ7 tҒWpoظpw;6G,Ҳ8)e]T;-,Konu:4c}5jwP,IfgeLP0_\NU/}~ܔP\ڲ\^TؖP=UaغԹ+K#u>{TsN9;E@!yx|Q!zoiXu%פ1m ="W30Fkb??ӿǖ(KBfR蓀 uwmm__vk ns&ϲ%GzOq̼P(PVeL%6f7* Uo7391-tl#Y&-Kp}OIG K"q4Esn#'8]Z;6=iD6%AhmԮG"9ǘfeyM)ܯۻ/}|x[8J.xJsxx:y^NRh>`0}vzw#0pVއHGA3un̔o o]u; <;&) ЉDS؏>ߵyJ)Ao'`ICz1B;*-EO_"m @. iN/ 6y*/ )S [ݾ0qӆFn0IyzDəzv=n"G"wt=RW]2\v49e: LNˌt\uZtd )%TUo@ȓ"$(VfOw}%^?>Vq5uUʙmJ)w9 ,#{i/!\:ⰙZ%&JY#P=DdyN\ӟx\9"8g LZh$#B,(PQedZS% &NyBupo6F.r~8# <_N tr^%Zw_IGYaA<Ӝ sPkk s,OSB I8[ûw#҇%Nn +1Z68D* pΗKqwPbup):T`F`,Exbre޾oN82IFfȜC=426 }60:Zۭj{}~ 0/TNw=)í#>\c0e0ԅqI:jULy^&1vG("+y쨛$zX^i9Bh!Xj  L#;pq5fΥO'8[9QCǨ*9>n14Fnk.)}{~ͻ6m "L{DHJiѮa%M%%@wG)V/?/x,G~q-s9 In:+j3 xnMXxӖ=߿_L>I8I.6<1Kj:'VP:F ,%Jy]^|HLjIDھ 1ym0WZ&(t8LY!10>01^՞S ~>vj)nZJmWL\ASxr~>Y$#@b!OG/x_EᾷD4uy1\ .H8zշ?y&$1 !&U@ !"rn=FS?ݟKtM@ txBbΐz(/i&_ůO 6p :yor:$\iq3 +)~P>Ws4Fcq ZhcAƮfHB]WBYGߍXpG\X +~p>ڮ=vǧG3y眙y?nug]Pǧkɂo߾#0D"^b!Sm4|aӄ`fiQho5N%IUf"""D>cIQ95F]p*k4\L5(ҲDݭf׈PJ#d4\bDHI*כzhmwG#sӼQCL-m8BBXJЮLāĬ9'p>3kZ[OЩ>t)i}C4tZ^1KIkb:yc!%잒q@a9˯bφcrw;2pɜFLA숂(D! q(缚PU5w8Z xm`T.яN)j{1Q IDAT>D4ZM['b"hvD|$ܷR)OwIVFhϥHJI&f$5 Hpp=zEWL!HJɕGo`cױo~7WiU8jØujÎmnvTh,"a̟$<+w26꘧4!<cTBP0> l"Prj[NJZͫ>y,ÿ\U ,rxx>n1?}UKX(e.Ki.~ RӉD4eqb4-Z\$O_byTrNizBZ]D(p3ѵd>K&m(,C\r^ח8FU D,IfI (,Si> $4r"X-"Vdv|?ލO\䝯S g!Eɑ1S@:cdP(nG8զ@˲Pgəskamz`| U^GIt Dd)}oV@n؅ @'!p`RJ%y%ey*p y#b C[kZQhPΩ>[>E59N#|:R &}8 b2wwWSʠP@ U<$B:"܇ ?~xRRGO@bZF~$%71^qLijkBjkVPAPpcp,~ys%N614MjCm(:&FNʂM:O7_ſWښkキk;tt wU-Y@@ݞ9d{9L2w)SN͔ enn2e@L [OU pr<}p YJqzWN%9ՑuH,cO HG4K\.ikQ8KyR:~TBTL\fGf1tJ+! 3i^~Oo& W+>8Ja4 tw(y=(V.pvonjynTx8}jǯ?? Z;8c8:XNA22gӎduih, TdtWo_i6nyFZZ$|-/u)Q%6ևOǣG/I&6$ Ջ-=܇ƾpTÀ1?P'[k1RJrZvwzc;wW6 h{Q1Ȕ XBRZ=54W0<ޯo~ǣOǧ.Q%s~Dbf4P|W˒/ggཏ1KSo:MuD({am=-c k")%H zkHbyӓk}w_giqVw7Oe2ǛRޣ/5܎*@r"FNtyxPR $i'?]"*9FkRzڰ )F)a&00Tӑ{ ok~I04F'u>3}~ޜB4M 0޻z.w 1mi9g?|:|<+Y1xr:-PCXktS.чyx,) xy2YAH3زw!mN" CW=JlU5Kk 撵1z@\y1t\hcJsO3*WO/~Af1q0.izX_L^(,rWqf۶3_v9ej˴^.C6/bNѵ?>Z9aJf}Ҩm Ŷ|1[4n훷OzkP^9K}⩔ڽK  ,={$1FW ~ Wwθ4z}X3jy1ҳ.r ĤUvLl!bo^xq,F"e?W. nmBFD{kNDAQf $)Iyf?Gm6D[e~CY5}is?_m7iӇ[qj恽nY^ͧ"Y:x'y)i'fF ߷Q)%" ˋyy0|a"0uYjG2ͥd&`2C)z*w&a2_߿.-R+K^} "G8 RfwwaLRJD "9gdM'3(i*L-SunTRN%yʀsN99C2c'X/Ym4Lϗf%`ǹGQJ%N:}!СlɎs7ca6WYgOH6gy}o_~ 3 lY׮Q &VLi<=|J:taZN]zWէ#1~ظl?7a1p'm~?HRQ~4z͜'6٧Ym4 -tܮTÛۗuWd0>RJEJZn;8;!H@^(tCկyQ6?Zݎv$-U+ 9鰮CK !Cqs #1ewPb: =ܽԶцHN8qjr9O>j~>jGu91=ÚT}~lf:!12}?XRo: pk7JBˇw\lzl 0ìP;5BМ߾?6,{d5Ü "'sŒai4fZ5 O|@oLJ}ݑk*|ǨPB< L8)ED1N_z0AD9gUC$#)pV5zvћ(T}6$fp g@v/dJ 6v$'9aĄKct$$ C{.׶0sNmݷ}۷BĀ8ͫ"bc/u]ȅx4@2~wT~6,e#&P 'm2Hyr}.$1:#aXGXë#؏oO/|~ЧגĨNhihq}:>1FW$-adDݎ@a*&`tܫ6KSvm SPĜѶ2nvܪ+HȐXȚ,#/^oM| FգE׭҄0 r$CNR)i4M{^kc攒86#M'/?&O-ZN \3k2guq?/tljr*qcif@?ZIzdL aQhB0]LS.f a?]Lpד6PXŚg3`〱Uni= m`^ eJi[fS*]{ITm{圥$$}ߞ̹_aa쳋*qTQ h@? 3x!(S2ݨw'0{&l={-`x~qP2,Aħy6ck} J)yl/ҒdwO)iRvtڏ:υ)GP"I+|`NyXYG|v}$C*D`ZϭΙbqNi^U%Mᠪi@s GMNl-k=LGChRI9ewiJevI `6"fD6w [ /N;B4#q6gnM$4O`j.ugbJ٨㎄!'iMBvռ: 1ƳV-iYZ߶iiP];"Fxw9g~;N39IKq;eE±vC\81 lק-H0_ C9O2h Nݦ64;`r]m,|$Հz<|r_Z2Mzϋ\dHXJi/ HDdfx eJSayԱM_n7kwkYBfS)*b nt0Z2ޝEj潇EX~_;I2C*iov !d$bV8(PZ3{& RyJ} gqy2:wyehZw^93p7t`$x& Bwq-ųUpv1G<ۭxein9A@Pm!<vS#`4pg*~o?~4i1[>V΂f&Y1U3~")6ę~-8iσgfV老,{RH)AW݋Gr^!a8~~ gFVhc$@б ud,q:iג 1.soRk,qi7D e^nU/e~&ԵDL r)hDh1N `*Q@H̷ Hz8Oo޼t9y w u 4rTD ' , R@|w6JKF4@Ԑ`=61Ͼ'?>t#t;ԣHYrM=! 6ͩ ^)G>Ru{~ 6M nic~ZV138LyzF*3s4Q%y]9 F13s$z~lovM}>/M2Q¸Si>r!#1P){Nzu-Y>gOWۮ3=eL4T(S̉j"%=GEuyJ)gx_EJ.j0m9I6p43E߷6nO~߾S9>^lfϖ8"#'D2sIݔ|}p2\oC X,ƒRHSۺ_?OLVYhw &$Bj B'2N0Ed?C<~Av"7% ˤ6h*{_uψ[Jn:(v NDX7߿+v*i$D4$4R[27XdzyNvOwj׷S 43CiTJX-' }v҄0b#huj] ВA ƶM/Ȳ7wvxDyHmz<|N~5#r/kznϗgzK >~|chUFRӔsX2_>}y{󷵟rdBZ'N[%$N"e2L6k([+:j¯^^}6K `ovYzqmZf̛,a7^m{KqԒ%R*mCF@ tfYĢff>P .kAHhDTm=-;yێ -TrI 'zýOO^j| ++"fm?2L@=F÷c 7r]ІCJY#R.3!:g>SV)Pbͺ[;OyJdcs_7øHhmeݗCw^_}|a7ugm b.eo;S Oӄe^Ҕfn`mLuDd ^\msN/י_<< t04K=|j,`"8t?DF6O_hs9ZǗ_'㭊=DƉet,}) }v}nTYՂIGrh ϧL$ű|=?vׄ2g1e!E~zPI{}HBs1 eA0sMpUuwU]s#s+d0֧\,=׬6`v $^y?_}}:t BJ"l0a=׻sIiu>imO7D9!PH)I $ zyyN.k N$ ]c0"@t '}3AOL]pY~&Z>R̝UXh պ{.//^ֽ"nFBM@JOoOH4Mi@ d)멶o޿{jO::Q#8VS:2`>\M#=g#2`DL +W6!fnNZ.KJXkWrǧu@=M6s7-_VZջGLK)z2r#]0:guUy?&uk% ZR`EHu#*LJُ)mbE~1wMQoOC40 j7np +E$z$[Ӯ5o4n㈷& `1Sm!\V@tۇ??<.j1U2rY7%,Zz^RyNC*Z^{ fsyk.X͆i<4G7ԥ|Hi۠Ȁ>9H!)vSĝ#ڄKS(n˽Oχ٫$9w(zHa䴟88X@2qmYBt$ T֕$44l`q/[՗y|7M]B1/Vq48E$(Ҷ( _ ȤHHqg?D"S j:~7lł13O< @ #)WcvR'7YV&ֶ!R7# qܶ-*eQBpR4OAs_+XJmmw5Xk'_?!w?T+s@1"Sw׼PX$D jO5F#>MZ}|h^KϹe ћons~8Cf h wpuBasT5ReC79eE[P[zo<='Q(jĮT>ZY~vD4NӲsl՜U$t ܭi߬˶9­{k@Ch͵ik%攈#߼Euc`rbgUpś-NR>>k/b1zQTK)r;8lus"1uawǽ-v#2P"Jk%53bA 8۵Ow]|igm餠p~0#"y T D`?W!ݮ60a?t CH28Nhxyv854"C 'D A jNܑ_?U="ziX|~`f`R bH?U#03PBމӐ81<{< TZ0NCVP`Д3VmC 9 =?{u7's  |ssΧ)笪9gdq8sW3 aG 7"m0jPHPW߾ [y4rqt~kMu72U,ϔ>O_A5N8xfNhTbTK.A9hאRo>$HQ AkA(E z^J-{kA0^bDAHCwz.u͙F.M"֖/^ ig0u3tbb@ j[Z'Qw`!NøTZ%!" Ⱥ  <nLEnVP.9S@?s/ݏvڹVtNtҡ|(D5R[R00Rջǘ̙x?ns^ur֛^J'>5VZzw.No/v؃aw]1BDuZ7O_?{U(VGP\C@a,5/b!&1=yvU"q+[bYAk%9I*#;Hֻ9bGq۶Z{73G 6 =²l^j-}߼gI[7-RUql~#~)9R\Pl[+Lx[^;MӲ,(Fu@3vW0\_W?z|8]xf%Zr:ڼ^nBYhUǼtB"Ґz^}R3 vYlmn^0Hܵi"Mǹ9V8a4TڧiveiZ"/'ˋRJ>'"m#%8픸Ѵik)uR!bk*u4e0@lO1IVm٧80!19Nc^՛M`09 J@p4öC7uKE{aJDD$"9RD)Mkk% -DH@= %H),B=nfC0z AZv_|7/CqxKCoCVNY Ɇ0#nw/ڗnSEf0귨_d+uz>v$ &UV!tU$f~{~?=ӇG01q[<ώi+ mNe 3pYG!H[gw_r,`JP)u]Wp, C'bbT륔"{&n 2G|x|B0||a[6,w#Cw!^8-\{;wyeϿnSڍa:ZDZBD&wwwkSכ$qsUk0+aH[o];"qpDDʵ+/_j7̓SE㔚.nh-1s>]vǷOqٛG8ĺp#2?ߗZj!H Bc{~(eeԪ!˥u=pZޗS҇5;e {!vۺլ'cٍ.[xCL<r Tkvߖ咷z=U445,TiP!j5DTjA^E 0X֊=r']fbHm(^NPL48 M{x_|aNӹ8z) > :7_7iܴEMFH$BfjZzg1 ZĘiZh9gpUҮwOO{zmJH^rmS`ǔv'ռ-fOo>y)/ÇdESc9 z ഞsGƩ!9Q6N|8:ZR-u5N^sEkG3o?<~놇! nb}KM׳?ˣmm #3Y@c""X;:3)TMX{9Zru33^L=9q#$Aww;WE^O~tNZ)uE7s)m8JmR+8u1ަo ǀ`м6SoHBH{@@^Ume(ڻZPغ7O;OQ{|q~桪n<[wlRI2k4BTȄX!nz" pZFU n$|wx{wW{ؽzGXs,V0RO~E͗m^[98Yhxpúm)9Bg@PUR86A49LL]kq֦04lmw'h9k~=O/+O!yZ{vu2g jk(1D֛u] "s+Z/aB!td˲qi|z΃!uAy<qm\K AdX>lb t5pZ4oem,6j9KG]U5#¿s,+3c MVk9 -0iwo?%K3۲ [o.?o~+-N[YK6σ"mHn %r03y]Wm,SmHA㡔\{ED*hE-v9~ώ=! *//[sa$z\r3cO8Ĩ{Yq杻#8Ws$ w9()}syI~vxΑwO[C BU$[y$7,]5@$!N^Z%dKеg°m: ;U#\;3"\3^[IipĘn#P~䲬` VgǨnOOd77(3W/bbFarY sG7׮;F ?tF[uڿ>1XkZoA42/:Ҳe`Dڶ8R.G~Y50J@iѥe?&ZM?M)Zc {b.l",PJs%- jmֽH0ܨ@wsnS~Λ9:bnŽḟ.E!a5B  cj0J@i4qΧ 8𦷙4 ^_Oq8y). Rc5p l!Hw]V;3-XD$i os1Pan@Cq9tiZkefF8"!UZO~߲E@75DR+@e-ukރltgsGe7MndzJ˭,viֻwU~ $$UBkU$XSwpz9RyU3!iAz<|:ר`"ڄݷ0C^czc b%A#IN!Լq)8߯1H>I$VzoP8On"(g#lOtօVYXk,F К0~rFYZƪu秝丗5bU)9s"RnQOTt#Ĝm12 2)i4bx% 9Bý)'>kQCkE1 o[S`@Uժ1S]mHa6C0,H-u'P*pIZzv4h5)p-D`\cL/پh.9Y>MaH㳽nn9bvt:1&)%kJoʲ^/ZxA,u~SdmzZk+. Zp?{vLʉCݽVxvz91X[j7E ]i(-o5PGLj4g쓹/wSeFS"bS\C-IQ"B/{w#QH3uWnB-.$>a^[v7@i Fk@#ǢQ| IDATd6af`Z աDFڴĠA"! ܎]SW=I4#8,8Ma3~ʡ1 /zٶY::/ԵI8~|]WxUqaMǦJe5Ⱥ>wk}zw(`#N_)t "× bGZy%Gk"Dw#?pɽaTj[JM[62Gϻt<ߟջo~<-Zb [i˿kUM10m )vj{H?{3§qOxz5_iBrTU$z:k^!RRIڦ$Odq)NC'wGW 0w ϠKux|wSD5f k^Nv^gGYߝxԴ9A棽[y\˖?xb,,.bSL)hĿ>;V]vcjLg|[D4"Fh0nkéֱ՘O1wlRr}w[QcMTrqpuGA31ܢ JLlDL`<0ĦM$xLC靚o>;zPvPծESDz](8L%a8aj&QHݮ;jkwòmT&XZSio C߆6]Ka`"ջrLڱ$6hB!>Oqn]"#6q@[H7 ڦi܍;nge>dfo}?|68JDQ,]XO_c5{vw+GN|]h~yhXug~~V{{8qԡݽ|6Ș2Թ]z}?Ff%Y3b)mkݿ:ڛP/z^5ppw0'$D~K,;sOTɖWt40zo޼F ]6^/}_/fሕ!ioh$92 Md'^[M1JL4ȭ RZS(1TkRBλj*7/$gÃ=Ck{lN !2Vj6q$ךjU%M͝bʪM̒жmF;!1ySDP)0T= v'۝~???>T?է|]~gxL__/m7Aї?zj8<{15[;<;\!4-nkMji14OLLȮH0P 1b`@eVR8斅)94\Ծ_ŞywWuбfWdGo_凵Ry+mEfaH8ȒS}d q K Pj3'bP2B:Zzik2+9C- %6t1jbdڝ%(wFo ]49P"AjN!3 Qj tۯ?ޥC @9nwG<}|r޾h6x Rgw}% =%H@G~j :<$Н*xVTL]@L/%2FJG Gr]ikӶ}8k`"VuX֭U(@]?􏿾5,cCfjkL d88A#N2 ݛ#a q&gF9`<}`Yrx*C=;|~|sW/" }@鵣pEütbQ"dbus5@D$2an ݰ4%B5dF fFiDI!tU4`7@65wk4y>-m]Cq篎~ݍ>5zǵ"?9@)yvN'=x/.OӇ]?MXcy~1nO]gCnu$[A.hf۽(q,Tr u~>3;՚yϥtewWCw/Ks}PVFm((:on @ME#ZPZ6Q PDpSlE:"aZ5RВ3̹ tP%DKPEOVdrɻp,aoWmCоYEjwLWYm)+s)[p[^ji~P//{~߮~;暲lnY0cքP ֵ #ZfEtY@RAUVr+gUQc aA?{4)ox-}G{o?} E\?%8<ć㪯h[!Z)M6y 9 㼥☻Jz7VTptU-);PRډJEaȬ ιZ+ik *VD8 drmKfrА 㲞sMl[r~6|qò^Ͽ˿~+ ۷~{ey]|h/0hZ*Dz)im<++c 0xB:@@Qt%2m}:]dK mÉTG:=w<~{{8=0xzp/!w~u#U!珿v M\^˿zÁIz=o?}7?+&a9"P7 2 ZMx > ,BӊDR(#0֪TjA{5tR [\+"QZn3l9ZR-'T J_v!ցy+~&|c ϰ Bɩ1"]yYRן]ZVD!Π7nŰc fRe0@8fv_VԍuD5PAwş3ޑp]㻨RޜӽUȾp%)Yl<:M:rG]ar:E}*ӧ d~x4are_&wqd1Y)rn-0_\]yW^Zab)boR#LR3kzYᡋBwMwLtc|cx4~/ܧ9uNQ{5^)=f\x8mSNd!ewAB-lM.ՆaM9XVUջ Zk%-U[c$USyZD臀]>qnx]vaħAP~}neIyk`G>>U췌uCA8[B* bN낦7j:~?2#kJITJ@oPRĪEDU*U-G%ZZk]JZe.:bTATd-;!a_GAܚ17t0fd NJm,u>`s~wu@OR&p;[3` h\n4{aW̍t/o۫f5!v+r?sѹ5Z}bVyրu"u٩1ۦ.К(yCN<*sm`=W:4}8%)0viYq,nmc_>˓^N_?j-e jF* rs>e(֜jM=U6$l\Pt_p@=mz)ܮ7ƪ*pi} I[;Hc "ʿ]$9˲!xj"=.ڵӃҴbLy6~1#MZiGǾo4g# W;js @GT |Zz~PHhU{Kh w?T ֪徼ܒavcɿҲe S:Zfø/K*ĂXTɸ[NDzл ZkmDj`YWQbyI o>H+L&Kh{C1_1t _ׯ˯q>]# T?N[|S_.9߮jѿ{УH.}Ӊi׃yIۭ'OUiHJSz7hڼsxYW&Zo9Θn.ִQ@%C{Kj JoKeוcMMy4 dqԚ7?hd˾[ñvYvA]/u-#^ϗm z1F~]_82C#BkDSj ǙyGYfQyשSΙ5XkU5A"< Pri*^|<״H- Coik0o9wmY. vBUݶnhg4Vimyk}xT*ARsr;{oARwCT61,t/kH%SUY.a}zY3b8vrm95٪%$eR˶Ơ#Ҷ[@o8r4%;Qߑmc0qKkX vD"76riL-1 j]AC!N4By6Vv}m[k)Gk)tyݤV7`*l?9n<5|f\yAsb5_pJjk~nvu9!~ȭ2i޿Qyڱyy^-0mA48# jl@q<Ъj@OUXBacq/}ݿ~})-DZc;{îRr8 sn欉B,`\txZMv3m$weJ3dN|=_K]Tգ=OiWA!@E5\JOsD92 /p^u ֏4?}giDGlJ,z~_ner}6įlU.پ?XVdPo`>}Oן>8CFQ+u{rY/]7L Cz_;x2?~u?|[`"ܷ/ۃO%X6H6f|}>GOџҾuI$w]! Wn=Cl ;M+-okeGDBt”F#ެX벯?o׶Ax7?|+#$I~Vn[R.rmwYӾք\oD0Ss„,KYv4={0*( C-MGzM M޻6"TfF6(6D$BUs 범ROuo).[ZVڕE$睍-yu6ֵI70v.a_3>No֧eU:e#,c?}7G~T-&zIOǧ6)Kxủ85On~|F}AeTjgnK!eZMv--wa>xyW A eck+ߖrħ)`iyY,]mshh|^%|O(B]Rwj|PH J1. -WIj6[2Ck%gW\|DȱQ$q( IGW4뺀vO;IR%;06T7!2DDZS1?ñCy"Hp䋵Z[iyAB k le $u].FVy}28|ey];d+y-{?aY^LJ>m)nBçX}.?E9v(ϙr$C*3_LOtmm!Qc5r==?|K{(NSS^.ǓyI[9?0s#ihk{׼{3˗O-tzkq ʲHMԜ`wKz޼'e8 *ZSĮkɵZ6Һz몴akMyIE5Z\&1Xxpadg:vdItK֠`˷֭KłߊPsSKIk`:,LWj:XߖY"4>Қ xO[) 9ax˺]w_p IDAT4c(aăw.C kNǸض^lC6O[rж A?2n9°5ݒMw$Ox 58iЍiÇ܍vHԢS.P֤cB4` x玱NyK@cx C5u奘"cPG0;-Hb e+tʩ"$ǵ Ve3Yk;TI 11X~ä[ςZ+/OTUs!Kuk "R*]`" *~w&0y 2)ն}Aq s<$|0 ecDK:Rږy-龷\_Km#\ػ'ژ$럎&uY: m-/_xv@yhQ{%Vk=_ 8uӷ1Dol5pp,r̀bjaZ  NbJGzOއΖaT9&l0 j;? z.]1mݫt5ts]jf2R*..%WzYW-#[kv&l}.cTRj/N6\k+28 ;\rBc[05Lf~e$vޘl7%{1S"IM5fe=rK{ID)xrn뾯 b!o *N㔸emYWڗᑌ)iK-$o.nw7<@HsZ&@&" bP8qAE1ƾCo0Ov ַO__]h8?>>_J{p:[Jji,6}YJ-}I,2ٓt0TXqCTA^ @hP;KޑjZ{]Meٻ=1؉/UTa[ڗj$/)7GeQ覡5#c޷/K=MnהR[J붋4.G YZȘ 5eKI_L{[o[y}S|K"ٌUå4$OyM{^iܓhё ,ou߷|އZ_<=*]ey>< cU1I1W&.><<[!aFNm[ki]ûGi>ڹTԈH7z獋DJU\徥}<>y8 wuΗ\٘*MTiC{Ny:s ˖. B-u_(r+ c0K}fR8.CpS!D>Y/=okX2Xk&BxUnT@co1gc8Č(88qܧ=k.l1SN{21l՞eFsixOO.B>γ+}O8s}_R]t¡1Yk;ҍut2eߥBR-2(~IOn\좋#c)-!nZKe oG  UJ^UP¿t0 8G UJ] ;SrjU58gakRV<==Zrnpnr&H*فq֖Z8d5`j.6XDySjs`pԲ Tc J*ч.T;j bۖl x򇁽r1"B5fosj8E`d%RwSoȩkfO׺T7"p:a&#&k+9U絤L"X1k56f"tH˚b߻ּwUm[yղĀakjn jkOHQŢ}8qr[AoaBo-Z!H8e{39q E/^}jۻTJ[6L4t^c<!,R`;|%m 4 ,hAD vd}-7G(z-˲ j+"$"ъ<.A Qp`#R&׻5D1Гjе ]Fz/[f½c`Y;FXo}?oK],Խ` C1쩀 *uoOfC{] 9cm_MoMC`d ț1 ׾ !4}kpx:y_^׊n%_{3HJUjPZScx|y}\ui٨@Vh& C@ke ɚam) Ae_9zjeO@/_Jn}=3xoQlRݗz(ݧi:9]T Pjg dc!SG#\k]Z{j$1s0y}e/ 0/!F NR9[ҚWgU{ɹV6\^o8[v֎Tm-K]_h= Ce)yjmZTCJGv_wŻ}ɻ{<8g)[uHowA8WR`!/_y͌FeՖsVD2,o?y"%r*uPUG 'o^ζcRdǖ ̈RI @jftM뵥^4˥ cGtۺU4477w/V_6mӢa^[K. l4YࣇN]: 9'ƈD;=)e(v?D[K~iPbjg[.PZPo&&'ܲR#֚1> ~m_=*0b OCU ;Z IYq % ekrϷ4`Qm] yzZ4HuEѴo:h}7j-Zgi;v a f.oФzng(Rk%]j'wŞnݺl뺫jMyӶ<}<%!]g$VQ!i3"JP[(} hEC͒"X:DQP:B^4s. q(HD5#8˶8ҞhV/) zт*h;!<@![)څpܶrZmʝr!@7eT40TcQۚ5*,)q\˝8U{tB)E)4)=i7̷eɨvyrYrDM|35 D.Ԁ'g@mLG6=Zm./YQy{ﭷ=oؐ1d^^W,܋0T|tֺ7|;7.8D"fgq.,4 bں(esqͧ#a%n{sbpA3 ] XwZ+;L JReCJ (3 Uџq0jn޲^je=@V*UR$V<4. @ٓQ&QBfnҀUm߰:P)wbߕtYR1Dd1`FymYEʖ6N Tjm9Ty 2MRMnhqfo[Om=8N󩶺W;0E\kQAN3 &_!m\z z0qWBCDTr{'DvjloNH0=w~nxgt@[j"mK }xd9B)8;<2FƱ*hτ@sc[3PUmWjvRDFwV2J!Tb'iaxV' 3mSggU~O6iz߻ X{ڛw8ϵԡfͻ{3Gil,}u-Q(a-&NRo}7L$0s09$@DڴrUkZHnŔ3;A!vN k'8U,%Jc@Tm#֥=Ra8 ϗVS^;L?I;[l?ܪa"&ZwCv]U)228󖖼T̀E[MhC.DjzCf4uF:O]u~kIYET> D^{Xw*yi^Nj؍>8c/cmSֺ}f1-q:{|7"D`LKiuyUjԐc"rjD\QzM ɜodJlaw@ HR4f,KT1J_6V~o:0AK_2(H ք` 9ow]()'kG%Ty`1m)0rv-5 L=J;8ߍb_{ߒcUz*1}[΂Tu:ZV!r:9K4,v;1O߮_όɟNnOPZs2Çnhf0a4b$g~y';:0hu`Oe얌MTIwvlep^K&ə{/RUU\ra#4i =3-JdVfS#xЍ5[.v0fs8!gN_sj!E\y/& 4'"Aog{dJTB)!:0NDov(Fˮ:YJ!g]\yPv+:t1>F#mH0yڶMK0t6rMɟsW]m-jYY V/UU w{ǣ5v}3tK"M KNY oۦLh&Hv|&3q*8}7̇Gq1Bir RA)@@ RҺjw QA.BH)A 0.@;c \ z= e,quy b橔8\\KIE?߾k|7?6YZB >㘟_'QqbBUwzMJ Wpiڴ\$·k  xYђc(/o%4l$=Oথ$GRpXa]| @Jg|a6dmՑu>-/G/k㣉 1U6@wcb+%&OH$t/-/: ud u'|:_0wCs"vh+uWc܍J޷UR-4+8OCH \:}uoO%YRJo!8M1ZkSc?bPon.n$L+77RyJ?O!`r7d?i  fMe%vye)c?-eU8ͦb]{VI82qO$lZbPڔdU:1쩼tmz*1XH1@C ^56ٿLuM"O|׬x:8NjnV;|:j+UD.y5e~:7(G;نySqۡ(J\R2iFXSJw~]\ @)$=,Tbʔ K SRH@9Cɀrz=.fDt=+%)BRDN4 \W;۵%z4WR>ɺey]m򊵌1ak9D+E-Psi֚38ỏ/w7?ٙu^_+>}C!Q:K4aɋ_^|dvb?cZ{*%pXtZkeU}~^61C^ ӄS~zMS^O̗㯾 F83yna gL ;N!L>bNwxAbXIވze\rpeY~}a,OR.vМr/<`d2R.DuYSBy2)aV,P7K$0}ʄ2(}]_Ep0bR-UT )eYRDv'&5ӴuZ0kMYx&!j^_|] uӯc\ׅJ[;:xúuM.`!fq~c~Vhk0u>CƸJɺوnFJ4n& Igh^IM VH~zݦ4߿s(#r8jO꽋zv~B&f1_,aDR:݌f\\<3Mt!\4L洛6iukF}_|:NKmX4Qyw^FJa?_G/h7_ ;u>ϣwf5??!SJcJ>})"\B:u]5KPXJF3-2傅΃ж}d]{YurV&KS]U%3͂3DR(RFOjy޹Laq]M BuOS&mv IDAT 񩮪&DQh< YkcqaNh$S.tZBIMdO瘎Ut>אP":C RLhj BS*+n+Wo f[źn=jf.R \UL\67}*6τBôq/J.ERFr^x?|,ԬJ꺽lv_fkMXav}2eowy6޿%@JUN3ՑB. R1D) g%2KRJ{!bV4MIJxϛau=LU2L\m沸vD6 >OȘFoOƜ=PWja p3# U0kbV[IXS8UCox)<)\D4Ja1DŽ$S$6'R'N(\AoM)Iૺɴ!E\4CJ삥Q-/ .-lXIA:LizV'.Q:H8vTnN a3t\'ur#dnCqaѲ2tT$H \JFk@rz4s ޝWs21^VH&@ ?× +);<ϋP$/>oG[#T#R(y7/Χq JVc! H1boq()&Z})2MFkҚs0XF$(ĴB~*BS|?g`9!W(u}$tOQ0ъXr4*.n#g}(g⮯,OB :+DgZba°xI0 nE' e+'Vs}pBO$yH0@c>`&7))`{la@}bVFUy-13/+u,T5@KˡP2/6$@,ͮ#)dd1.K(PRrRzE_qM +QQEBLjt~ZJlxniRxάsɺ@$'M~Gd{Kmu."\܈HsZfE 9f&:Լk3wJ Сֵ#),,+"9Ӵ6sỳ@p2-eRP^GhRq e6wUTX^9mucJ m8oyl9.dkW.{\RzYt8iMG)+n8%Y%cNKU]Uz3-1)ݯ{+T*W"\+~j^GoHl8-4ZofF I u)(6]?rinvkŞ,{ h%, %L*&+&Z JcT~mݘoү@~k势L`Xs2`MM +-hUkW#벾fPe/Zˎ('Uݡ.Lrda5T C˧vh!(0@_]DA)@HjG RAkKxX_~i&]z j|ct݋tO/o~w KY/Hl]tHQPB[^)¦ƭV Hq-Mm_7xܫPtG.hDiKӳww;J=4vݎJs,e<}sǏo]I-<<4Mi+|̩'Nfr:}xPyJobq\BVӚ e|2ˊ#T \pI)ϻ'aZJrvн>}%9/[+U\ 4|bv\5pS^qA))A9{k\|>jgs.2M@?F>8ivRNJ%>sh75TIk}J%0yp$LJoLαЌE KImșpF)EB #  `ɐ"$#PQXj7zZU":3E?hQBpφ&Z3t  (x d&K5ZRL y/WrO%uNLXp&,8fZ a:T4Ffgj^#`V+6X_2T;Si%?ar \u5R7(jV}9,V6/yƧИˉn dFV[S!VН[&1?(_M잻XDp>%֤@5YT?mzؾ]xAb%/3Q&ɮ< By9z.);޼bCn[!*<>>xrN"Pxr JL)˥h-ZpNH)_di`J|DYkʪajBYp39CR\jOka D 9\7_/4uss q 9̟?__~>i<)cP߱fͶD)EAFr=|>rG}ǟU6s~xfD;e1NtH|Dխ??x).Jcu凧ݽ?=5*~O.>W^G$ώZbE Ǔpٚ瓭j:h<~xȐXDO˒4LxKgζZ/ߏm]R5%SIW;4͛JA'߿8b|N7/k{j]yr|wO?yPNΔ-- .bOgnt✧1&55V BjN9xLp5wi O鳼̝ b%#tWWK%<e5JoeMPzϤDVxu؀gEjtΚu΅JH1q>clr/Gm9s)y1΅"(Ʈ"ؤÞqhi auP] ? f3fFVtߝ'SbfJ՚義1᯿៏Is}e+4j{J *ȳ}fr<ywƞQ% -/q^U~WcS9Y@}?^ʹ|{߭ q`$y4S pDxfZ/$$:cB 9%H# Ɵ?:^sJ_cH'>l>*ɭG)`WFXS4V~NQW=ǏYe}B& K`B놐ۼp\T#OsAIN#~ gRnWؔxxZa͘aW*a7 :[TۜT, u;=>õǗ(M8}3CULH+Ґ|녞.p e?XPHQd:H˻2MzG-~#EW2ǝ[=NүaV+hTˆjqV]nJZVb^q69͹] P:zWEH;`@+s. V\Ҭ9|$l@rǔVC̗ML ވdB\/ö)U-a3JimI9L.T@~|4Y]=Ӳ9k.uua4ۛ@hxF[Yt=$Aܶ&&gCy" )@^]1aZ}7 R O/W}4MѸiY8Y!jkхȕ P~@/SF]fRLh`?:RX)3H xL/N6Xk)Rb_oa٪\Z|'%__ l|JJ S*c{E901v⯿ՌQXMMdVTW^p$ 8gZԴ0UUx/7=֭oj"ʢk2K%.Y;4)$ UΉ"uB6€V2g"H-7mUgݾ^aʛhfM!lKT 0K3b RUBj" goT$0$B[k2!+u̴8kW3H^1c !SRMK% \fd1r^:3b.L9x69^sA@~ (8_±"@\81V֒.(XY"lUD~#m%}VL`1qK15J0Ry&JJQ!-B/݆R7&Z_'f`%4Lѝ)٠Eij8&u p #I.۰k_ u#+Fkt4r&J|,ϩ 0eI# QzMi*1(0yB80'd F0m*m)( !9dL{$WRwM+ |PJ! JIwNfQ` (EwY量_{KQæ;1h) m`%֊W_[N yzNxnM'6k7q-^nI%r%E[-$ܓpMLQyJxOMp.miـqd]`19=_'Œ.k:%s\!'Y1:gP7&DrAR!ZrH-uDJ.>C Ml=e@ϝg# R ;&|nu 6SBS΄3A[r̋IBVBG6wd-Gmq miW.Iаz*HBɛN =F/)jo6hRyw2}7'x;n:XZj%&s9)ǘl8s%z k1Y%!,8GFvKxG%`M\޾_4e})j/?і9Q _ƣlT!C&B}7XF/;8_W:yb|8crA΂\sU*U8E["(s,$Z2rO #adyS-"7v@8 ŕRa +k!8@K))~5|;&:-OX:itLuiUx^4 ܵdI양N5KZU]tNsc U9BH-z8f>0.{fsΌ+ ,%+!ucv%m٣'# m'Vbތ/eKϮ<иmWC2+ʅH+ڕ'e(${ײ34]ƬR[M%gZk1|bK 9iz)D! Y'D0"i Bʄfc><. i? _~IlKJ C6nkBA,VxqYtGkniNRy4 #U.)$z*At ^$L IDAT7lc$Wt9Fݰ&HOJU/>\BsqRRZb9!4n뙇ҕX ΡFCN_.u9)0c"@fg(4ض%}U0v[5>u؋+͋>Y |)/cuZW^y!^azU_u*z9/)OY䜢w2;8"ooog`!xdb{CU^QtbY/16LX [.ho{d?X U.,>9.%E'kZr :5zc!Jӫ]^e)4 =]i^V9;t0EdRޕRҴ?M!斄`J`R~qPN1KYuM=4FƪuY1Ň4_tcöYtWwuv }n'"nIV}W3a ε㸖0Ȧ%BWTy=cNQ.$^"3ySBW%XA %e)1x);~^h `Rv1z")3cw0&dbh]}ԯRuwI8o}m.*0f4?fKۏoӗ7OzpZqHN2dsu|8!:U[7,[֬5\0_PLr8O\ߥ䷛Pƺo}UZitq~v2C{-.87|jy*|?٢eY?-Oφ=.E]<7;Mìc[ `rxw)]g .w|q3:]:Wwy t:*68K: 1Ϸ.Yf0`v7׏ x`i SYn@kN2ts[bWZ>NV$TUi !i9g0H;bj+`A C7g?J3G%{8 Mz4XEX2T 4s\w'ǷjτV-{go!NFsYW޺ڇ-Edo~*;nbW.KRDt%/p&/=y)n;|u6b@y iU>OS y3loا ͆^$Յ@JSYUvU uBh@T">w\M8;$AmK! ϳiS"K}SDΑ;o nex<5ѹ5t ,M̈ZQe^fT33G0A]QDT=(OrsFT缁{zoMTKUMdi -tɼV,D+*f;V @45v;fmK 1֥ݷ񘧩\>4ZN]CR[(7SS]6a杆e gTj+x1ܩMU!4eS9G爠Y2#p71RIp;fhd>lieZ{woŝ(CŃGNKwvKɥUufCnjiq!ThFR؛kpe! 0/y?t rn[\C~3BJ!SPw Z:w1qJ2bd睡hmVC"ZM~}?32.Ɣ D=7%Bbp)Vplְ ju&T:w).]^^^FSv JiR=x" 5̳Zn\N6b%Wj*j^rʹ "+%/\kD:fBTET #&R✇S9s\KF2C3mNr--׳͈ =YߥO_@fv@/>ηUF&=z2K]1sSk Uuff^FZkIrXԴT1 $G$cN1㸌5mJk-ZA^rJε ˒SQMH9fVk%w,K.{?3&)S*L_rЏ &Zi(GB20fWnDjy294e !GAD^ m"yCfTUEtĪέWdL9Llf>1eBk/R7t]/!]:91;K->%nskG2e8P Am>/D |o2H!"`ͭf4UUDVI\ىHyҚ9bn-TEՊ˲0xly2.+kSB>/ YDZkkEk)ᤦ̬❫ a:o緿OJ^b} A@~굯|yo~f%tp jCjιj{_JYg]QU#bD\"RVo+jk pDBI!"13;f1Ndh\JYq7Qªn *R"t.͠|+"jҼsLkusƞC3v]"bkH QJSR "8}ߔRknm/Q+.N'"Z5hya<@J|i<(. I@i.hXg?|=LӇcOO~D1Y@cdςW/сATI1q) "r_UTB[9f~TDTkslfLb fbBpއVz^eq\fJLfD:=2B̀X+92j7U(.TLZ1Z NMaCx]]߱ct,uYGRZ~` πD>b^{16f.'$U"WK1ZٻUjk"!h~9s Cˈ=/?}k?;wȺgdv$m}s_w;u?v[xxq;lsaAՙX{]X5 !YV#BYTy)b)e٬nʊ%@Fb*0ju# ~]lDgԂKK̨}8Spe29 UQ%@~!@Db ZVͽU뻎)缺kuf{JQѪ֟1Z[k+"iZ]e 1fgulS1u] Zk!ŔTNVHSO.xJEvö_?~B$F(}N><]`p>Op]q(d.C5i-1#!;"dcR_U_WZ#cETȱ2ʎEsjSDf޹e'G]xd<"VY\ j{^x;ŽWR3<&Ctl*!xeJLM$$"5%]k-lTj y !#;sCShر9`̳s~+%)(sa"91 1@oVbMªGԵ8ǍCcö΋p>!,xu'۹ͣϽ/o/ tXfϷ[m2_tllK?>"erAyk:r<PUxS5 km2gL^IU ȱd;B{'SɒaL8(vӴ\+rw{'\_pu fӌ|vZ,cWj02CS$$J\sV͚ʳ܊ ;%CFS Z!98vEL޹. ҬSR[t^ƘsvZ!]1>>!"N'")N)tqw ǼMKIҋWk|X2TEpb n CHq8w.w2Ӟ=!>du9.b՚M*8"FGЙt<9"St p8pRK154@#yёc.V,Hp8A&݀n;shVN8,Z~۝扐vik̮ꐬ6nxWկW>W#D0PD@T3%h{zO'E'/|us墿{!zhBmmuG.Hx9= vpg$M8#..*itn9y\Klȭ[ax}dw»o?9>98t/yYXuw ^NWO ?~V4?|7{㟿ͷT,X6C/ ÈjͶKݲsf? [v!0SĬLt"Te/|bRpB!xOWR~ͥyf z/KʃO?!_I_|qu sإZz%V*&NL,ٻO.0./cԘ">kr!ƀ0pX{l;@ wMt[˲0Z8&?s %/*mwH&ؤ30)@6q`DNə` 1e6Yϗ1C]l9H~e1jẐ u]RhuU3pm`mǧb[i˸<}|;O{5Mfr:u)9?2lVJmvWWR'>)xBKfd]R qJ!4)ZbsO46gC;_'$oo{aL}9K YJL5WC]LIJT$ƈ!Rf38֝xvDJXD|Hsp!ĮDt)9UԒ@ZuB11(ZB LdwM  1gW,?+󵚱qH+~X?g 1Tlm\ I<-c-3lfКl609| ZK]J)029B@"ʹ!sdbjd;Ufd&e"CBS#F&$Bh,ƌT8G&2=D$D@Mo11AD@Ռl=5ׁVggFU@}U/?_&u3B[2@"0P5fg%o׹5gd p@ㅟ {4f,^Vl L" =S Dh?L Z[k>= ޾/(Ln`gA[߆BfuSe{Om ,̞;3Zz& |^Æk+V`J~{5XEk?+#Zreo~GYR<۳ے5! 2|x>=i EWyݺ`@kIDAT~C?Pc35f?a]tC{Ǘ}ttGV GvmU<~??>y?ߩƏ.I5hȷL}@>?Qe/8~(?β<}fg2ii| c|>?GᓆIENDB`camping-3.2.6/extras/images/uniform.png000066400000000000000000001205051465547410100201120ustar00rootroot00000000000000PNG  IHDRgAMA7tEXtSoftwareAdobe ImageReadyqe<PLTE{eyat[öŹҕm밣…xZ̯'֣pٹʱltG󩛽嵵 v]y픋p{khz}f4Ǽԧi}zbّAߢv`Ѣ-瞏xb{f+bGw_{hww`ड़hQmSǿŚ]{dbJrYĺצaʾhfNi{czc:$u~wcxꤡswKIDATx읍[V3!&1 ) WZ$GTg]:wӯ׭an߽ / өw^T@^'s=| x4?'O x4?~O!z'oh⃸, 844O g^$ˡuYYYH}>t{^}٬z .,BYdwՃ Xm鰧TdCU "-I YU F1HkmȊI˺!)H1jA 2j0U&$ME|m3T7Q Y!6  !-%$]LKoG^5i[0 Dr/2\M+A_ ֳE>QT ZWyz,Ǖލhd7AR];4P,_ިBP2zF]Bݹ=^ތד00^d1)ۻw#v(-e;z>d! uCQn"'n*ME֒+[@Eܫ-s &oEDb s$PIФYӱ_3bPME'_9BpWnPO߃VYJ$)p&qC ?ERcL',#+Zq&y=^3vc櫓jW i(yeT-"ωj>=^{>zMb7+$CxAiWUlm]?^v$1^CN{97#qqVP.0{z+}W } Y`۶- 2whyڟe!I[ xRW(ޔu8L}f/\2J6TSr.`fd C$Fܖ: d&jZ9S\7URrC>K{ YGCv~v-v/&*v `jy{r}eY\y"C"<=T++%6@tt˥n뉚 ղ?+Ȟ7;D-aYOz'ݤIYSTÝCXEdnpL dYಫbDSAɽp5=\r 8 ;.7k`M n' . "h^I~'H_Kqx0]}dO,0 m&ە`Uw9=*0@*LT TBLZZ,ܪŇGΟ JL(PV$Nc Wp89; nV`\r)W{dU @Elz*- ;Q77[?];L9a 6\IlGE"IX&RMv#-O1~pi94 ߨWpΕBg=qSc$ê9f;-"SK'vpD҂?)zqGIRi $'zn4,in$ظwfFr E w`1N*S7Z,e6EΡkD@/ʀh-M{m 뺱G_oNTѱ| Ln$j1EByC歮OϺERoOsE $٤'zT>ͯ[Fq|Lo-Tt+)kpH["`m͍0e9.S{ZP{k #JbKJ<]zDridΙeF*Iڨ~X*Ʉ'D;B,Mcn".X+Ժ*IMOܞ {ʮȧ"&!cj|4_[NZj(Bn8l"T=wwwd$g! )6XN d$ J_F z.CM6`egJ V>&T,G8|`xF"pnd@꒒J|Db(Lȴ׭yA`agJBHUY24G>RWﮇ_b>&v`@,=-⚯WUDNeaII L y.BSs**! {SX*Rf*Y#IeHz7xE͢ n>!Ne7mBM < H\zVJlth롩J򴄯 #_˺RWJ-A@*CeRD|1nYEfGL߯RufWi,.\C\#и ބ1*45jR8ִ}z92ZiRVI(2"Df?-pw,f!u,BNoM%IQ7:*aΪeDSrfl+^@VQNh?%ZTBH.u`u-)*Y˴g",p1-]8*Z޲:!WAfXSՇjڑ8UjCGZR|5C*~h2hd_ҠΥ^6Y%ʐ@%\'tjǹٜ4Z**GGh\E]MJ nTC.kxP($.bD:濸A[A ^qU#]XE+mWIܵ t`K.obWC+"Q+"ƞ-E^! hEkY*Q _P|Ls!c3^UExW+T>f k/ #gLM4`(YSDR6$IBEՠ{AYI>7@%tNfEPY+5t*2'#b kBX"&\| b"\^<44|Pz_TKhgEopZ"cL,:.soY GMnBrh{; {XuhzS{OL.^5Օx$,SԱNcYR['6dЯB9ø?;:Y-GG5x |P&9n/ 0^gfU.wZ鬓=,4=׃ #{'%W)k|>BwPt䢔M&xi U|3$U 40[v_M*nh )iC5~| A1{a](ƒ{(ZlXECjjUCiNMۯy?tRwPגTOks{T0ZZD M9Nl2v `#5?)ͳW^}q!﹢xIG29P%- c.ed'9BNc[2 ogN#W++Ϟ=[y7.S=맡E4{i"4]['n08__NT`MPbkH,ҔJ ,~J&qGHJ7`.+-$|A$ި6<%GwhqZƃMH`7qz:md_cOEx͟1`'Y>D])\ŅJl) ߈ظ&uL?mߣ{F(l6*i^#xOSJSuHDojV͓8\"._߽'- '2'@)W:'>Ku^'kh|Nж,g> ۓsƠQYB8 ٣#.؏Oo^ܤ| 2џorZ Ty >C h3{ЪC xh'JұRj`޽6)EI$T58f}IRVZ0qnVt(9Sb}>gR[_YWЍ0]vk3wg@G.wSYܷCUUIUPT2)6;/!͜^B:H4}[WIRոM2}&N݈m %CqArwDԀW R/0-6Rf DSs(_侇vI[.;jɾzY7澑NN-zhyR1G3o ^~T}tf<8 -iOZOKPi$. Q{ -*h\Ǎ!7o54 ,Rws]i5ũ+\/Aٮ@lǬx`ht]֠aJ`G@X3bTidLp ůh嵓r _>@Ket!V(4$U%J0͎V74E.n`ko0WB~"f*~]FF'o_j@նȸePEV,NS7j{.gah*2h,~o| {m=ƂuȢg #Fv8S=8# pY,܏ŧ;<x>7*cEI)6V])%@}IBECnR4<N:RFԾ`yؑO!z, x#,u|n_~o~g߿yϞ^ҏ+ H8b!vBqIC|H:? HXtxl!n}nlkAs`J-s/`6V^s?oZLq; sԧ- ȧ;yղ6tXqfM?ul8|I\G5v+XS// ի <^~wtՏxmT&VFT !SLڟ(Be kZ=LBBVq,PYntRvއڵj@V[tO njZuʜ/f[2,%OV+e]֍.i@C ~Մny,ė{i錙vZC}iH1W>V>ŧ:s]Taɿ|KbrLQvir*rJJA4Ht=u n[\쵣e.`*ΐJ&p۽A {^!gy:%#jkyCF vg.^Npg]{(n4>rΊ[WHe'?e_grXrʂaL|!o;#NfJ$8+i߁@t0htlgaXrj?3]?JlFGFIf0|IC&Fl#\GN9bǀ@hC&!AkI  ? 3}N`@e]{I<ÍFf6ER Kw >=&|Wˢv"7НM/`JWsrIC&F:-U'~5Jil4i<0"5kF;' f6ih:tifU|ꕕվ7Z_ &7[ Ux4qH>@hvcS]CeрYem+z$ݷ٠]FXuĐǘ?3z6go6<_?L3?QC}xR;O~hH9Az`W&pVȿmHT$YÖIŠ}|5g LQs[Z|,+d.ShѲ2]¹v=*~~A϶g< AkVY$wɧI~xрG״kF[0U^o7z󇷟SfW]Gx挗O{.Agqf bNH":rdp;AwJ_k%_Pė/޼~ehVUU.#Nӵڇc⒫6Qע!=eu=W+?zO9Ϟ}w5ǖHNOtc#}GU{l#fsog_ <~Qޡ?wRdb3>"dÀڹh իfcCْJuܝqCKJiZ?j z-(!Q8͕T(VWQg T%ԮM{7QM~ܙhXUhv~eQxԞ}#xJw;t;gte():cait 8AO1|o[vkoG섺_RIzuT{"vܻ7,!ď,! DŽِ搾%:[ `.wb#+F,z?o_wx7[$a}RtRT|gNͥ\?UWH::[rIw鼵[FP:,0w«.pNc;F]qB~&rJz8K[NWW>q:=tYNg}ӑW_|GCyEJE%f{L,_`kEB,Z*VUeN|3mt_<{}\͌hWnjXuNg+IW?^C4LfڏX÷/43Ǽ 覼e5-4} wUe]+eBL2 ?T;ħFD䑹)br0MRI)^}K-OeoݻM*w(9- HQ],p^J1'FM۹0(VgqXS^ś/~~|;?dBP8X9"9f5m\l~r>:˦_{ CYOW?zg5|bi h<avsgN! SRcTVJ=,p@L :+t7 \2Hk7HjSĴArYe"Dȫ$YP߭tBRK\Ijjv4%eץdZ'ء!|-ph@cvNE[C:-~(`OKCc7=XAn_]=/B*Z_9>6, 1-#]kml."XqʝT/VW_}gҋ^ l?{5Ӑƒh l/+6.f?"+kcybgE[/]RY%ihhRܿ\o&qhfMRvH\$.݅qLFWW8"Az7MwA_/>{K6$/:$mA|"Ha)􎓒΄]VEfɭ0eV8ܡ?-I~M 9IJ2,ɍAY$ KyO=w/u&t2Bn#q\޾bq0 Υ$)Jyn`. J$"F pjis3?7.ab6<ґC;4~\eLjnYsciϼ=wzq.|'J hT4,И, ^V:@WkzC=iK9#H;$7s,XfI X&Cv;<0 ИҡB2( jYcE]E]T*AhGȵd^EX8#A>M2;>(n[=)y2k knob~wHVޫq90r͵Y Ǐ̲.^`%W|fݪVx0zyГX 8 x _ :oRKfH:WF?lt?web5nρVu!.J^9d};x<0HCf2 "YVKY瀆*۟JʍVIq&GX6˽]^>ixs4!(]01knO06XpcՍSI pƐ~DB;G8~)KI 䑀l֑'[`M\&OBp\:Lǯ;ԏ\OG^ZHwXx `e&vhRëa##㪀uHjr޷kQrA6ǡ\X U] o4Uu2&4"Kjow88yk]:cM.LV4Z>`CY^x5K- W'֐䙚u. Ű\v5OR! bREʺ=, \B ;[S< J0,6+IVQ,и܌]*B2dYt/ z'` ~?R]cN825'b'@1U`+z4IhC?fU!uv6(fXQTngA__lzfw=)&J"$\0v+eI:Wao?tԛ8y( Jgpdzz4x$.7  &S;|,܉\k.nTn2=ml{\.Iu\f{9t71*Pug[K~h:9^jh !̙^#f5|w3 m@;$eIFQaMji(gV}:6"v(A)((u%5pox5C螸i/ Gpi H֜I"f Q_?Kt?)atn;ez<;n K+ǻP'Wn%3]}ڝ (Mar$^ΌYiYdҁ7'5`Kz9śIo4`ww99nSA[9"0AU4&7Fbr $E3aXJ /$fUthz+2k], ޱ'NOiK ^C7[~ȜQ@w5q0U`$es_6|\B~Ҙckw8EbޫIywGI20馻]aaG8~/?ݔ5 ]0asܺӜJ"U4K`FA:r-F%YCFVF]!Ze:VgQ;AY'+>a1+KK@XRIX%G-f>m2n%X9;:|:pݛgS琿œ/ 0 5R\N}f*r Yq 7;01 v;MUBf)n l0d_gns~?'#JD!I/ae^Fr==qK! $E{糦mM@ .Lx2w6G!3kb%8WIէTɞ#|_SX]kshr7rhRv)w![ 88<6٠i[}FdY7``yNаw]p[$ȶ?hj"tvm4NTаζ_3Г63dqm^E#hs|FL JoL=! (M, 7?U[@'*η(!-Xj5})uC>F*i*Zq8< Knϳ!YK ɀsv&JhAr|999sRgN|O?S#s+>{L$N@ҙz=;45x7E܊ēnhHaHh"FI3sRxsPCV>0pS蓷 N[[|$,et&J7/T2_\fv+ׯᣝF GiY@20s6v .!B[} il2lI tgpiR]8R!N|>_9FBUN@JPߦK%6|$뜽1h[=؊h ^\׬GDBz' |7\FnҜK +>#`jtta͆vRCл{ZMuژpI˖xh9Vk1C]gU"/ ɀW~l.n Ej\jRV"DE:zzoHC]&ӥ˓`薚8@02wciVz :葹6Y88$ݒH~>᎝~ 7Cঊp7Pu0tݪG=Y1HwvS^K k+X{.Ἣ D=vwـU!C|}ŅIj/ ]]GriO(+k[*d4- pH.vmt:I'ր' N =,ք(z E(CH0\ /Cysq|;`E*.w-^_Zc 7 # wlNZ iսӽSTzcH|pA|-p}Y5%El/plX.rN1&eԸ0Ok/! ;cc4;9]0[]&ܠUbi"&Z՞t:!sA-bh(l@hl6|,p*BxeKpc(~ToLez8I0r#hhY0` QuVr%kT,ZӱMpT2:TFN6NPOw{̸kmgj!PZ8UIHʮ}sG-p^ԥ,7.# {pg.S2.r4jP)Y*:^S9Wlݵ]bH闔X!4v>?׿^nRL[fﷄQ]ޚÁq;u:gH?^_ ߂ ^<[I#{p7]qYCYPb1Z#C ᅵBvIp4wL(U+޿n0nStzo e*@<7鋩ӗ2/$ghF2$J_?~owG}o\Z6`<(9@$kp1 zj3N,0n8{e'D-fzƺh8QOn$\f8'HD׼n>15A.Fn#GWJSt,3Hq7N߽Sߜ;\%ekV0|A~p*LjVFhyF9Y9^+/;C f#RLZXߋo AӃY~{:`88ES{(`^o#Q#HkWAfcM'e9pͽ[X1 {SNE|K  vrm ^yaX&ҤK\V!NJbC- K? gqe\k0`:2{AG2CyѹFgzJp M[D im˱̰+l:LWllxl`\ dE\Z]D~Ď]7K5wH.$s!P<#GMnxC ` }\Ay؏&M;Ͼ5g_ yW9T2rАNXK~:A!`}g_=\0g0`aflmx;8|6`mNgqcZ:`IjL_2P;P8Eڸ~]GF 6eq`x fA 6'"P}::l^VIةёnUZLw=d:}W*Te`uSHlm:c+\UI%i9 AjS0(s5ΚJdn "lj€u #5Jdg麠e>#A^ҥ' 6Kn|xIMgZ*? 06ݶ9= w2gdILLny@zUnqs|"ȡ'*pOa8C, p5`|#\~^MW[ ~L` de[u 2D6˳CSx@A{h5]*%TGNS!|p1CV57JHnF{~4`ܡ6[l [ǐ H·g4}u|_+a`a;bAKCP-ҍSrjx̍:}=NYGj/iHԍH"i& ]gQ-m5aT ֦^Opϓqh4JU0(~7Y$8@ ~}󪲃f34ehﷲ2 )~ƿ D%8;p)LϠ^3utO {d`ql pacգNMutCZ' c-y '  _JdY =# EqaYǏ Isx Qp!Lo 1LX.g;Ώ9tR@Vn 9G#;mzN{JZލw7MX a Kr#@굡gt;!Z]߸K?&E*9^H3׏`B8$AonyU?7ڻ[V{K]߯38qހs0`8 Y޾Yw+^E(~3Q|x$`/XVnψvjʪo5p px%TH%#4i8HbΡxv)&s%d֝<ދsiW/M.'N‡|H beYB~y>gho ebَ\tj.;њ<}*`TE{cYEx`\| +oGwBz=H/:2Gt x=?em7m2O1:<J3Zww"ԏv7Yѐ_/\yiex^Fx@e#;z7u3I|~3Np-9*uIR(>s  +^ppJ=_v(˄[˒rץκͮsc^.9~WEx */IrWͦ\v*g-CʉUO 򱱓׵t$etq\Az&ADOkDҺʤ*>B'C ibVO Em0\X\'W v3Z˩ͱ o<yJǞ\M^]ndC*^ =E3W.(uN %mt?ص֒$EiF%bob׀Kf<7SH^ՊPӤ'd)Ma1 #-*~-i.+WUi!Ύ7OWtjn&.>P<{@Q.ڪG4}߇ D3_!( u}zyFBU6Tn 8HF)VlTvnʳTr M1|~/4eŊ=Ok֪), T%5Ոtlf*n`ׁ R室WK -`E|ZáNSr%k;&[d 8^,V; KW+,׮)>4;X(:R g5}})):[GA1`f&;rFƽv/w ҋ\Cjl-"[Ai)RV_j*DNtȺm p֊;U@ǯN yZ^f$*!;.h.;@)DfUge[%(UZ'yXD@LrԳS z9"ϵ&*qyV)e5tMul܋N̶c@1m12SthC~8SnҘܗsE.!|F^LzG tX+Ɠ.qV8$t]|ohS+,*tZS~hEХCd 7/[H0+X`v& JA!єGO(Z@Ȝՠz^e|5x @ZUo5d0ؕG+2TkCmAtĞǸI%( O+\ک@Gn;nZff緁&\I\hcҩTQβ}t:AX/ֻᰞ=:֋{@T鉴ӭ㶥IL,vGg@O* 3\'WSѸ 9ósD6ٚa̞Vn7 "̬$ "d%o;4S]_v GHJHAՖ TeS5G$@ N98B1 X[%cYOj?SzXW7' }ɠ=7^\_ܸt׌d٪$Wp 8>OJ]5ٵ{"9S3oG7 \Wl BfH~FDžiUɨ|]مb$J.*2/9Rb _Vc'&_= >"il| "m-Nr^}~4Ctks& D 0˺R<0>O|X5̧4`K\,Gͦ0|-=r s `=r ž (| TO2R}Gt( 0'ǟ]\|4WiD8mr\T&1V ƚ9U DWN5NH)o" ?׾ǀM%XQ-f<*~t N,8Jw CݡVJ&M~݋p@=c<; -Y~R!^8ꛦj.oXn'U9=&KhTV.F3կ҃OTZ*lpl$H!UF6`O)0&':CIkzFl[iUod8RM'K%|yVJrdx,aAg:ˠrL'hT4JՅOɪ{N`dmws@bj,EGM;l"x88a(gWgBwa!'G+&h 3H%d;qյAila++A Ea۱!II*P_h-tZӔqa]Ld#ỎYwVkGxF;|)DdDW/)Qt9dJ`-HDvڅ xN tuv0-Jiΐ%*`&ʣ@{Q !<Ó;ONEYŌf \'6ꠍGV;2c=S%܂@y{JSO*>߳^SWU+Ex@)uJ'\V$S52:㽎TO§4v 5Vi &P5;PoR`EAkBv,jvRŎYM__̢@\DX/8=2I6puckDo̔GS)X+v:x\CkwT!m' jk%(R٫c #%b-s1QVA!N;#v`]6ـsI` ;BV5'Ms"\qPpr Kƶ8D d/\4$5 &'L{15Q9(3R-FS"r=Ķ&`R(OiӋNlS|8TXN.K/||7kcE{0`#Sj&Erb0Q){8uXw~ml=T|`ce خfG)/`^)9$<_b=y'SL: T8eSI0*ڪfG:::&^4D3*/ ĘnaŅܮ$}#fxnLi^F/\H8;N;P:v+{vEWerkdEAhP` tW`Ysۮt4D8< 0&+ ]t)t_[.prFt_qsB/jϻܻQ)dF8܄f/XY p(Yq(N[9k0N: $D }Nq\%㑶xwW(MǪx\t[Z+5r>eC7 qjRo'bB0\,r0VL[8ħЛj rH0UI) $l8d}; NYG0Gvb-X*b墣җE_Z\MSZuF7 +p:aFrvQƀ=+1:;,kNˢP]!,}<)&8lKppoer 6"ik&G'l4I x]n$ͮ q ݹ\qxֵl0o`Mlpq7Eb 6bi$]9"2T%+32{/*3ډ{Sz1j{ %,{^[4X}C\ty^*Oꈤ0X?moj]&nv@\`w7ULl<+bpP9CP:VGeQ~:9dPGWڹ87CSdkØs?rO3Pȟ+2S׫)`][ #VB9dvߓĽ5EQ~׺:` ^oa{1j0i 0>|$Ӄݒ`nR젼m"{c`9ܩrfFҤ., ^d5z削NV 7^.e6 Mqҗ;.4`7Ui[VF)`pC*Bs𥷥|Y m|<yd(s$q݁$PfLd(N`QGN|e7 RN>)>ws^Mq qRݠꙸnHL_mwL9 0%4_7sm:EZ ;nppPo x88?sOzHΓ5|:rNpPb: >*0,lV͔=Yl66^j{'+=^hVpp\TE||!&tn{/͉eNPdG."\Ipvr{]fR+0nΜl@ɋahc\KRnJB{ol8'3u x:_gV U vpN7OtGюt}iY۸x|©=5je7&&JZen3S )$ AÍq{u6,n{fUpQUklO\r!JZ /.*dc˦&H9)jPp*cK0nc6%2rF7MZƠ͇6%OG0^j=0Dĥ,Vs. =Q]:r~8&ܣm?qCqko*YAR({m7粏X%X;s0 ǿb /Z-He` ljVBX8iB8JsـoB" {c~p !M}nl Pb:VmDmO)ភ|qFn 7͗(ƧCpJ.1߉ h|{b7}tlXsޟw +0f—nbS|'7xw6}tI!сۀ{Bm%-|Ïo$ӟ{繃tdcJJU;:QWE^o L'S?͙JT Ǔx3Y9 9h9e%ql7(qp/zcL}ψ/Gr UAcCjꦿs~A`Gp5|$E]d <~m oZx46ܽB6ڗ\) 8X)҂K_t abߏ"o>|+St)yw✕ ԌoҒt3m*$~F:Y < 0?uK3[-fG5v+0[AL`i4l019w] )q2}g6wb|) _1;EUc_'lԏq򷻡2gz^١df▀C  _="w2DŽ޾$LtE{Ki JAw*.0*d>[e?+&s ҨU?afҝܓL9 3S]Ox߾_wER6mx4tDxͬ%YßmLЌ0f)%t0UtOKOߜ]MK>N pkj&c-+Y=ױ);SjхNiQS_Fxijc?#i"d:k6_$y3Bnf=5]++/{*7_ #d8^Y(il&Mo5ݟq17{9~5KY-#Uk+/vh 1[=[pNj^P ܚ6a7n&o%7ח_[E7kLӉ=(]R/.Ày !rW `jꙟ6ߟ~˄0OBd3lNSCxH}:dW77[Z|L$xc(JbhPcig aaS~|\27ސpSfC{ҺƬPͷK<#1紨vBJOi, 7u6d{KH?ۚ} 7L0 O?L/{ n p Ba6:~[~ߒã@+; Wz=Kp|6Naq(A&"s~6ŗ |-—N; gfJ˓cM )u*ždo;&v \i 7 #u7*^+ѵ2o&"lR1~{}uo5Yŝvq%(p# 6JgRUNtMWU~?;9. Ú@&󆥴a\ q> 4w 4؍\O&K0]v$(?||,K O>>v7KhG( .6ɋ_W*7Jw ѫ@ȍK)uQ4?Di)nZn׸5BN't&;qX4;}xg̪]KpP+nZdL= MV{9~T i諚N ?(/Ly,۩-J}gS-B_&K40SoƗ>20Ct Gg+&\`|fݕ~)G0_3 Jof.c^B696Mf]t\9$PՖ@2q[c\d od?'iQ%f,J!i[ tb_0nXx3ZзpznA!Xh`z?_tC$8=ݔ W2|7PKK0޿/kO$feaʊ>& J:&G/SY#<6n>u$\m/ܛkNn|:wi|νߤE-{{EClvxoIox45i5i?vU.JtLe<!%rNVȉ"vpUC7&UcϾ)K3B}Lp!`[?7%:ͰquM}X,Agm0c,[y|ɵ4f*>|KO|٢*?'O='7=6nm s]]\E6 5?dCAkd>.R0X7+v碤ds@} >P<;^Q>__lc&wX 2RȉmM5Bݛ9q+EGn"ԖVvOV8.2c5=yl#խ@Xʲ<`Q]r~T? l0@2U!n8Bi_WUT]{yxXz!8ݱȹPZ%3~Q}X02d ܜDR})Y\\ ƭ^@1 <@G<^;FyƵ bYS!<[+S2s?>]ៅ&a ̅x ރ#,cnrU_$~WoXOGIݍ*ʱH0* J|1՘!ȅ}dVJ)|hncȌ\}4n#=k("fd;yDeA# r"뵴ŋ?5X+S'Uw'I[oa3@e:N)e|cBz^ &.V;S!{:ҵP '}c`oʵ(MI'\9 ]vɊ rݲwN+]V; t"n5)O.B,g]x|戡^8Ko ;>!EIJ9p'$P-[:`+*X웞J?/ûn& Xt K& 7馚+M P/uUs9Yϑ8.#eRk_)Lᘑ>eoŷ?SyIxUĔ)FGGF@ҘU[Dj %3-/e`ߗC q6r|5 &CwzX,6d=0Ѫ>p\1FTEIRXy8sk E-?5ve,}ǥ p^DLl7]e$vqibƝr{윬9>o-ǝQ0x󝝟zT=>8>8dJ #I(U$ ]%dϱXp:TQbDBQ3J$9/XJ#s ^Wh!'@i ܖ-?i[`w"ads9s&& $e[R J,rTEqҐ"!*kx_t蔐qx#%=/>;j •gFy(ׅ{de('N?[ɮB6:t }a4!8 tuׁ|hdY,W `( !zC04ba6 DZ1PCC(ϗ'`x?4ʞqZPU I (3{ T_]tS<l$RqXHXsG_N#W@_u#9sr3 k()Hd5LC&@5u^//fsC0ҩC'u (&%FwR7T(妳9`d x1"M F(:$4.JR5]ՑI*2_Z(\ vʀ= 9E#J %QLeFxe,'>osN^]w=d 5N5uI"Pҙb[k5oxQ[$h@ƛ3iX3~E552ގ%@I eEnM`cjuIBNdP`L7+C0h$X\{<^2ֲB[ yUkK#!+uhJLzѵZS+֠DIёjAVy?'?>MC>w ȱHmv!/!nmKרS0%ѕǻiUJZD&=YFPQT^S)ю[3,c骪酜2ݴFEd7rFGC#N(RN͈E>lNH|W5k(pH20#5++w K6_ }1@3(7["&IXO?N(7p"j(K(XBRۻa4|nPK LLoǫ75kWk ØZ@1UERM>K PjG~ր;}ƝuOV7KcpQE.,'UuPQl `0qNnr|=@-,W{=@IC•6@*| }VLJ{/ 0o8kScuU \Agw(ڳrR*j/eC&Zz:\;S5!/zKO x2\ e͟DkUXz]Afs' pvxX9)q"^wElp`d44@\676z/H(h. AB$]UdW_H:RH#afz*rimdjR Xu>E+DT_LzaXXҒXV TPLUD@&#TwO֧?Ӹ o5:!2ըi1^)HݰC"kz8[9KAM>N.ηax\J˿`P꜒ %z||,Okԕ(&rO?^GWx]fX"[O5<3Ir^/owB}HDڛi BpY>(` 6BzyWƾU6pKU G(^)O/kN@*Al뷳|J75c6m/1oOwkh pg(6D1pH9t*Ba*Ip4gN ƀ,o]Z'sUyw՞ XI%f KAa{JV:~ufo,}0 p+aakMSػ쁿.|:Pi/PrEغDG{:T: ۸>bQ'e|_|{acȭگ>f?z6`Zj eK,y$D? W8AiDPu`^%Bo[Oٽ`EV/tm{~ǏYxq h$ZкZ+˷Ǯ/Z_bnwl}Y+@l 0{(ļҰcC)$>ap Jk\7e[C=J:vÕvsv">=Y!#K$>o#.ۮ@iYEp}i"tEŏb[ kƯq(y=+kvIDuT(ȐQ2jVNMUgв]$Ǖ㐮~3!C`Rt^J$PKXgy@3)A3clSE=ԺKܑHl6&rW@ag3`)GIRY)}ak7j{v/dzKqMko,<| (sdj 쨾 BIfCJ9^F2G`%rmB㇃WgS: _n/JÚA w_NrPlf>rsF{h / qSjXbo{+K) Q JR vUQ !gwOo7i2Vwz'lBP"CYkBbSgPWrtmC/ U" _Z8X,vI^zBs2 %|ˉl%7{F@T6`t(qಞPJA=g}(9w^g\[S>)>ָ^+Qo^Iq9/MhrVB@;o~Ҕ^U¥_dJpv˵ZM_UU$UU[-`M" /];qe ps(4osۏ|> ǀ?N:&{NB^?Pz*IP D(LHcy$eea]fs@ⷷxEKd=xu㛖ލ [QGѬ %jp 0׶Ό.LZ'ZfIt5x$ -%? Ƞt:W=O? ŬUDI-Hpg2m )@ŏT|jԛ/ß }T!"XG,իTA#7:ҘoUlF]ZV0RkqS|@c] aDGg$I0$s*-wB(آE:~rσ[|GmBY- -:@*΀ [%S-U(ϣw74?߳z);QP)ކw\}( hB>?:zXJukeQQ?sӗ2#0 <8ZqSK)^{xMǛI/0`J{;!=Yczr3{c_q͋i鄳FsMw21=<ӵPJi䮚UV Sgo_^Gzupy=aEZ$JR({ڇA+[]'l2+95lFCP6:{ֺ#ddLB{y%u0/ al9 ?hSR {DCp!0ښe*^[ F M %$`6 3XO޵OzLz2dn8~K(d{(;mw,;Vve)k9YX{pdC*g'9N |@)F/"\S;G`\m^2"茓)= vJLvICbRz{DWQzռh怟q R]FK0> T0~=I=o8 RAP>vpصSuR6]xz٨$,XKe\IQ4tr(y>=F^)eogrLr: scr@}M*5qenBsٯ0nL)5m8U`#{\Ռx;>Zq'㐁**'R`Z0lh':n'Fo@K'%[!oyPz8t0`;5& BWT4^Aۭ{JS;K]pj>H:AZ.) ^*b_̢2^0c3|i) ,1޾ dq%_`:LIgWI%M{ ?P =F_.u=`  1 7p?{t"ֽt*Sm.):: %MV Vǒp%~2LgҒBA$i\lOgk3g˫' Y>#8Řp5c/0FCiDr=U-l*nZ`KxJƇb{NK۬j* ?`&x`I` )ppwk9'rO*|6dME"x:`M-E2BoH*hrG‰;\_ǀWIp)]<e* @Sloҿ*J(-EL9ʜcx╰sf]_+FQJ~c{68=X/8A$P!}06FJf6jdf:è@ՅZ~|V{42iK/Dp1RejHlE%R$J|ܧڸu٭do=ATLGYx&Aalf{n9~(Rq,9F6{kD JPlv 5F9;2{Ko;vX:' }wQKeVhEJF׷"9Nzxz.jCRwN2]kca>X! ?['iYT )MRq<ƒ(/s]Qj|<Ы'J^ﴬt[ÌgKDEwg&<886Җ[A f|c(~FY.UV5J7GGD#7 0kZK3m* _~YɪKϦAS!g 4'Ñ4N,dZ@P܌ w'yxw\W+n^utb`;E@o+!mln1J 6zPF@ĹI2ug?8 H}mˢIU?M/|aV%8g1IKE~p!C(lō($ERޣxo86k.eĘ l4gKKccb!~KY[1nq Ip0~=E{럺~Od-UatA'F8hgH,2%Ӌ>#.tM_ C!1o>c4%'mpIԚ;S}FyܺnWɹS e\qNzFc?ʴ+2~ΫI?x :r+Wu ծ_GN\fOIENDB`camping-3.2.6/extras/images/whale-bounce.png000066400000000000000000001075221465547410100210100ustar00rootroot00000000000000PNG  IHDRjgAMA7tEXtSoftwareAdobe ImageReadyqe<PLTE[fSvjzЌdմŘtHWeo_i9EL0wKPPrrٍwڥ[htz̥//y&P\c3:Bjtffh##'3484swyga6۵DEH9HS[TUYl`mu*jC%*2DڡALTP.sxḸ[q{s%w$x_"TP{*4=V_j#+8yJHdFX rȂq=enmm殢c[P&" mLB5Vuqp 1QDvhgZ@;0` j52.kFdQPS$LKP boou h9d..7kko eLS<>C [ \~~+*)󫫭 ~Ypssu:+02:Z]_cLPQclf)sIDATx[H\/Z Xr b+h]eyXBYK(AĜ IrBêA?&ް|hJ\1$0Tg^F"Ɋ'59ckͺIgֲ.;M"XwV? zUmK'ML$d7iߏ,NU儌r.B|Cr;2ݕ |B)bk(a$k0o@H2ߖgRߟH$_B^VB0iD)dbTđsƐ$ѿ"I qW+(dbe$.$io6^B@Jh%іTS' $a¢(KS*l_W9nز?CX |D/]"QuB:؜ u1vqgy} ᯞH9]7jEZ)_DXg'.LɻROY?%2$y"]?#3Y8?0E5~~r56A=$DJ8qOW[3 F~1)a,#t 2Dw -ÊFȹtwf;†HlRW|qGPm&erw T_8򭻬C.qOe +Îj٨XqG$!$ CSJbZ|1*#EY jkhr84fw|X1\(#K9C;-EΦQGVʊȟβʚf\j"mQ dM!lchMsH!=OsuV)uIB#)9"Sf}W]VmltGVB_u_uY470Q=PQV]=1#do̱QocYJUh-zYh"-rؘB-P()J0M}&M"r}M]ƁaÎQgDçO}@/&YH \߂`VRDy0dq(2足(Z!BS`EyPeBH+yTX_gNY, .~^$QxE8N"ئ*O,rURU \I$aeab*H"K% Y$_GbDD'ab=bZ aa"ZF;Nv97g9brz i."|EظɎik<|ӪPWpu)6V>(*BWee1Vf#23u6EW*&f<ɤweCA OAB\PCqfMEޔ"Ȕ+ " ɞm`~ Ddɷޗ1ׂ]_"qkL' ȳ$(AV,ģHƙCwTÎL^JH?S؊1x!$s%za" 4\%5]aSȬMءDO`d|1$VY粦>nf@|."flY(Ӑ겍+[s*3I3 pMئX]򥂱HddZ&\ՓQXwkǣ^򺽾:a8H",DcԼ2&,AhI]QU$&{Xf4N0+f*dj$v?t"1i[Bbu0>#'+e[YDBʝ)e֝ޑX7enK޺XDEץ䘼_sPTY"ȷ%xԹ4"Q p.n205IZϤ-[dDZ^˜_ t? Y wT`X 2^8; Cէ\<cAN@dqj( <'&r`H`:+14O E9e<5H-81%mZ YzdV`]2٤Q!Qh"@ AۺI>rxNS_]]MNm#>'1ingb 4rcD=2UE!$R(?& !#!w BlqK3w4yxSw!O0vŷ|mmm"Epc~~fx1r*KM QE45`֧/&E7$a49–߿2F$ၧ']ʿ(˄F[^?O`Wl}\,9Ѻag|;BB3"=o]w+QXP~jtUX7$=4\z42S Q܁"o꒧5G{!vYyoܻrE'~e/8_]+&'9FN77O7ol47yEV[9?fQL(%&lh# e 8s9❫bb` F(87T9?0>1;8/kpwE݁/n[vm;H[[$F{Sm- t$PeH^eC__6=" \fcDY{X0@#b2z(enoh(*G #♍!keoW Ë@m~[w>ܘXiA%dX\J]sΏ J7#(o'w>{㡰Ww/7;ÊP`n0Ϝ*/Ul퍜ƴEDJ2\'H[vF݈D2ؤ `{Ç?.Z{29m ܬo1 x++En,ybjww}t1 WFWQָh%g~pmeĂ }ft=e} (H'F%Uֱ&O]7***nT{+lr"o=Yh{lqyitU溯:${0@$uECGYaէ#F9QZJIQZvFO8\Z,s?;}@H{h9as g&A yͭCoRwDzChM1.DhtBr>tDR\>@ܽq<;n8*OQoBW7Zt.8Y\`-0x[-ƋaLc([GW"9Hz(H2qN"T!ݽ>6V4S1?MWR]I~?.䈧[ ׾M] lmmm[c(f./xdqwDb=kI$L9cxP[XV1Gyh(<˛a*bssa7p!; ?cfSff<Ѫ̵BbDW'T_2NM,*m .go@9m`> ^֝zZ@'Y%R{F8Cdf7#q{"m+8ov𕥩s/X@>{g$$ w;MEKƕ WFjCwheؘFxjQ}$UD";E6_[r}$0pZ%NHEYѥ $D&P0@ sWϭ7LgCCmghnk[`x0*}H[ۢtno;V_`7d#no<_^ [}+}= -XhV )">BhJ[]#(eg 2Omn}e1̌c~u#wGCs[A;#"6OQoͅ!M]0V#N2Cc2Қ4,366rUIZZLR*rX2|lwM錬@` u͛q-OE>ZWN9oWrsc”z8' _0x;mMS+<$,qF`eg=B(>u0/Uk-DVKx0-he駟\<:W? _%`BzJĴ<0800mir9ޭx`ldln 6ZjB6H JY`|k*ٚDR˪j:-T1L (b H*(o)w(GMɟi6Z#ErΣʉƕsYix}h469CƾKMV˵Nu C/CM Ym ŨR Y4q-]gN"ɬD@ fT$L xj!퐌LƆ|>_LOF斍ͥ E{ȿ5߷?]v9 s`z<+>v1'cPf,tp^M0S361ؒA: +r"}p`M3q" HPϽǃא7۽l(ZO4±z䌶7L9+]Ѩ+r&{Ub]` Fd-k#ss'%Nˋi;JgБYHT! WPgS3Tr098Q <"xxӁxl`||`|`j*dA֨("@ޤ),o-M+*L"P^e IHyaJҜpK&F}#HSr鮬E\ڋ-go  ΰ~/$Q }~㖙/Z%үiRr8w~I&ݢ霄m)<u:\vhqqU@+:0*ЮbAR jER ̆e|3!|f$aOEew;`hB3_sa$^P~vG6RSުS&dD}LkK+'eADcqnBsU=`{Ffhx}EC;\?X6`-is+dM2GZ*%A ͚,!%HrHR*~u #kO%o#ZR E.>oWY]Xp [xi BLH{E!%,}UDʖBi}RMJuD~ͷlN\>HVB'O|brQ9Q0,CY)1è5^`~#g!R17ɢȷ;͛1B,55~FT 7`6hnjs43>תxXd~D+qL2ԻܽTI6v,&DN`=bJGjFPzS)rRy"՚JsNjN::*\G6 <]Ku#lv?Gw~O+a*"=_@^}P99z0@?;>JTI[;{in  .8Pl4W:tNb|uQT'ňN W ;E;9dAku3Zu޽{r5jj ;?HȨ涒1jѤC%IzMbuf RZc*'!a֒Xj̺kUnR_']U)9DbkԺJ%<@TYwg:K?|!ҌCM͙[/tzfk wI-BgCz`*Ϋhq`  \sEY(]nX?=`65 nCM-x XfA0myH[ZO\SJ "!ԓjjoWbtG!:ykJ6njU-6Q`72xj$_ŋ"ҙONP5s"dq߻ /p*~=Wwv97${~=uL͇.xPP=\`υt{3f5^tHg3EPH}!RXM1UZ5IU?3 ImqX x  FsIwS?.v &1=!zja#X3b&`Ib0TlڳFL_Fg3^lR0ȧ<yAhYěn9)(^ 䝼0Ի';u˫ӞEX.`$`Zٌ}_۽7U6Ϳ"+kqgN8Sz0:'d-' Dt؃6B#M~&w0 !31#:|Y-79zy`I륵\(jS> -%de}k:5#i$vha6,VY!` F$XE?rȵN-C==y\z8WD}#"T@,ftAVeH$xg[Oc|\Q牔;{mAfpB7201GP$yˢיX/½{0=xygy[ zWeW"3#pV1$AK} [z35k4:4Jfn!܈s 10 :Z-uQ~v,|M@/)CP2hQv]`!%P>'|`4m9|Z|@*X^&v:5QTUqɎ\D: Y'?8dZMY=TphoccOJcn+wWVےHK!"w{{"QF&?m&;6EVta!L=,n:"Z$ ?bgɼ2D&n%`f 2Qc=|Mdn0Iڿ5XYtafĺf,?V9ă+=N䳬.;WTO 7OZ B톱w}#ΗWd+!X9LjJr*}incem4"Ք>!.X.`y6%D#Q<6y8eRw%RE"R.֜<4RGA¦GoEq^rzT#lLnj8 #>$3&جY2Ҷ e(-( ŢdEp@v-3ɔsP.KDҁ'f 2 x*V\!YJޓ5/ڈ P&7mz"?`D:Ək%B΃/3to7EX 0Da4}-vj H&jrV:IH 8kޢBl BH5ī?|s,m1m9kmyЙѼI΃D5Ք~55GCd#U1,Y&x&j'54ᬘh!k_*X/,ޟ)=\hx4N(GRU5 AeUޱ.D):7'2I@3¬IԵi^g- C\U|~l2A4_(wNxWZ`Fye{a'D rxù Ԕfhk`0k;`k3~ 5n2G#2 'aLh[6Dy+",H("I# '3hV5Wm,@Y".\rL$ OjPdbF*RE`XNy*oP)Ђ\30Ӊ''6e^_;Qő4CN1 @  ehk"kE ȎHkCLaK31b\AVU TjBq:6Ԁ:G+aoGR"\TYgut XUjڐ* @͐0G#3ơz`"  d7a 3qG8KUYի @[7!5 [[IW0  z kd+ڀƑuj觍}^PN¬*K ɨ<qDջy0i{0XVLV[DЃl֒V2,+6@vS$;9Z lB:U'.~ֺpiV"2BoIsf˻WulHu5JxT6˕3R"P3/Nj2CWemEv#yLO>(= l9!GvH(iA5U=އ 0V;{6nx̊x kas8v;ŚoaaaǞC󔈵r!`9\7ej'X!t`[s3@tdBMd-]+L K֏Lاn0'/?Sh(*b˃{8 b,cTMx͹_Ey&§o&"yG(j&ߊr(3m e.YZy==bOSnkYV^?Y>؎ ȔPQo*!RQ ('T<Go==.c{PF͔d3dM摝S(wu k9G('욗̱k@W"1гIςۓ`eƌ{zjtC쯻RBRs;!DOyhG}"kڳk"i"YC.Q(AA ZD$uPw/_!gûhMN :'WwE$=ޡ_p{ľS/lD[RX(':5vDF;+Dg"|7ju{(:4!(X"/\> * T,'e)_sFrk'QI˚`8S+ 6X }~IDki %KYqϜ$r.dbsDuVj˵!ƑRU 5*Iv9xN0"&Įu ;gL$V؄-XQeYA> K 5l!\{ DfPχĭ.Hs;>c+u`*'sݘFA.eMM tg[mwD=ggSuckw=1/ы5؉5y[<ƙ࢏ްaI|>k,.JP,?vC$Z΄T(؜xD4ԮPClB#~: a!/lXZM3sMOՊzIF$l2YHO^YNF*/Y` 5Hm3k9-Y uX<찞FX f#FLZUH,oZ's/d_zaV@Dy"$xe~V-FqZisHFQtq6\9iY2 R}(yZ0+@dT Pn:xƼʂ!lJ0D&ojo܄MWEDmj CL|O'Obm{@NZn#^_-j  _{TM~=^1e-9ɞHLU"+x["e2-yS*^b; $R6>*2H@-$XUuq7=k iIih+ uIr4ߝ/t'6!L[аb@A{@T{zUX8)/J#t]'^ة`J'Rhb]oZC9k{?SzaZ(sƠz2V!TUs/9B(.DJMmղHW93!ў@s؜h *K:siqo:ܙ 3K-$'i/$AG0lgHMJ!4]AX(.6OTgI5,i)޴B]ջF~¶|__3s31SQ#QA1dg2!)NW:R"%NK3E_؛ug=uGJ5We(»hlkw&-8_H\t;2(kV ZUo?a!!l`u$( 4_'&?+cw Cv1 ~19,ҏX:Fb$z-[-N$DHѲ l/گa I9|8l V[["Btq'YdLzŻ @1tO#ZGqsefHq Q$)'l9!Wd&Ofi@fkHwCݬt Rg Kpn"+'A$Ȋ$QK]Hh_Hq%6HR9&YiZ \ZiCQ(b9^uLlSQ,8Qp"uO; TTf"@Pf$9fŻٜ: g2TG 6Nnͧ݅L[>sRT2 ˅0)htF@(-GCMZ.<bovfZc6*:OTfCHxpBzm&KWR*F#\6*7;f);ܨyX/yK. nuR-yMn$ZvYΠ^N_bM_De{"q 3)~2 1,HV k x,5FawpUzq!7n,kw7,/\A7GNf[6OtT`{ SswB:hݤl^n!# Y\qRYT;IM?0#LˌV3V_9BDsr7JJB|s7ߔu?}֕ڒH9YR{ԃrP:.iM.~Y-BIQ z$gӉQdecPڟd Rc}…P7-Bc'+yb̓<' ` lqvHa\ @4&.37Qmm7A3`.8ub1mBֈ%e4tp҅N<&dIQJ$P 0ɵUU#a8'}r ##5ıy4gQur [(*6HPáwF$1*WΞ|MIP$\{ᛒI0~fD"Tr@%?V>hl[uk)aH);cCRYh \]HkI-5(}ˁޙhT#Gj}JmTr#EN1(j@$5,9#ZX,PfK~FڅvȭJm 4K[i?lvN"=yX.6v-' `sW5fO;PA$ uƏ bgVK }PYE~Q'tIv t$\$C4d3‡5yJ,ka &sZc e⺻C1&T#.զ H+MEVNZKrH0H(Wl;ͅS@?bn pYf(9.~rSt. ,gk=Md yx8ϖ-9 %%Nծ::sA}ZFssΰ;**zc~HM9!jZH3$bR,IEv6og WiGv2 9m@fjfaXLL\Cl0׹I."q0DTwݔ: (+zzknݻ_/o=n>.w LK?8_ .!=hqjx:S]IJEFhSjyA+4J>e='}&=K`?\aBl"y莢<=ZR%5ȳ8yRP^.=E!\aM Ń(sedb=H:!𧻓.{6CuuQ"r"s":2}4LЭǎ݃P!plcGOX֋r(}K^ɱ w1~ȱY`yww ecQf SBC04LvN , 6]i\J؜i0|W-!pZHQGg*&Euk&s9.B]kdD1oZD+0J $tZ8E<#4$r,~vǎ>BmUʃT D}j YIJ&Nplo3R緔qËGYI] 0q/'` 2!m‰QuY $$&+LrȱWEZ{SmfxJooD0rM*>Vtq7 zQ_AT -(@dyɞZ:yFЯg55Q?OB8g0z9Xu9I}T"$%2L=?ʁ°LoȾ܂2VvɹIj뺢$eqOV ̋Z_W#_1+%%GkONExzDu1y  22;lHf(ʊ$CK"eB 5L+ %=P(U%=lA/Vu 91(/`_5 i@TrXmr_Dp3bW6b>ݛ&a`JeEEG,V;uz{d/T ιY%ieI@ :Hc#[e!'LΊ`J$+rI@t( Ϙj8X=ڻ d9,YpVe*OeWj3ef-bCu kh^Lޟ_pmt8 gj\4{+~4tݩF Ʈ|J @i Y;gN EWFwv~\&G|,,Gax!;cl\JSsySkusYUUF$,"`{:yrL65o쌵9İ^] UXYPy|{G" @Idb};EʮbᾤzBv5g+v gY@Z--IR~p _* 5&j _HY@}ѯ^yz-Хi)CXŜ<9 w=>vMy1?(Dˤkj7SȁTj;K1/-XNSxH$g--pRcic oޕDRgh&a@vH\AXYLs LIGZfVlȧh;9'>~'kOt|BKM>okhA>xG^2XQ&È kP T#TR.S5CQFEG`;5 v|e2 zXW/I͆rdȾ4J9+ISr kйX։$ӌ]xd 4L+W~9sD4rUO5:NB#[buBmAXy Y lt9sFVQ+}w2_)Eiz>ozKśK6.Wx( w`ðhy4Vri(IaRZpb݉6rf!O.;YfMؿdttj8X8Xq:'VB#;޹M:2wNƃ`*T)U4ǰ(3\kߖW^gpS"NM*Je Rb14 WHؘځr`(PA/CKfXxzf(|S>2W: M/Cen3ei.ulfV|*}eC'f~1 U/WKV5$Q+H܏@lLf>ҚIIDUMAn@35v]zobs6n7m_xDێHP;hpq5̷nu/ tO *ӺD(ܕJ$*ڥD-E5=+*3': ss1싥C.rK+.TL- {JiŽHZ`iSY üpB-P"oy0 WrZe}@ O @'9)EHGR1_s4ݕNbb^!ӨwZQRqdžʛ*#Ec=BkǶFH5`-v ӿR Z2mn# ]ndX7"7GV̼"~~4H . m> Z:^{v'H`do r]mXYY!icöɳx,kkX gso4X g(1Z1#s.$ 9h Mr1QLu+oW%ڒ=P!H4@{.Lq7AL.(ći4K zW?^\}񂂆:zu``j`M`,ҶH6&A3#ss"ȫ>?Q\帹ȼAF=HTos%A,2TӰ<ڦC4S=]W M'm"۰ ޙCj|m;D)%/\Vܺ|T^{`ԔoB[/ F# me_ejƍ w9:Z1sՏQ! dd:CxDI *B/>v,cQ)MVB Lb#Sz(qO]A@%Njf$ B-nxe+O(ݘq(J3}u *nAEh\ʕ\z\hswj;}Eo_0%@j`H+z;I$h9\ hSwvx/D4Ӛjj9EI> *Ju0!B" ESiXۘ7J6Pm1! Nw^RK!\유9 fJnԷܝe4ud2YoCo}+>xb/9ci@M5wqb;TTbks[upWDҧ/'+/ 뉔M[!OrȈҁ0Z$/8(; L="q;pć$WR7ч#,(璄YJlݙbpbZ6-ج}*yCyi%X7Hy1y}H&CSׯ'dil_mo_xu R,ɡʛHDgK9H/u68  Mp]K:jY;uSO wʜM*^\-|mˋbEbb3 {xcc>w@訳rr%;8O~z:0TFF= $,؆V/ww; @|cN*12f$1A3.*X`vio][T>td;R|"-Cϳߒu2F0HND`FI3ZsIuT]TWByp^_=k|nwhi~>Jm W weLExu4+ T mJCnm1( (0lD+W?x٩?b3 ADb!R%u}abX x"iPƬ'b]Ų%6UV塉hRxO&3Ukcg_`G">/+&>ds Iԣ;tL5q|`|k30X%0 :#ki<؍P, W-(j5&xaVG= ɾǷb؜0T^XXXu6vzhGw<3V,𱻍|4zS~I#f10&UN+_U7F;BHlXǁ.|>EG,K 0 +Bes;e O|\rne@sw # :CKNݺwW&>9?OĽVfJ/0 LC* /36]Lr~AC*򻴻cҳ4*rv ͖ -,hDJ&.c0@ zOKBG*Q{!lK$)*zm8HD1orR*Νޅ9VpO~F뎦S@ֽo_X=rR9x҃U]jMWˆ$'dL4ObWQ1Z-c[%D pG|qӞHf89̑RBa&`Ef $ڡ(S]zw ܆FZ`<DsT&Vv7_9ExrDQAH* j^Sͪt䆚W])~Z'jbK&q^MR{T t6R mV=:nz@NJ:<ÒHWR51b,=V:T/wH`I(Q_$2Ծ;{ߞ?^B_|@ cN28PRipjJ04(!R/T8{r[J8=KVNمٳYB(ݞh{8ۛ^i8qˏ_oߟ[{1554v۶#Z[PFoWMpGU1PymCW7XfDPߕP.}W-buϼE_|n^~gDxYFJLJGS)}ֳC=y6}+:y>Iɓ>|p|9Y#CCi9;x cU '>|ҳUBsimk~>[_WgF_d ?~d7j6 6ϸ: vLNnT*S/~mڌldG'x/ vߝ_X^,R?%s{9E@vo~`sr`܀ܷ3Y:Q^ 7^nռҭJκ*?e/n}!2ixTT-cLA]Ul!YZ_Q'tZYzHtE ɐzPoQ]jñfIy %= %YZ$D,C*AjW/=2ԤئU&xy˺Ծn|m99_JMRG|ئ8%S*&xƥLhnԞM_IUP|Ϳ7z[&S.MbMS#R#Kk4 WޥٔCX{tK͎pҤylvUƾ2r@U)~y_£Yxw4 uJ֥ŇԺO^z+v`FkDӃd8ʩ+_ ]z! IIkiuSWFC De>Ȑz[,x9i$uIsOiԙ,U*$&qzpG!z}Zko3p:^3[=:EB S >J@4`ٙ:"aIQ?< ~+_M,D޼]0d!X ~`(Ґ3Y/_L6ᘆtBV}S$Gߞy!|EV$薩q~]qA $'Z0OfH"  7i$xlM2C=:h̠&%-T^J 4A+'uo 8yz>tK|`&A2C 0N#hyV#qaVQ5_8;{w=}Iot>8 $5K$ls$`tV } F0 A}RV$Ǩ?8id恬LSf1g5 4#7{Sѝ>< Fw>E&s̀.~F5GW!R~TdBłryzi26kcuK'5Z<␗RnSwsIVep:t[TJHImX{qq[3ϵE@budH% W }2kssrgG,~u[TR{xq64EAJ^t"BJx@zkzGGG${wʐJ64wyfT/~yيjMG~[T:sacQ-v1NjZǗa %UCpunܸNzeOT2]jnO?Z@UgwU2-&,u ޹NR4IaԜǦl(OuːJ乿Y;{O68ߥIbDJB4WW'CL$W7rܒi"lxe;<*1 GáH^#CZPnKu}PFHv2: HNm8ؼj[[ɚ4c{7\lhٛ<:!-ar&cg#P8l2oUzEY %=}ىl9[?b7r Fsdy ⅌&6?&b 7sR2ur<^ bWkrorbluө3ɪK0 UDVHf̕21sdHEys- cZ1g)/:xǺz^ $y%8;j9I;xH;v,:'!+%B,]x3Ne[c ;š6XUiB!u6;Q!`LVgn偱ݮm4t䡱Ҿ *ۍ&]BJi02hGGuk.Պ$W-d2LWU]Ŧ'LDSӝQͦv[tǺ5Jeuh&@PDd2EkaS⬦y}+m@RY{.\jd ^Xw<;^RnnDb1JXSd&KKIIC;WSMǏ 0HC]7ӯNJ±s#8W^㿙ܗ`"X4:ӤT62 k?y}Ep]2Hq懟dƇ[?iJĢ7n4&d pKlWjz6<ܴKFQ?G?y;eH,ĿxS׮ML Mml~=RDuܳX:H@[eW?o||u_~~ eڏM%4U7ީ?)CKsDG]WǀY;:kdHnp7mu+x>SI_jGvwː#ѡ,r<:Co R!ױ!Z..;6Ay~^EJ(ԯa>ͳ2$iOul,{=QratlbĨixd )_٪K(M`.Y,#Md5[Hvːr|[ʜ|r*C}|lUf+=}HOyhJaΚ"цwvjZ5)C!神PGuBۧyϰd:h}×eIXVYq>L&:i'uOQ7x\r %eH$`tsh>YuZ`Ѷ]wOGӃAZ4hhv7}rdzr<>.hN}==S7Z=q28։hku(/DS¬*W0NykQ]%R*2QZzZ:sOHhHtₖ&gDVO}oo'sWdrE{WM$EdHf{z ɔv|Z:*CZ6+`s9/YnҎJ~h9B͞(2 I I®J"Vɐ-  F&RxtOiAPy1ɐ H%*P{8߳io$C* 隟C3lڔ%C*k~zKʫK29!͕= @IL]S!- м: ;f(Om id] ̫0bANITj'bTwz waNITH-Aqnw BIGn̕2+!`tRU8A^7"! OU +@P 8kDr\M QBZ 4Jrv pJv"x^;ʑHy )ׄD x#ܣq^0%, pvy@ rN 'D pC.Pr@p01NK K92%҂jeYn`0ŋ I, RN0;HC bmNIewVQr eHPzi:A^<"('qXbcj5ftgLI4 A@\Dy qZr@nͻ.CV/ŅYpQ@,Ub8GRm @ ni띜Soqxe)U8œA`(%w1cI3Ba(NX,T2"t1! PA'fO9jPdA0r#wdip9YC R!Z@ͱʩqY  =qB1a$%ÂrA% `h()U"Kad ֶi;Hz W63>Dx՜ҟCXJTX9@(S,ur$kOq@r^9+`A`bq#3HɔT)u:aYIќZM ަPu39⢨鸐T;:-(0#+t i&!meae Tt6j,@85+V'ܴo E"/DY()U$IN(u7phXC^tq;HA$PX8=NVGS=cnK(|UIR@/3ʢ4^h02^7p q2sV.RmႴ7lAaIAĹ]XՐ$G p$Z8)`򃩁3R:,zpNM9 WAj}K(@²%^CŋpJt&?;DX!m$, pBr{N ;G(@\IirA}hΒo\eAJyY u@ ~8m#:r)= 8%XaVS\m7|:! rmrȤ/B$A(` o mT)ԫY Q%!A_9XT7,Iv$NҲy"U 2ʭB8l !i(`.M4ՎJ-e-nXCpڐ+8:Ϣw\ЉW"D]v 8&R\ \xvVdg9(!Fݓ#^ #]k))SA. :@ ,a$r@^j`  9'a;< dDS3`،:(j[?d<:+4{)$@ 3\VSJ)ZbT Bn4ꊻ(#H\> 3.pz3?#r".,N\i D;4 XP0א4< p*Kc f "'T!ۖVYNIz"*RFVWJRD&j)?Uy 5*K*R"K U ݣBʄZVV?NI&qUujH0/\U]r'T1;(aS-ꅔ{wEE;2qByO o!h-`2ԠJTi[KeH _ rHiyH2EZt:! Af W!pN8tɧd[$Q2)) I*%C- i>Lˆf* RjHL$CE$CE$ I$ I IRB d."e^IENDB`camping-3.2.6/extras/rdoc/000077500000000000000000000000001465547410100154045ustar00rootroot00000000000000camping-3.2.6/extras/rdoc/generator/000077500000000000000000000000001465547410100173725ustar00rootroot00000000000000camping-3.2.6/extras/rdoc/generator/singledarkfish.rb000066400000000000000000000135431465547410100227220ustar00rootroot00000000000000#!ruby # vim: noet ts=2 sts=8 sw=2 require 'rubygems' gem 'rdoc', '>= 2.4' require 'pp' require 'pathname' require 'fileutils' require 'erb' require 'yaml' require 'rdoc/rdoc' require 'rdoc/generator' require 'rdoc/generator/markup' require 'uri' # # Darkfish RDoc HTML Generator # # $Id: darkfish.rb 52 2009-01-07 02:08:11Z deveiant $ # # == Author/s # * Michael Granger (ged@FaerieMUD.org) # # == Contributors # * Mahlon E. Smith (mahlon@martini.nu) # * Eric Hodel (drbrain@segment7.net) # # == License # # Copyright (c) 2007, 2008, Michael Granger. All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions are met: # # * Redistributions of source code must retain the above copyright notice, # this list of conditions and the following disclaimer. # # * Redistributions in binary form must reproduce the above copyright notice, # this list of conditions and the following disclaimer in the documentation # and/or other materials provided with the distribution. # # * Neither the name of the author/s, nor the names of the project's # contributors may be used to endorse or promote products derived from this # software without specific prior written permission. # # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" # AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE # DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE # FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL # DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR # SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER # CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, # OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. # class RDoc::Generator::SingleDarkfish < RDoc::Generator::Darkfish RDoc::RDoc.add_generator( self ) RDoc::Context.instance_eval do org = instance_method(:http_url) define_method(:http_url) do |prefix| if RDoc::Generator::SingleDarkfish.current? prefix + full_name.gsub("::", '-') else org.bind(self).call(prefix) end end end RDoc::AnyMethod.instance_eval do org = instance_method(:path) define_method(:path) do if RDoc::Generator::SingleDarkfish.current? "/api.html##{@aref}" else org.bind(self).call end end end def self.current? RDoc::RDoc.current.generator.class.ancestors.include?(self) end ################################################################# ### I N S T A N C E M E T H O D S ################################################################# ### Initialize a few instance variables before we start def initialize( options ) @options = options @options.diagram = false template = @options.template || 'darkfish' template_dir = $LOAD_PATH.map do |path| File.join path, GENERATOR_DIR, 'template', template end.find do |dir| File.directory? dir end raise RDoc::Error, "could not find template #{template.inspect}" unless template_dir @template_dir = Pathname.new File.expand_path(template_dir) @basedir = Pathname.pwd.expand_path end ###### public ###### def class_dir '/api.html#class-' end def template(name) "#{name}.rhtml" end ### Build the initial indices and output objects ### based on an array of TopLevel objects containing ### the extracted information. def generate( top_levels ) @outputdir = Pathname.new( @options.op_dir ).expand_path( @basedir ) @files = top_levels.sort @classes = RDoc::TopLevel.all_classes_and_modules.sort @methods = @classes.map { |m| m.method_list }.flatten.sort @modsort = get_sorted_module_list( @classes ) # Now actually write the output write_style_sheet generate_thing(:readme, 'index.html') generate_thing(:reference, 'api.html') generate_thing(:toc, 'book.html') generate_book rescue StandardError => err debug_msg "%s: %s\n %s" % [ err.class.name, err.message, err.backtrace.join("\n ") ] raise end def generate_book chapters.each do |file| templatefile = @template_dir + template(:page) outfile = @basedir + @options.op_dir + file.path rel_prefix = @outputdir.relative_path_from(outfile.dirname) render_template(templatefile, binding, outfile) end end def generate_thing(name, to) debug_msg "Rendering #{name}..." templatefile = @template_dir + template(name) outfile = @basedir + @options.op_dir + to rel_prefix = @outputdir.relative_path_from(outfile.dirname) FileUtils.mkdir_p(File.dirname(outfile)) render_template(templatefile, binding, outfile) end def methods_for(klass) klass.methods_by_type.each do |type, visibilities| next if visibilities.empty? visibilities.each do |visibility, methods| next if methods.empty? methods.each do |method| yield method, type, visibility end end end end ## For book.rhtml def chapters @chapters ||= @files.select do |file| next unless file.full_name =~ /^book\// (class << file; self; end).class_eval { attr_accessor :title, :content, :toc, :id } file.toc = [] file.content = file.description file.title = file.content[%r{

(.*?)

}, 1] file.content.gsub!(%r{

(.*?)

}) do |match| arr = [make_id($1), $1] file.toc << arr '

%s

' % arr end true end end def make_id(title) title.downcase.gsub(/\s/, '-').gsub(/[^\w-]+/, '') end end # Roc::Generator::SingleDarkfish # :stopdoc:camping-3.2.6/extras/rdoc/generator/template/000077500000000000000000000000001465547410100212055ustar00rootroot00000000000000camping-3.2.6/extras/rdoc/generator/template/flipbook/000077500000000000000000000000001465547410100230125ustar00rootroot00000000000000camping-3.2.6/extras/rdoc/generator/template/flipbook/.document000066400000000000000000000000001465547410100246170ustar00rootroot00000000000000camping-3.2.6/extras/rdoc/generator/template/flipbook/images/000077500000000000000000000000001465547410100242575ustar00rootroot00000000000000camping-3.2.6/extras/rdoc/generator/template/flipbook/images/Camping.gif000066400000000000000000000247411465547410100263340ustar00rootroot00000000000000GIF89a3f3333f333ff3fffff3f3f̙3f3333f3333333333f3333333f3f33ff3f3f3f3333f3333333f3̙333333f333ff3ffffff3f33f3ff3f3f3ffff3fffffffffff3fffffff3fff̙ffff3fffff3f̙3333f33̙3ff3ffff̙f3f̙3f̙̙3f̙3f3333f333ff3fffff̙̙3̙f̙̙̙3f̙3f3f3333f333ff3fffff3f3f̙3fM!, H*\ȰÇ#JHŋ3jȱǏ? TG$L@\2:{`~g+5|BIM, H = d ўR 4ui =4yB2L3ZaʊCΚ Ī"lʓO)RLMbJV R 1TM*K{3?eRn-hN m[hp@Y-&\4ĩ"=Zkd%fpQ^ر%{7VȝG[57y·֭t]g=U!Wy֜'x%('\pvU^T_,&X{x\3`7`%X"ȕZbjDvVy65,8)W\rX%qФVuML |\) +g\Msz]eZF,b^j`p2:5W d M [[{<Te_iy'䊄h̀Ҝ يuWUkY9"˽9%4Zq=byN:nRɊJحRYa]ZԲG|Ι颜!VLzSIxXضT+b+&ն)cECUr( dYY{@wp{ rma RWx |tqX<k Mʘ/iLR=zwc^l+2A]-0C3u 4D1,Wl=),_ \)}ԹGmrl{E3 R4М}^\1tWHxX}dsJxyy[y .M 4 ^,93D92'2Mr0ⴰwUOHka^Qx1]_9 dK9 8ʆ?ͬfWV\P1ݮTEYL 2MZp_Pb#w6,4laZ\9ax`=4=R)(X! 9|ADSu@,S[v<غVr_$ 1<-Ӹ3(TUCE1Myz. 30 ix?%kC~^[d5agH3GIx/b\J2f'<p Ke$׶2RE" :q6t*AP'1aA\<~kVo:Otu/"\_m IAIK:q20l*Y({" kyTekd̨m.s)/%E Fh%*JT~gY/|Dmebqn 1UIu Y*[1CΠ6|7+8@ݰ/lj:xs5C5bg4SS-Dw_KIXSD{4NٙZfx7?9ռԌP͙ zh!Mp3E$pPNM+xIӞ. qی8E~ӤiydB 'k e uy=BWW8gh꾉O Kw 2PS 1}b/"u:Z v S _mf6D11"P'k@ 3 d&5׃ȲQ%>1q;E" Pe~U'=ߢ06@]S-<1*w;%R +qS!b>tc&#"{XDoh}Q}+/!,Wp XW%Ȳ~!r lStrMN!?EGP1"kq xw! 7,pNӐ Ry7 1UyqȦ/"B [.I:+%}"i!W C)'#q+P+MvVUBʐw)lsVӎB&PQJɱt!W!7H A$k}[Y ҃zɓ!G)D j ywǕjl`+1, ;=ezUWY9!FqֱW@VcDOyA K8<ɆB9} E2'XO &DͶriU!ҕjz4=hR),]\]]`1Y}UrQaw9}_w;5bvQd1%w=hEtnT-vh7xq^wLwyC2\0٢+O`[#S " >T/rVڄpCJQv~8hWRфh!yaé/1i21/| vM1($΂ʡ0B. d oF  "q;@!eaUИp\90\ I-!,P`P0TA' gau%)"J P!gTJiD$,y!%b61ࢋR!+P5%2{UyELאؘV,4AWQxNd5|]VOeda N$UǮ\m<܍ܩpI˴Zfm+,X$ЩVӨ RynTrc*WhT*]\  I R<,Kř,ɍy<";qv p+2bY}ʝ:lí3ZV!\ǩk0j[l7XK1];|1DI!"ݫR Jtu- q}Y1UNP1<X|$2ɟ]l=lmq5C #cUV JՃ,cڏ[-Kzgz=ɱ ĻΏYL]ØÂx)\+eP1)`]{,@L՜>dmNVinZZMnPP:>ɝ>ɾ>M~ͬu+U".QT=,ɻ^+$pI&=,+@vZ=Φlp4;0m+Z,O Q! QJ^)<Ėlӎ;eDm>ʟE( H߿߭LY2ݔ-+P$PIn q!r!'G 5O@o+T S~/1CoT{Y<%n5 a,!"pm+RwiM %|N_hM/ ckJl^5 nN;{1OP$O1. } )`2PØ~ANU'/+N bJA+2p  A++ Xd0FD+XX +(qࡂ81"'1|Rb*K,kXat^|إ v G+,d!PrYU^e.e<&jY (|C-`Mv;MӢ&Lۉ?T('=@(zD9b" (V,%2 ;p),4V:(%) ChZ7Vj" :zС$K=ZJDQJӯ"V@YM.`.0RaHУ=#'*z(kR*($vB" 2q& (22!SNQ=dH@T/Qղh*i'+B*PEVRSM{&O {H .b,`9 TFز4h1l=*z)1T+V)Nk7L0hRkJK+X9v-$hAl* bZ /sIH9:¾hp'= 8dͪ@&PD*hfA@VLXLY֎\Į@ܒ,FGl k""SE!B 4i pbU@*%K15۬{:H$Wg 8i3Pb4o 踘x*֣@c(wdžokBd(h"Vb(FkA^C4=M+2I{&(%$! ÚApXaENB:"͂Pp֖!ѰBnR(y觕V+~UA"3M;j!ka$F0 Q#†ltT*K% O#^c7<_lcC\!h8vY`P"P M;jb:eS3a8≌X!c{C)&z@,1(E 2J#, dZujEE2Ce/yip2c\36ǂVr$Qr*V|OI QL D4䰀WO,X eA<d"p1NJDYA9ل $#hP: j$B&~*A"sHldW5bVXFD ˏ4X%@䲊* ˾hd$j:R){PV̢*}M30cvI&k*(<$aˋh Mh]iV$>&&RPj2Ft8WQqh8S.H rtLȝ,2bg($9*V(@t" a 9\GV RRQ~WlAfE͊VBOjAL¼(!y\(#ǫV qD[U!d@50"gM0d6:r 5/T2ѠgxRxN Q[)yҢjT61X8Gn:O|W? Ԟ^h_<,$-7J#1j7+!…'+deRC_qg 'jkega-Y*IZ3 * E0 )qrB؂ Ѐ 4D)N:Nh@#ǂFX Ys`aXt 2B;|I\CV( eLߧ9yE\'D=3JѐFo~JZa )NV./ ސ&$*Jºc)H=LE'vTPz*Jq)tnؚ:MdN֭.O A?"U\d)..-3&wpbwU[vK zݩkU5!RgE {]VA {/IGX'^"]m>7QN ua b`~mIQ%(`Mɓqp>Ai[жxBV06DNjE(/ZΊP,,ejdz1Ƚe^ ~TPs~pC-+4cW{>lck3m>896p.IyX x+⃂< hSC֨@l! 0 (˾! ¸+?q (@49`/:pMb<A)كQj =Ïh8ؗ!@5e5$$:#@ 8*9=@2۔Z<1U5,2tY< \ȉwZ!鉠=2 E##y|A8zQRH;Y4cDȄ Ӽ,يCAF\'Pq) PE^ ۉ#2(L "PA؍1:@dA*;љP U,+>4YL9((G U܏B>qpDEZ39_dܙ+>%ix 8V $ DE'xX#+ bMA O9QI+@2F+=.P2I+z;SPS"xA=Њ4>ͻUBJ =@G -UF|?H!1,JbbM6kݝO>۸<$X12Ph; \EX+VxSM :V׀<) SX ۛ?#@'֡K 2R⓶М*5R YPMxA=Lx'Vİ̡\K#! Ra )\[A3<LU0&j4 ya͐aD+`:TkHpqCQ ndP_AO ƿILt |V`=xъk܍żx@婇~<&H8O 6Gm0 S8SӐՊ( [ٸ@՟U X|[y VHXU Q MҒW,) =@ůLH$鳍ZsƑ4S;+ħE$yNI5[x]-sSg-`ҌÝ#zOL\SA_y[M)f!@Uf:}AUU[߫URA܅QŒX#tVL糘+ɒZC ߠjcm:_T~/p.+Rɡ (jj>::ʫЋnk`LLȄkNl^lnlR;camping-3.2.6/extras/rdoc/generator/template/flipbook/images/loadingAnimation.gif000066400000000000000000000133761465547410100302350ustar00rootroot00000000000000GIF89a ! NETSCAPE2.0! ,  @Rihlp,tm#6N+rrD4h@FCjz]Lj]﹬R3-H$wPy |KI\K Q\]PI ~$ ~ J %:`@ XP@AO 2|(D**HE}lF=$Ƌ@\9L J()? ``C=4RO@>}5TDU^=UQH~Xe֫:|AoG4Q]V;ޙ  jUzҧ]$jQM ʆķ7721@ܒDH;܋%ЅqۛXMgNάgMWz!`dѥ]uja}a ݸ6;5լDzTh,5~z~@y[:3M7gG]q=@c5 4u흃5__9_AMtB\K ⵘ}" @lDiQu+baq&{#'LHjz"$& $B! ,  EI@*PR*;ò[Ll7DҎ%GI0)mIԲ`I}>ă}-RwFu}y|O(p*"rt{RA>( ^( "4 333..Чɞ v.*o 3u zz( D.z Ha рP*6=%8A%ϤH]JQ&2Lz녆Aɔ>50j( ]T.s *I@m:nTx^7}v(Hy!8/ 4^ֹGi`zA"?l8]'STҧX*3@XMS<@蜮-./Ay5|Uv۬ϰVr4<A:\4bmdvi{˿ӛW'7naq{"8V_u_^2_[koTpbp :M skx_"Vؓ! ,  %R4 yԬԢ\K٩l'#QZ0ՠVTyïe]U4f!Pc0zkW+_ ^ў`v"Ŋa@x:@O,ȬtK5zsRtH5y3ʥ>yz=3[/Z|&x @7nGdn B^|{ZCN3Z&$p{f;^C}Co %! ,  %$5,%:k.]#> B"ZRp]2Shk9E-e<ͩkGj6-twsz|mOQtix41 ) U"  BQ B)# B+u  )ێ #" pg߿=QǍPa Q*֑Xb<88l$Q,? y2=0յT#rΗ5 9`jLmgTJ2MWy6jukիKy0iP0JCg&(`oq]혗']u}C*  ^\sxaGtKpeÈa@jvlgVDz$ͣQ>mb16}4 @l`ѪzvsGt 2y֐/V+SNJ=^"+Y  09ڛ>0}Lj`abqO '8^Xd|5 [ahWY7֑W6!i!qHX&(@3&B! ,  Q$@RS" k; S]3X'+ζS tUik `m0c]wldt$v}k~zq,lryux?)B ; 00;+ ­G 0-g+ .z%$z +l(jx9hNt<; 8O7yxH#|bH$ѼLipȅѐ 2"-A(P$tj@{s(W$( =!u5(jmn wmĸoA@w/d1+:'l1f#|,z20Hg@1}թٯ[3S+|7Uǃ.{ P卵0ʶc=+PL03:~p'v{On<~)@uP6 k (àK'vK V`I9(ZLG!! ,  PeRN*',Ԣv*o<bKs M3k>oR sAE1fm):kvvM{wl rtqPm|uF~?W a3& A66 - 3- .,6o 3/h-o 'dzz&(CGo߉Oa9ќD= kOOEuj(ǑLʏTIR&K.H= ttI£B0E:qjԪPhY‰ ʾBFdÎT0@ܝK!Aݽ_aw9hP(!Ѣ,fʓmZ,rƝ[|.1鑣wZh%.:^mRi綽7V޿}c\u;o>oüu+],Ebd]/\.*h3;߯~ |HoXg6] !! ,  E9$#%5(,Ԓr(i0.݊`lXtD%ydJ6H^.pl[k=.˿(>Sx~gypv}lNtjx{e> ( ~~ -- ,-(Ĺ ' e -/p(u '" u GN šL1:I<9uG '9cɕ-Q$L'lD7UTTׄ tѦBU@%JJŪ)W?LSY bZvmމБ]xF0I{VXg|-BfE#|.@4OP4u8S g ֥_n;Ө՞(բQ)wǮ<.(ʳ2?eG|LՅ"x/-/y+,p=E1 dQٷ~2Kꔛ$r `m*YsC zFn ak}nnI#Q! ,  %R4Ԝ"CA,(~ں8׼دDA֐2hs݌+p]ˤObKw8^>uz`H~htkv"xm|1Z, -#51"1''[#, ,}, ' p#h1 w a"w #p"b1V-akcB)P@;]XqČ#N-A"߲SyrL4eMʚph\@eq/o D2(𯘳q s:gu@Fu+QAUӯZ#xbR4vG1 Mɻ;8FP`B۩3he11O~ ˟>z(C3A=\Z-ڇAa-qiSRYmG(1;Ž=dtqB[! ,  %dihlp,40T:Ԕn琘9␄\< P)JZ+^f(-N Ǥ4ant#o~$z|u#Svx@"'% #m# S%  $%§ #ְͽ$& A"%  $#H݃'/9BH EdOC)Lq#Fo gBbDIʤC'CRg̙G 4^M=F\dd3 X堙Kl٣̊@@T?}IVUT+ol|gr TeluӭޒvG]l<ݑsg_D )4EQ_i)Pi#!h Da %.@qMOQ `pnyHWC"$s* ]hp$V 8 $(the section + the method body) function m(name) { var base = $(name); return base.parent().add(base.next()); } // #class-something -> $(the section below) function s(name) { return $(name).next(); } $('.source-link a').click(function() { var link = $(this); var code = link.parent().next(); if (link.text() == 'show source') { code.show(); link.text('hide source'); } else { code.hide(); link.text('show source'); } return false; }); if ($('.ref')[0]) { $('.mod').hide(); $('.method').hide(); var hash = window.location.hash.replace(/--$/, ''); if (hash.substring(0, 2) == "#M") { // Show the method and the section m(hash).show(); } else if (hash.substring(0, 7) == "#class-") { // Show the section. s(hash).show(); } else { // Show the first section. s("h2:first").show(); } // We need to scroll! if (hash != window.location.hash) { window.location.hash = hash; } $('a[href*="#class-"]').click(function() { var link = this.href; var id = link.substring(link.indexOf("#")); if ($(this).parent().is("h2")) { // We're in a headline s(id).toggle(); window.location.hash = id + "--"; return false; } else { // A normal link s(id).show(); } }); $('a[href*="#M"]').click(function() { var link = this.href; var id = link.substring(link.indexOf("#")); if ($(this).parent().is("h4")) { // We're in a headline window.location.hash = id + "--"; s(id).toggle(); return false; } else { // Normal link m(id).show(); } }); } });camping-3.2.6/extras/rdoc/generator/template/flipbook/js/jquery.js000066400000000000000000001547341465547410100253210ustar00rootroot00000000000000/* * jQuery 1.2.6 - New Wave Javascript * * Copyright (c) 2008 John Resig (jquery.com) * Dual licensed under the MIT (MIT-LICENSE.txt) * and GPL (GPL-LICENSE.txt) licenses. * * $Date: 2008-09-25 09:50:52 -0700 (Thu, 25 Sep 2008) $ * $Rev: 38 $ */ (function(){var _jQuery=window.jQuery,_$=window.$;var jQuery=window.jQuery=window.$=function(selector,context){return new jQuery.fn.init(selector,context);};var quickExpr=/^[^<]*(<(.|\s)+>)[^>]*$|^#(\w+)$/,isSimple=/^.[^:#\[\.]*$/,undefined;jQuery.fn=jQuery.prototype={init:function(selector,context){selector=selector||document;if(selector.nodeType){this[0]=selector;this.length=1;return this;}if(typeof selector=="string"){var match=quickExpr.exec(selector);if(match&&(match[1]||!context)){if(match[1])selector=jQuery.clean([match[1]],context);else{var elem=document.getElementById(match[3]);if(elem){if(elem.id!=match[3])return jQuery().find(selector);return jQuery(elem);}selector=[];}}else return jQuery(context).find(selector);}else if(jQuery.isFunction(selector))return jQuery(document)[jQuery.fn.ready?"ready":"load"](selector);return this.setArray(jQuery.makeArray(selector));},jquery:"1.2.6",size:function(){return this.length;},length:0,get:function(num){return num==undefined?jQuery.makeArray(this):this[num];},pushStack:function(elems){var ret=jQuery(elems);ret.prevObject=this;return ret;},setArray:function(elems){this.length=0;Array.prototype.push.apply(this,elems);return this;},each:function(callback,args){return jQuery.each(this,callback,args);},index:function(elem){var ret=-1;return jQuery.inArray(elem&&elem.jquery?elem[0]:elem,this);},attr:function(name,value,type){var options=name;if(name.constructor==String)if(value===undefined)return this[0]&&jQuery[type||"attr"](this[0],name);else{options={};options[name]=value;}return this.each(function(i){for(name in options)jQuery.attr(type?this.style:this,name,jQuery.prop(this,options[name],type,i,name));});},css:function(key,value){if((key=='width'||key=='height')&&parseFloat(value)<0)value=undefined;return this.attr(key,value,"curCSS");},text:function(text){if(typeof text!="object"&&text!=null)return this.empty().append((this[0]&&this[0].ownerDocument||document).createTextNode(text));var ret="";jQuery.each(text||this,function(){jQuery.each(this.childNodes,function(){if(this.nodeType!=8)ret+=this.nodeType!=1?this.nodeValue:jQuery.fn.text([this]);});});return ret;},wrapAll:function(html){if(this[0])jQuery(html,this[0].ownerDocument).clone().insertBefore(this[0]).map(function(){var elem=this;while(elem.firstChild)elem=elem.firstChild;return elem;}).append(this);return this;},wrapInner:function(html){return this.each(function(){jQuery(this).contents().wrapAll(html);});},wrap:function(html){return this.each(function(){jQuery(this).wrapAll(html);});},append:function(){return this.domManip(arguments,true,false,function(elem){if(this.nodeType==1)this.appendChild(elem);});},prepend:function(){return this.domManip(arguments,true,true,function(elem){if(this.nodeType==1)this.insertBefore(elem,this.firstChild);});},before:function(){return this.domManip(arguments,false,false,function(elem){this.parentNode.insertBefore(elem,this);});},after:function(){return this.domManip(arguments,false,true,function(elem){this.parentNode.insertBefore(elem,this.nextSibling);});},end:function(){return this.prevObject||jQuery([]);},find:function(selector){var elems=jQuery.map(this,function(elem){return jQuery.find(selector,elem);});return this.pushStack(/[^+>] [^+>]/.test(selector)||selector.indexOf("..")>-1?jQuery.unique(elems):elems);},clone:function(events){var ret=this.map(function(){if(jQuery.browser.msie&&!jQuery.isXMLDoc(this)){var clone=this.cloneNode(true),container=document.createElement("div");container.appendChild(clone);return jQuery.clean([container.innerHTML])[0];}else return this.cloneNode(true);});var clone=ret.find("*").andSelf().each(function(){if(this[expando]!=undefined)this[expando]=null;});if(events===true)this.find("*").andSelf().each(function(i){if(this.nodeType==3)return;var events=jQuery.data(this,"events");for(var type in events)for(var handler in events[type])jQuery.event.add(clone[i],type,events[type][handler],events[type][handler].data);});return ret;},filter:function(selector){return this.pushStack(jQuery.isFunction(selector)&&jQuery.grep(this,function(elem,i){return selector.call(elem,i);})||jQuery.multiFilter(selector,this));},not:function(selector){if(selector.constructor==String)if(isSimple.test(selector))return this.pushStack(jQuery.multiFilter(selector,this,true));else selector=jQuery.multiFilter(selector,this);var isArrayLike=selector.length&&selector[selector.length-1]!==undefined&&!selector.nodeType;return this.filter(function(){return isArrayLike?jQuery.inArray(this,selector)<0:this!=selector;});},add:function(selector){return this.pushStack(jQuery.unique(jQuery.merge(this.get(),typeof selector=='string'?jQuery(selector):jQuery.makeArray(selector))));},is:function(selector){return!!selector&&jQuery.multiFilter(selector,this).length>0;},hasClass:function(selector){return this.is("."+selector);},val:function(value){if(value==undefined){if(this.length){var elem=this[0];if(jQuery.nodeName(elem,"select")){var index=elem.selectedIndex,values=[],options=elem.options,one=elem.type=="select-one";if(index<0)return null;for(var i=one?index:0,max=one?index+1:options.length;i=0||jQuery.inArray(this.name,value)>=0);else if(jQuery.nodeName(this,"select")){var values=jQuery.makeArray(value);jQuery("option",this).each(function(){this.selected=(jQuery.inArray(this.value,values)>=0||jQuery.inArray(this.text,values)>=0);});if(!values.length)this.selectedIndex=-1;}else this.value=value;});},html:function(value){return value==undefined?(this[0]?this[0].innerHTML:null):this.empty().append(value);},replaceWith:function(value){return this.after(value).remove();},eq:function(i){return this.slice(i,i+1);},slice:function(){return this.pushStack(Array.prototype.slice.apply(this,arguments));},map:function(callback){return this.pushStack(jQuery.map(this,function(elem,i){return callback.call(elem,i,elem);}));},andSelf:function(){return this.add(this.prevObject);},data:function(key,value){var parts=key.split(".");parts[1]=parts[1]?"."+parts[1]:"";if(value===undefined){var data=this.triggerHandler("getData"+parts[1]+"!",[parts[0]]);if(data===undefined&&this.length)data=jQuery.data(this[0],key);return data===undefined&&parts[1]?this.data(parts[0]):data;}else return this.trigger("setData"+parts[1]+"!",[parts[0],value]).each(function(){jQuery.data(this,key,value);});},removeData:function(key){return this.each(function(){jQuery.removeData(this,key);});},domManip:function(args,table,reverse,callback){var clone=this.length>1,elems;return this.each(function(){if(!elems){elems=jQuery.clean(args,this.ownerDocument);if(reverse)elems.reverse();}var obj=this;if(table&&jQuery.nodeName(this,"table")&&jQuery.nodeName(elems[0],"tr"))obj=this.getElementsByTagName("tbody")[0]||this.appendChild(this.ownerDocument.createElement("tbody"));var scripts=jQuery([]);jQuery.each(elems,function(){var elem=clone?jQuery(this).clone(true)[0]:this;if(jQuery.nodeName(elem,"script"))scripts=scripts.add(elem);else{if(elem.nodeType==1)scripts=scripts.add(jQuery("script",elem).remove());callback.call(obj,elem);}});scripts.each(evalScript);});}};jQuery.fn.init.prototype=jQuery.fn;function evalScript(i,elem){if(elem.src)jQuery.ajax({url:elem.src,async:false,dataType:"script"});else jQuery.globalEval(elem.text||elem.textContent||elem.innerHTML||"");if(elem.parentNode)elem.parentNode.removeChild(elem);}function now(){return+new Date;}jQuery.extend=jQuery.fn.extend=function(){var target=arguments[0]||{},i=1,length=arguments.length,deep=false,options;if(target.constructor==Boolean){deep=target;target=arguments[1]||{};i=2;}if(typeof target!="object"&&typeof target!="function")target={};if(length==i){target=this;--i;}for(;i-1;}},swap:function(elem,options,callback){var old={};for(var name in options){old[name]=elem.style[name];elem.style[name]=options[name];}callback.call(elem);for(var name in options)elem.style[name]=old[name];},css:function(elem,name,force){if(name=="width"||name=="height"){var val,props={position:"absolute",visibility:"hidden",display:"block"},which=name=="width"?["Left","Right"]:["Top","Bottom"];function getWH(){val=name=="width"?elem.offsetWidth:elem.offsetHeight;var padding=0,border=0;jQuery.each(which,function(){padding+=parseFloat(jQuery.curCSS(elem,"padding"+this,true))||0;border+=parseFloat(jQuery.curCSS(elem,"border"+this+"Width",true))||0;});val-=Math.round(padding+border);}if(jQuery(elem).is(":visible"))getWH();else jQuery.swap(elem,props,getWH);return Math.max(0,val);}return jQuery.curCSS(elem,name,force);},curCSS:function(elem,name,force){var ret,style=elem.style;function color(elem){if(!jQuery.browser.safari)return false;var ret=defaultView.getComputedStyle(elem,null);return!ret||ret.getPropertyValue("color")=="";}if(name=="opacity"&&jQuery.browser.msie){ret=jQuery.attr(style,"opacity");return ret==""?"1":ret;}if(jQuery.browser.opera&&name=="display"){var save=style.outline;style.outline="0 solid black";style.outline=save;}if(name.match(/float/i))name=styleFloat;if(!force&&style&&style[name])ret=style[name];else if(defaultView.getComputedStyle){if(name.match(/float/i))name="float";name=name.replace(/([A-Z])/g,"-$1").toLowerCase();var computedStyle=defaultView.getComputedStyle(elem,null);if(computedStyle&&!color(elem))ret=computedStyle.getPropertyValue(name);else{var swap=[],stack=[],a=elem,i=0;for(;a&&color(a);a=a.parentNode)stack.unshift(a);for(;i]*?)\/>/g,function(all,front,tag){return tag.match(/^(abbr|br|col|img|input|link|meta|param|hr|area|embed)$/i)?all:front+">";});var tags=jQuery.trim(elem).toLowerCase(),div=context.createElement("div");var wrap=!tags.indexOf("",""]||!tags.indexOf("",""]||tags.match(/^<(thead|tbody|tfoot|colg|cap)/)&&[1,"","
"]||!tags.indexOf("",""]||(!tags.indexOf("",""]||!tags.indexOf("",""]||jQuery.browser.msie&&[1,"div
","
"]||[0,"",""];div.innerHTML=wrap[1]+elem+wrap[2];while(wrap[0]--)div=div.lastChild;if(jQuery.browser.msie){var tbody=!tags.indexOf(""&&tags.indexOf("=0;--j)if(jQuery.nodeName(tbody[j],"tbody")&&!tbody[j].childNodes.length)tbody[j].parentNode.removeChild(tbody[j]);if(/^\s/.test(elem))div.insertBefore(context.createTextNode(elem.match(/^\s*/)[0]),div.firstChild);}elem=jQuery.makeArray(div.childNodes);}if(elem.length===0&&(!jQuery.nodeName(elem,"form")&&!jQuery.nodeName(elem,"select")))return;if(elem[0]==undefined||jQuery.nodeName(elem,"form")||elem.options)ret.push(elem);else ret=jQuery.merge(ret,elem);});return ret;},attr:function(elem,name,value){if(!elem||elem.nodeType==3||elem.nodeType==8)return undefined;var notxml=!jQuery.isXMLDoc(elem),set=value!==undefined,msie=jQuery.browser.msie;name=notxml&&jQuery.props[name]||name;if(elem.tagName){var special=/href|src|style/.test(name);if(name=="selected"&&jQuery.browser.safari)elem.parentNode.selectedIndex;if(name in elem&¬xml&&!special){if(set){if(name=="type"&&jQuery.nodeName(elem,"input")&&elem.parentNode)throw"type property can't be changed";elem[name]=value;}if(jQuery.nodeName(elem,"form")&&elem.getAttributeNode(name))return elem.getAttributeNode(name).nodeValue;return elem[name];}if(msie&¬xml&&name=="style")return jQuery.attr(elem.style,"cssText",value);if(set)elem.setAttribute(name,""+value);var attr=msie&¬xml&&special?elem.getAttribute(name,2):elem.getAttribute(name);return attr===null?undefined:attr;}if(msie&&name=="opacity"){if(set){elem.zoom=1;elem.filter=(elem.filter||"").replace(/alpha\([^)]*\)/,"")+(parseInt(value)+''=="NaN"?"":"alpha(opacity="+value*100+")");}return elem.filter&&elem.filter.indexOf("opacity=")>=0?(parseFloat(elem.filter.match(/opacity=([^)]*)/)[1])/100)+'':"";}name=name.replace(/-([a-z])/ig,function(all,letter){return letter.toUpperCase();});if(set)elem[name]=value;return elem[name];},trim:function(text){return(text||"").replace(/^\s+|\s+$/g,"");},makeArray:function(array){var ret=[];if(array!=null){var i=array.length;if(i==null||array.split||array.setInterval||array.call)ret[0]=array;else while(i)ret[--i]=array[i];}return ret;},inArray:function(elem,array){for(var i=0,length=array.length;i*",this).remove();while(this.firstChild)this.removeChild(this.firstChild);}},function(name,fn){jQuery.fn[name]=function(){return this.each(fn,arguments);};});jQuery.each(["Height","Width"],function(i,name){var type=name.toLowerCase();jQuery.fn[type]=function(size){return this[0]==window?jQuery.browser.opera&&document.body["client"+name]||jQuery.browser.safari&&window["inner"+name]||document.compatMode=="CSS1Compat"&&document.documentElement["client"+name]||document.body["client"+name]:this[0]==document?Math.max(Math.max(document.body["scroll"+name],document.documentElement["scroll"+name]),Math.max(document.body["offset"+name],document.documentElement["offset"+name])):size==undefined?(this.length?jQuery.css(this[0],type):null):this.css(type,size.constructor==String?size:size+"px");};});function num(elem,prop){return elem[0]&&parseInt(jQuery.curCSS(elem[0],prop,true),10)||0;}var chars=jQuery.browser.safari&&parseInt(jQuery.browser.version)<417?"(?:[\\w*_-]|\\\\.)":"(?:[\\w\u0128-\uFFFF*_-]|\\\\.)",quickChild=new RegExp("^>\\s*("+chars+"+)"),quickID=new RegExp("^("+chars+"+)(#)("+chars+"+)"),quickClass=new RegExp("^([#.]?)("+chars+"*)");jQuery.extend({expr:{"":function(a,i,m){return m[2]=="*"||jQuery.nodeName(a,m[2]);},"#":function(a,i,m){return a.getAttribute("id")==m[2];},":":{lt:function(a,i,m){return im[3]-0;},nth:function(a,i,m){return m[3]-0==i;},eq:function(a,i,m){return m[3]-0==i;},first:function(a,i){return i==0;},last:function(a,i,m,r){return i==r.length-1;},even:function(a,i){return i%2==0;},odd:function(a,i){return i%2;},"first-child":function(a){return a.parentNode.getElementsByTagName("*")[0]==a;},"last-child":function(a){return jQuery.nth(a.parentNode.lastChild,1,"previousSibling")==a;},"only-child":function(a){return!jQuery.nth(a.parentNode.lastChild,2,"previousSibling");},parent:function(a){return a.firstChild;},empty:function(a){return!a.firstChild;},contains:function(a,i,m){return(a.textContent||a.innerText||jQuery(a).text()||"").indexOf(m[3])>=0;},visible:function(a){return"hidden"!=a.type&&jQuery.css(a,"display")!="none"&&jQuery.css(a,"visibility")!="hidden";},hidden:function(a){return"hidden"==a.type||jQuery.css(a,"display")=="none"||jQuery.css(a,"visibility")=="hidden";},enabled:function(a){return!a.disabled;},disabled:function(a){return a.disabled;},checked:function(a){return a.checked;},selected:function(a){return a.selected||jQuery.attr(a,"selected");},text:function(a){return"text"==a.type;},radio:function(a){return"radio"==a.type;},checkbox:function(a){return"checkbox"==a.type;},file:function(a){return"file"==a.type;},password:function(a){return"password"==a.type;},submit:function(a){return"submit"==a.type;},image:function(a){return"image"==a.type;},reset:function(a){return"reset"==a.type;},button:function(a){return"button"==a.type||jQuery.nodeName(a,"button");},input:function(a){return/input|select|textarea|button/i.test(a.nodeName);},has:function(a,i,m){return jQuery.find(m[3],a).length;},header:function(a){return/h\d/i.test(a.nodeName);},animated:function(a){return jQuery.grep(jQuery.timers,function(fn){return a==fn.elem;}).length;}}},parse:[/^(\[) *@?([\w-]+) *([!*$^~=]*) *('?"?)(.*?)\4 *\]/,/^(:)([\w-]+)\("?'?(.*?(\(.*?\))?[^(]*?)"?'?\)/,new RegExp("^([:.#]*)("+chars+"+)")],multiFilter:function(expr,elems,not){var old,cur=[];while(expr&&expr!=old){old=expr;var f=jQuery.filter(expr,elems,not);expr=f.t.replace(/^\s*,\s*/,"");cur=not?elems=f.r:jQuery.merge(cur,f.r);}return cur;},find:function(t,context){if(typeof t!="string")return[t];if(context&&context.nodeType!=1&&context.nodeType!=9)return[];context=context||document;var ret=[context],done=[],last,nodeName;while(t&&last!=t){var r=[];last=t;t=jQuery.trim(t);var foundToken=false,re=quickChild,m=re.exec(t);if(m){nodeName=m[1].toUpperCase();for(var i=0;ret[i];i++)for(var c=ret[i].firstChild;c;c=c.nextSibling)if(c.nodeType==1&&(nodeName=="*"||c.nodeName.toUpperCase()==nodeName))r.push(c);ret=r;t=t.replace(re,"");if(t.indexOf(" ")==0)continue;foundToken=true;}else{re=/^([>+~])\s*(\w*)/i;if((m=re.exec(t))!=null){r=[];var merge={};nodeName=m[2].toUpperCase();m=m[1];for(var j=0,rl=ret.length;j=0;if(!not&&pass||not&&!pass)tmp.push(r[i]);}return tmp;},filter:function(t,r,not){var last;while(t&&t!=last){last=t;var p=jQuery.parse,m;for(var i=0;p[i];i++){m=p[i].exec(t);if(m){t=t.substring(m[0].length);m[2]=m[2].replace(/\\/g,"");break;}}if(!m)break;if(m[1]==":"&&m[2]=="not")r=isSimple.test(m[3])?jQuery.filter(m[3],r,true).r:jQuery(r).not(m[3]);else if(m[1]==".")r=jQuery.classFilter(r,m[2],not);else if(m[1]=="["){var tmp=[],type=m[3];for(var i=0,rl=r.length;i=0)^not)tmp.push(a);}r=tmp;}else if(m[1]==":"&&m[2]=="nth-child"){var merge={},tmp=[],test=/(-?)(\d*)n((?:\+|-)?\d*)/.exec(m[3]=="even"&&"2n"||m[3]=="odd"&&"2n+1"||!/\D/.test(m[3])&&"0n+"+m[3]||m[3]),first=(test[1]+(test[2]||1))-0,last=test[3]-0;for(var i=0,rl=r.length;i=0)add=true;if(add^not)tmp.push(node);}r=tmp;}else{var fn=jQuery.expr[m[1]];if(typeof fn=="object")fn=fn[m[2]];if(typeof fn=="string")fn=eval("false||function(a,i){return "+fn+";}");r=jQuery.grep(r,function(elem,i){return fn(elem,i,m,r);},not);}}return{r:r,t:t};},dir:function(elem,dir){var matched=[],cur=elem[dir];while(cur&&cur!=document){if(cur.nodeType==1)matched.push(cur);cur=cur[dir];}return matched;},nth:function(cur,result,dir,elem){result=result||1;var num=0;for(;cur;cur=cur[dir])if(cur.nodeType==1&&++num==result)break;return cur;},sibling:function(n,elem){var r=[];for(;n;n=n.nextSibling){if(n.nodeType==1&&n!=elem)r.push(n);}return r;}});jQuery.event={add:function(elem,types,handler,data){if(elem.nodeType==3||elem.nodeType==8)return;if(jQuery.browser.msie&&elem.setInterval)elem=window;if(!handler.guid)handler.guid=this.guid++;if(data!=undefined){var fn=handler;handler=this.proxy(fn,function(){return fn.apply(this,arguments);});handler.data=data;}var events=jQuery.data(elem,"events")||jQuery.data(elem,"events",{}),handle=jQuery.data(elem,"handle")||jQuery.data(elem,"handle",function(){if(typeof jQuery!="undefined"&&!jQuery.event.triggered)return jQuery.event.handle.apply(arguments.callee.elem,arguments);});handle.elem=elem;jQuery.each(types.split(/\s+/),function(index,type){var parts=type.split(".");type=parts[0];handler.type=parts[1];var handlers=events[type];if(!handlers){handlers=events[type]={};if(!jQuery.event.special[type]||jQuery.event.special[type].setup.call(elem)===false){if(elem.addEventListener)elem.addEventListener(type,handle,false);else if(elem.attachEvent)elem.attachEvent("on"+type,handle);}}handlers[handler.guid]=handler;jQuery.event.global[type]=true;});elem=null;},guid:1,global:{},remove:function(elem,types,handler){if(elem.nodeType==3||elem.nodeType==8)return;var events=jQuery.data(elem,"events"),ret,index;if(events){if(types==undefined||(typeof types=="string"&&types.charAt(0)=="."))for(var type in events)this.remove(elem,type+(types||""));else{if(types.type){handler=types.handler;types=types.type;}jQuery.each(types.split(/\s+/),function(index,type){var parts=type.split(".");type=parts[0];if(events[type]){if(handler)delete events[type][handler.guid];else for(handler in events[type])if(!parts[1]||events[type][handler].type==parts[1])delete events[type][handler];for(ret in events[type])break;if(!ret){if(!jQuery.event.special[type]||jQuery.event.special[type].teardown.call(elem)===false){if(elem.removeEventListener)elem.removeEventListener(type,jQuery.data(elem,"handle"),false);else if(elem.detachEvent)elem.detachEvent("on"+type,jQuery.data(elem,"handle"));}ret=null;delete events[type];}}});}for(ret in events)break;if(!ret){var handle=jQuery.data(elem,"handle");if(handle)handle.elem=null;jQuery.removeData(elem,"events");jQuery.removeData(elem,"handle");}}},trigger:function(type,data,elem,donative,extra){data=jQuery.makeArray(data);if(type.indexOf("!")>=0){type=type.slice(0,-1);var exclusive=true;}if(!elem){if(this.global[type])jQuery("*").add([window,document]).trigger(type,data);}else{if(elem.nodeType==3||elem.nodeType==8)return undefined;var val,ret,fn=jQuery.isFunction(elem[type]||null),event=!data[0]||!data[0].preventDefault;if(event){data.unshift({type:type,target:elem,preventDefault:function(){},stopPropagation:function(){},timeStamp:now()});data[0][expando]=true;}data[0].type=type;if(exclusive)data[0].exclusive=true;var handle=jQuery.data(elem,"handle");if(handle)val=handle.apply(elem,data);if((!fn||(jQuery.nodeName(elem,'a')&&type=="click"))&&elem["on"+type]&&elem["on"+type].apply(elem,data)===false)val=false;if(event)data.shift();if(extra&&jQuery.isFunction(extra)){ret=extra.apply(elem,val==null?data:data.concat(val));if(ret!==undefined)val=ret;}if(fn&&donative!==false&&val!==false&&!(jQuery.nodeName(elem,'a')&&type=="click")){this.triggered=true;try{elem[type]();}catch(e){}}this.triggered=false;}return val;},handle:function(event){var val,ret,namespace,all,handlers;event=arguments[0]=jQuery.event.fix(event||window.event);namespace=event.type.split(".");event.type=namespace[0];namespace=namespace[1];all=!namespace&&!event.exclusive;handlers=(jQuery.data(this,"events")||{})[event.type];for(var j in handlers){var handler=handlers[j];if(all||handler.type==namespace){event.handler=handler;event.data=handler.data;ret=handler.apply(this,arguments);if(val!==false)val=ret;if(ret===false){event.preventDefault();event.stopPropagation();}}}return val;},fix:function(event){if(event[expando]==true)return event;var originalEvent=event;event={originalEvent:originalEvent};var props="altKey attrChange attrName bubbles button cancelable charCode clientX clientY ctrlKey currentTarget data detail eventPhase fromElement handler keyCode metaKey newValue originalTarget pageX pageY prevValue relatedNode relatedTarget screenX screenY shiftKey srcElement target timeStamp toElement type view wheelDelta which".split(" ");for(var i=props.length;i;i--)event[props[i]]=originalEvent[props[i]];event[expando]=true;event.preventDefault=function(){if(originalEvent.preventDefault)originalEvent.preventDefault();originalEvent.returnValue=false;};event.stopPropagation=function(){if(originalEvent.stopPropagation)originalEvent.stopPropagation();originalEvent.cancelBubble=true;};event.timeStamp=event.timeStamp||now();if(!event.target)event.target=event.srcElement||document;if(event.target.nodeType==3)event.target=event.target.parentNode;if(!event.relatedTarget&&event.fromElement)event.relatedTarget=event.fromElement==event.target?event.toElement:event.fromElement;if(event.pageX==null&&event.clientX!=null){var doc=document.documentElement,body=document.body;event.pageX=event.clientX+(doc&&doc.scrollLeft||body&&body.scrollLeft||0)-(doc.clientLeft||0);event.pageY=event.clientY+(doc&&doc.scrollTop||body&&body.scrollTop||0)-(doc.clientTop||0);}if(!event.which&&((event.charCode||event.charCode===0)?event.charCode:event.keyCode))event.which=event.charCode||event.keyCode;if(!event.metaKey&&event.ctrlKey)event.metaKey=event.ctrlKey;if(!event.which&&event.button)event.which=(event.button&1?1:(event.button&2?3:(event.button&4?2:0)));return event;},proxy:function(fn,proxy){proxy.guid=fn.guid=fn.guid||proxy.guid||this.guid++;return proxy;},special:{ready:{setup:function(){bindReady();return;},teardown:function(){return;}},mouseenter:{setup:function(){if(jQuery.browser.msie)return false;jQuery(this).bind("mouseover",jQuery.event.special.mouseenter.handler);return true;},teardown:function(){if(jQuery.browser.msie)return false;jQuery(this).unbind("mouseover",jQuery.event.special.mouseenter.handler);return true;},handler:function(event){if(withinElement(event,this))return true;event.type="mouseenter";return jQuery.event.handle.apply(this,arguments);}},mouseleave:{setup:function(){if(jQuery.browser.msie)return false;jQuery(this).bind("mouseout",jQuery.event.special.mouseleave.handler);return true;},teardown:function(){if(jQuery.browser.msie)return false;jQuery(this).unbind("mouseout",jQuery.event.special.mouseleave.handler);return true;},handler:function(event){if(withinElement(event,this))return true;event.type="mouseleave";return jQuery.event.handle.apply(this,arguments);}}}};jQuery.fn.extend({bind:function(type,data,fn){return type=="unload"?this.one(type,data,fn):this.each(function(){jQuery.event.add(this,type,fn||data,fn&&data);});},one:function(type,data,fn){var one=jQuery.event.proxy(fn||data,function(event){jQuery(this).unbind(event,one);return(fn||data).apply(this,arguments);});return this.each(function(){jQuery.event.add(this,type,one,fn&&data);});},unbind:function(type,fn){return this.each(function(){jQuery.event.remove(this,type,fn);});},trigger:function(type,data,fn){return this.each(function(){jQuery.event.trigger(type,data,this,true,fn);});},triggerHandler:function(type,data,fn){return this[0]&&jQuery.event.trigger(type,data,this[0],false,fn);},toggle:function(fn){var args=arguments,i=1;while(i=0){var selector=url.slice(off,url.length);url=url.slice(0,off);}callback=callback||function(){};var type="GET";if(params)if(jQuery.isFunction(params)){callback=params;params=null;}else{params=jQuery.param(params);type="POST";}var self=this;jQuery.ajax({url:url,type:type,dataType:"html",data:params,complete:function(res,status){if(status=="success"||status=="notmodified")self.html(selector?jQuery("
").append(res.responseText.replace(//g,"")).find(selector):res.responseText);self.each(callback,[res.responseText,status,res]);}});return this;},serialize:function(){return jQuery.param(this.serializeArray());},serializeArray:function(){return this.map(function(){return jQuery.nodeName(this,"form")?jQuery.makeArray(this.elements):this;}).filter(function(){return this.name&&!this.disabled&&(this.checked||/select|textarea/i.test(this.nodeName)||/text|hidden|password/i.test(this.type));}).map(function(i,elem){var val=jQuery(this).val();return val==null?null:val.constructor==Array?jQuery.map(val,function(val,i){return{name:elem.name,value:val};}):{name:elem.name,value:val};}).get();}});jQuery.each("ajaxStart,ajaxStop,ajaxComplete,ajaxError,ajaxSuccess,ajaxSend".split(","),function(i,o){jQuery.fn[o]=function(f){return this.bind(o,f);};});var jsc=now();jQuery.extend({get:function(url,data,callback,type){if(jQuery.isFunction(data)){callback=data;data=null;}return jQuery.ajax({type:"GET",url:url,data:data,success:callback,dataType:type});},getScript:function(url,callback){return jQuery.get(url,null,callback,"script");},getJSON:function(url,data,callback){return jQuery.get(url,data,callback,"json");},post:function(url,data,callback,type){if(jQuery.isFunction(data)){callback=data;data={};}return jQuery.ajax({type:"POST",url:url,data:data,success:callback,dataType:type});},ajaxSetup:function(settings){jQuery.extend(jQuery.ajaxSettings,settings);},ajaxSettings:{url:location.href,global:true,type:"GET",timeout:0,contentType:"application/x-www-form-urlencoded",processData:true,async:true,data:null,username:null,password:null,accepts:{xml:"application/xml, text/xml",html:"text/html",script:"text/javascript, application/javascript",json:"application/json, text/javascript",text:"text/plain",_default:"*/*"}},lastModified:{},ajax:function(s){s=jQuery.extend(true,s,jQuery.extend(true,{},jQuery.ajaxSettings,s));var jsonp,jsre=/=\?(&|$)/g,status,data,type=s.type.toUpperCase();if(s.data&&s.processData&&typeof s.data!="string")s.data=jQuery.param(s.data);if(s.dataType=="jsonp"){if(type=="GET"){if(!s.url.match(jsre))s.url+=(s.url.match(/\?/)?"&":"?")+(s.jsonp||"callback")+"=?";}else if(!s.data||!s.data.match(jsre))s.data=(s.data?s.data+"&":"")+(s.jsonp||"callback")+"=?";s.dataType="json";}if(s.dataType=="json"&&(s.data&&s.data.match(jsre)||s.url.match(jsre))){jsonp="jsonp"+jsc++;if(s.data)s.data=(s.data+"").replace(jsre,"="+jsonp+"$1");s.url=s.url.replace(jsre,"="+jsonp+"$1");s.dataType="script";window[jsonp]=function(tmp){data=tmp;success();complete();window[jsonp]=undefined;try{delete window[jsonp];}catch(e){}if(head)head.removeChild(script);};}if(s.dataType=="script"&&s.cache==null)s.cache=false;if(s.cache===false&&type=="GET"){var ts=now();var ret=s.url.replace(/(\?|&)_=.*?(&|$)/,"$1_="+ts+"$2");s.url=ret+((ret==s.url)?(s.url.match(/\?/)?"&":"?")+"_="+ts:"");}if(s.data&&type=="GET"){s.url+=(s.url.match(/\?/)?"&":"?")+s.data;s.data=null;}if(s.global&&!jQuery.active++)jQuery.event.trigger("ajaxStart");var remote=/^(?:\w+:)?\/\/([^\/?#]+)/;if(s.dataType=="script"&&type=="GET"&&remote.test(s.url)&&remote.exec(s.url)[1]!=location.host){var head=document.getElementsByTagName("head")[0];var script=document.createElement("script");script.src=s.url;if(s.scriptCharset)script.charset=s.scriptCharset;if(!jsonp){var done=false;script.onload=script.onreadystatechange=function(){if(!done&&(!this.readyState||this.readyState=="loaded"||this.readyState=="complete")){done=true;success();complete();head.removeChild(script);}};}head.appendChild(script);return undefined;}var requestDone=false;var xhr=window.ActiveXObject?new ActiveXObject("Microsoft.XMLHTTP"):new XMLHttpRequest();if(s.username)xhr.open(type,s.url,s.async,s.username,s.password);else xhr.open(type,s.url,s.async);try{if(s.data)xhr.setRequestHeader("Content-Type",s.contentType);if(s.ifModified)xhr.setRequestHeader("If-Modified-Since",jQuery.lastModified[s.url]||"Thu, 01 Jan 1970 00:00:00 GMT");xhr.setRequestHeader("X-Requested-With","XMLHttpRequest");xhr.setRequestHeader("Accept",s.dataType&&s.accepts[s.dataType]?s.accepts[s.dataType]+", */*":s.accepts._default);}catch(e){}if(s.beforeSend&&s.beforeSend(xhr,s)===false){s.global&&jQuery.active--;xhr.abort();return false;}if(s.global)jQuery.event.trigger("ajaxSend",[xhr,s]);var onreadystatechange=function(isTimeout){if(!requestDone&&xhr&&(xhr.readyState==4||isTimeout=="timeout")){requestDone=true;if(ival){clearInterval(ival);ival=null;}status=isTimeout=="timeout"&&"timeout"||!jQuery.httpSuccess(xhr)&&"error"||s.ifModified&&jQuery.httpNotModified(xhr,s.url)&&"notmodified"||"success";if(status=="success"){try{data=jQuery.httpData(xhr,s.dataType,s.dataFilter);}catch(e){status="parsererror";}}if(status=="success"){var modRes;try{modRes=xhr.getResponseHeader("Last-Modified");}catch(e){}if(s.ifModified&&modRes)jQuery.lastModified[s.url]=modRes;if(!jsonp)success();}else jQuery.handleError(s,xhr,status);complete();if(s.async)xhr=null;}};if(s.async){var ival=setInterval(onreadystatechange,13);if(s.timeout>0)setTimeout(function(){if(xhr){xhr.abort();if(!requestDone)onreadystatechange("timeout");}},s.timeout);}try{xhr.send(s.data);}catch(e){jQuery.handleError(s,xhr,null,e);}if(!s.async)onreadystatechange();function success(){if(s.success)s.success(data,status);if(s.global)jQuery.event.trigger("ajaxSuccess",[xhr,s]);}function complete(){if(s.complete)s.complete(xhr,status);if(s.global)jQuery.event.trigger("ajaxComplete",[xhr,s]);if(s.global&&!--jQuery.active)jQuery.event.trigger("ajaxStop");}return xhr;},handleError:function(s,xhr,status,e){if(s.error)s.error(xhr,status,e);if(s.global)jQuery.event.trigger("ajaxError",[xhr,s,e]);},active:0,httpSuccess:function(xhr){try{return!xhr.status&&location.protocol=="file:"||(xhr.status>=200&&xhr.status<300)||xhr.status==304||xhr.status==1223||jQuery.browser.safari&&xhr.status==undefined;}catch(e){}return false;},httpNotModified:function(xhr,url){try{var xhrRes=xhr.getResponseHeader("Last-Modified");return xhr.status==304||xhrRes==jQuery.lastModified[url]||jQuery.browser.safari&&xhr.status==undefined;}catch(e){}return false;},httpData:function(xhr,type,filter){var ct=xhr.getResponseHeader("content-type"),xml=type=="xml"||!type&&ct&&ct.indexOf("xml")>=0,data=xml?xhr.responseXML:xhr.responseText;if(xml&&data.documentElement.tagName=="parsererror")throw"parsererror";if(filter)data=filter(data,type);if(type=="script")jQuery.globalEval(data);if(type=="json")data=eval("("+data+")");return data;},param:function(a){var s=[];if(a.constructor==Array||a.jquery)jQuery.each(a,function(){s.push(encodeURIComponent(this.name)+"="+encodeURIComponent(this.value));});else for(var j in a)if(a[j]&&a[j].constructor==Array)jQuery.each(a[j],function(){s.push(encodeURIComponent(j)+"="+encodeURIComponent(this));});else s.push(encodeURIComponent(j)+"="+encodeURIComponent(jQuery.isFunction(a[j])?a[j]():a[j]));return s.join("&").replace(/%20/g,"+");}});jQuery.fn.extend({show:function(speed,callback){return speed?this.animate({height:"show",width:"show",opacity:"show"},speed,callback):this.filter(":hidden").each(function(){this.style.display=this.oldblock||"";if(jQuery.css(this,"display")=="none"){var elem=jQuery("<"+this.tagName+" />").appendTo("body");this.style.display=elem.css("display");if(this.style.display=="none")this.style.display="block";elem.remove();}}).end();},hide:function(speed,callback){return speed?this.animate({height:"hide",width:"hide",opacity:"hide"},speed,callback):this.filter(":visible").each(function(){this.oldblock=this.oldblock||jQuery.css(this,"display");this.style.display="none";}).end();},_toggle:jQuery.fn.toggle,toggle:function(fn,fn2){return jQuery.isFunction(fn)&&jQuery.isFunction(fn2)?this._toggle.apply(this,arguments):fn?this.animate({height:"toggle",width:"toggle",opacity:"toggle"},fn,fn2):this.each(function(){jQuery(this)[jQuery(this).is(":hidden")?"show":"hide"]();});},slideDown:function(speed,callback){return this.animate({height:"show"},speed,callback);},slideUp:function(speed,callback){return this.animate({height:"hide"},speed,callback);},slideToggle:function(speed,callback){return this.animate({height:"toggle"},speed,callback);},fadeIn:function(speed,callback){return this.animate({opacity:"show"},speed,callback);},fadeOut:function(speed,callback){return this.animate({opacity:"hide"},speed,callback);},fadeTo:function(speed,to,callback){return this.animate({opacity:to},speed,callback);},animate:function(prop,speed,easing,callback){var optall=jQuery.speed(speed,easing,callback);return this[optall.queue===false?"each":"queue"](function(){if(this.nodeType!=1)return false;var opt=jQuery.extend({},optall),p,hidden=jQuery(this).is(":hidden"),self=this;for(p in prop){if(prop[p]=="hide"&&hidden||prop[p]=="show"&&!hidden)return opt.complete.call(this);if(p=="height"||p=="width"){opt.display=jQuery.css(this,"display");opt.overflow=this.style.overflow;}}if(opt.overflow!=null)this.style.overflow="hidden";opt.curAnim=jQuery.extend({},prop);jQuery.each(prop,function(name,val){var e=new jQuery.fx(self,opt,name);if(/toggle|show|hide/.test(val))e[val=="toggle"?hidden?"show":"hide":val](prop);else{var parts=val.toString().match(/^([+-]=)?([\d+-.]+)(.*)$/),start=e.cur(true)||0;if(parts){var end=parseFloat(parts[2]),unit=parts[3]||"px";if(unit!="px"){self.style[name]=(end||1)+unit;start=((end||1)/e.cur(true))*start;self.style[name]=start+unit;}if(parts[1])end=((parts[1]=="-="?-1:1)*end)+start;e.custom(start,end,unit);}else e.custom(start,val,"");}});return true;});},queue:function(type,fn){if(jQuery.isFunction(type)||(type&&type.constructor==Array)){fn=type;type="fx";}if(!type||(typeof type=="string"&&!fn))return queue(this[0],type);return this.each(function(){if(fn.constructor==Array)queue(this,type,fn);else{queue(this,type).push(fn);if(queue(this,type).length==1)fn.call(this);}});},stop:function(clearQueue,gotoEnd){var timers=jQuery.timers;if(clearQueue)this.queue([]);this.each(function(){for(var i=timers.length-1;i>=0;i--)if(timers[i].elem==this){if(gotoEnd)timers[i](true);timers.splice(i,1);}});if(!gotoEnd)this.dequeue();return this;}});var queue=function(elem,type,array){if(elem){type=type||"fx";var q=jQuery.data(elem,type+"queue");if(!q||array)q=jQuery.data(elem,type+"queue",jQuery.makeArray(array));}return q;};jQuery.fn.dequeue=function(type){type=type||"fx";return this.each(function(){var q=queue(this,type);q.shift();if(q.length)q[0].call(this);});};jQuery.extend({speed:function(speed,easing,fn){var opt=speed&&speed.constructor==Object?speed:{complete:fn||!fn&&easing||jQuery.isFunction(speed)&&speed,duration:speed,easing:fn&&easing||easing&&easing.constructor!=Function&&easing};opt.duration=(opt.duration&&opt.duration.constructor==Number?opt.duration:jQuery.fx.speeds[opt.duration])||jQuery.fx.speeds.def;opt.old=opt.complete;opt.complete=function(){if(opt.queue!==false)jQuery(this).dequeue();if(jQuery.isFunction(opt.old))opt.old.call(this);};return opt;},easing:{linear:function(p,n,firstNum,diff){return firstNum+diff*p;},swing:function(p,n,firstNum,diff){return((-Math.cos(p*Math.PI)/2)+0.5)*diff+firstNum;}},timers:[],timerId:null,fx:function(elem,options,prop){this.options=options;this.elem=elem;this.prop=prop;if(!options.orig)options.orig={};}});jQuery.fx.prototype={update:function(){if(this.options.step)this.options.step.call(this.elem,this.now,this);(jQuery.fx.step[this.prop]||jQuery.fx.step._default)(this);if(this.prop=="height"||this.prop=="width")this.elem.style.display="block";},cur:function(force){if(this.elem[this.prop]!=null&&this.elem.style[this.prop]==null)return this.elem[this.prop];var r=parseFloat(jQuery.css(this.elem,this.prop,force));return r&&r>-10000?r:parseFloat(jQuery.curCSS(this.elem,this.prop))||0;},custom:function(from,to,unit){this.startTime=now();this.start=from;this.end=to;this.unit=unit||this.unit||"px";this.now=this.start;this.pos=this.state=0;this.update();var self=this;function t(gotoEnd){return self.step(gotoEnd);}t.elem=this.elem;jQuery.timers.push(t);if(jQuery.timerId==null){jQuery.timerId=setInterval(function(){var timers=jQuery.timers;for(var i=0;ithis.options.duration+this.startTime){this.now=this.end;this.pos=this.state=1;this.update();this.options.curAnim[this.prop]=true;var done=true;for(var i in this.options.curAnim)if(this.options.curAnim[i]!==true)done=false;if(done){if(this.options.display!=null){this.elem.style.overflow=this.options.overflow;this.elem.style.display=this.options.display;if(jQuery.css(this.elem,"display")=="none")this.elem.style.display="block";}if(this.options.hide)this.elem.style.display="none";if(this.options.hide||this.options.show)for(var p in this.options.curAnim)jQuery.attr(this.elem.style,p,this.options.orig[p]);}if(done)this.options.complete.call(this.elem);return false;}else{var n=t-this.startTime;this.state=n/this.options.duration;this.pos=jQuery.easing[this.options.easing||(jQuery.easing.swing?"swing":"linear")](this.state,n,0,1,this.options.duration);this.now=this.start+((this.end-this.start)*this.pos);this.update();}return true;}};jQuery.extend(jQuery.fx,{speeds:{slow:600,fast:200,def:400},step:{scrollLeft:function(fx){fx.elem.scrollLeft=fx.now;},scrollTop:function(fx){fx.elem.scrollTop=fx.now;},opacity:function(fx){jQuery.attr(fx.elem.style,"opacity",fx.now);},_default:function(fx){fx.elem.style[fx.prop]=fx.now+fx.unit;}}});jQuery.fn.offset=function(){var left=0,top=0,elem=this[0],results;if(elem)with(jQuery.browser){var parent=elem.parentNode,offsetChild=elem,offsetParent=elem.offsetParent,doc=elem.ownerDocument,safari2=safari&&parseInt(version)<522&&!/adobeair/i.test(userAgent),css=jQuery.curCSS,fixed=css(elem,"position")=="fixed";if(elem.getBoundingClientRect){var box=elem.getBoundingClientRect();add(box.left+Math.max(doc.documentElement.scrollLeft,doc.body.scrollLeft),box.top+Math.max(doc.documentElement.scrollTop,doc.body.scrollTop));add(-doc.documentElement.clientLeft,-doc.documentElement.clientTop);}else{add(elem.offsetLeft,elem.offsetTop);while(offsetParent){add(offsetParent.offsetLeft,offsetParent.offsetTop);if(mozilla&&!/^t(able|d|h)$/i.test(offsetParent.tagName)||safari&&!safari2)border(offsetParent);if(!fixed&&css(offsetParent,"position")=="fixed")fixed=true;offsetChild=/^body$/i.test(offsetParent.tagName)?offsetChild:offsetParent;offsetParent=offsetParent.offsetParent;}while(parent&&parent.tagName&&!/^body|html$/i.test(parent.tagName)){if(!/^inline|table.*$/i.test(css(parent,"display")))add(-parent.scrollLeft,-parent.scrollTop);if(mozilla&&css(parent,"overflow")!="visible")border(parent);parent=parent.parentNode;}if((safari2&&(fixed||css(offsetChild,"position")=="absolute"))||(mozilla&&css(offsetChild,"position")!="absolute"))add(-doc.body.offsetLeft,-doc.body.offsetTop);if(fixed)add(Math.max(doc.documentElement.scrollLeft,doc.body.scrollLeft),Math.max(doc.documentElement.scrollTop,doc.body.scrollTop));}results={top:top,left:left};}function border(elem){add(jQuery.curCSS(elem,"borderLeftWidth",true),jQuery.curCSS(elem,"borderTopWidth",true));}function add(l,t){left+=parseInt(l,10)||0;top+=parseInt(t,10)||0;}return results;};jQuery.fn.extend({position:function(){var left=0,top=0,results;if(this[0]){var offsetParent=this.offsetParent(),offset=this.offset(),parentOffset=/^body|html$/i.test(offsetParent[0].tagName)?{top:0,left:0}:offsetParent.offset();offset.top-=num(this,'marginTop');offset.left-=num(this,'marginLeft');parentOffset.top+=num(offsetParent,'borderTopWidth');parentOffset.left+=num(offsetParent,'borderLeftWidth');results={top:offset.top-parentOffset.top,left:offset.left-parentOffset.left};}return results;},offsetParent:function(){var offsetParent=this[0].offsetParent;while(offsetParent&&(!/^body|html$/i.test(offsetParent.tagName)&&jQuery.css(offsetParent,'position')=='static'))offsetParent=offsetParent.offsetParent;return jQuery(offsetParent);}});jQuery.each(['Left','Top'],function(i,name){var method='scroll'+name;jQuery.fn[method]=function(val){if(!this[0])return;return val!=undefined?this.each(function(){this==window||this==document?window.scrollTo(!i?val:jQuery(window).scrollLeft(),i?val:jQuery(window).scrollTop()):this[method]=val;}):this[0]==window||this[0]==document?self[i?'pageYOffset':'pageXOffset']||jQuery.boxModel&&document.documentElement[method]||document.body[method]:this[0][method];};});jQuery.each(["Height","Width"],function(i,name){var tl=i?"Left":"Top",br=i?"Right":"Bottom";jQuery.fn["inner"+name]=function(){return this[name.toLowerCase()]()+num(this,"padding"+tl)+num(this,"padding"+br);};jQuery.fn["outer"+name]=function(margin){return this["inner"+name]()+num(this,"border"+tl+"Width")+num(this,"border"+br+"Width")+(margin?num(this,"margin"+tl)+num(this,"margin"+br):0);};});})();camping-3.2.6/extras/rdoc/generator/template/flipbook/page.rhtml000066400000000000000000000017301465547410100247770ustar00rootroot00000000000000 <%= h @options.title %>

<%= Time.now %>

<%= file.content %>
camping-3.2.6/extras/rdoc/generator/template/flipbook/rdoc.css000066400000000000000000000050111465547410100244500ustar00rootroot00000000000000 body { font: normal 14px verdana,arial,'Bitstream Vera Sans',helvetica,sans-serif; line-height: 160%; padding: 0; margin: 0; margin-bottom: 30px; background-color: #694; } p#version, h1, h2, h3, h4 { font-family: Utopia, Georgia, serif; font-weight: bold; letter-spacing: -0.018em; } #logo { width: 150px; margin: 2em auto; display: block; } h1 { font-size: 36px; margin: 1em; } h2 { font-size: 24px } h3, p#version { font-size: 19px } h4 { font-size: 17px; font-weight: normal; } h2.ruled { padding-top: 35px; border-top: solid 1px #AA5; } /* Link styles */ :link, :visited { color: #00b; } :link:hover, :visited:hover { background-color: #eee; color: #B22; } /* menu */ #menu { background-color: #dfa; padding: 4px 12px; margin: 0; } #menu #version { padding: 0; margin: 0; } #menu #links { float: right; padding:0; margin: 0;} #menu #links ul { display: inline; } #menu #links li { display: inline; } #fullpage { width: 720px; margin: 3em auto; } .page_shade, .page { clear: both; padding: 0px 5px 5px 0px; background-color: #fcfcf9; border: solid 1px #983; } .page { margin-left: -5px; margin-top: -5px; padding: 20px 35px; } .page .header { float: right; color: #777; font-size: 10px; } .page h1, .page h2, .page h3 { clear: both; text-align: center; } .ref h2 a { background-color: #eee; color: black; display: block; text-align: left; text-decoration: none; padding: 0.5em 1em; } .ref h2 a:hover { text-decoration: underline; } .ref h4 a { border-bottom: solid 1px #CC9; color: #000; display: block; text-decoration: none; } .ref h4 a:hover { border-color: #694; } .ref .mod { padding: 0 2em; } pre { font-weight: bold; color: #730; } tt { color: #703; font-size: 12pt; } pre.sourcecode { color: #DDDDDD; background-color: #775915; padding: 4px 8px; margin: 0; display: none; font-size: 8pt; } .source-link { text-align: right; font-size: 8pt; } .ruby-comment { color: green; font-style: italic } .ruby-constant { color: #CCDDFF; font-weight: bold; } .ruby-identifier { color: #CCCCCC; } .ruby-ivar { color: #BBCCFF; } .ruby-keyword { color: #EEEEFF; font-weight: bold } .ruby-node { color: #FFFFFF; } .ruby-operator { color: #CCCCCC; } .ruby-regexp { color: #DDFFDD; } .ruby-value { color: #FFAAAA; font-style: italic } .kw { color: #DDDDFF; font-weight: bold } .cmt { color: #CCFFCC; font-style: italic } .str { color: #EECCCC; font-style: italic } .re { color: #EECCCC; }camping-3.2.6/extras/rdoc/generator/template/flipbook/readme.rhtml000066400000000000000000000021301465547410100253130ustar00rootroot00000000000000 <%= h @options.title %>

<%= Time.now %>

<%= h @options.title %>

<%= @files.find { |f| f.full_name == @options.main_page }.description.sub(%r{^\s*}i, '') %>
camping-3.2.6/extras/rdoc/generator/template/flipbook/reference.rhtml000066400000000000000000000043651465547410100260300ustar00rootroot00000000000000 Camping, the Reference

<%= Time.now %>

Camping, the Reference

<% @modsort.each_with_index do |klass, index| %>

<%= klass.type.capitalize %> <%= klass.full_name %> <% if klass.type == 'class' %> < <% unless String === klass.superclass %> <%= klass.superclass.full_name %> <% else %> <%= klass.superclass %> <% end %> <% end %>

<%= klass.description.gsub(/<(\/?)h(\d)>/) do # replace

with

and so. "<#{$1}h#{$2.to_i + 1}>" end %>

Methods

<% methods_for(klass) do |method, type, visibility| %>

<%= visibility.to_s.capitalize %> <%= type.capitalize %> method: <%= method.pretty_name %><%= method.params %>

<%= method.description %>
<%= method.markup_code %>
<% end %>
<% end %>
camping-3.2.6/extras/rdoc/generator/template/flipbook/toc.rhtml000066400000000000000000000024571465547410100246570ustar00rootroot00000000000000 <%= @options.title %>

<%= Time.now %>

Camping, the Book

    <% chapters.each do |chapter| %>
  1. <%= chapter.title %> <% unless chapter.toc.empty? %>
      <% chapter.toc.each do |id, title| %>
    • <%= title %>
    • <% end %>
    <% end %>
  2. <% end %>
camping-3.2.6/lib/000077500000000000000000000000001465547410100137155ustar00rootroot00000000000000camping-3.2.6/lib/camping-unabridged.rb000066400000000000000000001021601465547410100177620ustar00rootroot00000000000000# == About camping.rb # # Camping comes with two versions of its source code. The code contained in # lib/camping.rb is compressed, stripped of whitespace, using compact algorithms # to keep it tight. The unspoken rule is that camping.rb should be flowed with # no more than 80 characters per line and must not exceed four kilobytes. # # On the other hand, lib/camping-unabridged.rb contains the same code, laid out # nicely with piles of documentation everywhere. This documentation is entirely # generated from lib/camping-unabridged.rb using RDoc and our "flipbook" template # found in the extras directory of any camping distribution. require "cam\ping/loads" $LOADED_FEATURES << "camping.rb" E ||= "content-type" Z ||= "text/html" class Object #:nodoc: def meta_def(m,&b) #:nodoc: (class<Camping.goes :Nuts # copies the Camping module into Nuts. This means that you should never use # any of these methods/classes on the Camping module, but rather on your own # app. Here's a short explanation on how Camping is organized: # # * Camping::Controllers is where your controllers live. # * Camping::Models is where your models live. # * Camping::Views is where your views live. # * Camping::Base is a module which is included in all your controllers. # * Camping::Helpers is a module with useful helpers, both for the controllers # and the views. You should fill this up with your own helpers. # # Camping also ships with: # # * Camping::Session adds states to your app. # * Camping::Server starts up your app in development. # * Camping::Reloader automatically reloads your apps when a file has changed. # # More importantly, Camping also installs The Camping Server, # please see Camping::Server. module Camping C = self S = IO.read(__FILE__) rescue nil P = "

Cam\ping Problem!

%s

" U = Rack::Utils Apps = [] # Our array of Apps SK = "camping" #Key for r.session G = [] # Our array of Gear # An object-like Hash. # All Camping query string and cookie variables are loaded as this. # # To access the query string, for instance, use the @input variable. # # module Blog::Controllers # class Index < R '/' # def get # if (page = @input.page.to_i) > 0 # page -= 1 # end # @posts = Post.all, :offset => page * 20, :limit => 20 # render :index # end # end # end # # In the above example if you visit /?page=2, you'll get the second # page of twenty posts. You can also use @input['page'] to get the # value for the page query variable. class H < Hash # Gets or sets keys in the hash. # # @cookies.my_favorite = :macadamian # @cookies.my_favorite # => :macadamian # def method_missing(m,*a) m.to_s=~/=$/?self[$`]=a[0]:a==[]?self[m.to_s]:super end undef id, type if ?? == 63 end O=H.new;O[:url_prefix]="" # Our Hash of Options class Cookies < H attr_accessor :_p # # Cookies that are set at this response def _n; @n ||= {} end alias _s []= def set(k, v, o = {}) _s(j=k.to_s, v) _n[j] = {:value => v, :path => _p}.update(o) end def []=(k, v) set k, v, v.is_a?(Hash) ? v : {} end end # Helpers contains methods available in your controllers and views. You may # add methods of your own to this module, including many helper methods from # Rails. This is analogous to Rails' ApplicationHelper module. # # == Using ActionPack Helpers # # If you'd like to include helpers from Rails' modules, you'll need to look # up the helper module in the Rails documentation at http://api.rubyonrails.org/. # # For example, if you look up the ActionView::Helpers::FormTagHelper # class, you'll find that it's loaded from the action_view/helpers/form_tag_helper.rb # file. You'll need to have the ActionPack gem installed for this to work. # # A helper often depends on other helpers, so you would have to look up # the dependencies too. FormTagHelper for instance required the # content_tag provided by TagHelper. # # require 'action_view/helpers/form_tag_helper' # # module Nuts::Helpers # include ActionView::Helpers::TagHelper # include ActionView::Helpers::FormTagHelper # end # # == Return a response immediately # If you need to return a response inside a helper, you can use throw :halt. # # module Nuts::Helpers # def requires_login! # unless @state.user_id # redirect Login # throw :halt # end # end # end # # module Nuts::Controllers # class Admin # def get # requires_login! # "Never gets here unless you're logged in" # end # end # end module Helpers # From inside your controllers and views, you will often need to figure out # the route used to get to a certain controller +c+. Pass the controller # class and any arguments into the R method, a string containing the route # will be returned to you. # # Assuming you have a specific route in an edit controller: # # class Edit < R '/edit/(\d+)' # # A specific route to the Edit controller can be built with: # # R(Edit, 1) # # Which outputs: /edit/1. # # If a controller has many routes, the route will be selected if it is the # first in the routing list to have the right number of arguments. # # == Using R in the View # # Keep in mind that this route doesn't include the root path. You will # need to use / (the slash method above) in your controllers. # Or, go ahead and use the Helpers#URL method to build a complete URL for # a route. # # However, in your views, the :href, :src and :action attributes # automatically pass through the slash method, so you are encouraged to # use R or URL in your views. # # module Nuts::Views # def menu # div.menu! do # a 'Home', :href => URL() # a 'Profile', :href => "/profile" # a 'Logout', :href => R(Logout) # a 'Google', :href => 'http://google.com' # end # end # end # # Let's say the above example takes place inside an application mounted at # http://localhost:3301/frodo and that a controller named # Logout is assigned to route /logout. # The HTML will come out as: # # # def R(c,*g) p,h=/\(.+?\)/,g.grep(Hash) g-=h raise "bad route" if !u = c.urls.find{|x| break x if x.scan(p).size == g.size && /^#{x}\/?$/ =~ (x=g.inject(x){|x,a| x.sub p,U.escape((a.to_param rescue a))}.gsub(CampTools.descape){$1}) } h.any?? u+"?"+U.build_query(h[0]) : u end # Simply builds a complete path from a path +p+ within the app. If your # application is mounted at /blog: # # self / "/view/1" #=> "/blog/view/1" # self / "styles.css" #=> "styles.css" # self / R(Edit, 1) #=> "/blog/edit/1" # def /(p) p[0] == ?/ ? (@root + @url_prefix.dup.prepend("/").chop + p) : p end # Builds a URL route to a controller or a path, returning a URI object. # This way you'll get the hostname and the port number, a complete URL. # # You can use this to grab URLs for controllers using the R-style syntax. # So, if your application is mounted at http://test.ing/blog/ # and you have a View controller which routes as R '/view/(\d+)': # # URL(View, @post.id) #=> # # # Or you can use the direct path: # # self.URL #=> # # self.URL + "view/12" #=> # # URL("/view/12") #=> # # # It's okay to pass URL strings through this method as well: # # URL("http://google.com") #=> # # # Any string which doesn't begin with a slash will pass through # unscathed. def URL c='/',*a c = R(c, *a) if c.respond_to? :urls c = self/c c = @request.url[/.{8,}?(?=\/|$)/]+c if c[0]==?/ #/ URI(c) end # Just a helper to tell you the App Name # During the instantiation of the app, "Camping" is replaced with the Apps namespace. def app_name;"Camping"end end # Camping::Base is built into each controller by way of the generic routing # class Camping::R. In some ways, this class is trying to do too much, but # it saves code for all the glue to stay in one place. Forgivable, # considering that it's only really a handful of methods and accessors. # # Everything in this module is accessible inside your controllers. module Base attr_accessor :env, :request, :root, :input, :cookies, :state, :status, :headers, :body, :url_prefix T = {} L = :layout # Finds a template, returning either: # # false # => Could not find template # true # => Found template in Views # instance of Tilt # => Found template in a file def lookup(n) T.fetch(n.to_sym) { |k| # Find a view defined in the Views module first t = Views.method_defined?(k) || # Find inline templates (delimited by @@), and then put it in a new Template and return that. # `:_t` is the options key for inline templates. Inline templates are added in `Camping#goes`. (t = O[:_t].keys.grep(/^#{n}\./)[0]and Template[t].new{O[:_t][t]}) || # Find templates in a views directory, and return the first view that matches the symbol provided. # Then pipe that template file into Template, which is just Tilt. (f = Dir[[O[:views] || "views", "#{n}.*"]*'/'][0]) && # Grab any settings set for the template files, as set by their filename extension # and add that to the options of Template (Tilt), or an empty Hash # What does adding settings for a template look like? : # module Nuts # def r404(path) # @path = path # render :not_found # end # end Template.new(f, O[f[/\.(\w+)$/, 1].to_sym] || {}) O[:dynamic_templates] ? t : T[k] = t } end # Display a view, calling it by its method name +v+. If a layout # method is found in Camping::Views, it will be used to wrap the HTML. # # module Nuts::Controllers # class Show # def get # @posts = Post.find :all # render :index # end # end # end # def render(v, *a, &b) if t = lookup(v) # Has this controller rendered before? r = @_r # Set @_r to truthy value @_r = (o = Hash === a[-1] ? a.pop : {}) s = (t == true) ? mab { send(v, *a, &b) } : t.render(self, o[:locals] || {}, &b) s = render(L, o.merge(L => false)) { s } if o[L] or o[L].nil? && lookup(L) && !r && v.to_s[0] != ?_ s else raise "no template: #{v}" end end # You can directly return HTML from your controller for quick debugging # by calling this method and passing some Markaby to it. # # module Nuts::Controllers # class Info # def get; mab{ code @headers.inspect } end # end # end # # You can also pass true to use the :layout HTML wrapping method def mab(&b) extend Mab mab(&b) end # A quick means of setting this controller's status, body and headers # based on a Rack response: # # r(302, 'Location' => self / "/view/12", '') # r(*another_app.call(@env)) # # You can also switch the body and the header if you want: # # r(404, "Could not find page") # # See also: #r404, #r500 and #r501 def r(s, b, h = {}) b, h = h, b if Hash === b @status = s @headers.merge!(h) @body = b end # Formulate a redirect response: a 302 status with Location header # and a blank body. Uses Helpers#URL to build the location from a # controller route or path. # # So, given a root of http://localhost:3301/articles: # # redirect "view/12" # redirects to "//localhost:3301/articles/view/12" # redirect View, 12 # redirects to "//localhost:3301/articles/view/12" # # NOTE: This method doesn't magically exit your methods and redirect. # You'll need to return redirect(...) if this isn't the last statement # in your code, or throw :halt if it's in a helper. # # See: Controllers def redirect(*a) r(302,'','Location'=>URL(*a).to_s) end # Called when a controller was not found. You can override this if you # want to customize the error page: # # module Nuts # def r404(path) # @path = path # render :not_found # end # end def r404(p) P % "#{p} not found" end # Called when an exception is raised. However, if there is a parse error # in Camping or in your application's source code, it will not be caught. # # +k+ is the controller class, +m+ is the request method (GET, POST, etc.) # and +e+ is the Exception which can be mined for useful info. # # By default this simply re-raises the error so a Rack middleware can # handle it, but you are free to override it here: # # module Nuts # def r500(klass, method, exception) # send_email_alert(klass, method, exception) # render :server_error # end # end def r500(k,m,e) raise e end # Called if an undefined method is called on a controller, along with the # request method +m+ (GET, POST, etc.) def r501(m) P % "#{m.upcase} not implemented" end # Serves the string +c+ with the MIME type of the filename +p+. # Defaults to text/html. def serve(p, c) t = Rack::Mime.mime_type(p[/\..*$/], Z) and @headers[E] = t c end # Turn a controller into a Rack response. This is designed to be used to # pipe controllers into the r method. A great way to forward your # requests! # # class Read < '/(\d+)' # def get(id) # Post.find(id) # rescue # r *Blog.get(:NotFound, @headers.REQUEST_URI) # end # end def to_a @env['rack.session'][SK] = Hash[@state] r = Rack::Response.new(@body, @status, @headers) @cookies._n.each do |k, v| r.set_cookie(k, v) end r.to_a end # initialize # Turns a camping controller class into an object and sets up # the environment with input, cookies, state, headers, etc... def initialize(env, m, p) #:nodoc: r = @request = Rack::Request.new(@env = env) @root, @input, @cookies, @state, @headers, @status, @method, @url_prefix = r.script_name.sub(/\/$/,''), n(r.params), Cookies[r.cookies], H[r.session[SK]||{}], {E=>Z}, m =~ /r(\d+)/ ? $1.to_i : 200, m, p @cookies._p = self/"/" end # n method # accepts parameters and converts them to a hash. # helper method for initialize def n(h) # :nodoc: if Hash === h h.inject(H[]) { |m, (k, v)| m[k] = n(v) m } else h end end # All requests pass through this method before going to the controller. # Some magic in Camping can be performed by overriding this method. def service(*a) r = catch(:halt){send(@method, *a)} @body ||= r self end end # Controllers receive the requests and send a response back to the client. # A controller is simply a class which must implement the HTTP methods it # wants to accept: # # module Nuts::Controllers # class Index # def get # "Hello World" # end # end # # class Posts # def post # Post.create(@input) # redirect Index # end # end # end # # == Defining a controller # # There are two ways to define controllers: # # 1. Define a class and let Camping figure out the route. # 2. Add the route explicitly using R. # # If you don't use R, Camping will first split the controller name up by # words (HelloWorld => Hello and World). # # After that, it will do the following: # # * Replace Index with / # * Replace X with ([^/]+) # * Replace N with (\\\d+) # * Turn everything else into lowercase # * Join the words with slashes # #-- # NB! N will actually be replaced with (\d+), but it needs to be escaped # here in order to work correctly with RDoc. #++ # # Here are a few examples: # # Index # => / # PostN # => /post/(\d+) # PageX # => /page/([^/]+) # Pages # => /pages # # == The request # # The following variables aid in describing a request: # # * @env contains the environment as defined in https://github.com/rack/rack/blob/main/SPEC.rdoc # * @request is Rack::Request.new(@env) # * @root is the path where the app is mounted # * @cookies is a hash with the cookies sent by the client # * @state is a hash with the sessions (see Camping::Session) # * @method is the HTTP method in lowercase # * @url_prefix is the set prefix of the route matched by your controller # # == The response # # You can change these variables to your needs: # # * @status is the HTTP status (defaults to 200) # * @headers is a hash with the headers # * @body is the body (a string or something which responds to #each) # * Any changes in @cookies and @state will also be sent to the client # # If you haven't set @body, it will use the return value of the method: # # module Nuts::Controllers # class Index < Camper # def get # "This is the body" # end # end # # class Posts < Camper # def get # @body = "Hello World!" # "This is ignored" # end # end # end module Controllers @r = [] # An empty controller class that our other Classes inherit from. # Camper is used by the R method internally. class Camper end class << self # Add routes to a controller class by piling them into the R method. # # The route is a regexp which will match the request path. Anything # enclosed in parenthesis will be sent to the method as arguments. # # module Camping::Controllers # class Edit < R '/edit/(\d+)', '/new' # def get(id) # if id # edit # else # new # end # end # end # end # # Routes may be inherited using the R command as well. In this case you'll # pass the ancestor Controller as the first argument to R. # # module Camping::Controllers # class Post < R Edit, '/edit/(\d+)', '/new' # def get(id) # if id # edit # else # new # end # end # end # end # def R *u r,uf=@r,u.first Class.new((uf.is_a?(Class) && (uf.ancestors.include?(Camper))) ? u.shift : Camper) { meta_def(:urls){u} meta_def(:inherited){|x|r<< x} } end # A Helper method to map and return the actual routes of our controllers def v @r.map(&:urls) end # Dispatch routes to controller classes. # For each class, routes are checked for a match based on their order in the routing list # given to Controllers::R. If no routes were given, the dispatcher uses a slash followed # by the lowercased name of the controller. # # Controllers are searched in this order: # # * Classes without routes, since they refer to a very specific URL. # * Classes with routes are searched in order of their creation. # # So, define your catch-all controllers last. def D(p, m, e) p = '/' if !p || !p[0] a=O[:_t].find{|n,_|n==p} and return [I, :serve, *a] @r.map { |k| k.urls.map { |x| return (k.method_defined?(m)) ? [k, m, *$~[1..-1].map{|x|U.unescape(x)}] : [I, 'r501', m] if p =~ /^#{x}\/?$/ } } [I, 'r404', p] end # A lambda to avoid internal controller route A = -> (c, u, p) { d = p.dup d.chop! if u == '' u.prepend("/"+d) if !["I"].include? c.to_s if c.to_s == "Index" while d[-1] == "/"; d.chop! end u.prepend("/"+d) end u } N = H.new { |_,x| x.downcase }.merge! "N" => '(\d+)', "X" => '([^/]+)', "Index" => '' # The route maker, called by Camping internally. # # Still, it's worth know what this method does. Since Ruby doesn't keep # track of class creation order, we're keeping an internal list of the # controllers which inherit from R(). This method goes through and adds # all the remaining routes to the beginning of the list and ensures all # the controllers have the right mixins. # # Anyway, if you are calling the URI dispatcher from outside of a # Camping server, you'll definitely need to call this to set things up. # Don't call it too early though - any controllers added after this # method was called won't work properly. def M(p) def M(p) #:nodoc: end # TODO: Refactor this to make it less convoluted around making urls. constants.filter {|c| c.to_s != 'Camper'}.map { |c| k = const_get(c) k.include(C,X,Base,Helpers,Models) @r=[k]+@r if @r-[k]==@r mu = false # Should we make urls? ka = k.ancestors # This complicated code checks the ancestor chain of a controller to see it has it's own urls, # or if it's urls are from one of it's ancestors. ancestor URLs need to be discarded. if (k.respond_to?(:urls) && ka[1].respond_to?(:urls)) && (k.urls == ka[1].urls) mu = true unless ka[1].name == nil end k.meta_def(:urls){[A.(k,"#{c.to_s.scan(/.[^A-Z]*/).map(&N.method(:[]))*'/'}", p)]} if (!k.respond_to?(:urls) || mu == true) } end end # Internal controller with no route. Used to show internal messages. I = R() end X = Controllers class << self # Create method to setup routes for Camping upon reload. def make_camp X.M prx Apps.map(&:make_camp) end # Helper method for getting routes from the controllers. # helps Camping::Server map routes to multiple apps. # Usage: # # Nuts.routes # returns routes for Nuts # Camping.routes # def routes (Apps.map(&:routes)< e).to_a end # The Camping scriptable dispatcher. Any unhandled method call to the app # module will be sent to a controller class, specified as an argument. # # Blog.get(:Index) # #=> # # # The controller object contains all the @cookies, @body, @headers, etc. # formulated by the response. # # You can also feed environment variables and query variables as a hash, # the final argument. # # Blog.post(:Login, :input => {'username' => 'admin', 'password' => 'camping'}) # #=> # # # Blog.get(:Info, :env => {'HTTP_HOST' => 'wagon'}) # #=> #'wagon'} ...> # def method_missing(m, c, *a) h = Hash === a[-1] ? a.pop : {} e = H[Rack::MockRequest.env_for('',h.delete(:env)||{})] k = X.const_get(c).new(e,m.to_s,prx) # rescue => error # : wrong number of arguments # Campguide::make_sense error.message h.each { |i, v| k.send("#{i}=", v) } k.service(*a) end # Injects a middleware: # # module Blog # use Rack::MethodOverride # use Rack::Session::Memcache, :key => "session" # end # # This piece of code feels a bit confusing, but let's walk through it. # Rack apps all implement a Call method. This is how Ruby web servers # call the app, or code that you've set up. In our case, our camping # apps. # # The Use method is setting up a new middleware, it shifts the first # argument supplied to Use, which should be the Middleware name, then # initializes it. That's your new middleware. Rack based middleware accept # a single argument to their initialize methods, which is an app. Optionally # settings and a block are supplied. # # So a new app is made, and its settings are supplied, then immediately # sent to the new middleware we just added. But the cool part is where we # call meta_def. meta_def takes a symbol and a block, and defines a class # method into the current context. Our current context is our camping app. # So when we call it below we're redefining the call method to call the new # middleware that we just added. The `m` variable below represents our # newly created middleware object, that we initialized with our old app. and # because we're defining a new call method with a block, it's captured in # that block. # # This creates a sequence of middleware that isn't recorded anywhere, but # nonetheless is set up in the proper order and called in the proper order. def use(*a, &b) m = a.shift.new(method(:call), *a, &b) meta_def(:call) { |e| m.call(e) } m end # Add gear to your app: # # module Blog # pack Camping::Gear::CSRF # end # # Why have plugins in the first place if we can just include and extend our # modules and classes directly? To perform setup actions! # # Sometimes you might have ClassMethods that you want to modify Camping with, # This gives us a way to do that. In your gear: # # module MyGear # module ClassMethods # # Define Class Methods here # end # def self.included(mod) # mod.extend(ClassMethods) # end # end # # Optionally a plugin may have a setup method and a ClassMethods module: # # module MyGear # def self.setup(s) # # Perform setup actions # end # module ClassMethods # # Define Class Methods here # end # end # # Camping gear can also provide Helper methods to our controllers: # # module MyGear # module HelperMethods # # Define Helper Methods here # end # # # This is plumbing in our Gear to add our Helper methods. # class << self # def included(mod) # mod::Helpers.include(HelperMethods) # end # end # end # # Helper methods are available in our controllers. def pack(*a, &b) G << g = a.shift include g g.setup(self, *a, &b) # if g.respond_to?(:setup) # Force all gear to have a setup function end # Helper method to list gear def gear G end # A hash where you can set different settings. def options O end # Shortcut for setting options: # # module Blog # set :secret, "Hello!" # end def set(k, v) O[k] = v end # When you are running multiple applications, you may want to create # independent modules for each Camping application. Camping::goes # defines a top level constant with the whole MVC rack inside: # # require 'camping' # Camping.goes :Nuts # # module Nuts::Controllers; ... end # module Nuts::Models; ... end # module Nuts::Views; ... end # # Additionally, you can pass a Binding as the second parameter, # which enables you to create a Camping-based application within # another module. # # Here's an example of name spacing your web interface and # code for a worker process together: # # module YourApplication # Camping.goes :Web, binding() # module Web # ... # end # module Worker # ... # end # end # # All the applications will be available in Camping::Apps. # # Camping offers a shortcut for adding thin files, and templates to your apps. # Add them at the end of the same ruby file that you call `Camping.goes`: # # require 'camping' # Camping.goes :Nuts # # module Nuts::Controllers; ... end # module Nuts::Models; ... end # module Nuts::Views; ... end # # __END__ # # @@ /style.css # * { margin: 0; padding: 0 } # # @@ /test.foo #

Hello friends! Nice to meet you.

# # @@ index.erb # Hello <%= @world %> # # Also sets the apps Meta Data. Can be found at O[:_meta] # # @app: {String} - The app in question # @parent: {String} - # @root: {String} - # @line_number: {Int} - The line number that the app was declared # @file: {String} - The file location for this # def goes(m, g=TOPLEVEL_BINDING) # setup caller data sp = caller[0].split('`')[0].split(":") fl, ln = sp[0]+' ', sp[1].to_i # Create the app Apps << a = eval(S.gsub(/Camping/,m.to_s), g, fl, 1) caller[0]=~/:/ IO.read(a.set:__FILE__,$`)=~/^__END__/ && (b=$'.split(/^@@\s*(.+?)\s*\r?\n/m)).shift rescue nil a.set :_t,H[*b||[]] # setup parental data a.set :_meta, H[file: fl, line_number: ln, parent: self, root: (name != "Cam\ping" ? '/' + CampTools.to_snake(name) : '/')] # configures the app, using the kuddly options and # settings provided. C.configure(a) end end # Views is an empty module for storing methods which create HTML. The HTML # is described using the Markaby language. # # == Defining and calling templates # # Templates are simply Ruby methods with Markaby inside: # # module Blog::Views # def index # p "Welcome to my blog" # end # # def show # h1 @post.title # self << @post.content # end # end # # In your controllers you just call render :template_name which will # invoke the template. The views and controllers will share instance # variables (as you can see above). # # == Using the layout method # # If your Views module has a layout method defined, it will be # called with a block which will insert content from your view: # # module Blog::Views # def layout # html do # head { title "My Blog "} # body { self << yield } # end # end # end module Views; include X, Helpers end # Models is an empty Ruby module for housing model classes derived # from ActiveRecord::Base. As a shortcut, you may derive from Base # which is an alias for ActiveRecord::Base. # # module Camping::Models # class Post < Base; belongs_to :user end # class User < Base; has_many :posts end # end # # == Where Models are Used # # Models are used in your controller classes. However, if your model class # name conflicts with a controller class name, you will need to refer to it # using the Models module. # # module Camping::Controllers # class Post < R '/post/(\d+)' # def get(post_id) # @post = Models::Post.find post_id # render :index # end # end # end # # Models cannot be referred from Views at this time. module Models autoload :Base, 'camping/sequel' Helpers.include(X, self) end autoload :Mab, 'camping/mab' autoload :Template, 'camping/template' # Load default Gear pack Gear::Inspection pack Gear::Filters pack Gear::Nancy pack Gear::Kuddly pack Gear::Firewatch C end camping-3.2.6/lib/camping.rb000066400000000000000000000120751465547410100156650ustar00rootroot00000000000000require "cam\ping/loads";E||="content-type";Z||="text/html" class Object;def meta_def m,&b;(class<v,:path=>_p}.update o;end;def []=(k,v)set k,v,v.is_a?(Hash)?v:{} end end module Helpers;def R c,*g;p,h= /\(.+?\)/,g.grep(Hash);g-=h;raise"bad route"if !u=c.urls.find{|x|break x if x.scan(p).size==g.size&&/^#{x}\/?$/=~(x=g.inject(x){|x,a|x.sub p,U.escape((a. to_param rescue a))}.gsub(CampTools.descape){$1})};h.any?? u+"?"+U.build_query(h[0]) : u end;def /(p) p[0]==?/ ?(@root+@url_prefix.dup.prepend("/").chop+p) : p end def URL c='/',*a;c=R(c,*a)if c.respond_to?( :urls);c=self/c;c=@request.url[/.{8,}?(?=\/|$)/]+c if c[0]==?/;URI c end def app_name;"Camping"end end module Base;attr_accessor:env,:request,:root,:input,:cookies,:state,:status, :headers,:body,:url_prefix;T={};L=:layout def lookup n;T.fetch(n.to_sym){|k|t=Views. method_defined?(k)||(t=O[:_t].keys.grep(/^#{n}\./)[0]and Template[t].new{ O[:_t][t]})||(f=Dir[[O[:views]||"views","#{n}.*"]*'/'][0])&&Template. new(f,O[f[/\.(\w+)$/,1].to_sym]||{});O[:dynamic_templates]?t: T[k]=t}end def render v,*a,&b;if t=lookup(v);r=@_r;@_r=o=Hash===a[-1]?a.pop: {};s=(t==true)?mab{ send v,*a,&b}: t.render(self,o[:locals]||{},&b);s=render(L,o.merge(L=>false)){s } if o[L] or o[L].nil?&&lookup(L)&&!r&&v.to_s[0]!=?_;s else raise "no template: #{v}" end end def mab(&b)extend Mab;mab(&b)end def r s,b,h={};b,h=h,b if Hash===b;@status=s;@headers.merge!(h);@body=b end def redirect *a;r 302,'','Location'=>URL(*a).to_s;end def r404 p;P%"#{p} not found"end def r500 k,m,e;raise e end def r501 m;P % "#{m.upcase} not implemented"end def serve p,c;t=Rack::Mime.mime_type(p[/\..*$/], Z)and@headers[E]=t;c;end def to_a;@env['rack.session'][SK]=Hash[@state];r=Rack::Response.new(@body, @status,@headers);@cookies._n.each{|k,v|r.set_cookie k,v};r.to_a end def initialize env,m,p r=@request=Rack::Request.new(@env=env);@root,@input,@cookies,@state,@headers, @status,@method,@url_prefix=r.script_name.sub(/\/$/,''),n(r.params), Cookies[r.cookies],H[r.session[SK]||{}],{E=>Z},m=~/r(\d+)/?$1.to_i: 200,m,p @cookies._p=self/"/";end def n h;Hash===h ?h.inject(H[]){|m,(k,v)|m[k]=n(v);m}: h end def service *a;r=catch(:halt){send(@method,*a)};@body||=r;self end end module Controllers;@r=[];class Camper end;class<(c,u,p){d=p.dup;d.chop! if u=='';u.prepend("/"+d) if !["I"]. include? c.to_s if c.to_s=="Index";while d[-1]=="/";d.chop! end;u.prepend("/"+d)end;u} N=H.new{|_,x|x.downcase}.merge! "N"=>'(\d+)',"X"=>'([^/]+)',"Index"=>'' def M p;def M p;end constants.filter{|c|c.to_s!='Camper'}.map{|c|k=const_get(c); k.include(C,X,Base,Helpers,Models) @r=[k]+@r if @r-[k]==@r;mu=false;ka=k.ancestors if (k.respond_to?(:urls) && ka[1].respond_to?(:urls)) && (k.urls == ka[1].urls) mu = true unless ka[1].name == nil end k.meta_def(:urls){[A.(k,"#{c.to_s.scan(/.[^A-Z]*/).map(&N.method(:[]))*'/'}",p)]} if (!k .respond_to?(:urls) || mu==true)};end end;I=R()end;X=Controllers class<e).to_a end def method_missing m,c,*a;h=Hash===a[-1]?a.pop : {};e=H[Rack::MockRequest. env_for('',h.delete(:env)||{})];k=X.const_get(c).new(e,m.to_s,prx);h.each{|i,v| k.send"#{i}=",v};k.service(*a)end def use*a,&b;m=a.shift.new(method(:call),*a,&b);meta_def(:call){|e|m.call(e)};m;end def pack*a,&b;G<< g=a.shift;include g;g.setup(self,*a,&b)end def gear;G end;def options;O;end;def set k,v;O[k]=v end def goes m,g=TOPLEVEL_BINDING;sp=caller[0].split('`')[0].split(":");fl,ln= sp[0]+' ',sp[1].to_i;Apps<< a=eval(S.gsub(/Camping/,m.to_s),g,fl,1);caller[0]=~/:/ IO.read(a.set:__FILE__,$`)=~/^__END__/&&(b=$'.split(/^@@\s*(.+?)\s*\r?\n/m) ).shift rescue nil;a.set :_t,H[*b||[]] a.set :_meta, H[file: fl, line_number: ln, parent: self, root: (name != "Cam\ping" ? '/' + CampTools.to_snake(name) : '/')];C.configure(a)end end module Views;include X,Helpers end;module Models autoload :Base, 'camping/sequel' Helpers.include(X,self) end;autoload:Mab,'camping/mab' autoload:Template,'camping/template';pack Gear::Inspection;pack Gear::Filters pack Gear::Nancy;pack Gear::Kuddly;pack Gear::Firewatch;C end camping-3.2.6/lib/camping/000077500000000000000000000000001465547410100153335ustar00rootroot00000000000000camping-3.2.6/lib/camping/ar.rb000066400000000000000000000047771465547410100163010ustar00rootroot00000000000000class MissingLibrary < Exception #:nodoc: all end begin require 'active_record' rescue LoadError => e raise MissingLibrary, "ActiveRecord could not be loaded (is it installed?): #{e.message}" end $AR_EXTRAS = %{ Base = ActiveRecord::Base unless const_defined? :Base class ActiveRecordCloser def initialize(app) @app = app end def call(env) @app.call(env) ensure conn = ActiveRecord::Base.connection conn.close if conn.respond_to?(:close) end end Camping.use ActiveRecordCloser class SchemaInfo < Base end def self.V(n) @final = [n, @final.to_f].max m = (@migrations ||= []) Class.new(ActiveRecord::Migration[6.1]) do meta_def(:version) { n } meta_def(:inherited) { |k| m << k } end end def self.create_schema(opts = {}) opts[:assume] ||= 0 opts[:version] ||= @final if @migrations unless SchemaInfo.table_exists? ActiveRecord::Schema.define do create_table SchemaInfo.table_name do |t| t.column :version, :float end end end si = SchemaInfo.first || SchemaInfo.new(:version => opts[:assume]) if si.version < opts[:version] @migrations.sort_by { |m| m.version }.each do |k| k.migrate(:up) if si.version < k.version and k.version <= opts[:version] k.migrate(:down) if si.version > k.version and k.version > opts[:version] end si.update(:version => opts[:version]) end end end } module Camping module Models A = ActiveRecord # Base is an alias for ActiveRecord::Base. The big warning I'm going to give you # about this: *Base overloads table_name_prefix.* This means that if you have a # model class Blog::Models::Post, it's table name will be blog_posts. # # ActiveRecord is not loaded if you never reference this class. The minute you # use the ActiveRecord or Camping::Models::Base class, then the ActiveRecord library # is loaded. Base = A::Base # The default prefix for Camping model classes is the topmost module name lowercase # and followed with an underscore. # # Tepee::Models::Page.table_name_prefix # #=> "tepee_pages" # def Base.table_name_prefix "#{name[/\w+/]}_".downcase.sub(/^(#{A}|camping)_/i,'') end module_eval $AR_EXTRAS end end Camping::S.sub!(/autoload\s*:Base\s*,\s*['"]camping\/ar['"]/, $AR_EXTRAS) Camping::Apps.each do |c| c::Models.module_eval $AR_EXTRAS.gsub('Camping', c.to_s) end camping-3.2.6/lib/camping/campguide.rb000066400000000000000000000011361465547410100176170ustar00rootroot00000000000000# Campguide is a small helper to map common errors in a Camping project to a quick resolution. module Campguide Errors = {"wrong number of arguments (given 3, expected 0)" => "ArgumentError. This is sometimes caused when you try to send a request to a controller when a camping app hasn't made camp yet. make certain to call Camping.make_camp to set up your apps."} class << self # accepts string error messages and tries to match them with better explanations of the error. def make_sense(error_message) message = Errors[error_message] puts message ? message : error_message end end end camping-3.2.6/lib/camping/commands.rb000066400000000000000000000217361465547410100174720ustar00rootroot00000000000000module Camping module CommandsHelpers # transform app_name to snake case def self.to_snake_case(string) string = string.to_s if string.class == Symbol string.gsub(/::/, '/'). gsub(/([A-Z]+)([A-Z][a-z])/,'\1_\2'). gsub(/([a-z\d])([A-Z])/,'\1_\2'). tr("-", "_"). downcase end # transform app_name to camel Case def self.to_camel_case(string) cammelled = "" to_snake_case(string).split("_").each do |seq| cammelled << seq.capitalize end cammelled end # Helper method that generates an app name from command line input. def self.app_name_from_input(app_name) app_name = :Camp if app_name == nil app_name = app_name.to_sym if app_name.class == String snake_app_name = to_snake_case(app_name) camel_app_name = to_camel_case(snake_app_name) {app_name: camel_app_name.to_sym, snake_name: snake_app_name, camel_name: camel_app_name} end RouteCollection = Struct.new(:routes) class RouteCollection # Displays formatted routes from a route collection # Assumes that Route structs are stored in :routes. def display current_app, current_controller, current_method = "", "", "" puts "App VERB Route" routes.each { |r| if current_app != r.app.to_s current_app = r.app.to_s puts "-----------------------------------" puts r.app_header end if current_controller != r.controller.to_s current_controller = r.controller.to_s puts r.controller_header end puts r.padded_message true } end end # Route Struct, for making and formatting a route. Route = Struct.new(:http_method, :controller, :app, :url) class Route def to_s "#{controller}: #{http_method}: #{url} - #{replace_reg url}" end # pad the controller name to be the right length, if we can. def padded_message(with_method = false) "#{pad}#{(with_method ? http_method.to_s.upcase.ljust(pad.length, " ") : pad)}#{replace_reg url}" end def app_header "#{app.to_s}" end def controller_header "#{pad}#{app.to_s}::#{controller.to_s}" end protected def http_methods ["get", "post", "put", "patch", "delete"] end def replace_reg(pattern = "") xstr = "([^/]+)"; nstr = "(\d+)" pattern = pattern.gsub(xstr, ":string").gsub("(\\d+)", ":integer") unless pattern == "/" pattern end def pad " " end end class RoutesParser def self.parse(app) new(app).parse end def initialize(app = Camping) @parent_app, @routes = app, [] end def parse routes = @parent_app.make_camp collected_routes = [] make_routes = -> (a) { a::X.all.map {|c| k = a::X.const_get(c) im = k.instance_methods(false).map!(&:to_s) methods = im & ["get", "post", "put", "patch", "delete"] if k.respond_to?:urls methods.each { |m| k.urls.each { |u| collected_routes.append Camping::CommandsHelpers::Route.new(m,c,a.to_s,u) } } end } } if @parent_app == Camping @parent_app::Apps.each {|a| make_routes.(a) } else make_routes.(@parent_app) end routes_collection = Camping::CommandsHelpers::RouteCollection.new(collected_routes) end end end class Generators class << self # write a file def write(file, content) raise "Cannot write to nil file." unless file folder = File.dirname(file) `mkdir -p #{folder}` unless File.exist?(folder) File.open(file, 'w') { |f| f.write content } end # read a file def read(file) File.read(file) end def make_camp_file(app_name="Tent") write "camp.rb", <<-RUBY require 'camping' Camping.goes :#{app_name} module #{app_name} module Models end module Controllers class Index def get @title = "#{app_name}" render :index end end end module Helpers end module Views def layout html do head do title '#{app_name}' link :rel => 'stylesheet', :type => 'text/css', :href => '/styles.css', :media => 'screen' end body do h1 '#{app_name}' div.wrapper! do self << yield end end end end def index h2 "Let's go Camping" end end end RUBY end # makes a gitignore. def make_gitignore write '.gitignore', <<-GIT .DS_Store node_modules/ tmp/ db/camping.db db/camping.sqlite3 db/camping.sqlite GIT end def make_ruby_version write '.ruby-version', <<-RUBY #{RUBY_VERSION} RUBY end # writes a rakefile def make_rakefile write 'Rakefile', <<-TXT # Rakefile require 'rake' require 'rake/clean' require 'rake/testtask' require 'tempfile' require 'open3' task :default => :test task :test => 'test:all' namespace 'test' do Rake::TestTask.new('all') do |t| t.libs << 'test' t.test_files = FileList['test/test_*.rb'] end end TXT end # write a config.kdl def make_configkdl write 'config.kdl', <<-KDL // config.kdl hostname "localhost" KDL end # write a Gemfile def make_gemfile write 'Gemfile', <<-GEM # frozen_string_literal: true source 'https://rubygems.org' gem 'camping' gem 'falcon' gem 'rake' group :production do gem 'rack-ssl-enforcer' end group :development do end group :test do gem 'minitest', '~> 5.0' gem 'minitest-reporters' gem 'rack-test' gem 'minitest-hooks' end GEM end # write a README.md def make_readme write 'README.md', <<-READ # Camping Camping is really fun and I hope you enjoy it. Start camping by running: `camping` in the root directory. To start Camping in development mode run: `camping -e development READ end def make_public_folder Dir.mkdir("public") unless Dir.exist?("public") end def make_test_folder Dir.mkdir("test") unless Dir.exist?("test") write 'test/test_helper.rb', <<-RUBY $:.unshift File.dirname(__FILE__) + '/../' # shift to act like we're in the regular degular directory begin require 'rubygems' rescue LoadError end require 'camping' require 'minitest/autorun' require 'minitest' require 'rack/test' require "minitest/reporters" Minitest::Reporters.use! [Minitest::Reporters::DefaultReporter.new(:color => true)] class TestCase < Minitest::Test include Rack::Test::Methods def self.inherited(mod) mod.app = Object.const_get(mod.to_s[/\w+/]) super end class << self attr_accessor :app end def body() last_response.body end def app() self.class.app end def assert_reverse begin yield rescue Exception else assert false, "Block didn't fail" end end def assert_body(str) case str when Regexp assert_match(str, last_response.body.strip) else assert_equal(str.to_s, last_response.body.strip) end end def assert_status(code) assert_equal(code, last_response.status) end def test_silly; end end RUBY end end end class Commands class << self # A helper method to spit out Routes for an application def routes(theApp = Camping, silent = false) routes = Camping::CommandsHelpers::RoutesParser.parse theApp routes.display unless silent == true return routes end def new_cmd(app_name=:Camp) # Normalize the app_name Camping::CommandsHelpers.app_name_from_input(app_name) => {app_name:, snake_name:, camel_name:} # make a directory then move there. # _original_dir = Dir.pwd Dir.mkdir("#{snake_name}") unless Dir.exist?("#{snake_name}") Dir.chdir("#{snake_name}") # generate a new camping app in a directory named after it: Generators::make_camp_file(camel_name) Generators::make_gitignore() Generators::make_rakefile() Generators::make_ruby_version() Generators::make_configkdl() Generators::make_gemfile() Generators::make_readme() Generators::make_public_folder() Generators::make_test_folder() # optionally add omnibus support # add src/ folder # add lib/ folder # add views/ folder # optionally add a local database too, through guidebook # add db/ folder # add db/migrate folder # add db/config.kdl # append migrations stuff to Rakefile. `ls` end # TODO: Create this generator # generates the apps folder from apps found in camp.rb or config.ru # def generate_apps_folder # end end end end camping-3.2.6/lib/camping/loader.rb000066400000000000000000000160331465547410100171310ustar00rootroot00000000000000require 'zeitwerk' require 'listen' module Camping # == The Camping Reloader # # Camping apps are generally small and predictable. Many Camping apps are # contained within a single file. Larger apps are split into a handful of # other Ruby libraries within the same directory. # # Since Camping apps (and their dependencies) are loaded with Ruby's require # method, there is a record of them in $LOADED_FEATURES. Which leaves a # perfect space for this class to manage auto-reloading an app if any of its # immediate dependencies changes. # # == Wrapping Your Apps # # Since bin/camping and the Camping::Server class already use the Reloader, # you probably don't need to hack it on your own. But, if you're rolling your # own situation, here's how. # # Rather than this: # # require 'yourapp' # # Use this: # # require 'camping/reloader' # reloader = Camping::Reloader.new('/path/to/yourapp.rb') # blog = reloader.apps[:Blog] # wiki = reloader.apps[:Wiki] # # The blog and wiki objects will behave exactly like your # Blog and Wiki, but they will update themselves if yourapp.rb changes. # # You can also give Reloader more than one script. class Loader attr_reader :file Loaders = [] def initialize(file=nil, &blk) @file = file @mtime = Time.at(0) @requires = [] @apps = {} @callback = blk @root = Dir.pwd @file = @root + '/camp.rb' if @file == nil @zeit = Zeitwerk::Loader.new Loaders << @zeit # setup Zeit for this reloader setup_zeit(@zeit) dirs = [@root] dirs << "#{@root}/apps" if Dir.exist? "#{@root}/apps" dirs << "#{@root}/lib" if Dir.exist? "#{@root}/lib" # setup recursive listener on the apps and lib directories from the source script. @listener = Listen.to(*dirs) do |modified, added, removed| @mtime = Time.now reload! end start end # pass through methods to the Listener. # for testing purposes. def processing_events? = @listener.processing? def stop = @listener.stop def pause = @listener.pause def start = @listener.start def name @name ||= begin base = @file.dup base = File.dirname(base) if base =~ /\bconfig\.ru$/ base.sub!(/\.[^.]+/, '') File.basename(base).to_sym end end # remove_constants called inside this. def load_everything() all_requires = $LOADED_FEATURES.dup all_apps = Camping::Apps.dup # Zeitwerk will Autoload stuff, which is great. But we don't want Zeitwerk # autoloading when we evaluate the camp.rb file because it will try to # autoload any controllers and helpers that we've defined in there from # the descendant /apps and /lib directory, which then make it break. @zeit.unload load_file @zeit.setup reload_directory("#{@root}/apps") reload_directory("#{@root}/lib") Camping.make_camp ensure @requires = [] new_apps = Camping::Apps - all_apps @apps = new_apps.inject({}) do |hash, app| if file = app.options[:__FILE__] full = File.expand_path(file) @requires << [file, full] end key = app.name.to_sym hash[key] = app if !@apps.include?(key) @callback.call(app) if @callback app.create if app.respond_to?(:create) app.kindling if app.respond_to?(:kindling) end hash end ($LOADED_FEATURES - all_requires).each do |req| full = full_path(req) @requires << [req, full] # if dirs.any? { |x| full.index(x) == 0 } end @mtime = mtime self end # load_file # # Rack::Builder is mainly used to parse a config.ru file and to # build a rack app with middleware from that. def load_file if @file =~ /\.ru$/ @app = Rack::Builder.parse_file(@file) else load(@file) end @requires << [@file, File.expand_path(@file)] end # removes all constants recursively included using this script as a root. # so everything in /apps, and /lib in relation from this script. def remove_constants @requires.each do |(path, full)| $LOADED_FEATURES.delete(path) unless path.match? "concurrent-ruby" end @apps.each do |name, app| Camping::Apps.delete(app) Object.send :remove_const, name end.dup ensure @apps.clear @requires.clear end # Reloads the file if needed. No harm is done by calling this multiple # times, so feel free call just to be sure. def reload return if @mtime >= mtime rescue nil reload! end # Force a reload. def reload! remove_constants load_everything end # Checks if both scripts watches the same file. def ==(other) @file == other.file end def apps if @app { name => @app } else @apps end end private # sets up Zeit autoloading for the script locations. def setup_zeit(loader) loader.push_dir("#{@root}/apps") if can_add_directory "#{@root}/apps" loader.push_dir("#{@root}/lib") if can_add_directory "#{@root}/lib" loader.ignore("#{@root}/lib/camping-unabridged.rb") loader.enable_reloading if ENV['environment'] == 'development' loader.setup end # verifies that we can add a directory to the loader. # used for testing to prevent multiple loaders from watching the same directory. def can_add_directory(directory) if Dir.exist?(directory) Loaders.each do |loader| return false if loader.dirs.include? directory end true else false end end # Splits the descendent files and folders found in a given directory for eager loading and recursion. # If the given directory doesn't exist or is empty, then nothing is returned. def folders_and_files_in(directory) directory = directory + "/*" # unless directory [Dir.glob(directory).select {|f| !File.directory? f }, Dir.glob(directory).select {|f| File.directory? f }] end # Reloads a directory recursively. loading more shallow files before deeper files. # If the given directory doesn't exist or is empty, then nothing happens. def reload_directory(directory) files, folders = folders_and_files_in(directory) files.each {|file| next if file.include? "unabridged" @requires << [file, File.expand_path(file)] load file } folders.each {|folder| reload_directory folder } end def mtime @requires.map do |(path, full)| File.mtime(full) end.reject {|t| t > Time.now }.max || Time.now end # Figures out the full path of a required file. def full_path(req) return req if File.exist?(req) dir = $LOAD_PATH.detect { |l| File.exist?(File.join(l, req)) } if dir File.expand_path(req, File.expand_path(dir)) else req end end end Reloader = Loader end camping-3.2.6/lib/camping/loads.rb000066400000000000000000000005021465547410100167570ustar00rootroot00000000000000# Loads things into camping.rb # external dependencies require "uri" require "rack" require 'rubygems' require 'bundler/setup' # internal dependencies require 'camping/tools' require 'camping/campguide' require 'gear/filters' require 'gear/nancy' require 'gear/inspection' require 'gear/kuddly' require 'gear/firewatch' camping-3.2.6/lib/camping/mab.rb000066400000000000000000000015651465547410100164260ustar00rootroot00000000000000class MissingLibrary < Exception #:nodoc: all end begin require 'mab' rescue LoadError => e raise MissingLibrary, "Mab could not be loaded (is it installed?): #{e.message}" end $MAB_CODE = %q{ module Mab include ::Mab::Mixin::HTML5 include Views alias << text! def xhtml(*a, &b) warn "xhtml_strict is no longer supported (or an active standard); using HTML5 instead" html(*a, &b) end def xhtml_strict(*a, &b) xhtml(*a, &b) end def xhtml_transitional(*a, &b) xhtml(*a, &b) end def xhtml_frameset(*a, &b) xhtml(*a, &b) end def helpers() self end def html(*) doctype!; super end def mab_done(tag) h=tag._attributes [:href,:action,:src].map{|a|h[a]&&=self/h[a]} end end } Camping::S.sub!(/autoload\s*:Mab\s*,\s*['"]camping\/mab['"]/, $MAB_CODE) Camping::Apps.each do |c| c.module_eval $MAB_CODE end camping-3.2.6/lib/camping/sequel.rb000066400000000000000000000013001465547410100171500ustar00rootroot00000000000000class MissingLibrary < Exception #:nodoc: all end begin require 'sequel' rescue LoadError => e raise MissingLibrary, "Sequel gem could not be loaded (is it installed?): #{e.message}" end $SEQUEL_EXTRAS = %{ Sequel::Model.plugin :timestamps Base = Sequel::Model # class SchemaInfo < Base # end } module Camping module Models module_eval $SEQUEL_EXTRAS end end Camping::S.sub!(/autoload\s*:Base\s*,\s*['"]camping\/sequel['"]/, $SEQUEL_EXTRAS) Camping::Apps.each do |c| c::Models.module_eval $SEQUEL_EXTRAS.gsub('Camping', c.to_s) end ENV['DATABASE_URL'] = 'db/camp.db' if ENV['DATABASE_URL'] == '' # connect to a database by default DB = Sequel.connect(ENV['DATABASE_URL']) camping-3.2.6/lib/camping/server.rb000066400000000000000000000174431465547410100171770ustar00rootroot00000000000000require 'irb' require 'erb' require 'rack' require 'rackup' require 'camping/version' require 'gear/firewatch' require 'camping/loader' require 'camping/commands' # == The Camping Server (for development) # # Camping includes a pretty nifty server which is built for development. # It follows these rules: # # * Load all Camping apps in a file. # * Mount those apps according to their name. (e.g. Blog is mounted at /blog.) # * Run each app's create method upon startup. # * Reload the app if its modification time changes. # * Reload the app if it requires any files under the same directory and one # of their modification times changes. # * Support the X-Sendfile header. # # Run it like this: # # camping blog.rb # Mounts Blog at / # # And visit http://localhost:3301/ in your browser. module Camping class Server < Rackup::Server class Options def parse!(args) args = args.dup options = {} opt_parser = OptionParser.new("", 24, ' ') do |opts| opts.banner = "Usage: camping Or: camping my-camping-app.rb" # opts.define_head "#{File.basename($0)}, the microframework ON-button for ruby #{RUBY_VERSION} (#{RUBY_RELEASE_DATE}) [#{RUBY_PLATFORM}]" opts.separator "" opts.separator "Specific options:" opts.on("-h", "--host HOSTNAME", "Host for web server to bind to (default is all IPs)") { |v| options[:Host] = v } opts.on("-p", "--port NUM", "Port for web server (defaults to 3301)") { |v| options[:Port] = v } opts.on("-c", "--console", "Run in console mode with IRB") { options[:server] = "console" } opts.on("-e", "--env ENVIRONMENT", "Sets the environment. (defaults: development)") { |v| options[:environment] = ENV['environment'] = v } server_list = ["thin", "webrick", "console", "puma", "tipi", "falcon"] opts.on("-s", "--server NAME", "Server to force (#{server_list.join(', ')})") { |v| options[:server] = v } opts.separator "" opts.separator "Common options:" # No argument, shows at tail. This will print an options summary. # Try it and see! opts.on("-?", "--help", "Show this message") do puts opts exit end # Another typical switch to print the version. opts.on("-v", "--version", "Show version") { options[:version] = true } # Show Routes opts.on("-r", "--routes", "Show Routes") { options[:routes] = true } end opt_parser.parse!(args) if args.empty? args << "camp.rb" end options[:script] = args.shift options end end def initialize(*) super @reloader = Camping::Reloader.new(options[:script]) do |app| if !app.options.has_key?(:dynamic_templates) app.options[:dynamic_templates] = true end end end def loader @reloader || nil end def opt_parser Options.new end def default_options super.merge({ :Port => 3301 }) end # redefine logging middleware class << self def logging_middleware lambda { |server| /CGI/.match?(server.server.name) || server.options[:quiet] ? nil : [Camping::Firewatch, $stderr] } end end def middleware h = super h["development"] << [XSendfile] h["deployment"] << [XSendfile] h end # Starts the Camping Server. Camping server inherits from Rack::Server so # referencing their documentation would be a good idea. # @file: String, file location for a camp.rb file. def start(file = nil) commands = ARGV # Parse commands case commands[0] when "new" Camping::Commands.new_cmd(commands[1]) exit end if options[:version] == true puts "Camping v#{Camping::VERSION}" exit end Dir['kindling/*.rb'].each do |kindling| require_relative File.expand_path(kindling) end @reloader.reload! r = @reloader if options[:routes] == true eval("self", TOPLEVEL_BINDING).meta_def(:reload!) { r.reload!; nil } ARGV.clear Camping::Commands.routes exit end if options[:server] == "console" puts "** Starting console" eval("self", TOPLEVEL_BINDING).meta_def(:reload!) { r.reload!; nil } ARGV.clear IRB.start exit else name = server.name[/\w+$/] puts "** Starting #{name} on #{options[:Host]}:#{options[:Port]}" super() end end # defines the public directory to be /public def public_dir File.expand_path('../public', @reloader.file) end # add the public directory as a Rack app serving files first, then the # current value of self, which is our camping apps, as an app. def app Rack::Cascade.new([Rack::Files.new(public_dir), self], [405, 404, 403]) end # path_matches? # accepts a regular expression string # in our case our apps and controllers def path_matches?(path, *reg) p = T.(path) reg.each do |r| return true if Regexp.new(T.(r)).match?(p) && p == T.(r) end false end # Ensure trailing slash lambda T ||= -> (u) { u << "/" if u[-1] != "/"; u } # call(env) res # == How routing works # # The first app added using Camping.goes is set at the root, we walk through # the defined routes of the first app to see if there is a match. # With no match we then walk through every other defined app. # When we reach a matching route we call that app and Camping's router # handles the rest. # # Mounting apps at different directories is now explicit by setting the # url_prefix option: # # camping.goes :Nuts # Mounts Nuts at / # module Auth # set :url_prefix, "auth/" # end # camping.goes :Auth # Mounts Auth at /auth/ # camping.goes :Blog # Mounts Blog at / # # Note that routes that you set explicitly with R are not prefixed. This # us explicit control over routes: # # module Auth::Controllers # class Whatever < R '/thing/' # Mounts at /thing/ # def get # render :some_view # end # end # end # def call(env) if ENV['environment'] == 'development' @reloader.reload end # our switch statement iterates through possible app outcomes, no apps # loaded, one app loaded, or multiple apps loaded. case @reloader.apps.length when 0 [200, {'content-type' => 'text/html'}, ["I'm sorry but no apps were found."]] when 1 @reloader.apps.values.first.call(env) # When we have one else # 2 and up get special treatment @reloader.apps.each do |name, app| app.routes.each do |r| if (path_matches?(env['PATH_INFO'], r)) return app.call(env) next end end end # Just return the first app if we didn't find a match. @reloader.apps.values.first.call(env) end end class XSendfile def initialize(app) @app = app end def call(env) status, headers, body = @app.call(env) if key = headers.keys.grep(/X-Sendfile/i).first filename = headers[key] content = open(filename,'rb') { | io | io.read} headers['Content-Length'] = size(content).to_s body = [content] end return status, headers, body end if "".respond_to?(:bytesize) def size(str) str.bytesize end else def size(str) str.size end end end end end camping-3.2.6/lib/camping/session.rb000066400000000000000000000024451465547410100173500ustar00rootroot00000000000000require 'rack/session' class InsecureSecretError < Exception #:nodoc: all end module Camping # == Getting Started # # To get sessions working for your application: # 1. require 'camping/session' # 2. Define a secret (and keep it secret): set :secret, "SECRET!" # 3. Mixin the module: include Camping::Session # 4. Throughout your application, use the @state var like a hash # to store your application's data. # # require 'camping/session' # 1 # # module Nuts # set :secret, "Oh yeah!" # 2 # include Camping::Session # 3 # end # # == Other backends # # Camping only ships with session-cookies. However, the @state # variable is simply a shortcut for @env['rack.session']. Therefore # you can also use any middleware which sets this variable: # # module Nuts # use Rack::Session::Memcache # end module Session def self.included(app) key = "#{app}.state".downcase secret = app.options[:secret] || ['camping-secret',__FILE__, File.mtime('Rakefile')].join(":")*2 raise InsecureSecretError, "Your Session Secret is too short. Minimum length is 64." if secret.length < 64 app.use Rack::Session::Cookie, :key => key, :secrets => secret end end end camping-3.2.6/lib/camping/template.rb000066400000000000000000000005561465547410100175010ustar00rootroot00000000000000class MissingLibrary < Exception #:nodoc: all end begin require 'tilt' rescue LoadError => e raise MissingLibrary, "Tilt could not be loaded (is it installed?): #{e.message}" end $TILT_CODE = %{ Template = Tilt } Camping::S.sub!(/autoload\s*:Template\s*,\s*['"]camping\/template['"]/, $TILT_CODE) Camping::Apps.each do |c| c.module_eval $TILT_CODE end camping-3.2.6/lib/camping/tools.rb000066400000000000000000000021641465547410100170230ustar00rootroot00000000000000# frozen_string_literal: true # Camping Tools is a toolbox for Camping module Camping module Tools class << self # to_snake # Accepts a string and snake Cases it. # Also accepts symbols and coerces them into a string. def to_snake(string) string = string.to_s if string.class == Symbol string.gsub(/::/, '/'). gsub(/([A-Z]+)([A-Z][a-z])/,'\1_\2'). gsub(/([a-z\d])([A-Z])/,'\1_\2'). tr("-", "_"). downcase end # Normalize Slashes # normalizes the leading and trailing slashes of a string to only have a # single trailing slash and no leading slashes. def normalize_slashes(string) f = string.dup return "" if f == "" # Short circuit for blank prefixes. f.chop!until f[-1] != "/" f.slice!(0)until f[0] != "/" f << "/" end alias norms normalize_slashes # A Regex to descape escaped characters. # used to correct URLs that are escpaed using Rack Util's escape function. def descape /\\(.)/ end end end end CampTools = 🏕 = Camping::Tools camping-3.2.6/lib/camping/version.rb000066400000000000000000000001141465547410100173410ustar00rootroot00000000000000module Camping VERSION = "3.2.6" def self.version VERSION end end camping-3.2.6/lib/gear/000077500000000000000000000000001465547410100146335ustar00rootroot00000000000000camping-3.2.6/lib/gear/filters.rb000066400000000000000000000023011465547410100166240ustar00rootroot00000000000000# This gear is originally from filtering_camping gem by judofyr, and techarc. module Gear module Filters module ClassMethods def filters @_filters ||= {:before => [], :after => []} end def before(actions, &blk) actions = [actions] unless actions.is_a?(Array) actions.each do |action| filters[:before] << [action, blk] end end def after(actions, &blk) actions = [actions] unless actions.is_a?(Array) actions.each do |action| filters[:after] << [action, blk] end end end def self.included(mod) mod.extend(ClassMethods) end def self.setup(app, *a, &block) end def run_filters(type) o = self.class.to_s.split("::") filters = Object.const_get(o.first).filters filters[type].each do |filter| if (filter[0].is_a?(Symbol) && (filter[0] == o.last.to_sym || filter[0] == :all)) || (filter[0].is_a?(String) && /^#{filter[0]}\/?$/ =~ @env.REQUEST_URI) self.instance_eval(&filter[1]) end end end def service(*a) run_filters(:before) super(*a) run_filters(:after) self end end end camping-3.2.6/lib/gear/firewatch.rb000066400000000000000000000036401465547410100171370ustar00rootroot00000000000000require 'rack' require 'rack/utils' require 'rack/common_logger' require 'dry/logger' # Firewatch is Camping's logger. # It wraps Rack::CommonLogger, and gives a mechanism to Redirect logs. module Camping class Firewatch < Rack::CommonLogger class << self def logger @logger ||= default_logger end def logger=(new_logger) @logger = new_logger end def default_logger Dry.Logger(:Camping, template: default_template).add_backend(stream: "logs/development.log") end def default_template "[%s - %