From df43c987cf02a3521ac65cf5bd4a4f54cf749177 Mon Sep 17 00:00:00 2001 From: Klaus Silveira Date: Fri, 18 May 2012 01:38:33 -0300 Subject: [PATCH] Initial commit --- .gitignore | 28 + .htaccess | 7 + .travis.yml | 5 + LICENSE.txt | 9 + README.md | 53 + config.ini | 6 + controllers/blobController.php | 32 + controllers/commitController.php | 79 ++ controllers/indexController.php | 10 + controllers/rssController.php | 16 + controllers/statsController.php | 20 + controllers/treeController.php | 67 + index.php | 52 + lib/Application/Utils.php | 206 +++ lib/Application/UtilsServiceProvider.php | 22 + lib/Git/Client.php | 122 ++ lib/Git/Commit/Author.php | 35 + lib/Git/Commit/Commit.php | 148 +++ lib/Git/GitServiceProvider.php | 23 + lib/Git/Model/Blob.php | 67 + lib/Git/Model/Diff.php | 60 + lib/Git/Model/Line.php | 48 + lib/Git/Model/Symlink.php | 44 + lib/Git/Model/Tree.php | 172 +++ lib/Git/Repository.php | 465 +++++++ lib/Git/ScopeAware.php | 29 + phpunit.xml.dist | 18 + tests/ClientTest.php | 371 ++++++ vendor/Twig/Autoloader.php | 46 + vendor/Twig/Compiler.php | 242 ++++ vendor/Twig/CompilerInterface.php | 35 + vendor/Twig/Environment.php | 1106 +++++++++++++++++ vendor/Twig/Error.php | 199 +++ vendor/Twig/Error/Loader.php | 20 + vendor/Twig/Error/Runtime.php | 21 + vendor/Twig/Error/Syntax.php | 21 + vendor/Twig/ExpressionParser.php | 488 ++++++++ vendor/Twig/Extension.php | 93 ++ vendor/Twig/Extension/Core.php | 1037 ++++++++++++++++ vendor/Twig/Extension/Debug.php | 64 + vendor/Twig/Extension/Escaper.php | 106 ++ vendor/Twig/Extension/Optimizer.php | 35 + vendor/Twig/Extension/Sandbox.php | 112 ++ vendor/Twig/ExtensionInterface.php | 84 ++ vendor/Twig/Filter.php | 75 ++ vendor/Twig/Filter/Function.php | 33 + vendor/Twig/Filter/Method.php | 34 + vendor/Twig/Filter/Node.php | 37 + vendor/Twig/FilterInterface.php | 40 + vendor/Twig/Function.php | 63 + vendor/Twig/Function/Function.php | 34 + vendor/Twig/Function/Method.php | 35 + vendor/Twig/Function/Node.php | 37 + vendor/Twig/FunctionInterface.php | 37 + vendor/Twig/Lexer.php | 406 ++++++ vendor/Twig/LexerInterface.php | 29 + vendor/Twig/Loader/Array.php | 102 ++ vendor/Twig/Loader/Chain.php | 100 ++ vendor/Twig/Loader/Filesystem.php | 152 +++ vendor/Twig/Loader/String.php | 59 + vendor/Twig/LoaderInterface.php | 53 + vendor/Twig/Markup.php | 38 + vendor/Twig/Node.php | 227 ++++ vendor/Twig/Node/AutoEscape.php | 40 + vendor/Twig/Node/Block.php | 45 + vendor/Twig/Node/BlockReference.php | 38 + vendor/Twig/Node/Body.php | 20 + vendor/Twig/Node/Do.php | 39 + vendor/Twig/Node/Embed.php | 39 + vendor/Twig/Node/Expression.php | 21 + vendor/Twig/Node/Expression/Array.php | 86 ++ vendor/Twig/Node/Expression/AssignName.php | 28 + vendor/Twig/Node/Expression/Binary.php | 40 + vendor/Twig/Node/Expression/Binary/Add.php | 18 + vendor/Twig/Node/Expression/Binary/And.php | 18 + .../Node/Expression/Binary/BitwiseAnd.php | 18 + .../Twig/Node/Expression/Binary/BitwiseOr.php | 18 + .../Node/Expression/Binary/BitwiseXor.php | 18 + vendor/Twig/Node/Expression/Binary/Concat.php | 18 + vendor/Twig/Node/Expression/Binary/Div.php | 18 + vendor/Twig/Node/Expression/Binary/Equal.php | 17 + .../Twig/Node/Expression/Binary/FloorDiv.php | 29 + .../Twig/Node/Expression/Binary/Greater.php | 17 + .../Node/Expression/Binary/GreaterEqual.php | 17 + vendor/Twig/Node/Expression/Binary/In.php | 33 + vendor/Twig/Node/Expression/Binary/Less.php | 17 + .../Twig/Node/Expression/Binary/LessEqual.php | 17 + vendor/Twig/Node/Expression/Binary/Mod.php | 18 + vendor/Twig/Node/Expression/Binary/Mul.php | 18 + .../Twig/Node/Expression/Binary/NotEqual.php | 17 + vendor/Twig/Node/Expression/Binary/NotIn.php | 33 + vendor/Twig/Node/Expression/Binary/Or.php | 18 + vendor/Twig/Node/Expression/Binary/Power.php | 33 + vendor/Twig/Node/Expression/Binary/Range.php | 33 + vendor/Twig/Node/Expression/Binary/Sub.php | 18 + .../Twig/Node/Expression/BlockReference.php | 52 + vendor/Twig/Node/Expression/Conditional.php | 31 + vendor/Twig/Node/Expression/Constant.php | 23 + .../Node/Expression/ExtensionReference.php | 34 + vendor/Twig/Node/Expression/Filter.php | 61 + .../Twig/Node/Expression/Filter/Default.php | 44 + vendor/Twig/Node/Expression/Function.php | 66 + vendor/Twig/Node/Expression/GetAttr.php | 53 + vendor/Twig/Node/Expression/MethodCall.php | 37 + vendor/Twig/Node/Expression/Name.php | 76 ++ vendor/Twig/Node/Expression/Parent.php | 48 + vendor/Twig/Node/Expression/TempName.php | 22 + vendor/Twig/Node/Expression/Test.php | 54 + vendor/Twig/Node/Expression/Test/Constant.php | 36 + vendor/Twig/Node/Expression/Test/Defined.php | 55 + .../Twig/Node/Expression/Test/Divisibleby.php | 34 + vendor/Twig/Node/Expression/Test/Even.php | 33 + vendor/Twig/Node/Expression/Test/Null.php | 32 + vendor/Twig/Node/Expression/Test/Odd.php | 33 + vendor/Twig/Node/Expression/Test/Sameas.php | 30 + vendor/Twig/Node/Expression/Unary.php | 30 + vendor/Twig/Node/Expression/Unary/Neg.php | 18 + vendor/Twig/Node/Expression/Unary/Not.php | 18 + vendor/Twig/Node/Expression/Unary/Pos.php | 18 + vendor/Twig/Node/Flush.php | 37 + vendor/Twig/Node/For.php | 113 ++ vendor/Twig/Node/ForLoop.php | 56 + vendor/Twig/Node/If.php | 67 + vendor/Twig/Node/Import.php | 51 + vendor/Twig/Node/Include.php | 100 ++ vendor/Twig/Node/Macro.php | 84 ++ vendor/Twig/Node/Module.php | 372 ++++++ vendor/Twig/Node/Print.php | 40 + vendor/Twig/Node/Sandbox.php | 48 + vendor/Twig/Node/SandboxedModule.php | 61 + vendor/Twig/Node/SandboxedPrint.php | 60 + vendor/Twig/Node/Set.php | 102 ++ vendor/Twig/Node/SetTemp.php | 35 + vendor/Twig/Node/Spaceless.php | 41 + vendor/Twig/Node/Text.php | 40 + vendor/Twig/NodeInterface.php | 30 + vendor/Twig/NodeOutputInterface.php | 20 + vendor/Twig/NodeTraverser.php | 89 ++ vendor/Twig/NodeVisitor/Escaper.php | 164 +++ vendor/Twig/NodeVisitor/Optimizer.php | 247 ++++ vendor/Twig/NodeVisitor/SafeAnalysis.php | 119 ++ vendor/Twig/NodeVisitor/Sandbox.php | 106 ++ vendor/Twig/NodeVisitorInterface.php | 48 + vendor/Twig/Parser.php | 384 ++++++ vendor/Twig/ParserInterface.php | 28 + vendor/Twig/Sandbox/SecurityError.php | 20 + vendor/Twig/Sandbox/SecurityPolicy.php | 120 ++ .../Twig/Sandbox/SecurityPolicyInterface.php | 25 + vendor/Twig/Template.php | 450 +++++++ vendor/Twig/TemplateInterface.php | 47 + vendor/Twig/Test/Function.php | 31 + vendor/Twig/Test/Method.php | 32 + vendor/Twig/Test/Node.php | 35 + vendor/Twig/TestInterface.php | 26 + vendor/Twig/Token.php | 219 ++++ vendor/Twig/TokenParser.php | 34 + vendor/Twig/TokenParser/AutoEscape.php | 88 ++ vendor/Twig/TokenParser/Block.php | 83 ++ vendor/Twig/TokenParser/Do.php | 42 + vendor/Twig/TokenParser/Embed.php | 66 + vendor/Twig/TokenParser/Extends.php | 54 + vendor/Twig/TokenParser/Filter.php | 61 + vendor/Twig/TokenParser/Flush.php | 42 + vendor/Twig/TokenParser/For.php | 89 ++ vendor/Twig/TokenParser/From.php | 74 ++ vendor/Twig/TokenParser/If.php | 93 ++ vendor/Twig/TokenParser/Import.php | 47 + vendor/Twig/TokenParser/Include.php | 80 ++ vendor/Twig/TokenParser/Macro.php | 69 + vendor/Twig/TokenParser/Sandbox.php | 55 + vendor/Twig/TokenParser/Set.php | 84 ++ vendor/Twig/TokenParser/Spaceless.php | 59 + vendor/Twig/TokenParser/Use.php | 85 ++ vendor/Twig/TokenParserBroker.php | 113 ++ vendor/Twig/TokenParserBrokerInterface.php | 45 + vendor/Twig/TokenParserInterface.php | 42 + vendor/Twig/TokenStream.php | 145 +++ vendor/silex.phar | Bin 0 -> 508736 bytes views/blame.twig | 37 + views/branch_menu.twig | 15 + views/commit.twig | 58 + views/commits.twig | 46 + views/error.twig | 17 + views/file.twig | 43 + views/footer.twig | 3 + views/index.twig | 25 + views/layout.twig | 19 + views/menu.twig | 5 + views/navigation.twig | 19 + views/rss.twig | 17 + views/stats.twig | 61 + views/tree.twig | 57 + web/Makefile | 2 + web/css/style.css | 788 ++++++++++++ web/img/feed.png | Bin 0 -> 735 bytes web/img/glyphicons-halflings-white.png | Bin 0 -> 8777 bytes web/img/glyphicons-halflings.png | Bin 0 -> 13826 bytes web/js/bootstrap.js | 1 + web/js/codemirror.js | 1 + web/js/html5.js | 3 + web/js/jquery.js | 4 + web/js/main.js | 12 + web/less/accordion.less | 33 + web/less/alerts.less | 58 + web/less/bootstrap.less | 64 + web/less/breadcrumbs.less | 25 + web/less/button-groups.less | 191 +++ web/less/buttons.less | 191 +++ web/less/carousel.less | 121 ++ web/less/close.less | 29 + web/less/code.less | 57 + web/less/codemirror.less | 125 ++ web/less/component-animations.less | 20 + web/less/dropdowns.less | 154 +++ web/less/files.less | 144 +++ web/less/forms.less | 584 +++++++++ web/less/grid.less | 5 + web/less/hero-unit.less | 22 + web/less/labels-badges.less | 55 + web/less/layouts.less | 17 + web/less/mixins.less | 631 ++++++++++ web/less/modals.less | 90 ++ web/less/navbar.less | 367 ++++++ web/less/navs.less | 363 ++++++ web/less/pager.less | 36 + web/less/pagination.less | 56 + web/less/popovers.less | 49 + web/less/progress-bars.less | 117 ++ web/less/reset.less | 126 ++ web/less/responsive-1200px-min.less | 26 + web/less/responsive-767px-max.less | 149 +++ web/less/responsive-768px-979px.less | 17 + web/less/responsive-navbar.less | 146 +++ web/less/responsive-utilities.less | 41 + web/less/responsive.less | 48 + web/less/scaffolding.less | 31 + web/less/sprites.less | 205 +++ web/less/tables.less | 210 ++++ web/less/thumbnails.less | 47 + web/less/tooltip.less | 35 + web/less/type.less | 235 ++++ web/less/utilities.less | 31 + web/less/variables.less | 209 ++++ web/less/wells.less | 27 + 244 files changed, 20826 insertions(+) create mode 100644 .gitignore create mode 100644 .htaccess create mode 100644 .travis.yml create mode 100644 LICENSE.txt create mode 100644 README.md create mode 100644 config.ini create mode 100644 controllers/blobController.php create mode 100644 controllers/commitController.php create mode 100644 controllers/indexController.php create mode 100644 controllers/rssController.php create mode 100644 controllers/statsController.php create mode 100644 controllers/treeController.php create mode 100644 index.php create mode 100644 lib/Application/Utils.php create mode 100644 lib/Application/UtilsServiceProvider.php create mode 100644 lib/Git/Client.php create mode 100644 lib/Git/Commit/Author.php create mode 100644 lib/Git/Commit/Commit.php create mode 100644 lib/Git/GitServiceProvider.php create mode 100644 lib/Git/Model/Blob.php create mode 100644 lib/Git/Model/Diff.php create mode 100644 lib/Git/Model/Line.php create mode 100644 lib/Git/Model/Symlink.php create mode 100644 lib/Git/Model/Tree.php create mode 100644 lib/Git/Repository.php create mode 100644 lib/Git/ScopeAware.php create mode 100644 phpunit.xml.dist create mode 100644 tests/ClientTest.php create mode 100644 vendor/Twig/Autoloader.php create mode 100644 vendor/Twig/Compiler.php create mode 100644 vendor/Twig/CompilerInterface.php create mode 100644 vendor/Twig/Environment.php create mode 100644 vendor/Twig/Error.php create mode 100644 vendor/Twig/Error/Loader.php create mode 100644 vendor/Twig/Error/Runtime.php create mode 100644 vendor/Twig/Error/Syntax.php create mode 100644 vendor/Twig/ExpressionParser.php create mode 100644 vendor/Twig/Extension.php create mode 100644 vendor/Twig/Extension/Core.php create mode 100644 vendor/Twig/Extension/Debug.php create mode 100644 vendor/Twig/Extension/Escaper.php create mode 100644 vendor/Twig/Extension/Optimizer.php create mode 100644 vendor/Twig/Extension/Sandbox.php create mode 100644 vendor/Twig/ExtensionInterface.php create mode 100644 vendor/Twig/Filter.php create mode 100644 vendor/Twig/Filter/Function.php create mode 100644 vendor/Twig/Filter/Method.php create mode 100644 vendor/Twig/Filter/Node.php create mode 100644 vendor/Twig/FilterInterface.php create mode 100644 vendor/Twig/Function.php create mode 100644 vendor/Twig/Function/Function.php create mode 100644 vendor/Twig/Function/Method.php create mode 100644 vendor/Twig/Function/Node.php create mode 100644 vendor/Twig/FunctionInterface.php create mode 100644 vendor/Twig/Lexer.php create mode 100644 vendor/Twig/LexerInterface.php create mode 100644 vendor/Twig/Loader/Array.php create mode 100644 vendor/Twig/Loader/Chain.php create mode 100644 vendor/Twig/Loader/Filesystem.php create mode 100644 vendor/Twig/Loader/String.php create mode 100644 vendor/Twig/LoaderInterface.php create mode 100644 vendor/Twig/Markup.php create mode 100644 vendor/Twig/Node.php create mode 100644 vendor/Twig/Node/AutoEscape.php create mode 100644 vendor/Twig/Node/Block.php create mode 100644 vendor/Twig/Node/BlockReference.php create mode 100644 vendor/Twig/Node/Body.php create mode 100644 vendor/Twig/Node/Do.php create mode 100644 vendor/Twig/Node/Embed.php create mode 100644 vendor/Twig/Node/Expression.php create mode 100644 vendor/Twig/Node/Expression/Array.php create mode 100644 vendor/Twig/Node/Expression/AssignName.php create mode 100644 vendor/Twig/Node/Expression/Binary.php create mode 100644 vendor/Twig/Node/Expression/Binary/Add.php create mode 100644 vendor/Twig/Node/Expression/Binary/And.php create mode 100644 vendor/Twig/Node/Expression/Binary/BitwiseAnd.php create mode 100644 vendor/Twig/Node/Expression/Binary/BitwiseOr.php create mode 100644 vendor/Twig/Node/Expression/Binary/BitwiseXor.php create mode 100644 vendor/Twig/Node/Expression/Binary/Concat.php create mode 100644 vendor/Twig/Node/Expression/Binary/Div.php create mode 100644 vendor/Twig/Node/Expression/Binary/Equal.php create mode 100644 vendor/Twig/Node/Expression/Binary/FloorDiv.php create mode 100644 vendor/Twig/Node/Expression/Binary/Greater.php create mode 100644 vendor/Twig/Node/Expression/Binary/GreaterEqual.php create mode 100644 vendor/Twig/Node/Expression/Binary/In.php create mode 100644 vendor/Twig/Node/Expression/Binary/Less.php create mode 100644 vendor/Twig/Node/Expression/Binary/LessEqual.php create mode 100644 vendor/Twig/Node/Expression/Binary/Mod.php create mode 100644 vendor/Twig/Node/Expression/Binary/Mul.php create mode 100644 vendor/Twig/Node/Expression/Binary/NotEqual.php create mode 100644 vendor/Twig/Node/Expression/Binary/NotIn.php create mode 100644 vendor/Twig/Node/Expression/Binary/Or.php create mode 100644 vendor/Twig/Node/Expression/Binary/Power.php create mode 100644 vendor/Twig/Node/Expression/Binary/Range.php create mode 100644 vendor/Twig/Node/Expression/Binary/Sub.php create mode 100644 vendor/Twig/Node/Expression/BlockReference.php create mode 100644 vendor/Twig/Node/Expression/Conditional.php create mode 100644 vendor/Twig/Node/Expression/Constant.php create mode 100644 vendor/Twig/Node/Expression/ExtensionReference.php create mode 100644 vendor/Twig/Node/Expression/Filter.php create mode 100644 vendor/Twig/Node/Expression/Filter/Default.php create mode 100644 vendor/Twig/Node/Expression/Function.php create mode 100644 vendor/Twig/Node/Expression/GetAttr.php create mode 100644 vendor/Twig/Node/Expression/MethodCall.php create mode 100644 vendor/Twig/Node/Expression/Name.php create mode 100644 vendor/Twig/Node/Expression/Parent.php create mode 100644 vendor/Twig/Node/Expression/TempName.php create mode 100644 vendor/Twig/Node/Expression/Test.php create mode 100644 vendor/Twig/Node/Expression/Test/Constant.php create mode 100644 vendor/Twig/Node/Expression/Test/Defined.php create mode 100644 vendor/Twig/Node/Expression/Test/Divisibleby.php create mode 100644 vendor/Twig/Node/Expression/Test/Even.php create mode 100644 vendor/Twig/Node/Expression/Test/Null.php create mode 100644 vendor/Twig/Node/Expression/Test/Odd.php create mode 100644 vendor/Twig/Node/Expression/Test/Sameas.php create mode 100644 vendor/Twig/Node/Expression/Unary.php create mode 100644 vendor/Twig/Node/Expression/Unary/Neg.php create mode 100644 vendor/Twig/Node/Expression/Unary/Not.php create mode 100644 vendor/Twig/Node/Expression/Unary/Pos.php create mode 100644 vendor/Twig/Node/Flush.php create mode 100644 vendor/Twig/Node/For.php create mode 100644 vendor/Twig/Node/ForLoop.php create mode 100644 vendor/Twig/Node/If.php create mode 100644 vendor/Twig/Node/Import.php create mode 100644 vendor/Twig/Node/Include.php create mode 100644 vendor/Twig/Node/Macro.php create mode 100644 vendor/Twig/Node/Module.php create mode 100644 vendor/Twig/Node/Print.php create mode 100644 vendor/Twig/Node/Sandbox.php create mode 100644 vendor/Twig/Node/SandboxedModule.php create mode 100644 vendor/Twig/Node/SandboxedPrint.php create mode 100644 vendor/Twig/Node/Set.php create mode 100644 vendor/Twig/Node/SetTemp.php create mode 100644 vendor/Twig/Node/Spaceless.php create mode 100644 vendor/Twig/Node/Text.php create mode 100644 vendor/Twig/NodeInterface.php create mode 100644 vendor/Twig/NodeOutputInterface.php create mode 100644 vendor/Twig/NodeTraverser.php create mode 100644 vendor/Twig/NodeVisitor/Escaper.php create mode 100644 vendor/Twig/NodeVisitor/Optimizer.php create mode 100644 vendor/Twig/NodeVisitor/SafeAnalysis.php create mode 100644 vendor/Twig/NodeVisitor/Sandbox.php create mode 100644 vendor/Twig/NodeVisitorInterface.php create mode 100644 vendor/Twig/Parser.php create mode 100644 vendor/Twig/ParserInterface.php create mode 100644 vendor/Twig/Sandbox/SecurityError.php create mode 100644 vendor/Twig/Sandbox/SecurityPolicy.php create mode 100644 vendor/Twig/Sandbox/SecurityPolicyInterface.php create mode 100644 vendor/Twig/Template.php create mode 100644 vendor/Twig/TemplateInterface.php create mode 100644 vendor/Twig/Test/Function.php create mode 100644 vendor/Twig/Test/Method.php create mode 100644 vendor/Twig/Test/Node.php create mode 100644 vendor/Twig/TestInterface.php create mode 100644 vendor/Twig/Token.php create mode 100644 vendor/Twig/TokenParser.php create mode 100644 vendor/Twig/TokenParser/AutoEscape.php create mode 100644 vendor/Twig/TokenParser/Block.php create mode 100644 vendor/Twig/TokenParser/Do.php create mode 100644 vendor/Twig/TokenParser/Embed.php create mode 100644 vendor/Twig/TokenParser/Extends.php create mode 100644 vendor/Twig/TokenParser/Filter.php create mode 100644 vendor/Twig/TokenParser/Flush.php create mode 100644 vendor/Twig/TokenParser/For.php create mode 100644 vendor/Twig/TokenParser/From.php create mode 100644 vendor/Twig/TokenParser/If.php create mode 100644 vendor/Twig/TokenParser/Import.php create mode 100644 vendor/Twig/TokenParser/Include.php create mode 100644 vendor/Twig/TokenParser/Macro.php create mode 100644 vendor/Twig/TokenParser/Sandbox.php create mode 100644 vendor/Twig/TokenParser/Set.php create mode 100644 vendor/Twig/TokenParser/Spaceless.php create mode 100644 vendor/Twig/TokenParser/Use.php create mode 100644 vendor/Twig/TokenParserBroker.php create mode 100644 vendor/Twig/TokenParserBrokerInterface.php create mode 100644 vendor/Twig/TokenParserInterface.php create mode 100644 vendor/Twig/TokenStream.php create mode 100644 vendor/silex.phar create mode 100644 views/blame.twig create mode 100644 views/branch_menu.twig create mode 100644 views/commit.twig create mode 100644 views/commits.twig create mode 100644 views/error.twig create mode 100644 views/file.twig create mode 100644 views/footer.twig create mode 100644 views/index.twig create mode 100644 views/layout.twig create mode 100644 views/menu.twig create mode 100644 views/navigation.twig create mode 100644 views/rss.twig create mode 100644 views/stats.twig create mode 100644 views/tree.twig create mode 100755 web/Makefile create mode 100644 web/css/style.css create mode 100644 web/img/feed.png create mode 100755 web/img/glyphicons-halflings-white.png create mode 100755 web/img/glyphicons-halflings.png create mode 100755 web/js/bootstrap.js create mode 100644 web/js/codemirror.js create mode 100755 web/js/html5.js create mode 100755 web/js/jquery.js create mode 100755 web/js/main.js create mode 100755 web/less/accordion.less create mode 100755 web/less/alerts.less create mode 100755 web/less/bootstrap.less create mode 100755 web/less/breadcrumbs.less create mode 100755 web/less/button-groups.less create mode 100755 web/less/buttons.less create mode 100755 web/less/carousel.less create mode 100755 web/less/close.less create mode 100755 web/less/code.less create mode 100644 web/less/codemirror.less create mode 100755 web/less/component-animations.less create mode 100755 web/less/dropdowns.less create mode 100644 web/less/files.less create mode 100755 web/less/forms.less create mode 100755 web/less/grid.less create mode 100755 web/less/hero-unit.less create mode 100755 web/less/labels-badges.less create mode 100755 web/less/layouts.less create mode 100755 web/less/mixins.less create mode 100755 web/less/modals.less create mode 100755 web/less/navbar.less create mode 100755 web/less/navs.less create mode 100755 web/less/pager.less create mode 100755 web/less/pagination.less create mode 100755 web/less/popovers.less create mode 100755 web/less/progress-bars.less create mode 100755 web/less/reset.less create mode 100755 web/less/responsive-1200px-min.less create mode 100755 web/less/responsive-767px-max.less create mode 100755 web/less/responsive-768px-979px.less create mode 100755 web/less/responsive-navbar.less create mode 100755 web/less/responsive-utilities.less create mode 100755 web/less/responsive.less create mode 100755 web/less/scaffolding.less create mode 100755 web/less/sprites.less create mode 100755 web/less/tables.less create mode 100755 web/less/thumbnails.less create mode 100755 web/less/tooltip.less create mode 100755 web/less/type.less create mode 100755 web/less/utilities.less create mode 100755 web/less/variables.less create mode 100755 web/less/wells.less diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..5b201dc --- /dev/null +++ b/.gitignore @@ -0,0 +1,28 @@ +cache/ +*.diff +*.err +*.orig +*.log +*.rej +*.swo +*.swp +*.zip +*.vi +*~ +*.sass-cache +.DS_Store +._* +Thumbs.db +.cache +.project +.settings +.tmproj +*.esproj +nbproject +*.sublime-project +*.sublime-workspace +.hg +.svn +.CVS +.idea +node_modules diff --git a/.htaccess b/.htaccess new file mode 100644 index 0000000..eee5bd9 --- /dev/null +++ b/.htaccess @@ -0,0 +1,7 @@ + + Options -MultiViews + + RewriteEngine On + RewriteCond %{REQUEST_FILENAME} !-f + RewriteRule ^ index.php [L] + \ No newline at end of file diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 0000000..357ecd9 --- /dev/null +++ b/.travis.yml @@ -0,0 +1,5 @@ +language: php +php: + - 5.3 + - 5.4 +script: phpunit diff --git a/LICENSE.txt b/LICENSE.txt new file mode 100644 index 0000000..f8757b3 --- /dev/null +++ b/LICENSE.txt @@ -0,0 +1,9 @@ +Copyright (c) 2012, Klaus Silveira and contributors +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 GitList nor the names of its 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 HOLDER 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. diff --git a/README.md b/README.md new file mode 100644 index 0000000..9b3dbbe --- /dev/null +++ b/README.md @@ -0,0 +1,53 @@ +# GitList: an elegant and modern git repository viewer +[![Build Status](https://secure.travis-ci.org/klaussilveira/GitList.png)](http://travis-ci.org/klaussilveira/GitList) + +GitList is an elegant and modern web interface for interacting with multiple git repositories. It allows you to browse repositories using your favorite browser, viewing files under different revisions, commit history, diffs. It also generates RSS feeds for each repository, allowing you to stay up-to-date with the latest changes anytime, anywhere. GitList was written in PHP, on top of the [Silex](http://silex.sensiolabs.org/) microframework and powered by the Twig template engine. This means that GitList is easy to install and easy to customize. Also, the GitList gorgeous interface was made possible due to [Bootstrap](http://twitter.github.com/bootstrap/). + +## Features +* Multiple repository support +* Multiple branch support +* Multiple tag support +* Commit history, blame, diff +* RSS feeds +* Syntax highlighting +* Repository statistics + +## Authors and contributors +* [Klaus Silveira](http://www.klaussilveira.com) (Creator, developer) + +## License +[New BSD license](http://www.opensource.org/licenses/bsd-license.php) + +## Todo +* improve the current test code coverage +* test the interface +* error handling can be greatly improved during parsing +* submodule support +* multilanguage support + +## Requirements +In order to run GitList on your server, you'll need: + +* git +* Apache and mod_rewrite enabled +* PHP 5.3.3 + +## Installing +Download the GitList latest package and decompress to your `/var/www/gitlist` folder, or anywhere else you want to place GitList. You can also clone the repository: + +``` +git clone https://github.com/klaussilveira/gitlist.git /var/www/gitlist +``` + +Now open up the `config.ini` and configure your installation. You'll have to provide where your repositories are located and the base GitList URL (in our case, http://localhost/gitlist). Now, let's create the cache folder and give the correct permissions: + +``` +cd /var/www/gitlist +mkdir cache +chmod 777 cache +``` + +That's it, installation complete! + +## Further information +If you want to know more about customizing GitList, check the [Customization](https://github.com/klaussilveira/gitlist/wiki/Customizing) page on the wiki. Also, if you're having problems with GitList, check the [Troubleshooting](https://github.com/klaussilveira/gitlist/wiki/Customizing) page. Don't forget to report issues and suggest new features! :) diff --git a/config.ini b/config.ini new file mode 100644 index 0000000..186c20d --- /dev/null +++ b/config.ini @@ -0,0 +1,6 @@ +[git] +client = '/usr/bin/git' ; Your git executable path +repositories = '/home/git/' ; Path to your repositories (with ending slash) + +[app] +baseurl = 'http://localhost/gitlist' ; Base URL of the application (without ending slash) diff --git a/controllers/blobController.php b/controllers/blobController.php new file mode 100644 index 0000000..2e107bf --- /dev/null +++ b/controllers/blobController.php @@ -0,0 +1,32 @@ +get('{repo}/blob/{branch}/{file}/', function($repo, $branch, $file) use($app) { + $repository = $app['git']->getRepository($app['git.repos'] . $repo); + $blob = $repository->getBlob("$branch:$file"); + $breadcrumbs = $app['utils']->getBreadcrumbs("$repo/tree/$branch/$file"); + $fileType = $app['utils']->getFileType($file); + + return $app['twig']->render('file.twig', array( + 'baseurl' => $app['baseurl'], + 'page' => 'files', + 'file' => $file, + 'fileType' => $fileType, + 'blob' => $blob->output(), + 'repo' => $repo, + 'branch' => $branch, + 'breadcrumbs' => $breadcrumbs, + 'branches' => $repository->getBranches(), + 'tags' => $repository->getTags(), + )); +})->assert('file', '.+') + ->assert('repo', '[\w-._]+') + ->assert('branch', '[\w-._]+'); + +$app->get('{repo}/raw/{branch}/{file}', function($repo, $branch, $file) use($app) { + $repository = $app['git']->getRepository($app['git.repos'] . $repo); + $blob = $repository->getBlob("$branch:$file")->output(); + + return new Symfony\Component\HttpFoundation\Response($blob, 200, array('Content-Type' => 'text/plain')); +})->assert('file', '.+') + ->assert('repo', '[\w-._]+') + ->assert('branch', '[\w-._]+'); \ No newline at end of file diff --git a/controllers/commitController.php b/controllers/commitController.php new file mode 100644 index 0000000..cb98927 --- /dev/null +++ b/controllers/commitController.php @@ -0,0 +1,79 @@ +get('{repo}/commits/{branch}', function($repo, $branch) use($app) { + $repository = $app['git']->getRepository($app['git.repos'] . $repo); + $commits = $repository->getCommits(); + + foreach ($commits as $commit) { + $date = $commit->getDate(); + $date = $date->format('m/d/Y'); + $categorized[$date][] = $commit; + } + + return $app['twig']->render('commits.twig', array( + 'baseurl' => $app['baseurl'], + 'page' => 'commits', + 'repo' => $repo, + 'branch' => $branch, + 'branches' => $repository->getBranches(), + 'tags' => $repository->getTags(), + 'commits' => $categorized, + )); +})->assert('repo', '[\w-._]+') + ->assert('branch', '[\w-._]+') + ->value('branch', 'master'); + +$app->get('{repo}/commits/{branch}/{file}/', function($repo, $branch, $file) use($app) { + $repository = $app['git']->getRepository($app['git.repos'] . $repo); + $commits = $repository->getCommits($file); + + foreach ($commits as $commit) { + $date = $commit->getDate(); + $date = $date->format('m/d/Y'); + $categorized[$date][] = $commit; + } + + return $app['twig']->render('commits.twig', array( + 'baseurl' => $app['baseurl'], + 'page' => 'commits', + 'repo' => $repo, + 'branch' => $branch, + 'branches' => $repository->getBranches(), + 'tags' => $repository->getTags(), + 'commits' => $categorized, + )); +})->assert('repo', '[\w-._]+') + ->assert('file', '.+') + ->assert('branch', '[\w-._]+'); + +$app->get('{repo}/commit/{commit}/', function($repo, $commit) use($app) { + $repository = $app['git']->getRepository($app['git.repos'] . $repo); + $commit = $repository->getCommit($commit); + + return $app['twig']->render('commit.twig', array( + 'baseurl' => $app['baseurl'], + 'page' => 'commits', + 'branch' => 'master', + 'repo' => $repo, + 'commit' => $commit, + )); +})->assert('repo', '[\w-._]+') + ->assert('commit', '[a-f0-9]+'); + +$app->get('{repo}/blame/{branch}/{file}/', function($repo, $branch, $file) use($app) { + $repository = $app['git']->getRepository($app['git.repos'] . $repo); + $blames = $repository->getBlame($file); + + return $app['twig']->render('blame.twig', array( + 'baseurl' => $app['baseurl'], + 'page' => 'commits', + 'file' => $file, + 'repo' => $repo, + 'branch' => $branch, + 'branches' => $repository->getBranches(), + 'tags' => $repository->getTags(), + 'blames' => $blames, + )); +})->assert('repo', '[\w-._]+') + ->assert('file', '.+') + ->assert('branch', '[\w-._]+'); diff --git a/controllers/indexController.php b/controllers/indexController.php new file mode 100644 index 0000000..df63f5b --- /dev/null +++ b/controllers/indexController.php @@ -0,0 +1,10 @@ +get('/', function() use($app) { + $repositories = $app['git']->getRepositories($app['git.repos']); + + return $app['twig']->render('index.twig', array( + 'baseurl' => $app['baseurl'], + 'repositories' => $repositories, + )); +}); \ No newline at end of file diff --git a/controllers/rssController.php b/controllers/rssController.php new file mode 100644 index 0000000..c337ca2 --- /dev/null +++ b/controllers/rssController.php @@ -0,0 +1,16 @@ +get('{repo}/{branch}/rss/', function($repo, $branch) use($app) { + $repository = $app['git']->getRepository($app['git.repos'] . $repo); + $commits = $repository->getCommits(); + + $html = $app['twig']->render('rss.twig', array( + 'baseurl' => $app['baseurl'], + 'repo' => $repo, + 'branch' => $branch, + 'commits' => $commits, + )); + + return new Symfony\Component\HttpFoundation\Response($html, 200, array('Content-Type' => 'application/rss+xml')); +})->assert('repo', '[\w-._]+') + ->assert('branch', '[\w-._]+'); \ No newline at end of file diff --git a/controllers/statsController.php b/controllers/statsController.php new file mode 100644 index 0000000..ac41f2a --- /dev/null +++ b/controllers/statsController.php @@ -0,0 +1,20 @@ +get('{repo}/stats/{branch}', function($repo, $branch) use($app) { + $repository = $app['git']->getRepository($app['git.repos'] . $repo); + $stats = $repository->getStatistics($branch); + $authors = $repository->getAuthorStatistics(); + + return $app['twig']->render('stats.twig', array( + 'baseurl' => $app['baseurl'], + 'page' => 'stats', + 'repo' => $repo, + 'branch' => $branch, + 'branches' => $repository->getBranches(), + 'tags' => $repository->getTags(), + 'stats' => $stats, + 'authors' => $authors, + )); +})->assert('repo', '[\w-._]+') + ->assert('branch', '[\w-._]+') + ->value('branch', 'master'); \ No newline at end of file diff --git a/controllers/treeController.php b/controllers/treeController.php new file mode 100644 index 0000000..02d5058 --- /dev/null +++ b/controllers/treeController.php @@ -0,0 +1,67 @@ +get('{repo}/', function($repo) use($app) { + $repository = $app['git']->getRepository($app['git.repos'] . $repo); + $tree = $repository->getTree('master'); + $breadcrumbs = $app['utils']->getBreadcrumbs("$repo/"); + + return $app['twig']->render('tree.twig', array( + 'baseurl' => $app['baseurl'], + 'page' => 'files', + 'files' => $tree->output(), + 'repo' => $repo, + 'branch' => 'master', + 'path' => '', + 'parent' => '', + 'breadcrumbs' => $breadcrumbs, + 'branches' => $repository->getBranches(), + 'tags' => $repository->getTags(), + )); +})->assert('repo', '[\w-._]+'); + +$app->get('{repo}/tree/{branch}/', function($repo, $branch) use($app) { + $repository = $app['git']->getRepository($app['git.repos'] . $repo); + $tree = $repository->getTree($branch); + $breadcrumbs = $app['utils']->getBreadcrumbs("$repo/"); + + return $app['twig']->render('tree.twig', array( + 'baseurl' => $app['baseurl'], + 'page' => 'files', + 'files' => $tree->output(), + 'repo' => $repo, + 'branch' => $branch, + 'path' => '', + 'parent' => '', + 'breadcrumbs' => $breadcrumbs, + 'branches' => $repository->getBranches(), + 'tags' => $repository->getTags(), + )); +})->assert('repo', '[\w-._]+') + ->assert('branch', '[\w-._]+'); + +$app->get('{repo}/tree/{branch}/{tree}/', function($repo, $branch, $tree) use($app) { + $repository = $app['git']->getRepository($app['git.repos'] . $repo); + $files = $repository->getTree("$branch:$tree/"); + $breadcrumbs = $app['utils']->getBreadcrumbs("$repo/tree/$branch/$tree"); + + if (($slash = strrpos($tree, '/')) !== false) { + $parent = '/' . substr($tree, 0, $slash); + } else { + $parent = '/'; + } + + return $app['twig']->render('tree.twig', array( + 'baseurl' => $app['baseurl'], + 'page' => 'files', + 'files' => $files->output(), + 'repo' => $repo, + 'branch' => $branch, + 'path' => "$tree/", + 'parent' => $parent, + 'breadcrumbs' => $breadcrumbs, + 'branches' => $repository->getBranches(), + 'tags' => $repository->getTags(), + )); +})->assert('tree', '.+') + ->assert('repo', '[\w-._]+') + ->assert('branch', '[\w-._]+'); \ No newline at end of file diff --git a/index.php b/index.php new file mode 100644 index 0000000..037f878 --- /dev/null +++ b/index.php @@ -0,0 +1,52 @@ +registerNamespace('Git', __DIR__.'/lib'); +$app['autoloader']->registerNamespace('Application', __DIR__.'/lib'); +$app->register(new Silex\Provider\TwigServiceProvider(), array( + 'twig.path' => __DIR__.'/views', + 'twig.class_path' => __DIR__.'/vendor', + 'twig.options' => array('cache' => __DIR__.'/cache'), +)); +$app->register(new Git\GitServiceProvider(), array( + 'git.client' => $config['git']['client'], + 'git.repos' => $config['git']['repositories'], +)); +$app->register(new Application\UtilsServiceProvider()); + +// Add the md5() function to Twig scope +$app['twig']->addFilter('md5', new Twig_Filter_Function('md5')); + +// Load controllers +include 'controllers/indexController.php'; +include 'controllers/treeController.php'; +include 'controllers/blobController.php'; +include 'controllers/commitController.php'; +include 'controllers/statsController.php'; +include 'controllers/rssController.php'; + +// Error handling +$app->error(function (\Exception $e, $code) use ($app) { + return $app['twig']->render('error.twig', array( + 'baseurl' => $app['baseurl'], + 'message' => $e->getMessage(), + )); +}); + +$app->run(); diff --git a/lib/Application/Utils.php b/lib/Application/Utils.php new file mode 100644 index 0000000..401c6cf --- /dev/null +++ b/lib/Application/Utils.php @@ -0,0 +1,206 @@ +setPath($path); + } + + /** + * Creates a new repository on the specified path + * + * @param string $path Path where the new repository will be created + * @return Repository Instance of Repository + */ + public function createRepository($path) + { + if (file_exists($path . '/.git/HEAD') && !file_exists($path . '/HEAD')) { + throw new \RuntimeException('A GIT repository already exists at ' . $path); + } + + $repository = new Repository($path, $this); + return $repository->create(); + } + + /** + * Opens a repository at the specified path + * + * @param string $path Path where the repository is located + * @return Repository Instance of Repository + */ + public function getRepository($path) + { + if (!file_exists($path) || !file_exists($path . '/.git/HEAD') && !file_exists($path . '/HEAD')) { + throw new \RuntimeException('There is no GIT repository at ' . $path); + } + + return new Repository($path, $this); + } + + /** + * Searches for valid repositories on the specified path + * + * @param string $path Path where repositories will be searched + * @return array Found repositories, containing their name, path and description + */ + public function getRepositories($path) + { + $dir = new \DirectoryIterator($path); + + foreach ($dir as $file) { + $isBare = file_exists($file->getPathname() . '/HEAD'); + $isRepository = file_exists($file->getPathname() . '/.git/HEAD'); + + if ($file->isDir() && !$file->isDot() && $isRepository || $isBare) { + if ($isBare) { + $description = file_get_contents($file->getPathname() . '/description'); + } else { + $description = file_get_contents($file->getPathname() . '/.git/description'); + } + + $repositories[] = array('name' => $file->getFilename(), 'path' => $file->getPathname(), 'description' => $description); + } + } + + if (!isset($repositories)) { + throw new \RuntimeException('There are no GIT repositories in ' . $path); + } + + sort($repositories); + + return $repositories; + } + + /** + * Execute a git command on the repository being manipulated + * + * This method will start a new process on the current machine and + * run git commands. Once the command has been run, the method will + * return the command line output. + * + * @param Repository $repository Repository where the command will be run + * @param string $command Git command to be run + * @return string Returns the command output + */ + public function run(Repository $repository, $command) + { + $descriptors = array(0 => array("pipe", "r"), 1 => array("pipe", "w")); + $process = proc_open($this->getPath() . ' ' . $command, $descriptors, $pipes, $repository->getPath()); + + if (is_resource($process)) { + $stdout = stream_get_contents($pipes[1]); + fclose($pipes[1]); + proc_close($process); + return $stdout; + } + } + + /** + * Get the current Git binary path + * + * @return string Path where the Git binary is located + */ + protected function getPath() + { + return $this->path; + } + + /** + * Set the current Git binary path + * + * @param string $path Path where the Git binary is located + */ + protected function setPath($path) + { + $this->path = $path; + } +} diff --git a/lib/Git/Commit/Author.php b/lib/Git/Commit/Author.php new file mode 100644 index 0000000..3fc2237 --- /dev/null +++ b/lib/Git/Commit/Author.php @@ -0,0 +1,35 @@ +setName($name); + $this->setEmail($email); + } + + public function getName() + { + return $this->name; + } + + public function setName($name) + { + $this->name = $name; + } + + public function getEmail() + { + return $this->email; + } + + public function setEmail($email) + { + $this->email = $email; + } +} \ No newline at end of file diff --git a/lib/Git/Commit/Commit.php b/lib/Git/Commit/Commit.php new file mode 100644 index 0000000..8697306 --- /dev/null +++ b/lib/Git/Commit/Commit.php @@ -0,0 +1,148 @@ +setHash($data['hash']); + $this->setShortHash($data['short_hash']); + $this->setTreeHash($data['tree']); + $this->setParentHash($data['parent']); + + $this->setAuthor( + new Author($data['author'], $data['author_email']) + ); + + $this->setDate( + new \DateTime('@' . $data['date']) + ); + + $this->setCommiter( + new Author($data['commiter'], $data['commiter_email']) + ); + + $this->setCommiterDate( + new \DateTime('@' . $data['commiter_date']) + ); + + $this->setMessage($data['message']); + } + + public function getHash() + { + return $this->hash; + } + + public function setHash($hash) + { + $this->hash = $hash; + } + + public function getShortHash() + { + return $this->shortHash; + } + + public function setShortHash($shortHash) + { + $this->shortHash = $shortHash; + } + + public function getTreeHash() + { + return $this->treeHash; + } + + public function setTreeHash($treeHash) + { + $this->treeHash = $treeHash; + } + + public function getParentHash() + { + return $this->parentHash; + } + + public function setParentHash($parentHash) + { + $this->parentHash = $parentHash; + } + + public function getAuthor() + { + return $this->author; + } + + public function setAuthor($author) + { + $this->author = $author; + } + + public function getDate() + { + return $this->date; + } + + public function setDate($date) + { + $this->date = $date; + } + + public function getCommiter() + { + return $this->commiter; + } + + public function setCommiter($commiter) + { + $this->commiter = $commiter; + } + + public function getCommiterDate() + { + return $this->commiterDate; + } + + public function setCommiterDate($commiterDate) + { + $this->commiterDate = $commiterDate; + } + + public function getMessage() + { + return $this->message; + } + + public function setMessage($message) + { + $this->message = $message; + } + + public function getDiffs() + { + return $this->diffs; + } + + public function setDiffs($diffs) + { + $this->diffs = $diffs; + } + + public function getChangedFiles() + { + return sizeof($this->diffs); + } +} \ No newline at end of file diff --git a/lib/Git/GitServiceProvider.php b/lib/Git/GitServiceProvider.php new file mode 100644 index 0000000..1e8ab6d --- /dev/null +++ b/lib/Git/GitServiceProvider.php @@ -0,0 +1,23 @@ +setClient($client); + $this->setRepository($repository); + $this->setHash($hash); + } + + public function output() + { + $data = $this->getClient()->run($this->getRepository(), 'show ' . $this->getHash()); + return $data; + } + + public function getMode() + { + return $this->mode; + } + + public function setMode($mode) + { + $this->mode = $mode; + } + + public function getHash() + { + return $this->hash; + } + + public function setHash($hash) + { + $this->hash = $hash; + } + + public function getName() + { + return $this->name; + } + + public function setName($name) + { + $this->name = $name; + } + + public function getSize() + { + return $this->size; + } + + public function setSize($size) + { + $this->size = $size; + } +} \ No newline at end of file diff --git a/lib/Git/Model/Diff.php b/lib/Git/Model/Diff.php new file mode 100644 index 0000000..17fa0e6 --- /dev/null +++ b/lib/Git/Model/Diff.php @@ -0,0 +1,60 @@ +lines[] = new Line($line); + } + + public function getLines() + { + return $this->lines; + } + + public function setIndex($index) + { + $this->index = $index; + } + + public function getIndex() + { + return $this->index; + } + + public function setOld($old) + { + $this->old = $old; + } + + public function getOld() + { + return $this->old; + } + + public function setNew($new) + { + $this->new = $new; + $this->file = substr($new, 6); + } + + public function getNew() + { + return $this->new; + } + + public function getFile() + { + return $this->file; + } +} \ No newline at end of file diff --git a/lib/Git/Model/Line.php b/lib/Git/Model/Line.php new file mode 100644 index 0000000..fbd010a --- /dev/null +++ b/lib/Git/Model/Line.php @@ -0,0 +1,48 @@ +setType('chunk'); + } + + if ($data[0] == '-') { + $this->setType('old'); + } + + if ($data[0] == '+') { + $this->setType('new'); + } + } + + $this->setLine($data); + } + + public function getLine() + { + return $this->line; + } + + public function setLine($line) + { + $this->line = $line; + } + + public function getType() + { + return $this->type; + } + + public function setType($type) + { + $this->type = $type; + } +} \ No newline at end of file diff --git a/lib/Git/Model/Symlink.php b/lib/Git/Model/Symlink.php new file mode 100644 index 0000000..b887796 --- /dev/null +++ b/lib/Git/Model/Symlink.php @@ -0,0 +1,44 @@ +mode; + } + + public function setMode($mode) + { + $this->mode = $mode; + } + + public function getName() + { + return $this->name; + } + + public function setName($name) + { + $this->name = $name; + } + + public function getPath() + { + return $this->path; + } + + public function setPath($path) + { + $this->path = $path; + } +} \ No newline at end of file diff --git a/lib/Git/Model/Tree.php b/lib/Git/Model/Tree.php new file mode 100644 index 0000000..bccb71c --- /dev/null +++ b/lib/Git/Model/Tree.php @@ -0,0 +1,172 @@ +setClient($client); + $this->setRepository($repository); + $this->setHash($hash); + } + + public function parse() + { + $data = $this->getClient()->run($this->getRepository(), 'ls-tree -l ' . $this->getHash()); + $lines = explode("\n", $data); + $files = array(); + $root = array(); + + foreach ($lines as $key => $line) { + if (empty($line)) { + unset($lines[$key]); + continue; + } + + $files[] = preg_split("/[\s]+/", $line); + } + + foreach ($files as $file) { + if ($file[1] == 'commit') { + // submodule + continue; + } + + if ($file[0] == '120000') { + $show = $this->getClient()->run($this->getRepository(), 'show ' . $file[2]); + $tree = new Symlink; + $tree->setMode($file[0]); + $tree->setName($file[4]); + $tree->setPath($show); + $root[] = $tree; + continue; + } + + if ($file[1] == 'blob') { + $blob = new Blob($file[2], $this->getClient(), $this->getRepository()); + $blob->setMode($file[0]); + $blob->setName($file[4]); + $blob->setSize($file[3]); + $root[] = $blob; + continue; + } + + $tree = new Tree($file[2], $this->getClient(), $this->getRepository()); + $tree->setMode($file[0]); + $tree->setName($file[4]); + $root[] = $tree; + } + + $this->data = $root; + } + + public function output() + { + $files = $folders = array(); + + foreach ($this as $node) { + if ($node instanceof Blob) { + $file['type'] = 'blob'; + $file['name'] = $node->getName(); + $file['size'] = $node->getSize(); + $file['mode'] = $node->getMode(); + $file['hash'] = $node->getHash(); + $files[] = $file; + continue; + } + + if ($node instanceof Tree) { + $folder['type'] = 'folder'; + $folder['name'] = $node->getName(); + $folder['size'] = ''; + $folder['mode'] = $node->getMode(); + $folder['hash'] = $node->getHash(); + $folders[] = $folder; + continue; + } + + if ($node instanceof Symlink) { + $folder['type'] = 'symlink'; + $folder['name'] = $node->getName(); + $folder['size'] = ''; + $folder['mode'] = $node->getMode(); + $folder['hash'] = ''; + $folder['path'] = $node->getPath(); + $folders[] = $folder; + } + } + + // Little hack to make folders appear before files + $files = array_merge($folders, $files); + + return $files; + } + + public function valid() { + return isset($this->data[$this->position]); + } + + public function hasChildren() { + return is_array($this->data[$this->position]); + } + + public function next() { + $this->position++; + } + + public function current() { + return $this->data[$this->position]; + } + + public function getChildren() { + return $this->data[$this->position]; + } + + public function rewind() { + $this->position = 0; + } + + public function key() { + return $this->position; + } + + public function getMode() + { + return $this->mode; + } + + public function setMode($mode) + { + $this->mode = $mode; + } + + public function getHash() + { + return $this->hash; + } + + public function setHash($hash) + { + $this->hash = $hash; + } + + public function getName() + { + return $this->name; + } + + public function setName($name) + { + $this->name = $name; + } +} \ No newline at end of file diff --git a/lib/Git/Repository.php b/lib/Git/Repository.php new file mode 100644 index 0000000..edc9c7a --- /dev/null +++ b/lib/Git/Repository.php @@ -0,0 +1,465 @@ +setPath($path); + $this->setClient($client); + } + + public function setClient(Client $client) + { + $this->client = $client; + } + + public function getClient() + { + return $this->client; + } + + public function create() + { + mkdir($this->getPath()); + $this->getClient()->run($this, 'init'); + + return $this; + } + + public function getConfig($key) + { + $key = $this->getClient()->run($this, 'config ' . $key); + return trim($key); + } + + public function setConfig($key, $value) + { + $this->getClient()->run($this, "config $key \"$value\""); + + return $this; + } + + /** + * Add untracked files + * + * @access public + * @param mixed $files Files to be added to the repository + */ + public function add($files = '.') + { + if(is_array($files)) { + $files = implode(' ', $files); + } + + $this->getClient()->run($this, "add $files"); + + return $this; + } + + /** + * Add all untracked files + * + * @access public + */ + public function addAll() + { + $this->getClient()->run($this, "add -A"); + + return $this; + } + + /** + * Commit changes to the repository + * + * @access public + * @param string $message Description of the changes made + */ + public function commit($message) + { + $this->getClient()->run($this, "commit -m '$message'"); + + return $this; + } + + /** + * Checkout a branch + * + * @access public + * @param string $branch Branch to be checked out + */ + public function checkout($branch) + { + $this->getClient()->run($this, "checkout $branch"); + + return $this; + } + + /** + * Pull repository changes + * + * @access public + */ + public function pull() + { + $this->getClient()->run($this, "pull"); + + return $this; + } + + /** + * Update remote references + * + * @access public + * @param string $repository Repository to be pushed + * @param string $refspec Refspec for the push + */ + public function push($repository = null, $refspec = null) + { + $command = "push"; + + if($repository) { + $command .= " $repository"; + } + + if($refspec) { + $command .= " $refspec"; + } + + $this->getClient()->run($this, $command); + + return $this; + } + + /** + * Show a list of the repository branches + * + * @access public + * @return array List of branches + */ + public function getBranches() + { + $branches = $this->getClient()->run($this, "branch"); + $branches = explode("\n", $branches); + $branches = array_filter(preg_replace('/[\*\s]/', '', $branches)); + + return $branches; + } + + /** + * Show the current repository branch + * + * @access public + * @return string Current repository branch + */ + public function getCurrentBranch() + { + $branches = $this->getClient()->run($this, "branch"); + $branches = explode("\n", $branches); + + foreach($branches as $branch) { + if($branch[0] == '*') { + return substr($branch, 2); + } + } + } + + /** + * Check if a specified branch exists + * + * @access public + * @param string $branch Branch to be checked + * @return boolean True if the branch exists + */ + public function hasBranch($branch) + { + $branches = $this->getBranches(); + $status = in_array($branch, $branches); + return $status; + } + + /** + * Create a new repository branch + * + * @access public + * @param string $branch Branch name + */ + public function createBranch($branch) + { + $this->getClient()->run($this, "branch $branch"); + } + + /** + * Show a list of the repository tags + * + * @access public + * @return array List of tags + */ + public function getTags() + { + $tags = $this->getClient()->run($this, "tag"); + $tags = explode("\n", $tags); + + if (empty($tags[0])) { + return NULL; + } + + return $tags; + } + + /** + * Show the repository commit log + * + * @access public + * @return array Commit log + */ + public function getCommits($file = null) + { + $command = 'log --pretty=format:\'"%h": {"hash": "%H", "short_hash": "%h", "tree": "%T", "parent": "%P", "author": "%an", "author_email": "%ae", "date": "%at", "commiter": "%cn", "commiter_email": "%ce", "commiter_date": "%ct", "message": "%f"}\''; + + if ($file) { + $command .= " $file"; + } + + $logs = $this->getClient()->run($this, $command); + $logs = str_replace("\n", ',', $logs); + $logs = json_decode("{ $logs }", true); + + foreach ($logs as $log) { + $log['message'] = str_replace('-', ' ', $log['message']); + $commit = new Commit; + $commit->importData($log); + $commits[] = $commit; + } + + return $commits; + } + + public function getRelatedCommits($hash) + { + $logs = $this->getClient()->run($this, 'log --pretty=format:\'"%h": {"hash": "%H", "short_hash": "%h", "tree": "%T", "parent": "%P", "author": "%an", "author_email": "%ae", "date": "%at", "commiter": "%cn", "commiter_email": "%ce", "commiter_date": "%ct", "message": "%f"}\''); + $logs = str_replace("\n", ',', $logs); + $logs = json_decode("{ $logs }", true); + + foreach ($logs as $log) { + $log['message'] = str_replace('-', ' ', $log['message']); + $logTree = $this->getClient()->run($this, 'diff-tree -t -r ' . $log['hash']); + $lines = explode("\n", $logTree); + array_shift($lines); + $files = array(); + + foreach ($lines as $key => $line) { + if (empty($line)) { + unset($lines[$key]); + continue; + } + + $files[] = preg_split("/[\s]+/", $line); + } + + // Now let's find the commits who have our hash within them + foreach ($files as $file) { + if ($file[1] == 'commit') { + continue; + } + + if ($file[3] == $hash) { + $commit = new Commit; + $commit->importData($log); + $commits[] = $commit; + break; + } + } + } + + return $commits; + } + + public function getCommit($commit) + { + $logs = $this->getClient()->run($this, 'show --pretty=format:\'{"hash": "%H", "short_hash": "%h", "tree": "%T", "parent": "%P", "author": "%an", "author_email": "%ae", "date": "%at", "commiter": "%cn", "commiter_email": "%ce", "commiter_date": "%ct", "message": "%f"}\' ' . $commit); + $logs = explode("\n", $logs); + + // Read commit metadata + $data = json_decode($logs[0], true); + $data['message'] = str_replace('-', ' ', $data['message']); + $commit = new Commit; + $commit->importData($data); + unset($logs[0]); + + // Read diff logs + foreach ($logs as $log) { + if ('diff' === substr($log, 0, 4)) { + if (isset($diff)) { + $diffs[] = $diff; + } + + $diff = new Diff; + continue; + } + + if ('index' === substr($log, 0, 5)) { + $diff->setIndex($log); + continue; + } + + if ('---' === substr($log, 0, 3)) { + $diff->setOld($log); + continue; + } + + if ('+++' === substr($log, 0, 3)) { + $diff->setNew($log); + continue; + } + + $diff->addLine($log); + } + + if (isset($diff)) { + $diffs[] = $diff; + } + + $commit->setDiffs($diffs); + + return $commit; + } + + public function getAuthorStatistics() + { + $logs = $this->getClient()->run($this, 'log --pretty=format:\'%an||%ae\''); + $logs = explode("\n", $logs); + $logs = array_count_values($logs); + arsort($logs); + + foreach ($logs as $user => $count) { + $user = explode('||', $user); + $data[] = array('name' => $user[0], 'email' => $user[1], 'commits' => $count); + } + + return $data; + } + + public function getStatistics($branch) + { + // Calculate amount of files, extensions and file size + $logs = $this->getClient()->run($this, 'ls-tree -r -l ' . $branch); + $lines = explode("\n", $logs); + $files = array(); + $data['extensions'] = array(); + $data['size'] = 0; + $data['files'] = 0; + + foreach ($lines as $key => $line) { + if (empty($line)) { + unset($lines[$key]); + continue; + } + + $files[] = preg_split("/[\s]+/", $line); + } + + foreach ($files as $file) { + if ($file[1] == 'blob') { + $data['files']++; + } + + if (is_numeric($file[3])) { + $data['size'] += $file[3]; + } + + if (($pos = strrpos($file[4], '.')) !== FALSE) { + $data['extensions'][] = substr($file[4], $pos); + } + } + + $data['extensions'] = array_count_values($data['extensions']); + arsort($data['extensions']); + + return $data; + } + + /** + * Get the Tree for the provided folder + * + * @param string $tree Folder that will be parsed + * @return Tree Instance of Tree for the provided folder + */ + public function getTree($tree) + { + $tree = new Tree($tree, $this->getClient(), $this); + $tree->parse(); + return $tree; + } + + /** + * Get the Blob for the provided file + * + * @param string $blob File that will be parsed + * @return Blob Instance of Blob for the provided file + */ + public function getBlob($blob) + { + return new Blob($blob, $this->getClient(), $this); + } + + /** + * Blames the provided file and parses the output + * + * @param string $file File that will be blamed + * @return array Commits hashes containing the lines + */ + public function getBlame($file) + { + $logs = $this->getClient()->run($this, "blame -s $file"); + $logs = explode("\n", $logs); + + foreach ($logs as $log) { + if ($log == '') { + continue; + } + + $split = preg_split("/[a-zA-Z0-9^]{8}[\s]+[0-9]+\)/", $log); + preg_match_all("/([a-zA-Z0-9^]{8})[\s]+([0-9]+)\)/", $log, $match); + + $commit = $match[1][0]; + + if (!isset($blame[$commit]['line'])) { + $blame[$commit]['line'] = ''; + } + + $blame[$commit]['line'] .= PHP_EOL . $split[1]; + } + + return $blame; + } + + /** + * Get the current Repository path + * + * @return string Path where the repository is located + */ + public function getPath() + { + return $this->path; + } + + /** + * Set the current Repository path + * + * @param string $path Path where the repository is located + */ + public function setPath($path) + { + $this->path = $path; + } +} diff --git a/lib/Git/ScopeAware.php b/lib/Git/ScopeAware.php new file mode 100644 index 0000000..7e65780 --- /dev/null +++ b/lib/Git/ScopeAware.php @@ -0,0 +1,29 @@ +client = $client; + } + + public function getClient() + { + return $this->client; + } + + public function getRepository() + { + return $this->repository; + } + + public function setRepository($repository) + { + $this->repository = $repository; + } +} \ No newline at end of file diff --git a/phpunit.xml.dist b/phpunit.xml.dist new file mode 100644 index 0000000..a6b3fb4 --- /dev/null +++ b/phpunit.xml.dist @@ -0,0 +1,18 @@ + + + + + + ./tests/ + + + diff --git a/tests/ClientTest.php b/tests/ClientTest.php new file mode 100644 index 0000000..812c9c9 --- /dev/null +++ b/tests/ClientTest.php @@ -0,0 +1,371 @@ +markTestSkipped('There are no write permissions in order to create test repositories.'); + } + + $this->client = new Client('/usr/bin/git'); + } + + /** + * @expectedException RuntimeException + */ + public function testIsNotFindingRepositories() + { + $this->client->getRepositories($this->repoPath); + } + + /** + * @expectedException RuntimeException + */ + public function testIsNotAbleToGetUnexistingRepository() + { + $this->client->getRepository($this->repoPath); + } + + public function testIsCreatingRepository() + { + $repository = $this->client->createRepository($this->repoPath); + $this->assertRegExp("/nothing to commit/", $repository->getClient()->run($repository, 'status')); + } + + /** + * @expectedException RuntimeException + */ + public function testIsNotAbleToCreateRepositoryDueToExistingOne() + { + $this->client->createRepository($this->repoPath); + } + + public function testIsListingRepositories() + { + $this->client->createRepository($this->repoPath . '/../anothertestrepo'); + $this->client->createRepository($this->repoPath . '/../bigbadrepo'); + $repositories = $this->client->getRepositories($this->repoPath . '/../'); + + $this->assertEquals($repositories[0]['name'], 'anothertestrepo'); + $this->assertEquals($repositories[1]['name'], 'bigbadrepo'); + $this->assertEquals($repositories[2]['name'], 'testrepo'); + } + + public function testIsConfiguratingRepository() + { + $repository = $this->client->getRepository($this->repoPath); + $repository->setConfig('user.name', 'Luke Skywalker'); + $repository->setConfig('user.email', 'luke@republic.com'); + + $this->assertEquals($repository->getConfig('user.name'), 'Luke Skywalker'); + $this->assertEquals($repository->getConfig('user.email'), 'luke@republic.com'); + } + + /** + * @depends testIsCreatingRepository + */ + public function testIsAdding() + { + $repository = $this->client->getRepository($this->repoPath); + file_put_contents($this->repoPath . '/test_file.txt', 'Your mother is so ugly, glCullFace always returns TRUE.'); + $repository->add('test_file.txt'); + $this->assertRegExp("/new file: test_file.txt/", $repository->getClient()->run($repository, 'status')); + } + + /** + * @depends testIsAdding + */ + public function testIsAddingDot() + { + $repository = $this->client->getRepository($this->repoPath); + + file_put_contents($this->repoPath . '/test_file1.txt', 'Your mother is so ugly, glCullFace always returns TRUE.'); + file_put_contents($this->repoPath . '/test_file2.txt', 'Your mother is so ugly, glCullFace always returns TRUE.'); + file_put_contents($this->repoPath . '/test_file3.txt', 'Your mother is so ugly, glCullFace always returns TRUE.'); + + $repository->add(); + + $this->assertRegExp("/new file: test_file1.txt/", $repository->getClient()->run($repository, 'status')); + $this->assertRegExp("/new file: test_file2.txt/", $repository->getClient()->run($repository, 'status')); + $this->assertRegExp("/new file: test_file3.txt/", $repository->getClient()->run($repository, 'status')); + } + + /** + * @depends testIsAddingDot + */ + public function testIsAddingAll() + { + $repository = $this->client->getRepository($this->repoPath); + + file_put_contents($this->repoPath . '/test_file4.txt', 'Your mother is so ugly, glCullFace always returns TRUE.'); + file_put_contents($this->repoPath . '/test_file5.txt', 'Your mother is so ugly, glCullFace always returns TRUE.'); + file_put_contents($this->repoPath . '/test_file6.txt', 'Your mother is so ugly, glCullFace always returns TRUE.'); + + $repository->addAll(); + + $this->assertRegExp("/new file: test_file4.txt/", $repository->getClient()->run($repository, 'status')); + $this->assertRegExp("/new file: test_file5.txt/", $repository->getClient()->run($repository, 'status')); + $this->assertRegExp("/new file: test_file6.txt/", $repository->getClient()->run($repository, 'status')); + } + + /** + * @depends testIsAddingAll + */ + public function testIsCommiting() + { + $repository = $this->client->getRepository($this->repoPath); + $repository->commit("The truth unveiled"); + $this->assertRegExp("/The truth unveiled/", $repository->getClient()->run($repository, 'log')); + } + + public function testIsCreatingBranches() + { + $repository = $this->client->getRepository($this->repoPath); + $repository->createBranch('issue12'); + $repository->createBranch('issue42'); + $branches = $repository->getBranches(); + $this->assertContains('issue12', $branches); + $this->assertContains('issue42', $branches); + $this->assertContains('master', $branches); + } + + public function testIsGettingCurrentBranch() + { + $repository = $this->client->getRepository($this->repoPath); + $branch = $repository->getCurrentBranch(); + $this->assertTrue($branch === 'master'); + } + + /** + * @depends testIsCommiting + */ + public function testIsGettingCommits() + { + $repository = $this->client->getRepository($this->repoPath); + $commits = $repository->getCommits(); + + foreach ($commits as $commit) { + $this->assertInstanceOf('Git\Commit\Commit', $commit); + $this->assertTrue($commit->getMessage() === 'The truth unveiled'); + $this->assertInstanceOf('Git\Commit\Author', $commit->getAuthor()); + $this->assertEquals($commit->getAuthor()->getName(), 'Luke Skywalker'); + $this->assertEquals($commit->getAuthor()->getEmail(), 'luke@republic.com'); + $this->assertEquals($commit->getCommiter()->getName(), 'Luke Skywalker'); + $this->assertEquals($commit->getCommiter()->getEmail(), 'luke@republic.com'); + $this->assertEquals($commit->getParentHash(), ''); + $this->assertInstanceOf('DateTime', $commit->getDate()); + $this->assertInstanceOf('DateTime', $commit->getCommiterDate()); + $this->assertRegExp('/[a-f0-9]+/', $commit->getHash()); + $this->assertRegExp('/[a-f0-9]+/', $commit->getShortHash()); + $this->assertRegExp('/[a-f0-9]+/', $commit->getTreeHash()); + } + } + + /** + * @depends testIsGettingCommits + */ + public function testIsGettingCommitsFromSpecificFile() + { + $repository = $this->client->getRepository($this->repoPath); + $commits = $repository->getCommits('test_file4.txt'); + + foreach ($commits as $commit) { + $this->assertInstanceOf('Git\Commit\Commit', $commit); + $this->assertTrue($commit->getMessage() === 'The truth unveiled'); + $this->assertInstanceOf('Git\Commit\Author', $commit->getAuthor()); + $this->assertEquals($commit->getAuthor()->getName(), 'Luke Skywalker'); + $this->assertEquals($commit->getAuthor()->getEmail(), 'luke@republic.com'); + } + } + + public function testIsGettingTree() + { + $repository = $this->client->getRepository($this->repoPath); + $files = $repository->getTree('master'); + + foreach ($files as $file) { + $this->assertInstanceOf('Git\Model\Blob', $file); + $this->assertRegExp('/test_file[0-9]*.txt/', $file->getName()); + $this->assertEquals($file->getSize(), '55'); + $this->assertEquals($file->getMode(), '100644'); + $this->assertRegExp('/[a-f0-9]+/', $file->getHash()); + } + } + + public function testIsGettingTreeOutput() + { + $repository = $this->client->getRepository($this->repoPath); + $files = $repository->getTree('master')->output(); + + foreach ($files as $file) { + $this->assertEquals('blob', $file['type']); + $this->assertRegExp('/test_file[0-9]*.txt/', $file['name']); + $this->assertEquals($file['size'], '55'); + $this->assertEquals($file['mode'], '100644'); + $this->assertRegExp('/[a-f0-9]+/', $file['hash']); + } + } + + public function testIsGettingTreesWithinTree() + { + $repository = $this->client->getRepository($this->repoPath); + + // Creating folders + mkdir($this->repoPath . '/MyFolder'); + mkdir($this->repoPath . '/MyTest'); + mkdir($this->repoPath . '/MyFolder/Tests'); + + // Populating created folders + file_put_contents($this->repoPath . '/MyFolder/crazy.php', 'Lorem ipsum dolor sit amet'); + file_put_contents($this->repoPath . '/MyFolder/skywalker.php', 'Lorem ipsum dolor sit amet'); + file_put_contents($this->repoPath . '/MyTest/fortytwo.php', 'Lorem ipsum dolor sit amet'); + file_put_contents($this->repoPath . '/MyFolder/Tests/web.php', 'Lorem ipsum dolor sit amet'); + file_put_contents($this->repoPath . '/MyFolder/Tests/cli.php', 'Lorem ipsum dolor sit amet'); + + // Adding and commiting + $repository->addAll(); + $repository->commit("Creating folders for testIsGettingTreesWithinTrees"); + + // Checking tree + $files = $repository->getTree('master')->output(); + + $this->assertEquals('folder', $files[0]['type']); + $this->assertEquals('MyFolder', $files[0]['name']); + $this->assertEquals('', $files[0]['size']); + $this->assertEquals('040000', $files[0]['mode']); + $this->assertEquals('4143e982237f3bdf56b5350f862c334054aad69e', $files[0]['hash']); + + $this->assertEquals('folder', $files[1]['type']); + $this->assertEquals('MyTest', $files[1]['name']); + $this->assertEquals('', $files[1]['size']); + $this->assertEquals('040000', $files[1]['mode']); + $this->assertEquals('632240595eabd59e4217d196d6c12efb81f9c011', $files[1]['hash']); + + $this->assertEquals('blob', $files[2]['type']); + $this->assertEquals('test_file.txt', $files[2]['name']); + $this->assertEquals('55', $files[2]['size']); + $this->assertEquals('100644', $files[2]['mode']); + $this->assertEquals('a773bfc0fda6f878e3d17d78c667d18297c8831f', $files[2]['hash']); + } + + public function testIsGettingBlobsWithinTrees() + { + $repository = $this->client->getRepository($this->repoPath); + $files = $repository->getTree('master:MyFolder/')->output(); + + $this->assertEquals('folder', $files[0]['type']); + $this->assertEquals('Tests', $files[0]['name']); + $this->assertEquals('', $files[0]['size']); + $this->assertEquals('040000', $files[0]['mode']); + $this->assertEquals('8542f67d011ff2ea5ec49a729ba81a52935676d1', $files[0]['hash']); + + $this->assertEquals('blob', $files[1]['type']); + $this->assertEquals('crazy.php', $files[1]['name']); + $this->assertEquals('26', $files[1]['size']); + $this->assertEquals('100644', $files[1]['mode']); + $this->assertEquals('d781006b2d05cc31751954a0fb920c990e825aad', $files[1]['hash']); + + $this->assertEquals('blob', $files[2]['type']); + $this->assertEquals('skywalker.php', $files[2]['name']); + $this->assertEquals('26', $files[2]['size']); + $this->assertEquals('100644', $files[2]['mode']); + $this->assertEquals('d781006b2d05cc31751954a0fb920c990e825aad', $files[2]['hash']); + } + + public function testIsGettingBlobOutput() + { + $repository = $this->client->getRepository($this->repoPath); + $blob = $repository->getBlob('master:MyFolder/crazy.php')->output(); + $this->assertEquals('Lorem ipsum dolor sit amet', $blob); + + $blob = $repository->getBlob('master:test_file4.txt')->output(); + $this->assertEquals('Your mother is so ugly, glCullFace always returns TRUE.', $blob); + } + + public function testIsGettingStatistics() + { + $repository = $this->client->getRepository($this->repoPath); + $stats = $repository->getStatistics('master'); + + $this->assertEquals('7', $stats['extensions']['.txt']); + $this->assertEquals('5', $stats['extensions']['.php']); + $this->assertEquals('515', $stats['size']); + $this->assertEquals('12', $stats['files']); + } + + public function testIsGettingAuthorStatistics() + { + $repository = $this->client->getRepository($this->repoPath); + $stats = $repository->getAuthorStatistics(); + + $this->assertEquals('Luke Skywalker', $stats[0]['name']); + $this->assertEquals('luke@republic.com', $stats[0]['email']); + $this->assertEquals('2', $stats[0]['commits']); + + $repository->setConfig('user.name', 'Princess Leia'); + $repository->setConfig('user.email', 'sexyleia@republic.com'); + file_put_contents($this->repoPath . '/MyFolder/crazy.php', 'Lorem ipsum dolor sit AMET'); + $repository->addAll(); + $repository->commit("Fixing AMET case"); + + $stats = $repository->getAuthorStatistics(); + + $this->assertEquals('Luke Skywalker', $stats[0]['name']); + $this->assertEquals('luke@republic.com', $stats[0]['email']); + $this->assertEquals('2', $stats[0]['commits']); + + $this->assertEquals('Princess Leia', $stats[1]['name']); + $this->assertEquals('sexyleia@republic.com', $stats[1]['email']); + $this->assertEquals('1', $stats[1]['commits']); + } + + public static function tearDownAfterClass() + { + recursiveDelete('/tmp/gitlist'); + } +} diff --git a/vendor/Twig/Autoloader.php b/vendor/Twig/Autoloader.php new file mode 100644 index 0000000..a93b8ca --- /dev/null +++ b/vendor/Twig/Autoloader.php @@ -0,0 +1,46 @@ + + */ +class Twig_Autoloader +{ + /** + * Registers Twig_Autoloader as an SPL autoloader. + */ + static public function register() + { + ini_set('unserialize_callback_func', 'spl_autoload_call'); + spl_autoload_register(array(new self, 'autoload')); + } + + /** + * Handles autoloading of classes. + * + * @param string $class A class name. + * + * @return boolean Returns true if the class has been loaded + */ + static public function autoload($class) + { + if (0 !== strpos($class, 'Twig')) { + return; + } + + if (is_file($file = dirname(__FILE__).'/../'.str_replace(array('_', "\0"), array('/', ''), $class).'.php')) { + require $file; + } + } +} diff --git a/vendor/Twig/Compiler.php b/vendor/Twig/Compiler.php new file mode 100644 index 0000000..ae415a2 --- /dev/null +++ b/vendor/Twig/Compiler.php @@ -0,0 +1,242 @@ + + */ +class Twig_Compiler implements Twig_CompilerInterface +{ + protected $lastLine; + protected $source; + protected $indentation; + protected $env; + protected $debugInfo; + protected $sourceOffset; + protected $sourceLine; + + /** + * Constructor. + * + * @param Twig_Environment $env The twig environment instance + */ + public function __construct(Twig_Environment $env) + { + $this->env = $env; + $this->debugInfo = array(); + } + + /** + * Returns the environment instance related to this compiler. + * + * @return Twig_Environment The environment instance + */ + public function getEnvironment() + { + return $this->env; + } + + /** + * Gets the current PHP code after compilation. + * + * @return string The PHP code + */ + public function getSource() + { + return $this->source; + } + + /** + * Compiles a node. + * + * @param Twig_NodeInterface $node The node to compile + * @param integer $indentation The current indentation + * + * @return Twig_Compiler The current compiler instance + */ + public function compile(Twig_NodeInterface $node, $indentation = 0) + { + $this->lastLine = null; + $this->source = ''; + $this->sourceOffset = 0; + $this->sourceLine = 0; + $this->indentation = $indentation; + + $node->compile($this); + + return $this; + } + + public function subcompile(Twig_NodeInterface $node, $raw = true) + { + if (false === $raw) { + $this->addIndentation(); + } + + $node->compile($this); + + return $this; + } + + /** + * Adds a raw string to the compiled code. + * + * @param string $string The string + * + * @return Twig_Compiler The current compiler instance + */ + public function raw($string) + { + $this->source .= $string; + + return $this; + } + + /** + * Writes a string to the compiled code by adding indentation. + * + * @return Twig_Compiler The current compiler instance + */ + public function write() + { + $strings = func_get_args(); + foreach ($strings as $string) { + $this->addIndentation(); + $this->source .= $string; + } + + return $this; + } + + public function addIndentation() + { + $this->source .= str_repeat(' ', $this->indentation * 4); + + return $this; + } + + /** + * Adds a quoted string to the compiled code. + * + * @param string $value The string + * + * @return Twig_Compiler The current compiler instance + */ + public function string($value) + { + $this->source .= sprintf('"%s"', addcslashes($value, "\0\t\"\$\\")); + + return $this; + } + + /** + * Returns a PHP representation of a given value. + * + * @param mixed $value The value to convert + * + * @return Twig_Compiler The current compiler instance + */ + public function repr($value) + { + if (is_int($value) || is_float($value)) { + if (false !== $locale = setlocale(LC_NUMERIC, 0)) { + setlocale(LC_NUMERIC, 'C'); + } + + $this->raw($value); + + if (false !== $locale) { + setlocale(LC_NUMERIC, $locale); + } + } elseif (null === $value) { + $this->raw('null'); + } elseif (is_bool($value)) { + $this->raw($value ? 'true' : 'false'); + } elseif (is_array($value)) { + $this->raw('array('); + $i = 0; + foreach ($value as $key => $value) { + if ($i++) { + $this->raw(', '); + } + $this->repr($key); + $this->raw(' => '); + $this->repr($value); + } + $this->raw(')'); + } else { + $this->string($value); + } + + return $this; + } + + /** + * Adds debugging information. + * + * @param Twig_NodeInterface $node The related twig node + * + * @return Twig_Compiler The current compiler instance + */ + public function addDebugInfo(Twig_NodeInterface $node) + { + if ($node->getLine() != $this->lastLine) { + $this->sourceLine += substr_count($this->source, "\n", $this->sourceOffset); + $this->sourceOffset = strlen($this->source); + $this->debugInfo[$this->sourceLine] = $node->getLine(); + + $this->lastLine = $node->getLine(); + $this->write("// line {$node->getLine()}\n"); + } + + return $this; + } + + public function getDebugInfo() + { + return $this->debugInfo; + } + + /** + * Indents the generated code. + * + * @param integer $step The number of indentation to add + * + * @return Twig_Compiler The current compiler instance + */ + public function indent($step = 1) + { + $this->indentation += $step; + + return $this; + } + + /** + * Outdents the generated code. + * + * @param integer $step The number of indentation to remove + * + * @return Twig_Compiler The current compiler instance + */ + public function outdent($step = 1) + { + $this->indentation -= $step; + + if ($this->indentation < 0) { + throw new Twig_Error('Unable to call outdent() as the indentation would become negative'); + } + + return $this; + } +} diff --git a/vendor/Twig/CompilerInterface.php b/vendor/Twig/CompilerInterface.php new file mode 100644 index 0000000..0a13edf --- /dev/null +++ b/vendor/Twig/CompilerInterface.php @@ -0,0 +1,35 @@ + + */ +interface Twig_CompilerInterface +{ + /** + * Compiles a node. + * + * @param Twig_NodeInterface $node The node to compile + * + * @return Twig_CompilerInterface The current compiler instance + */ + function compile(Twig_NodeInterface $node); + + /** + * Gets the current PHP code after compilation. + * + * @return string The PHP code + */ + function getSource(); +} diff --git a/vendor/Twig/Environment.php b/vendor/Twig/Environment.php new file mode 100644 index 0000000..81c8fe6 --- /dev/null +++ b/vendor/Twig/Environment.php @@ -0,0 +1,1106 @@ + + */ +class Twig_Environment +{ + const VERSION = '1.8.0'; + + protected $charset; + protected $loader; + protected $debug; + protected $autoReload; + protected $cache; + protected $lexer; + protected $parser; + protected $compiler; + protected $baseTemplateClass; + protected $extensions; + protected $parsers; + protected $visitors; + protected $filters; + protected $tests; + protected $functions; + protected $globals; + protected $runtimeInitialized; + protected $loadedTemplates; + protected $strictVariables; + protected $unaryOperators; + protected $binaryOperators; + protected $templateClassPrefix = '__TwigTemplate_'; + protected $functionCallbacks; + protected $filterCallbacks; + protected $staging; + + /** + * Constructor. + * + * Available options: + * + * * debug: When set to `true`, the generated templates have a __toString() + * method that you can use to display the generated nodes (default to + * false). + * + * * charset: The charset used by the templates (default to utf-8). + * + * * base_template_class: The base template class to use for generated + * templates (default to Twig_Template). + * + * * cache: An absolute path where to store the compiled templates, or + * false to disable compilation cache (default). + * + * * auto_reload: Whether to reload the template is the original source changed. + * If you don't provide the auto_reload option, it will be + * determined automatically base on the debug value. + * + * * strict_variables: Whether to ignore invalid variables in templates + * (default to false). + * + * * autoescape: Whether to enable auto-escaping (default to html): + * * false: disable auto-escaping + * * true: equivalent to html + * * html, js: set the autoescaping to one of the supported strategies + * * PHP callback: a PHP callback that returns an escaping strategy based on the template "filename" + * + * * optimizations: A flag that indicates which optimizations to apply + * (default to -1 which means that all optimizations are enabled; + * set it to 0 to disable). + * + * @param Twig_LoaderInterface $loader A Twig_LoaderInterface instance + * @param array $options An array of options + */ + public function __construct(Twig_LoaderInterface $loader = null, $options = array()) + { + if (null !== $loader) { + $this->setLoader($loader); + } + + $options = array_merge(array( + 'debug' => false, + 'charset' => 'UTF-8', + 'base_template_class' => 'Twig_Template', + 'strict_variables' => false, + 'autoescape' => 'html', + 'cache' => false, + 'auto_reload' => null, + 'optimizations' => -1, + ), $options); + + $this->debug = (bool) $options['debug']; + $this->charset = $options['charset']; + $this->baseTemplateClass = $options['base_template_class']; + $this->autoReload = null === $options['auto_reload'] ? $this->debug : (bool) $options['auto_reload']; + $this->extensions = array( + 'core' => new Twig_Extension_Core(), + 'escaper' => new Twig_Extension_Escaper($options['autoescape']), + 'optimizer' => new Twig_Extension_Optimizer($options['optimizations']), + ); + $this->strictVariables = (bool) $options['strict_variables']; + $this->runtimeInitialized = false; + $this->setCache($options['cache']); + $this->functionCallbacks = array(); + $this->filterCallbacks = array(); + $this->staging = array( + 'functions' => array(), + 'filters' => array(), + 'tests' => array(), + 'token_parsers' => array(), + 'visitors' => array(), + 'globals' => array(), + ); + } + + /** + * Gets the base template class for compiled templates. + * + * @return string The base template class name + */ + public function getBaseTemplateClass() + { + return $this->baseTemplateClass; + } + + /** + * Sets the base template class for compiled templates. + * + * @param string $class The base template class name + */ + public function setBaseTemplateClass($class) + { + $this->baseTemplateClass = $class; + } + + /** + * Enables debugging mode. + */ + public function enableDebug() + { + $this->debug = true; + } + + /** + * Disables debugging mode. + */ + public function disableDebug() + { + $this->debug = false; + } + + /** + * Checks if debug mode is enabled. + * + * @return Boolean true if debug mode is enabled, false otherwise + */ + public function isDebug() + { + return $this->debug; + } + + /** + * Enables the auto_reload option. + */ + public function enableAutoReload() + { + $this->autoReload = true; + } + + /** + * Disables the auto_reload option. + */ + public function disableAutoReload() + { + $this->autoReload = false; + } + + /** + * Checks if the auto_reload option is enabled. + * + * @return Boolean true if auto_reload is enabled, false otherwise + */ + public function isAutoReload() + { + return $this->autoReload; + } + + /** + * Enables the strict_variables option. + */ + public function enableStrictVariables() + { + $this->strictVariables = true; + } + + /** + * Disables the strict_variables option. + */ + public function disableStrictVariables() + { + $this->strictVariables = false; + } + + /** + * Checks if the strict_variables option is enabled. + * + * @return Boolean true if strict_variables is enabled, false otherwise + */ + public function isStrictVariables() + { + return $this->strictVariables; + } + + /** + * Gets the cache directory or false if cache is disabled. + * + * @return string|false + */ + public function getCache() + { + return $this->cache; + } + + /** + * Sets the cache directory or false if cache is disabled. + * + * @param string|false $cache The absolute path to the compiled templates, + * or false to disable cache + */ + public function setCache($cache) + { + $this->cache = $cache ? $cache : false; + } + + /** + * Gets the cache filename for a given template. + * + * @param string $name The template name + * + * @return string The cache file name + */ + public function getCacheFilename($name) + { + if (false === $this->cache) { + return false; + } + + $class = substr($this->getTemplateClass($name), strlen($this->templateClassPrefix)); + + return $this->getCache().'/'.substr($class, 0, 2).'/'.substr($class, 2, 2).'/'.substr($class, 4).'.php'; + } + + /** + * Gets the template class associated with the given string. + * + * @param string $name The name for which to calculate the template class name + * @param integer $index The index if it is an embedded template + * + * @return string The template class name + */ + public function getTemplateClass($name, $index = null) + { + return $this->templateClassPrefix.md5($this->loader->getCacheKey($name)).(null === $index ? '' : '_'.$index); + } + + /** + * Gets the template class prefix. + * + * @return string The template class prefix + */ + public function getTemplateClassPrefix() + { + return $this->templateClassPrefix; + } + + /** + * Renders a template. + * + * @param string $name The template name + * @param array $context An array of parameters to pass to the template + * + * @return string The rendered template + */ + public function render($name, array $context = array()) + { + return $this->loadTemplate($name)->render($context); + } + + /** + * Displays a template. + * + * @param string $name The template name + * @param array $context An array of parameters to pass to the template + */ + public function display($name, array $context = array()) + { + $this->loadTemplate($name)->display($context); + } + + /** + * Loads a template by name. + * + * @param string $name The template name + * @param integer $index The index if it is an embedded template + * + * @return Twig_TemplateInterface A template instance representing the given template name + */ + public function loadTemplate($name, $index = null) + { + $cls = $this->getTemplateClass($name, $index); + + if (isset($this->loadedTemplates[$cls])) { + return $this->loadedTemplates[$cls]; + } + + if (!class_exists($cls, false)) { + if (false === $cache = $this->getCacheFilename($name)) { + eval('?>'.$this->compileSource($this->loader->getSource($name), $name)); + } else { + if (!is_file($cache) || ($this->isAutoReload() && !$this->isTemplateFresh($name, filemtime($cache)))) { + $this->writeCacheFile($cache, $this->compileSource($this->loader->getSource($name), $name)); + } + + require_once $cache; + } + } + + if (!$this->runtimeInitialized) { + $this->initRuntime(); + } + + return $this->loadedTemplates[$cls] = new $cls($this); + } + + /** + * Returns true if the template is still fresh. + * + * Besides checking the loader for freshness information, + * this method also checks if the enabled extensions have + * not changed. + * + * @param string $name The template name + * @param timestamp $time The last modification time of the cached template + * + * @return Boolean true if the template is fresh, false otherwise + */ + public function isTemplateFresh($name, $time) + { + foreach ($this->extensions as $extension) { + $r = new ReflectionObject($extension); + if (filemtime($r->getFileName()) > $time) { + return false; + } + } + + return $this->loader->isFresh($name, $time); + } + + public function resolveTemplate($names) + { + if (!is_array($names)) { + $names = array($names); + } + + foreach ($names as $name) { + if ($name instanceof Twig_Template) { + return $name; + } + + try { + return $this->loadTemplate($name); + } catch (Twig_Error_Loader $e) { + } + } + + if (1 === count($names)) { + throw $e; + } + + throw new Twig_Error_Loader(sprintf('Unable to find one of the following templates: "%s".', implode('", "', $names))); + } + + /** + * Clears the internal template cache. + */ + public function clearTemplateCache() + { + $this->loadedTemplates = array(); + } + + /** + * Clears the template cache files on the filesystem. + */ + public function clearCacheFiles() + { + if (false === $this->cache) { + return; + } + + foreach (new RecursiveIteratorIterator(new RecursiveDirectoryIterator($this->cache), RecursiveIteratorIterator::LEAVES_ONLY) as $file) { + if ($file->isFile()) { + @unlink($file->getPathname()); + } + } + } + + /** + * Gets the Lexer instance. + * + * @return Twig_LexerInterface A Twig_LexerInterface instance + */ + public function getLexer() + { + if (null === $this->lexer) { + $this->lexer = new Twig_Lexer($this); + } + + return $this->lexer; + } + + /** + * Sets the Lexer instance. + * + * @param Twig_LexerInterface A Twig_LexerInterface instance + */ + public function setLexer(Twig_LexerInterface $lexer) + { + $this->lexer = $lexer; + } + + /** + * Tokenizes a source code. + * + * @param string $source The template source code + * @param string $name The template name + * + * @return Twig_TokenStream A Twig_TokenStream instance + */ + public function tokenize($source, $name = null) + { + return $this->getLexer()->tokenize($source, $name); + } + + /** + * Gets the Parser instance. + * + * @return Twig_ParserInterface A Twig_ParserInterface instance + */ + public function getParser() + { + if (null === $this->parser) { + $this->parser = new Twig_Parser($this); + } + + return $this->parser; + } + + /** + * Sets the Parser instance. + * + * @param Twig_ParserInterface A Twig_ParserInterface instance + */ + public function setParser(Twig_ParserInterface $parser) + { + $this->parser = $parser; + } + + /** + * Parses a token stream. + * + * @param Twig_TokenStream $tokens A Twig_TokenStream instance + * + * @return Twig_Node_Module A Node tree + */ + public function parse(Twig_TokenStream $tokens) + { + return $this->getParser()->parse($tokens); + } + + /** + * Gets the Compiler instance. + * + * @return Twig_CompilerInterface A Twig_CompilerInterface instance + */ + public function getCompiler() + { + if (null === $this->compiler) { + $this->compiler = new Twig_Compiler($this); + } + + return $this->compiler; + } + + /** + * Sets the Compiler instance. + * + * @param Twig_CompilerInterface $compiler A Twig_CompilerInterface instance + */ + public function setCompiler(Twig_CompilerInterface $compiler) + { + $this->compiler = $compiler; + } + + /** + * Compiles a Node. + * + * @param Twig_NodeInterface $node A Twig_NodeInterface instance + * + * @return string The compiled PHP source code + */ + public function compile(Twig_NodeInterface $node) + { + return $this->getCompiler()->compile($node)->getSource(); + } + + /** + * Compiles a template source code. + * + * @param string $source The template source code + * @param string $name The template name + * + * @return string The compiled PHP source code + */ + public function compileSource($source, $name = null) + { + try { + return $this->compile($this->parse($this->tokenize($source, $name))); + } catch (Twig_Error $e) { + $e->setTemplateFile($name); + throw $e; + } catch (Exception $e) { + throw new Twig_Error_Runtime(sprintf('An exception has been thrown during the compilation of a template ("%s").', $e->getMessage()), -1, $name, $e); + } + } + + /** + * Sets the Loader instance. + * + * @param Twig_LoaderInterface $loader A Twig_LoaderInterface instance + */ + public function setLoader(Twig_LoaderInterface $loader) + { + $this->loader = $loader; + } + + /** + * Gets the Loader instance. + * + * @return Twig_LoaderInterface A Twig_LoaderInterface instance + */ + public function getLoader() + { + return $this->loader; + } + + /** + * Sets the default template charset. + * + * @param string $charset The default charset + */ + public function setCharset($charset) + { + $this->charset = $charset; + } + + /** + * Gets the default template charset. + * + * @return string The default charset + */ + public function getCharset() + { + return $this->charset; + } + + /** + * Initializes the runtime environment. + */ + public function initRuntime() + { + $this->runtimeInitialized = true; + + foreach ($this->getExtensions() as $extension) { + $extension->initRuntime($this); + } + } + + /** + * Returns true if the given extension is registered. + * + * @param string $name The extension name + * + * @return Boolean Whether the extension is registered or not + */ + public function hasExtension($name) + { + return isset($this->extensions[$name]); + } + + /** + * Gets an extension by name. + * + * @param string $name The extension name + * + * @return Twig_ExtensionInterface A Twig_ExtensionInterface instance + */ + public function getExtension($name) + { + if (!isset($this->extensions[$name])) { + throw new Twig_Error_Runtime(sprintf('The "%s" extension is not enabled.', $name)); + } + + return $this->extensions[$name]; + } + + /** + * Registers an extension. + * + * @param Twig_ExtensionInterface $extension A Twig_ExtensionInterface instance + */ + public function addExtension(Twig_ExtensionInterface $extension) + { + $this->extensions[$extension->getName()] = $extension; + $this->parsers = null; + $this->visitors = null; + $this->filters = null; + $this->tests = null; + $this->functions = null; + $this->globals = null; + } + + /** + * Removes an extension by name. + * + * @param string $name The extension name + */ + public function removeExtension($name) + { + unset($this->extensions[$name]); + $this->parsers = null; + $this->visitors = null; + $this->filters = null; + $this->tests = null; + $this->functions = null; + $this->globals = null; + } + + /** + * Registers an array of extensions. + * + * @param array $extensions An array of extensions + */ + public function setExtensions(array $extensions) + { + foreach ($extensions as $extension) { + $this->addExtension($extension); + } + } + + /** + * Returns all registered extensions. + * + * @return array An array of extensions + */ + public function getExtensions() + { + return $this->extensions; + } + + /** + * Registers a Token Parser. + * + * @param Twig_TokenParserInterface $parser A Twig_TokenParserInterface instance + */ + public function addTokenParser(Twig_TokenParserInterface $parser) + { + $this->staging['token_parsers'][] = $parser; + $this->parsers = null; + } + + /** + * Gets the registered Token Parsers. + * + * @return Twig_TokenParserBrokerInterface A broker containing token parsers + */ + public function getTokenParsers() + { + if (null === $this->parsers) { + $this->parsers = new Twig_TokenParserBroker(); + + if (isset($this->staging['token_parsers'])) { + foreach ($this->staging['token_parsers'] as $parser) { + $this->parsers->addTokenParser($parser); + } + } + + foreach ($this->getExtensions() as $extension) { + $parsers = $extension->getTokenParsers(); + foreach($parsers as $parser) { + if ($parser instanceof Twig_TokenParserInterface) { + $this->parsers->addTokenParser($parser); + } elseif ($parser instanceof Twig_TokenParserBrokerInterface) { + $this->parsers->addTokenParserBroker($parser); + } else { + throw new Twig_Error_Runtime('getTokenParsers() must return an array of Twig_TokenParserInterface or Twig_TokenParserBrokerInterface instances'); + } + } + } + } + + return $this->parsers; + } + + /** + * Gets registered tags. + * + * Be warned that this method cannot return tags defined by Twig_TokenParserBrokerInterface classes. + * + * @return Twig_TokenParserInterface[] An array of Twig_TokenParserInterface instances + */ + public function getTags() + { + $tags = array(); + foreach ($this->getTokenParsers()->getParsers() as $parser) { + if ($parser instanceof Twig_TokenParserInterface) { + $tags[$parser->getTag()] = $parser; + } + } + + return $tags; + } + + /** + * Registers a Node Visitor. + * + * @param Twig_NodeVisitorInterface $visitor A Twig_NodeVisitorInterface instance + */ + public function addNodeVisitor(Twig_NodeVisitorInterface $visitor) + { + $this->staging['visitors'][] = $visitor; + $this->visitors = null; + } + + /** + * Gets the registered Node Visitors. + * + * @return Twig_NodeVisitorInterface[] An array of Twig_NodeVisitorInterface instances + */ + public function getNodeVisitors() + { + if (null === $this->visitors) { + foreach ($this->getExtensions() as $extension) { + foreach ($extension->getNodeVisitors() as $visitor) { + $this->addNodeVisitor($visitor); + } + } + + $this->visitors = $this->staging['visitors']; + } + + return $this->visitors; + } + + /** + * Registers a Filter. + * + * @param string $name The filter name + * @param Twig_FilterInterface $filter A Twig_FilterInterface instance + */ + public function addFilter($name, Twig_FilterInterface $filter) + { + $this->staging['filters'][$name] = $filter; + $this->filters = null; + } + + /** + * Get a filter by name. + * + * Subclasses may override this method and load filters differently; + * so no list of filters is available. + * + * @param string $name The filter name + * + * @return Twig_Filter|false A Twig_Filter instance or false if the filter does not exists + */ + public function getFilter($name) + { + if (null === $this->filters) { + $this->getFilters(); + } + + if (isset($this->filters[$name])) { + return $this->filters[$name]; + } + + foreach ($this->filters as $pattern => $filter) { + $pattern = str_replace('\\*', '(.*?)', preg_quote($pattern, '#'), $count); + + if ($count) { + if (preg_match('#^'.$pattern.'$#', $name, $matches)) { + array_shift($matches); + $filter->setArguments($matches); + + return $filter; + } + } + } + + foreach ($this->filterCallbacks as $callback) { + if (false !== $filter = call_user_func($callback, $name)) { + return $filter; + } + } + + return false; + } + + public function registerUndefinedFilterCallback($callable) + { + $this->filterCallbacks[] = $callable; + } + + /** + * Gets the registered Filters. + * + * Be warned that this method cannot return filters defined with registerUndefinedFunctionCallback. + * + * @return Twig_FilterInterface[] An array of Twig_FilterInterface instances + * + * @see registerUndefinedFilterCallback + */ + public function getFilters() + { + if (null === $this->filters) { + foreach ($this->getExtensions() as $extension) { + foreach ($extension->getFilters() as $name => $filter) { + $this->addFilter($name, $filter); + } + } + + $this->filters = $this->staging['filters']; + } + + return $this->filters; + } + + /** + * Registers a Test. + * + * @param string $name The test name + * @param Twig_TestInterface $test A Twig_TestInterface instance + */ + public function addTest($name, Twig_TestInterface $test) + { + $this->staging['tests'][$name] = $test; + $this->tests = null; + } + + /** + * Gets the registered Tests. + * + * @return Twig_TestInterface[] An array of Twig_TestInterface instances + */ + public function getTests() + { + if (null === $this->tests) { + foreach ($this->getExtensions() as $extension) { + foreach ($extension->getTests() as $name => $test) { + $this->addTest($name, $test); + } + } + + $this->tests = $this->staging['tests']; + } + + return $this->tests; + } + + /** + * Registers a Function. + * + * @param string $name The function name + * @param Twig_FunctionInterface $function A Twig_FunctionInterface instance + */ + public function addFunction($name, Twig_FunctionInterface $function) + { + $this->staging['functions'][$name] = $function; + $this->functions = null; + } + + /** + * Get a function by name. + * + * Subclasses may override this method and load functions differently; + * so no list of functions is available. + * + * @param string $name function name + * + * @return Twig_Function|false A Twig_Function instance or false if the function does not exists + */ + public function getFunction($name) + { + if (null === $this->functions) { + $this->getFunctions(); + } + + if (isset($this->functions[$name])) { + return $this->functions[$name]; + } + + foreach ($this->functions as $pattern => $function) { + $pattern = str_replace('\\*', '(.*?)', preg_quote($pattern, '#'), $count); + + if ($count) { + if (preg_match('#^'.$pattern.'$#', $name, $matches)) { + array_shift($matches); + $function->setArguments($matches); + + return $function; + } + } + } + + foreach ($this->functionCallbacks as $callback) { + if (false !== $function = call_user_func($callback, $name)) { + return $function; + } + } + + return false; + } + + public function registerUndefinedFunctionCallback($callable) + { + $this->functionCallbacks[] = $callable; + } + + /** + * Gets registered functions. + * + * Be warned that this method cannot return functions defined with registerUndefinedFunctionCallback. + * + * @return Twig_FunctionInterface[] An array of Twig_FunctionInterface instances + * + * @see registerUndefinedFunctionCallback + */ + public function getFunctions() + { + if (null === $this->functions) { + foreach ($this->getExtensions() as $extension) { + foreach ($extension->getFunctions() as $name => $function) { + $this->addFunction($name, $function); + } + } + + $this->functions = $this->staging['functions']; + } + + return $this->functions; + } + + /** + * Registers a Global. + * + * @param string $name The global name + * @param mixed $value The global value + */ + public function addGlobal($name, $value) + { + $this->staging['globals'][$name] = $value; + $this->globals = null; + } + + /** + * Gets the registered Globals. + * + * @return array An array of globals + */ + public function getGlobals() + { + if (null === $this->globals) { + $this->globals = isset($this->staging['globals']) ? $this->staging['globals'] : array(); + foreach ($this->getExtensions() as $extension) { + $this->globals = array_merge($this->globals, $extension->getGlobals()); + } + } + + return $this->globals; + } + + /** + * Merges a context with the defined globals. + * + * @param array $context An array representing the context + * + * @return array The context merged with the globals + */ + public function mergeGlobals(array $context) + { + // we don't use array_merge as the context being generally + // bigger than globals, this code is faster. + foreach ($this->getGlobals() as $key => $value) { + if (!array_key_exists($key, $context)) { + $context[$key] = $value; + } + } + + return $context; + } + + /** + * Gets the registered unary Operators. + * + * @return array An array of unary operators + */ + public function getUnaryOperators() + { + if (null === $this->unaryOperators) { + $this->initOperators(); + } + + return $this->unaryOperators; + } + + /** + * Gets the registered binary Operators. + * + * @return array An array of binary operators + */ + public function getBinaryOperators() + { + if (null === $this->binaryOperators) { + $this->initOperators(); + } + + return $this->binaryOperators; + } + + public function computeAlternatives($name, $items) + { + $alternatives = array(); + foreach ($items as $item) { + $lev = levenshtein($name, $item); + if ($lev <= strlen($name) / 3 || false !== strpos($item, $name)) { + $alternatives[$item] = $lev; + } + } + asort($alternatives); + + return array_keys($alternatives); + } + + protected function initOperators() + { + $this->unaryOperators = array(); + $this->binaryOperators = array(); + foreach ($this->getExtensions() as $extension) { + $operators = $extension->getOperators(); + + if (!$operators) { + continue; + } + + if (2 !== count($operators)) { + throw new InvalidArgumentException(sprintf('"%s::getOperators()" does not return a valid operators array.', get_class($extension))); + } + + $this->unaryOperators = array_merge($this->unaryOperators, $operators[0]); + $this->binaryOperators = array_merge($this->binaryOperators, $operators[1]); + } + } + + protected function writeCacheFile($file, $content) + { + $dir = dirname($file); + if (!is_dir($dir)) { + if (false === @mkdir($dir, 0777, true) && !is_dir($dir)) { + throw new RuntimeException(sprintf("Unable to create the cache directory (%s).", $dir)); + } + } elseif (!is_writable($dir)) { + throw new RuntimeException(sprintf("Unable to write in the cache directory (%s).", $dir)); + } + + $tmpFile = tempnam(dirname($file), basename($file)); + if (false !== @file_put_contents($tmpFile, $content)) { + // rename does not work on Win32 before 5.2.6 + if (@rename($tmpFile, $file) || (@copy($tmpFile, $file) && unlink($tmpFile))) { + @chmod($file, 0644); + + return; + } + } + + throw new Twig_Error_Runtime(sprintf('Failed to write cache file "%s".', $file)); + } +} diff --git a/vendor/Twig/Error.php b/vendor/Twig/Error.php new file mode 100644 index 0000000..8ac2a2e --- /dev/null +++ b/vendor/Twig/Error.php @@ -0,0 +1,199 @@ + + */ +class Twig_Error extends Exception +{ + protected $lineno; + protected $filename; + protected $rawMessage; + protected $previous; + + /** + * Constructor. + * + * @param string $message The error message + * @param integer $lineno The template line where the error occurred + * @param string $filename The template file name where the error occurred + * @param Exception $previous The previous exception + */ + public function __construct($message, $lineno = -1, $filename = null, Exception $previous = null) + { + if (version_compare(PHP_VERSION, '5.3.0', '<')) { + $this->previous = $previous; + parent::__construct(''); + } else { + parent::__construct('', 0, $previous); + } + + $this->lineno = $lineno; + $this->filename = $filename; + + if (-1 === $this->lineno || null === $this->filename) { + $this->guessTemplateInfo(); + } + + $this->rawMessage = $message; + + $this->updateRepr(); + } + + /** + * Gets the raw message. + * + * @return string The raw message + */ + public function getRawMessage() + { + return $this->rawMessage; + } + + /** + * Gets the filename where the error occurred. + * + * @return string The filename + */ + public function getTemplateFile() + { + return $this->filename; + } + + /** + * Sets the filename where the error occurred. + * + * @param string $filename The filename + */ + public function setTemplateFile($filename) + { + $this->filename = $filename; + + $this->updateRepr(); + } + + /** + * Gets the template line where the error occurred. + * + * @return integer The template line + */ + public function getTemplateLine() + { + return $this->lineno; + } + + /** + * Sets the template line where the error occurred. + * + * @param integer $lineno The template line + */ + public function setTemplateLine($lineno) + { + $this->lineno = $lineno; + + $this->updateRepr(); + } + + /** + * For PHP < 5.3.0, provides access to the getPrevious() method. + * + * @param string $method The method name + * @param array $arguments The parameters to be passed to the method + * + * @return Exception The previous exception or null + */ + public function __call($method, $arguments) + { + if ('getprevious' == strtolower($method)) { + return $this->previous; + } + + throw new BadMethodCallException(sprintf('Method "Twig_Error::%s()" does not exist.', $method)); + } + + protected function updateRepr() + { + $this->message = $this->rawMessage; + + $dot = false; + if ('.' === substr($this->message, -1)) { + $this->message = substr($this->message, 0, -1); + $dot = true; + } + + if (null !== $this->filename) { + if (is_string($this->filename) || (is_object($this->filename) && method_exists($this->filename, '__toString'))) { + $filename = sprintf('"%s"', $this->filename); + } else { + $filename = json_encode($this->filename); + } + $this->message .= sprintf(' in %s', $filename); + } + + if ($this->lineno >= 0) { + $this->message .= sprintf(' at line %d', $this->lineno); + } + + if ($dot) { + $this->message .= '.'; + } + } + + protected function guessTemplateInfo() + { + $template = null; + foreach (debug_backtrace() as $trace) { + if (isset($trace['object']) && $trace['object'] instanceof Twig_Template && 'Twig_Template' !== get_class($trace['object'])) { + $template = $trace['object']; + + // update template filename + if (null === $this->filename) { + $this->filename = $template->getTemplateName(); + } + + break; + } + } + + if (null === $template || $this->lineno > -1) { + return; + } + + $r = new ReflectionObject($template); + $file = $r->getFileName(); + + $exceptions = array($e = $this); + while (($e instanceof self || method_exists($e, 'getPrevious')) && $e = $e->getPrevious()) { + $exceptions[] = $e; + } + + while ($e = array_pop($exceptions)) { + $traces = $e->getTrace(); + while ($trace = array_shift($traces)) { + if (!isset($trace['file']) || !isset($trace['line']) || $file != $trace['file']) { + continue; + } + + foreach ($template->getDebugInfo() as $codeLine => $templateLine) { + if ($codeLine <= $trace['line']) { + // update template line + $this->lineno = $templateLine; + + return; + } + } + } + } + } +} diff --git a/vendor/Twig/Error/Loader.php b/vendor/Twig/Error/Loader.php new file mode 100644 index 0000000..418a776 --- /dev/null +++ b/vendor/Twig/Error/Loader.php @@ -0,0 +1,20 @@ + + */ +class Twig_Error_Loader extends Twig_Error +{ +} diff --git a/vendor/Twig/Error/Runtime.php b/vendor/Twig/Error/Runtime.php new file mode 100644 index 0000000..8a387fa --- /dev/null +++ b/vendor/Twig/Error/Runtime.php @@ -0,0 +1,21 @@ + + */ +class Twig_Error_Runtime extends Twig_Error +{ +} diff --git a/vendor/Twig/Error/Syntax.php b/vendor/Twig/Error/Syntax.php new file mode 100644 index 0000000..a2650c3 --- /dev/null +++ b/vendor/Twig/Error/Syntax.php @@ -0,0 +1,21 @@ + + */ +class Twig_Error_Syntax extends Twig_Error +{ +} diff --git a/vendor/Twig/ExpressionParser.php b/vendor/Twig/ExpressionParser.php new file mode 100644 index 0000000..0f35930 --- /dev/null +++ b/vendor/Twig/ExpressionParser.php @@ -0,0 +1,488 @@ + + */ +class Twig_ExpressionParser +{ + const OPERATOR_LEFT = 1; + const OPERATOR_RIGHT = 2; + + protected $parser; + protected $unaryOperators; + protected $binaryOperators; + + public function __construct(Twig_Parser $parser, array $unaryOperators, array $binaryOperators) + { + $this->parser = $parser; + $this->unaryOperators = $unaryOperators; + $this->binaryOperators = $binaryOperators; + } + + public function parseExpression($precedence = 0) + { + $expr = $this->getPrimary(); + $token = $this->parser->getCurrentToken(); + while ($this->isBinary($token) && $this->binaryOperators[$token->getValue()]['precedence'] >= $precedence) { + $op = $this->binaryOperators[$token->getValue()]; + $this->parser->getStream()->next(); + + if (isset($op['callable'])) { + $expr = call_user_func($op['callable'], $this->parser, $expr); + } else { + $expr1 = $this->parseExpression(self::OPERATOR_LEFT === $op['associativity'] ? $op['precedence'] + 1 : $op['precedence']); + $class = $op['class']; + $expr = new $class($expr, $expr1, $token->getLine()); + } + + $token = $this->parser->getCurrentToken(); + } + + if (0 === $precedence) { + return $this->parseConditionalExpression($expr); + } + + return $expr; + } + + protected function getPrimary() + { + $token = $this->parser->getCurrentToken(); + + if ($this->isUnary($token)) { + $operator = $this->unaryOperators[$token->getValue()]; + $this->parser->getStream()->next(); + $expr = $this->parseExpression($operator['precedence']); + $class = $operator['class']; + + return $this->parsePostfixExpression(new $class($expr, $token->getLine())); + } elseif ($token->test(Twig_Token::PUNCTUATION_TYPE, '(')) { + $this->parser->getStream()->next(); + $expr = $this->parseExpression(); + $this->parser->getStream()->expect(Twig_Token::PUNCTUATION_TYPE, ')', 'An opened parenthesis is not properly closed'); + + return $this->parsePostfixExpression($expr); + } + + return $this->parsePrimaryExpression(); + } + + protected function parseConditionalExpression($expr) + { + while ($this->parser->getStream()->test(Twig_Token::PUNCTUATION_TYPE, '?')) { + $this->parser->getStream()->next(); + $expr2 = $this->parseExpression(); + $this->parser->getStream()->expect(Twig_Token::PUNCTUATION_TYPE, ':', 'The ternary operator must have a default value'); + $expr3 = $this->parseExpression(); + + $expr = new Twig_Node_Expression_Conditional($expr, $expr2, $expr3, $this->parser->getCurrentToken()->getLine()); + } + + return $expr; + } + + protected function isUnary(Twig_Token $token) + { + return $token->test(Twig_Token::OPERATOR_TYPE) && isset($this->unaryOperators[$token->getValue()]); + } + + protected function isBinary(Twig_Token $token) + { + return $token->test(Twig_Token::OPERATOR_TYPE) && isset($this->binaryOperators[$token->getValue()]); + } + + public function parsePrimaryExpression() + { + $token = $this->parser->getCurrentToken(); + switch ($token->getType()) { + case Twig_Token::NAME_TYPE: + $this->parser->getStream()->next(); + switch ($token->getValue()) { + case 'true': + case 'TRUE': + $node = new Twig_Node_Expression_Constant(true, $token->getLine()); + break; + + case 'false': + case 'FALSE': + $node = new Twig_Node_Expression_Constant(false, $token->getLine()); + break; + + case 'none': + case 'NONE': + case 'null': + case 'NULL': + $node = new Twig_Node_Expression_Constant(null, $token->getLine()); + break; + + default: + if ('(' === $this->parser->getCurrentToken()->getValue()) { + $node = $this->getFunctionNode($token->getValue(), $token->getLine()); + } else { + $node = new Twig_Node_Expression_Name($token->getValue(), $token->getLine()); + } + } + break; + + case Twig_Token::NUMBER_TYPE: + $this->parser->getStream()->next(); + $node = new Twig_Node_Expression_Constant($token->getValue(), $token->getLine()); + break; + + case Twig_Token::STRING_TYPE: + case Twig_Token::INTERPOLATION_START_TYPE: + $node = $this->parseStringExpression(); + break; + + default: + if ($token->test(Twig_Token::PUNCTUATION_TYPE, '[')) { + $node = $this->parseArrayExpression(); + } elseif ($token->test(Twig_Token::PUNCTUATION_TYPE, '{')) { + $node = $this->parseHashExpression(); + } else { + throw new Twig_Error_Syntax(sprintf('Unexpected token "%s" of value "%s"', Twig_Token::typeToEnglish($token->getType(), $token->getLine()), $token->getValue()), $token->getLine()); + } + } + + return $this->parsePostfixExpression($node); + } + + public function parseStringExpression() + { + $stream = $this->parser->getStream(); + + $nodes = array(); + // a string cannot be followed by another string in a single expression + $nextCanBeString = true; + while (true) { + if ($stream->test(Twig_Token::STRING_TYPE) && $nextCanBeString) { + $token = $stream->next(); + $nodes[] = new Twig_Node_Expression_Constant($token->getValue(), $token->getLine()); + $nextCanBeString = false; + } elseif ($stream->test(Twig_Token::INTERPOLATION_START_TYPE)) { + $stream->next(); + $nodes[] = $this->parseExpression(); + $stream->expect(Twig_Token::INTERPOLATION_END_TYPE); + $nextCanBeString = true; + } else { + break; + } + } + + $expr = array_shift($nodes); + foreach ($nodes as $node) { + $expr = new Twig_Node_Expression_Binary_Concat($expr, $node, $node->getLine()); + } + + return $expr; + } + + public function parseArrayExpression() + { + $stream = $this->parser->getStream(); + $stream->expect(Twig_Token::PUNCTUATION_TYPE, '[', 'An array element was expected'); + + $node = new Twig_Node_Expression_Array(array(), $stream->getCurrent()->getLine()); + $first = true; + while (!$stream->test(Twig_Token::PUNCTUATION_TYPE, ']')) { + if (!$first) { + $stream->expect(Twig_Token::PUNCTUATION_TYPE, ',', 'An array element must be followed by a comma'); + + // trailing ,? + if ($stream->test(Twig_Token::PUNCTUATION_TYPE, ']')) { + break; + } + } + $first = false; + + $node->addElement($this->parseExpression()); + } + $stream->expect(Twig_Token::PUNCTUATION_TYPE, ']', 'An opened array is not properly closed'); + + return $node; + } + + public function parseHashExpression() + { + $stream = $this->parser->getStream(); + $stream->expect(Twig_Token::PUNCTUATION_TYPE, '{', 'A hash element was expected'); + + $node = new Twig_Node_Expression_Array(array(), $stream->getCurrent()->getLine()); + $first = true; + while (!$stream->test(Twig_Token::PUNCTUATION_TYPE, '}')) { + if (!$first) { + $stream->expect(Twig_Token::PUNCTUATION_TYPE, ',', 'A hash value must be followed by a comma'); + + // trailing ,? + if ($stream->test(Twig_Token::PUNCTUATION_TYPE, '}')) { + break; + } + } + $first = false; + + // a hash key can be: + // + // * a number -- 12 + // * a string -- 'a' + // * a name, which is equivalent to a string -- a + // * an expression, which must be enclosed in parentheses -- (1 + 2) + if ($stream->test(Twig_Token::STRING_TYPE) || $stream->test(Twig_Token::NAME_TYPE) || $stream->test(Twig_Token::NUMBER_TYPE)) { + $token = $stream->next(); + $key = new Twig_Node_Expression_Constant($token->getValue(), $token->getLine()); + } elseif ($stream->test(Twig_Token::PUNCTUATION_TYPE, '(')) { + $key = $this->parseExpression(); + } else { + $current = $stream->getCurrent(); + + throw new Twig_Error_Syntax(sprintf('A hash key must be a quoted string, a number, a name, or an expression enclosed in parentheses (unexpected token "%s" of value "%s"', Twig_Token::typeToEnglish($current->getType(), $current->getLine()), $current->getValue()), $current->getLine()); + } + + $stream->expect(Twig_Token::PUNCTUATION_TYPE, ':', 'A hash key must be followed by a colon (:)'); + $value = $this->parseExpression(); + + $node->addElement($value, $key); + } + $stream->expect(Twig_Token::PUNCTUATION_TYPE, '}', 'An opened hash is not properly closed'); + + return $node; + } + + public function parsePostfixExpression($node) + { + while (true) { + $token = $this->parser->getCurrentToken(); + if ($token->getType() == Twig_Token::PUNCTUATION_TYPE) { + if ('.' == $token->getValue() || '[' == $token->getValue()) { + $node = $this->parseSubscriptExpression($node); + } elseif ('|' == $token->getValue()) { + $node = $this->parseFilterExpression($node); + } else { + break; + } + } else { + break; + } + } + + return $node; + } + + public function getFunctionNode($name, $line) + { + $args = $this->parseArguments(); + switch ($name) { + case 'parent': + if (!count($this->parser->getBlockStack())) { + throw new Twig_Error_Syntax('Calling "parent" outside a block is forbidden', $line); + } + + if (!$this->parser->getParent() && !$this->parser->hasTraits()) { + throw new Twig_Error_Syntax('Calling "parent" on a template that does not extend nor "use" another template is forbidden', $line); + } + + return new Twig_Node_Expression_Parent($this->parser->peekBlockStack(), $line); + case 'block': + return new Twig_Node_Expression_BlockReference($args->getNode(0), false, $line); + case 'attribute': + if (count($args) < 2) { + throw new Twig_Error_Syntax('The "attribute" function takes at least two arguments (the variable and the attributes)', $line); + } + + return new Twig_Node_Expression_GetAttr($args->getNode(0), $args->getNode(1), count($args) > 2 ? $args->getNode(2) : new Twig_Node_Expression_Array(array(), $line), Twig_TemplateInterface::ANY_CALL, $line); + default: + if (null !== $alias = $this->parser->getImportedFunction($name)) { + $arguments = new Twig_Node_Expression_Array(array(), $line); + foreach ($args as $n) { + $arguments->addElement($n); + } + + $node = new Twig_Node_Expression_MethodCall($alias['node'], $alias['name'], $arguments, $line); + $node->setAttribute('safe', true); + + return $node; + } + + $class = $this->getFunctionNodeClass($name); + + return new $class($name, $args, $line); + } + } + + public function parseSubscriptExpression($node) + { + $stream = $this->parser->getStream(); + $token = $stream->next(); + $lineno = $token->getLine(); + $arguments = new Twig_Node_Expression_Array(array(), $lineno); + $type = Twig_TemplateInterface::ANY_CALL; + if ($token->getValue() == '.') { + $token = $stream->next(); + if ( + $token->getType() == Twig_Token::NAME_TYPE + || + $token->getType() == Twig_Token::NUMBER_TYPE + || + ($token->getType() == Twig_Token::OPERATOR_TYPE && preg_match(Twig_Lexer::REGEX_NAME, $token->getValue())) + ) { + $arg = new Twig_Node_Expression_Constant($token->getValue(), $lineno); + + if ($stream->test(Twig_Token::PUNCTUATION_TYPE, '(')) { + $type = Twig_TemplateInterface::METHOD_CALL; + foreach ($this->parseArguments() as $n) { + $arguments->addElement($n); + } + } + } else { + throw new Twig_Error_Syntax('Expected name or number', $lineno); + } + } else { + $type = Twig_TemplateInterface::ARRAY_CALL; + + $arg = $this->parseExpression(); + + // slice? + if ($stream->test(Twig_Token::PUNCTUATION_TYPE, ':')) { + $stream->next(); + + if ($stream->test(Twig_Token::PUNCTUATION_TYPE, ']')) { + $length = new Twig_Node_Expression_Constant(null, $token->getLine()); + } else { + $length = $this->parseExpression(); + } + + $class = $this->getFilterNodeClass('slice'); + $arguments = new Twig_Node(array($arg, $length)); + $filter = new $class($node, new Twig_Node_Expression_Constant('slice', $token->getLine()), $arguments, $token->getLine()); + + $stream->expect(Twig_Token::PUNCTUATION_TYPE, ']'); + + return $filter; + } + + $stream->expect(Twig_Token::PUNCTUATION_TYPE, ']'); + } + + return new Twig_Node_Expression_GetAttr($node, $arg, $arguments, $type, $lineno); + } + + public function parseFilterExpression($node) + { + $this->parser->getStream()->next(); + + return $this->parseFilterExpressionRaw($node); + } + + public function parseFilterExpressionRaw($node, $tag = null) + { + while (true) { + $token = $this->parser->getStream()->expect(Twig_Token::NAME_TYPE); + + $name = new Twig_Node_Expression_Constant($token->getValue(), $token->getLine()); + if (!$this->parser->getStream()->test(Twig_Token::PUNCTUATION_TYPE, '(')) { + $arguments = new Twig_Node(); + } else { + $arguments = $this->parseArguments(); + } + + $class = $this->getFilterNodeClass($name->getAttribute('value')); + + $node = new $class($node, $name, $arguments, $token->getLine(), $tag); + + if (!$this->parser->getStream()->test(Twig_Token::PUNCTUATION_TYPE, '|')) { + break; + } + + $this->parser->getStream()->next(); + } + + return $node; + } + + public function parseArguments() + { + $args = array(); + $stream = $this->parser->getStream(); + + $stream->expect(Twig_Token::PUNCTUATION_TYPE, '(', 'A list of arguments must be opened by a parenthesis'); + while (!$stream->test(Twig_Token::PUNCTUATION_TYPE, ')')) { + if (!empty($args)) { + $stream->expect(Twig_Token::PUNCTUATION_TYPE, ',', 'Arguments must be separated by a comma'); + } + $args[] = $this->parseExpression(); + } + $stream->expect(Twig_Token::PUNCTUATION_TYPE, ')', 'A list of arguments must be closed by a parenthesis'); + + return new Twig_Node($args); + } + + public function parseAssignmentExpression() + { + $targets = array(); + while (true) { + $token = $this->parser->getStream()->expect(Twig_Token::NAME_TYPE, null, 'Only variables can be assigned to'); + if (in_array($token->getValue(), array('true', 'false', 'none'))) { + throw new Twig_Error_Syntax(sprintf('You cannot assign a value to "%s"', $token->getValue()), $token->getLine()); + } + $targets[] = new Twig_Node_Expression_AssignName($token->getValue(), $token->getLine()); + + if (!$this->parser->getStream()->test(Twig_Token::PUNCTUATION_TYPE, ',')) { + break; + } + $this->parser->getStream()->next(); + } + + return new Twig_Node($targets); + } + + public function parseMultitargetExpression() + { + $targets = array(); + while (true) { + $targets[] = $this->parseExpression(); + if (!$this->parser->getStream()->test(Twig_Token::PUNCTUATION_TYPE, ',')) { + break; + } + $this->parser->getStream()->next(); + } + + return new Twig_Node($targets); + } + + protected function getFunctionNodeClass($name) + { + $functionMap = $this->parser->getEnvironment()->getFunctions(); + if (isset($functionMap[$name]) && $functionMap[$name] instanceof Twig_Function_Node) { + return $functionMap[$name]->getClass(); + } + + return 'Twig_Node_Expression_Function'; + } + + protected function getFilterNodeClass($name) + { + $filterMap = $this->parser->getEnvironment()->getFilters(); + if (isset($filterMap[$name]) && $filterMap[$name] instanceof Twig_Filter_Node) { + return $filterMap[$name]->getClass(); + } + + return 'Twig_Node_Expression_Filter'; + } +} diff --git a/vendor/Twig/Extension.php b/vendor/Twig/Extension.php new file mode 100644 index 0000000..931fc03 --- /dev/null +++ b/vendor/Twig/Extension.php @@ -0,0 +1,93 @@ +dateFormats[0] = $format; + } + + if (null !== $dateIntervalFormat) { + $this->dateFormats[1] = $dateIntervalFormat; + } + } + + /** + * Gets the default format to be used by the date filter. + * + * @return array The default date format string and the default date interval format string + */ + public function getDateFormat() + { + return $this->dateFormats; + } + + /** + * Sets the default timezone to be used by the date filter. + * + * @param DateTimeZone|string $timezone The default timezone string or a DateTimeZone object + */ + public function setTimezone($timezone) + { + $this->timezone = $timezone instanceof DateTimeZone ? $timezone : new DateTimeZone($timezone); + } + + /** + * Gets the default timezone to be used by the date filter. + * + * @return DateTimeZone The default timezone currently in use + */ + public function getTimezone() + { + return $this->timezone; + } + + /** + * Sets the default format to be used by the number_format filter. + * + * @param integer $decimal The number of decimal places to use. + * @param string $decimalPoint The character(s) to use for the decimal point. + * @param string $thousandSep The character(s) to use for the thousands separator. + */ + public function setNumberFormat($decimal, $decimalPoint, $thousandSep) + { + $this->numberFormat = array($decimal, $decimalPoint, $thousandSep); + } + + /** + * Get the default format used by the number_format filter. + * + * @return array The arguments for number_format() + */ + public function getNumberFormat() + { + return $this->numberFormat; + } + + /** + * Returns the token parser instance to add to the existing list. + * + * @return array An array of Twig_TokenParser instances + */ + public function getTokenParsers() + { + return array( + new Twig_TokenParser_For(), + new Twig_TokenParser_If(), + new Twig_TokenParser_Extends(), + new Twig_TokenParser_Include(), + new Twig_TokenParser_Block(), + new Twig_TokenParser_Use(), + new Twig_TokenParser_Filter(), + new Twig_TokenParser_Macro(), + new Twig_TokenParser_Import(), + new Twig_TokenParser_From(), + new Twig_TokenParser_Set(), + new Twig_TokenParser_Spaceless(), + new Twig_TokenParser_Flush(), + new Twig_TokenParser_Do(), + new Twig_TokenParser_Embed(), + ); + } + + /** + * Returns a list of filters to add to the existing list. + * + * @return array An array of filters + */ + public function getFilters() + { + $filters = array( + // formatting filters + 'date' => new Twig_Filter_Function('twig_date_format_filter', array('needs_environment' => true)), + 'format' => new Twig_Filter_Function('sprintf'), + 'replace' => new Twig_Filter_Function('strtr'), + 'number_format' => new Twig_Filter_Function('twig_number_format_filter', array('needs_environment' => true)), + + // encoding + 'url_encode' => new Twig_Filter_Function('twig_urlencode_filter'), + 'json_encode' => new Twig_Filter_Function('twig_jsonencode_filter'), + 'convert_encoding' => new Twig_Filter_Function('twig_convert_encoding'), + + // string filters + 'title' => new Twig_Filter_Function('twig_title_string_filter', array('needs_environment' => true)), + 'capitalize' => new Twig_Filter_Function('twig_capitalize_string_filter', array('needs_environment' => true)), + 'upper' => new Twig_Filter_Function('strtoupper'), + 'lower' => new Twig_Filter_Function('strtolower'), + 'striptags' => new Twig_Filter_Function('strip_tags'), + 'trim' => new Twig_Filter_Function('trim'), + 'nl2br' => new Twig_Filter_Function('nl2br', array('pre_escape' => 'html', 'is_safe' => array('html'))), + + // array helpers + 'join' => new Twig_Filter_Function('twig_join_filter'), + 'sort' => new Twig_Filter_Function('twig_sort_filter'), + 'merge' => new Twig_Filter_Function('twig_array_merge'), + + // string/array filters + 'reverse' => new Twig_Filter_Function('twig_reverse_filter', array('needs_environment' => true)), + 'length' => new Twig_Filter_Function('twig_length_filter', array('needs_environment' => true)), + 'slice' => new Twig_Filter_Function('twig_slice', array('needs_environment' => true)), + + // iteration and runtime + 'default' => new Twig_Filter_Node('Twig_Node_Expression_Filter_Default'), + '_default' => new Twig_Filter_Function('_twig_default_filter'), + + 'keys' => new Twig_Filter_Function('twig_get_array_keys_filter'), + + // escaping + 'escape' => new Twig_Filter_Function('twig_escape_filter', array('needs_environment' => true, 'is_safe_callback' => 'twig_escape_filter_is_safe')), + 'e' => new Twig_Filter_Function('twig_escape_filter', array('needs_environment' => true, 'is_safe_callback' => 'twig_escape_filter_is_safe')), + ); + + if (function_exists('mb_get_info')) { + $filters['upper'] = new Twig_Filter_Function('twig_upper_filter', array('needs_environment' => true)); + $filters['lower'] = new Twig_Filter_Function('twig_lower_filter', array('needs_environment' => true)); + } + + return $filters; + } + + /** + * Returns a list of global functions to add to the existing list. + * + * @return array An array of global functions + */ + public function getFunctions() + { + return array( + 'range' => new Twig_Function_Function('range'), + 'constant' => new Twig_Function_Function('constant'), + 'cycle' => new Twig_Function_Function('twig_cycle'), + 'random' => new Twig_Function_Function('twig_random', array('needs_environment' => true)), + 'date' => new Twig_Function_Function('twig_date_converter', array('needs_environment' => true)), + ); + } + + /** + * Returns a list of tests to add to the existing list. + * + * @return array An array of tests + */ + public function getTests() + { + return array( + 'even' => new Twig_Test_Node('Twig_Node_Expression_Test_Even'), + 'odd' => new Twig_Test_Node('Twig_Node_Expression_Test_Odd'), + 'defined' => new Twig_Test_Node('Twig_Node_Expression_Test_Defined'), + 'sameas' => new Twig_Test_Node('Twig_Node_Expression_Test_Sameas'), + 'none' => new Twig_Test_Node('Twig_Node_Expression_Test_Null'), + 'null' => new Twig_Test_Node('Twig_Node_Expression_Test_Null'), + 'divisibleby' => new Twig_Test_Node('Twig_Node_Expression_Test_Divisibleby'), + 'constant' => new Twig_Test_Node('Twig_Node_Expression_Test_Constant'), + 'empty' => new Twig_Test_Function('twig_test_empty'), + 'iterable' => new Twig_Test_Function('twig_test_iterable'), + ); + } + + /** + * Returns a list of operators to add to the existing list. + * + * @return array An array of operators + */ + public function getOperators() + { + return array( + array( + 'not' => array('precedence' => 50, 'class' => 'Twig_Node_Expression_Unary_Not'), + '-' => array('precedence' => 500, 'class' => 'Twig_Node_Expression_Unary_Neg'), + '+' => array('precedence' => 500, 'class' => 'Twig_Node_Expression_Unary_Pos'), + ), + array( + 'b-and' => array('precedence' => 5, 'class' => 'Twig_Node_Expression_Binary_BitwiseAnd', 'associativity' => Twig_ExpressionParser::OPERATOR_LEFT), + 'b-xor' => array('precedence' => 5, 'class' => 'Twig_Node_Expression_Binary_BitwiseXor', 'associativity' => Twig_ExpressionParser::OPERATOR_LEFT), + 'b-or' => array('precedence' => 5, 'class' => 'Twig_Node_Expression_Binary_BitwiseOr', 'associativity' => Twig_ExpressionParser::OPERATOR_LEFT), + 'or' => array('precedence' => 10, 'class' => 'Twig_Node_Expression_Binary_Or', 'associativity' => Twig_ExpressionParser::OPERATOR_LEFT), + 'and' => array('precedence' => 15, 'class' => 'Twig_Node_Expression_Binary_And', 'associativity' => Twig_ExpressionParser::OPERATOR_LEFT), + '==' => array('precedence' => 20, 'class' => 'Twig_Node_Expression_Binary_Equal', 'associativity' => Twig_ExpressionParser::OPERATOR_LEFT), + '!=' => array('precedence' => 20, 'class' => 'Twig_Node_Expression_Binary_NotEqual', 'associativity' => Twig_ExpressionParser::OPERATOR_LEFT), + '<' => array('precedence' => 20, 'class' => 'Twig_Node_Expression_Binary_Less', 'associativity' => Twig_ExpressionParser::OPERATOR_LEFT), + '>' => array('precedence' => 20, 'class' => 'Twig_Node_Expression_Binary_Greater', 'associativity' => Twig_ExpressionParser::OPERATOR_LEFT), + '>=' => array('precedence' => 20, 'class' => 'Twig_Node_Expression_Binary_GreaterEqual', 'associativity' => Twig_ExpressionParser::OPERATOR_LEFT), + '<=' => array('precedence' => 20, 'class' => 'Twig_Node_Expression_Binary_LessEqual', 'associativity' => Twig_ExpressionParser::OPERATOR_LEFT), + 'not in' => array('precedence' => 20, 'class' => 'Twig_Node_Expression_Binary_NotIn', 'associativity' => Twig_ExpressionParser::OPERATOR_LEFT), + 'in' => array('precedence' => 20, 'class' => 'Twig_Node_Expression_Binary_In', 'associativity' => Twig_ExpressionParser::OPERATOR_LEFT), + '..' => array('precedence' => 25, 'class' => 'Twig_Node_Expression_Binary_Range', 'associativity' => Twig_ExpressionParser::OPERATOR_LEFT), + '+' => array('precedence' => 30, 'class' => 'Twig_Node_Expression_Binary_Add', 'associativity' => Twig_ExpressionParser::OPERATOR_LEFT), + '-' => array('precedence' => 30, 'class' => 'Twig_Node_Expression_Binary_Sub', 'associativity' => Twig_ExpressionParser::OPERATOR_LEFT), + '~' => array('precedence' => 40, 'class' => 'Twig_Node_Expression_Binary_Concat', 'associativity' => Twig_ExpressionParser::OPERATOR_LEFT), + '*' => array('precedence' => 60, 'class' => 'Twig_Node_Expression_Binary_Mul', 'associativity' => Twig_ExpressionParser::OPERATOR_LEFT), + '/' => array('precedence' => 60, 'class' => 'Twig_Node_Expression_Binary_Div', 'associativity' => Twig_ExpressionParser::OPERATOR_LEFT), + '//' => array('precedence' => 60, 'class' => 'Twig_Node_Expression_Binary_FloorDiv', 'associativity' => Twig_ExpressionParser::OPERATOR_LEFT), + '%' => array('precedence' => 60, 'class' => 'Twig_Node_Expression_Binary_Mod', 'associativity' => Twig_ExpressionParser::OPERATOR_LEFT), + 'is' => array('precedence' => 100, 'callable' => array($this, 'parseTestExpression'), 'associativity' => Twig_ExpressionParser::OPERATOR_LEFT), + 'is not' => array('precedence' => 100, 'callable' => array($this, 'parseNotTestExpression'), 'associativity' => Twig_ExpressionParser::OPERATOR_LEFT), + '**' => array('precedence' => 200, 'class' => 'Twig_Node_Expression_Binary_Power', 'associativity' => Twig_ExpressionParser::OPERATOR_RIGHT), + ), + ); + } + + public function parseNotTestExpression(Twig_Parser $parser, $node) + { + return new Twig_Node_Expression_Unary_Not($this->parseTestExpression($parser, $node), $parser->getCurrentToken()->getLine()); + } + + public function parseTestExpression(Twig_Parser $parser, $node) + { + $stream = $parser->getStream(); + $name = $stream->expect(Twig_Token::NAME_TYPE)->getValue(); + $arguments = null; + if ($stream->test(Twig_Token::PUNCTUATION_TYPE, '(')) { + $arguments = $parser->getExpressionParser()->parseArguments(); + } + + $class = $this->getTestNodeClass($parser->getEnvironment(), $name); + + return new $class($node, $name, $arguments, $parser->getCurrentToken()->getLine()); + } + + protected function getTestNodeClass(Twig_Environment $env, $name) + { + $testMap = $env->getTests(); + if (isset($testMap[$name]) && $testMap[$name] instanceof Twig_Test_Node) { + return $testMap[$name]->getClass(); + } + + return 'Twig_Node_Expression_Test'; + } + + /** + * Returns the name of the extension. + * + * @return string The extension name + */ + public function getName() + { + return 'core'; + } +} + +/** + * Cycles over a value. + * + * @param ArrayAccess|array $values An array or an ArrayAccess instance + * @param integer $i The cycle value + * + * @return string The next value in the cycle + */ +function twig_cycle($values, $i) +{ + if (!is_array($values) && !$values instanceof ArrayAccess) { + return $values; + } + + return $values[$i % count($values)]; +} + +/** + * Returns a random value depending on the supplied parameter type: + * - a random item from a Traversable or array + * - a random character from a string + * - a random integer between 0 and the integer parameter + * + * @param Twig_Environment $env A Twig_Environment instance + * @param Traversable|array|int|string $values The values to pick a random item from + * + * @throws Twig_Error_Runtime When $values is an empty array (does not apply to an empty string which is returned as is). + * + * @return mixed A random value from the given sequence + */ +function twig_random(Twig_Environment $env, $values = null) +{ + if (null === $values) { + return mt_rand(); + } + + if (is_int($values) || is_float($values)) { + return $values < 0 ? mt_rand($values, 0) : mt_rand(0, $values); + } + + if ($values instanceof Traversable) { + $values = iterator_to_array($values); + } elseif (is_string($values)) { + if ('' === $values) { + return ''; + } + if (null !== $charset = $env->getCharset()) { + if ('UTF-8' != $charset) { + $values = twig_convert_encoding($values, 'UTF-8', $charset); + } + + // unicode version of str_split() + // split at all positions, but not after the start and not before the end + $values = preg_split('/(? $value) { + $values[$i] = twig_convert_encoding($value, $charset, 'UTF-8'); + } + } + } else { + return $values[mt_rand(0, strlen($values) - 1)]; + } + } + + if (!is_array($values)) { + return $values; + } + + if (0 === count($values)) { + throw new Twig_Error_Runtime('The random function cannot pick from an empty array.'); + } + + return $values[array_rand($values, 1)]; +} + +/** + * Converts a date to the given format. + * + *
+ *   {{ post.published_at|date("m/d/Y") }}
+ * 
+ * + * @param Twig_Environment $env A Twig_Environment instance + * @param DateTime|DateInterval|string $date A date + * @param string $format A format + * @param DateTimeZone|string $timezone A timezone + * + * @return string The formatter date + */ +function twig_date_format_filter(Twig_Environment $env, $date, $format = null, $timezone = null) +{ + if (null === $format) { + $formats = $env->getExtension('core')->getDateFormat(); + $format = $date instanceof DateInterval ? $formats[1] : $formats[0]; + } + + if ($date instanceof DateInterval || $date instanceof DateTime) { + if (null !== $timezone) { + $date = clone $date; + $date->setTimezone($timezone instanceof DateTimeZone ? $timezone : new DateTimeZone($timezone)); + } + + return $date->format($format); + } + + return twig_date_converter($env, $date, $timezone)->format($format); +} + +/** + * Converts an input to a DateTime instance. + * + *
+ *    {% if date(user.created_at) < date('+2days') %}
+ *      {# do something #}
+ *    {% endif %}
+ * 
+ * + * @param Twig_Environment $env A Twig_Environment instance + * @param DateTime|string $date A date + * @param DateTimeZone|string $timezone A timezone + * + * @return DateTime A DateTime instance + */ +function twig_date_converter(Twig_Environment $env, $date = null, $timezone = null) +{ + if ($date instanceof DateTime) { + return $date; + } + + $asString = (string) $date; + + if (ctype_digit($asString) || (!empty($asString) && '-' === $asString[0] && ctype_digit(substr($asString, 1)))) { + $date = new DateTime('@'.$date); + $date->setTimezone(new DateTimeZone(date_default_timezone_get())); + } else { + $date = new DateTime($date); + } + + // set Timezone + if (null !== $timezone) { + if (!$timezone instanceof DateTimeZone) { + $timezone = new DateTimeZone($timezone); + } + + $date->setTimezone($timezone); + } elseif (($timezone = $env->getExtension('core')->getTimezone()) instanceof DateTimeZone) { + $date->setTimezone($timezone); + } + + return $date; +} + +/** + * Number format filter. + * + * All of the formatting options can be left null, in that case the defaults will + * be used. Supplying any of the parameters will override the defaults set in the + * environment object. + * + * @param Twig_Environment $env A Twig_Environment instance + * @param mixed $number A float/int/string of the number to format + * @param int $decimal The number of decimal points to display. + * @param string $decimalPoint The character(s) to use for the decimal point. + * @param string $thousandSep The character(s) to use for the thousands separator. + * + * @return string The formatted number + */ +function twig_number_format_filter(Twig_Environment $env, $number, $decimal = null, $decimalPoint = null, $thousandSep = null) +{ + $defaults = $env->getExtension('core')->getNumberFormat(); + if (null === $decimal) { + $decimal = $defaults[0]; + } + + if (null === $decimalPoint) { + $decimalPoint = $defaults[1]; + } + + if (null === $thousandSep) { + $thousandSep = $defaults[2]; + } + + return number_format((float) $number, $decimal, $decimalPoint, $thousandSep); +} + +/** + * URL encodes a string. + * + * @param string $url A URL + * @param bool $raw true to use rawurlencode() instead of urlencode + * + * @return string The URL encoded value + */ +function twig_urlencode_filter($url, $raw = false) +{ + if ($raw) { + return rawurlencode($url); + } + + return urlencode($url); +} + +if (version_compare(PHP_VERSION, '5.3.0', '<')) { + /** + * JSON encodes a variable. + * + * @param mixed $value The value to encode. + * @param integer $options Not used on PHP 5.2.x + * + * @return mixed The JSON encoded value + */ + function twig_jsonencode_filter($value, $options = 0) + { + if ($value instanceof Twig_Markup) { + $value = (string) $value; + } elseif (is_array($value)) { + array_walk_recursive($value, '_twig_markup2string'); + } + + return json_encode($value); + } +} else { + /** + * JSON encodes a variable. + * + * @param mixed $value The value to encode. + * @param integer $options Bitmask consisting of JSON_HEX_QUOT, JSON_HEX_TAG, JSON_HEX_AMP, JSON_HEX_APOS, JSON_NUMERIC_CHECK, JSON_PRETTY_PRINT, JSON_UNESCAPED_SLASHES, JSON_FORCE_OBJECT + * + * @return mixed The JSON encoded value + */ + function twig_jsonencode_filter($value, $options = 0) + { + if ($value instanceof Twig_Markup) { + $value = (string) $value; + } elseif (is_array($value)) { + array_walk_recursive($value, '_twig_markup2string'); + } + + return json_encode($value, $options); + } +} + +function _twig_markup2string(&$value) +{ + if ($value instanceof Twig_Markup) { + $value = (string) $value; + } +} + +/** + * Merges an array with another one. + * + *
+ *  {% set items = { 'apple': 'fruit', 'orange': 'fruit' } %}
+ *
+ *  {% set items = items|merge({ 'peugeot': 'car' }) %}
+ *
+ *  {# items now contains { 'apple': 'fruit', 'orange': 'fruit', 'peugeot': 'car' } #}
+ * 
+ * + * @param array $arr1 An array + * @param array $arr2 An array + * + * @return array The merged array + */ +function twig_array_merge($arr1, $arr2) +{ + if (!is_array($arr1) || !is_array($arr2)) { + throw new Twig_Error_Runtime('The merge filter only works with arrays or hashes.'); + } + + return array_merge($arr1, $arr2); +} + +/** + * Slices a variable. + * + * @param Twig_Environment $env A Twig_Environment instance + * @param mixed $item A variable + * @param integer $start Start of the slice + * @param integer $length Size of the slice + * @param Boolean $preserveKeys Whether to preserve key or not (when the input is an array) + * + * @return mixed The sliced variable + */ +function twig_slice(Twig_Environment $env, $item, $start, $length = null, $preserveKeys = false) +{ + if ($item instanceof Traversable) { + $item = iterator_to_array($item, false); + } + + if (is_array($item)) { + return array_slice($item, $start, $length, $preserveKeys); + } + + $item = (string) $item; + + if (function_exists('mb_get_info') && null !== $charset = $env->getCharset()) { + return mb_substr($item, $start, null === $length ? mb_strlen($item, $charset) - $start : $length, $charset); + } + + return null === $length ? substr($item, $start) : substr($item, $start, $length); +} + +/** + * Joins the values to a string. + * + * The separator between elements is an empty string per default, you can define it with the optional parameter. + * + *
+ *  {{ [1, 2, 3]|join('|') }}
+ *  {# returns 1|2|3 #}
+ *
+ *  {{ [1, 2, 3]|join }}
+ *  {# returns 123 #}
+ * 
+ * + * @param array $value An array + * @param string $glue The separator + * + * @return string The concatenated string + */ +function twig_join_filter($value, $glue = '') +{ + if ($value instanceof Traversable) { + $value = iterator_to_array($value, false); + } + + return implode($glue, (array) $value); +} + +// The '_default' filter is used internally to avoid using the ternary operator +// which costs a lot for big contexts (before PHP 5.4). So, on average, +// a function call is cheaper. +function _twig_default_filter($value, $default = '') +{ + if (twig_test_empty($value)) { + return $default; + } + + return $value; +} + +/** + * Returns the keys for the given array. + * + * It is useful when you want to iterate over the keys of an array: + * + *
+ *  {% for key in array|keys %}
+ *      {# ... #}
+ *  {% endfor %}
+ * 
+ * + * @param array $array An array + * + * @return array The keys + */ +function twig_get_array_keys_filter($array) +{ + if (is_object($array) && $array instanceof Traversable) { + return array_keys(iterator_to_array($array)); + } + + if (!is_array($array)) { + return array(); + } + + return array_keys($array); +} + +/** + * Reverses a variable. + * + * @param Twig_Environment $env A Twig_Environment instance + * @param array|Traversable|string $item An array, a Traversable instance, or a string + * @param Boolean $preserveKeys Whether to preserve key or not + * + * @return mixed The reversed input + */ +function twig_reverse_filter(Twig_Environment $env, $item, $preserveKeys = false) +{ + if (is_object($item) && $item instanceof Traversable) { + return array_reverse(iterator_to_array($item), $preserveKeys); + } + + if (is_array($item)) { + return array_reverse($item, $preserveKeys); + } + + if (null !== $charset = $env->getCharset()) { + $string = (string) $item; + + if ('UTF-8' != $charset) { + $item = twig_convert_encoding($string, 'UTF-8', $charset); + } + + preg_match_all('/./us', $item, $matches); + + $string = implode('', array_reverse($matches[0])); + + if ('UTF-8' != $charset) { + $string = twig_convert_encoding($string, $charset, 'UTF-8'); + } + + return $string; + } + + return strrev((string) $item); +} + +/** + * Sorts an array. + * + * @param array $array An array + */ +function twig_sort_filter($array) +{ + asort($array); + + return $array; +} + +/* used internally */ +function twig_in_filter($value, $compare) +{ + if (is_array($compare)) { + return in_array($value, $compare); + } elseif (is_string($compare)) { + if (!strlen((string) $value)) { + return empty($compare); + } + + return false !== strpos($compare, (string) $value); + } elseif (is_object($compare) && $compare instanceof Traversable) { + return in_array($value, iterator_to_array($compare, false)); + } + + return false; +} + +/** + * Escapes a string. + * + * @param Twig_Environment $env A Twig_Environment instance + * @param string $string The value to be escaped + * @param string $strategy The escaping strategy + * @param string $charset The charset + * @param Boolean $autoescape Whether the function is called by the auto-escaping feature (true) or by the developer (false) + */ +function twig_escape_filter(Twig_Environment $env, $string, $strategy = 'html', $charset = null, $autoescape = false) +{ + if ($autoescape && is_object($string) && $string instanceof Twig_Markup) { + return $string; + } + + if (!is_string($string) && !(is_object($string) && method_exists($string, '__toString'))) { + return $string; + } + + if (null === $charset) { + $charset = $env->getCharset(); + } + + $string = (string) $string; + + switch ($strategy) { + case 'js': + // escape all non-alphanumeric characters + // into their \xHH or \uHHHH representations + if ('UTF-8' != $charset) { + $string = twig_convert_encoding($string, 'UTF-8', $charset); + } + + if (null === $string = preg_replace_callback('#[^\p{L}\p{N} ]#u', '_twig_escape_js_callback', $string)) { + throw new Twig_Error_Runtime('The string to escape is not a valid UTF-8 string.'); + } + + if ('UTF-8' != $charset) { + $string = twig_convert_encoding($string, $charset, 'UTF-8'); + } + + return $string; + + case 'html': + // see http://php.net/htmlspecialchars + + // Using a static variable to avoid initializing the array + // each time the function is called. Moving the declaration on the + // top of the function slow downs other escaping strategies. + static $htmlspecialcharsCharsets = array( + 'iso-8859-1' => true, 'iso8859-1' => true, + 'iso-8859-15' => true, 'iso8859-15' => true, + 'utf-8' => true, + 'cp866' => true, 'ibm866' => true, '866' => true, + 'cp1251' => true, 'windows-1251' => true, 'win-1251' => true, + '1251' => true, + 'cp1252' => true, 'windows-1252' => true, '1252' => true, + 'koi8-r' => true, 'koi8-ru' => true, 'koi8r' => true, + 'big5' => true, '950' => true, + 'gb2312' => true, '936' => true, + 'big5-hkscs' => true, + 'shift_jis' => true, 'sjis' => true, '932' => true, + 'euc-jp' => true, 'eucjp' => true, + 'iso8859-5' => true, 'iso-8859-5' => true, 'macroman' => true, + ); + + if (isset($htmlspecialcharsCharsets[strtolower($charset)])) { + return htmlspecialchars($string, ENT_QUOTES | ENT_SUBSTITUTE, $charset); + } + + $string = twig_convert_encoding($string, 'UTF-8', $charset); + $string = htmlspecialchars($string, ENT_QUOTES | ENT_SUBSTITUTE, 'UTF-8'); + + return twig_convert_encoding($string, $charset, 'UTF-8'); + + default: + throw new Twig_Error_Runtime(sprintf('Invalid escaping strategy "%s" (valid ones: html, js).', $strategy)); + } +} + +/* used internally */ +function twig_escape_filter_is_safe(Twig_Node $filterArgs) +{ + foreach ($filterArgs as $arg) { + if ($arg instanceof Twig_Node_Expression_Constant) { + return array($arg->getAttribute('value')); + } + + return array(); + } + + return array('html'); +} + +if (function_exists('iconv')) { + function twig_convert_encoding($string, $to, $from) + { + return iconv($from, $to, $string); + } +} elseif (function_exists('mb_convert_encoding')) { + function twig_convert_encoding($string, $to, $from) + { + return mb_convert_encoding($string, $to, $from); + } +} else { + function twig_convert_encoding($string, $to, $from) + { + throw new Twig_Error_Runtime('No suitable convert encoding function (use UTF-8 as your encoding or install the iconv or mbstring extension).'); + } +} + +function _twig_escape_js_callback($matches) +{ + $char = $matches[0]; + + // \xHH + if (!isset($char[1])) { + return '\\x'.substr('00'.bin2hex($char), -2); + } + + // \uHHHH + $char = twig_convert_encoding($char, 'UTF-16BE', 'UTF-8'); + + return '\\u'.substr('0000'.bin2hex($char), -4); +} + +// add multibyte extensions if possible +if (function_exists('mb_get_info')) { + /** + * Returns the length of a variable. + * + * @param Twig_Environment $env A Twig_Environment instance + * @param mixed $thing A variable + * + * @return integer The length of the value + */ + function twig_length_filter(Twig_Environment $env, $thing) + { + return is_scalar($thing) ? mb_strlen($thing, $env->getCharset()) : count($thing); + } + + /** + * Converts a string to uppercase. + * + * @param Twig_Environment $env A Twig_Environment instance + * @param string $string A string + * + * @return string The uppercased string + */ + function twig_upper_filter(Twig_Environment $env, $string) + { + if (null !== ($charset = $env->getCharset())) { + return mb_strtoupper($string, $charset); + } + + return strtoupper($string); + } + + /** + * Converts a string to lowercase. + * + * @param Twig_Environment $env A Twig_Environment instance + * @param string $string A string + * + * @return string The lowercased string + */ + function twig_lower_filter(Twig_Environment $env, $string) + { + if (null !== ($charset = $env->getCharset())) { + return mb_strtolower($string, $charset); + } + + return strtolower($string); + } + + /** + * Returns a titlecased string. + * + * @param Twig_Environment $env A Twig_Environment instance + * @param string $string A string + * + * @return string The titlecased string + */ + function twig_title_string_filter(Twig_Environment $env, $string) + { + if (null !== ($charset = $env->getCharset())) { + return mb_convert_case($string, MB_CASE_TITLE, $charset); + } + + return ucwords(strtolower($string)); + } + + /** + * Returns a capitalized string. + * + * @param Twig_Environment $env A Twig_Environment instance + * @param string $string A string + * + * @return string The capitalized string + */ + function twig_capitalize_string_filter(Twig_Environment $env, $string) + { + if (null !== ($charset = $env->getCharset())) { + return mb_strtoupper(mb_substr($string, 0, 1, $charset), $charset). + mb_strtolower(mb_substr($string, 1, mb_strlen($string, $charset), $charset), $charset); + } + + return ucfirst(strtolower($string)); + } +} +// and byte fallback +else +{ + /** + * Returns the length of a variable. + * + * @param Twig_Environment $env A Twig_Environment instance + * @param mixed $thing A variable + * + * @return integer The length of the value + */ + function twig_length_filter(Twig_Environment $env, $thing) + { + return is_scalar($thing) ? strlen($thing) : count($thing); + } + + /** + * Returns a titlecased string. + * + * @param Twig_Environment $env A Twig_Environment instance + * @param string $string A string + * + * @return string The titlecased string + */ + function twig_title_string_filter(Twig_Environment $env, $string) + { + return ucwords(strtolower($string)); + } + + /** + * Returns a capitalized string. + * + * @param Twig_Environment $env A Twig_Environment instance + * @param string $string A string + * + * @return string The capitalized string + */ + function twig_capitalize_string_filter(Twig_Environment $env, $string) + { + return ucfirst(strtolower($string)); + } +} + +/* used internally */ +function twig_ensure_traversable($seq) +{ + if ($seq instanceof Traversable || is_array($seq)) { + return $seq; + } + + return array(); +} + +/** + * Checks if a variable is empty. + * + *
+ * {# evaluates to true if the foo variable is null, false, or the empty string #}
+ * {% if foo is empty %}
+ *     {# ... #}
+ * {% endif %}
+ * 
+ * + * @param mixed $value A variable + * + * @return Boolean true if the value is empty, false otherwise + */ +function twig_test_empty($value) +{ + if ($value instanceof Countable) { + return 0 == count($value); + } + + return false === $value || (empty($value) && '0' != $value); +} + +/** + * Checks if a variable is traversable. + * + *
+ * {# evaluates to true if the foo variable is an array or a traversable object #}
+ * {% if foo is traversable %}
+ *     {# ... #}
+ * {% endif %}
+ * 
+ * + * @param mixed $value A variable + * + * @return Boolean true if the value is traversable + */ +function twig_test_iterable($value) +{ + return $value instanceof Traversable || is_array($value); +} diff --git a/vendor/Twig/Extension/Debug.php b/vendor/Twig/Extension/Debug.php new file mode 100644 index 0000000..aab7093 --- /dev/null +++ b/vendor/Twig/Extension/Debug.php @@ -0,0 +1,64 @@ + new Twig_Function_Function('twig_var_dump', array('is_safe' => $isDumpOutputHtmlSafe ? array('html') : array(), 'needs_context' => true, 'needs_environment' => true)), + ); + } + + /** + * Returns the name of the extension. + * + * @return string The extension name + */ + public function getName() + { + return 'debug'; + } +} + +function twig_var_dump(Twig_Environment $env, $context) +{ + if (!$env->isDebug()) { + return; + } + + ob_start(); + + $count = func_num_args(); + if (2 === $count) { + $vars = array(); + foreach ($context as $key => $value) { + if (!$value instanceof Twig_Template) { + $vars[$key] = $value; + } + } + + var_dump($vars); + } else { + for ($i = 2; $i < $count; $i++) { + var_dump(func_get_arg($i)); + } + } + + return ob_get_clean(); +} diff --git a/vendor/Twig/Extension/Escaper.php b/vendor/Twig/Extension/Escaper.php new file mode 100644 index 0000000..b0aa8b1 --- /dev/null +++ b/vendor/Twig/Extension/Escaper.php @@ -0,0 +1,106 @@ +setDefaultStrategy($defaultStrategy); + } + + /** + * Returns the token parser instances to add to the existing list. + * + * @return array An array of Twig_TokenParserInterface or Twig_TokenParserBrokerInterface instances + */ + public function getTokenParsers() + { + return array(new Twig_TokenParser_AutoEscape()); + } + + /** + * Returns the node visitor instances to add to the existing list. + * + * @return array An array of Twig_NodeVisitorInterface instances + */ + public function getNodeVisitors() + { + return array(new Twig_NodeVisitor_Escaper()); + } + + /** + * Returns a list of filters to add to the existing list. + * + * @return array An array of filters + */ + public function getFilters() + { + return array( + 'raw' => new Twig_Filter_Function('twig_raw_filter', array('is_safe' => array('all'))), + ); + } + + /** + * Sets the default strategy to use when not defined by the user. + * + * The strategy can be a valid PHP callback that takes the template + * "filename" as an argument and returns the strategy to use. + * + * @param mixed $defaultStrategy An escaping strategy + */ + public function setDefaultStrategy($defaultStrategy) + { + // for BC + if (true === $defaultStrategy) { + $defaultStrategy = 'html'; + } + + $this->defaultStrategy = $defaultStrategy; + } + + /** + * Gets the default strategy to use when not defined by the user. + * + * @param string $filename The template "filename" + * + * @return string The default strategy to use for the template + */ + public function getDefaultStrategy($filename) + { + if (is_callable($this->defaultStrategy)) { + return call_user_func($this->defaultStrategy, $filename); + } + + return $this->defaultStrategy; + } + + /** + * Returns the name of the extension. + * + * @return string The extension name + */ + public function getName() + { + return 'escaper'; + } +} + +/** + * Marks a variable as being safe. + * + * @param string $string A PHP variable + */ +function twig_raw_filter($string) +{ + return $string; +} + diff --git a/vendor/Twig/Extension/Optimizer.php b/vendor/Twig/Extension/Optimizer.php new file mode 100644 index 0000000..013fcb6 --- /dev/null +++ b/vendor/Twig/Extension/Optimizer.php @@ -0,0 +1,35 @@ +optimizers = $optimizers; + } + + /** + * {@inheritdoc} + */ + public function getNodeVisitors() + { + return array(new Twig_NodeVisitor_Optimizer($this->optimizers)); + } + + /** + * {@inheritdoc} + */ + public function getName() + { + return 'optimizer'; + } +} diff --git a/vendor/Twig/Extension/Sandbox.php b/vendor/Twig/Extension/Sandbox.php new file mode 100644 index 0000000..bf76c11 --- /dev/null +++ b/vendor/Twig/Extension/Sandbox.php @@ -0,0 +1,112 @@ +policy = $policy; + $this->sandboxedGlobally = $sandboxed; + } + + /** + * Returns the token parser instances to add to the existing list. + * + * @return array An array of Twig_TokenParserInterface or Twig_TokenParserBrokerInterface instances + */ + public function getTokenParsers() + { + return array(new Twig_TokenParser_Sandbox()); + } + + /** + * Returns the node visitor instances to add to the existing list. + * + * @return array An array of Twig_NodeVisitorInterface instances + */ + public function getNodeVisitors() + { + return array(new Twig_NodeVisitor_Sandbox()); + } + + public function enableSandbox() + { + $this->sandboxed = true; + } + + public function disableSandbox() + { + $this->sandboxed = false; + } + + public function isSandboxed() + { + return $this->sandboxedGlobally || $this->sandboxed; + } + + public function isSandboxedGlobally() + { + return $this->sandboxedGlobally; + } + + public function setSecurityPolicy(Twig_Sandbox_SecurityPolicyInterface $policy) + { + $this->policy = $policy; + } + + public function getSecurityPolicy() + { + return $this->policy; + } + + public function checkSecurity($tags, $filters, $functions) + { + if ($this->isSandboxed()) { + $this->policy->checkSecurity($tags, $filters, $functions); + } + } + + public function checkMethodAllowed($obj, $method) + { + if ($this->isSandboxed()) { + $this->policy->checkMethodAllowed($obj, $method); + } + } + + public function checkPropertyAllowed($obj, $method) + { + if ($this->isSandboxed()) { + $this->policy->checkPropertyAllowed($obj, $method); + } + } + + public function ensureToStringAllowed($obj) + { + if (is_object($obj)) { + $this->policy->checkMethodAllowed($obj, '__toString'); + } + + return $obj; + } + + /** + * Returns the name of the extension. + * + * @return string The extension name + */ + public function getName() + { + return 'sandbox'; + } +} diff --git a/vendor/Twig/ExtensionInterface.php b/vendor/Twig/ExtensionInterface.php new file mode 100644 index 0000000..0bfed88 --- /dev/null +++ b/vendor/Twig/ExtensionInterface.php @@ -0,0 +1,84 @@ + + */ +interface Twig_ExtensionInterface +{ + /** + * Initializes the runtime environment. + * + * This is where you can load some file that contains filter functions for instance. + * + * @param Twig_Environment $environment The current Twig_Environment instance + */ + function initRuntime(Twig_Environment $environment); + + /** + * Returns the token parser instances to add to the existing list. + * + * @return array An array of Twig_TokenParserInterface or Twig_TokenParserBrokerInterface instances + */ + function getTokenParsers(); + + /** + * Returns the node visitor instances to add to the existing list. + * + * @return array An array of Twig_NodeVisitorInterface instances + */ + function getNodeVisitors(); + + /** + * Returns a list of filters to add to the existing list. + * + * @return array An array of filters + */ + function getFilters(); + + /** + * Returns a list of tests to add to the existing list. + * + * @return array An array of tests + */ + function getTests(); + + /** + * Returns a list of functions to add to the existing list. + * + * @return array An array of functions + */ + function getFunctions(); + + /** + * Returns a list of operators to add to the existing list. + * + * @return array An array of operators + */ + function getOperators(); + + /** + * Returns a list of global variables to add to the existing list. + * + * @return array An array of global variables + */ + function getGlobals(); + + /** + * Returns the name of the extension. + * + * @return string The extension name + */ + function getName(); +} diff --git a/vendor/Twig/Filter.php b/vendor/Twig/Filter.php new file mode 100644 index 0000000..1a4806c --- /dev/null +++ b/vendor/Twig/Filter.php @@ -0,0 +1,75 @@ + + */ +abstract class Twig_Filter implements Twig_FilterInterface +{ + protected $options; + protected $arguments = array(); + + public function __construct(array $options = array()) + { + $this->options = array_merge(array( + 'needs_environment' => false, + 'needs_context' => false, + 'pre_escape' => null, + 'preserves_safety' => null, + ), $options); + } + + public function setArguments($arguments) + { + $this->arguments = $arguments; + } + + public function getArguments() + { + return $this->arguments; + } + + public function needsEnvironment() + { + return $this->options['needs_environment']; + } + + public function needsContext() + { + return $this->options['needs_context']; + } + + public function getSafe(Twig_Node $filterArgs) + { + if (isset($this->options['is_safe'])) { + return $this->options['is_safe']; + } + + if (isset($this->options['is_safe_callback'])) { + return call_user_func($this->options['is_safe_callback'], $filterArgs); + } + + return null; + } + + public function getPreservesSafety() + { + return $this->options['preserves_safety']; + } + + public function getPreEscape() + { + return $this->options['pre_escape']; + } +} diff --git a/vendor/Twig/Filter/Function.php b/vendor/Twig/Filter/Function.php new file mode 100644 index 0000000..1de078b --- /dev/null +++ b/vendor/Twig/Filter/Function.php @@ -0,0 +1,33 @@ + + */ +class Twig_Filter_Function extends Twig_Filter +{ + protected $function; + + public function __construct($function, array $options = array()) + { + parent::__construct($options); + + $this->function = $function; + } + + public function compile() + { + return $this->function; + } +} diff --git a/vendor/Twig/Filter/Method.php b/vendor/Twig/Filter/Method.php new file mode 100644 index 0000000..d831e0f --- /dev/null +++ b/vendor/Twig/Filter/Method.php @@ -0,0 +1,34 @@ + + */ +class Twig_Filter_Method extends Twig_Filter +{ + protected $extension, $method; + + public function __construct(Twig_ExtensionInterface $extension, $method, array $options = array()) + { + parent::__construct($options); + + $this->extension = $extension; + $this->method = $method; + } + + public function compile() + { + return sprintf('$this->env->getExtension(\'%s\')->%s', $this->extension->getName(), $this->method); + } +} diff --git a/vendor/Twig/Filter/Node.php b/vendor/Twig/Filter/Node.php new file mode 100644 index 0000000..7481c05 --- /dev/null +++ b/vendor/Twig/Filter/Node.php @@ -0,0 +1,37 @@ + + */ +class Twig_Filter_Node extends Twig_Filter +{ + protected $class; + + public function __construct($class, array $options = array()) + { + parent::__construct($options); + + $this->class = $class; + } + + public function getClass() + { + return $this->class; + } + + public function compile() + { + } +} diff --git a/vendor/Twig/FilterInterface.php b/vendor/Twig/FilterInterface.php new file mode 100644 index 0000000..f398026 --- /dev/null +++ b/vendor/Twig/FilterInterface.php @@ -0,0 +1,40 @@ + + */ +interface Twig_FilterInterface +{ + /** + * Compiles a filter. + * + * @return string The PHP code for the filter + */ + function compile(); + + function needsEnvironment(); + + function needsContext(); + + function getSafe(Twig_Node $filterArgs); + + function getPreservesSafety(); + + function getPreEscape(); + + function setArguments($arguments); + + function getArguments(); +} diff --git a/vendor/Twig/Function.php b/vendor/Twig/Function.php new file mode 100644 index 0000000..cd7643f --- /dev/null +++ b/vendor/Twig/Function.php @@ -0,0 +1,63 @@ + + */ +abstract class Twig_Function implements Twig_FunctionInterface +{ + protected $options; + protected $arguments = array(); + + public function __construct(array $options = array()) + { + $this->options = array_merge(array( + 'needs_environment' => false, + 'needs_context' => false, + ), $options); + } + + public function setArguments($arguments) + { + $this->arguments = $arguments; + } + + public function getArguments() + { + return $this->arguments; + } + + public function needsEnvironment() + { + return $this->options['needs_environment']; + } + + public function needsContext() + { + return $this->options['needs_context']; + } + + public function getSafe(Twig_Node $functionArgs) + { + if (isset($this->options['is_safe'])) { + return $this->options['is_safe']; + } + + if (isset($this->options['is_safe_callback'])) { + return call_user_func($this->options['is_safe_callback'], $functionArgs); + } + + return array(); + } +} diff --git a/vendor/Twig/Function/Function.php b/vendor/Twig/Function/Function.php new file mode 100644 index 0000000..3237d8c --- /dev/null +++ b/vendor/Twig/Function/Function.php @@ -0,0 +1,34 @@ + + */ +class Twig_Function_Function extends Twig_Function +{ + protected $function; + + public function __construct($function, array $options = array()) + { + parent::__construct($options); + + $this->function = $function; + } + + public function compile() + { + return $this->function; + } +} diff --git a/vendor/Twig/Function/Method.php b/vendor/Twig/Function/Method.php new file mode 100644 index 0000000..7328566 --- /dev/null +++ b/vendor/Twig/Function/Method.php @@ -0,0 +1,35 @@ + + */ +class Twig_Function_Method extends Twig_Function +{ + protected $extension, $method; + + public function __construct(Twig_ExtensionInterface $extension, $method, array $options = array()) + { + parent::__construct($options); + + $this->extension = $extension; + $this->method = $method; + } + + public function compile() + { + return sprintf('$this->env->getExtension(\'%s\')->%s', $this->extension->getName(), $this->method); + } +} diff --git a/vendor/Twig/Function/Node.php b/vendor/Twig/Function/Node.php new file mode 100644 index 0000000..a687a84 --- /dev/null +++ b/vendor/Twig/Function/Node.php @@ -0,0 +1,37 @@ + + */ +class Twig_Function_Node extends Twig_Filter +{ + protected $class; + + public function __construct($class, array $options = array()) + { + parent::__construct($options); + + $this->class = $class; + } + + public function getClass() + { + return $this->class; + } + + public function compile() + { + } +} diff --git a/vendor/Twig/FunctionInterface.php b/vendor/Twig/FunctionInterface.php new file mode 100644 index 0000000..d402d17 --- /dev/null +++ b/vendor/Twig/FunctionInterface.php @@ -0,0 +1,37 @@ + + */ +interface Twig_FunctionInterface +{ + /** + * Compiles a function. + * + * @return string The PHP code for the function + */ + function compile(); + + function needsEnvironment(); + + function needsContext(); + + function getSafe(Twig_Node $filterArgs); + + function setArguments($arguments); + + function getArguments(); +} diff --git a/vendor/Twig/Lexer.php b/vendor/Twig/Lexer.php new file mode 100644 index 0000000..4d0ba9f --- /dev/null +++ b/vendor/Twig/Lexer.php @@ -0,0 +1,406 @@ + + */ +class Twig_Lexer implements Twig_LexerInterface +{ + protected $tokens; + protected $code; + protected $cursor; + protected $lineno; + protected $end; + protected $state; + protected $states; + protected $brackets; + protected $env; + protected $filename; + protected $options; + protected $regexes; + + const STATE_DATA = 0; + const STATE_BLOCK = 1; + const STATE_VAR = 2; + const STATE_STRING = 3; + const STATE_INTERPOLATION = 4; + + const REGEX_NAME = '/[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*/A'; + const REGEX_NUMBER = '/[0-9]+(?:\.[0-9]+)?/A'; + const REGEX_STRING = '/"([^#"\\\\]*(?:\\\\.[^#"\\\\]*)*)"|\'([^\'\\\\]*(?:\\\\.[^\'\\\\]*)*)\'/As'; + const REGEX_DQ_STRING_DELIM = '/"/A'; + const REGEX_DQ_STRING_PART = '/[^#"\\\\]*(?:(?:\\\\.|#(?!\{))[^#"\\\\]*)*/As'; + const PUNCTUATION = '()[]{}?:.,|'; + + public function __construct(Twig_Environment $env, array $options = array()) + { + $this->env = $env; + + $this->options = array_merge(array( + 'tag_comment' => array('{#', '#}'), + 'tag_block' => array('{%', '%}'), + 'tag_variable' => array('{{', '}}'), + 'whitespace_trim' => '-', + 'interpolation' => array('#{', '}'), + ), $options); + + $this->regexes = array( + 'lex_var' => '/\s*'.preg_quote($this->options['whitespace_trim'].$this->options['tag_variable'][1], '/').'\s*|\s*'.preg_quote($this->options['tag_variable'][1], '/').'/A', + 'lex_block' => '/\s*(?:'.preg_quote($this->options['whitespace_trim'].$this->options['tag_block'][1], '/').'\s*|\s*'.preg_quote($this->options['tag_block'][1], '/').')\n?/A', + 'lex_raw_data' => '/('.preg_quote($this->options['tag_block'][0].$this->options['whitespace_trim'], '/').'|'.preg_quote($this->options['tag_block'][0], '/').')\s*endraw\s*(?:'.preg_quote($this->options['whitespace_trim'].$this->options['tag_block'][1], '/').'\s*|\s*'.preg_quote($this->options['tag_block'][1], '/').')/s', + 'operator' => $this->getOperatorRegex(), + 'lex_comment' => '/(?:'.preg_quote($this->options['whitespace_trim'], '/').preg_quote($this->options['tag_comment'][1], '/').'\s*|'.preg_quote($this->options['tag_comment'][1], '/').')\n?/s', + 'lex_block_raw' => '/\s*raw\s*(?:'.preg_quote($this->options['whitespace_trim'].$this->options['tag_block'][1], '/').'\s*|\s*'.preg_quote($this->options['tag_block'][1], '/').')/As', + 'lex_block_line' => '/\s*line\s+(\d+)\s*'.preg_quote($this->options['tag_block'][1], '/').'/As', + 'lex_tokens_start' => '/('.preg_quote($this->options['tag_variable'][0], '/').'|'.preg_quote($this->options['tag_block'][0], '/').'|'.preg_quote($this->options['tag_comment'][0], '/').')('.preg_quote($this->options['whitespace_trim'], '/').')?/s', + 'interpolation_start' => '/'.preg_quote($this->options['interpolation'][0], '/').'\s*/A', + 'interpolation_end' => '/\s*'.preg_quote($this->options['interpolation'][1], '/').'/A', + ); + } + + /** + * Tokenizes a source code. + * + * @param string $code The source code + * @param string $filename A unique identifier for the source code + * + * @return Twig_TokenStream A token stream instance + */ + public function tokenize($code, $filename = null) + { + if (function_exists('mb_internal_encoding') && ((int) ini_get('mbstring.func_overload')) & 2) { + $mbEncoding = mb_internal_encoding(); + mb_internal_encoding('ASCII'); + } + + $this->code = str_replace(array("\r\n", "\r"), "\n", $code); + $this->filename = $filename; + $this->cursor = 0; + $this->lineno = 1; + $this->end = strlen($this->code); + $this->tokens = array(); + $this->state = self::STATE_DATA; + $this->states = array(); + $this->brackets = array(); + $this->position = -1; + + // find all token starts in one go + preg_match_all($this->regexes['lex_tokens_start'], $this->code, $matches, PREG_OFFSET_CAPTURE); + $this->positions = $matches; + + while ($this->cursor < $this->end) { + // dispatch to the lexing functions depending + // on the current state + switch ($this->state) { + case self::STATE_DATA: + $this->lexData(); + break; + + case self::STATE_BLOCK: + $this->lexBlock(); + break; + + case self::STATE_VAR: + $this->lexVar(); + break; + + case self::STATE_STRING: + $this->lexString(); + break; + + case self::STATE_INTERPOLATION: + $this->lexInterpolation(); + break; + } + } + + $this->pushToken(Twig_Token::EOF_TYPE); + + if (!empty($this->brackets)) { + list($expect, $lineno) = array_pop($this->brackets); + throw new Twig_Error_Syntax(sprintf('Unclosed "%s"', $expect), $lineno, $this->filename); + } + + if (isset($mbEncoding)) { + mb_internal_encoding($mbEncoding); + } + + return new Twig_TokenStream($this->tokens, $this->filename); + } + + protected function lexData() + { + // if no matches are left we return the rest of the template as simple text token + if ($this->position == count($this->positions[0]) - 1) { + $this->pushToken(Twig_Token::TEXT_TYPE, substr($this->code, $this->cursor)); + $this->cursor = $this->end; + + return; + } + + // Find the first token after the current cursor + $position = $this->positions[0][++$this->position]; + while ($position[1] < $this->cursor) { + if ($this->position == count($this->positions[0]) - 1) { + return; + } + $position = $this->positions[0][++$this->position]; + } + + // push the template text first + $text = $textContent = substr($this->code, $this->cursor, $position[1] - $this->cursor); + if (isset($this->positions[2][$this->position][0])) { + $text = rtrim($text); + } + $this->pushToken(Twig_Token::TEXT_TYPE, $text); + $this->moveCursor($textContent.$position[0]); + + switch ($this->positions[1][$this->position][0]) { + case $this->options['tag_comment'][0]: + $this->lexComment(); + break; + + case $this->options['tag_block'][0]: + // raw data? + if (preg_match($this->regexes['lex_block_raw'], $this->code, $match, null, $this->cursor)) { + $this->moveCursor($match[0]); + $this->lexRawData(); + // {% line \d+ %} + } elseif (preg_match($this->regexes['lex_block_line'], $this->code, $match, null, $this->cursor)) { + $this->moveCursor($match[0]); + $this->lineno = (int) $match[1]; + } else { + $this->pushToken(Twig_Token::BLOCK_START_TYPE); + $this->pushState(self::STATE_BLOCK); + } + break; + + case $this->options['tag_variable'][0]: + $this->pushToken(Twig_Token::VAR_START_TYPE); + $this->pushState(self::STATE_VAR); + break; + } + } + + protected function lexBlock() + { + if (empty($this->brackets) && preg_match($this->regexes['lex_block'], $this->code, $match, null, $this->cursor)) { + $this->pushToken(Twig_Token::BLOCK_END_TYPE); + $this->moveCursor($match[0]); + $this->popState(); + } else { + $this->lexExpression(); + } + } + + protected function lexVar() + { + if (empty($this->brackets) && preg_match($this->regexes['lex_var'], $this->code, $match, null, $this->cursor)) { + $this->pushToken(Twig_Token::VAR_END_TYPE); + $this->moveCursor($match[0]); + $this->popState(); + } else { + $this->lexExpression(); + } + } + + protected function lexExpression() + { + // whitespace + if (preg_match('/\s+/A', $this->code, $match, null, $this->cursor)) { + $this->moveCursor($match[0]); + + if ($this->cursor >= $this->end) { + throw new Twig_Error_Syntax(sprintf('Unexpected end of file: Unclosed "%s"', $this->state === self::STATE_BLOCK ? 'block' : 'variable'), $this->lineno, $this->filename); + } + } + + // operators + if (preg_match($this->regexes['operator'], $this->code, $match, null, $this->cursor)) { + $this->pushToken(Twig_Token::OPERATOR_TYPE, $match[0]); + $this->moveCursor($match[0]); + } + // names + elseif (preg_match(self::REGEX_NAME, $this->code, $match, null, $this->cursor)) { + $this->pushToken(Twig_Token::NAME_TYPE, $match[0]); + $this->moveCursor($match[0]); + } + // numbers + elseif (preg_match(self::REGEX_NUMBER, $this->code, $match, null, $this->cursor)) { + $number = (float) $match[0]; // floats + if (ctype_digit($match[0]) && $number <= PHP_INT_MAX) { + $number = (int) $match[0]; // integers lower than the maximum + } + $this->pushToken(Twig_Token::NUMBER_TYPE, $number); + $this->moveCursor($match[0]); + } + // punctuation + elseif (false !== strpos(self::PUNCTUATION, $this->code[$this->cursor])) { + // opening bracket + if (false !== strpos('([{', $this->code[$this->cursor])) { + $this->brackets[] = array($this->code[$this->cursor], $this->lineno); + } + // closing bracket + elseif (false !== strpos(')]}', $this->code[$this->cursor])) { + if (empty($this->brackets)) { + throw new Twig_Error_Syntax(sprintf('Unexpected "%s"', $this->code[$this->cursor]), $this->lineno, $this->filename); + } + + list($expect, $lineno) = array_pop($this->brackets); + if ($this->code[$this->cursor] != strtr($expect, '([{', ')]}')) { + throw new Twig_Error_Syntax(sprintf('Unclosed "%s"', $expect), $lineno, $this->filename); + } + } + + $this->pushToken(Twig_Token::PUNCTUATION_TYPE, $this->code[$this->cursor]); + ++$this->cursor; + } + // strings + elseif (preg_match(self::REGEX_STRING, $this->code, $match, null, $this->cursor)) { + $this->pushToken(Twig_Token::STRING_TYPE, stripcslashes(substr($match[0], 1, -1))); + $this->moveCursor($match[0]); + } + // opening double quoted string + elseif (preg_match(self::REGEX_DQ_STRING_DELIM, $this->code, $match, null, $this->cursor)) { + $this->brackets[] = array('"', $this->lineno); + $this->pushState(self::STATE_STRING); + $this->moveCursor($match[0]); + } + // unlexable + else { + throw new Twig_Error_Syntax(sprintf('Unexpected character "%s"', $this->code[$this->cursor]), $this->lineno, $this->filename); + } + } + + protected function lexRawData() + { + if (!preg_match($this->regexes['lex_raw_data'], $this->code, $match, PREG_OFFSET_CAPTURE, $this->cursor)) { + throw new Twig_Error_Syntax(sprintf('Unexpected end of file: Unclosed "block"'), $this->lineno, $this->filename); + } + + $text = substr($this->code, $this->cursor, $match[0][1] - $this->cursor); + $this->moveCursor($text.$match[0][0]); + + if (false !== strpos($match[1][0], $this->options['whitespace_trim'])) { + $text = rtrim($text); + } + + $this->pushToken(Twig_Token::TEXT_TYPE, $text); + } + + protected function lexComment() + { + if (!preg_match($this->regexes['lex_comment'], $this->code, $match, PREG_OFFSET_CAPTURE, $this->cursor)) { + throw new Twig_Error_Syntax('Unclosed comment', $this->lineno, $this->filename); + } + + $this->moveCursor(substr($this->code, $this->cursor, $match[0][1] - $this->cursor).$match[0][0]); + } + + protected function lexString() + { + if (preg_match($this->regexes['interpolation_start'], $this->code, $match, null, $this->cursor)) { + $this->brackets[] = array($this->options['interpolation'][0], $this->lineno); + $this->pushToken(Twig_Token::INTERPOLATION_START_TYPE); + $this->moveCursor($match[0]); + $this->pushState(self::STATE_INTERPOLATION); + + } elseif (preg_match(self::REGEX_DQ_STRING_PART, $this->code, $match, null, $this->cursor) && strlen($match[0]) > 0) { + $this->pushToken(Twig_Token::STRING_TYPE, stripcslashes($match[0])); + $this->moveCursor($match[0]); + + } elseif (preg_match(self::REGEX_DQ_STRING_DELIM, $this->code, $match, null, $this->cursor)) { + + list($expect, $lineno) = array_pop($this->brackets); + if ($this->code[$this->cursor] != '"') { + throw new Twig_Error_Syntax(sprintf('Unclosed "%s"', $expect), $lineno, $this->filename); + } + + $this->popState(); + ++$this->cursor; + + return; + } + } + + protected function lexInterpolation() + { + $bracket = end($this->brackets); + if ($this->options['interpolation'][0] === $bracket[0] && preg_match($this->regexes['interpolation_end'], $this->code, $match, null, $this->cursor)) { + array_pop($this->brackets); + $this->pushToken(Twig_Token::INTERPOLATION_END_TYPE); + $this->moveCursor($match[0]); + $this->popState(); + } else { + $this->lexExpression(); + } + } + + protected function pushToken($type, $value = '') + { + // do not push empty text tokens + if (Twig_Token::TEXT_TYPE === $type && '' === $value) { + return; + } + + $this->tokens[] = new Twig_Token($type, $value, $this->lineno); + } + + protected function moveCursor($text) + { + $this->cursor += strlen($text); + $this->lineno += substr_count($text, "\n"); + } + + protected function getOperatorRegex() + { + $operators = array_merge( + array('='), + array_keys($this->env->getUnaryOperators()), + array_keys($this->env->getBinaryOperators()) + ); + + $operators = array_combine($operators, array_map('strlen', $operators)); + arsort($operators); + + $regex = array(); + foreach ($operators as $operator => $length) { + // an operator that ends with a character must be followed by + // a whitespace or a parenthesis + if (ctype_alpha($operator[$length - 1])) { + $regex[] = preg_quote($operator, '/').'(?=[\s()])'; + } else { + $regex[] = preg_quote($operator, '/'); + } + } + + return '/'.implode('|', $regex).'/A'; + } + + protected function pushState($state) + { + $this->states[] = $this->state; + $this->state = $state; + } + + protected function popState() + { + if (0 === count($this->states)) { + throw new Exception('Cannot pop state without a previous state'); + } + + $this->state = array_pop($this->states); + } +} diff --git a/vendor/Twig/LexerInterface.php b/vendor/Twig/LexerInterface.php new file mode 100644 index 0000000..0223384 --- /dev/null +++ b/vendor/Twig/LexerInterface.php @@ -0,0 +1,29 @@ + + */ +interface Twig_LexerInterface +{ + /** + * Tokenizes a source code. + * + * @param string $code The source code + * @param string $filename A unique identifier for the source code + * + * @return Twig_TokenStream A token stream instance + */ + function tokenize($code, $filename = null); +} diff --git a/vendor/Twig/Loader/Array.php b/vendor/Twig/Loader/Array.php new file mode 100644 index 0000000..32bb7e4 --- /dev/null +++ b/vendor/Twig/Loader/Array.php @@ -0,0 +1,102 @@ + + */ +class Twig_Loader_Array implements Twig_LoaderInterface +{ + protected $templates; + + /** + * Constructor. + * + * @param array $templates An array of templates (keys are the names, and values are the source code) + * + * @see Twig_Loader + */ + public function __construct(array $templates) + { + $this->templates = array(); + foreach ($templates as $name => $template) { + $this->templates[$name] = $template; + } + } + + /** + * Adds or overrides a template. + * + * @param string $name The template name + * @param string $template The template source + */ + public function setTemplate($name, $template) + { + $this->templates[(string) $name] = $template; + } + + /** + * Gets the source code of a template, given its name. + * + * @param string $name The name of the template to load + * + * @return string The template source code + */ + public function getSource($name) + { + $name = (string) $name; + if (!isset($this->templates[$name])) { + throw new Twig_Error_Loader(sprintf('Template "%s" is not defined.', $name)); + } + + return $this->templates[$name]; + } + + /** + * Gets the cache key to use for the cache for a given template name. + * + * @param string $name The name of the template to load + * + * @return string The cache key + */ + public function getCacheKey($name) + { + $name = (string) $name; + if (!isset($this->templates[$name])) { + throw new Twig_Error_Loader(sprintf('Template "%s" is not defined.', $name)); + } + + return $this->templates[$name]; + } + + /** + * Returns true if the template is still fresh. + * + * @param string $name The template name + * @param timestamp $time The last modification time of the cached template + */ + public function isFresh($name, $time) + { + $name = (string) $name; + if (!isset($this->templates[$name])) { + throw new Twig_Error_Loader(sprintf('Template "%s" is not defined.', $name)); + } + + return true; + } +} diff --git a/vendor/Twig/Loader/Chain.php b/vendor/Twig/Loader/Chain.php new file mode 100644 index 0000000..48dd8b8 --- /dev/null +++ b/vendor/Twig/Loader/Chain.php @@ -0,0 +1,100 @@ + + */ +class Twig_Loader_Chain implements Twig_LoaderInterface +{ + protected $loaders; + + /** + * Constructor. + * + * @param Twig_LoaderInterface[] $loaders An array of loader instances + */ + public function __construct(array $loaders = array()) + { + $this->loaders = array(); + foreach ($loaders as $loader) { + $this->addLoader($loader); + } + } + + /** + * Adds a loader instance. + * + * @param Twig_LoaderInterface $loader A Loader instance + */ + public function addLoader(Twig_LoaderInterface $loader) + { + $this->loaders[] = $loader; + } + + /** + * Gets the source code of a template, given its name. + * + * @param string $name The name of the template to load + * + * @return string The template source code + */ + public function getSource($name) + { + foreach ($this->loaders as $loader) { + try { + return $loader->getSource($name); + } catch (Twig_Error_Loader $e) { + } + } + + throw new Twig_Error_Loader(sprintf('Template "%s" is not defined.', $name)); + } + + /** + * Gets the cache key to use for the cache for a given template name. + * + * @param string $name The name of the template to load + * + * @return string The cache key + */ + public function getCacheKey($name) + { + foreach ($this->loaders as $loader) { + try { + return $loader->getCacheKey($name); + } catch (Twig_Error_Loader $e) { + } + } + + throw new Twig_Error_Loader(sprintf('Template "%s" is not defined.', $name)); + } + + /** + * Returns true if the template is still fresh. + * + * @param string $name The template name + * @param timestamp $time The last modification time of the cached template + */ + public function isFresh($name, $time) + { + foreach ($this->loaders as $loader) { + try { + return $loader->isFresh($name, $time); + } catch (Twig_Error_Loader $e) { + } + } + + throw new Twig_Error_Loader(sprintf('Template "%s" is not defined.', $name)); + } +} diff --git a/vendor/Twig/Loader/Filesystem.php b/vendor/Twig/Loader/Filesystem.php new file mode 100644 index 0000000..5cd40f9 --- /dev/null +++ b/vendor/Twig/Loader/Filesystem.php @@ -0,0 +1,152 @@ + + */ +class Twig_Loader_Filesystem implements Twig_LoaderInterface +{ + protected $paths; + protected $cache; + + /** + * Constructor. + * + * @param string|array $paths A path or an array of paths where to look for templates + */ + public function __construct($paths) + { + $this->setPaths($paths); + } + + /** + * Returns the paths to the templates. + * + * @return array The array of paths where to look for templates + */ + public function getPaths() + { + return $this->paths; + } + + /** + * Sets the paths where templates are stored. + * + * @param string|array $paths A path or an array of paths where to look for templates + */ + public function setPaths($paths) + { + if (!is_array($paths)) { + $paths = array($paths); + } + + $this->paths = array(); + foreach ($paths as $path) { + $this->addPath($path); + } + } + + /** + * Adds a path where templates are stored. + * + * @param string $path A path where to look for templates + */ + public function addPath($path) + { + // invalidate the cache + $this->cache = array(); + + if (!is_dir($path)) { + throw new Twig_Error_Loader(sprintf('The "%s" directory does not exist.', $path)); + } + + $this->paths[] = $path; + } + + /** + * Gets the source code of a template, given its name. + * + * @param string $name The name of the template to load + * + * @return string The template source code + */ + public function getSource($name) + { + return file_get_contents($this->findTemplate($name)); + } + + /** + * Gets the cache key to use for the cache for a given template name. + * + * @param string $name The name of the template to load + * + * @return string The cache key + */ + public function getCacheKey($name) + { + return $this->findTemplate($name); + } + + /** + * Returns true if the template is still fresh. + * + * @param string $name The template name + * @param timestamp $time The last modification time of the cached template + */ + public function isFresh($name, $time) + { + return filemtime($this->findTemplate($name)) <= $time; + } + + protected function findTemplate($name) + { + // normalize name + $name = preg_replace('#/{2,}#', '/', strtr($name, '\\', '/')); + + if (isset($this->cache[$name])) { + return $this->cache[$name]; + } + + $this->validateName($name); + + foreach ($this->paths as $path) { + if (is_file($path.'/'.$name)) { + return $this->cache[$name] = $path.'/'.$name; + } + } + + throw new Twig_Error_Loader(sprintf('Unable to find template "%s" (looked into: %s).', $name, implode(', ', $this->paths))); + } + + protected function validateName($name) + { + if (false !== strpos($name, "\0")) { + throw new Twig_Error_Loader('A template name cannot contain NUL bytes.'); + } + + $parts = explode('/', $name); + $level = 0; + foreach ($parts as $part) { + if ('..' === $part) { + --$level; + } elseif ('.' !== $part) { + ++$level; + } + + if ($level < 0) { + throw new Twig_Error_Loader(sprintf('Looks like you try to load a template outside configured directories (%s).', $name)); + } + } + } +} diff --git a/vendor/Twig/Loader/String.php b/vendor/Twig/Loader/String.php new file mode 100644 index 0000000..26eb009 --- /dev/null +++ b/vendor/Twig/Loader/String.php @@ -0,0 +1,59 @@ + + */ +class Twig_Loader_String implements Twig_LoaderInterface +{ + /** + * Gets the source code of a template, given its name. + * + * @param string $name The name of the template to load + * + * @return string The template source code + */ + public function getSource($name) + { + return $name; + } + + /** + * Gets the cache key to use for the cache for a given template name. + * + * @param string $name The name of the template to load + * + * @return string The cache key + */ + public function getCacheKey($name) + { + return $name; + } + + /** + * Returns true if the template is still fresh. + * + * @param string $name The template name + * @param timestamp $time The last modification time of the cached template + */ + public function isFresh($name, $time) + { + return true; + } +} diff --git a/vendor/Twig/LoaderInterface.php b/vendor/Twig/LoaderInterface.php new file mode 100644 index 0000000..d8ae444 --- /dev/null +++ b/vendor/Twig/LoaderInterface.php @@ -0,0 +1,53 @@ + + */ +interface Twig_LoaderInterface +{ + /** + * Gets the source code of a template, given its name. + * + * @param string $name The name of the template to load + * + * @return string The template source code + * + * @throws Twig_Error_Loader When $name is not found + */ + function getSource($name); + + /** + * Gets the cache key to use for the cache for a given template name. + * + * @param string $name The name of the template to load + * + * @return string The cache key + * + * @throws Twig_Error_Loader When $name is not found + */ + function getCacheKey($name); + + /** + * Returns true if the template is still fresh. + * + * @param string $name The template name + * @param timestamp $time The last modification time of the cached template + * + * @return Boolean true if the template is fresh, false otherwise + * + * @throws Twig_Error_Loader When $name is not found + */ + function isFresh($name, $time); +} diff --git a/vendor/Twig/Markup.php b/vendor/Twig/Markup.php new file mode 100644 index 0000000..7099b29 --- /dev/null +++ b/vendor/Twig/Markup.php @@ -0,0 +1,38 @@ + + */ +class Twig_Markup implements Countable +{ + protected $content; + protected $charset; + + public function __construct($content, $charset) + { + $this->content = (string) $content; + $this->charset = $charset; + } + + public function __toString() + { + return $this->content; + } + + public function count() + { + return function_exists('mb_get_info') ? mb_strlen($this->content, $this->charset) : strlen($this->content); + } +} diff --git a/vendor/Twig/Node.php b/vendor/Twig/Node.php new file mode 100644 index 0000000..651ffc4 --- /dev/null +++ b/vendor/Twig/Node.php @@ -0,0 +1,227 @@ + + */ +class Twig_Node implements Twig_NodeInterface +{ + protected $nodes; + protected $attributes; + protected $lineno; + protected $tag; + + /** + * Constructor. + * + * The nodes are automatically made available as properties ($this->node). + * The attributes are automatically made available as array items ($this['name']). + * + * @param array $nodes An array of named nodes + * @param array $attributes An array of attributes (should not be nodes) + * @param integer $lineno The line number + * @param string $tag The tag name associated with the Node + */ + public function __construct(array $nodes = array(), array $attributes = array(), $lineno = 0, $tag = null) + { + $this->nodes = $nodes; + $this->attributes = $attributes; + $this->lineno = $lineno; + $this->tag = $tag; + } + + public function __toString() + { + $attributes = array(); + foreach ($this->attributes as $name => $value) { + $attributes[] = sprintf('%s: %s', $name, str_replace("\n", '', var_export($value, true))); + } + + $repr = array(get_class($this).'('.implode(', ', $attributes)); + + if (count($this->nodes)) { + foreach ($this->nodes as $name => $node) { + $len = strlen($name) + 4; + $noderepr = array(); + foreach (explode("\n", (string) $node) as $line) { + $noderepr[] = str_repeat(' ', $len).$line; + } + + $repr[] = sprintf(' %s: %s', $name, ltrim(implode("\n", $noderepr))); + } + + $repr[] = ')'; + } else { + $repr[0] .= ')'; + } + + return implode("\n", $repr); + } + + public function toXml($asDom = false) + { + $dom = new DOMDocument('1.0', 'UTF-8'); + $dom->formatOutput = true; + $dom->appendChild($xml = $dom->createElement('twig')); + + $xml->appendChild($node = $dom->createElement('node')); + $node->setAttribute('class', get_class($this)); + + foreach ($this->attributes as $name => $value) { + $node->appendChild($attribute = $dom->createElement('attribute')); + $attribute->setAttribute('name', $name); + $attribute->appendChild($dom->createTextNode($value)); + } + + foreach ($this->nodes as $name => $n) { + if (null === $n) { + continue; + } + + $child = $n->toXml(true)->getElementsByTagName('node')->item(0); + $child = $dom->importNode($child, true); + $child->setAttribute('name', $name); + + $node->appendChild($child); + } + + return $asDom ? $dom : $dom->saveXml(); + } + + public function compile(Twig_Compiler $compiler) + { + foreach ($this->nodes as $node) { + $node->compile($compiler); + } + } + + public function getLine() + { + return $this->lineno; + } + + public function getNodeTag() + { + return $this->tag; + } + + /** + * Returns true if the attribute is defined. + * + * @param string The attribute name + * + * @return Boolean true if the attribute is defined, false otherwise + */ + public function hasAttribute($name) + { + return array_key_exists($name, $this->attributes); + } + + /** + * Gets an attribute. + * + * @param string The attribute name + * + * @return mixed The attribute value + */ + public function getAttribute($name) + { + if (!array_key_exists($name, $this->attributes)) { + throw new Twig_Error_Runtime(sprintf('Attribute "%s" does not exist for Node "%s".', $name, get_class($this))); + } + + return $this->attributes[$name]; + } + + /** + * Sets an attribute. + * + * @param string The attribute name + * @param mixed The attribute value + */ + public function setAttribute($name, $value) + { + $this->attributes[$name] = $value; + } + + /** + * Removes an attribute. + * + * @param string The attribute name + */ + public function removeAttribute($name) + { + unset($this->attributes[$name]); + } + + /** + * Returns true if the node with the given identifier exists. + * + * @param string The node name + * + * @return Boolean true if the node with the given name exists, false otherwise + */ + public function hasNode($name) + { + return array_key_exists($name, $this->nodes); + } + + /** + * Gets a node by name. + * + * @param string The node name + * + * @return Twig_Node A Twig_Node instance + */ + public function getNode($name) + { + if (!array_key_exists($name, $this->nodes)) { + throw new Twig_Error_Runtime(sprintf('Node "%s" does not exist for Node "%s".', $name, get_class($this))); + } + + return $this->nodes[$name]; + } + + /** + * Sets a node. + * + * @param string The node name + * @param Twig_Node A Twig_Node instance + */ + public function setNode($name, $node = null) + { + $this->nodes[$name] = $node; + } + + /** + * Removes a node by name. + * + * @param string The node name + */ + public function removeNode($name) + { + unset($this->nodes[$name]); + } + + public function count() + { + return count($this->nodes); + } + + public function getIterator() + { + return new ArrayIterator($this->nodes); + } +} diff --git a/vendor/Twig/Node/AutoEscape.php b/vendor/Twig/Node/AutoEscape.php new file mode 100644 index 0000000..a0c2ee6 --- /dev/null +++ b/vendor/Twig/Node/AutoEscape.php @@ -0,0 +1,40 @@ + + */ +class Twig_Node_AutoEscape extends Twig_Node +{ + public function __construct($value, Twig_NodeInterface $body, $lineno, $tag = 'autoescape') + { + parent::__construct(array('body' => $body), array('value' => $value), $lineno, $tag); + } + + /** + * Compiles the node to PHP. + * + * @param Twig_Compiler A Twig_Compiler instance + */ + public function compile(Twig_Compiler $compiler) + { + $compiler->subcompile($this->getNode('body')); + } +} diff --git a/vendor/Twig/Node/Block.php b/vendor/Twig/Node/Block.php new file mode 100644 index 0000000..5548ad0 --- /dev/null +++ b/vendor/Twig/Node/Block.php @@ -0,0 +1,45 @@ + + */ +class Twig_Node_Block extends Twig_Node +{ + public function __construct($name, Twig_NodeInterface $body, $lineno, $tag = null) + { + parent::__construct(array('body' => $body), array('name' => $name), $lineno, $tag); + } + + /** + * Compiles the node to PHP. + * + * @param Twig_Compiler A Twig_Compiler instance + */ + public function compile(Twig_Compiler $compiler) + { + $compiler + ->addDebugInfo($this) + ->write(sprintf("public function block_%s(\$context, array \$blocks = array())\n", $this->getAttribute('name')), "{\n") + ->indent() + ; + + $compiler + ->subcompile($this->getNode('body')) + ->outdent() + ->write("}\n\n") + ; + } +} diff --git a/vendor/Twig/Node/BlockReference.php b/vendor/Twig/Node/BlockReference.php new file mode 100644 index 0000000..53f6287 --- /dev/null +++ b/vendor/Twig/Node/BlockReference.php @@ -0,0 +1,38 @@ + + */ +class Twig_Node_BlockReference extends Twig_Node implements Twig_NodeOutputInterface +{ + public function __construct($name, $lineno, $tag = null) + { + parent::__construct(array(), array('name' => $name), $lineno, $tag); + } + + /** + * Compiles the node to PHP. + * + * @param Twig_Compiler A Twig_Compiler instance + */ + public function compile(Twig_Compiler $compiler) + { + $compiler + ->addDebugInfo($this) + ->write(sprintf("\$this->displayBlock('%s', \$context, \$blocks);\n", $this->getAttribute('name'))) + ; + } +} diff --git a/vendor/Twig/Node/Body.php b/vendor/Twig/Node/Body.php new file mode 100644 index 0000000..f72bf50 --- /dev/null +++ b/vendor/Twig/Node/Body.php @@ -0,0 +1,20 @@ + + */ +class Twig_Node_Body extends Twig_Node +{ +} diff --git a/vendor/Twig/Node/Do.php b/vendor/Twig/Node/Do.php new file mode 100644 index 0000000..aa419d9 --- /dev/null +++ b/vendor/Twig/Node/Do.php @@ -0,0 +1,39 @@ + + */ +class Twig_Node_Do extends Twig_Node +{ + public function __construct(Twig_Node_Expression $expr, $lineno, $tag = null) + { + parent::__construct(array('expr' => $expr), array(), $lineno, $tag); + } + + /** + * Compiles the node to PHP. + * + * @param Twig_Compiler A Twig_Compiler instance + */ + public function compile(Twig_Compiler $compiler) + { + $compiler + ->addDebugInfo($this) + ->write('') + ->subcompile($this->getNode('expr')) + ->raw(";\n") + ; + } +} diff --git a/vendor/Twig/Node/Embed.php b/vendor/Twig/Node/Embed.php new file mode 100644 index 0000000..5edb953 --- /dev/null +++ b/vendor/Twig/Node/Embed.php @@ -0,0 +1,39 @@ + + */ +class Twig_Node_Embed extends Twig_Node_Include +{ + // we don't inject the module to avoid node visitors to traverse it twice (as it will be already visited in the main module) + public function __construct($filename, $index, Twig_Node_Expression $variables = null, $only = false, $ignoreMissing = false, $lineno, $tag = null) + { + parent::__construct(new Twig_Node_Expression_Constant('not_used', $lineno), $variables, $only, $ignoreMissing, $lineno, $tag); + + $this->setAttribute('filename', $filename); + $this->setAttribute('index', $index); + } + + protected function addGetTemplate(Twig_Compiler $compiler) + { + $compiler + ->write("\$this->env->loadTemplate(") + ->string($this->getAttribute('filename')) + ->raw(', ') + ->string($this->getAttribute('index')) + ->raw(")") + ; + } +} diff --git a/vendor/Twig/Node/Expression.php b/vendor/Twig/Node/Expression.php new file mode 100644 index 0000000..13b170e --- /dev/null +++ b/vendor/Twig/Node/Expression.php @@ -0,0 +1,21 @@ + + */ +abstract class Twig_Node_Expression extends Twig_Node +{ +} diff --git a/vendor/Twig/Node/Expression/Array.php b/vendor/Twig/Node/Expression/Array.php new file mode 100644 index 0000000..1da785f --- /dev/null +++ b/vendor/Twig/Node/Expression/Array.php @@ -0,0 +1,86 @@ +index = -1; + foreach ($this->getKeyValuePairs() as $pair) { + if ($pair['key'] instanceof Twig_Node_Expression_Constant && ctype_digit((string) $pair['key']->getAttribute('value')) && $pair['key']->getAttribute('value') > $this->index) { + $this->index = $pair['key']->getAttribute('value'); + } + } + } + + public function getKeyValuePairs() + { + $pairs = array(); + + foreach (array_chunk($this->nodes, 2) as $pair) { + $pairs[] = array( + 'key' => $pair[0], + 'value' => $pair[1], + ); + } + + return $pairs; + } + + public function hasElement(Twig_Node_Expression $key) + { + foreach ($this->getKeyValuePairs() as $pair) { + // we compare the string representation of the keys + // to avoid comparing the line numbers which are not relevant here. + if ((string) $key == (string) $pair['key']) { + return true; + } + } + + return false; + } + + public function addElement(Twig_Node_Expression $value, Twig_Node_Expression $key = null) + { + if (null === $key) { + $key = new Twig_Node_Expression_Constant(++$this->index, $value->getLine()); + } + + array_push($this->nodes, $key, $value); + } + + /** + * Compiles the node to PHP. + * + * @param Twig_Compiler A Twig_Compiler instance + */ + public function compile(Twig_Compiler $compiler) + { + $compiler->raw('array('); + $first = true; + foreach ($this->getKeyValuePairs() as $pair) { + if (!$first) { + $compiler->raw(', '); + } + $first = false; + + $compiler + ->subcompile($pair['key']) + ->raw(' => ') + ->subcompile($pair['value']) + ; + } + $compiler->raw(')'); + } +} diff --git a/vendor/Twig/Node/Expression/AssignName.php b/vendor/Twig/Node/Expression/AssignName.php new file mode 100644 index 0000000..2ddea78 --- /dev/null +++ b/vendor/Twig/Node/Expression/AssignName.php @@ -0,0 +1,28 @@ +raw('$context[') + ->string($this->getAttribute('name')) + ->raw(']') + ; + } +} diff --git a/vendor/Twig/Node/Expression/Binary.php b/vendor/Twig/Node/Expression/Binary.php new file mode 100644 index 0000000..9dd5de2 --- /dev/null +++ b/vendor/Twig/Node/Expression/Binary.php @@ -0,0 +1,40 @@ + $left, 'right' => $right), array(), $lineno); + } + + /** + * Compiles the node to PHP. + * + * @param Twig_Compiler A Twig_Compiler instance + */ + public function compile(Twig_Compiler $compiler) + { + $compiler + ->raw('(') + ->subcompile($this->getNode('left')) + ->raw(' ') + ; + $this->operator($compiler); + $compiler + ->raw(' ') + ->subcompile($this->getNode('right')) + ->raw(')') + ; + } + + abstract public function operator(Twig_Compiler $compiler); +} diff --git a/vendor/Twig/Node/Expression/Binary/Add.php b/vendor/Twig/Node/Expression/Binary/Add.php new file mode 100644 index 0000000..0ef8e11 --- /dev/null +++ b/vendor/Twig/Node/Expression/Binary/Add.php @@ -0,0 +1,18 @@ +raw('+'); + } +} diff --git a/vendor/Twig/Node/Expression/Binary/And.php b/vendor/Twig/Node/Expression/Binary/And.php new file mode 100644 index 0000000..d5752eb --- /dev/null +++ b/vendor/Twig/Node/Expression/Binary/And.php @@ -0,0 +1,18 @@ +raw('&&'); + } +} diff --git a/vendor/Twig/Node/Expression/Binary/BitwiseAnd.php b/vendor/Twig/Node/Expression/Binary/BitwiseAnd.php new file mode 100644 index 0000000..9a46d84 --- /dev/null +++ b/vendor/Twig/Node/Expression/Binary/BitwiseAnd.php @@ -0,0 +1,18 @@ +raw('&'); + } +} diff --git a/vendor/Twig/Node/Expression/Binary/BitwiseOr.php b/vendor/Twig/Node/Expression/Binary/BitwiseOr.php new file mode 100644 index 0000000..058a20b --- /dev/null +++ b/vendor/Twig/Node/Expression/Binary/BitwiseOr.php @@ -0,0 +1,18 @@ +raw('|'); + } +} diff --git a/vendor/Twig/Node/Expression/Binary/BitwiseXor.php b/vendor/Twig/Node/Expression/Binary/BitwiseXor.php new file mode 100644 index 0000000..f4da73d --- /dev/null +++ b/vendor/Twig/Node/Expression/Binary/BitwiseXor.php @@ -0,0 +1,18 @@ +raw('^'); + } +} diff --git a/vendor/Twig/Node/Expression/Binary/Concat.php b/vendor/Twig/Node/Expression/Binary/Concat.php new file mode 100644 index 0000000..f9a6462 --- /dev/null +++ b/vendor/Twig/Node/Expression/Binary/Concat.php @@ -0,0 +1,18 @@ +raw('.'); + } +} diff --git a/vendor/Twig/Node/Expression/Binary/Div.php b/vendor/Twig/Node/Expression/Binary/Div.php new file mode 100644 index 0000000..e0797a6 --- /dev/null +++ b/vendor/Twig/Node/Expression/Binary/Div.php @@ -0,0 +1,18 @@ +raw('/'); + } +} diff --git a/vendor/Twig/Node/Expression/Binary/Equal.php b/vendor/Twig/Node/Expression/Binary/Equal.php new file mode 100644 index 0000000..7b1236d --- /dev/null +++ b/vendor/Twig/Node/Expression/Binary/Equal.php @@ -0,0 +1,17 @@ +raw('=='); + } +} diff --git a/vendor/Twig/Node/Expression/Binary/FloorDiv.php b/vendor/Twig/Node/Expression/Binary/FloorDiv.php new file mode 100644 index 0000000..7fbd055 --- /dev/null +++ b/vendor/Twig/Node/Expression/Binary/FloorDiv.php @@ -0,0 +1,29 @@ +raw('intval(floor('); + parent::compile($compiler); + $compiler->raw('))'); + } + + public function operator(Twig_Compiler $compiler) + { + return $compiler->raw('/'); + } +} diff --git a/vendor/Twig/Node/Expression/Binary/Greater.php b/vendor/Twig/Node/Expression/Binary/Greater.php new file mode 100644 index 0000000..a110bd9 --- /dev/null +++ b/vendor/Twig/Node/Expression/Binary/Greater.php @@ -0,0 +1,17 @@ +raw('>'); + } +} diff --git a/vendor/Twig/Node/Expression/Binary/GreaterEqual.php b/vendor/Twig/Node/Expression/Binary/GreaterEqual.php new file mode 100644 index 0000000..3754fed --- /dev/null +++ b/vendor/Twig/Node/Expression/Binary/GreaterEqual.php @@ -0,0 +1,17 @@ +raw('>='); + } +} diff --git a/vendor/Twig/Node/Expression/Binary/In.php b/vendor/Twig/Node/Expression/Binary/In.php new file mode 100644 index 0000000..788f937 --- /dev/null +++ b/vendor/Twig/Node/Expression/Binary/In.php @@ -0,0 +1,33 @@ +raw('twig_in_filter(') + ->subcompile($this->getNode('left')) + ->raw(', ') + ->subcompile($this->getNode('right')) + ->raw(')') + ; + } + + public function operator(Twig_Compiler $compiler) + { + return $compiler->raw('in'); + } +} diff --git a/vendor/Twig/Node/Expression/Binary/Less.php b/vendor/Twig/Node/Expression/Binary/Less.php new file mode 100644 index 0000000..45fd300 --- /dev/null +++ b/vendor/Twig/Node/Expression/Binary/Less.php @@ -0,0 +1,17 @@ +raw('<'); + } +} diff --git a/vendor/Twig/Node/Expression/Binary/LessEqual.php b/vendor/Twig/Node/Expression/Binary/LessEqual.php new file mode 100644 index 0000000..e38e257 --- /dev/null +++ b/vendor/Twig/Node/Expression/Binary/LessEqual.php @@ -0,0 +1,17 @@ +raw('<='); + } +} diff --git a/vendor/Twig/Node/Expression/Binary/Mod.php b/vendor/Twig/Node/Expression/Binary/Mod.php new file mode 100644 index 0000000..9924114 --- /dev/null +++ b/vendor/Twig/Node/Expression/Binary/Mod.php @@ -0,0 +1,18 @@ +raw('%'); + } +} diff --git a/vendor/Twig/Node/Expression/Binary/Mul.php b/vendor/Twig/Node/Expression/Binary/Mul.php new file mode 100644 index 0000000..c91529c --- /dev/null +++ b/vendor/Twig/Node/Expression/Binary/Mul.php @@ -0,0 +1,18 @@ +raw('*'); + } +} diff --git a/vendor/Twig/Node/Expression/Binary/NotEqual.php b/vendor/Twig/Node/Expression/Binary/NotEqual.php new file mode 100644 index 0000000..26867ba --- /dev/null +++ b/vendor/Twig/Node/Expression/Binary/NotEqual.php @@ -0,0 +1,17 @@ +raw('!='); + } +} diff --git a/vendor/Twig/Node/Expression/Binary/NotIn.php b/vendor/Twig/Node/Expression/Binary/NotIn.php new file mode 100644 index 0000000..f347b7b --- /dev/null +++ b/vendor/Twig/Node/Expression/Binary/NotIn.php @@ -0,0 +1,33 @@ +raw('!twig_in_filter(') + ->subcompile($this->getNode('left')) + ->raw(', ') + ->subcompile($this->getNode('right')) + ->raw(')') + ; + } + + public function operator(Twig_Compiler $compiler) + { + return $compiler->raw('not in'); + } +} diff --git a/vendor/Twig/Node/Expression/Binary/Or.php b/vendor/Twig/Node/Expression/Binary/Or.php new file mode 100644 index 0000000..adba49c --- /dev/null +++ b/vendor/Twig/Node/Expression/Binary/Or.php @@ -0,0 +1,18 @@ +raw('||'); + } +} diff --git a/vendor/Twig/Node/Expression/Binary/Power.php b/vendor/Twig/Node/Expression/Binary/Power.php new file mode 100644 index 0000000..b2c5904 --- /dev/null +++ b/vendor/Twig/Node/Expression/Binary/Power.php @@ -0,0 +1,33 @@ +raw('pow(') + ->subcompile($this->getNode('left')) + ->raw(', ') + ->subcompile($this->getNode('right')) + ->raw(')') + ; + } + + public function operator(Twig_Compiler $compiler) + { + return $compiler->raw('**'); + } +} diff --git a/vendor/Twig/Node/Expression/Binary/Range.php b/vendor/Twig/Node/Expression/Binary/Range.php new file mode 100644 index 0000000..bea4f2a --- /dev/null +++ b/vendor/Twig/Node/Expression/Binary/Range.php @@ -0,0 +1,33 @@ +raw('range(') + ->subcompile($this->getNode('left')) + ->raw(', ') + ->subcompile($this->getNode('right')) + ->raw(')') + ; + } + + public function operator(Twig_Compiler $compiler) + { + return $compiler->raw('..'); + } +} diff --git a/vendor/Twig/Node/Expression/Binary/Sub.php b/vendor/Twig/Node/Expression/Binary/Sub.php new file mode 100644 index 0000000..d446399 --- /dev/null +++ b/vendor/Twig/Node/Expression/Binary/Sub.php @@ -0,0 +1,18 @@ +raw('-'); + } +} diff --git a/vendor/Twig/Node/Expression/BlockReference.php b/vendor/Twig/Node/Expression/BlockReference.php new file mode 100644 index 0000000..174d909 --- /dev/null +++ b/vendor/Twig/Node/Expression/BlockReference.php @@ -0,0 +1,52 @@ + + */ +class Twig_Node_Expression_BlockReference extends Twig_Node_Expression +{ + public function __construct(Twig_NodeInterface $name, $asString = false, $lineno, $tag = null) + { + parent::__construct(array('name' => $name), array('as_string' => $asString, 'output' => false), $lineno, $tag); + } + + /** + * Compiles the node to PHP. + * + * @param Twig_Compiler A Twig_Compiler instance + */ + public function compile(Twig_Compiler $compiler) + { + if ($this->getAttribute('as_string')) { + $compiler->raw('(string) '); + } + + if ($this->getAttribute('output')) { + $compiler + ->addDebugInfo($this) + ->write("\$this->displayBlock(") + ->subcompile($this->getNode('name')) + ->raw(", \$context, \$blocks);\n") + ; + } else { + $compiler + ->raw("\$this->renderBlock(") + ->subcompile($this->getNode('name')) + ->raw(", \$context, \$blocks)") + ; + } + } +} diff --git a/vendor/Twig/Node/Expression/Conditional.php b/vendor/Twig/Node/Expression/Conditional.php new file mode 100644 index 0000000..edcb1e2 --- /dev/null +++ b/vendor/Twig/Node/Expression/Conditional.php @@ -0,0 +1,31 @@ + $expr1, 'expr2' => $expr2, 'expr3' => $expr3), array(), $lineno); + } + + public function compile(Twig_Compiler $compiler) + { + $compiler + ->raw('((') + ->subcompile($this->getNode('expr1')) + ->raw(') ? (') + ->subcompile($this->getNode('expr2')) + ->raw(') : (') + ->subcompile($this->getNode('expr3')) + ->raw('))') + ; + } +} diff --git a/vendor/Twig/Node/Expression/Constant.php b/vendor/Twig/Node/Expression/Constant.php new file mode 100644 index 0000000..a91dc69 --- /dev/null +++ b/vendor/Twig/Node/Expression/Constant.php @@ -0,0 +1,23 @@ + $value), $lineno); + } + + public function compile(Twig_Compiler $compiler) + { + $compiler->repr($this->getAttribute('value')); + } +} diff --git a/vendor/Twig/Node/Expression/ExtensionReference.php b/vendor/Twig/Node/Expression/ExtensionReference.php new file mode 100644 index 0000000..cb4efad --- /dev/null +++ b/vendor/Twig/Node/Expression/ExtensionReference.php @@ -0,0 +1,34 @@ + + */ +class Twig_Node_Expression_ExtensionReference extends Twig_Node_Expression +{ + public function __construct($name, $lineno, $tag = null) + { + parent::__construct(array(), array('name' => $name), $lineno, $tag); + } + + /** + * Compiles the node to PHP. + * + * @param Twig_Compiler A Twig_Compiler instance + */ + public function compile(Twig_Compiler $compiler) + { + $compiler->raw(sprintf("\$this->env->getExtension('%s')", $this->getAttribute('name'))); + } +} diff --git a/vendor/Twig/Node/Expression/Filter.php b/vendor/Twig/Node/Expression/Filter.php new file mode 100644 index 0000000..8a0903a --- /dev/null +++ b/vendor/Twig/Node/Expression/Filter.php @@ -0,0 +1,61 @@ + $node, 'filter' => $filterName, 'arguments' => $arguments), array(), $lineno, $tag); + } + + public function compile(Twig_Compiler $compiler) + { + $name = $this->getNode('filter')->getAttribute('value'); + + if (false === $filter = $compiler->getEnvironment()->getFilter($name)) { + $message = sprintf('The filter "%s" does not exist', $name); + if ($alternatives = $compiler->getEnvironment()->computeAlternatives($name, array_keys($compiler->getEnvironment()->getFilters()))) { + $message = sprintf('%s. Did you mean "%s"', $message, implode('", "', $alternatives)); + } + + throw new Twig_Error_Syntax($message, $this->getLine()); + } + + $this->compileFilter($compiler, $filter); + } + + protected function compileFilter(Twig_Compiler $compiler, Twig_FilterInterface $filter) + { + $compiler + ->raw($filter->compile().'(') + ->raw($filter->needsEnvironment() ? '$this->env, ' : '') + ->raw($filter->needsContext() ? '$context, ' : '') + ; + + foreach ($filter->getArguments() as $argument) { + $compiler + ->string($argument) + ->raw(', ') + ; + } + + $compiler->subcompile($this->getNode('node')); + + foreach ($this->getNode('arguments') as $node) { + $compiler + ->raw(', ') + ->subcompile($node) + ; + } + + $compiler->raw(')'); + } +} diff --git a/vendor/Twig/Node/Expression/Filter/Default.php b/vendor/Twig/Node/Expression/Filter/Default.php new file mode 100644 index 0000000..1cb3342 --- /dev/null +++ b/vendor/Twig/Node/Expression/Filter/Default.php @@ -0,0 +1,44 @@ + + * {{ var.foo|default('foo item on var is not defined') }} + * + * + * @package twig + * @author Fabien Potencier + */ +class Twig_Node_Expression_Filter_Default extends Twig_Node_Expression_Filter +{ + public function __construct(Twig_NodeInterface $node, Twig_Node_Expression_Constant $filterName, Twig_NodeInterface $arguments, $lineno, $tag = null) + { + $default = new Twig_Node_Expression_Filter($node, new Twig_Node_Expression_Constant('_default', $node->getLine()), $arguments, $node->getLine()); + + if ('default' === $filterName->getAttribute('value') && ($node instanceof Twig_Node_Expression_Name || $node instanceof Twig_Node_Expression_GetAttr)) { + $test = new Twig_Node_Expression_Test_Defined(clone $node, 'defined', new Twig_Node(), $node->getLine()); + $false = count($arguments) ? $arguments->getNode(0) : new Twig_Node_Expression_Constant('', $node->getLine()); + + $node = new Twig_Node_Expression_Conditional($test, $default, $false, $node->getLine()); + } else { + $node = $default; + } + + parent::__construct($node, $filterName, $arguments, $lineno, $tag); + } + + public function compile(Twig_Compiler $compiler) + { + $compiler->subcompile($this->getNode('node')); + } +} diff --git a/vendor/Twig/Node/Expression/Function.php b/vendor/Twig/Node/Expression/Function.php new file mode 100644 index 0000000..9342bb1 --- /dev/null +++ b/vendor/Twig/Node/Expression/Function.php @@ -0,0 +1,66 @@ + $arguments), array('name' => $name), $lineno); + } + + public function compile(Twig_Compiler $compiler) + { + $name = $this->getAttribute('name'); + + if (false === $function = $compiler->getEnvironment()->getFunction($name)) { + $message = sprintf('The function "%s" does not exist', $name); + if ($alternatives = $compiler->getEnvironment()->computeAlternatives($name, array_keys($compiler->getEnvironment()->getFunctions()))) { + $message = sprintf('%s. Did you mean "%s"', $message, implode('", "', $alternatives)); + } + + throw new Twig_Error_Syntax($message, $this->getLine()); + } + + $compiler->raw($function->compile().'('); + + $first = true; + + if ($function->needsEnvironment()) { + $compiler->raw('$this->env'); + $first = false; + } + + if ($function->needsContext()) { + if (!$first) { + $compiler->raw(', '); + } + $compiler->raw('$context'); + $first = false; + } + + foreach ($function->getArguments() as $argument) { + if (!$first) { + $compiler->raw(', '); + } + $compiler->string($argument); + $first = false; + } + + foreach ($this->getNode('arguments') as $node) { + if (!$first) { + $compiler->raw(', '); + } + $compiler->subcompile($node); + $first = false; + } + + $compiler->raw(')'); + } +} diff --git a/vendor/Twig/Node/Expression/GetAttr.php b/vendor/Twig/Node/Expression/GetAttr.php new file mode 100644 index 0000000..6498444 --- /dev/null +++ b/vendor/Twig/Node/Expression/GetAttr.php @@ -0,0 +1,53 @@ + $node, 'attribute' => $attribute, 'arguments' => $arguments), array('type' => $type, 'is_defined_test' => false, 'ignore_strict_check' => false), $lineno); + } + + public function compile(Twig_Compiler $compiler) + { + if (function_exists('twig_template_get_attributes')) { + $compiler->raw('twig_template_get_attributes($this, '); + } else { + $compiler->raw('$this->getAttribute('); + } + + if ($this->getAttribute('ignore_strict_check')) { + $this->getNode('node')->setAttribute('ignore_strict_check', true); + } + + $compiler->subcompile($this->getNode('node')); + + $compiler->raw(', ')->subcompile($this->getNode('attribute')); + + if (count($this->getNode('arguments')) || Twig_TemplateInterface::ANY_CALL !== $this->getAttribute('type') || $this->getAttribute('is_defined_test') || $this->getAttribute('ignore_strict_check')) { + $compiler->raw(', ')->subcompile($this->getNode('arguments')); + + if (Twig_TemplateInterface::ANY_CALL !== $this->getAttribute('type') || $this->getAttribute('is_defined_test') || $this->getAttribute('ignore_strict_check')) { + $compiler->raw(', ')->repr($this->getAttribute('type')); + } + + if ($this->getAttribute('is_defined_test') || $this->getAttribute('ignore_strict_check')) { + $compiler->raw(', '.($this->getAttribute('is_defined_test') ? 'true' : 'false')); + } + + if ($this->getAttribute('ignore_strict_check')) { + $compiler->raw(', '.($this->getAttribute('ignore_strict_check') ? 'true' : 'false')); + } + } + + $compiler->raw(')'); + } +} diff --git a/vendor/Twig/Node/Expression/MethodCall.php b/vendor/Twig/Node/Expression/MethodCall.php new file mode 100644 index 0000000..5093808 --- /dev/null +++ b/vendor/Twig/Node/Expression/MethodCall.php @@ -0,0 +1,37 @@ + $node, 'arguments' => $arguments), array('method' => $method, 'safe' => false), $lineno); + } + + public function compile(Twig_Compiler $compiler) + { + $compiler + ->subcompile($this->getNode('node')) + ->raw('->') + ->raw($this->getAttribute('method')) + ->raw('(') + ; + $first = true; + foreach ($this->getNode('arguments')->getKeyValuePairs() as $pair) { + if (!$first) { + $compiler->raw(', '); + } + $first = false; + + $compiler->subcompile($pair['value']); + } + $compiler->raw(')'); + } +} diff --git a/vendor/Twig/Node/Expression/Name.php b/vendor/Twig/Node/Expression/Name.php new file mode 100644 index 0000000..8f5a1ea --- /dev/null +++ b/vendor/Twig/Node/Expression/Name.php @@ -0,0 +1,76 @@ + '$this', + '_context' => '$context', + '_charset' => '$this->env->getCharset()', + ); + + public function __construct($name, $lineno) + { + parent::__construct(array(), array('name' => $name, 'is_defined_test' => false, 'ignore_strict_check' => false), $lineno); + } + + public function compile(Twig_Compiler $compiler) + { + $name = $this->getAttribute('name'); + + if ($this->getAttribute('is_defined_test')) { + if ($this->isSpecial()) { + $compiler->repr(true); + } else { + $compiler->raw('array_key_exists(')->repr($name)->raw(', $context)'); + } + } elseif ($this->isSpecial()) { + $compiler->raw($this->specialVars[$name]); + } else { + // remove the non-PHP 5.4 version when PHP 5.3 support is dropped + // as the non-optimized version is just a workaround for slow ternary operator + // when the context has a lot of variables + if (version_compare(phpversion(), '5.4.0RC1', '>=') && ($this->getAttribute('ignore_strict_check') || !$compiler->getEnvironment()->isStrictVariables())) { + // PHP 5.4 ternary operator performance was optimized + $compiler + ->raw('(isset($context[') + ->string($name) + ->raw(']) ? $context[') + ->string($name) + ->raw('] : null)') + ; + } else { + $compiler + ->raw('$this->getContext($context, ') + ->string($name) + ; + + if ($this->getAttribute('ignore_strict_check')) { + $compiler->raw(', true'); + } + + $compiler + ->raw(')') + ; + } + } + } + + public function isSpecial() + { + return isset($this->specialVars[$this->getAttribute('name')]); + } + + public function isSimple() + { + return !$this->isSpecial() && !$this->getAttribute('is_defined_test'); + } +} diff --git a/vendor/Twig/Node/Expression/Parent.php b/vendor/Twig/Node/Expression/Parent.php new file mode 100644 index 0000000..ea97349 --- /dev/null +++ b/vendor/Twig/Node/Expression/Parent.php @@ -0,0 +1,48 @@ + + */ +class Twig_Node_Expression_Parent extends Twig_Node_Expression +{ + public function __construct($name, $lineno, $tag = null) + { + parent::__construct(array(), array('output' => false, 'name' => $name), $lineno, $tag); + } + + /** + * Compiles the node to PHP. + * + * @param Twig_Compiler A Twig_Compiler instance + */ + public function compile(Twig_Compiler $compiler) + { + if ($this->getAttribute('output')) { + $compiler + ->addDebugInfo($this) + ->write("\$this->displayParentBlock(") + ->string($this->getAttribute('name')) + ->raw(", \$context, \$blocks);\n") + ; + } else { + $compiler + ->raw("\$this->renderParentBlock(") + ->string($this->getAttribute('name')) + ->raw(", \$context, \$blocks)") + ; + } + } +} diff --git a/vendor/Twig/Node/Expression/TempName.php b/vendor/Twig/Node/Expression/TempName.php new file mode 100644 index 0000000..eea9d47 --- /dev/null +++ b/vendor/Twig/Node/Expression/TempName.php @@ -0,0 +1,22 @@ + $name), $lineno); + } + + public function compile(Twig_Compiler $compiler) + { + $compiler->raw('$_')->raw($this->getAttribute('name'))->raw('_'); + } +} diff --git a/vendor/Twig/Node/Expression/Test.php b/vendor/Twig/Node/Expression/Test.php new file mode 100644 index 0000000..4e0b25e --- /dev/null +++ b/vendor/Twig/Node/Expression/Test.php @@ -0,0 +1,54 @@ + $node, 'arguments' => $arguments), array('name' => $name), $lineno); + } + + public function compile(Twig_Compiler $compiler) + { + $name = $this->getAttribute('name'); + $testMap = $compiler->getEnvironment()->getTests(); + if (!isset($testMap[$name])) { + $message = sprintf('The test "%s" does not exist', $name); + if ($alternatives = $compiler->getEnvironment()->computeAlternatives($name, array_keys($compiler->getEnvironment()->getTests()))) { + $message = sprintf('%s. Did you mean "%s"', $message, implode('", "', $alternatives)); + } + + throw new Twig_Error_Syntax($message, $this->getLine()); + } + + $name = $this->getAttribute('name'); + $node = $this->getNode('node'); + + $compiler + ->raw($testMap[$name]->compile().'(') + ->subcompile($node) + ; + + if (null !== $this->getNode('arguments')) { + $compiler->raw(', '); + + $max = count($this->getNode('arguments')) - 1; + foreach ($this->getNode('arguments') as $i => $arg) { + $compiler->subcompile($arg); + + if ($i != $max) { + $compiler->raw(', '); + } + } + } + + $compiler->raw(')'); + } +} diff --git a/vendor/Twig/Node/Expression/Test/Constant.php b/vendor/Twig/Node/Expression/Test/Constant.php new file mode 100644 index 0000000..6e6b6fd --- /dev/null +++ b/vendor/Twig/Node/Expression/Test/Constant.php @@ -0,0 +1,36 @@ + + * {% if post.status is constant('Post::PUBLISHED') %} + * the status attribute is exactly the same as Post::PUBLISHED + * {% endif %} + * + * + * @package twig + * @author Fabien Potencier + */ +class Twig_Node_Expression_Test_Constant extends Twig_Node_Expression_Test +{ + public function compile(Twig_Compiler $compiler) + { + $compiler + ->raw('(') + ->subcompile($this->getNode('node')) + ->raw(' === constant(') + ->subcompile($this->getNode('arguments')->getNode(0)) + ->raw('))') + ; + } +} diff --git a/vendor/Twig/Node/Expression/Test/Defined.php b/vendor/Twig/Node/Expression/Test/Defined.php new file mode 100644 index 0000000..e7c6828 --- /dev/null +++ b/vendor/Twig/Node/Expression/Test/Defined.php @@ -0,0 +1,55 @@ + + * {# defined works with variable names and variable attributes #} + * {% if foo is defined %} + * {# ... #} + * {% endif %} + * + * + * @package twig + * @author Fabien Potencier + */ +class Twig_Node_Expression_Test_Defined extends Twig_Node_Expression_Test +{ + public function __construct(Twig_NodeInterface $node, $name, Twig_NodeInterface $arguments = null, $lineno) + { + parent::__construct($node, $name, $arguments, $lineno); + + if ($node instanceof Twig_Node_Expression_Name) { + $node->setAttribute('is_defined_test', true); + } elseif ($node instanceof Twig_Node_Expression_GetAttr) { + $node->setAttribute('is_defined_test', true); + + $this->changeIgnoreStrictCheck($node); + } else { + throw new Twig_Error_Syntax('The "defined" test only works with simple variables', $this->getLine()); + } + } + + protected function changeIgnoreStrictCheck(Twig_Node_Expression_GetAttr $node) + { + $node->setAttribute('ignore_strict_check', true); + + if ($node->getNode('node') instanceof Twig_Node_Expression_GetAttr) { + $this->changeIgnoreStrictCheck($node->getNode('node')); + } + } + + public function compile(Twig_Compiler $compiler) + { + $compiler->subcompile($this->getNode('node')); + } +} diff --git a/vendor/Twig/Node/Expression/Test/Divisibleby.php b/vendor/Twig/Node/Expression/Test/Divisibleby.php new file mode 100644 index 0000000..05563d5 --- /dev/null +++ b/vendor/Twig/Node/Expression/Test/Divisibleby.php @@ -0,0 +1,34 @@ + + * {% if loop.index is divisibleby(3) %} + * + * + * @package twig + * @author Fabien Potencier + */ +class Twig_Node_Expression_Test_Divisibleby extends Twig_Node_Expression_Test +{ + public function compile(Twig_Compiler $compiler) + { + $compiler + ->raw('(0 == ') + ->subcompile($this->getNode('node')) + ->raw(' % ') + ->subcompile($this->getNode('arguments')->getNode(0)) + ->raw(')') + ; + } +} diff --git a/vendor/Twig/Node/Expression/Test/Even.php b/vendor/Twig/Node/Expression/Test/Even.php new file mode 100644 index 0000000..08e6d82 --- /dev/null +++ b/vendor/Twig/Node/Expression/Test/Even.php @@ -0,0 +1,33 @@ + + * {{ var is even }} + * + * + * @package twig + * @author Fabien Potencier + */ +class Twig_Node_Expression_Test_Even extends Twig_Node_Expression_Test +{ + public function compile(Twig_Compiler $compiler) + { + $compiler + ->raw('(') + ->subcompile($this->getNode('node')) + ->raw(' % 2 == 0') + ->raw(')') + ; + } +} diff --git a/vendor/Twig/Node/Expression/Test/Null.php b/vendor/Twig/Node/Expression/Test/Null.php new file mode 100644 index 0000000..55061db --- /dev/null +++ b/vendor/Twig/Node/Expression/Test/Null.php @@ -0,0 +1,32 @@ + + * {{ var is none }} + * + * + * @package twig + * @author Fabien Potencier + */ +class Twig_Node_Expression_Test_Null extends Twig_Node_Expression_Test +{ + public function compile(Twig_Compiler $compiler) + { + $compiler + ->raw('(null === ') + ->subcompile($this->getNode('node')) + ->raw(')') + ; + } +} diff --git a/vendor/Twig/Node/Expression/Test/Odd.php b/vendor/Twig/Node/Expression/Test/Odd.php new file mode 100644 index 0000000..5fecebc --- /dev/null +++ b/vendor/Twig/Node/Expression/Test/Odd.php @@ -0,0 +1,33 @@ + + * {{ var is odd }} + * + * + * @package twig + * @author Fabien Potencier + */ +class Twig_Node_Expression_Test_Odd extends Twig_Node_Expression_Test +{ + public function compile(Twig_Compiler $compiler) + { + $compiler + ->raw('(') + ->subcompile($this->getNode('node')) + ->raw(' % 2 == 1') + ->raw(')') + ; + } +} diff --git a/vendor/Twig/Node/Expression/Test/Sameas.php b/vendor/Twig/Node/Expression/Test/Sameas.php new file mode 100644 index 0000000..8639b96 --- /dev/null +++ b/vendor/Twig/Node/Expression/Test/Sameas.php @@ -0,0 +1,30 @@ + + */ +class Twig_Node_Expression_Test_Sameas extends Twig_Node_Expression_Test +{ + public function compile(Twig_Compiler $compiler) + { + $compiler + ->raw('(') + ->subcompile($this->getNode('node')) + ->raw(' === ') + ->subcompile($this->getNode('arguments')->getNode(0)) + ->raw(')') + ; + } +} diff --git a/vendor/Twig/Node/Expression/Unary.php b/vendor/Twig/Node/Expression/Unary.php new file mode 100644 index 0000000..c514388 --- /dev/null +++ b/vendor/Twig/Node/Expression/Unary.php @@ -0,0 +1,30 @@ + $node), array(), $lineno); + } + + public function compile(Twig_Compiler $compiler) + { + $compiler->raw('('); + $this->operator($compiler); + $compiler + ->subcompile($this->getNode('node')) + ->raw(')') + ; + } + + abstract public function operator(Twig_Compiler $compiler); +} diff --git a/vendor/Twig/Node/Expression/Unary/Neg.php b/vendor/Twig/Node/Expression/Unary/Neg.php new file mode 100644 index 0000000..2a3937e --- /dev/null +++ b/vendor/Twig/Node/Expression/Unary/Neg.php @@ -0,0 +1,18 @@ +raw('-'); + } +} diff --git a/vendor/Twig/Node/Expression/Unary/Not.php b/vendor/Twig/Node/Expression/Unary/Not.php new file mode 100644 index 0000000..f94073c --- /dev/null +++ b/vendor/Twig/Node/Expression/Unary/Not.php @@ -0,0 +1,18 @@ +raw('!'); + } +} diff --git a/vendor/Twig/Node/Expression/Unary/Pos.php b/vendor/Twig/Node/Expression/Unary/Pos.php new file mode 100644 index 0000000..04edb52 --- /dev/null +++ b/vendor/Twig/Node/Expression/Unary/Pos.php @@ -0,0 +1,18 @@ +raw('+'); + } +} diff --git a/vendor/Twig/Node/Flush.php b/vendor/Twig/Node/Flush.php new file mode 100644 index 0000000..8f2ab9d --- /dev/null +++ b/vendor/Twig/Node/Flush.php @@ -0,0 +1,37 @@ + + */ +class Twig_Node_Flush extends Twig_Node +{ + public function __construct($lineno, $tag) + { + parent::__construct(array(), array(), $lineno, $tag); + } + + /** + * Compiles the node to PHP. + * + * @param Twig_Compiler A Twig_Compiler instance + */ + public function compile(Twig_Compiler $compiler) + { + $compiler + ->addDebugInfo($this) + ->write("flush();\n") + ; + } +} diff --git a/vendor/Twig/Node/For.php b/vendor/Twig/Node/For.php new file mode 100644 index 0000000..d9d25b3 --- /dev/null +++ b/vendor/Twig/Node/For.php @@ -0,0 +1,113 @@ + + */ +class Twig_Node_For extends Twig_Node +{ + protected $loop; + + public function __construct(Twig_Node_Expression_AssignName $keyTarget, Twig_Node_Expression_AssignName $valueTarget, Twig_Node_Expression $seq, Twig_Node_Expression $ifexpr = null, Twig_NodeInterface $body, Twig_NodeInterface $else = null, $lineno, $tag = null) + { + $body = new Twig_Node(array($body, $this->loop = new Twig_Node_ForLoop($lineno, $tag))); + + if (null !== $ifexpr) { + $body = new Twig_Node_If(new Twig_Node(array($ifexpr, $body)), null, $lineno, $tag); + } + + parent::__construct(array('key_target' => $keyTarget, 'value_target' => $valueTarget, 'seq' => $seq, 'body' => $body, 'else' => $else), array('with_loop' => true, 'ifexpr' => null !== $ifexpr), $lineno, $tag); + } + + /** + * Compiles the node to PHP. + * + * @param Twig_Compiler A Twig_Compiler instance + */ + public function compile(Twig_Compiler $compiler) + { + $compiler + ->addDebugInfo($this) + // the (array) cast bypasses a PHP 5.2.6 bug + ->write("\$context['_parent'] = (array) \$context;\n") + ->write("\$context['_seq'] = twig_ensure_traversable(") + ->subcompile($this->getNode('seq')) + ->raw(");\n") + ; + + if (null !== $this->getNode('else')) { + $compiler->write("\$context['_iterated'] = false;\n"); + } + + if ($this->getAttribute('with_loop')) { + $compiler + ->write("\$context['loop'] = array(\n") + ->write(" 'parent' => \$context['_parent'],\n") + ->write(" 'index0' => 0,\n") + ->write(" 'index' => 1,\n") + ->write(" 'first' => true,\n") + ->write(");\n") + ; + + if (!$this->getAttribute('ifexpr')) { + $compiler + ->write("if (is_array(\$context['_seq']) || (is_object(\$context['_seq']) && \$context['_seq'] instanceof Countable)) {\n") + ->indent() + ->write("\$length = count(\$context['_seq']);\n") + ->write("\$context['loop']['revindex0'] = \$length - 1;\n") + ->write("\$context['loop']['revindex'] = \$length;\n") + ->write("\$context['loop']['length'] = \$length;\n") + ->write("\$context['loop']['last'] = 1 === \$length;\n") + ->outdent() + ->write("}\n") + ; + } + } + + $this->loop->setAttribute('else', null !== $this->getNode('else')); + $this->loop->setAttribute('with_loop', $this->getAttribute('with_loop')); + $this->loop->setAttribute('ifexpr', $this->getAttribute('ifexpr')); + + $compiler + ->write("foreach (\$context['_seq'] as ") + ->subcompile($this->getNode('key_target')) + ->raw(" => ") + ->subcompile($this->getNode('value_target')) + ->raw(") {\n") + ->indent() + ->subcompile($this->getNode('body')) + ->outdent() + ->write("}\n") + ; + + if (null !== $this->getNode('else')) { + $compiler + ->write("if (!\$context['_iterated']) {\n") + ->indent() + ->subcompile($this->getNode('else')) + ->outdent() + ->write("}\n") + ; + } + + $compiler->write("\$_parent = \$context['_parent'];\n"); + + // remove some "private" loop variables (needed for nested loops) + $compiler->write('unset($context[\'_seq\'], $context[\'_iterated\'], $context[\''.$this->getNode('key_target')->getAttribute('name').'\'], $context[\''.$this->getNode('value_target')->getAttribute('name').'\'], $context[\'_parent\'], $context[\'loop\']);'."\n"); + + // keep the values set in the inner context for variables defined in the outer context + $compiler->write("\$context = array_merge(\$_parent, array_intersect_key(\$context, \$_parent));\n"); + } +} diff --git a/vendor/Twig/Node/ForLoop.php b/vendor/Twig/Node/ForLoop.php new file mode 100644 index 0000000..38f2e85 --- /dev/null +++ b/vendor/Twig/Node/ForLoop.php @@ -0,0 +1,56 @@ + + */ +class Twig_Node_ForLoop extends Twig_Node +{ + public function __construct($lineno, $tag = null) + { + parent::__construct(array(), array('with_loop' => false, 'ifexpr' => false, 'else' => false), $lineno, $tag); + } + + /** + * Compiles the node to PHP. + * + * @param Twig_Compiler A Twig_Compiler instance + */ + public function compile(Twig_Compiler $compiler) + { + if ($this->getAttribute('else')) { + $compiler->write("\$context['_iterated'] = true;\n"); + } + + if ($this->getAttribute('with_loop')) { + $compiler + ->write("++\$context['loop']['index0'];\n") + ->write("++\$context['loop']['index'];\n") + ->write("\$context['loop']['first'] = false;\n") + ; + + if (!$this->getAttribute('ifexpr')) { + $compiler + ->write("if (isset(\$context['loop']['length'])) {\n") + ->indent() + ->write("--\$context['loop']['revindex0'];\n") + ->write("--\$context['loop']['revindex'];\n") + ->write("\$context['loop']['last'] = 0 === \$context['loop']['revindex0'];\n") + ->outdent() + ->write("}\n") + ; + } + } + } +} diff --git a/vendor/Twig/Node/If.php b/vendor/Twig/Node/If.php new file mode 100644 index 0000000..aa12efb --- /dev/null +++ b/vendor/Twig/Node/If.php @@ -0,0 +1,67 @@ + + */ +class Twig_Node_If extends Twig_Node +{ + public function __construct(Twig_NodeInterface $tests, Twig_NodeInterface $else = null, $lineno, $tag = null) + { + parent::__construct(array('tests' => $tests, 'else' => $else), array(), $lineno, $tag); + } + + /** + * Compiles the node to PHP. + * + * @param Twig_Compiler A Twig_Compiler instance + */ + public function compile(Twig_Compiler $compiler) + { + $compiler->addDebugInfo($this); + for ($i = 0; $i < count($this->getNode('tests')); $i += 2) { + if ($i > 0) { + $compiler + ->outdent() + ->write("} elseif (") + ; + } else { + $compiler + ->write('if (') + ; + } + + $compiler + ->subcompile($this->getNode('tests')->getNode($i)) + ->raw(") {\n") + ->indent() + ->subcompile($this->getNode('tests')->getNode($i + 1)) + ; + } + + if ($this->hasNode('else') && null !== $this->getNode('else')) { + $compiler + ->outdent() + ->write("} else {\n") + ->indent() + ->subcompile($this->getNode('else')) + ; + } + + $compiler + ->outdent() + ->write("}\n"); + } +} diff --git a/vendor/Twig/Node/Import.php b/vendor/Twig/Node/Import.php new file mode 100644 index 0000000..a327411 --- /dev/null +++ b/vendor/Twig/Node/Import.php @@ -0,0 +1,51 @@ + + */ +class Twig_Node_Import extends Twig_Node +{ + public function __construct(Twig_Node_Expression $expr, Twig_Node_Expression $var, $lineno, $tag = null) + { + parent::__construct(array('expr' => $expr, 'var' => $var), array(), $lineno, $tag); + } + + /** + * Compiles the node to PHP. + * + * @param Twig_Compiler A Twig_Compiler instance + */ + public function compile(Twig_Compiler $compiler) + { + $compiler + ->addDebugInfo($this) + ->write('') + ->subcompile($this->getNode('var')) + ->raw(' = ') + ; + + if ($this->getNode('expr') instanceof Twig_Node_Expression_Name && '_self' === $this->getNode('expr')->getAttribute('name')) { + $compiler->raw("\$this"); + } else { + $compiler + ->raw('$this->env->loadTemplate(') + ->subcompile($this->getNode('expr')) + ->raw(")") + ; + } + + $compiler->raw(";\n"); + } +} diff --git a/vendor/Twig/Node/Include.php b/vendor/Twig/Node/Include.php new file mode 100644 index 0000000..5b6be7a --- /dev/null +++ b/vendor/Twig/Node/Include.php @@ -0,0 +1,100 @@ + + */ +class Twig_Node_Include extends Twig_Node implements Twig_NodeOutputInterface +{ + public function __construct(Twig_Node_Expression $expr, Twig_Node_Expression $variables = null, $only = false, $ignoreMissing = false, $lineno, $tag = null) + { + parent::__construct(array('expr' => $expr, 'variables' => $variables), array('only' => (Boolean) $only, 'ignore_missing' => (Boolean) $ignoreMissing), $lineno, $tag); + } + + /** + * Compiles the node to PHP. + * + * @param Twig_Compiler A Twig_Compiler instance + */ + public function compile(Twig_Compiler $compiler) + { + $compiler->addDebugInfo($this); + + if ($this->getAttribute('ignore_missing')) { + $compiler + ->write("try {\n") + ->indent() + ; + } + + $this->addGetTemplate($compiler); + + $compiler->raw('->display('); + + $this->addTemplateArguments($compiler); + + $compiler->raw(");\n"); + + if ($this->getAttribute('ignore_missing')) { + $compiler + ->outdent() + ->write("} catch (Twig_Error_Loader \$e) {\n") + ->indent() + ->write("// ignore missing template\n") + ->outdent() + ->write("}\n\n") + ; + } + } + + protected function addGetTemplate(Twig_Compiler $compiler) + { + if ($this->getNode('expr') instanceof Twig_Node_Expression_Constant) { + $compiler + ->write("\$this->env->loadTemplate(") + ->subcompile($this->getNode('expr')) + ->raw(")") + ; + } else { + $compiler + ->write("\$template = \$this->env->resolveTemplate(") + ->subcompile($this->getNode('expr')) + ->raw(");\n") + ->write('$template') + ; + } + } + + protected function addTemplateArguments(Twig_Compiler $compiler) + { + if (false === $this->getAttribute('only')) { + if (null === $this->getNode('variables')) { + $compiler->raw('$context'); + } else { + $compiler + ->raw('array_merge($context, ') + ->subcompile($this->getNode('variables')) + ->raw(')') + ; + } + } else { + if (null === $this->getNode('variables')) { + $compiler->raw('array()'); + } else { + $compiler->subcompile($this->getNode('variables')); + } + } + } +} diff --git a/vendor/Twig/Node/Macro.php b/vendor/Twig/Node/Macro.php new file mode 100644 index 0000000..e0c3dca --- /dev/null +++ b/vendor/Twig/Node/Macro.php @@ -0,0 +1,84 @@ + + */ +class Twig_Node_Macro extends Twig_Node +{ + public function __construct($name, Twig_NodeInterface $body, Twig_NodeInterface $arguments, $lineno, $tag = null) + { + parent::__construct(array('body' => $body, 'arguments' => $arguments), array('name' => $name), $lineno, $tag); + } + + /** + * Compiles the node to PHP. + * + * @param Twig_Compiler A Twig_Compiler instance + */ + public function compile(Twig_Compiler $compiler) + { + $arguments = array(); + foreach ($this->getNode('arguments') as $argument) { + $arguments[] = '$'.$argument->getAttribute('name').' = null'; + } + + $compiler + ->addDebugInfo($this) + ->write(sprintf("public function get%s(%s)\n", $this->getAttribute('name'), implode(', ', $arguments)), "{\n") + ->indent() + ; + + if (!count($this->getNode('arguments'))) { + $compiler->write("\$context = \$this->env->getGlobals();\n\n"); + } else { + $compiler + ->write("\$context = \$this->env->mergeGlobals(array(\n") + ->indent() + ; + + foreach ($this->getNode('arguments') as $argument) { + $compiler + ->write('') + ->string($argument->getAttribute('name')) + ->raw(' => $'.$argument->getAttribute('name')) + ->raw(",\n") + ; + } + + $compiler + ->outdent() + ->write("));\n\n") + ; + } + + $compiler + ->write("\$blocks = array();\n\n") + ->write("ob_start();\n") + ->write("try {\n") + ->indent() + ->subcompile($this->getNode('body')) + ->outdent() + ->write("} catch(Exception \$e) {\n") + ->indent() + ->write("ob_end_clean();\n\n") + ->write("throw \$e;\n") + ->outdent() + ->write("}\n\n") + ->write("return ob_get_clean();\n") + ->outdent() + ->write("}\n\n") + ; + } +} diff --git a/vendor/Twig/Node/Module.php b/vendor/Twig/Node/Module.php new file mode 100644 index 0000000..4c7a107 --- /dev/null +++ b/vendor/Twig/Node/Module.php @@ -0,0 +1,372 @@ + + */ +class Twig_Node_Module extends Twig_Node +{ + public function __construct(Twig_NodeInterface $body, Twig_Node_Expression $parent = null, Twig_NodeInterface $blocks, Twig_NodeInterface $macros, Twig_NodeInterface $traits, $embeddedTemplates, $filename) + { + // embedded templates are set as attributes so that they are only visited once by the visitors + parent::__construct(array('parent' => $parent, 'body' => $body, 'blocks' => $blocks, 'macros' => $macros, 'traits' => $traits), array('filename' => $filename, 'index' => null, 'embedded_templates' => $embeddedTemplates), 1); + } + + public function setIndex($index) + { + $this->setAttribute('index', $index); + } + + /** + * Compiles the node to PHP. + * + * @param Twig_Compiler A Twig_Compiler instance + */ + public function compile(Twig_Compiler $compiler) + { + $this->compileTemplate($compiler); + + foreach ($this->getAttribute('embedded_templates') as $template) { + $compiler->subcompile($template); + } + } + + protected function compileTemplate(Twig_Compiler $compiler) + { + if (!$this->getAttribute('index')) { + $compiler->write('compileClassHeader($compiler); + + if (count($this->getNode('blocks')) || count($this->getNode('traits')) || null === $this->getNode('parent') || $this->getNode('parent') instanceof Twig_Node_Expression_Constant) { + $this->compileConstructor($compiler); + } + + $this->compileGetParent($compiler); + + $this->compileDisplayHeader($compiler); + + $this->compileDisplayBody($compiler); + + $this->compileDisplayFooter($compiler); + + $compiler->subcompile($this->getNode('blocks')); + + $this->compileMacros($compiler); + + $this->compileGetTemplateName($compiler); + + $this->compileIsTraitable($compiler); + + $this->compileDebugInfo($compiler); + + $this->compileClassFooter($compiler); + } + + protected function compileGetParent(Twig_Compiler $compiler) + { + if (null === $this->getNode('parent')) { + return; + } + + $compiler + ->write("protected function doGetParent(array \$context)\n", "{\n") + ->indent() + ->write("return ") + ; + + if ($this->getNode('parent') instanceof Twig_Node_Expression_Constant) { + $compiler->subcompile($this->getNode('parent')); + } else { + $compiler + ->raw("\$this->env->resolveTemplate(") + ->subcompile($this->getNode('parent')) + ->raw(")") + ; + } + + $compiler + ->raw(";\n") + ->outdent() + ->write("}\n\n") + ; + } + + protected function compileDisplayBody(Twig_Compiler $compiler) + { + $compiler->subcompile($this->getNode('body')); + + if (null !== $this->getNode('parent')) { + if ($this->getNode('parent') instanceof Twig_Node_Expression_Constant) { + $compiler->write("\$this->parent"); + } else { + $compiler->write("\$this->getParent(\$context)"); + } + $compiler->raw("->display(\$context, array_merge(\$this->blocks, \$blocks));\n"); + } + } + + protected function compileClassHeader(Twig_Compiler $compiler) + { + $compiler + ->write("\n\n") + // if the filename contains */, add a blank to avoid a PHP parse error + ->write("/* ".str_replace('*/', '* /', $this->getAttribute('filename'))." */\n") + ->write('class '.$compiler->getEnvironment()->getTemplateClass($this->getAttribute('filename'), $this->getAttribute('index'))) + ->raw(sprintf(" extends %s\n", $compiler->getEnvironment()->getBaseTemplateClass())) + ->write("{\n") + ->indent() + ; + } + + protected function compileConstructor(Twig_Compiler $compiler) + { + $compiler + ->write("public function __construct(Twig_Environment \$env)\n", "{\n") + ->indent() + ->write("parent::__construct(\$env);\n\n") + ; + + // parent + if (null === $this->getNode('parent')) { + $compiler->write("\$this->parent = false;\n\n"); + } elseif ($this->getNode('parent') instanceof Twig_Node_Expression_Constant) { + $compiler + ->write("\$this->parent = \$this->env->loadTemplate(") + ->subcompile($this->getNode('parent')) + ->raw(");\n\n") + ; + } + + $countTraits = count($this->getNode('traits')); + if ($countTraits) { + // traits + foreach ($this->getNode('traits') as $i => $trait) { + $this->compileLoadTemplate($compiler, $trait->getNode('template'), sprintf('$_trait_%s', $i)); + + $compiler + ->addDebugInfo($trait->getNode('template')) + ->write(sprintf("if (!\$_trait_%s->isTraitable()) {\n", $i)) + ->indent() + ->write("throw new Twig_Error_Runtime('Template \"'.") + ->subcompile($trait->getNode('template')) + ->raw(".'\" cannot be used as a trait.');\n") + ->outdent() + ->write("}\n") + ->write(sprintf("\$_trait_%s_blocks = \$_trait_%s->getBlocks();\n\n", $i, $i)) + ; + + foreach ($trait->getNode('targets') as $key => $value) { + $compiler + ->write(sprintf("\$_trait_%s_blocks[", $i)) + ->subcompile($value) + ->raw(sprintf("] = \$_trait_%s_blocks[", $i)) + ->string($key) + ->raw(sprintf("]; unset(\$_trait_%s_blocks[", $i)) + ->string($key) + ->raw("]);\n\n") + ; + } + } + + if ($countTraits > 1) { + $compiler + ->write("\$this->traits = array_merge(\n") + ->indent() + ; + + for ($i = 0; $i < $countTraits; $i++) { + $compiler + ->write(sprintf("\$_trait_%s_blocks".($i == $countTraits - 1 ? '' : ',')."\n", $i)) + ; + } + + $compiler + ->outdent() + ->write(");\n\n") + ; + } else { + $compiler + ->write("\$this->traits = \$_trait_0_blocks;\n\n") + ; + } + + $compiler + ->write("\$this->blocks = array_merge(\n") + ->indent() + ->write("\$this->traits,\n") + ->write("array(\n") + ; + } else { + $compiler + ->write("\$this->blocks = array(\n") + ; + } + + // blocks + $compiler + ->indent() + ; + + foreach ($this->getNode('blocks') as $name => $node) { + $compiler + ->write(sprintf("'%s' => array(\$this, 'block_%s'),\n", $name, $name)) + ; + } + + if ($countTraits) { + $compiler + ->outdent() + ->write(")\n") + ; + } + + $compiler + ->outdent() + ->write(");\n") + ->outdent() + ->write("}\n\n"); + ; + } + + protected function compileDisplayHeader(Twig_Compiler $compiler) + { + $compiler + ->write("protected function doDisplay(array \$context, array \$blocks = array())\n", "{\n") + ->indent() + ; + } + + protected function compileDisplayFooter(Twig_Compiler $compiler) + { + $compiler + ->outdent() + ->write("}\n\n") + ; + } + + protected function compileClassFooter(Twig_Compiler $compiler) + { + $compiler + ->outdent() + ->write("}\n") + ; + } + + protected function compileMacros(Twig_Compiler $compiler) + { + $compiler->subcompile($this->getNode('macros')); + } + + protected function compileGetTemplateName(Twig_Compiler $compiler) + { + $compiler + ->write("public function getTemplateName()\n", "{\n") + ->indent() + ->write('return ') + ->repr($this->getAttribute('filename')) + ->raw(";\n") + ->outdent() + ->write("}\n\n") + ; + } + + protected function compileIsTraitable(Twig_Compiler $compiler) + { + // A template can be used as a trait if: + // * it has no parent + // * it has no macros + // * it has no body + // + // Put another way, a template can be used as a trait if it + // only contains blocks and use statements. + $traitable = null === $this->getNode('parent') && 0 === count($this->getNode('macros')); + if ($traitable) { + if ($this->getNode('body') instanceof Twig_Node_Body) { + $nodes = $this->getNode('body')->getNode(0); + } else { + $nodes = $this->getNode('body'); + } + + if (!count($nodes)) { + $nodes = new Twig_Node(array($nodes)); + } + + foreach ($nodes as $node) { + if (!count($node)) { + continue; + } + + if ($node instanceof Twig_Node_Text && ctype_space($node->getAttribute('data'))) { + continue; + } + + if ($node instanceof Twig_Node_BlockReference) { + continue; + } + + $traitable = false; + break; + } + } + + if ($traitable) { + return; + } + + $compiler + ->write("public function isTraitable()\n", "{\n") + ->indent() + ->write(sprintf("return %s;\n", $traitable ? 'true' : 'false')) + ->outdent() + ->write("}\n\n") + ; + } + + protected function compileDebugInfo(Twig_Compiler $compiler) + { + $compiler + ->write("public function getDebugInfo()\n", "{\n") + ->indent() + ->write(sprintf("return %s;\n", str_replace("\n", '', var_export(array_reverse($compiler->getDebugInfo(), true), true)))) + ->outdent() + ->write("}\n") + ; + } + + protected function compileLoadTemplate(Twig_Compiler $compiler, $node, $var) + { + if ($node instanceof Twig_Node_Expression_Constant) { + $compiler + ->write(sprintf("%s = \$this->env->loadTemplate(", $var)) + ->subcompile($node) + ->raw(");\n") + ; + } else { + $compiler + ->write(sprintf("%s = ", $var)) + ->subcompile($node) + ->raw(";\n") + ->write(sprintf("if (!%s", $var)) + ->raw(" instanceof Twig_Template) {\n") + ->indent() + ->write(sprintf("%s = \$this->env->loadTemplate(%s);\n", $var, $var)) + ->outdent() + ->write("}\n") + ; + } + } +} diff --git a/vendor/Twig/Node/Print.php b/vendor/Twig/Node/Print.php new file mode 100644 index 0000000..766725f --- /dev/null +++ b/vendor/Twig/Node/Print.php @@ -0,0 +1,40 @@ + + */ +class Twig_Node_Print extends Twig_Node implements Twig_NodeOutputInterface +{ + public function __construct(Twig_Node_Expression $expr, $lineno, $tag = null) + { + parent::__construct(array('expr' => $expr), array(), $lineno, $tag); + } + + /** + * Compiles the node to PHP. + * + * @param Twig_Compiler A Twig_Compiler instance + */ + public function compile(Twig_Compiler $compiler) + { + $compiler + ->addDebugInfo($this) + ->write('echo ') + ->subcompile($this->getNode('expr')) + ->raw(";\n") + ; + } +} diff --git a/vendor/Twig/Node/Sandbox.php b/vendor/Twig/Node/Sandbox.php new file mode 100644 index 0000000..cbfcb41 --- /dev/null +++ b/vendor/Twig/Node/Sandbox.php @@ -0,0 +1,48 @@ + + */ +class Twig_Node_Sandbox extends Twig_Node +{ + public function __construct(Twig_NodeInterface $body, $lineno, $tag = null) + { + parent::__construct(array('body' => $body), array(), $lineno, $tag); + } + + /** + * Compiles the node to PHP. + * + * @param Twig_Compiler A Twig_Compiler instance + */ + public function compile(Twig_Compiler $compiler) + { + $compiler + ->addDebugInfo($this) + ->write("\$sandbox = \$this->env->getExtension('sandbox');\n") + ->write("if (!\$alreadySandboxed = \$sandbox->isSandboxed()) {\n") + ->indent() + ->write("\$sandbox->enableSandbox();\n") + ->outdent() + ->write("}\n") + ->subcompile($this->getNode('body')) + ->write("if (!\$alreadySandboxed) {\n") + ->indent() + ->write("\$sandbox->disableSandbox();\n") + ->outdent() + ->write("}\n") + ; + } +} diff --git a/vendor/Twig/Node/SandboxedModule.php b/vendor/Twig/Node/SandboxedModule.php new file mode 100644 index 0000000..6dd63e5 --- /dev/null +++ b/vendor/Twig/Node/SandboxedModule.php @@ -0,0 +1,61 @@ + + */ +class Twig_Node_SandboxedModule extends Twig_Node_Module +{ + protected $usedFilters; + protected $usedTags; + protected $usedFunctions; + + public function __construct(Twig_Node_Module $node, array $usedFilters, array $usedTags, array $usedFunctions) + { + parent::__construct($node->getNode('body'), $node->getNode('parent'), $node->getNode('blocks'), $node->getNode('macros'), $node->getNode('traits'), $node->getAttribute('embedded_templates'), $node->getAttribute('filename'), $node->getLine(), $node->getNodeTag()); + + $this->setAttribute('index', $node->getAttribute('index')); + + $this->usedFilters = $usedFilters; + $this->usedTags = $usedTags; + $this->usedFunctions = $usedFunctions; + } + + protected function compileDisplayBody(Twig_Compiler $compiler) + { + $compiler->write("\$this->checkSecurity();\n"); + + parent::compileDisplayBody($compiler); + } + + protected function compileDisplayFooter(Twig_Compiler $compiler) + { + parent::compileDisplayFooter($compiler); + + $compiler + ->write("protected function checkSecurity() {\n") + ->indent() + ->write("\$this->env->getExtension('sandbox')->checkSecurity(\n") + ->indent() + ->write(!$this->usedTags ? "array(),\n" : "array('".implode('\', \'', $this->usedTags)."'),\n") + ->write(!$this->usedFilters ? "array(),\n" : "array('".implode('\', \'', $this->usedFilters)."'),\n") + ->write(!$this->usedFunctions ? "array()\n" : "array('".implode('\', \'', $this->usedFunctions)."')\n") + ->outdent() + ->write(");\n") + ->outdent() + ->write("}\n\n") + ; + } +} diff --git a/vendor/Twig/Node/SandboxedPrint.php b/vendor/Twig/Node/SandboxedPrint.php new file mode 100644 index 0000000..77730d8 --- /dev/null +++ b/vendor/Twig/Node/SandboxedPrint.php @@ -0,0 +1,60 @@ + + */ +class Twig_Node_SandboxedPrint extends Twig_Node_Print +{ + public function __construct(Twig_Node_Expression $expr, $lineno, $tag = null) + { + parent::__construct($expr, $lineno, $tag); + } + + /** + * Compiles the node to PHP. + * + * @param Twig_Compiler A Twig_Compiler instance + */ + public function compile(Twig_Compiler $compiler) + { + $compiler + ->addDebugInfo($this) + ->write('echo $this->env->getExtension(\'sandbox\')->ensureToStringAllowed(') + ->subcompile($this->getNode('expr')) + ->raw(");\n") + ; + } + + /** + * Removes node filters. + * + * This is mostly needed when another visitor adds filters (like the escaper one). + * + * @param Twig_Node $node A Node + */ + protected function removeNodeFilter($node) + { + if ($node instanceof Twig_Node_Expression_Filter) { + return $this->removeNodeFilter($node->getNode('node')); + } + + return $node; + } +} diff --git a/vendor/Twig/Node/Set.php b/vendor/Twig/Node/Set.php new file mode 100644 index 0000000..70bb519 --- /dev/null +++ b/vendor/Twig/Node/Set.php @@ -0,0 +1,102 @@ + + */ +class Twig_Node_Set extends Twig_Node +{ + public function __construct($capture, Twig_NodeInterface $names, Twig_NodeInterface $values, $lineno, $tag = null) + { + parent::__construct(array('names' => $names, 'values' => $values), array('capture' => $capture, 'safe' => false), $lineno, $tag); + + /* + * Optimizes the node when capture is used for a large block of text. + * + * {% set foo %}foo{% endset %} is compiled to $context['foo'] = new Twig_Markup("foo"); + */ + if ($this->getAttribute('capture')) { + $this->setAttribute('safe', true); + + $values = $this->getNode('values'); + if ($values instanceof Twig_Node_Text) { + $this->setNode('values', new Twig_Node_Expression_Constant($values->getAttribute('data'), $values->getLine())); + $this->setAttribute('capture', false); + } + } + } + + /** + * Compiles the node to PHP. + * + * @param Twig_Compiler A Twig_Compiler instance + */ + public function compile(Twig_Compiler $compiler) + { + $compiler->addDebugInfo($this); + + if (count($this->getNode('names')) > 1) { + $compiler->write('list('); + foreach ($this->getNode('names') as $idx => $node) { + if ($idx) { + $compiler->raw(', '); + } + + $compiler->subcompile($node); + } + $compiler->raw(')'); + } else { + if ($this->getAttribute('capture')) { + $compiler + ->write("ob_start();\n") + ->subcompile($this->getNode('values')) + ; + } + + $compiler->subcompile($this->getNode('names'), false); + + if ($this->getAttribute('capture')) { + $compiler->raw(" = ('' === \$tmp = ob_get_clean()) ? '' : new Twig_Markup(\$tmp, \$this->env->getCharset())"); + } + } + + if (!$this->getAttribute('capture')) { + $compiler->raw(' = '); + + if (count($this->getNode('names')) > 1) { + $compiler->write('array('); + foreach ($this->getNode('values') as $idx => $value) { + if ($idx) { + $compiler->raw(', '); + } + + $compiler->subcompile($value); + } + $compiler->raw(')'); + } else { + if ($this->getAttribute('safe')) { + $compiler + ->raw("('' === \$tmp = ") + ->subcompile($this->getNode('values')) + ->raw(") ? '' : new Twig_Markup(\$tmp, \$this->env->getCharset())") + ; + } else { + $compiler->subcompile($this->getNode('values')); + } + } + } + + $compiler->raw(";\n"); + } +} diff --git a/vendor/Twig/Node/SetTemp.php b/vendor/Twig/Node/SetTemp.php new file mode 100644 index 0000000..3bdd1cb --- /dev/null +++ b/vendor/Twig/Node/SetTemp.php @@ -0,0 +1,35 @@ + $name), $lineno); + } + + public function compile(Twig_Compiler $compiler) + { + $name = $this->getAttribute('name'); + $compiler + ->addDebugInfo($this) + ->write('if (isset($context[') + ->string($name) + ->raw('])) { $_') + ->raw($name) + ->raw('_ = $context[') + ->repr($name) + ->raw(']; } else { $_') + ->raw($name) + ->raw("_ = null; }\n") + ; + } +} diff --git a/vendor/Twig/Node/Spaceless.php b/vendor/Twig/Node/Spaceless.php new file mode 100644 index 0000000..4601346 --- /dev/null +++ b/vendor/Twig/Node/Spaceless.php @@ -0,0 +1,41 @@ + + */ +class Twig_Node_Spaceless extends Twig_Node +{ + public function __construct(Twig_NodeInterface $body, $lineno, $tag = 'spaceless') + { + parent::__construct(array('body' => $body), array(), $lineno, $tag); + } + + /** + * Compiles the node to PHP. + * + * @param Twig_Compiler A Twig_Compiler instance + */ + public function compile(Twig_Compiler $compiler) + { + $compiler + ->addDebugInfo($this) + ->write("ob_start();\n") + ->subcompile($this->getNode('body')) + ->write("echo trim(preg_replace('/>\s+<', ob_get_clean()));\n") + ; + } +} diff --git a/vendor/Twig/Node/Text.php b/vendor/Twig/Node/Text.php new file mode 100644 index 0000000..0c1c092 --- /dev/null +++ b/vendor/Twig/Node/Text.php @@ -0,0 +1,40 @@ + + */ +class Twig_Node_Text extends Twig_Node implements Twig_NodeOutputInterface +{ + public function __construct($data, $lineno) + { + parent::__construct(array(), array('data' => $data), $lineno); + } + + /** + * Compiles the node to PHP. + * + * @param Twig_Compiler A Twig_Compiler instance + */ + public function compile(Twig_Compiler $compiler) + { + $compiler + ->addDebugInfo($this) + ->write('echo ') + ->string($this->getAttribute('data')) + ->raw(";\n") + ; + } +} diff --git a/vendor/Twig/NodeInterface.php b/vendor/Twig/NodeInterface.php new file mode 100644 index 0000000..29a84b0 --- /dev/null +++ b/vendor/Twig/NodeInterface.php @@ -0,0 +1,30 @@ + + */ +interface Twig_NodeInterface extends Countable, IteratorAggregate +{ + /** + * Compiles the node to PHP. + * + * @param Twig_Compiler A Twig_Compiler instance + */ + function compile(Twig_Compiler $compiler); + + function getLine(); + + function getNodeTag(); +} diff --git a/vendor/Twig/NodeOutputInterface.php b/vendor/Twig/NodeOutputInterface.php new file mode 100644 index 0000000..7183956 --- /dev/null +++ b/vendor/Twig/NodeOutputInterface.php @@ -0,0 +1,20 @@ + + */ +interface Twig_NodeOutputInterface +{ +} diff --git a/vendor/Twig/NodeTraverser.php b/vendor/Twig/NodeTraverser.php new file mode 100644 index 0000000..1e82b03 --- /dev/null +++ b/vendor/Twig/NodeTraverser.php @@ -0,0 +1,89 @@ + + */ +class Twig_NodeTraverser +{ + protected $env; + protected $visitors; + + /** + * Constructor. + * + * @param Twig_Environment $env A Twig_Environment instance + * @param array $visitors An array of Twig_NodeVisitorInterface instances + */ + public function __construct(Twig_Environment $env, array $visitors = array()) + { + $this->env = $env; + $this->visitors = array(); + foreach ($visitors as $visitor) { + $this->addVisitor($visitor); + } + } + + /** + * Adds a visitor. + * + * @param Twig_NodeVisitorInterface $visitor A Twig_NodeVisitorInterface instance + */ + public function addVisitor(Twig_NodeVisitorInterface $visitor) + { + if (!isset($this->visitors[$visitor->getPriority()])) { + $this->visitors[$visitor->getPriority()] = array(); + } + + $this->visitors[$visitor->getPriority()][] = $visitor; + } + + /** + * Traverses a node and calls the registered visitors. + * + * @param Twig_NodeInterface $node A Twig_NodeInterface instance + */ + public function traverse(Twig_NodeInterface $node) + { + ksort($this->visitors); + foreach ($this->visitors as $visitors) { + foreach ($visitors as $visitor) { + $node = $this->traverseForVisitor($visitor, $node); + } + } + + return $node; + } + + protected function traverseForVisitor(Twig_NodeVisitorInterface $visitor, Twig_NodeInterface $node = null) + { + if (null === $node) { + return null; + } + + $node = $visitor->enterNode($node, $this->env); + + foreach ($node as $k => $n) { + if (false !== $n = $this->traverseForVisitor($visitor, $n)) { + $node->setNode($k, $n); + } else { + $node->removeNode($k); + } + } + + return $visitor->leaveNode($node, $this->env); + } +} diff --git a/vendor/Twig/NodeVisitor/Escaper.php b/vendor/Twig/NodeVisitor/Escaper.php new file mode 100644 index 0000000..5b1249d --- /dev/null +++ b/vendor/Twig/NodeVisitor/Escaper.php @@ -0,0 +1,164 @@ + + */ +class Twig_NodeVisitor_Escaper implements Twig_NodeVisitorInterface +{ + protected $statusStack = array(); + protected $blocks = array(); + protected $safeAnalysis; + protected $traverser; + protected $defaultStrategy = false; + + public function __construct() + { + $this->safeAnalysis = new Twig_NodeVisitor_SafeAnalysis(); + } + + /** + * Called before child nodes are visited. + * + * @param Twig_NodeInterface $node The node to visit + * @param Twig_Environment $env The Twig environment instance + * + * @return Twig_NodeInterface The modified node + */ + public function enterNode(Twig_NodeInterface $node, Twig_Environment $env) + { + if ($node instanceof Twig_Node_Module) { + if ($env->hasExtension('escaper') && $defaultStrategy = $env->getExtension('escaper')->getDefaultStrategy($node->getAttribute('filename'))) { + $this->defaultStrategy = $defaultStrategy; + } + } elseif ($node instanceof Twig_Node_AutoEscape) { + $this->statusStack[] = $node->getAttribute('value'); + } elseif ($node instanceof Twig_Node_Block) { + $this->statusStack[] = isset($this->blocks[$node->getAttribute('name')]) ? $this->blocks[$node->getAttribute('name')] : $this->needEscaping($env); + } + + return $node; + } + + /** + * Called after child nodes are visited. + * + * @param Twig_NodeInterface $node The node to visit + * @param Twig_Environment $env The Twig environment instance + * + * @return Twig_NodeInterface The modified node + */ + public function leaveNode(Twig_NodeInterface $node, Twig_Environment $env) + { + if ($node instanceof Twig_Node_Module) { + $this->defaultStrategy = false; + } elseif ($node instanceof Twig_Node_Expression_Filter) { + return $this->preEscapeFilterNode($node, $env); + } elseif ($node instanceof Twig_Node_Print) { + return $this->escapePrintNode($node, $env, $this->needEscaping($env)); + } + + if ($node instanceof Twig_Node_AutoEscape || $node instanceof Twig_Node_Block) { + array_pop($this->statusStack); + } elseif ($node instanceof Twig_Node_BlockReference) { + $this->blocks[$node->getAttribute('name')] = $this->needEscaping($env); + } + + return $node; + } + + protected function escapePrintNode(Twig_Node_Print $node, Twig_Environment $env, $type) + { + if (false === $type) { + return $node; + } + + $expression = $node->getNode('expr'); + + if ($this->isSafeFor($type, $expression, $env)) { + return $node; + } + + $class = get_class($node); + + return new $class( + $this->getEscaperFilter($type, $expression), + $node->getLine() + ); + } + + protected function preEscapeFilterNode(Twig_Node_Expression_Filter $filter, Twig_Environment $env) + { + $name = $filter->getNode('filter')->getAttribute('value'); + + if (false !== $f = $env->getFilter($name)) { + $type = $f->getPreEscape(); + if (null === $type) { + return $filter; + } + + $node = $filter->getNode('node'); + if ($this->isSafeFor($type, $node, $env)) { + return $filter; + } + + $filter->setNode('node', $this->getEscaperFilter($type, $node)); + + return $filter; + } + + return $filter; + } + + protected function isSafeFor($type, Twig_NodeInterface $expression, $env) + { + $safe = $this->safeAnalysis->getSafe($expression); + + if (null === $safe) { + if (null === $this->traverser) { + $this->traverser = new Twig_NodeTraverser($env, array($this->safeAnalysis)); + } + $this->traverser->traverse($expression); + $safe = $this->safeAnalysis->getSafe($expression); + } + + return in_array($type, $safe) || in_array('all', $safe); + } + + protected function needEscaping(Twig_Environment $env) + { + if (count($this->statusStack)) { + return $this->statusStack[count($this->statusStack) - 1]; + } + + return $this->defaultStrategy ? $this->defaultStrategy : false; + } + + protected function getEscaperFilter($type, Twig_NodeInterface $node) + { + $line = $node->getLine(); + $name = new Twig_Node_Expression_Constant('escape', $line); + $args = new Twig_Node(array(new Twig_Node_Expression_Constant((string) $type, $line), new Twig_Node_Expression_Constant(null, $line), new Twig_Node_Expression_Constant(true, $line))); + + return new Twig_Node_Expression_Filter($node, $name, $args, $line); + } + + /** + * {@inheritdoc} + */ + public function getPriority() + { + return 0; + } +} diff --git a/vendor/Twig/NodeVisitor/Optimizer.php b/vendor/Twig/NodeVisitor/Optimizer.php new file mode 100644 index 0000000..cbc61fc --- /dev/null +++ b/vendor/Twig/NodeVisitor/Optimizer.php @@ -0,0 +1,247 @@ + + */ +class Twig_NodeVisitor_Optimizer implements Twig_NodeVisitorInterface +{ + const OPTIMIZE_ALL = -1; + const OPTIMIZE_NONE = 0; + const OPTIMIZE_FOR = 2; + const OPTIMIZE_RAW_FILTER = 4; + const OPTIMIZE_VAR_ACCESS = 8; + + protected $loops = array(); + protected $optimizers; + protected $prependedNodes = array(); + protected $inABody = false; + + /** + * Constructor. + * + * @param integer $optimizers The optimizer mode + */ + public function __construct($optimizers = -1) + { + if (!is_int($optimizers) || $optimizers > 2) { + throw new InvalidArgumentException(sprintf('Optimizer mode "%s" is not valid.', $optimizers)); + } + + $this->optimizers = $optimizers; + } + + /** + * {@inheritdoc} + */ + public function enterNode(Twig_NodeInterface $node, Twig_Environment $env) + { + if (self::OPTIMIZE_FOR === (self::OPTIMIZE_FOR & $this->optimizers)) { + $this->enterOptimizeFor($node, $env); + } + + if (!version_compare(phpversion(), '5.4.0RC1', '>=') && self::OPTIMIZE_VAR_ACCESS === (self::OPTIMIZE_VAR_ACCESS & $this->optimizers) && !$env->isStrictVariables() && !$env->hasExtension('sandbox')) { + if ($this->inABody) { + if (!$node instanceof Twig_Node_Expression) { + if (get_class($node) !== 'Twig_Node') { + array_unshift($this->prependedNodes, array()); + } + } else { + $node = $this->optimizeVariables($node, $env); + } + } elseif ($node instanceof Twig_Node_Body) { + $this->inABody = true; + } + } + + return $node; + } + + /** + * {@inheritdoc} + */ + public function leaveNode(Twig_NodeInterface $node, Twig_Environment $env) + { + $expression = $node instanceof Twig_Node_Expression; + + if (self::OPTIMIZE_FOR === (self::OPTIMIZE_FOR & $this->optimizers)) { + $this->leaveOptimizeFor($node, $env); + } + + if (self::OPTIMIZE_RAW_FILTER === (self::OPTIMIZE_RAW_FILTER & $this->optimizers)) { + $node = $this->optimizeRawFilter($node, $env); + } + + $node = $this->optimizePrintNode($node, $env); + + if (self::OPTIMIZE_VAR_ACCESS === (self::OPTIMIZE_VAR_ACCESS & $this->optimizers) && !$env->isStrictVariables() && !$env->hasExtension('sandbox')) { + if ($node instanceof Twig_Node_Body) { + $this->inABody = false; + } elseif ($this->inABody) { + if (!$expression && get_class($node) !== 'Twig_Node' && $prependedNodes = array_shift($this->prependedNodes)) { + $nodes = array(); + foreach (array_unique($prependedNodes) as $name) { + $nodes[] = new Twig_Node_SetTemp($name, $node->getLine()); + } + + $nodes[] = $node; + $node = new Twig_Node($nodes); + } + } + } + + return $node; + } + + protected function optimizeVariables($node, $env) + { + if ('Twig_Node_Expression_Name' === get_class($node) && $node->isSimple()) { + $this->prependedNodes[0][] = $node->getAttribute('name'); + + return new Twig_Node_Expression_TempName($node->getAttribute('name'), $node->getLine()); + } + + return $node; + } + + /** + * Optimizes print nodes. + * + * It replaces: + * + * * "echo $this->render(Parent)Block()" with "$this->display(Parent)Block()" + * + * @param Twig_NodeInterface $node A Node + * @param Twig_Environment $env The current Twig environment + */ + protected function optimizePrintNode($node, $env) + { + if (!$node instanceof Twig_Node_Print) { + return $node; + } + + if ( + $node->getNode('expr') instanceof Twig_Node_Expression_BlockReference || + $node->getNode('expr') instanceof Twig_Node_Expression_Parent + ) { + $node->getNode('expr')->setAttribute('output', true); + + return $node->getNode('expr'); + } + + return $node; + } + + /** + * Removes "raw" filters. + * + * @param Twig_NodeInterface $node A Node + * @param Twig_Environment $env The current Twig environment + */ + protected function optimizeRawFilter($node, $env) + { + if ($node instanceof Twig_Node_Expression_Filter && 'raw' == $node->getNode('filter')->getAttribute('value')) { + return $node->getNode('node'); + } + + return $node; + } + + /** + * Optimizes "for" tag by removing the "loop" variable creation whenever possible. + * + * @param Twig_NodeInterface $node A Node + * @param Twig_Environment $env The current Twig environment + */ + protected function enterOptimizeFor($node, $env) + { + if ($node instanceof Twig_Node_For) { + // disable the loop variable by default + $node->setAttribute('with_loop', false); + array_unshift($this->loops, $node); + } elseif (!$this->loops) { + // we are outside a loop + return; + } + + // when do we need to add the loop variable back? + + // the loop variable is referenced for the current loop + elseif ($node instanceof Twig_Node_Expression_Name && 'loop' === $node->getAttribute('name')) { + $this->addLoopToCurrent(); + } + + // block reference + elseif ($node instanceof Twig_Node_BlockReference || $node instanceof Twig_Node_Expression_BlockReference) { + $this->addLoopToCurrent(); + } + + // include without the only attribute + elseif ($node instanceof Twig_Node_Include && !$node->getAttribute('only')) { + $this->addLoopToAll(); + } + + // the loop variable is referenced via an attribute + elseif ($node instanceof Twig_Node_Expression_GetAttr + && (!$node->getNode('attribute') instanceof Twig_Node_Expression_Constant + || 'parent' === $node->getNode('attribute')->getAttribute('value') + ) + && (true === $this->loops[0]->getAttribute('with_loop') + || ($node->getNode('node') instanceof Twig_Node_Expression_Name + && 'loop' === $node->getNode('node')->getAttribute('name') + ) + ) + ) { + $this->addLoopToAll(); + } + } + + /** + * Optimizes "for" tag by removing the "loop" variable creation whenever possible. + * + * @param Twig_NodeInterface $node A Node + * @param Twig_Environment $env The current Twig environment + */ + protected function leaveOptimizeFor($node, $env) + { + if ($node instanceof Twig_Node_For) { + array_shift($this->loops); + } + } + + protected function addLoopToCurrent() + { + $this->loops[0]->setAttribute('with_loop', true); + } + + protected function addLoopToAll() + { + foreach ($this->loops as $loop) { + $loop->setAttribute('with_loop', true); + } + } + + /** + * {@inheritdoc} + */ + public function getPriority() + { + return 255; + } +} diff --git a/vendor/Twig/NodeVisitor/SafeAnalysis.php b/vendor/Twig/NodeVisitor/SafeAnalysis.php new file mode 100644 index 0000000..9f511b5 --- /dev/null +++ b/vendor/Twig/NodeVisitor/SafeAnalysis.php @@ -0,0 +1,119 @@ +data[$hash])) { + foreach($this->data[$hash] as $bucket) { + if ($bucket['key'] === $node) { + return $bucket['value']; + } + } + } + + return null; + } + + protected function setSafe(Twig_NodeInterface $node, array $safe) + { + $hash = spl_object_hash($node); + if (isset($this->data[$hash])) { + foreach($this->data[$hash] as &$bucket) { + if ($bucket['key'] === $node) { + $bucket['value'] = $safe; + + return; + } + } + } + $this->data[$hash][] = array( + 'key' => $node, + 'value' => $safe, + ); + } + + public function enterNode(Twig_NodeInterface $node, Twig_Environment $env) + { + return $node; + } + + public function leaveNode(Twig_NodeInterface $node, Twig_Environment $env) + { + if ($node instanceof Twig_Node_Expression_Constant) { + // constants are marked safe for all + $this->setSafe($node, array('all')); + } elseif ($node instanceof Twig_Node_Expression_BlockReference) { + // blocks are safe by definition + $this->setSafe($node, array('all')); + } elseif ($node instanceof Twig_Node_Expression_Parent) { + // parent block is safe by definition + $this->setSafe($node, array('all')); + } elseif ($node instanceof Twig_Node_Expression_Conditional) { + // intersect safeness of both operands + $safe = $this->intersectSafe($this->getSafe($node->getNode('expr2')), $this->getSafe($node->getNode('expr3'))); + $this->setSafe($node, $safe); + } elseif ($node instanceof Twig_Node_Expression_Filter) { + // filter expression is safe when the filter is safe + $name = $node->getNode('filter')->getAttribute('value'); + $args = $node->getNode('arguments'); + if (false !== $filter = $env->getFilter($name)) { + $safe = $filter->getSafe($args); + if (null === $safe) { + $safe = $this->intersectSafe($this->getSafe($node->getNode('node')), $filter->getPreservesSafety()); + } + $this->setSafe($node, $safe); + } else { + $this->setSafe($node, array()); + } + } elseif ($node instanceof Twig_Node_Expression_Function) { + // function expression is safe when the function is safe + $name = $node->getAttribute('name'); + $args = $node->getNode('arguments'); + $function = $env->getFunction($name); + if (false !== $function) { + $this->setSafe($node, $function->getSafe($args)); + } else { + $this->setSafe($node, array()); + } + } elseif ($node instanceof Twig_Node_Expression_MethodCall) { + if ($node->getAttribute('safe')) { + $this->setSafe($node, array('all')); + } else { + $this->setSafe($node, array()); + } + } else { + $this->setSafe($node, array()); + } + + return $node; + } + + protected function intersectSafe(array $a = null, array $b = null) + { + if (null === $a || null === $b) { + return array(); + } + + if (in_array('all', $a)) { + return $b; + } + + if (in_array('all', $b)) { + return $a; + } + + return array_intersect($a, $b); + } + + /** + * {@inheritdoc} + */ + public function getPriority() + { + return 0; + } +} diff --git a/vendor/Twig/NodeVisitor/Sandbox.php b/vendor/Twig/NodeVisitor/Sandbox.php new file mode 100644 index 0000000..61ef0c6 --- /dev/null +++ b/vendor/Twig/NodeVisitor/Sandbox.php @@ -0,0 +1,106 @@ + + */ +class Twig_NodeVisitor_Sandbox implements Twig_NodeVisitorInterface +{ + protected $inAModule = false; + protected $tags; + protected $filters; + protected $functions; + + /** + * Called before child nodes are visited. + * + * @param Twig_NodeInterface $node The node to visit + * @param Twig_Environment $env The Twig environment instance + * + * @return Twig_NodeInterface The modified node + */ + public function enterNode(Twig_NodeInterface $node, Twig_Environment $env) + { + // in a sandbox tag, only include tags are allowed + if ($node instanceof Twig_Node_Sandbox && !$node->getNode('body') instanceof Twig_Node_Include) { + foreach ($node->getNode('body') as $n) { + if ($n instanceof Twig_Node_Text && ctype_space($n->getAttribute('data'))) { + continue; + } + + if (!$n instanceof Twig_Node_Include) { + throw new Twig_Error_Syntax('Only "include" tags are allowed within a "sandbox" section', $n->getLine()); + } + } + } + + if ($node instanceof Twig_Node_Module) { + $this->inAModule = true; + $this->tags = array(); + $this->filters = array(); + $this->functions = array(); + + return $node; + } elseif ($this->inAModule) { + // look for tags + if ($node->getNodeTag()) { + $this->tags[] = $node->getNodeTag(); + } + + // look for filters + if ($node instanceof Twig_Node_Expression_Filter) { + $this->filters[] = $node->getNode('filter')->getAttribute('value'); + } + + // look for functions + if ($node instanceof Twig_Node_Expression_Function) { + $this->functions[] = $node->getAttribute('name'); + } + + // wrap print to check __toString() calls + if ($node instanceof Twig_Node_Print) { + return new Twig_Node_SandboxedPrint($node->getNode('expr'), $node->getLine(), $node->getNodeTag()); + } + } + + return $node; + } + + /** + * Called after child nodes are visited. + * + * @param Twig_NodeInterface $node The node to visit + * @param Twig_Environment $env The Twig environment instance + * + * @return Twig_NodeInterface The modified node + */ + public function leaveNode(Twig_NodeInterface $node, Twig_Environment $env) + { + if ($node instanceof Twig_Node_Module) { + $this->inAModule = false; + + return new Twig_Node_SandboxedModule($node, array_unique($this->filters), array_unique($this->tags), array_unique($this->functions)); + } + + return $node; + } + + /** + * {@inheritdoc} + */ + public function getPriority() + { + return 0; + } +} diff --git a/vendor/Twig/NodeVisitorInterface.php b/vendor/Twig/NodeVisitorInterface.php new file mode 100644 index 0000000..e0123b5 --- /dev/null +++ b/vendor/Twig/NodeVisitorInterface.php @@ -0,0 +1,48 @@ + + */ +interface Twig_NodeVisitorInterface +{ + /** + * Called before child nodes are visited. + * + * @param Twig_NodeInterface $node The node to visit + * @param Twig_Environment $env The Twig environment instance + * + * @return Twig_NodeInterface The modified node + */ + function enterNode(Twig_NodeInterface $node, Twig_Environment $env); + + /** + * Called after child nodes are visited. + * + * @param Twig_NodeInterface $node The node to visit + * @param Twig_Environment $env The Twig environment instance + * + * @return Twig_NodeInterface The modified node + */ + function leaveNode(Twig_NodeInterface $node, Twig_Environment $env); + + /** + * Returns the priority for this visitor. + * + * Priority should be between -10 and 10 (0 is the default). + * + * @return integer The priority level + */ + function getPriority(); +} diff --git a/vendor/Twig/Parser.php b/vendor/Twig/Parser.php new file mode 100644 index 0000000..0bfcec7 --- /dev/null +++ b/vendor/Twig/Parser.php @@ -0,0 +1,384 @@ + + */ +class Twig_Parser implements Twig_ParserInterface +{ + protected $stack = array(); + protected $stream; + protected $parent; + protected $handlers; + protected $visitors; + protected $expressionParser; + protected $blocks; + protected $blockStack; + protected $macros; + protected $env; + protected $reservedMacroNames; + protected $importedFunctions; + protected $tmpVarCount; + protected $traits; + protected $embeddedTemplates = array(); + + /** + * Constructor. + * + * @param Twig_Environment $env A Twig_Environment instance + */ + public function __construct(Twig_Environment $env) + { + $this->env = $env; + } + + public function getEnvironment() + { + return $this->env; + } + + public function getVarName() + { + return sprintf('__internal_%s_%d', substr($this->env->getTemplateClass($this->stream->getFilename()), strlen($this->env->getTemplateClassPrefix())), ++$this->tmpVarCount); + } + + /** + * Converts a token stream to a node tree. + * + * @param Twig_TokenStream $stream A token stream instance + * + * @return Twig_Node_Module A node tree + */ + public function parse(Twig_TokenStream $stream, $test = null, $dropNeedle = false) + { + // push all variables into the stack to keep the current state of the parser + $vars = get_object_vars($this); + unset($vars['stack'], $vars['env'], $vars['handlers'], $vars['visitors'], $vars['expressionParser']); + $this->stack[] = $vars; + + $this->tmpVarCount = 0; + + // tag handlers + if (null === $this->handlers) { + $this->handlers = $this->env->getTokenParsers(); + $this->handlers->setParser($this); + } + + // node visitors + if (null === $this->visitors) { + $this->visitors = $this->env->getNodeVisitors(); + } + + if (null === $this->expressionParser) { + $this->expressionParser = new Twig_ExpressionParser($this, $this->env->getUnaryOperators(), $this->env->getBinaryOperators()); + } + + $this->stream = $stream; + $this->parent = null; + $this->blocks = array(); + $this->macros = array(); + $this->traits = array(); + $this->blockStack = array(); + $this->importedFunctions = array(array()); + $this->embeddedTemplates = array(); + + try { + $body = $this->subparse($test, $dropNeedle); + + if (null !== $this->parent) { + if (null === $body = $this->filterBodyNodes($body)) { + $body = new Twig_Node(); + } + } + } catch (Twig_Error_Syntax $e) { + if (null === $e->getTemplateFile()) { + $e->setTemplateFile($this->stream->getFilename()); + } + + throw $e; + } + + $node = new Twig_Node_Module(new Twig_Node_Body(array($body)), $this->parent, new Twig_Node($this->blocks), new Twig_Node($this->macros), new Twig_Node($this->traits), $this->embeddedTemplates, $this->stream->getFilename()); + + $traverser = new Twig_NodeTraverser($this->env, $this->visitors); + + $node = $traverser->traverse($node); + + // restore previous stack so previous parse() call can resume working + foreach (array_pop($this->stack) as $key => $val) { + $this->$key = $val; + } + + return $node; + } + + public function subparse($test, $dropNeedle = false) + { + $lineno = $this->getCurrentToken()->getLine(); + $rv = array(); + while (!$this->stream->isEOF()) { + switch ($this->getCurrentToken()->getType()) { + case Twig_Token::TEXT_TYPE: + $token = $this->stream->next(); + $rv[] = new Twig_Node_Text($token->getValue(), $token->getLine()); + break; + + case Twig_Token::VAR_START_TYPE: + $token = $this->stream->next(); + $expr = $this->expressionParser->parseExpression(); + $this->stream->expect(Twig_Token::VAR_END_TYPE); + $rv[] = new Twig_Node_Print($expr, $token->getLine()); + break; + + case Twig_Token::BLOCK_START_TYPE: + $this->stream->next(); + $token = $this->getCurrentToken(); + + if ($token->getType() !== Twig_Token::NAME_TYPE) { + throw new Twig_Error_Syntax('A block must start with a tag name', $token->getLine(), $this->stream->getFilename()); + } + + if (null !== $test && call_user_func($test, $token)) { + if ($dropNeedle) { + $this->stream->next(); + } + + if (1 === count($rv)) { + return $rv[0]; + } + + return new Twig_Node($rv, array(), $lineno); + } + + $subparser = $this->handlers->getTokenParser($token->getValue()); + if (null === $subparser) { + if (null !== $test) { + throw new Twig_Error_Syntax(sprintf('Unexpected tag name "%s" (expecting closing tag for the "%s" tag defined near line %s)', $token->getValue(), $test[0]->getTag(), $lineno), $token->getLine(), $this->stream->getFilename()); + } + + $message = sprintf('Unknown tag name "%s"', $token->getValue()); + if ($alternatives = $this->env->computeAlternatives($token->getValue(), array_keys($this->env->getTags()))) { + $message = sprintf('%s. Did you mean "%s"', $message, implode('", "', $alternatives)); + } + + throw new Twig_Error_Syntax($message, $token->getLine(), $this->stream->getFilename()); + } + + $this->stream->next(); + + $node = $subparser->parse($token); + if (null !== $node) { + $rv[] = $node; + } + break; + + default: + throw new Twig_Error_Syntax('Lexer or parser ended up in unsupported state.', -1, $this->stream->getFilename()); + } + } + + if (1 === count($rv)) { + return $rv[0]; + } + + return new Twig_Node($rv, array(), $lineno); + } + + public function addHandler($name, $class) + { + $this->handlers[$name] = $class; + } + + public function addNodeVisitor(Twig_NodeVisitorInterface $visitor) + { + $this->visitors[] = $visitor; + } + + public function getBlockStack() + { + return $this->blockStack; + } + + public function peekBlockStack() + { + return $this->blockStack[count($this->blockStack) - 1]; + } + + public function popBlockStack() + { + array_pop($this->blockStack); + } + + public function pushBlockStack($name) + { + $this->blockStack[] = $name; + } + + public function hasBlock($name) + { + return isset($this->blocks[$name]); + } + + public function getBlock($name) + { + return $this->blocks[$name]; + } + + public function setBlock($name, $value) + { + $this->blocks[$name] = new Twig_Node_Body(array($value), array(), $value->getLine()); + } + + public function hasMacro($name) + { + return isset($this->macros[$name]); + } + + public function setMacro($name, Twig_Node_Macro $node) + { + if (null === $this->reservedMacroNames) { + $this->reservedMacroNames = array(); + $r = new ReflectionClass($this->env->getBaseTemplateClass()); + foreach ($r->getMethods() as $method) { + $this->reservedMacroNames[] = $method->getName(); + } + } + + if (in_array($name, $this->reservedMacroNames)) { + throw new Twig_Error_Syntax(sprintf('"%s" cannot be used as a macro name as it is a reserved keyword', $name), $node->getLine()); + } + + $this->macros[$name] = $node; + } + + public function addTrait($trait) + { + $this->traits[] = $trait; + } + + public function hasTraits() + { + return count($this->traits) > 0; + } + + public function embedTemplate(Twig_Node_Module $template) + { + $template->setIndex(count($this->embeddedTemplates) + 1); + + $this->embeddedTemplates[] = $template; + } + + public function addImportedFunction($alias, $name, Twig_Node_Expression $node) + { + $this->importedFunctions[0][$alias] = array('name' => $name, 'node' => $node); + } + + public function getImportedFunction($alias) + { + foreach ($this->importedFunctions as $functions) { + if (isset($functions[$alias])) { + return $functions[$alias]; + } + } + } + + public function isMainScope() + { + return 1 === count($this->importedFunctions); + } + + public function pushLocalScope() + { + array_unshift($this->importedFunctions, array()); + } + + public function popLocalScope() + { + array_shift($this->importedFunctions); + } + + /** + * Gets the expression parser. + * + * @return Twig_ExpressionParser The expression parser + */ + public function getExpressionParser() + { + return $this->expressionParser; + } + + public function getParent() + { + return $this->parent; + } + + public function setParent($parent) + { + $this->parent = $parent; + } + + /** + * Gets the token stream. + * + * @return Twig_TokenStream The token stream + */ + public function getStream() + { + return $this->stream; + } + + /** + * Gets the current token. + * + * @return Twig_Token The current token + */ + public function getCurrentToken() + { + return $this->stream->getCurrent(); + } + + protected function filterBodyNodes(Twig_NodeInterface $node) + { + // check that the body does not contain non-empty output nodes + if ( + ($node instanceof Twig_Node_Text && !ctype_space($node->getAttribute('data'))) + || + (!$node instanceof Twig_Node_Text && !$node instanceof Twig_Node_BlockReference && $node instanceof Twig_NodeOutputInterface) + ) { + if (false !== strpos((string) $node, chr(0xEF).chr(0xBB).chr(0xBF))) { + throw new Twig_Error_Syntax('A template that extends another one cannot have a body but a byte order mark (BOM) has been detected; it must be removed.', $node->getLine(), $this->stream->getFilename()); + } + + throw new Twig_Error_Syntax('A template that extends another one cannot have a body.', $node->getLine(), $this->stream->getFilename()); + } + + // bypass "set" nodes as they "capture" the output + if ($node instanceof Twig_Node_Set) { + return $node; + } + + if ($node instanceof Twig_NodeOutputInterface) { + return; + } + + foreach ($node as $k => $n) { + if (null !== $n && null === $n = $this->filterBodyNodes($n)) { + $node->removeNode($k); + } + } + + return $node; + } +} diff --git a/vendor/Twig/ParserInterface.php b/vendor/Twig/ParserInterface.php new file mode 100644 index 0000000..4cb6d28 --- /dev/null +++ b/vendor/Twig/ParserInterface.php @@ -0,0 +1,28 @@ + + */ +interface Twig_ParserInterface +{ + /** + * Converts a token stream to a node tree. + * + * @param Twig_TokenStream $stream A token stream instance + * + * @return Twig_Node_Module A node tree + */ + function parse(Twig_TokenStream $stream); +} diff --git a/vendor/Twig/Sandbox/SecurityError.php b/vendor/Twig/Sandbox/SecurityError.php new file mode 100644 index 0000000..debabb7 --- /dev/null +++ b/vendor/Twig/Sandbox/SecurityError.php @@ -0,0 +1,20 @@ + + */ +class Twig_Sandbox_SecurityError extends Twig_Error +{ +} diff --git a/vendor/Twig/Sandbox/SecurityPolicy.php b/vendor/Twig/Sandbox/SecurityPolicy.php new file mode 100644 index 0000000..ba912ef --- /dev/null +++ b/vendor/Twig/Sandbox/SecurityPolicy.php @@ -0,0 +1,120 @@ + + */ +class Twig_Sandbox_SecurityPolicy implements Twig_Sandbox_SecurityPolicyInterface +{ + protected $allowedTags; + protected $allowedFilters; + protected $allowedMethods; + protected $allowedProperties; + protected $allowedFunctions; + + public function __construct(array $allowedTags = array(), array $allowedFilters = array(), array $allowedMethods = array(), array $allowedProperties = array(), array $allowedFunctions = array()) + { + $this->allowedTags = $allowedTags; + $this->allowedFilters = $allowedFilters; + $this->setAllowedMethods($allowedMethods); + $this->allowedProperties = $allowedProperties; + $this->allowedFunctions = $allowedFunctions; + } + + public function setAllowedTags(array $tags) + { + $this->allowedTags = $tags; + } + + public function setAllowedFilters(array $filters) + { + $this->allowedFilters = $filters; + } + + public function setAllowedMethods(array $methods) + { + $this->allowedMethods = array(); + foreach ($methods as $class => $m) { + $this->allowedMethods[$class] = array_map('strtolower', is_array($m) ? $m : array($m)); + } + } + + public function setAllowedProperties(array $properties) + { + $this->allowedProperties = $properties; + } + + public function setAllowedFunctions(array $functions) + { + $this->allowedFunctions = $functions; + } + + public function checkSecurity($tags, $filters, $functions) + { + foreach ($tags as $tag) { + if (!in_array($tag, $this->allowedTags)) { + throw new Twig_Sandbox_SecurityError(sprintf('Tag "%s" is not allowed.', $tag)); + } + } + + foreach ($filters as $filter) { + if (!in_array($filter, $this->allowedFilters)) { + throw new Twig_Sandbox_SecurityError(sprintf('Filter "%s" is not allowed.', $filter)); + } + } + + foreach ($functions as $function) { + if (!in_array($function, $this->allowedFunctions)) { + throw new Twig_Sandbox_SecurityError(sprintf('Function "%s" is not allowed.', $function)); + } + } + } + + public function checkMethodAllowed($obj, $method) + { + if ($obj instanceof Twig_TemplateInterface || $obj instanceof Twig_Markup) { + return true; + } + + $allowed = false; + $method = strtolower($method); + foreach ($this->allowedMethods as $class => $methods) { + if ($obj instanceof $class) { + $allowed = in_array($method, $methods); + + break; + } + } + + if (!$allowed) { + throw new Twig_Sandbox_SecurityError(sprintf('Calling "%s" method on a "%s" object is not allowed.', $method, get_class($obj))); + } + } + + public function checkPropertyAllowed($obj, $property) + { + $allowed = false; + foreach ($this->allowedProperties as $class => $properties) { + if ($obj instanceof $class) { + $allowed = in_array($property, is_array($properties) ? $properties : array($properties)); + + break; + } + } + + if (!$allowed) { + throw new Twig_Sandbox_SecurityError(sprintf('Calling "%s" property on a "%s" object is not allowed.', $property, get_class($obj))); + } + } +} diff --git a/vendor/Twig/Sandbox/SecurityPolicyInterface.php b/vendor/Twig/Sandbox/SecurityPolicyInterface.php new file mode 100644 index 0000000..d5015af --- /dev/null +++ b/vendor/Twig/Sandbox/SecurityPolicyInterface.php @@ -0,0 +1,25 @@ + + */ +interface Twig_Sandbox_SecurityPolicyInterface +{ + function checkSecurity($tags, $filters, $functions); + + function checkMethodAllowed($obj, $method); + + function checkPropertyAllowed($obj, $method); +} diff --git a/vendor/Twig/Template.php b/vendor/Twig/Template.php new file mode 100644 index 0000000..9357781 --- /dev/null +++ b/vendor/Twig/Template.php @@ -0,0 +1,450 @@ + + */ +abstract class Twig_Template implements Twig_TemplateInterface +{ + static protected $cache = array(); + + protected $parent; + protected $parents; + protected $env; + protected $blocks; + protected $traits; + + /** + * Constructor. + * + * @param Twig_Environment $env A Twig_Environment instance + */ + public function __construct(Twig_Environment $env) + { + $this->env = $env; + $this->blocks = array(); + $this->traits = array(); + } + + /** + * Returns the template name. + * + * @return string The template name + */ + abstract public function getTemplateName(); + + /** + * {@inheritdoc} + */ + public function getEnvironment() + { + return $this->env; + } + + /** + * Returns the parent template. + * + * This method is for internal use only and should never be called + * directly. + * + * @return Twig_TemplateInterface|false The parent template or false if there is no parent + */ + public function getParent(array $context) + { + if (null !== $this->parent) { + return $this->parent; + } + + $parent = $this->doGetParent($context); + if (false === $parent) { + return false; + } elseif ($parent instanceof Twig_Template) { + $name = $parent->getTemplateName(); + $this->parents[$name] = $parent; + $parent = $name; + } elseif (!isset($this->parents[$parent])) { + $this->parents[$parent] = $this->env->loadTemplate($parent); + } + + return $this->parents[$parent]; + } + + protected function doGetParent(array $context) + { + return false; + } + + public function isTraitable() + { + return true; + } + + /** + * Displays a parent block. + * + * This method is for internal use only and should never be called + * directly. + * + * @param string $name The block name to display from the parent + * @param array $context The context + * @param array $blocks The current set of blocks + */ + public function displayParentBlock($name, array $context, array $blocks = array()) + { + $name = (string) $name; + + if (isset($this->traits[$name])) { + $this->traits[$name][0]->displayBlock($name, $context, $blocks); + } elseif (false !== $parent = $this->getParent($context)) { + $parent->displayBlock($name, $context, $blocks); + } else { + throw new Twig_Error_Runtime(sprintf('The template has no parent and no traits defining the "%s" block', $name), -1, $this->getTemplateName()); + } + } + + /** + * Displays a block. + * + * This method is for internal use only and should never be called + * directly. + * + * @param string $name The block name to display + * @param array $context The context + * @param array $blocks The current set of blocks + */ + public function displayBlock($name, array $context, array $blocks = array()) + { + $name = (string) $name; + + if (isset($blocks[$name])) { + $b = $blocks; + unset($b[$name]); + call_user_func($blocks[$name], $context, $b); + } elseif (isset($this->blocks[$name])) { + call_user_func($this->blocks[$name], $context, $blocks); + } elseif (false !== $parent = $this->getParent($context)) { + $parent->displayBlock($name, $context, array_merge($this->blocks, $blocks)); + } + } + + /** + * Renders a parent block. + * + * This method is for internal use only and should never be called + * directly. + * + * @param string $name The block name to render from the parent + * @param array $context The context + * @param array $blocks The current set of blocks + * + * @return string The rendered block + */ + public function renderParentBlock($name, array $context, array $blocks = array()) + { + ob_start(); + $this->displayParentBlock($name, $context, $blocks); + + return ob_get_clean(); + } + + /** + * Renders a block. + * + * This method is for internal use only and should never be called + * directly. + * + * @param string $name The block name to render + * @param array $context The context + * @param array $blocks The current set of blocks + * + * @return string The rendered block + */ + public function renderBlock($name, array $context, array $blocks = array()) + { + ob_start(); + $this->displayBlock($name, $context, $blocks); + + return ob_get_clean(); + } + + /** + * Returns whether a block exists or not. + * + * This method is for internal use only and should never be called + * directly. + * + * This method does only return blocks defined in the current template + * or defined in "used" traits. + * + * It does not return blocks from parent templates as the parent + * template name can be dynamic, which is only known based on the + * current context. + * + * @param string $name The block name + * + * @return Boolean true if the block exists, false otherwise + */ + public function hasBlock($name) + { + return isset($this->blocks[(string) $name]); + } + + /** + * Returns all block names. + * + * This method is for internal use only and should never be called + * directly. + * + * @return array An array of block names + * + * @see hasBlock + */ + public function getBlockNames() + { + return array_keys($this->blocks); + } + + /** + * Returns all blocks. + * + * This method is for internal use only and should never be called + * directly. + * + * @return array An array of blocks + * + * @see hasBlock + */ + public function getBlocks() + { + return $this->blocks; + } + + /** + * {@inheritdoc} + */ + public function display(array $context, array $blocks = array()) + { + $this->displayWithErrorHandling($this->env->mergeGlobals($context), $blocks); + } + + /** + * {@inheritdoc} + */ + public function render(array $context) + { + $level = ob_get_level(); + ob_start(); + try { + $this->display($context); + } catch (Exception $e) { + while (ob_get_level() > $level) { + ob_end_clean(); + } + + throw $e; + } + + return ob_get_clean(); + } + + protected function displayWithErrorHandling(array $context, array $blocks = array()) + { + try { + $this->doDisplay($context, $blocks); + } catch (Twig_Error $e) { + throw $e; + } catch (Exception $e) { + throw new Twig_Error_Runtime(sprintf('An exception has been thrown during the rendering of a template ("%s").', $e->getMessage()), -1, null, $e); + } + } + + /** + * Auto-generated method to display the template with the given context. + * + * @param array $context An array of parameters to pass to the template + * @param array $blocks An array of blocks to pass to the template + */ + abstract protected function doDisplay(array $context, array $blocks = array()); + + /** + * Returns a variable from the context. + * + * This method is for internal use only and should never be called + * directly. + * + * This method should not be overriden in a sub-class as this is an + * implementation detail that has been introduced to optimize variable + * access for versions of PHP before 5.4. This is not a way to override + * the way to get a variable value. + * + * @param array $context The context + * @param string $item The variable to return from the context + * @param Boolean $ignoreStrictCheck Whether to ignore the strict variable check or not + * + * @return The content of the context variable + * + * @throws Twig_Error_Runtime if the variable does not exist and Twig is running in strict mode + */ + final protected function getContext($context, $item, $ignoreStrictCheck = false) + { + if (!array_key_exists($item, $context)) { + if ($ignoreStrictCheck || !$this->env->isStrictVariables()) { + return null; + } + + throw new Twig_Error_Runtime(sprintf('Variable "%s" does not exist', $item)); + } + + return $context[$item]; + } + + /** + * Returns the attribute value for a given array/object. + * + * @param mixed $object The object or array from where to get the item + * @param mixed $item The item to get from the array or object + * @param array $arguments An array of arguments to pass if the item is an object method + * @param string $type The type of attribute (@see Twig_TemplateInterface) + * @param Boolean $isDefinedTest Whether this is only a defined check + * @param Boolean $ignoreStrictCheck Whether to ignore the strict attribute check or not + * + * @return mixed The attribute value, or a Boolean when $isDefinedTest is true, or null when the attribute is not set and $ignoreStrictCheck is true + * + * @throws Twig_Error_Runtime if the attribute does not exist and Twig is running in strict mode and $isDefinedTest is false + */ + protected function getAttribute($object, $item, array $arguments = array(), $type = Twig_TemplateInterface::ANY_CALL, $isDefinedTest = false, $ignoreStrictCheck = false) + { + $item = (string) $item; + + // array + if (Twig_TemplateInterface::METHOD_CALL !== $type) { + if ((is_array($object) && array_key_exists($item, $object)) + || ($object instanceof ArrayAccess && isset($object[$item])) + ) { + if ($isDefinedTest) { + return true; + } + + return $object[$item]; + } + + if (Twig_TemplateInterface::ARRAY_CALL === $type) { + if ($isDefinedTest) { + return false; + } + + if ($ignoreStrictCheck || !$this->env->isStrictVariables()) { + return null; + } + + if (is_object($object)) { + throw new Twig_Error_Runtime(sprintf('Key "%s" in object (with ArrayAccess) of type "%s" does not exist', $item, get_class($object))); + } elseif (is_array($object)) { + throw new Twig_Error_Runtime(sprintf('Key "%s" for array with keys "%s" does not exist', $item, implode(', ', array_keys($object)))); + } else { + throw new Twig_Error_Runtime(sprintf('Impossible to access a key ("%s") on a "%s" variable', $item, gettype($object))); + } + } + } + + if (!is_object($object)) { + if ($isDefinedTest) { + return false; + } + + if ($ignoreStrictCheck || !$this->env->isStrictVariables()) { + return null; + } + + throw new Twig_Error_Runtime(sprintf('Item "%s" for "%s" does not exist', $item, is_array($object) ? 'Array' : $object)); + } + + $class = get_class($object); + + // object property + if (Twig_TemplateInterface::METHOD_CALL !== $type) { + /* apparently, this is not needed as this is already covered by the array_key_exists() call below + if (!isset(self::$cache[$class]['properties'])) { + foreach (get_object_vars($object) as $k => $v) { + self::$cache[$class]['properties'][$k] = true; + } + } + */ + + if (isset($object->$item) || array_key_exists($item, $object)) { + if ($isDefinedTest) { + return true; + } + + if ($this->env->hasExtension('sandbox')) { + $this->env->getExtension('sandbox')->checkPropertyAllowed($object, $item); + } + + return $object->$item; + } + } + + // object method + if (!isset(self::$cache[$class]['methods'])) { + self::$cache[$class]['methods'] = array_change_key_case(array_flip(get_class_methods($object))); + } + + $lcItem = strtolower($item); + if (isset(self::$cache[$class]['methods'][$lcItem])) { + $method = $item; + } elseif (isset(self::$cache[$class]['methods']['get'.$lcItem])) { + $method = 'get'.$item; + } elseif (isset(self::$cache[$class]['methods']['is'.$lcItem])) { + $method = 'is'.$item; + } elseif (isset(self::$cache[$class]['methods']['__call'])) { + $method = $item; + } else { + if ($isDefinedTest) { + return false; + } + + if ($ignoreStrictCheck || !$this->env->isStrictVariables()) { + return null; + } + + throw new Twig_Error_Runtime(sprintf('Method "%s" for object "%s" does not exist', $item, get_class($object))); + } + + if ($isDefinedTest) { + return true; + } + + if ($this->env->hasExtension('sandbox')) { + $this->env->getExtension('sandbox')->checkMethodAllowed($object, $method); + } + + $ret = call_user_func_array(array($object, $method), $arguments); + + // hack to be removed when macro calls are refactored + if ($object instanceof Twig_TemplateInterface) { + return $ret === '' ? '' : new Twig_Markup($ret, $this->env->getCharset()); + } + + return $ret; + } + + /** + * This method is only useful when testing Twig. Do not use it. + */ + static public function clearCache() + { + self::$cache = array(); + } +} diff --git a/vendor/Twig/TemplateInterface.php b/vendor/Twig/TemplateInterface.php new file mode 100644 index 0000000..08da116 --- /dev/null +++ b/vendor/Twig/TemplateInterface.php @@ -0,0 +1,47 @@ + + */ +interface Twig_TemplateInterface +{ + const ANY_CALL = 'any'; + const ARRAY_CALL = 'array'; + const METHOD_CALL = 'method'; + + /** + * Renders the template with the given context and returns it as string. + * + * @param array $context An array of parameters to pass to the template + * + * @return string The rendered template + */ + function render(array $context); + + /** + * Displays the template with the given context. + * + * @param array $context An array of parameters to pass to the template + * @param array $blocks An array of blocks to pass to the template + */ + function display(array $context, array $blocks = array()); + + /** + * Returns the bound environment for this template. + * + * @return Twig_Environment The current environment + */ + function getEnvironment(); +} diff --git a/vendor/Twig/Test/Function.php b/vendor/Twig/Test/Function.php new file mode 100644 index 0000000..1240a0f --- /dev/null +++ b/vendor/Twig/Test/Function.php @@ -0,0 +1,31 @@ + + */ +class Twig_Test_Function implements Twig_TestInterface +{ + protected $function; + + public function __construct($function) + { + $this->function = $function; + } + + public function compile() + { + return $this->function; + } +} diff --git a/vendor/Twig/Test/Method.php b/vendor/Twig/Test/Method.php new file mode 100644 index 0000000..a3b3948 --- /dev/null +++ b/vendor/Twig/Test/Method.php @@ -0,0 +1,32 @@ + + */ +class Twig_Test_Method implements Twig_TestInterface +{ + protected $extension, $method; + + public function __construct(Twig_ExtensionInterface $extension, $method) + { + $this->extension = $extension; + $this->method = $method; + } + + public function compile() + { + return sprintf('$this->env->getExtension(\'%s\')->%s', $this->extension->getName(), $this->method); + } +} diff --git a/vendor/Twig/Test/Node.php b/vendor/Twig/Test/Node.php new file mode 100644 index 0000000..47a978e --- /dev/null +++ b/vendor/Twig/Test/Node.php @@ -0,0 +1,35 @@ + + */ +class Twig_Test_Node implements Twig_TestInterface +{ + protected $class; + + public function __construct($class) + { + $this->class = $class; + } + + public function getClass() + { + return $this->class; + } + + public function compile() + { + } +} diff --git a/vendor/Twig/TestInterface.php b/vendor/Twig/TestInterface.php new file mode 100644 index 0000000..c2ff725 --- /dev/null +++ b/vendor/Twig/TestInterface.php @@ -0,0 +1,26 @@ + + */ +interface Twig_TestInterface +{ + /** + * Compiles a test. + * + * @return string The PHP code for the test + */ + function compile(); +} diff --git a/vendor/Twig/Token.php b/vendor/Twig/Token.php new file mode 100644 index 0000000..918bb91 --- /dev/null +++ b/vendor/Twig/Token.php @@ -0,0 +1,219 @@ + + */ +class Twig_Token +{ + protected $value; + protected $type; + protected $lineno; + + const EOF_TYPE = -1; + const TEXT_TYPE = 0; + const BLOCK_START_TYPE = 1; + const VAR_START_TYPE = 2; + const BLOCK_END_TYPE = 3; + const VAR_END_TYPE = 4; + const NAME_TYPE = 5; + const NUMBER_TYPE = 6; + const STRING_TYPE = 7; + const OPERATOR_TYPE = 8; + const PUNCTUATION_TYPE = 9; + const INTERPOLATION_START_TYPE = 10; + const INTERPOLATION_END_TYPE = 11; + + /** + * Constructor. + * + * @param integer $type The type of the token + * @param string $value The token value + * @param integer $lineno The line position in the source + */ + public function __construct($type, $value, $lineno) + { + $this->type = $type; + $this->value = $value; + $this->lineno = $lineno; + } + + /** + * Returns a string representation of the token. + * + * @return string A string representation of the token + */ + public function __toString() + { + return sprintf('%s(%s)', self::typeToString($this->type, true, $this->lineno), $this->value); + } + + /** + * Tests the current token for a type and/or a value. + * + * Parameters may be: + * * just type + * * type and value (or array of possible values) + * * just value (or array of possible values) (NAME_TYPE is used as type) + * + * @param array|integer $type The type to test + * @param array|string|null $values The token value + * + * @return Boolean + */ + public function test($type, $values = null) + { + if (null === $values && !is_int($type)) { + $values = $type; + $type = self::NAME_TYPE; + } + + return ($this->type === $type) && ( + null === $values || + (is_array($values) && in_array($this->value, $values)) || + $this->value == $values + ); + } + + /** + * Gets the line. + * + * @return integer The source line + */ + public function getLine() + { + return $this->lineno; + } + + /** + * Gets the token type. + * + * @return integer The token type + */ + public function getType() + { + return $this->type; + } + + /** + * Gets the token value. + * + * @return string The token value + */ + public function getValue() + { + return $this->value; + } + + /** + * Returns the constant representation (internal) of a given type. + * + * @param integer $type The type as an integer + * @param Boolean $short Whether to return a short representation or not + * @param integer $line The code line + * + * @return string The string representation + */ + static public function typeToString($type, $short = false, $line = -1) + { + switch ($type) { + case self::EOF_TYPE: + $name = 'EOF_TYPE'; + break; + case self::TEXT_TYPE: + $name = 'TEXT_TYPE'; + break; + case self::BLOCK_START_TYPE: + $name = 'BLOCK_START_TYPE'; + break; + case self::VAR_START_TYPE: + $name = 'VAR_START_TYPE'; + break; + case self::BLOCK_END_TYPE: + $name = 'BLOCK_END_TYPE'; + break; + case self::VAR_END_TYPE: + $name = 'VAR_END_TYPE'; + break; + case self::NAME_TYPE: + $name = 'NAME_TYPE'; + break; + case self::NUMBER_TYPE: + $name = 'NUMBER_TYPE'; + break; + case self::STRING_TYPE: + $name = 'STRING_TYPE'; + break; + case self::OPERATOR_TYPE: + $name = 'OPERATOR_TYPE'; + break; + case self::PUNCTUATION_TYPE: + $name = 'PUNCTUATION_TYPE'; + break; + case self::INTERPOLATION_START_TYPE: + $name = 'INTERPOLATION_START_TYPE'; + break; + case self::INTERPOLATION_END_TYPE: + $name = 'INTERPOLATION_END_TYPE'; + break; + default: + throw new Twig_Error_Syntax(sprintf('Token of type "%s" does not exist.', $type), $line); + } + + return $short ? $name : 'Twig_Token::'.$name; + } + + /** + * Returns the english representation of a given type. + * + * @param integer $type The type as an integer + * @param integer $line The code line + * + * @return string The string representation + */ + static public function typeToEnglish($type, $line = -1) + { + switch ($type) { + case self::EOF_TYPE: + return 'end of template'; + case self::TEXT_TYPE: + return 'text'; + case self::BLOCK_START_TYPE: + return 'begin of statement block'; + case self::VAR_START_TYPE: + return 'begin of print statement'; + case self::BLOCK_END_TYPE: + return 'end of statement block'; + case self::VAR_END_TYPE: + return 'end of print statement'; + case self::NAME_TYPE: + return 'name'; + case self::NUMBER_TYPE: + return 'number'; + case self::STRING_TYPE: + return 'string'; + case self::OPERATOR_TYPE: + return 'operator'; + case self::PUNCTUATION_TYPE: + return 'punctuation'; + case self::INTERPOLATION_START_TYPE: + return 'begin of string interpolation'; + case self::INTERPOLATION_END_TYPE: + return 'end of string interpolation'; + default: + throw new Twig_Error_Syntax(sprintf('Token of type "%s" does not exist.', $type), $line); + } + } +} diff --git a/vendor/Twig/TokenParser.php b/vendor/Twig/TokenParser.php new file mode 100644 index 0000000..ab18bfa --- /dev/null +++ b/vendor/Twig/TokenParser.php @@ -0,0 +1,34 @@ + + */ +abstract class Twig_TokenParser implements Twig_TokenParserInterface +{ + /** + * @var Twig_Parser + */ + protected $parser; + + /** + * Sets the parser associated with this token parser + * + * @param $parser A Twig_Parser instance + */ + public function setParser(Twig_Parser $parser) + { + $this->parser = $parser; + } +} diff --git a/vendor/Twig/TokenParser/AutoEscape.php b/vendor/Twig/TokenParser/AutoEscape.php new file mode 100644 index 0000000..0040845 --- /dev/null +++ b/vendor/Twig/TokenParser/AutoEscape.php @@ -0,0 +1,88 @@ + + * {% autoescape true %} + * Everything will be automatically escaped in this block + * {% endautoescape %} + * + * {% autoescape false %} + * Everything will be outputed as is in this block + * {% endautoescape %} + * + * {% autoescape true js %} + * Everything will be automatically escaped in this block + * using the js escaping strategy + * {% endautoescape %} + * + */ +class Twig_TokenParser_AutoEscape extends Twig_TokenParser +{ + /** + * Parses a token and returns a node. + * + * @param Twig_Token $token A Twig_Token instance + * + * @return Twig_NodeInterface A Twig_NodeInterface instance + */ + public function parse(Twig_Token $token) + { + $lineno = $token->getLine(); + + if ($this->parser->getStream()->test(Twig_Token::BLOCK_END_TYPE)) { + $value = 'html'; + } else { + $expr = $this->parser->getExpressionParser()->parseExpression(); + if (!$expr instanceof Twig_Node_Expression_Constant) { + throw new Twig_Error_Syntax('An escaping strategy must be a string or a Boolean.', $lineno); + } + $value = $expr->getAttribute('value'); + + $compat = true === $value || false === $value; + + if (true === $value) { + $value = 'html'; + } + + if ($compat && $this->parser->getStream()->test(Twig_Token::NAME_TYPE)) { + if (false === $value) { + throw new Twig_Error_Syntax('Unexpected escaping strategy as you set autoescaping to false.', $lineno); + } + + $value = $this->parser->getStream()->next()->getValue(); + } + } + + $this->parser->getStream()->expect(Twig_Token::BLOCK_END_TYPE); + $body = $this->parser->subparse(array($this, 'decideBlockEnd'), true); + $this->parser->getStream()->expect(Twig_Token::BLOCK_END_TYPE); + + return new Twig_Node_AutoEscape($value, $body, $lineno, $this->getTag()); + } + + public function decideBlockEnd(Twig_Token $token) + { + return $token->test('endautoescape'); + } + + /** + * Gets the tag name associated with this token parser. + * + * @return string The tag name + */ + public function getTag() + { + return 'autoescape'; + } +} diff --git a/vendor/Twig/TokenParser/Block.php b/vendor/Twig/TokenParser/Block.php new file mode 100644 index 0000000..994078e --- /dev/null +++ b/vendor/Twig/TokenParser/Block.php @@ -0,0 +1,83 @@ + + * {% block head %} + * + * {% block title %}{% endblock %} - My Webpage + * {% endblock %} + * + */ +class Twig_TokenParser_Block extends Twig_TokenParser +{ + /** + * Parses a token and returns a node. + * + * @param Twig_Token $token A Twig_Token instance + * + * @return Twig_NodeInterface A Twig_NodeInterface instance + */ + public function parse(Twig_Token $token) + { + $lineno = $token->getLine(); + $stream = $this->parser->getStream(); + $name = $stream->expect(Twig_Token::NAME_TYPE)->getValue(); + if ($this->parser->hasBlock($name)) { + throw new Twig_Error_Syntax(sprintf("The block '$name' has already been defined line %d", $this->parser->getBlock($name)->getLine()), $lineno); + } + $this->parser->setBlock($name, $block = new Twig_Node_Block($name, new Twig_Node(array()), $lineno)); + $this->parser->pushLocalScope(); + $this->parser->pushBlockStack($name); + + if ($stream->test(Twig_Token::BLOCK_END_TYPE)) { + $stream->next(); + + $body = $this->parser->subparse(array($this, 'decideBlockEnd'), true); + if ($stream->test(Twig_Token::NAME_TYPE)) { + $value = $stream->next()->getValue(); + + if ($value != $name) { + throw new Twig_Error_Syntax(sprintf("Expected endblock for block '$name' (but %s given)", $value), $lineno); + } + } + } else { + $body = new Twig_Node(array( + new Twig_Node_Print($this->parser->getExpressionParser()->parseExpression(), $lineno), + )); + } + $stream->expect(Twig_Token::BLOCK_END_TYPE); + + $block->setNode('body', $body); + $this->parser->popBlockStack(); + $this->parser->popLocalScope(); + + return new Twig_Node_BlockReference($name, $lineno, $this->getTag()); + } + + public function decideBlockEnd(Twig_Token $token) + { + return $token->test('endblock'); + } + + /** + * Gets the tag name associated with this token parser. + * + * @return string The tag name + */ + public function getTag() + { + return 'block'; + } +} diff --git a/vendor/Twig/TokenParser/Do.php b/vendor/Twig/TokenParser/Do.php new file mode 100644 index 0000000..593d1c6 --- /dev/null +++ b/vendor/Twig/TokenParser/Do.php @@ -0,0 +1,42 @@ +parser->getExpressionParser()->parseExpression(); + + $this->parser->getStream()->expect(Twig_Token::BLOCK_END_TYPE); + + return new Twig_Node_Do($expr, $token->getLine(), $this->getTag()); + } + + /** + * Gets the tag name associated with this token parser. + * + * @return string The tag name + */ + public function getTag() + { + return 'do'; + } +} diff --git a/vendor/Twig/TokenParser/Embed.php b/vendor/Twig/TokenParser/Embed.php new file mode 100644 index 0000000..69cb5f3 --- /dev/null +++ b/vendor/Twig/TokenParser/Embed.php @@ -0,0 +1,66 @@ +parser->getStream(); + + $parent = $this->parser->getExpressionParser()->parseExpression(); + + list($variables, $only, $ignoreMissing) = $this->parseArguments(); + + // inject a fake parent to make the parent() function work + $stream->injectTokens(array( + new Twig_Token(Twig_Token::BLOCK_START_TYPE, '', $token->getLine()), + new Twig_Token(Twig_Token::NAME_TYPE, 'extends', $token->getLine()), + new Twig_Token(Twig_Token::STRING_TYPE, '__parent__', $token->getLine()), + new Twig_Token(Twig_Token::BLOCK_END_TYPE, '', $token->getLine()), + )); + + $module = $this->parser->parse($stream, array($this, 'decideBlockEnd'), true); + + // override the parent with the correct one + $module->setNode('parent', $parent); + + $this->parser->embedTemplate($module); + + $stream->expect(Twig_Token::BLOCK_END_TYPE); + + return new Twig_Node_Embed($module->getAttribute('filename'), $module->getAttribute('index'), $variables, $only, $ignoreMissing, $token->getLine(), $this->getTag()); + } + + public function decideBlockEnd(Twig_Token $token) + { + return $token->test('endembed'); + } + + /** + * Gets the tag name associated with this token parser. + * + * @return string The tag name + */ + public function getTag() + { + return 'embed'; + } +} diff --git a/vendor/Twig/TokenParser/Extends.php b/vendor/Twig/TokenParser/Extends.php new file mode 100644 index 0000000..54f49ad --- /dev/null +++ b/vendor/Twig/TokenParser/Extends.php @@ -0,0 +1,54 @@ + + * {% extends "base.html" %} + * + */ +class Twig_TokenParser_Extends extends Twig_TokenParser +{ + /** + * Parses a token and returns a node. + * + * @param Twig_Token $token A Twig_Token instance + * + * @return Twig_NodeInterface A Twig_NodeInterface instance + */ + public function parse(Twig_Token $token) + { + if (!$this->parser->isMainScope()) { + throw new Twig_Error_Syntax('Cannot extend from a block', $token->getLine()); + } + + if (null !== $this->parser->getParent()) { + throw new Twig_Error_Syntax('Multiple extends tags are forbidden', $token->getLine()); + } + $this->parser->setParent($this->parser->getExpressionParser()->parseExpression()); + + $this->parser->getStream()->expect(Twig_Token::BLOCK_END_TYPE); + + return null; + } + + /** + * Gets the tag name associated with this token parser. + * + * @return string The tag name + */ + public function getTag() + { + return 'extends'; + } +} diff --git a/vendor/Twig/TokenParser/Filter.php b/vendor/Twig/TokenParser/Filter.php new file mode 100644 index 0000000..2b97475 --- /dev/null +++ b/vendor/Twig/TokenParser/Filter.php @@ -0,0 +1,61 @@ + + * {% filter upper %} + * This text becomes uppercase + * {% endfilter %} + * + */ +class Twig_TokenParser_Filter extends Twig_TokenParser +{ + /** + * Parses a token and returns a node. + * + * @param Twig_Token $token A Twig_Token instance + * + * @return Twig_NodeInterface A Twig_NodeInterface instance + */ + public function parse(Twig_Token $token) + { + $name = $this->parser->getVarName(); + $ref = new Twig_Node_Expression_BlockReference(new Twig_Node_Expression_Constant($name, $token->getLine()), true, $token->getLine(), $this->getTag()); + + $filter = $this->parser->getExpressionParser()->parseFilterExpressionRaw($ref, $this->getTag()); + $this->parser->getStream()->expect(Twig_Token::BLOCK_END_TYPE); + + $body = $this->parser->subparse(array($this, 'decideBlockEnd'), true); + $this->parser->getStream()->expect(Twig_Token::BLOCK_END_TYPE); + + $block = new Twig_Node_Block($name, $body, $token->getLine()); + $this->parser->setBlock($name, $block); + + return new Twig_Node_Print($filter, $token->getLine(), $this->getTag()); + } + + public function decideBlockEnd(Twig_Token $token) + { + return $token->test('endfilter'); + } + + /** + * Gets the tag name associated with this token parser. + * + * @return string The tag name + */ + public function getTag() + { + return 'filter'; + } +} diff --git a/vendor/Twig/TokenParser/Flush.php b/vendor/Twig/TokenParser/Flush.php new file mode 100644 index 0000000..4e15e78 --- /dev/null +++ b/vendor/Twig/TokenParser/Flush.php @@ -0,0 +1,42 @@ +parser->getStream()->expect(Twig_Token::BLOCK_END_TYPE); + + return new Twig_Node_Flush($token->getLine(), $this->getTag()); + } + + /** + * Gets the tag name associated with this token parser. + * + * @return string The tag name + */ + public function getTag() + { + return 'flush'; + } +} diff --git a/vendor/Twig/TokenParser/For.php b/vendor/Twig/TokenParser/For.php new file mode 100644 index 0000000..2cbb580 --- /dev/null +++ b/vendor/Twig/TokenParser/For.php @@ -0,0 +1,89 @@ + + *
    + * {% for user in users %} + *
  • {{ user.username|e }}
  • + * {% endfor %} + *
+ * + */ +class Twig_TokenParser_For extends Twig_TokenParser +{ + /** + * Parses a token and returns a node. + * + * @param Twig_Token $token A Twig_Token instance + * + * @return Twig_NodeInterface A Twig_NodeInterface instance + */ + public function parse(Twig_Token $token) + { + $lineno = $token->getLine(); + $targets = $this->parser->getExpressionParser()->parseAssignmentExpression(); + $this->parser->getStream()->expect(Twig_Token::OPERATOR_TYPE, 'in'); + $seq = $this->parser->getExpressionParser()->parseExpression(); + + $ifexpr = null; + if ($this->parser->getStream()->test(Twig_Token::NAME_TYPE, 'if')) { + $this->parser->getStream()->next(); + $ifexpr = $this->parser->getExpressionParser()->parseExpression(); + } + + $this->parser->getStream()->expect(Twig_Token::BLOCK_END_TYPE); + $body = $this->parser->subparse(array($this, 'decideForFork')); + if ($this->parser->getStream()->next()->getValue() == 'else') { + $this->parser->getStream()->expect(Twig_Token::BLOCK_END_TYPE); + $else = $this->parser->subparse(array($this, 'decideForEnd'), true); + } else { + $else = null; + } + $this->parser->getStream()->expect(Twig_Token::BLOCK_END_TYPE); + + if (count($targets) > 1) { + $keyTarget = $targets->getNode(0); + $keyTarget = new Twig_Node_Expression_AssignName($keyTarget->getAttribute('name'), $keyTarget->getLine()); + $valueTarget = $targets->getNode(1); + $valueTarget = new Twig_Node_Expression_AssignName($valueTarget->getAttribute('name'), $valueTarget->getLine()); + } else { + $keyTarget = new Twig_Node_Expression_AssignName('_key', $lineno); + $valueTarget = $targets->getNode(0); + $valueTarget = new Twig_Node_Expression_AssignName($valueTarget->getAttribute('name'), $valueTarget->getLine()); + } + + return new Twig_Node_For($keyTarget, $valueTarget, $seq, $ifexpr, $body, $else, $lineno, $this->getTag()); + } + + public function decideForFork(Twig_Token $token) + { + return $token->test(array('else', 'endfor')); + } + + public function decideForEnd(Twig_Token $token) + { + return $token->test('endfor'); + } + + /** + * Gets the tag name associated with this token parser. + * + * @return string The tag name + */ + public function getTag() + { + return 'for'; + } +} diff --git a/vendor/Twig/TokenParser/From.php b/vendor/Twig/TokenParser/From.php new file mode 100644 index 0000000..4e20f5c --- /dev/null +++ b/vendor/Twig/TokenParser/From.php @@ -0,0 +1,74 @@ + + * {% from 'forms.html' import forms %} + * + */ +class Twig_TokenParser_From extends Twig_TokenParser +{ + /** + * Parses a token and returns a node. + * + * @param Twig_Token $token A Twig_Token instance + * + * @return Twig_NodeInterface A Twig_NodeInterface instance + */ + public function parse(Twig_Token $token) + { + $macro = $this->parser->getExpressionParser()->parseExpression(); + $stream = $this->parser->getStream(); + $stream->expect('import'); + + $targets = array(); + do { + $name = $stream->expect(Twig_Token::NAME_TYPE)->getValue(); + + $alias = $name; + if ($stream->test('as')) { + $stream->next(); + + $alias = $stream->expect(Twig_Token::NAME_TYPE)->getValue(); + } + + $targets[$name] = $alias; + + if (!$stream->test(Twig_Token::PUNCTUATION_TYPE, ',')) { + break; + } + + $stream->next(); + } while (true); + + $stream->expect(Twig_Token::BLOCK_END_TYPE); + + $node = new Twig_Node_Import($macro, new Twig_Node_Expression_AssignName($this->parser->getVarName(), $token->getLine()), $token->getLine(), $this->getTag()); + + foreach($targets as $name => $alias) { + $this->parser->addImportedFunction($alias, 'get'.$name, $node->getNode('var')); + } + + return $node; + } + + /** + * Gets the tag name associated with this token parser. + * + * @return string The tag name + */ + public function getTag() + { + return 'from'; + } +} diff --git a/vendor/Twig/TokenParser/If.php b/vendor/Twig/TokenParser/If.php new file mode 100644 index 0000000..1a694af --- /dev/null +++ b/vendor/Twig/TokenParser/If.php @@ -0,0 +1,93 @@ + + * {% if users %} + *
    + * {% for user in users %} + *
  • {{ user.username|e }}
  • + * {% endfor %} + *
+ * {% endif %} + * + */ +class Twig_TokenParser_If extends Twig_TokenParser +{ + /** + * Parses a token and returns a node. + * + * @param Twig_Token $token A Twig_Token instance + * + * @return Twig_NodeInterface A Twig_NodeInterface instance + */ + public function parse(Twig_Token $token) + { + $lineno = $token->getLine(); + $expr = $this->parser->getExpressionParser()->parseExpression(); + $this->parser->getStream()->expect(Twig_Token::BLOCK_END_TYPE); + $body = $this->parser->subparse(array($this, 'decideIfFork')); + $tests = array($expr, $body); + $else = null; + + $end = false; + while (!$end) { + switch ($this->parser->getStream()->next()->getValue()) { + case 'else': + $this->parser->getStream()->expect(Twig_Token::BLOCK_END_TYPE); + $else = $this->parser->subparse(array($this, 'decideIfEnd')); + break; + + case 'elseif': + $expr = $this->parser->getExpressionParser()->parseExpression(); + $this->parser->getStream()->expect(Twig_Token::BLOCK_END_TYPE); + $body = $this->parser->subparse(array($this, 'decideIfFork')); + $tests[] = $expr; + $tests[] = $body; + break; + + case 'endif': + $end = true; + break; + + default: + throw new Twig_Error_Syntax(sprintf('Unexpected end of template. Twig was looking for the following tags "else", "elseif", or "endif" to close the "if" block started at line %d)', $lineno), -1); + } + } + + $this->parser->getStream()->expect(Twig_Token::BLOCK_END_TYPE); + + return new Twig_Node_If(new Twig_Node($tests), $else, $lineno, $this->getTag()); + } + + public function decideIfFork(Twig_Token $token) + { + return $token->test(array('elseif', 'else', 'endif')); + } + + public function decideIfEnd(Twig_Token $token) + { + return $token->test(array('endif')); + } + + /** + * Gets the tag name associated with this token parser. + * + * @return string The tag name + */ + public function getTag() + { + return 'if'; + } +} diff --git a/vendor/Twig/TokenParser/Import.php b/vendor/Twig/TokenParser/Import.php new file mode 100644 index 0000000..5219289 --- /dev/null +++ b/vendor/Twig/TokenParser/Import.php @@ -0,0 +1,47 @@ + + * {% import 'forms.html' as forms %} + * + */ +class Twig_TokenParser_Import extends Twig_TokenParser +{ + /** + * Parses a token and returns a node. + * + * @param Twig_Token $token A Twig_Token instance + * + * @return Twig_NodeInterface A Twig_NodeInterface instance + */ + public function parse(Twig_Token $token) + { + $macro = $this->parser->getExpressionParser()->parseExpression(); + $this->parser->getStream()->expect('as'); + $var = new Twig_Node_Expression_AssignName($this->parser->getStream()->expect(Twig_Token::NAME_TYPE)->getValue(), $token->getLine()); + $this->parser->getStream()->expect(Twig_Token::BLOCK_END_TYPE); + + return new Twig_Node_Import($macro, $var, $token->getLine(), $this->getTag()); + } + + /** + * Gets the tag name associated with this token parser. + * + * @return string The tag name + */ + public function getTag() + { + return 'import'; + } +} diff --git a/vendor/Twig/TokenParser/Include.php b/vendor/Twig/TokenParser/Include.php new file mode 100644 index 0000000..4a31786 --- /dev/null +++ b/vendor/Twig/TokenParser/Include.php @@ -0,0 +1,80 @@ + + * {% include 'header.html' %} + * Body + * {% include 'footer.html' %} + * + */ +class Twig_TokenParser_Include extends Twig_TokenParser +{ + /** + * Parses a token and returns a node. + * + * @param Twig_Token $token A Twig_Token instance + * + * @return Twig_NodeInterface A Twig_NodeInterface instance + */ + public function parse(Twig_Token $token) + { + $expr = $this->parser->getExpressionParser()->parseExpression(); + + list($variables, $only, $ignoreMissing) = $this->parseArguments(); + + return new Twig_Node_Include($expr, $variables, $only, $ignoreMissing, $token->getLine(), $this->getTag()); + } + + protected function parseArguments() + { + $stream = $this->parser->getStream(); + + $ignoreMissing = false; + if ($stream->test(Twig_Token::NAME_TYPE, 'ignore')) { + $stream->next(); + $stream->expect(Twig_Token::NAME_TYPE, 'missing'); + + $ignoreMissing = true; + } + + $variables = null; + if ($stream->test(Twig_Token::NAME_TYPE, 'with')) { + $stream->next(); + + $variables = $this->parser->getExpressionParser()->parseExpression(); + } + + $only = false; + if ($stream->test(Twig_Token::NAME_TYPE, 'only')) { + $stream->next(); + + $only = true; + } + + $stream->expect(Twig_Token::BLOCK_END_TYPE); + + return array($variables, $only, $ignoreMissing); + } + + /** + * Gets the tag name associated with this token parser. + * + * @return string The tag name + */ + public function getTag() + { + return 'include'; + } +} diff --git a/vendor/Twig/TokenParser/Macro.php b/vendor/Twig/TokenParser/Macro.php new file mode 100644 index 0000000..ffd5848 --- /dev/null +++ b/vendor/Twig/TokenParser/Macro.php @@ -0,0 +1,69 @@ + + * {% macro input(name, value, type, size) %} + * + * {% endmacro %} + * + */ +class Twig_TokenParser_Macro extends Twig_TokenParser +{ + /** + * Parses a token and returns a node. + * + * @param Twig_Token $token A Twig_Token instance + * + * @return Twig_NodeInterface A Twig_NodeInterface instance + */ + public function parse(Twig_Token $token) + { + $lineno = $token->getLine(); + $name = $this->parser->getStream()->expect(Twig_Token::NAME_TYPE)->getValue(); + + $arguments = $this->parser->getExpressionParser()->parseArguments(); + + $this->parser->getStream()->expect(Twig_Token::BLOCK_END_TYPE); + $this->parser->pushLocalScope(); + $body = $this->parser->subparse(array($this, 'decideBlockEnd'), true); + if ($this->parser->getStream()->test(Twig_Token::NAME_TYPE)) { + $value = $this->parser->getStream()->next()->getValue(); + + if ($value != $name) { + throw new Twig_Error_Syntax(sprintf("Expected endmacro for macro '$name' (but %s given)", $value), $lineno); + } + } + $this->parser->popLocalScope(); + $this->parser->getStream()->expect(Twig_Token::BLOCK_END_TYPE); + + $this->parser->setMacro($name, new Twig_Node_Macro($name, new Twig_Node_Body(array($body)), $arguments, $lineno, $this->getTag())); + + return null; + } + + public function decideBlockEnd(Twig_Token $token) + { + return $token->test('endmacro'); + } + + /** + * Gets the tag name associated with this token parser. + * + * @return string The tag name + */ + public function getTag() + { + return 'macro'; + } +} diff --git a/vendor/Twig/TokenParser/Sandbox.php b/vendor/Twig/TokenParser/Sandbox.php new file mode 100644 index 0000000..0277c70 --- /dev/null +++ b/vendor/Twig/TokenParser/Sandbox.php @@ -0,0 +1,55 @@ + + * {% sandbox %} + * {% include 'user.html' %} + * {% endsandbox %} + * + * + * @see http://www.twig-project.org/doc/api.html#sandbox-extension for details + */ +class Twig_TokenParser_Sandbox extends Twig_TokenParser +{ + /** + * Parses a token and returns a node. + * + * @param Twig_Token $token A Twig_Token instance + * + * @return Twig_NodeInterface A Twig_NodeInterface instance + */ + public function parse(Twig_Token $token) + { + $this->parser->getStream()->expect(Twig_Token::BLOCK_END_TYPE); + $body = $this->parser->subparse(array($this, 'decideBlockEnd'), true); + $this->parser->getStream()->expect(Twig_Token::BLOCK_END_TYPE); + + return new Twig_Node_Sandbox($body, $token->getLine(), $this->getTag()); + } + + public function decideBlockEnd(Twig_Token $token) + { + return $token->test('endsandbox'); + } + + /** + * Gets the tag name associated with this token parser. + * + * @return string The tag name + */ + public function getTag() + { + return 'sandbox'; + } +} diff --git a/vendor/Twig/TokenParser/Set.php b/vendor/Twig/TokenParser/Set.php new file mode 100644 index 0000000..3b4479c --- /dev/null +++ b/vendor/Twig/TokenParser/Set.php @@ -0,0 +1,84 @@ + + * {% set foo = 'foo' %} + * + * {% set foo = [1, 2] %} + * + * {% set foo = {'foo': 'bar'} %} + * + * {% set foo = 'foo' ~ 'bar' %} + * + * {% set foo, bar = 'foo', 'bar' %} + * + * {% set foo %}Some content{% endset %} + * + */ +class Twig_TokenParser_Set extends Twig_TokenParser +{ + /** + * Parses a token and returns a node. + * + * @param Twig_Token $token A Twig_Token instance + * + * @return Twig_NodeInterface A Twig_NodeInterface instance + */ + public function parse(Twig_Token $token) + { + $lineno = $token->getLine(); + $stream = $this->parser->getStream(); + $names = $this->parser->getExpressionParser()->parseAssignmentExpression(); + + $capture = false; + if ($stream->test(Twig_Token::OPERATOR_TYPE, '=')) { + $stream->next(); + $values = $this->parser->getExpressionParser()->parseMultitargetExpression(); + + $stream->expect(Twig_Token::BLOCK_END_TYPE); + + if (count($names) !== count($values)) { + throw new Twig_Error_Syntax("When using set, you must have the same number of variables and assignements.", $lineno); + } + } else { + $capture = true; + + if (count($names) > 1) { + throw new Twig_Error_Syntax("When using set with a block, you cannot have a multi-target.", $lineno); + } + + $stream->expect(Twig_Token::BLOCK_END_TYPE); + + $values = $this->parser->subparse(array($this, 'decideBlockEnd'), true); + $stream->expect(Twig_Token::BLOCK_END_TYPE); + } + + return new Twig_Node_Set($capture, $names, $values, $lineno, $this->getTag()); + } + + public function decideBlockEnd(Twig_Token $token) + { + return $token->test('endset'); + } + + /** + * Gets the tag name associated with this token parser. + * + * @return string The tag name + */ + public function getTag() + { + return 'set'; + } +} diff --git a/vendor/Twig/TokenParser/Spaceless.php b/vendor/Twig/TokenParser/Spaceless.php new file mode 100644 index 0000000..1e3fa8f --- /dev/null +++ b/vendor/Twig/TokenParser/Spaceless.php @@ -0,0 +1,59 @@ + + * {% spaceless %} + *
+ * foo + *
+ * {% endspaceless %} + * + * {# output will be
foo
#} + * + */ +class Twig_TokenParser_Spaceless extends Twig_TokenParser +{ + /** + * Parses a token and returns a node. + * + * @param Twig_Token $token A Twig_Token instance + * + * @return Twig_NodeInterface A Twig_NodeInterface instance + */ + public function parse(Twig_Token $token) + { + $lineno = $token->getLine(); + + $this->parser->getStream()->expect(Twig_Token::BLOCK_END_TYPE); + $body = $this->parser->subparse(array($this, 'decideSpacelessEnd'), true); + $this->parser->getStream()->expect(Twig_Token::BLOCK_END_TYPE); + + return new Twig_Node_Spaceless($body, $lineno, $this->getTag()); + } + + public function decideSpacelessEnd(Twig_Token $token) + { + return $token->test('endspaceless'); + } + + /** + * Gets the tag name associated with this token parser. + * + * @return string The tag name + */ + public function getTag() + { + return 'spaceless'; + } +} diff --git a/vendor/Twig/TokenParser/Use.php b/vendor/Twig/TokenParser/Use.php new file mode 100644 index 0000000..beafc80 --- /dev/null +++ b/vendor/Twig/TokenParser/Use.php @@ -0,0 +1,85 @@ + + * {% extends "base.html" %} + * + * {% use "blocks.html" %} + * + * {% block title %}{% endblock %} + * {% block content %}{% endblock %} + * + * + * @see http://www.twig-project.org/doc/templates.html#horizontal-reuse for details. + */ +class Twig_TokenParser_Use extends Twig_TokenParser +{ + /** + * Parses a token and returns a node. + * + * @param Twig_Token $token A Twig_Token instance + * + * @return Twig_NodeInterface A Twig_NodeInterface instance + */ + public function parse(Twig_Token $token) + { + $template = $this->parser->getExpressionParser()->parseExpression(); + + if (!$template instanceof Twig_Node_Expression_Constant) { + throw new Twig_Error_Syntax('The template references in a "use" statement must be a string.', $token->getLine()); + } + + $stream = $this->parser->getStream(); + + $targets = array(); + if ($stream->test('with')) { + $stream->next(); + + do { + $name = $stream->expect(Twig_Token::NAME_TYPE)->getValue(); + + $alias = $name; + if ($stream->test('as')) { + $stream->next(); + + $alias = $stream->expect(Twig_Token::NAME_TYPE)->getValue(); + } + + $targets[$name] = new Twig_Node_Expression_Constant($alias, -1); + + if (!$stream->test(Twig_Token::PUNCTUATION_TYPE, ',')) { + break; + } + + $stream->next(); + } while (true); + } + + $stream->expect(Twig_Token::BLOCK_END_TYPE); + + $this->parser->addTrait(new Twig_Node(array('template' => $template, 'targets' => new Twig_Node($targets)))); + + return null; + } + + /** + * Gets the tag name associated with this token parser. + * + * @return string The tag name + */ + public function getTag() + { + return 'use'; + } +} diff --git a/vendor/Twig/TokenParserBroker.php b/vendor/Twig/TokenParserBroker.php new file mode 100644 index 0000000..b214e99 --- /dev/null +++ b/vendor/Twig/TokenParserBroker.php @@ -0,0 +1,113 @@ + + */ +class Twig_TokenParserBroker implements Twig_TokenParserBrokerInterface +{ + protected $parser; + protected $parsers = array(); + protected $brokers = array(); + + /** + * Constructor. + * + * @param array|Traversable $parsers A Traversable of Twig_TokenParserInterface instances + * @param array|Traversable $brokers A Traversable of Twig_TokenParserBrokerInterface instances + */ + public function __construct($parsers = array(), $brokers = array()) + { + foreach ($parsers as $parser) { + if (!$parser instanceof Twig_TokenParserInterface) { + throw new Twig_Error('$parsers must a an array of Twig_TokenParserInterface'); + } + $this->parsers[$parser->getTag()] = $parser; + } + foreach ($brokers as $broker) { + if (!$broker instanceof Twig_TokenParserBrokerInterface) { + throw new Twig_Error('$brokers must a an array of Twig_TokenParserBrokerInterface'); + } + $this->brokers[] = $broker; + } + } + + /** + * Adds a TokenParser. + * + * @param Twig_TokenParserInterface $parser A Twig_TokenParserInterface instance + */ + public function addTokenParser(Twig_TokenParserInterface $parser) + { + $this->parsers[$parser->getTag()] = $parser; + } + + /** + * Adds a TokenParserBroker. + * + * @param Twig_TokenParserBroker $broker A Twig_TokenParserBroker instance + */ + public function addTokenParserBroker(Twig_TokenParserBroker $broker) + { + $this->brokers[] = $broker; + } + + /** + * Gets a suitable TokenParser for a tag. + * + * First looks in parsers, then in brokers. + * + * @param string $tag A tag name + * + * @return null|Twig_TokenParserInterface A Twig_TokenParserInterface or null if no suitable TokenParser was found + */ + public function getTokenParser($tag) + { + if (isset($this->parsers[$tag])) { + return $this->parsers[$tag]; + } + $broker = end($this->brokers); + while (false !== $broker) { + $parser = $broker->getTokenParser($tag); + if (null !== $parser) { + return $parser; + } + $broker = prev($this->brokers); + } + + return null; + } + + public function getParsers() + { + return $this->parsers; + } + + public function getParser() + { + return $this->parser; + } + + public function setParser(Twig_ParserInterface $parser) + { + $this->parser = $parser; + foreach ($this->parsers as $tokenParser) { + $tokenParser->setParser($parser); + } + foreach ($this->brokers as $broker) { + $broker->setParser($parser); + } + } +} diff --git a/vendor/Twig/TokenParserBrokerInterface.php b/vendor/Twig/TokenParserBrokerInterface.php new file mode 100644 index 0000000..3ce8ca2 --- /dev/null +++ b/vendor/Twig/TokenParserBrokerInterface.php @@ -0,0 +1,45 @@ + + */ +interface Twig_TokenParserBrokerInterface +{ + /** + * Gets a TokenParser suitable for a tag. + * + * @param string $tag A tag name + * + * @return null|Twig_TokenParserInterface A Twig_TokenParserInterface or null if no suitable TokenParser was found + */ + function getTokenParser($tag); + + /** + * Calls Twig_TokenParserInterface::setParser on all parsers the implementation knows of. + * + * @param Twig_ParserInterface $parser A Twig_ParserInterface interface + */ + function setParser(Twig_ParserInterface $parser); + + /** + * Gets the Twig_ParserInterface. + * + * @return null|Twig_ParserInterface A Twig_ParserInterface instance of null + */ + function getParser(); +} diff --git a/vendor/Twig/TokenParserInterface.php b/vendor/Twig/TokenParserInterface.php new file mode 100644 index 0000000..192c018 --- /dev/null +++ b/vendor/Twig/TokenParserInterface.php @@ -0,0 +1,42 @@ + + */ +interface Twig_TokenParserInterface +{ + /** + * Sets the parser associated with this token parser + * + * @param $parser A Twig_Parser instance + */ + function setParser(Twig_Parser $parser); + + /** + * Parses a token and returns a node. + * + * @param Twig_Token $token A Twig_Token instance + * + * @return Twig_NodeInterface A Twig_NodeInterface instance + */ + function parse(Twig_Token $token); + + /** + * Gets the tag name associated with this token parser. + * + * @return string The tag name + */ + function getTag(); +} diff --git a/vendor/Twig/TokenStream.php b/vendor/Twig/TokenStream.php new file mode 100644 index 0000000..5708091 --- /dev/null +++ b/vendor/Twig/TokenStream.php @@ -0,0 +1,145 @@ + + */ +class Twig_TokenStream +{ + protected $tokens; + protected $current; + protected $filename; + + /** + * Constructor. + * + * @param array $tokens An array of tokens + * @param string $filename The name of the filename which tokens are associated with + */ + public function __construct(array $tokens, $filename = null) + { + $this->tokens = $tokens; + $this->current = 0; + $this->filename = $filename; + } + + /** + * Returns a string representation of the token stream. + * + * @return string + */ + public function __toString() + { + return implode("\n", $this->tokens); + } + + public function injectTokens(array $tokens) + { + $this->tokens = array_merge(array_slice($this->tokens, 0, $this->current), $tokens, array_slice($this->tokens, $this->current)); + } + + /** + * Sets the pointer to the next token and returns the old one. + * + * @return Twig_Token + */ + public function next() + { + if (!isset($this->tokens[++$this->current])) { + throw new Twig_Error_Syntax('Unexpected end of template', -1, $this->filename); + } + + return $this->tokens[$this->current - 1]; + } + + /** + * Tests a token and returns it or throws a syntax error. + * + * @return Twig_Token + */ + public function expect($type, $value = null, $message = null) + { + $token = $this->tokens[$this->current]; + if (!$token->test($type, $value)) { + $line = $token->getLine(); + throw new Twig_Error_Syntax(sprintf('%sUnexpected token "%s" of value "%s" ("%s" expected%s)', + $message ? $message.'. ' : '', + Twig_Token::typeToEnglish($token->getType(), $line), $token->getValue(), + Twig_Token::typeToEnglish($type, $line), $value ? sprintf(' with value "%s"', $value) : ''), + $line, + $this->filename + ); + } + $this->next(); + + return $token; + } + + /** + * Looks at the next token. + * + * @param integer $number + * + * @return Twig_Token + */ + public function look($number = 1) + { + if (!isset($this->tokens[$this->current + $number])) { + throw new Twig_Error_Syntax('Unexpected end of template', -1, $this->filename); + } + + return $this->tokens[$this->current + $number]; + } + + /** + * Tests the current token + * + * @return bool + */ + public function test($primary, $secondary = null) + { + return $this->tokens[$this->current]->test($primary, $secondary); + } + + /** + * Checks if end of stream was reached + * + * @return bool + */ + public function isEOF() + { + return $this->tokens[$this->current]->getType() === Twig_Token::EOF_TYPE; + } + + /** + * Gets the current token + * + * @return Twig_Token + */ + public function getCurrent() + { + return $this->tokens[$this->current]; + } + + /** + * Gets the filename associated with this stream + * + * @return string + */ + public function getFilename() + { + return $this->filename; + } +} diff --git a/vendor/silex.phar b/vendor/silex.phar new file mode 100644 index 0000000000000000000000000000000000000000..0762d5659f784d7bfc968720b101da997fdea55c GIT binary patch literal 508736 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%0AVbcFsc4Sjac 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%0DSm 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 P{Mj#l@ry5?Z$JM(9ZNYv literal 0 HcmV?d00001 diff --git a/views/blame.twig b/views/blame.twig new file mode 100644 index 0000000..8865444 --- /dev/null +++ b/views/blame.twig @@ -0,0 +1,37 @@ +{% extends 'layout.twig' %} +{% block title %}Gitlist{% endblock %} + +{% block body %} +{% include 'navigation.twig' %} + +
+
+
+ {% include 'menu.twig' %} +
+
+ + + +
+
+
{{ file }}
+
+ + {% for commit, blame in blames %} + + + + + {% endfor %} +
{{ commit }}
{{ blame.line }}
+
+ +
+ + {% include 'footer.twig' %} +
+{% endblock %} diff --git a/views/branch_menu.twig b/views/branch_menu.twig new file mode 100644 index 0000000..4db2d52 --- /dev/null +++ b/views/branch_menu.twig @@ -0,0 +1,15 @@ +
+ + +
\ No newline at end of file diff --git a/views/commit.twig b/views/commit.twig new file mode 100644 index 0000000..6cf368e --- /dev/null +++ b/views/commit.twig @@ -0,0 +1,58 @@ +{% extends 'layout.twig' %} +{% block title %}Gitlist{% endblock %} + +{% block body %} +{% include 'navigation.twig' %} + +
+
+
+ {% include 'menu.twig' %} +
+
+ + + +
+
+ Browse code +

{{ commit.getMessage }}

+
+
+ + {{ commit.getAuthor.getName }} authored in {{ commit.getDate | date('d/m/Y \\a\\t H:i:s') }}
Showing {{ commit.getChangedFiles }} changed files
+
+
+ +
    + {% for diff in commit.getDiffs %} +
  • {{ diff.getFile }} {{ diff.getIndex }}
  • + {% endfor %} +
+ + {% for diff in commit.getDiffs %} +
+ + +
+ {% for line in diff.getLines %} + {{ line.getLine }} + {% endfor %} +
+
+ {% endfor %} + +
+ + {% include 'footer.twig' %} +
+{% endblock %} diff --git a/views/commits.twig b/views/commits.twig new file mode 100644 index 0000000..9f794f0 --- /dev/null +++ b/views/commits.twig @@ -0,0 +1,46 @@ +{% extends 'layout.twig' %} +{% block title %}Gitlist{% endblock %} + +{% block body %} +{% include 'navigation.twig' %} + +
+
+
+ {% include 'branch_menu.twig' %} + {% include 'menu.twig' %} +
+
+ + + + {% for date, commit in commits %} + + + + + + + + {% for item in commit %} + + + + + {% endfor %} + +
{{ date | date("F j, Y") }}
+ View {{ item.getShortHash }} +

{{ item.getMessage }}

+ {{ item.getAuthor.getName }} authored in {{ item.getDate | date('d/m/Y \\a\\t H:i:s') }} +
+ {% endfor %} + +
+ + {% include 'footer.twig' %} +
+{% endblock %} diff --git a/views/error.twig b/views/error.twig new file mode 100644 index 0000000..2ab344d --- /dev/null +++ b/views/error.twig @@ -0,0 +1,17 @@ +{% extends 'layout.twig' %} +{% block title %}Gitlist{% endblock %} + +{% block body %} +{% include 'navigation.twig' %} + +
+ +
+ Oops! {{ message }} +
+ +
+ + {% include 'footer.twig' %} +
+{% endblock %} diff --git a/views/file.twig b/views/file.twig new file mode 100644 index 0000000..752e008 --- /dev/null +++ b/views/file.twig @@ -0,0 +1,43 @@ +{% extends 'layout.twig' %} +{% block title %}Gitlist{% endblock %} + +{% block body %} +{% include 'navigation.twig' %} + +
+
+
+ {% include 'branch_menu.twig' %} + {% include 'menu.twig' %} +
+
+ + + +
+
+
+ + +
+ +
+ +
+ + {% include 'footer.twig' %} +
+{% endblock %} diff --git a/views/footer.twig b/views/footer.twig new file mode 100644 index 0000000..fe790c4 --- /dev/null +++ b/views/footer.twig @@ -0,0 +1,3 @@ + diff --git a/views/index.twig b/views/index.twig new file mode 100644 index 0000000..5b23f45 --- /dev/null +++ b/views/index.twig @@ -0,0 +1,25 @@ +{% extends 'layout.twig' %} +{% block title %}Gitlist{% endblock %} + +{% block body %} +{% include 'navigation.twig' %} + +
+ + {% for repository in repositories %} +
+ +
+

{{ repository.description }}

+
+
+ {% endfor %} + +
+ + {% include 'footer.twig' %} +
+{% endblock %} diff --git a/views/layout.twig b/views/layout.twig new file mode 100644 index 0000000..84d6938 --- /dev/null +++ b/views/layout.twig @@ -0,0 +1,19 @@ + + + + + {% block title %}Welcome!{% endblock %} + + + + + + {% block body %}{% endblock %} + + + + + + diff --git a/views/menu.twig b/views/menu.twig new file mode 100644 index 0000000..3a90049 --- /dev/null +++ b/views/menu.twig @@ -0,0 +1,5 @@ + \ No newline at end of file diff --git a/views/navigation.twig b/views/navigation.twig new file mode 100644 index 0000000..7c5deff --- /dev/null +++ b/views/navigation.twig @@ -0,0 +1,19 @@ + diff --git a/views/rss.twig b/views/rss.twig new file mode 100644 index 0000000..54ad521 --- /dev/null +++ b/views/rss.twig @@ -0,0 +1,17 @@ + + + + Latest commits in {{ repo }}:{{ branch }} + RSS provided by Gitlist + {{ baseurl }}/ + + {% for commit in commits %} + + {{ commit.getMessage }} + {{ commit.getAuthor.getName }} authored {{ commit.getShortHash }} in {{ commit.getDate | date('d/m/Y \\a\\t H:i:s') }} + {{ baseurl }}/{{ repo }}/commit/{{ commit.getShortHash }} + {{ commit.getDate | date('r') }} + + {% endfor %} + + \ No newline at end of file diff --git a/views/stats.twig b/views/stats.twig new file mode 100644 index 0000000..f225e5c --- /dev/null +++ b/views/stats.twig @@ -0,0 +1,61 @@ +{% extends 'layout.twig' %} +{% block title %}Gitlist{% endblock %} + +{% block body %} +{% include 'navigation.twig' %} + +
+
+
+ {% include 'branch_menu.twig' %} + {% include 'menu.twig' %} +
+
+ + + + + + + + + + + + + + + + + + +
File extensions ({{ stats.extensions|length }})Authors ({{ authors|length }})Other
+
    + {% for ext, amount in stats.extensions %} +
  • {{ ext }}: {{ amount }} files
  • + {% endfor %} +
+
+
    + {% for author in authors %} +
  • {{ author.name }}: {{ author.commits }} commits
  • + {% endfor %} +
+
+

+ Total files: {{ stats.files }} +

+ +

+ Total bytes: {{ stats.size }} bytes ({{ ((stats.size / 1024) / 1024) | number_format }} MB) +

+
+ +
+ + {% include 'footer.twig' %} +
+{% endblock %} diff --git a/views/tree.twig b/views/tree.twig new file mode 100644 index 0000000..2910086 --- /dev/null +++ b/views/tree.twig @@ -0,0 +1,57 @@ +{% extends 'layout.twig' %} +{% block title %}Gitlist{% endblock %} + +{% block body %} +{% include 'navigation.twig' %} + +
+
+
+ {% include 'branch_menu.twig' %} + {% include 'menu.twig' %} +
+
+ + + + + + + + + + + + + {% if parent is not empty %} + + + + + + {% endif %} + {% for file in files %} + + + + + + {% endfor %} + +
namemodesize
..
{{ file.name }}{{ file.mode }}{% if file.size %}{{ (file.size / 1024) | number_format }} kb{% endif %}
+ +
+ + {% include 'footer.twig' %} +
+{% endblock %} diff --git a/web/Makefile b/web/Makefile new file mode 100755 index 0000000..7bce7f7 --- /dev/null +++ b/web/Makefile @@ -0,0 +1,2 @@ +bootstrap: + lessc --compress less/bootstrap.less > css/style.css \ No newline at end of file diff --git a/web/css/style.css b/web/css/style.css new file mode 100644 index 0000000..3d2cb08 --- /dev/null +++ b/web/css/style.css @@ -0,0 +1,788 @@ +article,aside,details,figcaption,figure,footer,header,hgroup,nav,section{display:block;} +audio,canvas,video{display:inline-block;*display:inline;*zoom:1;} +audio:not([controls]){display:none;} +html{font-size:100%;-webkit-text-size-adjust:100%;-ms-text-size-adjust:100%;} +a:focus{outline:thin dotted #333;outline:5px auto -webkit-focus-ring-color;outline-offset:-2px;} +a:hover,a:active{outline:0;} +sub,sup{position:relative;font-size:75%;line-height:0;vertical-align:baseline;} +sup{top:-0.5em;} +sub{bottom:-0.25em;} +img{max-width:100%;vertical-align:middle;border:0;-ms-interpolation-mode:bicubic;} +button,input,select,textarea{margin:0;font-size:100%;vertical-align:middle;} +button,input{*overflow:visible;line-height:normal;} +button::-moz-focus-inner,input::-moz-focus-inner{padding:0;border:0;} +button,input[type="button"],input[type="reset"],input[type="submit"]{cursor:pointer;-webkit-appearance:button;} +input[type="search"]{-webkit-box-sizing:content-box;-moz-box-sizing:content-box;box-sizing:content-box;-webkit-appearance:textfield;} +input[type="search"]::-webkit-search-decoration,input[type="search"]::-webkit-search-cancel-button{-webkit-appearance:none;} +textarea{overflow:auto;vertical-align:top;} +.clearfix{*zoom:1;}.clearfix:before,.clearfix:after{display:table;content:"";} +.clearfix:after{clear:both;} +.hide-text{font:0/0 a;color:transparent;text-shadow:none;background-color:transparent;border:0;} +.input-block-level{display:block;width:100%;min-height:28px;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;-ms-box-sizing:border-box;box-sizing:border-box;} +body{margin:0;font-family:"Helvetica Neue",Helvetica,Arial,sans-serif;font-size:13px;line-height:18px;color:#333333;background-color:#ffffff;padding-top:60px;padding-bottom:40px;} +a{color:#4183c4;text-decoration:none;} +a:hover{color:#2c5d8d;text-decoration:underline;} +.row{margin-left:-20px;*zoom:1;}.row:before,.row:after{display:table;content:"";} +.row:after{clear:both;} +[class*="span"]{float:left;margin-left:20px;} +.container,.navbar-fixed-top .container,.navbar-fixed-bottom .container{width:940px;} +.span12{width:940px;} +.span11{width:860px;} +.span10{width:780px;} +.span9{width:700px;} +.span8{width:620px;} +.span7{width:540px;} +.span6{width:460px;} +.span5{width:380px;} +.span4{width:300px;} +.span3{width:220px;} +.span2{width:140px;} +.span1{width:60px;} +.offset12{margin-left:980px;} +.offset11{margin-left:900px;} +.offset10{margin-left:820px;} +.offset9{margin-left:740px;} +.offset8{margin-left:660px;} +.offset7{margin-left:580px;} +.offset6{margin-left:500px;} +.offset5{margin-left:420px;} +.offset4{margin-left:340px;} +.offset3{margin-left:260px;} +.offset2{margin-left:180px;} +.offset1{margin-left:100px;} +.row-fluid{width:100%;*zoom:1;}.row-fluid:before,.row-fluid:after{display:table;content:"";} +.row-fluid:after{clear:both;} +.row-fluid [class*="span"]{display:block;width:100%;min-height:28px;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;-ms-box-sizing:border-box;box-sizing:border-box;float:left;margin-left:2.127659574%;*margin-left:2.0744680846382977%;} +.row-fluid [class*="span"]:first-child{margin-left:0;} +.row-fluid .span12{width:99.99999998999999%;*width:99.94680850063828%;} +.row-fluid .span11{width:91.489361693%;*width:91.4361702036383%;} +.row-fluid .span10{width:82.97872339599999%;*width:82.92553190663828%;} +.row-fluid .span9{width:74.468085099%;*width:74.4148936096383%;} +.row-fluid .span8{width:65.95744680199999%;*width:65.90425531263828%;} +.row-fluid .span7{width:57.446808505%;*width:57.3936170156383%;} +.row-fluid .span6{width:48.93617020799999%;*width:48.88297871863829%;} +.row-fluid .span5{width:40.425531911%;*width:40.3723404216383%;} +.row-fluid .span4{width:31.914893614%;*width:31.8617021246383%;} +.row-fluid .span3{width:23.404255317%;*width:23.3510638276383%;} +.row-fluid .span2{width:14.89361702%;*width:14.8404255306383%;} +.row-fluid .span1{width:6.382978723%;*width:6.329787233638298%;} +.container{margin-right:auto;margin-left:auto;*zoom:1;}.container:before,.container:after{display:table;content:"";} +.container:after{clear:both;} +.container-fluid{padding-right:20px;padding-left:20px;*zoom:1;}.container-fluid:before,.container-fluid:after{display:table;content:"";} +.container-fluid:after{clear:both;} +p{margin:0 0 9px;font-family:"Helvetica Neue",Helvetica,Arial,sans-serif;font-size:13px;line-height:18px;}p small{font-size:11px;color:#999999;} +.lead{margin-bottom:18px;font-size:20px;font-weight:200;line-height:27px;} +h1,h2,h3,h4,h5,h6{margin:0;font-family:inherit;font-weight:bold;color:inherit;text-rendering:optimizelegibility;}h1 small,h2 small,h3 small,h4 small,h5 small,h6 small{font-weight:normal;color:#999999;} +h1{font-size:30px;line-height:36px;}h1 small{font-size:18px;} +h2{font-size:24px;line-height:36px;}h2 small{font-size:18px;} +h3{font-size:18px;line-height:27px;}h3 small{font-size:14px;} +h4,h5,h6{line-height:18px;} +h4{font-size:14px;}h4 small{font-size:12px;} +h5{font-size:12px;} +h6{font-size:11px;color:#999999;text-transform:uppercase;} +.page-header{padding-bottom:17px;margin:18px 0;border-bottom:1px solid #eeeeee;} +.page-header h1{line-height:1;} +ul,ol{padding:0;margin:0 0 9px 25px;} +ul ul,ul ol,ol ol,ol ul{margin-bottom:0;} +ul{list-style:disc;} +ol{list-style:decimal;} +li{line-height:18px;} +ul.unstyled,ol.unstyled{margin-left:0;list-style:none;} +dl{margin-bottom:18px;} +dt,dd{line-height:18px;} +dt{font-weight:bold;line-height:17px;} +dd{margin-left:9px;} +.dl-horizontal dt{float:left;width:120px;clear:left;text-align:right;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;} +.dl-horizontal dd{margin-left:130px;} +hr{margin:18px 0;border:0;border-top:1px solid #eeeeee;border-bottom:1px solid #ffffff;} +strong{font-weight:bold;} +em{font-style:italic;} +.muted{color:#999999;} +abbr[title]{cursor:help;border-bottom:1px dotted #ddd;} +abbr.initialism{font-size:90%;text-transform:uppercase;} +blockquote{padding:0 0 0 15px;margin:0 0 18px;border-left:5px solid #eeeeee;}blockquote p{margin-bottom:0;font-size:16px;font-weight:300;line-height:22.5px;} +blockquote small{display:block;line-height:18px;color:#999999;}blockquote small:before{content:'\2014 \00A0';} +blockquote.pull-right{float:right;padding-right:15px;padding-left:0;border-right:5px solid #eeeeee;border-left:0;}blockquote.pull-right p,blockquote.pull-right small{text-align:right;} +q:before,q:after,blockquote:before,blockquote:after{content:"";} +address{display:block;margin-bottom:18px;font-style:normal;line-height:18px;} +small{font-size:100%;} +cite{font-style:normal;} +code,pre{padding:0 3px 2px;font-family:Menlo,Monaco,Consolas,"Courier New",monospace;font-size:12px;color:#333333;-webkit-border-radius:3px;-moz-border-radius:3px;border-radius:3px;} +code{padding:2px 4px;color:#d14;background-color:#f7f7f9;border:1px solid #e1e1e8;} +pre{display:block;padding:8.5px;margin:0 0 9px;font-size:12.025px;line-height:18px;word-break:break-all;word-wrap:break-word;white-space:pre;white-space:pre-wrap;background-color:#f5f5f5;border:1px solid #ccc;border:1px solid rgba(0, 0, 0, 0.15);-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px;}pre.prettyprint{margin-bottom:18px;} +pre code{padding:0;color:inherit;background-color:transparent;border:0;} +.pre-scrollable{max-height:340px;overflow-y:scroll;} +form{margin:0 0 18px;} +fieldset{padding:0;margin:0;border:0;} +legend{display:block;width:100%;padding:0;margin-bottom:27px;font-size:19.5px;line-height:36px;color:#333333;border:0;border-bottom:1px solid #eee;}legend small{font-size:13.5px;color:#999999;} +label,input,button,select,textarea{font-size:13px;font-weight:normal;line-height:18px;} +input,button,select,textarea{font-family:"Helvetica Neue",Helvetica,Arial,sans-serif;} +label{display:block;margin-bottom:5px;color:#333333;} +input,textarea,select,.uneditable-input{display:inline-block;width:210px;height:18px;padding:4px;margin-bottom:9px;font-size:13px;line-height:18px;color:#555555;background-color:#ffffff;border:1px solid #cccccc;-webkit-border-radius:3px;-moz-border-radius:3px;border-radius:3px;} +.uneditable-textarea{width:auto;height:auto;} +label input,label textarea,label select{display:block;} +input[type="image"],input[type="checkbox"],input[type="radio"]{width:auto;height:auto;padding:0;margin:3px 0;*margin-top:0;line-height:normal;cursor:pointer;background-color:transparent;border:0 \9;-webkit-border-radius:0;-moz-border-radius:0;border-radius:0;} +input[type="image"]{border:0;} +input[type="file"]{width:auto;padding:initial;line-height:initial;background-color:#ffffff;background-color:initial;border:initial;-webkit-box-shadow:none;-moz-box-shadow:none;box-shadow:none;} +input[type="button"],input[type="reset"],input[type="submit"]{width:auto;height:auto;} +select,input[type="file"]{height:28px;*margin-top:4px;line-height:28px;} +input[type="file"]{line-height:18px \9;} +select{width:220px;background-color:#ffffff;} +select[multiple],select[size]{height:auto;} +input[type="image"]{-webkit-box-shadow:none;-moz-box-shadow:none;box-shadow:none;} +textarea{height:auto;} +input[type="hidden"]{display:none;} +.radio,.checkbox{min-height:18px;padding-left:18px;} +.radio input[type="radio"],.checkbox input[type="checkbox"]{float:left;margin-left:-18px;} +.controls>.radio:first-child,.controls>.checkbox:first-child{padding-top:5px;} +.radio.inline,.checkbox.inline{display:inline-block;padding-top:5px;margin-bottom:0;vertical-align:middle;} +.radio.inline+.radio.inline,.checkbox.inline+.checkbox.inline{margin-left:10px;} +input,textarea{-webkit-box-shadow:inset 0 1px 1px rgba(0, 0, 0, 0.075);-moz-box-shadow:inset 0 1px 1px rgba(0, 0, 0, 0.075);box-shadow:inset 0 1px 1px rgba(0, 0, 0, 0.075);-webkit-transition:border linear 0.2s,box-shadow linear 0.2s;-moz-transition:border linear 0.2s,box-shadow linear 0.2s;-ms-transition:border linear 0.2s,box-shadow linear 0.2s;-o-transition:border linear 0.2s,box-shadow linear 0.2s;transition:border linear 0.2s,box-shadow linear 0.2s;} +input:focus,textarea:focus{border-color:rgba(82, 168, 236, 0.8);outline:0;outline:thin dotted \9;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.075), 0 0 8px rgba(82,168,236,.6);-moz-box-shadow:inset 0 1px 1px rgba(0,0,0,.075), 0 0 8px rgba(82,168,236,.6);box-shadow:inset 0 1px 1px rgba(0,0,0,.075), 0 0 8px rgba(82,168,236,.6);} +input[type="file"]:focus,input[type="radio"]:focus,input[type="checkbox"]:focus,select:focus{outline:thin dotted #333;outline:5px auto -webkit-focus-ring-color;outline-offset:-2px;-webkit-box-shadow:none;-moz-box-shadow:none;box-shadow:none;} +.input-mini{width:60px;} +.input-small{width:90px;} +.input-medium{width:150px;} +.input-large{width:210px;} +.input-xlarge{width:270px;} +.input-xxlarge{width:530px;} +input[class*="span"],select[class*="span"],textarea[class*="span"],.uneditable-input[class*="span"],.row-fluid input[class*="span"],.row-fluid select[class*="span"],.row-fluid textarea[class*="span"],.row-fluid .uneditable-input[class*="span"]{float:none;margin-left:0;} +input,textarea,.uneditable-input{margin-left:0;} +input.span12, textarea.span12, .uneditable-input.span12{width:930px;} +input.span11, textarea.span11, .uneditable-input.span11{width:850px;} +input.span10, textarea.span10, .uneditable-input.span10{width:770px;} +input.span9, textarea.span9, .uneditable-input.span9{width:690px;} +input.span8, textarea.span8, .uneditable-input.span8{width:610px;} +input.span7, textarea.span7, .uneditable-input.span7{width:530px;} +input.span6, textarea.span6, .uneditable-input.span6{width:450px;} +input.span5, textarea.span5, .uneditable-input.span5{width:370px;} +input.span4, textarea.span4, .uneditable-input.span4{width:290px;} +input.span3, textarea.span3, .uneditable-input.span3{width:210px;} +input.span2, textarea.span2, .uneditable-input.span2{width:130px;} +input.span1, textarea.span1, .uneditable-input.span1{width:50px;} +input[disabled],select[disabled],textarea[disabled],input[readonly],select[readonly],textarea[readonly]{cursor:not-allowed;background-color:#eeeeee;border-color:#ddd;} +input[type="radio"][disabled],input[type="checkbox"][disabled],input[type="radio"][readonly],input[type="checkbox"][readonly]{background-color:transparent;} +.control-group.warning>label,.control-group.warning .help-block,.control-group.warning .help-inline{color:#c09853;} +.control-group.warning input,.control-group.warning select,.control-group.warning textarea{color:#c09853;border-color:#c09853;}.control-group.warning input:focus,.control-group.warning select:focus,.control-group.warning textarea:focus{border-color:#a47e3c;-webkit-box-shadow:0 0 6px #dbc59e;-moz-box-shadow:0 0 6px #dbc59e;box-shadow:0 0 6px #dbc59e;} +.control-group.warning .input-prepend .add-on,.control-group.warning .input-append .add-on{color:#c09853;background-color:#fcf8e3;border-color:#c09853;} +.control-group.error>label,.control-group.error .help-block,.control-group.error .help-inline{color:#b94a48;} +.control-group.error input,.control-group.error select,.control-group.error textarea{color:#b94a48;border-color:#b94a48;}.control-group.error input:focus,.control-group.error select:focus,.control-group.error textarea:focus{border-color:#953b39;-webkit-box-shadow:0 0 6px #d59392;-moz-box-shadow:0 0 6px #d59392;box-shadow:0 0 6px #d59392;} +.control-group.error .input-prepend .add-on,.control-group.error .input-append .add-on{color:#b94a48;background-color:#f2dede;border-color:#b94a48;} +.control-group.success>label,.control-group.success .help-block,.control-group.success .help-inline{color:#468847;} +.control-group.success input,.control-group.success select,.control-group.success textarea{color:#468847;border-color:#468847;}.control-group.success input:focus,.control-group.success select:focus,.control-group.success textarea:focus{border-color:#356635;-webkit-box-shadow:0 0 6px #7aba7b;-moz-box-shadow:0 0 6px #7aba7b;box-shadow:0 0 6px #7aba7b;} +.control-group.success .input-prepend .add-on,.control-group.success .input-append .add-on{color:#468847;background-color:#dff0d8;border-color:#468847;} +input:focus:required:invalid,textarea:focus:required:invalid,select:focus:required:invalid{color:#b94a48;border-color:#ee5f5b;}input:focus:required:invalid:focus,textarea:focus:required:invalid:focus,select:focus:required:invalid:focus{border-color:#e9322d;-webkit-box-shadow:0 0 6px #f8b9b7;-moz-box-shadow:0 0 6px #f8b9b7;box-shadow:0 0 6px #f8b9b7;} +.form-actions{padding:17px 20px 18px;margin-top:18px;margin-bottom:18px;background-color:#f5f5f5;border-top:1px solid #ddd;*zoom:1;}.form-actions:before,.form-actions:after{display:table;content:"";} +.form-actions:after{clear:both;} +.uneditable-input{overflow:hidden;white-space:nowrap;cursor:not-allowed;background-color:#ffffff;border-color:#eee;-webkit-box-shadow:inset 0 1px 2px rgba(0, 0, 0, 0.025);-moz-box-shadow:inset 0 1px 2px rgba(0, 0, 0, 0.025);box-shadow:inset 0 1px 2px rgba(0, 0, 0, 0.025);} +:-moz-placeholder{color:#999999;} +::-webkit-input-placeholder{color:#999999;} +.help-block,.help-inline{color:#555555;} +.help-block{display:block;margin-bottom:9px;} +.help-inline{display:inline-block;*display:inline;*zoom:1;vertical-align:middle;padding-left:5px;} +.input-prepend,.input-append{margin-bottom:5px;}.input-prepend input,.input-append input,.input-prepend select,.input-append select,.input-prepend .uneditable-input,.input-append .uneditable-input{position:relative;margin-bottom:0;*margin-left:0;vertical-align:middle;-webkit-border-radius:0 3px 3px 0;-moz-border-radius:0 3px 3px 0;border-radius:0 3px 3px 0;}.input-prepend input:focus,.input-append input:focus,.input-prepend select:focus,.input-append select:focus,.input-prepend .uneditable-input:focus,.input-append .uneditable-input:focus{z-index:2;} +.input-prepend .uneditable-input,.input-append .uneditable-input{border-left-color:#ccc;} +.input-prepend .add-on,.input-append .add-on{display:inline-block;width:auto;height:18px;min-width:16px;padding:4px 5px;font-weight:normal;line-height:18px;text-align:center;text-shadow:0 1px 0 #ffffff;vertical-align:middle;background-color:#eeeeee;border:1px solid #ccc;} +.input-prepend .add-on,.input-append .add-on,.input-prepend .btn,.input-append .btn{margin-left:-1px;-webkit-border-radius:0;-moz-border-radius:0;border-radius:0;} +.input-prepend .active,.input-append .active{background-color:#a9dba9;border-color:#46a546;} +.input-prepend .add-on,.input-prepend .btn{margin-right:-1px;} +.input-prepend .add-on:first-child,.input-prepend .btn:first-child{-webkit-border-radius:3px 0 0 3px;-moz-border-radius:3px 0 0 3px;border-radius:3px 0 0 3px;} +.input-append input,.input-append select,.input-append .uneditable-input{-webkit-border-radius:3px 0 0 3px;-moz-border-radius:3px 0 0 3px;border-radius:3px 0 0 3px;} +.input-append .uneditable-input{border-right-color:#ccc;border-left-color:#eee;} +.input-append .add-on:last-child,.input-append .btn:last-child{-webkit-border-radius:0 3px 3px 0;-moz-border-radius:0 3px 3px 0;border-radius:0 3px 3px 0;} +.input-prepend.input-append input,.input-prepend.input-append select,.input-prepend.input-append .uneditable-input{-webkit-border-radius:0;-moz-border-radius:0;border-radius:0;} +.input-prepend.input-append .add-on:first-child,.input-prepend.input-append .btn:first-child{margin-right:-1px;-webkit-border-radius:3px 0 0 3px;-moz-border-radius:3px 0 0 3px;border-radius:3px 0 0 3px;} +.input-prepend.input-append .add-on:last-child,.input-prepend.input-append .btn:last-child{margin-left:-1px;-webkit-border-radius:0 3px 3px 0;-moz-border-radius:0 3px 3px 0;border-radius:0 3px 3px 0;} +.search-query{padding-right:14px;padding-right:4px \9;padding-left:14px;padding-left:4px \9;margin-bottom:0;-webkit-border-radius:14px;-moz-border-radius:14px;border-radius:14px;} +.form-search input,.form-inline input,.form-horizontal input,.form-search textarea,.form-inline textarea,.form-horizontal textarea,.form-search select,.form-inline select,.form-horizontal select,.form-search .help-inline,.form-inline .help-inline,.form-horizontal .help-inline,.form-search .uneditable-input,.form-inline .uneditable-input,.form-horizontal .uneditable-input,.form-search .input-prepend,.form-inline .input-prepend,.form-horizontal .input-prepend,.form-search .input-append,.form-inline .input-append,.form-horizontal .input-append{display:inline-block;*display:inline;*zoom:1;margin-bottom:0;} +.form-search .hide,.form-inline .hide,.form-horizontal .hide{display:none;} +.form-search label,.form-inline label{display:inline-block;} +.form-search .input-append,.form-inline .input-append,.form-search .input-prepend,.form-inline .input-prepend{margin-bottom:0;} +.form-search .radio,.form-search .checkbox,.form-inline .radio,.form-inline .checkbox{padding-left:0;margin-bottom:0;vertical-align:middle;} +.form-search .radio input[type="radio"],.form-search .checkbox input[type="checkbox"],.form-inline .radio input[type="radio"],.form-inline .checkbox input[type="checkbox"]{float:left;margin-right:3px;margin-left:0;} +.control-group{margin-bottom:9px;} +legend+.control-group{margin-top:18px;-webkit-margin-top-collapse:separate;} +.form-horizontal .control-group{margin-bottom:18px;*zoom:1;}.form-horizontal .control-group:before,.form-horizontal .control-group:after{display:table;content:"";} +.form-horizontal .control-group:after{clear:both;} +.form-horizontal .control-label{float:left;width:140px;padding-top:5px;text-align:right;} +.form-horizontal .controls{*display:inline-block;*padding-left:20px;margin-left:160px;*margin-left:0;}.form-horizontal .controls:first-child{*padding-left:160px;} +.form-horizontal .help-block{margin-top:9px;margin-bottom:0;} +.form-horizontal .form-actions{padding-left:160px;} +table{max-width:100%;background-color:transparent;border-collapse:collapse;border-spacing:0;} +.table{width:100%;margin-bottom:18px;}.table th,.table td{padding:8px;line-height:18px;text-align:left;vertical-align:top;border-top:1px solid #dddddd;} +.table th{font-weight:bold;} +.table thead th{vertical-align:bottom;} +.table caption+thead tr:first-child th,.table caption+thead tr:first-child td,.table colgroup+thead tr:first-child th,.table colgroup+thead tr:first-child td,.table thead:first-child tr:first-child th,.table thead:first-child tr:first-child td{border-top:0;} +.table tbody+tbody{border-top:2px solid #dddddd;} +.table-condensed th,.table-condensed td{padding:4px 5px;} +.table-bordered{border:1px solid #dddddd;border-collapse:separate;*border-collapse:collapsed;border-left:0;-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px;}.table-bordered th,.table-bordered td{border-left:1px solid #dddddd;} +.table-bordered caption+thead tr:first-child th,.table-bordered caption+tbody tr:first-child th,.table-bordered caption+tbody tr:first-child td,.table-bordered colgroup+thead tr:first-child th,.table-bordered colgroup+tbody tr:first-child th,.table-bordered colgroup+tbody tr:first-child td,.table-bordered thead:first-child tr:first-child th,.table-bordered tbody:first-child tr:first-child th,.table-bordered tbody:first-child tr:first-child td{border-top:0;} +.table-bordered thead:first-child tr:first-child th:first-child,.table-bordered tbody:first-child tr:first-child td:first-child{-webkit-border-top-left-radius:4px;border-top-left-radius:4px;-moz-border-radius-topleft:4px;} +.table-bordered thead:first-child tr:first-child th:last-child,.table-bordered tbody:first-child tr:first-child td:last-child{-webkit-border-top-right-radius:4px;border-top-right-radius:4px;-moz-border-radius-topright:4px;} +.table-bordered thead:last-child tr:last-child th:first-child,.table-bordered tbody:last-child tr:last-child td:first-child{-webkit-border-radius:0 0 0 4px;-moz-border-radius:0 0 0 4px;border-radius:0 0 0 4px;-webkit-border-bottom-left-radius:4px;border-bottom-left-radius:4px;-moz-border-radius-bottomleft:4px;} +.table-bordered thead:last-child tr:last-child th:last-child,.table-bordered tbody:last-child tr:last-child td:last-child{-webkit-border-bottom-right-radius:4px;border-bottom-right-radius:4px;-moz-border-radius-bottomright:4px;} +.table-striped tbody tr:nth-child(odd) td,.table-striped tbody tr:nth-child(odd) th{background-color:#f9f9f9;} +.table tbody tr:hover td,.table tbody tr:hover th{background-color:#f5f5f5;} +table .span1{float:none;width:44px;margin-left:0;} +table .span2{float:none;width:124px;margin-left:0;} +table .span3{float:none;width:204px;margin-left:0;} +table .span4{float:none;width:284px;margin-left:0;} +table .span5{float:none;width:364px;margin-left:0;} +table .span6{float:none;width:444px;margin-left:0;} +table .span7{float:none;width:524px;margin-left:0;} +table .span8{float:none;width:604px;margin-left:0;} +table .span9{float:none;width:684px;margin-left:0;} +table .span10{float:none;width:764px;margin-left:0;} +table .span11{float:none;width:844px;margin-left:0;} +table .span12{float:none;width:924px;margin-left:0;} +table .span13{float:none;width:1004px;margin-left:0;} +table .span14{float:none;width:1084px;margin-left:0;} +table .span15{float:none;width:1164px;margin-left:0;} +table .span16{float:none;width:1244px;margin-left:0;} +table .span17{float:none;width:1324px;margin-left:0;} +table .span18{float:none;width:1404px;margin-left:0;} +table .span19{float:none;width:1484px;margin-left:0;} +table .span20{float:none;width:1564px;margin-left:0;} +table .span21{float:none;width:1644px;margin-left:0;} +table .span22{float:none;width:1724px;margin-left:0;} +table .span23{float:none;width:1804px;margin-left:0;} +table .span24{float:none;width:1884px;margin-left:0;} +.tree{width:100%;margin-bottom:18px;border:1px solid #cacaca;}.tree thead th{padding:8px;line-height:18px;text-align:left;vertical-align:bottom;background-color:#f4f4f4;background-image:-moz-linear-gradient(top, #fafafa, #eaeaea);background-image:-ms-linear-gradient(top, #fafafa, #eaeaea);background-image:-webkit-gradient(linear, 0 0, 0 100%, from(#fafafa), to(#eaeaea));background-image:-webkit-linear-gradient(top, #fafafa, #eaeaea);background-image:-o-linear-gradient(top, #fafafa, #eaeaea);background-image:linear-gradient(top, #fafafa, #eaeaea);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fafafa', endColorstr='#eaeaea', GradientType=0);border-bottom:1px solid #d7d7d7;font-weight:bold;color:#555555;text-shadow:1px 1px 1px #ffffff;} +.tree tbody td{padding:8px;line-height:18px;text-align:left;vertical-align:top;border-bottom:1px solid #EEE;background-color:#F8F8F8;} +.tree caption+thead tr:first-child th,.tree caption+thead tr:first-child td,.tree colgroup+thead tr:first-child th,.tree colgroup+thead tr:first-child td,.tree thead:first-child tr:first-child th,.tree thead:first-child tr:first-child td{border-top:0;} +.tree tbody tr:last-child td{border-bottom:0;} +.source-view{width:100%;margin-bottom:18px;border:1px solid #cacaca;}.source-view .source-header{padding:8px;line-height:18px;text-align:left;vertical-align:bottom;background-color:#f4f4f4;background-image:-moz-linear-gradient(top, #fafafa, #eaeaea);background-image:-ms-linear-gradient(top, #fafafa, #eaeaea);background-image:-webkit-gradient(linear, 0 0, 0 100%, from(#fafafa), to(#eaeaea));background-image:-webkit-linear-gradient(top, #fafafa, #eaeaea);background-image:-o-linear-gradient(top, #fafafa, #eaeaea);background-image:linear-gradient(top, #fafafa, #eaeaea);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fafafa', endColorstr='#eaeaea', GradientType=0);border-bottom:1px solid #d7d7d7;font-weight:bold;color:#555555;text-shadow:1px 1px 1px #ffffff;height:28px;}.source-view .source-header .meta{float:left;padding:4px 0;font-size:14px;} +.source-view pre{margin:0;padding:12px;border:none;} +.source-view #sourcecode{margin:0;padding:0;border:none;width:100%;height:600px;} +.source-view .source-diff{background-color:#f5f5f5;padding:12px;}.source-view .source-diff pre{margin:0;padding:0;border:none;-webkit-border-radius:0;-moz-border-radius:0;border-radius:0;} +.source-view .source-diff .new{background-color:#DFD;} +.source-view .source-diff .old{background-color:#FDD;} +.source-view .source-diff .chunk{background-color:#e8e8e8;color:#999999;} +.blame-view{width:100%;background-color:#f5f5f5;}.blame-view td{vertical-align:top;padding:8px;} +.blame-view tr{border-bottom:1px solid #cccccc;} +.blame-view tr:last-child{border-bottom:0;} +.blame-view .line{font-weight:700;border-right:1px solid #cccccc;} +.blame-view .commit{font-weight:700;border-right:1px solid #cccccc;} +.blame-view pre{margin:0;padding:0;border:none;-webkit-border-radius:0;-moz-border-radius:0;border-radius:0;} +.commit-view{width:100%;margin-bottom:18px;border:1px solid #cacaca;-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px;}.commit-view .commit-header{padding:8px;line-height:18px;text-align:left;vertical-align:bottom;background-color:#f4f4f4;background-image:-moz-linear-gradient(top, #fafafa, #eaeaea);background-image:-ms-linear-gradient(top, #fafafa, #eaeaea);background-image:-webkit-gradient(linear, 0 0, 0 100%, from(#fafafa), to(#eaeaea));background-image:-webkit-linear-gradient(top, #fafafa, #eaeaea);background-image:-o-linear-gradient(top, #fafafa, #eaeaea);background-image:linear-gradient(top, #fafafa, #eaeaea);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fafafa', endColorstr='#eaeaea', GradientType=0);border-bottom:1px solid #d7d7d7;font-weight:bold;text-shadow:1px 1px 1px #ffffff;height:28px;}.commit-view .commit-header h4{padding:4px 0;} +.commit-view .commit-body{padding:8px;} +.commit-list{list-style-type:none;}.commit-list li{padding:8px 5px 8px 5px;font-size:14px;font-weight:700;border-bottom:1px solid #d7d7d7;}.commit-list li .meta{font-weight:normal;font-size:14px;color:#999999;} +.commit-list li:last-child{border-bottom:0;margin-bottom:25px;} +.repository{margin-bottom:18px;border:1px solid #d7d7d7;-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px;}.repository .repository-header{border-bottom:1px solid #d7d7d7;text-shadow:1px 1px 1px #ffffff;padding:8px;font-weight:700;font-size:14px;} +.repository .repository-body{padding:8px;background-color:#f7f7f7;}.repository .repository-body p{margin:0;} +.rss{display:inline-block;width:16px;height:16px;*margin-right:.3em;line-height:16px;vertical-align:text-top;background-image:url("../img/feed.png");background-position:0 0;background-repeat:no-repeat;}.rss:last-child{*margin-left:0;} +[class^="icon-"],[class*=" icon-"]{display:inline-block;width:14px;height:14px;*margin-right:.3em;line-height:14px;vertical-align:text-top;background-image:url("../img/glyphicons-halflings.png");background-position:14px 14px;background-repeat:no-repeat;}[class^="icon-"]:last-child,[class*=" icon-"]:last-child{*margin-left:0;} +.icon-white{background-image:url("../img/glyphicons-halflings-white.png");} +.icon-spaced{padding:0 3px 0 3px;} +.icon-glass{background-position:0 0;} +.icon-music{background-position:-24px 0;} +.icon-search{background-position:-48px 0;} +.icon-envelope{background-position:-72px 0;} +.icon-heart{background-position:-96px 0;} +.icon-star{background-position:-120px 0;} +.icon-star-empty{background-position:-144px 0;} +.icon-user{background-position:-168px 0;} +.icon-film{background-position:-192px 0;} +.icon-th-large{background-position:-216px 0;} +.icon-th{background-position:-240px 0;} +.icon-th-list{background-position:-264px 0;} +.icon-ok{background-position:-288px 0;} +.icon-remove{background-position:-312px 0;} +.icon-zoom-in{background-position:-336px 0;} +.icon-zoom-out{background-position:-360px 0;} +.icon-off{background-position:-384px 0;} +.icon-signal{background-position:-408px 0;} +.icon-cog{background-position:-432px 0;} +.icon-trash{background-position:-456px 0;} +.icon-home{background-position:0 -24px;} +.icon-file{background-position:-24px -24px;} +.icon-time{background-position:-48px -24px;} +.icon-road{background-position:-72px -24px;} +.icon-download-alt{background-position:-96px -24px;} +.icon-download{background-position:-120px -24px;} +.icon-upload{background-position:-144px -24px;} +.icon-inbox{background-position:-168px -24px;} +.icon-play-circle{background-position:-192px -24px;} +.icon-repeat{background-position:-216px -24px;} +.icon-refresh{background-position:-240px -24px;} +.icon-list-alt{background-position:-264px -24px;} +.icon-lock{background-position:-287px -24px;} +.icon-flag{background-position:-312px -24px;} +.icon-headphones{background-position:-336px -24px;} +.icon-volume-off{background-position:-360px -24px;} +.icon-volume-down{background-position:-384px -24px;} +.icon-volume-up{background-position:-408px -24px;} +.icon-qrcode{background-position:-432px -24px;} +.icon-barcode{background-position:-456px -24px;} +.icon-tag{background-position:0 -48px;} +.icon-tags{background-position:-25px -48px;} +.icon-book{background-position:-48px -48px;} +.icon-bookmark{background-position:-72px -48px;} +.icon-print{background-position:-96px -48px;} +.icon-camera{background-position:-120px -48px;} +.icon-font{background-position:-144px -48px;} +.icon-bold{background-position:-167px -48px;} +.icon-italic{background-position:-192px -48px;} +.icon-text-height{background-position:-216px -48px;} +.icon-text-width{background-position:-240px -48px;} +.icon-align-left{background-position:-264px -48px;} +.icon-align-center{background-position:-288px -48px;} +.icon-align-right{background-position:-312px -48px;} +.icon-align-justify{background-position:-336px -48px;} +.icon-list{background-position:-360px -48px;} +.icon-indent-left{background-position:-384px -48px;} +.icon-indent-right{background-position:-408px -48px;} +.icon-facetime-video{background-position:-432px -48px;} +.icon-picture{background-position:-456px -48px;} +.icon-pencil{background-position:0 -72px;} +.icon-map-marker{background-position:-24px -72px;} +.icon-adjust{background-position:-48px -72px;} +.icon-tint{background-position:-72px -72px;} +.icon-edit{background-position:-96px -72px;} +.icon-share{background-position:-120px -72px;} +.icon-check{background-position:-144px -72px;} +.icon-move{background-position:-168px -72px;} +.icon-step-backward{background-position:-192px -72px;} +.icon-fast-backward{background-position:-216px -72px;} +.icon-backward{background-position:-240px -72px;} +.icon-play{background-position:-264px -72px;} +.icon-pause{background-position:-288px -72px;} +.icon-stop{background-position:-312px -72px;} +.icon-forward{background-position:-336px -72px;} +.icon-fast-forward{background-position:-360px -72px;} +.icon-step-forward{background-position:-384px -72px;} +.icon-eject{background-position:-408px -72px;} +.icon-chevron-left{background-position:-432px -72px;} +.icon-chevron-right{background-position:-456px -72px;} +.icon-plus-sign{background-position:0 -96px;} +.icon-minus-sign{background-position:-24px -96px;} +.icon-remove-sign{background-position:-48px -96px;} +.icon-ok-sign{background-position:-72px -96px;} +.icon-question-sign{background-position:-96px -96px;} +.icon-info-sign{background-position:-120px -96px;} +.icon-screenshot{background-position:-144px -96px;} +.icon-remove-circle{background-position:-168px -96px;} +.icon-ok-circle{background-position:-192px -96px;} +.icon-ban-circle{background-position:-216px -96px;} +.icon-arrow-left{background-position:-240px -96px;} +.icon-arrow-right{background-position:-264px -96px;} +.icon-arrow-up{background-position:-289px -96px;} +.icon-arrow-down{background-position:-312px -96px;} +.icon-share-alt{background-position:-336px -96px;} +.icon-resize-full{background-position:-360px -96px;} +.icon-resize-small{background-position:-384px -96px;} +.icon-plus{background-position:-408px -96px;} +.icon-minus{background-position:-433px -96px;} +.icon-asterisk{background-position:-456px -96px;} +.icon-exclamation-sign{background-position:0 -120px;} +.icon-gift{background-position:-24px -120px;} +.icon-leaf{background-position:-48px -120px;} +.icon-fire{background-position:-72px -120px;} +.icon-eye-open{background-position:-96px -120px;} +.icon-eye-close{background-position:-120px -120px;} +.icon-warning-sign{background-position:-144px -120px;} +.icon-plane{background-position:-168px -120px;} +.icon-calendar{background-position:-192px -120px;} +.icon-random{background-position:-216px -120px;} +.icon-comment{background-position:-240px -120px;} +.icon-magnet{background-position:-264px -120px;} +.icon-chevron-up{background-position:-288px -120px;} +.icon-chevron-down{background-position:-313px -119px;} +.icon-retweet{background-position:-336px -120px;} +.icon-shopping-cart{background-position:-360px -120px;} +.icon-folder-close{background-position:-384px -120px;} +.icon-folder-open{background-position:-408px -120px;} +.icon-resize-vertical{background-position:-432px -119px;} +.icon-resize-horizontal{background-position:-456px -118px;} +.icon-hdd{background-position:0 -144px;} +.icon-bullhorn{background-position:-24px -144px;} +.icon-bell{background-position:-48px -144px;} +.icon-certificate{background-position:-72px -144px;} +.icon-thumbs-up{background-position:-96px -144px;} +.icon-thumbs-down{background-position:-120px -144px;} +.icon-hand-right{background-position:-144px -144px;} +.icon-hand-left{background-position:-168px -144px;} +.icon-hand-up{background-position:-192px -144px;} +.icon-hand-down{background-position:-216px -144px;} +.icon-circle-arrow-right{background-position:-240px -144px;} +.icon-circle-arrow-left{background-position:-264px -144px;} +.icon-circle-arrow-up{background-position:-288px -144px;} +.icon-circle-arrow-down{background-position:-312px -144px;} +.icon-globe{background-position:-336px -144px;} +.icon-wrench{background-position:-360px -144px;} +.icon-tasks{background-position:-384px -144px;} +.icon-filter{background-position:-408px -144px;} +.icon-briefcase{background-position:-432px -144px;} +.icon-fullscreen{background-position:-456px -144px;} +.dropup,.dropdown{position:relative;} +.dropdown-toggle{*margin-bottom:-3px;} +.dropdown-toggle:active,.open .dropdown-toggle{outline:0;} +.caret{display:inline-block;width:0;height:0;vertical-align:top;border-top:4px solid #000000;border-right:4px solid transparent;border-left:4px solid transparent;content:"";opacity:0.3;filter:alpha(opacity=30);} +.dropdown .caret{margin-top:8px;margin-left:2px;} +.dropdown:hover .caret,.open .caret{opacity:1;filter:alpha(opacity=100);} +.dropdown-menu{position:absolute;top:100%;left:0;z-index:1000;display:none;float:left;min-width:160px;padding:4px 0;margin:1px 0 0;list-style:none;background-color:#ffffff;border:1px solid #ccc;border:1px solid rgba(0, 0, 0, 0.2);*border-right-width:2px;*border-bottom-width:2px;-webkit-border-radius:5px;-moz-border-radius:5px;border-radius:5px;-webkit-box-shadow:0 5px 10px rgba(0, 0, 0, 0.2);-moz-box-shadow:0 5px 10px rgba(0, 0, 0, 0.2);box-shadow:0 5px 10px rgba(0, 0, 0, 0.2);-webkit-background-clip:padding-box;-moz-background-clip:padding;background-clip:padding-box;}.dropdown-menu.pull-right{right:0;left:auto;} +.dropdown-menu .divider{*width:100%;height:1px;margin:8px 1px;*margin:-5px 0 5px;overflow:hidden;background-color:#e5e5e5;border-bottom:1px solid #ffffff;} +.dropdown-menu a{display:block;padding:3px 15px;clear:both;font-weight:normal;line-height:18px;color:#333333;white-space:nowrap;} +.dropdown-menu .dropdown-header{display:block;padding:3px 15px;font-size:11px;font-weight:bold;line-height:18px;color:#999999;text-shadow:0 1px 0 rgba(255, 255, 255, 0.5);text-transform:uppercase;} +.dropdown-menu li>a:hover,.dropdown-menu .active>a,.dropdown-menu .active>a:hover{color:#ffffff;text-decoration:none;background-color:#4183c4;} +.open{*z-index:1000;}.open .dropdown-menu{display:block;} +.pull-right .dropdown-menu{right:0;left:auto;} +.dropup .caret,.navbar-fixed-bottom .dropdown .caret{border-top:0;border-bottom:4px solid #000000;content:"\2191";} +.dropup .dropdown-menu,.navbar-fixed-bottom .dropdown .dropdown-menu{top:auto;bottom:100%;margin-bottom:1px;} +.typeahead{margin-top:2px;-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px;} +.well{min-height:20px;padding:19px;margin-bottom:20px;background-color:#f5f5f5;border:1px solid #eee;border:1px solid rgba(0, 0, 0, 0.05);-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px;-webkit-box-shadow:inset 0 1px 1px rgba(0, 0, 0, 0.05);-moz-box-shadow:inset 0 1px 1px rgba(0, 0, 0, 0.05);box-shadow:inset 0 1px 1px rgba(0, 0, 0, 0.05);}.well blockquote{border-color:#ddd;border-color:rgba(0, 0, 0, 0.15);} +.well-large{padding:24px;-webkit-border-radius:6px;-moz-border-radius:6px;border-radius:6px;} +.well-small{padding:9px;-webkit-border-radius:3px;-moz-border-radius:3px;border-radius:3px;} +.fade{opacity:0;filter:alpha(opacity=0);-webkit-transition:opacity 0.15s linear;-moz-transition:opacity 0.15s linear;-ms-transition:opacity 0.15s linear;-o-transition:opacity 0.15s linear;transition:opacity 0.15s linear;}.fade.in{opacity:1;filter:alpha(opacity=100);} +.collapse{position:relative;height:0;overflow:hidden;-webkit-transition:height 0.35s ease;-moz-transition:height 0.35s ease;-ms-transition:height 0.35s ease;-o-transition:height 0.35s ease;transition:height 0.35s ease;}.collapse.in{height:auto;} +.close{float:right;font-size:20px;font-weight:bold;line-height:18px;color:#000000;text-shadow:0 1px 0 #ffffff;opacity:0.2;filter:alpha(opacity=20);}.close:hover{color:#000000;text-decoration:none;cursor:pointer;opacity:0.4;filter:alpha(opacity=40);} +button.close{padding:0;cursor:pointer;background:transparent;border:0;-webkit-appearance:none;} +.btn{display:inline-block;*display:inline;*zoom:1;padding:4px 10px 4px;margin-bottom:0;font-size:13px;line-height:18px;*line-height:20px;color:#333333;text-align:center;text-shadow:0 1px 1px rgba(255, 255, 255, 0.75);vertical-align:middle;cursor:pointer;background-color:#f5f5f5;background-image:-moz-linear-gradient(top, #ffffff, #e6e6e6);background-image:-ms-linear-gradient(top, #ffffff, #e6e6e6);background-image:-webkit-gradient(linear, 0 0, 0 100%, from(#ffffff), to(#e6e6e6));background-image:-webkit-linear-gradient(top, #ffffff, #e6e6e6);background-image:-o-linear-gradient(top, #ffffff, #e6e6e6);background-image:linear-gradient(top, #ffffff, #e6e6e6);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffffff', endColorstr='#e6e6e6', GradientType=0);border-color:#e6e6e6 #e6e6e6 #bfbfbf;border-color:rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25);*background-color:#e6e6e6;filter:progid:DXImageTransform.Microsoft.gradient(enabled = false);border:1px solid #cccccc;*border:0;border-bottom-color:#b3b3b3;-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px;*margin-left:.3em;-webkit-box-shadow:inset 0 1px 0 rgba(255,255,255,.2), 0 1px 2px rgba(0,0,0,.05);-moz-box-shadow:inset 0 1px 0 rgba(255,255,255,.2), 0 1px 2px rgba(0,0,0,.05);box-shadow:inset 0 1px 0 rgba(255,255,255,.2), 0 1px 2px rgba(0,0,0,.05);}.btn:hover,.btn:active,.btn.active,.btn.disabled,.btn[disabled]{background-color:#e6e6e6;*background-color:#d9d9d9;} +.btn:active,.btn.active{background-color:#cccccc \9;} +.btn:first-child{*margin-left:0;} +.btn:hover{color:#333333;text-decoration:none;background-color:#e6e6e6;*background-color:#d9d9d9;background-position:0 -15px;-webkit-transition:background-position 0.1s linear;-moz-transition:background-position 0.1s linear;-ms-transition:background-position 0.1s linear;-o-transition:background-position 0.1s linear;transition:background-position 0.1s linear;} +.btn:focus{outline:thin dotted #333;outline:5px auto -webkit-focus-ring-color;outline-offset:-2px;} +.btn.active,.btn:active{background-color:#e6e6e6;background-color:#d9d9d9 \9;background-image:none;outline:0;-webkit-box-shadow:inset 0 2px 4px rgba(0,0,0,.15), 0 1px 2px rgba(0,0,0,.05);-moz-box-shadow:inset 0 2px 4px rgba(0,0,0,.15), 0 1px 2px rgba(0,0,0,.05);box-shadow:inset 0 2px 4px rgba(0,0,0,.15), 0 1px 2px rgba(0,0,0,.05);} +.btn.disabled,.btn[disabled]{cursor:default;background-color:#e6e6e6;background-image:none;opacity:0.65;filter:alpha(opacity=65);-webkit-box-shadow:none;-moz-box-shadow:none;box-shadow:none;} +.btn-large{padding:9px 14px;font-size:15px;line-height:normal;-webkit-border-radius:5px;-moz-border-radius:5px;border-radius:5px;} +.btn-large [class^="icon-"]{margin-top:1px;} +.btn-small{padding:5px 9px;font-size:11px;line-height:16px;} +.btn-small [class^="icon-"]{margin-top:-1px;} +.btn-mini{padding:2px 6px;font-size:11px;line-height:14px;} +.btn-primary,.btn-primary:hover,.btn-warning,.btn-warning:hover,.btn-danger,.btn-danger:hover,.btn-success,.btn-success:hover,.btn-info,.btn-info:hover,.btn-inverse,.btn-inverse:hover{color:#ffffff;text-shadow:0 -1px 0 rgba(0, 0, 0, 0.25);} +.btn-primary.active,.btn-warning.active,.btn-danger.active,.btn-success.active,.btn-info.active,.btn-inverse.active{color:rgba(255, 255, 255, 0.75);} +.btn{border-color:#ccc;border-color:rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25);} +.btn-primary{background-color:#417ac4;background-image:-moz-linear-gradient(top, #4183c4, #416dc4);background-image:-ms-linear-gradient(top, #4183c4, #416dc4);background-image:-webkit-gradient(linear, 0 0, 0 100%, from(#4183c4), to(#416dc4));background-image:-webkit-linear-gradient(top, #4183c4, #416dc4);background-image:-o-linear-gradient(top, #4183c4, #416dc4);background-image:linear-gradient(top, #4183c4, #416dc4);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#4183c4', endColorstr='#416dc4', GradientType=0);border-color:#416dc4 #416dc4 #2c4c8d;border-color:rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25);*background-color:#416dc4;filter:progid:DXImageTransform.Microsoft.gradient(enabled = false);}.btn-primary:hover,.btn-primary:active,.btn-primary.active,.btn-primary.disabled,.btn-primary[disabled]{background-color:#416dc4;*background-color:#3862b4;} +.btn-primary:active,.btn-primary.active{background-color:#3257a0 \9;} +.btn-warning{background-color:#faa732;background-image:-moz-linear-gradient(top, #fbb450, #f89406);background-image:-ms-linear-gradient(top, #fbb450, #f89406);background-image:-webkit-gradient(linear, 0 0, 0 100%, from(#fbb450), to(#f89406));background-image:-webkit-linear-gradient(top, #fbb450, #f89406);background-image:-o-linear-gradient(top, #fbb450, #f89406);background-image:linear-gradient(top, #fbb450, #f89406);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fbb450', endColorstr='#f89406', GradientType=0);border-color:#f89406 #f89406 #ad6704;border-color:rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25);*background-color:#f89406;filter:progid:DXImageTransform.Microsoft.gradient(enabled = false);}.btn-warning:hover,.btn-warning:active,.btn-warning.active,.btn-warning.disabled,.btn-warning[disabled]{background-color:#f89406;*background-color:#df8505;} +.btn-warning:active,.btn-warning.active{background-color:#c67605 \9;} +.btn-danger{background-color:#da4f49;background-image:-moz-linear-gradient(top, #ee5f5b, #bd362f);background-image:-ms-linear-gradient(top, #ee5f5b, #bd362f);background-image:-webkit-gradient(linear, 0 0, 0 100%, from(#ee5f5b), to(#bd362f));background-image:-webkit-linear-gradient(top, #ee5f5b, #bd362f);background-image:-o-linear-gradient(top, #ee5f5b, #bd362f);background-image:linear-gradient(top, #ee5f5b, #bd362f);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ee5f5b', endColorstr='#bd362f', GradientType=0);border-color:#bd362f #bd362f #802420;border-color:rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25);*background-color:#bd362f;filter:progid:DXImageTransform.Microsoft.gradient(enabled = false);}.btn-danger:hover,.btn-danger:active,.btn-danger.active,.btn-danger.disabled,.btn-danger[disabled]{background-color:#bd362f;*background-color:#a9302a;} +.btn-danger:active,.btn-danger.active{background-color:#942a25 \9;} +.btn-success{background-color:#5bb75b;background-image:-moz-linear-gradient(top, #62c462, #51a351);background-image:-ms-linear-gradient(top, #62c462, #51a351);background-image:-webkit-gradient(linear, 0 0, 0 100%, from(#62c462), to(#51a351));background-image:-webkit-linear-gradient(top, #62c462, #51a351);background-image:-o-linear-gradient(top, #62c462, #51a351);background-image:linear-gradient(top, #62c462, #51a351);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#62c462', endColorstr='#51a351', GradientType=0);border-color:#51a351 #51a351 #387038;border-color:rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25);*background-color:#51a351;filter:progid:DXImageTransform.Microsoft.gradient(enabled = false);}.btn-success:hover,.btn-success:active,.btn-success.active,.btn-success.disabled,.btn-success[disabled]{background-color:#51a351;*background-color:#499249;} +.btn-success:active,.btn-success.active{background-color:#408140 \9;} +.btn-info{background-color:#49afcd;background-image:-moz-linear-gradient(top, #5bc0de, #2f96b4);background-image:-ms-linear-gradient(top, #5bc0de, #2f96b4);background-image:-webkit-gradient(linear, 0 0, 0 100%, from(#5bc0de), to(#2f96b4));background-image:-webkit-linear-gradient(top, #5bc0de, #2f96b4);background-image:-o-linear-gradient(top, #5bc0de, #2f96b4);background-image:linear-gradient(top, #5bc0de, #2f96b4);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#5bc0de', endColorstr='#2f96b4', GradientType=0);border-color:#2f96b4 #2f96b4 #1f6377;border-color:rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25);*background-color:#2f96b4;filter:progid:DXImageTransform.Microsoft.gradient(enabled = false);}.btn-info:hover,.btn-info:active,.btn-info.active,.btn-info.disabled,.btn-info[disabled]{background-color:#2f96b4;*background-color:#2a85a0;} +.btn-info:active,.btn-info.active{background-color:#24748c \9;} +.btn-inverse{background-color:#414141;background-image:-moz-linear-gradient(top, #555555, #222222);background-image:-ms-linear-gradient(top, #555555, #222222);background-image:-webkit-gradient(linear, 0 0, 0 100%, from(#555555), to(#222222));background-image:-webkit-linear-gradient(top, #555555, #222222);background-image:-o-linear-gradient(top, #555555, #222222);background-image:linear-gradient(top, #555555, #222222);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#555555', endColorstr='#222222', GradientType=0);border-color:#222222 #222222 #000000;border-color:rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25);*background-color:#222222;filter:progid:DXImageTransform.Microsoft.gradient(enabled = false);}.btn-inverse:hover,.btn-inverse:active,.btn-inverse.active,.btn-inverse.disabled,.btn-inverse[disabled]{background-color:#222222;*background-color:#151515;} +.btn-inverse:active,.btn-inverse.active{background-color:#080808 \9;} +button.btn,input[type="submit"].btn{*padding-top:2px;*padding-bottom:2px;}button.btn::-moz-focus-inner,input[type="submit"].btn::-moz-focus-inner{padding:0;border:0;} +button.btn.btn-large,input[type="submit"].btn.btn-large{*padding-top:7px;*padding-bottom:7px;} +button.btn.btn-small,input[type="submit"].btn.btn-small{*padding-top:3px;*padding-bottom:3px;} +button.btn.btn-mini,input[type="submit"].btn.btn-mini{*padding-top:1px;*padding-bottom:1px;} +.btn-group{position:relative;*zoom:1;*margin-left:.3em;}.btn-group:before,.btn-group:after{display:table;content:"";} +.btn-group:after{clear:both;} +.btn-group:first-child{*margin-left:0;} +.btn-group+.btn-group{margin-left:5px;} +.btn-toolbar{margin-top:9px;margin-bottom:9px;}.btn-toolbar .btn-group{display:inline-block;*display:inline;*zoom:1;} +.btn-group>.btn{position:relative;float:left;margin-left:-1px;-webkit-border-radius:0;-moz-border-radius:0;border-radius:0;} +.btn-group>.btn:first-child{margin-left:0;-webkit-border-top-left-radius:4px;-moz-border-radius-topleft:4px;border-top-left-radius:4px;-webkit-border-bottom-left-radius:4px;-moz-border-radius-bottomleft:4px;border-bottom-left-radius:4px;} +.btn-group>.btn:last-child,.btn-group>.dropdown-toggle{-webkit-border-top-right-radius:4px;-moz-border-radius-topright:4px;border-top-right-radius:4px;-webkit-border-bottom-right-radius:4px;-moz-border-radius-bottomright:4px;border-bottom-right-radius:4px;} +.btn-group>.btn.large:first-child{margin-left:0;-webkit-border-top-left-radius:6px;-moz-border-radius-topleft:6px;border-top-left-radius:6px;-webkit-border-bottom-left-radius:6px;-moz-border-radius-bottomleft:6px;border-bottom-left-radius:6px;} +.btn-group>.btn.large:last-child,.btn-group>.large.dropdown-toggle{-webkit-border-top-right-radius:6px;-moz-border-radius-topright:6px;border-top-right-radius:6px;-webkit-border-bottom-right-radius:6px;-moz-border-radius-bottomright:6px;border-bottom-right-radius:6px;} +.btn-group>.btn:hover,.btn-group>.btn:focus,.btn-group>.btn:active,.btn-group>.btn.active{z-index:2;} +.btn-group .dropdown-toggle:active,.btn-group.open .dropdown-toggle{outline:0;} +.btn-group>.dropdown-toggle{padding-left:8px;padding-right:8px;-webkit-box-shadow:inset 1px 0 0 rgba(255,255,255,.125), inset 0 1px 0 rgba(255,255,255,.2), 0 1px 2px rgba(0,0,0,.05);-moz-box-shadow:inset 1px 0 0 rgba(255,255,255,.125), inset 0 1px 0 rgba(255,255,255,.2), 0 1px 2px rgba(0,0,0,.05);box-shadow:inset 1px 0 0 rgba(255,255,255,.125), inset 0 1px 0 rgba(255,255,255,.2), 0 1px 2px rgba(0,0,0,.05);*padding-top:4px;*padding-bottom:4px;} +.btn-group>.btn-mini.dropdown-toggle{padding-left:5px;padding-right:5px;} +.btn-group>.btn-small.dropdown-toggle{*padding-top:4px;*padding-bottom:4px;} +.btn-group>.btn-large.dropdown-toggle{padding-left:12px;padding-right:12px;} +.btn-group.open .dropdown-toggle{background-image:none;-webkit-box-shadow:inset 0 2px 4px rgba(0,0,0,.15), 0 1px 2px rgba(0,0,0,.05);-moz-box-shadow:inset 0 2px 4px rgba(0,0,0,.15), 0 1px 2px rgba(0,0,0,.05);box-shadow:inset 0 2px 4px rgba(0,0,0,.15), 0 1px 2px rgba(0,0,0,.05);} +.btn-group.open .btn.dropdown-toggle{background-color:#e6e6e6;} +.btn-group.open .btn-primary.dropdown-toggle{background-color:#416dc4;} +.btn-group.open .btn-warning.dropdown-toggle{background-color:#f89406;} +.btn-group.open .btn-danger.dropdown-toggle{background-color:#bd362f;} +.btn-group.open .btn-success.dropdown-toggle{background-color:#51a351;} +.btn-group.open .btn-info.dropdown-toggle{background-color:#2f96b4;} +.btn-group.open .btn-inverse.dropdown-toggle{background-color:#222222;} +.btn .caret{margin-top:7px;margin-left:0;} +.btn:hover .caret,.open.btn-group .caret{opacity:1;filter:alpha(opacity=100);} +.btn-mini .caret{margin-top:5px;} +.btn-small .caret{margin-top:6px;} +.btn-large .caret{margin-top:6px;border-left-width:5px;border-right-width:5px;border-top-width:5px;} +.dropup .btn-large .caret{border-bottom:5px solid #000000;border-top:0;} +.btn-primary .caret,.btn-warning .caret,.btn-danger .caret,.btn-info .caret,.btn-success .caret,.btn-inverse .caret{border-top-color:#ffffff;border-bottom-color:#ffffff;opacity:0.75;filter:alpha(opacity=75);} +.alert{padding:8px 35px 8px 14px;margin-bottom:18px;text-shadow:0 1px 0 rgba(255, 255, 255, 0.5);background-color:#fcf8e3;border:1px solid #fbeed5;-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px;color:#c09853;} +.alert-heading{color:inherit;} +.alert .close{position:relative;top:-2px;right:-21px;line-height:18px;} +.alert-success{background-color:#dff0d8;border-color:#d6e9c6;color:#468847;} +.alert-danger,.alert-error{background-color:#f2dede;border-color:#eed3d7;color:#b94a48;} +.alert-info{background-color:#d9edf7;border-color:#bce8f1;color:#3a87ad;} +.alert-block{padding-top:14px;padding-bottom:14px;} +.alert-block>p,.alert-block>ul{margin-bottom:0;} +.alert-block p+p{margin-top:5px;} +.nav{margin-left:0;margin-bottom:18px;list-style:none;} +.nav>li>a{display:block;} +.nav>li>a:hover{text-decoration:none;background-color:#eeeeee;} +.nav>.pull-right{float:right;} +.nav .nav-header{display:block;padding:3px 15px;font-size:11px;font-weight:bold;line-height:18px;color:#999999;text-shadow:0 1px 0 rgba(255, 255, 255, 0.5);text-transform:uppercase;} +.nav li+.nav-header{margin-top:9px;} +.nav-list{padding-left:15px;padding-right:15px;margin-bottom:0;} +.nav-list>li>a,.nav-list .nav-header{margin-left:-15px;margin-right:-15px;text-shadow:0 1px 0 rgba(255, 255, 255, 0.5);} +.nav-list>li>a{padding:3px 15px;} +.nav-list>.active>a,.nav-list>.active>a:hover{color:#ffffff;text-shadow:0 -1px 0 rgba(0, 0, 0, 0.2);background-color:#4183c4;} +.nav-list [class^="icon-"]{margin-right:2px;} +.nav-list .divider{*width:100%;height:1px;margin:8px 1px;*margin:-5px 0 5px;overflow:hidden;background-color:#e5e5e5;border-bottom:1px solid #ffffff;} +.nav-tabs,.nav-pills{*zoom:1;}.nav-tabs:before,.nav-pills:before,.nav-tabs:after,.nav-pills:after{display:table;content:"";} +.nav-tabs:after,.nav-pills:after{clear:both;} +.nav-tabs>li,.nav-pills>li{float:left;} +.nav-tabs>li>a,.nav-pills>li>a{padding-right:12px;padding-left:12px;margin-right:2px;line-height:14px;} +.nav-tabs{border-bottom:1px solid #ddd;} +.nav-tabs>li{margin-bottom:-1px;} +.nav-tabs>li>a{padding-top:8px;padding-bottom:8px;line-height:18px;border:1px solid transparent;-webkit-border-radius:4px 4px 0 0;-moz-border-radius:4px 4px 0 0;border-radius:4px 4px 0 0;}.nav-tabs>li>a:hover{border-color:#eeeeee #eeeeee #dddddd;} +.nav-tabs>.active>a,.nav-tabs>.active>a:hover{color:#555555;background-color:#ffffff;border:1px solid #ddd;border-bottom-color:transparent;cursor:default;} +.nav-pills>li>a{padding-top:8px;padding-bottom:8px;margin-top:2px;margin-bottom:2px;-webkit-border-radius:5px;-moz-border-radius:5px;border-radius:5px;} +.nav-pills>.active>a,.nav-pills>.active>a:hover{color:#ffffff;background-color:#4183c4;} +.nav-stacked>li{float:none;} +.nav-stacked>li>a{margin-right:0;} +.nav-tabs.nav-stacked{border-bottom:0;} +.nav-tabs.nav-stacked>li>a{border:1px solid #ddd;-webkit-border-radius:0;-moz-border-radius:0;border-radius:0;} +.nav-tabs.nav-stacked>li:first-child>a{-webkit-border-radius:4px 4px 0 0;-moz-border-radius:4px 4px 0 0;border-radius:4px 4px 0 0;} +.nav-tabs.nav-stacked>li:last-child>a{-webkit-border-radius:0 0 4px 4px;-moz-border-radius:0 0 4px 4px;border-radius:0 0 4px 4px;} +.nav-tabs.nav-stacked>li>a:hover{border-color:#ddd;z-index:2;} +.nav-pills.nav-stacked>li>a{margin-bottom:3px;} +.nav-pills.nav-stacked>li:last-child>a{margin-bottom:1px;} +.nav-tabs .dropdown-menu{-webkit-border-radius:0 0 5px 5px;-moz-border-radius:0 0 5px 5px;border-radius:0 0 5px 5px;} +.nav-pills .dropdown-menu{-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px;} +.nav-tabs .dropdown-toggle .caret,.nav-pills .dropdown-toggle .caret{border-top-color:#4183c4;border-bottom-color:#4183c4;margin-top:6px;} +.nav-tabs .dropdown-toggle:hover .caret,.nav-pills .dropdown-toggle:hover .caret{border-top-color:#2c5d8d;border-bottom-color:#2c5d8d;} +.nav-tabs .active .dropdown-toggle .caret,.nav-pills .active .dropdown-toggle .caret{border-top-color:#333333;border-bottom-color:#333333;} +.nav>.dropdown.active>a:hover{color:#000000;cursor:pointer;} +.nav-tabs .open .dropdown-toggle,.nav-pills .open .dropdown-toggle,.nav>li.dropdown.open.active>a:hover{color:#ffffff;background-color:#999999;border-color:#999999;} +.nav li.dropdown.open .caret,.nav li.dropdown.open.active .caret,.nav li.dropdown.open a:hover .caret{border-top-color:#ffffff;border-bottom-color:#ffffff;opacity:1;filter:alpha(opacity=100);} +.tabs-stacked .open>a:hover{border-color:#999999;} +.tabbable{*zoom:1;}.tabbable:before,.tabbable:after{display:table;content:"";} +.tabbable:after{clear:both;} +.tab-content{overflow:auto;} +.tabs-below>.nav-tabs,.tabs-right>.nav-tabs,.tabs-left>.nav-tabs{border-bottom:0;} +.tab-content>.tab-pane,.pill-content>.pill-pane{display:none;} +.tab-content>.active,.pill-content>.active{display:block;} +.tabs-below>.nav-tabs{border-top:1px solid #ddd;} +.tabs-below>.nav-tabs>li{margin-top:-1px;margin-bottom:0;} +.tabs-below>.nav-tabs>li>a{-webkit-border-radius:0 0 4px 4px;-moz-border-radius:0 0 4px 4px;border-radius:0 0 4px 4px;}.tabs-below>.nav-tabs>li>a:hover{border-bottom-color:transparent;border-top-color:#ddd;} +.tabs-below>.nav-tabs>.active>a,.tabs-below>.nav-tabs>.active>a:hover{border-color:transparent #ddd #ddd #ddd;} +.tabs-left>.nav-tabs>li,.tabs-right>.nav-tabs>li{float:none;} +.tabs-left>.nav-tabs>li>a,.tabs-right>.nav-tabs>li>a{min-width:74px;margin-right:0;margin-bottom:3px;} +.tabs-left>.nav-tabs{float:left;margin-right:19px;border-right:1px solid #ddd;} +.tabs-left>.nav-tabs>li>a{margin-right:-1px;-webkit-border-radius:4px 0 0 4px;-moz-border-radius:4px 0 0 4px;border-radius:4px 0 0 4px;} +.tabs-left>.nav-tabs>li>a:hover{border-color:#eeeeee #dddddd #eeeeee #eeeeee;} +.tabs-left>.nav-tabs .active>a,.tabs-left>.nav-tabs .active>a:hover{border-color:#ddd transparent #ddd #ddd;*border-right-color:#ffffff;} +.tabs-right>.nav-tabs{float:right;margin-left:19px;border-left:1px solid #ddd;} +.tabs-right>.nav-tabs>li>a{margin-left:-1px;-webkit-border-radius:0 4px 4px 0;-moz-border-radius:0 4px 4px 0;border-radius:0 4px 4px 0;} +.tabs-right>.nav-tabs>li>a:hover{border-color:#eeeeee #eeeeee #eeeeee #dddddd;} +.tabs-right>.nav-tabs .active>a,.tabs-right>.nav-tabs .active>a:hover{border-color:#ddd #ddd #ddd transparent;*border-left-color:#ffffff;} +.navbar{*position:relative;*z-index:2;overflow:visible;margin-bottom:18px;} +.navbar-inner{min-height:40px;padding-left:20px;padding-right:20px;border-bottom:1px solid #cacaca;background-color:#f4f4f4;background-image:-moz-linear-gradient(top, #fafafa, #eaeaea);background-image:-ms-linear-gradient(top, #fafafa, #eaeaea);background-image:-webkit-gradient(linear, 0 0, 0 100%, from(#fafafa), to(#eaeaea));background-image:-webkit-linear-gradient(top, #fafafa, #eaeaea);background-image:-o-linear-gradient(top, #fafafa, #eaeaea);background-image:linear-gradient(top, #fafafa, #eaeaea);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fafafa', endColorstr='#eaeaea', GradientType=0);-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px;-webkit-box-shadow:0 1px 0 rgba(255,255,255,0.4), 0 0 10px rgba(0,0,0,0.1);-moz-box-shadow:0 1px 0 rgba(255,255,255,0.4), 0 0 10px rgba(0,0,0,0.1);box-shadow:0 1px 0 rgba(255,255,255,0.4), 0 0 10px rgba(0,0,0,0.1);} +.navbar .container{width:auto;} +.nav-collapse.collapse{height:auto;} +.navbar{color:#333333;}.navbar .brand:hover{text-decoration:none;} +.navbar .brand{float:left;display:block;padding:10px 20px 12px;margin-left:-20px;font-size:20px;font-weight:200;line-height:1;color:#7b7b7b;text-shadow:1px 1px 1px #ffffff;font-weight:700;letter-spacing:1px;} +.navbar .navbar-text{margin-bottom:0;line-height:40px;} +.navbar .navbar-link{color:#222222;}.navbar .navbar-link:hover{color:#4183c4;} +.navbar .btn,.navbar .btn-group{margin-top:5px;} +.navbar .btn-group .btn{margin:0;} +.navbar-form{margin-bottom:0;*zoom:1;}.navbar-form:before,.navbar-form:after{display:table;content:"";} +.navbar-form:after{clear:both;} +.navbar-form input,.navbar-form select,.navbar-form .radio,.navbar-form .checkbox{margin-top:5px;} +.navbar-form input,.navbar-form select{display:inline-block;margin-bottom:0;} +.navbar-form input[type="image"],.navbar-form input[type="checkbox"],.navbar-form input[type="radio"]{margin-top:3px;} +.navbar-form .input-append,.navbar-form .input-prepend{margin-top:6px;white-space:nowrap;}.navbar-form .input-append input,.navbar-form .input-prepend input{margin-top:0;} +.navbar-search{position:relative;float:left;margin-top:6px;margin-bottom:0;}.navbar-search .search-query{padding:4px 9px;font-family:"Helvetica Neue",Helvetica,Arial,sans-serif;font-size:13px;font-weight:normal;line-height:1;color:#ffffff;background-color:#ffffff;border:1px solid #b3b3b3;-webkit-box-shadow:inset 0 1px 2px rgba(0,0,0,.1), 0 1px 0px rgba(255,255,255,.15);-moz-box-shadow:inset 0 1px 2px rgba(0,0,0,.1), 0 1px 0px rgba(255,255,255,.15);box-shadow:inset 0 1px 2px rgba(0,0,0,.1), 0 1px 0px rgba(255,255,255,.15);-webkit-transition:none;-moz-transition:none;-ms-transition:none;-o-transition:none;transition:none;}.navbar-search .search-query:-moz-placeholder{color:#cccccc;} +.navbar-search .search-query::-webkit-input-placeholder{color:#cccccc;} +.navbar-search .search-query:focus,.navbar-search .search-query.focused{padding:5px 10px;color:#333333;text-shadow:0 1px 0 #ffffff;background-color:#ffffff;border:0;-webkit-box-shadow:0 0 3px rgba(0, 0, 0, 0.15);-moz-box-shadow:0 0 3px rgba(0, 0, 0, 0.15);box-shadow:0 0 3px rgba(0, 0, 0, 0.15);outline:0;} +.navbar-fixed-top,.navbar-fixed-bottom{position:fixed;right:0;left:0;z-index:1030;margin-bottom:0;} +.navbar-fixed-top .navbar-inner,.navbar-fixed-bottom .navbar-inner{padding-left:0;padding-right:0;-webkit-border-radius:0;-moz-border-radius:0;border-radius:0;} +.navbar-fixed-top .container,.navbar-fixed-bottom .container{width:940px;} +.navbar-fixed-top{top:0;} +.navbar-fixed-bottom{bottom:0;} +.navbar .nav{position:relative;left:0;display:block;float:left;margin:0 10px 0 0;} +.navbar .nav.pull-right{float:right;} +.navbar .nav>li{display:block;float:left;} +.navbar .nav>li>a{float:none;padding:10px 10px 11px;line-height:19px;color:#222222;text-decoration:none;font-weight:700;} +.navbar .btn{display:inline-block;padding:4px 10px 4px;margin:5px 5px 6px;line-height:18px;} +.navbar .btn-group{margin:0;padding:5px 5px 6px;} +.navbar .nav>li>a:hover{color:#4183c4;text-decoration:none;background:transparent;} +.navbar .nav .active>a,.navbar .nav .active>a:hover{color:#4183c4;text-decoration:none;} +.navbar .divider-vertical{height:40px;width:1px;margin:0 9px;overflow:hidden;background-color:#eaeaea;border-right:1px solid #fafafa;} +.navbar .nav.pull-right{margin-left:10px;margin-right:0;} +.navbar .btn-navbar{display:none;float:right;padding:7px 10px;margin-left:5px;margin-right:5px;background-color:#f4f4f4;background-image:-moz-linear-gradient(top, #fafafa, #eaeaea);background-image:-ms-linear-gradient(top, #fafafa, #eaeaea);background-image:-webkit-gradient(linear, 0 0, 0 100%, from(#fafafa), to(#eaeaea));background-image:-webkit-linear-gradient(top, #fafafa, #eaeaea);background-image:-o-linear-gradient(top, #fafafa, #eaeaea);background-image:linear-gradient(top, #fafafa, #eaeaea);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fafafa', endColorstr='#eaeaea', GradientType=0);border-color:#eaeaea #eaeaea #c4c4c4;border-color:rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25);*background-color:#eaeaea;filter:progid:DXImageTransform.Microsoft.gradient(enabled = false);-webkit-box-shadow:inset 0 1px 0 rgba(255,255,255,.1), 0 1px 0 rgba(255,255,255,.075);-moz-box-shadow:inset 0 1px 0 rgba(255,255,255,.1), 0 1px 0 rgba(255,255,255,.075);box-shadow:inset 0 1px 0 rgba(255,255,255,.1), 0 1px 0 rgba(255,255,255,.075);}.navbar .btn-navbar:hover,.navbar .btn-navbar:active,.navbar .btn-navbar.active,.navbar .btn-navbar.disabled,.navbar .btn-navbar[disabled]{background-color:#eaeaea;*background-color:#dddddd;} +.navbar .btn-navbar:active,.navbar .btn-navbar.active{background-color:#d1d1d1 \9;} +.navbar .btn-navbar .icon-bar{display:block;width:18px;height:2px;background-color:#f5f5f5;-webkit-border-radius:1px;-moz-border-radius:1px;border-radius:1px;-webkit-box-shadow:0 1px 0 rgba(0, 0, 0, 0.25);-moz-box-shadow:0 1px 0 rgba(0, 0, 0, 0.25);box-shadow:0 1px 0 rgba(0, 0, 0, 0.25);} +.btn-navbar .icon-bar+.icon-bar{margin-top:3px;} +.navbar .dropdown-menu:before{content:'';display:inline-block;border-left:7px solid transparent;border-right:7px solid transparent;border-bottom:7px solid #ccc;border-bottom-color:rgba(0, 0, 0, 0.2);position:absolute;top:-7px;left:9px;} +.navbar .dropdown-menu:after{content:'';display:inline-block;border-left:6px solid transparent;border-right:6px solid transparent;border-bottom:6px solid #ffffff;position:absolute;top:-6px;left:10px;} +.navbar-fixed-bottom .dropdown-menu:before{border-top:7px solid #ccc;border-top-color:rgba(0, 0, 0, 0.2);border-bottom:0;bottom:-7px;top:auto;} +.navbar-fixed-bottom .dropdown-menu:after{border-top:6px solid #ffffff;border-bottom:0;bottom:-6px;top:auto;} +.navbar .nav li.dropdown .dropdown-toggle .caret,.navbar .nav li.dropdown.open .caret{border-top-color:#555555;border-bottom-color:#999999;} +.navbar .nav li.dropdown.active .caret{opacity:1;filter:alpha(opacity=100);} +.navbar .nav li.dropdown.open>.dropdown-toggle,.navbar .nav li.dropdown.active>.dropdown-toggle,.navbar .nav li.dropdown.open.active>.dropdown-toggle{background-color:transparent;} +.navbar .nav li.dropdown.active>.dropdown-toggle:hover{color:#ffffff;} +.navbar .pull-right .dropdown-menu,.navbar .dropdown-menu.pull-right{left:auto;right:0;}.navbar .pull-right .dropdown-menu:before,.navbar .dropdown-menu.pull-right:before{left:auto;right:12px;} +.navbar .pull-right .dropdown-menu:after,.navbar .dropdown-menu.pull-right:after{left:auto;right:13px;} +.breadcrumb{padding:7px 14px;margin:0 0 18px;list-style:none;background-color:#fbfbfb;background-image:-moz-linear-gradient(top, #ffffff, #f5f5f5);background-image:-ms-linear-gradient(top, #ffffff, #f5f5f5);background-image:-webkit-gradient(linear, 0 0, 0 100%, from(#ffffff), to(#f5f5f5));background-image:-webkit-linear-gradient(top, #ffffff, #f5f5f5);background-image:-o-linear-gradient(top, #ffffff, #f5f5f5);background-image:linear-gradient(top, #ffffff, #f5f5f5);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffffff', endColorstr='#f5f5f5', GradientType=0);border:1px solid #ddd;font-weight:700;-webkit-border-radius:3px;-moz-border-radius:3px;border-radius:3px;-webkit-box-shadow:inset 0 1px 0 #ffffff;-moz-box-shadow:inset 0 1px 0 #ffffff;box-shadow:inset 0 1px 0 #ffffff;}.breadcrumb li{display:inline-block;*display:inline;*zoom:1;text-shadow:0 1px 0 #ffffff;} +.breadcrumb .divider{padding:0 5px;color:#999999;} +.breadcrumb .active a{color:#333333;} +.pagination{height:36px;margin:18px 0;} +.pagination ul{display:inline-block;*display:inline;*zoom:1;margin-left:0;margin-bottom:0;-webkit-border-radius:3px;-moz-border-radius:3px;border-radius:3px;-webkit-box-shadow:0 1px 2px rgba(0, 0, 0, 0.05);-moz-box-shadow:0 1px 2px rgba(0, 0, 0, 0.05);box-shadow:0 1px 2px rgba(0, 0, 0, 0.05);} +.pagination li{display:inline;} +.pagination a{float:left;padding:0 14px;line-height:34px;text-decoration:none;border:1px solid #ddd;border-left-width:0;} +.pagination a:hover,.pagination .active a{background-color:#f5f5f5;} +.pagination .active a{color:#999999;cursor:default;} +.pagination .disabled span,.pagination .disabled a,.pagination .disabled a:hover{color:#999999;background-color:transparent;cursor:default;} +.pagination li:first-child a{border-left-width:1px;-webkit-border-radius:3px 0 0 3px;-moz-border-radius:3px 0 0 3px;border-radius:3px 0 0 3px;} +.pagination li:last-child a{-webkit-border-radius:0 3px 3px 0;-moz-border-radius:0 3px 3px 0;border-radius:0 3px 3px 0;} +.pagination-centered{text-align:center;} +.pagination-right{text-align:right;} +.pager{margin-left:0;margin-bottom:18px;list-style:none;text-align:center;*zoom:1;}.pager:before,.pager:after{display:table;content:"";} +.pager:after{clear:both;} +.pager li{display:inline;} +.pager a{display:inline-block;padding:5px 14px;background-color:#fff;border:1px solid #ddd;-webkit-border-radius:15px;-moz-border-radius:15px;border-radius:15px;} +.pager a:hover{text-decoration:none;background-color:#f5f5f5;} +.pager .next a{float:right;} +.pager .previous a{float:left;} +.pager .disabled a,.pager .disabled a:hover{color:#999999;background-color:#fff;cursor:default;} +.modal-open .dropdown-menu{z-index:2050;} +.modal-open .dropdown.open{*z-index:2050;} +.modal-open .popover{z-index:2060;} +.modal-open .tooltip{z-index:2070;} +.modal-backdrop{position:fixed;top:0;right:0;bottom:0;left:0;z-index:1040;background-color:#000000;}.modal-backdrop.fade{opacity:0;} +.modal-backdrop,.modal-backdrop.fade.in{opacity:0.8;filter:alpha(opacity=80);} +.modal{position:fixed;top:50%;left:50%;z-index:1050;overflow:auto;width:560px;margin:-250px 0 0 -280px;background-color:#ffffff;border:1px solid #999;border:1px solid rgba(0, 0, 0, 0.3);*border:1px solid #999;-webkit-border-radius:6px;-moz-border-radius:6px;border-radius:6px;-webkit-box-shadow:0 3px 7px rgba(0, 0, 0, 0.3);-moz-box-shadow:0 3px 7px rgba(0, 0, 0, 0.3);box-shadow:0 3px 7px rgba(0, 0, 0, 0.3);-webkit-background-clip:padding-box;-moz-background-clip:padding-box;background-clip:padding-box;}.modal.fade{-webkit-transition:opacity .3s linear, top .3s ease-out;-moz-transition:opacity .3s linear, top .3s ease-out;-ms-transition:opacity .3s linear, top .3s ease-out;-o-transition:opacity .3s linear, top .3s ease-out;transition:opacity .3s linear, top .3s ease-out;top:-25%;} +.modal.fade.in{top:50%;} +.modal-header{padding:9px 15px;border-bottom:1px solid #eee;}.modal-header .close{margin-top:2px;} +.modal-body{overflow-y:auto;max-height:400px;padding:15px;} +.modal-form{margin-bottom:0;} +.modal-footer{padding:14px 15px 15px;margin-bottom:0;text-align:right;background-color:#f5f5f5;border-top:1px solid #ddd;-webkit-border-radius:0 0 6px 6px;-moz-border-radius:0 0 6px 6px;border-radius:0 0 6px 6px;-webkit-box-shadow:inset 0 1px 0 #ffffff;-moz-box-shadow:inset 0 1px 0 #ffffff;box-shadow:inset 0 1px 0 #ffffff;*zoom:1;}.modal-footer:before,.modal-footer:after{display:table;content:"";} +.modal-footer:after{clear:both;} +.modal-footer .btn+.btn{margin-left:5px;margin-bottom:0;} +.modal-footer .btn-group .btn+.btn{margin-left:-1px;} +.tooltip{position:absolute;z-index:1020;display:block;visibility:visible;padding:5px;font-size:11px;opacity:0;filter:alpha(opacity=0);}.tooltip.in{opacity:0.8;filter:alpha(opacity=80);} +.tooltip.top{margin-top:-2px;} +.tooltip.right{margin-left:2px;} +.tooltip.bottom{margin-top:2px;} +.tooltip.left{margin-left:-2px;} +.tooltip.top .tooltip-arrow{bottom:0;left:50%;margin-left:-5px;border-left:5px solid transparent;border-right:5px solid transparent;border-top:5px solid #000000;} +.tooltip.left .tooltip-arrow{top:50%;right:0;margin-top:-5px;border-top:5px solid transparent;border-bottom:5px solid transparent;border-left:5px solid #000000;} +.tooltip.bottom .tooltip-arrow{top:0;left:50%;margin-left:-5px;border-left:5px solid transparent;border-right:5px solid transparent;border-bottom:5px solid #000000;} +.tooltip.right .tooltip-arrow{top:50%;left:0;margin-top:-5px;border-top:5px solid transparent;border-bottom:5px solid transparent;border-right:5px solid #000000;} +.tooltip-inner{max-width:200px;padding:3px 8px;color:#ffffff;text-align:center;text-decoration:none;background-color:#000000;-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px;} +.tooltip-arrow{position:absolute;width:0;height:0;} +.popover{position:absolute;top:0;left:0;z-index:1010;display:none;padding:5px;}.popover.top{margin-top:-5px;} +.popover.right{margin-left:5px;} +.popover.bottom{margin-top:5px;} +.popover.left{margin-left:-5px;} +.popover.top .arrow{bottom:0;left:50%;margin-left:-5px;border-left:5px solid transparent;border-right:5px solid transparent;border-top:5px solid #000000;} +.popover.right .arrow{top:50%;left:0;margin-top:-5px;border-top:5px solid transparent;border-bottom:5px solid transparent;border-right:5px solid #000000;} +.popover.bottom .arrow{top:0;left:50%;margin-left:-5px;border-left:5px solid transparent;border-right:5px solid transparent;border-bottom:5px solid #000000;} +.popover.left .arrow{top:50%;right:0;margin-top:-5px;border-top:5px solid transparent;border-bottom:5px solid transparent;border-left:5px solid #000000;} +.popover .arrow{position:absolute;width:0;height:0;} +.popover-inner{padding:3px;width:280px;overflow:hidden;background:#000000;background:rgba(0, 0, 0, 0.8);-webkit-border-radius:6px;-moz-border-radius:6px;border-radius:6px;-webkit-box-shadow:0 3px 7px rgba(0, 0, 0, 0.3);-moz-box-shadow:0 3px 7px rgba(0, 0, 0, 0.3);box-shadow:0 3px 7px rgba(0, 0, 0, 0.3);} +.popover-title{padding:9px 15px;line-height:1;background-color:#f5f5f5;border-bottom:1px solid #eee;-webkit-border-radius:3px 3px 0 0;-moz-border-radius:3px 3px 0 0;border-radius:3px 3px 0 0;} +.popover-content{padding:14px;background-color:#ffffff;-webkit-border-radius:0 0 3px 3px;-moz-border-radius:0 0 3px 3px;border-radius:0 0 3px 3px;-webkit-background-clip:padding-box;-moz-background-clip:padding-box;background-clip:padding-box;}.popover-content p,.popover-content ul,.popover-content ol{margin-bottom:0;} +.thumbnails{margin-left:-20px;list-style:none;*zoom:1;}.thumbnails:before,.thumbnails:after{display:table;content:"";} +.thumbnails:after{clear:both;} +.row-fluid .thumbnails{margin-left:0;} +.thumbnails>li{float:left;margin-bottom:18px;margin-left:20px;} +.thumbnail{display:block;padding:4px;line-height:1;border:1px solid #ddd;-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px;-webkit-box-shadow:0 1px 1px rgba(0, 0, 0, 0.075);-moz-box-shadow:0 1px 1px rgba(0, 0, 0, 0.075);box-shadow:0 1px 1px rgba(0, 0, 0, 0.075);} +a.thumbnail:hover{border-color:#4183c4;-webkit-box-shadow:0 1px 4px rgba(0, 105, 214, 0.25);-moz-box-shadow:0 1px 4px rgba(0, 105, 214, 0.25);box-shadow:0 1px 4px rgba(0, 105, 214, 0.25);} +.thumbnail>img{display:block;max-width:100%;margin-left:auto;margin-right:auto;} +.thumbnail .caption{padding:9px;} +.label,.badge{font-size:10.998px;font-weight:bold;line-height:14px;color:#ffffff;vertical-align:baseline;white-space:nowrap;text-shadow:0 -1px 0 rgba(0, 0, 0, 0.25);background-color:#999999;} +.label{padding:1px 4px 2px;-webkit-border-radius:3px;-moz-border-radius:3px;border-radius:3px;} +.badge{padding:1px 9px 2px;-webkit-border-radius:9px;-moz-border-radius:9px;border-radius:9px;} +a.label:hover,a.badge:hover{color:#ffffff;text-decoration:none;cursor:pointer;} +.label-important,.badge-important{background-color:#b94a48;} +.label-important[href],.badge-important[href]{background-color:#953b39;} +.label-warning,.badge-warning{background-color:#f89406;} +.label-warning[href],.badge-warning[href]{background-color:#c67605;} +.label-success,.badge-success{background-color:#468847;} +.label-success[href],.badge-success[href]{background-color:#356635;} +.label-info,.badge-info{background-color:#3a87ad;} +.label-info[href],.badge-info[href]{background-color:#2d6987;} +.label-inverse,.badge-inverse{background-color:#333333;} +.label-inverse[href],.badge-inverse[href]{background-color:#1a1a1a;} +@-webkit-keyframes progress-bar-stripes{from{background-position:40px 0;} to{background-position:0 0;}}@-moz-keyframes progress-bar-stripes{from{background-position:40px 0;} to{background-position:0 0;}}@-ms-keyframes progress-bar-stripes{from{background-position:40px 0;} to{background-position:0 0;}}@-o-keyframes progress-bar-stripes{from{background-position:0 0;} to{background-position:40px 0;}}@keyframes progress-bar-stripes{from{background-position:40px 0;} to{background-position:0 0;}}.progress{overflow:hidden;height:18px;margin-bottom:18px;background-color:#f7f7f7;background-image:-moz-linear-gradient(top, #f5f5f5, #f9f9f9);background-image:-ms-linear-gradient(top, #f5f5f5, #f9f9f9);background-image:-webkit-gradient(linear, 0 0, 0 100%, from(#f5f5f5), to(#f9f9f9));background-image:-webkit-linear-gradient(top, #f5f5f5, #f9f9f9);background-image:-o-linear-gradient(top, #f5f5f5, #f9f9f9);background-image:linear-gradient(top, #f5f5f5, #f9f9f9);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#f5f5f5', endColorstr='#f9f9f9', GradientType=0);-webkit-box-shadow:inset 0 1px 2px rgba(0, 0, 0, 0.1);-moz-box-shadow:inset 0 1px 2px rgba(0, 0, 0, 0.1);box-shadow:inset 0 1px 2px rgba(0, 0, 0, 0.1);-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px;} +.progress .bar{width:0%;height:18px;color:#ffffff;font-size:12px;text-align:center;text-shadow:0 -1px 0 rgba(0, 0, 0, 0.25);background-color:#0e90d2;background-image:-moz-linear-gradient(top, #149bdf, #0480be);background-image:-ms-linear-gradient(top, #149bdf, #0480be);background-image:-webkit-gradient(linear, 0 0, 0 100%, from(#149bdf), to(#0480be));background-image:-webkit-linear-gradient(top, #149bdf, #0480be);background-image:-o-linear-gradient(top, #149bdf, #0480be);background-image:linear-gradient(top, #149bdf, #0480be);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#149bdf', endColorstr='#0480be', GradientType=0);-webkit-box-shadow:inset 0 -1px 0 rgba(0, 0, 0, 0.15);-moz-box-shadow:inset 0 -1px 0 rgba(0, 0, 0, 0.15);box-shadow:inset 0 -1px 0 rgba(0, 0, 0, 0.15);-webkit-box-sizing:border-box;-moz-box-sizing:border-box;-ms-box-sizing:border-box;box-sizing:border-box;-webkit-transition:width 0.6s ease;-moz-transition:width 0.6s ease;-ms-transition:width 0.6s ease;-o-transition:width 0.6s ease;transition:width 0.6s ease;} +.progress-striped .bar{background-color:#149bdf;background-image:-webkit-gradient(linear, 0 100%, 100% 0, color-stop(0.25, rgba(255, 255, 255, 0.15)), color-stop(0.25, transparent), color-stop(0.5, transparent), color-stop(0.5, rgba(255, 255, 255, 0.15)), color-stop(0.75, rgba(255, 255, 255, 0.15)), color-stop(0.75, transparent), to(transparent));background-image:-webkit-linear-gradient(-45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);background-image:-moz-linear-gradient(-45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);background-image:-ms-linear-gradient(-45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);background-image:-o-linear-gradient(-45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);background-image:linear-gradient(-45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);-webkit-background-size:40px 40px;-moz-background-size:40px 40px;-o-background-size:40px 40px;background-size:40px 40px;} +.progress.active .bar{-webkit-animation:progress-bar-stripes 2s linear infinite;-moz-animation:progress-bar-stripes 2s linear infinite;-ms-animation:progress-bar-stripes 2s linear infinite;-o-animation:progress-bar-stripes 2s linear infinite;animation:progress-bar-stripes 2s linear infinite;} +.progress-danger .bar{background-color:#dd514c;background-image:-moz-linear-gradient(top, #ee5f5b, #c43c35);background-image:-ms-linear-gradient(top, #ee5f5b, #c43c35);background-image:-webkit-gradient(linear, 0 0, 0 100%, from(#ee5f5b), to(#c43c35));background-image:-webkit-linear-gradient(top, #ee5f5b, #c43c35);background-image:-o-linear-gradient(top, #ee5f5b, #c43c35);background-image:linear-gradient(top, #ee5f5b, #c43c35);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ee5f5b', endColorstr='#c43c35', GradientType=0);} +.progress-danger.progress-striped .bar{background-color:#ee5f5b;background-image:-webkit-gradient(linear, 0 100%, 100% 0, color-stop(0.25, rgba(255, 255, 255, 0.15)), color-stop(0.25, transparent), color-stop(0.5, transparent), color-stop(0.5, rgba(255, 255, 255, 0.15)), color-stop(0.75, rgba(255, 255, 255, 0.15)), color-stop(0.75, transparent), to(transparent));background-image:-webkit-linear-gradient(-45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);background-image:-moz-linear-gradient(-45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);background-image:-ms-linear-gradient(-45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);background-image:-o-linear-gradient(-45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);background-image:linear-gradient(-45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);} +.progress-success .bar{background-color:#5eb95e;background-image:-moz-linear-gradient(top, #62c462, #57a957);background-image:-ms-linear-gradient(top, #62c462, #57a957);background-image:-webkit-gradient(linear, 0 0, 0 100%, from(#62c462), to(#57a957));background-image:-webkit-linear-gradient(top, #62c462, #57a957);background-image:-o-linear-gradient(top, #62c462, #57a957);background-image:linear-gradient(top, #62c462, #57a957);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#62c462', endColorstr='#57a957', GradientType=0);} +.progress-success.progress-striped .bar{background-color:#62c462;background-image:-webkit-gradient(linear, 0 100%, 100% 0, color-stop(0.25, rgba(255, 255, 255, 0.15)), color-stop(0.25, transparent), color-stop(0.5, transparent), color-stop(0.5, rgba(255, 255, 255, 0.15)), color-stop(0.75, rgba(255, 255, 255, 0.15)), color-stop(0.75, transparent), to(transparent));background-image:-webkit-linear-gradient(-45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);background-image:-moz-linear-gradient(-45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);background-image:-ms-linear-gradient(-45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);background-image:-o-linear-gradient(-45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);background-image:linear-gradient(-45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);} +.progress-info .bar{background-color:#4bb1cf;background-image:-moz-linear-gradient(top, #5bc0de, #339bb9);background-image:-ms-linear-gradient(top, #5bc0de, #339bb9);background-image:-webkit-gradient(linear, 0 0, 0 100%, from(#5bc0de), to(#339bb9));background-image:-webkit-linear-gradient(top, #5bc0de, #339bb9);background-image:-o-linear-gradient(top, #5bc0de, #339bb9);background-image:linear-gradient(top, #5bc0de, #339bb9);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#5bc0de', endColorstr='#339bb9', GradientType=0);} +.progress-info.progress-striped .bar{background-color:#5bc0de;background-image:-webkit-gradient(linear, 0 100%, 100% 0, color-stop(0.25, rgba(255, 255, 255, 0.15)), color-stop(0.25, transparent), color-stop(0.5, transparent), color-stop(0.5, rgba(255, 255, 255, 0.15)), color-stop(0.75, rgba(255, 255, 255, 0.15)), color-stop(0.75, transparent), to(transparent));background-image:-webkit-linear-gradient(-45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);background-image:-moz-linear-gradient(-45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);background-image:-ms-linear-gradient(-45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);background-image:-o-linear-gradient(-45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);background-image:linear-gradient(-45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);} +.progress-warning .bar{background-color:#faa732;background-image:-moz-linear-gradient(top, #fbb450, #f89406);background-image:-ms-linear-gradient(top, #fbb450, #f89406);background-image:-webkit-gradient(linear, 0 0, 0 100%, from(#fbb450), to(#f89406));background-image:-webkit-linear-gradient(top, #fbb450, #f89406);background-image:-o-linear-gradient(top, #fbb450, #f89406);background-image:linear-gradient(top, #fbb450, #f89406);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fbb450', endColorstr='#f89406', GradientType=0);} +.progress-warning.progress-striped .bar{background-color:#fbb450;background-image:-webkit-gradient(linear, 0 100%, 100% 0, color-stop(0.25, rgba(255, 255, 255, 0.15)), color-stop(0.25, transparent), color-stop(0.5, transparent), color-stop(0.5, rgba(255, 255, 255, 0.15)), color-stop(0.75, rgba(255, 255, 255, 0.15)), color-stop(0.75, transparent), to(transparent));background-image:-webkit-linear-gradient(-45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);background-image:-moz-linear-gradient(-45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);background-image:-ms-linear-gradient(-45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);background-image:-o-linear-gradient(-45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);background-image:linear-gradient(-45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);} +.accordion{margin-bottom:18px;} +.accordion-group{margin-bottom:2px;border:1px solid #e5e5e5;-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px;} +.accordion-heading{border-bottom:0;} +.accordion-heading .accordion-toggle{display:block;padding:8px 15px;} +.accordion-toggle{cursor:pointer;} +.accordion-inner{padding:9px 15px;border-top:1px solid #e5e5e5;} +.carousel{position:relative;margin-bottom:18px;line-height:1;} +.carousel-inner{overflow:hidden;width:100%;position:relative;} +.carousel .item{display:none;position:relative;-webkit-transition:0.6s ease-in-out left;-moz-transition:0.6s ease-in-out left;-ms-transition:0.6s ease-in-out left;-o-transition:0.6s ease-in-out left;transition:0.6s ease-in-out left;} +.carousel .item>img{display:block;line-height:1;} +.carousel .active,.carousel .next,.carousel .prev{display:block;} +.carousel .active{left:0;} +.carousel .next,.carousel .prev{position:absolute;top:0;width:100%;} +.carousel .next{left:100%;} +.carousel .prev{left:-100%;} +.carousel .next.left,.carousel .prev.right{left:0;} +.carousel .active.left{left:-100%;} +.carousel .active.right{left:100%;} +.carousel-control{position:absolute;top:40%;left:15px;width:40px;height:40px;margin-top:-20px;font-size:60px;font-weight:100;line-height:30px;color:#ffffff;text-align:center;background:#222222;border:3px solid #ffffff;-webkit-border-radius:23px;-moz-border-radius:23px;border-radius:23px;opacity:0.5;filter:alpha(opacity=50);}.carousel-control.right{left:auto;right:15px;} +.carousel-control:hover{color:#ffffff;text-decoration:none;opacity:0.9;filter:alpha(opacity=90);} +.carousel-caption{position:absolute;left:0;right:0;bottom:0;padding:10px 15px 5px;background:#333333;background:rgba(0, 0, 0, 0.75);} +.carousel-caption h4,.carousel-caption p{color:#ffffff;} +.hero-unit{padding:60px;margin-bottom:30px;background-color:#eeeeee;-webkit-border-radius:6px;-moz-border-radius:6px;border-radius:6px;}.hero-unit h1{margin-bottom:0;font-size:60px;line-height:1;color:inherit;letter-spacing:-1px;} +.hero-unit p{font-size:18px;font-weight:200;line-height:27px;color:inherit;} +.pull-right{float:right;} +.pull-left{float:left;} +.hide{display:none;} +.show{display:block;} +.invisible{visibility:hidden;} +.space-right{padding:0 8px 0 0;} +.space-left{padding:0 0 0 8px;} +.CodeMirror{line-height:1em;font-family:monospace;} +.CodeMirror-scroll{overflow:auto;height:500px;position:relative;outline:none;} +.CodeMirror-gutter{position:absolute;left:0;top:0;z-index:10;background-color:#f7f7f7;border-right:1px solid #eee;min-width:2em;height:100%;} +.CodeMirror-gutter-text{color:#aaa;text-align:right;padding:.4em .2em .4em .4em;white-space:pre !important;} +.CodeMirror-lines{padding:.4em;white-space:pre;} +.CodeMirror pre{-moz-border-radius:0;-webkit-border-radius:0;-o-border-radius:0;border-radius:0;border-width:0;margin:0;padding:0;background:transparent;font-family:inherit;font-size:inherit;padding:0;margin:0;white-space:pre;word-wrap:normal;line-height:inherit;color:inherit;} +.CodeMirror-wrap pre{word-wrap:break-word;white-space:pre-wrap;word-break:normal;} +.CodeMirror-wrap .CodeMirror-scroll{overflow-x:hidden;} +.CodeMirror textarea{outline:none !important;} +.CodeMirror pre.CodeMirror-cursor{z-index:10;position:absolute;visibility:hidden;border-left:1px solid black;border-right:none;width:0;} +.cm-keymap-fat-cursor pre.CodeMirror-cursor{width:auto;border:0;background:transparent;background:rgba(0, 200, 0, 0.4);} +.cm-keymap-fat-cursor pre.CodeMirror-cursor:not(#nonsense_id){filter:progid:dximagetransform.microsoft.gradient(enabled=false);} +.CodeMirror-focused pre.CodeMirror-cursor{visibility:visible;} +div.CodeMirror-selected{background:#d9d9d9;} +.CodeMirror-focused div.CodeMirror-selected{background:#d7d4f0;} +.CodeMirror-searching{background:#ffa;background:rgba(255, 255, 0, 0.4);} +.cm-s-default span.cm-keyword{color:#708;} +.cm-s-default span.cm-atom{color:#219;} +.cm-s-default span.cm-number{color:#164;} +.cm-s-default span.cm-def{color:#00f;} +.cm-s-default span.cm-variable{color:black;} +.cm-s-default span.cm-variable-2{color:#05a;} +.cm-s-default span.cm-variable-3{color:#085;} +.cm-s-default span.cm-property{color:black;} +.cm-s-default span.cm-operator{color:black;} +.cm-s-default span.cm-comment{color:#a50;} +.cm-s-default span.cm-string{color:#a11;} +.cm-s-default span.cm-string-2{color:#f50;} +.cm-s-default span.cm-meta{color:#555;} +.cm-s-default span.cm-error{color:#f00;} +.cm-s-default span.cm-qualifier{color:#555;} +.cm-s-default span.cm-builtin{color:#30a;} +.cm-s-default span.cm-bracket{color:#cc7;} +.cm-s-default span.cm-tag{color:#170;} +.cm-s-default span.cm-attribute{color:#00c;} +.cm-s-default span.cm-header{color:blue;} +.cm-s-default span.cm-quote{color:#090;} +.cm-s-default span.cm-hr{color:#999;} +.cm-s-default span.cm-link{color:#00c;} +span.cm-header,span.cm-strong{font-weight:bold;} +span.cm-em{font-style:italic;} +span.cm-emstrong{font-style:italic;font-weight:bold;} +span.cm-link{text-decoration:underline;} +div.CodeMirror span.CodeMirror-matchingbracket{color:#0f0;} +div.CodeMirror span.CodeMirror-nonmatchingbracket{color:#f22;} diff --git a/web/img/feed.png b/web/img/feed.png new file mode 100644 index 0000000000000000000000000000000000000000..d1705aa348ddda5b251a7d744658d519776c72d6 GIT binary patch literal 735 zcmV<50wDc~P)IU`$e#o_VA-9m(PZZFk*oXyxlWud<$JOkN$?Ky zy0`pq6%@i|0Q-Pa79W3@<@=N33J#I{VUYarhUD1fUBqfpjoc)#v57b_9j1yUCf4x% zB|Eu0hvZ- zJ5wZ$VU#*d%9skme-p%)F`@e+-+zszrjr~UA~_PSMC|$cCPti|He!xNu7efiI$B=B z)7;HSysZf!*Es#5CN(=j%$-WKHK7i%rEmo|)>^`` zQ2_)KkeCd|cueSt@b9|Bwk450@Ohe+m-67ACn0)8R(e2L3Ii983UiPTAkf3&w+vwY z6o>=)sx$KKqcV}>Np@p@8f1$cr&;X2PZcFMzI?z?51v`>ghk-B>T{7nKtO-(ka6d)E%wIZgT{zyP$8Aj}n= RwRZpj002ovPDHLkV1kcRS4IE; literal 0 HcmV?d00001 diff --git a/web/img/glyphicons-halflings-white.png b/web/img/glyphicons-halflings-white.png new file mode 100755 index 0000000000000000000000000000000000000000..3bf6484a29d8da269f9bc874b25493a45fae3bae GIT binary patch literal 8777 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 B7G?kd literal 0 HcmV?d00001 diff --git a/web/img/glyphicons-halflings.png b/web/img/glyphicons-halflings.png new file mode 100755 index 0000000000000000000000000000000000000000..79bc568c21395d5a5ceab62fadb04457094b2ac7 GIT binary patch literal 13826 zcma)jby!@B+o%-915yyF0YFyB4?Ne(CRg z-#O<#&wb84`D17H-t*49Gi$BAvS#fBDJx22pcA4aAt7PN%1EdpAw8RXk~3bSJRMO{ zLOPzl2q2PL5H+wV#M#IJgd}PLHU^Q&+8CLER6#~2F82K(0VJg7mlo<;5G{o-d_b@b zi_u>l7MP9Q6B-FgKp19c1hfJ{$c#Z|7Pf*EM~$r%WELiZ6q=k0YzlVbAae^DR|k-q ztD-v4)e6XKLLn?fCII7mGGGIO7?HtjtZg0nV1g9?*yVeY|6XRLAp1uJVkJoNAEdMt zl*z=w4j?j47B*%e8y7nn*Jl>?&uqM(d6~#Qv9YtUvVUS_<7Q@Os%DRy=VF;OnbPZB&l+~Sg=;$olKxc@r)Yv8{FpRTZ&JYl7zK5_7had2=;im|h^ zOS1E@^NNabNpOiuiHY)jW|#UmR@T-LVq^;h{dM{mYw=&$PyZv9Puu}y1OYp!gTdDS z?kdXWUuEt5GU<9?B8*-aqzJHUs!SW&!V4sCD=ZRit}=F za#FB9kud@CK`bEFpnvsHQESM*Bx{Smy@b!&$kyyB9n2;mQzNJ~ghI&7+QrV?0tmKs zG<38vvbHufF>%IThd>Rse#s3_OPbdF5nnAWt zL)hVIta5&^8bd;2&ytl8Rfo+Tcz~_-Bx?#ZE2<3oUBe})+zpAGX&=O$_aCJBN!CBt zv~LUxtg{dH^uI`jCU#YZa*6x&AyIg@k@bxImc$%rVne48BslqY$+TLFj(v37h7yfx z$^jmG#g_Rs?ETA?`?LMJ^OpUDIY(RQdGlgR?XG$OKf8PyqRZyid2g!3%@a^C1igpD z2NKzV@|1wiF}EtKQRH|$CJJ9)q3e}#g7m#Zl(d`W;iCBregW~kz}j^J z#1PLChA^$dal^V@@cK(w}dv%n2!w4^wV*y35J)-xE{$fXwc@pa}RzJm5M)#tr)iJZA7 zBA<^jjwJWvLx1>RPDIS^k*z$pgpiQZ-O2S}m#&N|A4@|nID3F1~ z+{<)-J1C8b8ezW2FI#gotv2}C#wQERQ(Bd4_} zR$QREVi8_9nE3}6@Vks1@*cVLJrSLt#`lb0$M?!xg%%C;C!jFg2$sX)U0bprNA043 zt1cd;7oNIanP3?<(O0mgAc`)87;35OB;`nL3-yw7Fq`<#Hqz;v+Mj? z%y|w07f93V#m`17f@xa3g&Kss@<20hE22A#Ba2fDjWQe?u<#pkgd4DKg$db>BIa`q zqEeb}1&O#H`nWg^GT=P^c&c$+@UcRMn~k-y&+aN^ic}0j)s9vGd$m}}SL4iw!tr4e z74SRhmFujYvTL$e!;=bil=GRdGp3UA1~R?@@XL?>oK21E-g3xj0Gu;SC|l|8wmd~d zG@8i53Tu3s9ldBp@%(!A6E=rZOl&LAvv1Nkj=ysQ(9(~g-8X6}A>#Y#1a(KQ1TAh( z`*b|k%zN|vOG$C7_4PTiy8Lhr&rZ~I!*iV zG+W%bI&HR#n{T~n|CLrV#?k5#Et)n4f;XdM7~@Er-K9uS8vPNM>uZUibWxth=wqXp zt{0wO*|bZs%9J3Y;Tj4)?d>OBZ>YUb@tFh)1KiKdOeB10_CBOTMml4P#hsP|NnH`$ zn8C$aG#8|gqT#i}vYTeH^aF(r1JFKcz$K3~!6}2FX0@^RHCL+33v-FhYXz#e!VN4~ z3pAY$kL`HvPAaz%ZKvX4N680T6G=`cF|!UT=iU?gUR}#z>rLnIjH4UiW&X!Z2Ih$B z#MDHe_%!Yd4!bTFMGeNcO(+vEfWe=Y&#$#Dh_vk`s>hf<^Bj2jofdTiH?Cvh55o&b zE2N(49<70oDa2DrZnfjbhn{Jl;CT6QCOL517jsNXxh ztk>S%Nl!1kKE!_Y1E%82zuk(#fmi4VMZZ|C9XG#t=_a%pE(?AS@K%j{n=lj?kEKY< zW|3b0>CWE2bkN^RapDK@3*dIhwI~%Mb87ZxnF|-bX;tNwFf}3s_Ti{S8}(TUA=c4( zY2Z!UZS&H=Pk;r%irg?jcz?{s!|V*#QA4{2Fzp37$r+}Z-K{*#DE7B^Inz!%Q9nU} zU%!E(b~61SJ_R5KSY88G!*+2Crm?Vp1DUFviD)lB1c&Atk+dP7K7{oK1?N#HTx(Jx zis^|e#sUW_TPZE3IGu1R+xV`&BV&1NNkrD4j;(NEKdkpSdz8YLZ}ya474taW7yY@8 zsA-+N{3&saE60RSnI802s?NYn0KiULv+`y9hNB!6%B_qCFHMhVOa;O!ge!LzPKbk( zbOnDN{s12ui~i)C55qt9+S4F%_rqna@M}~Kvh3z-^-K67%2T=8H8g<_=LYj#`6IF< z&#}t=5w#4@^{y}B4J8rm?|c7nu!l2bJZ`U-W4@aT)V{Bm!c%#8HewtNPwZ4>dYBdQ z$`?MJMLJt7`j`p7Y7C@WWmQu(B(vQ&FMa>ZZpX>;(|`+m?2Yl|fhX43DejM5BMl`? zr(v=9l4R8Y3}+Abj6x1X^T?$#`1;s>I24lFFFn~&HRgQK%%Ey(mn=20z;U>um1z~Q zJG*-wAw;tG!?{U#JnA5M5rX*u%NF+}y;0xPbTQppWv;^8{aGUxG$gD!0YAlLo;KuE zkFzemm@vHoQYYv<_b|t(esPHC%z-nLF5Q9^?&hl?0?g0d9hVSdDc=X~B?dQzaRfp; z+2*{_ss{}_cv+!%k7WX20;r5{GER*rd{={D1l}-^Se~*W+_M}?z+w9HX;SR@AB6by zI0}UM&nJY!1O!_&a8xRuf`=Drhp4bwFD4GN;7|wXEpdq}@{E+u#{VT}-UEwtWPkxKl^Wa8Qi?#AQLxY4w+?_Y4 zd1glMwHFc0bglfOS-7V_h zjsOP>)fG0TPo!`fIkeDn-b_WlxJH)NqQqX{Cjt1+PPI$%JFTSWT#$Mj_6O?PY#fK3 zMy2&j?Y~|hc!Xla$G$#xZ0%AyTx!yYt=5!)nk&0@J-$=t?&(X;8%~rQYD<{9lr1z zs@8X~WZq3R1+cmT>`KWeE&^_UF>|q&Ay^}*sN63yo7B9nz}D!eQt$6m26sKn>O$P zmvsnQ7b9nJQ46`zs$s*Wtto!ux2}?)U%;Z5%hb7!$w!&8C`>TRG+*DdD0JLss5Xff zBThm&kGp*Qxmrsc3GjV@6TVB6)l|r!wyRJP)U%eM@Of-k4FDYmUY)1+7EUyRGbs_` zleaIf78kfz<{vx`Ls^b4Ogd8_rSR#I2AH%NK)|Vfh#}z~2k0bJcEvc$3He?p;bGVK zyam;#Nl5X&J8j^k<~QS18sq4NPR$kE>m%=`^Ki#+ieKpZYF?TTM#Jv80{<7eYn$&q2aN=p)lq6fG9}Dv2}g_RSVx*Iv-0C}kEWsUw>e$24l?hUH3zqG z2Sa%=_ql^t*`t3yW7`PZ(-yol6mNfiUV1c7e)%BgzOh%HQQd^uq9gC3O*vPSi&V!$ zuJ-gy-6_@)r?@+~#wK_V|QHgllM9B^dZanlnPLZqhL-@Wql1PDLO_j>7Nz?o z+_&sbFV42Gr7019rPl3IUH2}h2Wl+=p46k?>x70Pnt9Gn_CduyDht`=S4b}9&F^387k|mAZg2^t9(aD+I+W{ z#iMaSJ%Slg$*$}d;|(Q|7`BKm3z9) zh-*c!-WX<4{kD>(FE8TvP+#HUL}QrAKt*0vVL7!~ovM)?Ur`?N{))Ew;yk>PkfjG- z*)^I$qo~mV?U!~Gwi(1*M)0+vT9Jy~`kGC^1<}kh2R4PgR^?53j%>|Ns{2kn=ewGn zvPvguwaHo(xrDKI-r{x~q$onf~4u$MK|{q*`g)sDyNO(})q!R?7xZH;c=m6iWiHEU8Q0KT-e zKaAgECVApd!3(FjK2!e|a^g^-5f7L7jB^GFCrwQ_*B`o?=jeoDN_*x+cXrv8gf$36NQ*!QC!Kwg5~wLak^RyUvu(CifB7CA>(1lu6}+@1^DvB!>VYXX?9Ys*9wd&0abG}7TGJ`WsH;FX_s&}n4v(1m|Q)++R8J>#?XO`$8g+3q` zwN~X&6{@){!8Q1(2!in4P8(_gYuOhhFGZ;=C-6kTb%~vBQQ*b-=z*J+>E;6ujm;wX zvb?kY(oC=+ca4)i4a#h@{dTzWSLS3ag^66Gpkn{ke!AC9A{1jMRP%OcQ)<<@nxJH} zZIr?|jBinPoiR)snBOcecjcb@Wuh3my1iVRzl-u;gB}~Rjhub`?Cfu)nPL3L+b$kL zO32z2XK-0_shy`%ZT9<2V<1qI5Rel|E7W{`Hg#M|m&O0`Ua-&p;v}tapS>wTE*On` z756q!EO*AN?oxlV&@ybUeVWd1q~Tg`kpqG}F@V;VsN#&)R^`V00X5}(4*PmNqShEg zQih?Ga1nmgvx@-!Wngeg;A+L{F-(i zf_X7=?WU?j|23>ePpP8OODXHU69Lw_MmSudzHtic8)MWn1BPdI_Ae4ykPB0u9il*G zJ?$Q@);~I`)dd=AQuaxcTe2HSse|E|ii5U_*5>3~bz~#PL%91W(Nyd|=|ZA6*w`c7 z$R1sRD@XhF^&4gJ#exDQRqq3%$Y|oPc!wXV-=n37^UJ=Olj%RP#gEAol|$!AAbjxW zXq&hxEZQyPL4JOa6I*343W#)9&u%!GDhw_3B>yJ7)O`Ae76GRZenb(|eWOMZU_spF zuD{--T)B0<*4E?|ri0F<=p!twyj!hH;HlUN0Htt?hj8zO#!~F83W|K9Lvq z3{RaoPbjaDFu@z{^qW3cjj7kS$GR|;9I%R~LZ@6(ENvrteZFbkkow-9p%qZBx>J+M zq8}TEyApxpU@n((iw0bRrJvc6Cd$y8wbf4?-w4%S5$Slysc^DTKW~+Y`!?zI;_DZL zV9KO0`~P=A@%O2`KlPzF{xwsO>z5=mqo0Z23o-D!NekrdbEa^%TfV56v|FDM?4cKX z@rrk@JJ?1_5irzO66hc^C*{*Ke&o=Ijw!R*ZAgtQC0ezeL17SocQu_m!6VUsNTcVG zpwRaCZCIJ=OR~@li`X(c8LO9k&wjr&0Gd_GRou<{3Hu`Css}PU72iy4PZtFd(l9VK zR)fk*&dPTy&yMX{o8@~bPnX0_Q@UX-RN+o|sC$;fpA|xTEugMj7@)yJ{4@bO3x^+O zH0OTqp82(iEah+>0QWS z$@9x&MNFG_ayE3OJxi@l$%9i2{OAD1go7t5}Sv8p*L*?_XV-Inr zpe~mOfBekpsM*iZA4B0U-_aDDuQGQ>$du+c-pHfXyBaLv@T`?*-je(+>E!q1bXa1q z14-*PWvM+oFg(z{YlRS2em5Pw1U1&De`{t$Pg={frAk6|^cDRB$0e*ut zvJ=N0<2rG{&|2ECVoU=~V0R9rfUWk0Z${R3(A&#kkMCPoz`s?k7N+_8!1v32J*zyO zR9Lv8#NK_E; zsf^8eBN5l`rT5}^m`=Z(Oaw_(G`KLa6xX%V@W0keWi;An4+N4QThS_k{n&Vyk{0!?N_d)(8r)?>J|F`-ZusfRTzNO)+h%L=-)$92e&Ck?1oAE(~~ z$-n~o0g*n;RB*mqiaAn=Wlm0w2D6Yu&4fY#;MU1bvU(~NK6m1FUoPk+w;|b?nzGkO z_PUIl=pfDRhrLvm<;sb9>BFB~Sc4oJ;hS&xb#O~;Q7(2b8< zQ9Hg8isf_ddK#6OY$>r#Kxz@D+gtkY>hy|#o8Z-=^bH`o)WbuhhdK98@PHbw2Zt=7 zV$-oYeC$U<;|pnaU4187;%~hxdnq*JOnEGam?8hex6Iy=ZlWGzZv-4 zoJ{KX4x(J5=P>qor+5;Qvhp3GFBpXJ9fO3crB!vqua&Y$iFJdsGsQL15;##Wtx)a! zYY)JHGBW`d%x6ZI`{f6_r^+OdBbZk{<-B0y4iS|--^SLDWVMu&VT?M2Z|8*E=pfeq z);Kt;$?dDKuIJvdZG|d_=QWvbk?X!+UMjWng_S4uk_M}7f`V03>h!f-=Qxpm9ReU7 za!V9@Dytw&Y;Dn_tG@+O7`;DiSse1^ilx|o^~@+CRqBxKgXtuFTdkV9s}V3?Sy6{S z*XctI(Eyb3h^4g}R#0C=Al$1x3GX$~3fA}}eX>>DF+LFj4zJ()a-xd1d6P?W{`m*D z*x%43iLpP6D8xOj1Z<^h)%1C*{`|uBM zAKe~zJa>JT4Tqn|wxn>-+P9_i;yHBP@*ap6jMJgu7>d2GIq{>J`g;o%tKlmpM-RrSw{_pAKK; zSq)!`7M=VE#*z4?xSugikUTPD}y7GXhB{U`6@}s8z0d@C`F9EQ3#s|A3?{zk{KOin$?&5UgsTdnL zO1i!hQhbL?LiIIX*RA*iV$~) zB>zWXKyBeJC4}W_3SGU)PQseJzO;g~99>U&xx8@V2Qp$StzgO_?GxT!9UmQV2vt-^ zkab;==s?$tI#Akh4J+G|pAPYZQ5vA(8|@a9T2-p=)uPN{@6f@tmW11S)1s z!h%|zyG6Dc);F%IdWaK*t#r*khD51^8Ay)ixzUtt=#AX2VmjE zOFg-|2AdD>SmMSf?bo9uRB)zYaT{m9I%7Vs)$dLGX>bj<#I2?S8OUQRh(mJrJhADZ zT_^gL-3m0*JIokIbOUyiA83%98nW2{Wp2BW5akVi?klylc_3UwSpIlPTwb zEIG-t+EJ;a3(OZ-sGt+R_j^Z;x|qvjBr|7-{wn4kOG&^GRt$u`kMx zzV;Zy-UA7<xMJg(rd2`sKuS9&FoYuUoug>t*^~eJTjg>pWcBUABu-7%@{xM zICt)A_$aq9KQ1!{${`~7GXd+8ZDmu`rjx$oiC@GP<}zwn_dR8&M)WQdC&iw3E)YGG z>3e7ZNZUGzmYhW2?kKOPphuHB2q3zn7e!n3V8t*?@hpE5fc7snCI0l&iE)SiOs(W%=b1^y8b;aHjB&KaO|McF*t%v`zlW*&h5@1@_C^ zu@=`+#rV2TS56EeCh=>uP<-lPc^}fc208qOOb9~TKo;7L zA~1!rYZOt)&{UFvJI5a$VIW+Rn=eIQsZ^sU)8hNGK};PpknpE84hIhht07)(ER+4_ zxLhMx$;116i@tQodN*XTcFS{`!fPjk0n} z1udu3=k`@uaQK?j)YF!Z2n=fc zY`~>$*#BZX+mGk=DFM0Z|L3%DK(H(w+__!4UF`kf9Jf(YzE zR+p>6%a^g;g${|zdmK6-Gj(({7pl{TV*3&Z!Tg4cKvV0j;*Hb(Z#qmw#wdm`wZ8ts zjIUMJ`h#Vh4=S1zDw~a^H)q+6{ z#Hz!oYPE7ZFi~~AG7n#q$;s}pANs@VyV5vhU2&d`=@Es*pQh}pgHHCW`KB+GEa9ck zW`9DlW`Wvi6+8Jp#bM-ebD50CjykM&Y5Nb{=n_#L!>gatGhc`j`D$a>B*m5@1=_tY z1!7V55YfU?hSlU@@flw?^BFXCnLzGQ5nOAvVvjQP>otW|mQj7Pc1evAEdaVt_O7si zLf)Opv3>@Ky-^Y?)9yR;H}8pcbX&{bu?-8JE^rhUOvU2ko_d9PU&9pXO^>cRZ#zZo zCkq39jb4}nCKp>1oQXcr)#BC}eH;uS!al|lo`b0S;{)B1C!B9NGJ7sRRf8u~;@IH-gDB{~GwmgyVn+go-vI%&pi z&YpjGP!eesJV1P}>w0bDVqj#o(Td$rcY=Dy(vmsW4Lu7vblFZ1AkwFt&8yEeH+$MF z-`f?Kpo$}2=fdkh7scLN3X|LFczR*OC>3vQN$>T`HJ{7Et7(nPTo6piDNA7Mqp=3RT0d>DNW?+-b;wgbWc@xKrOgn@*hcG0Bl300~zM z1cqJaF;{x*c%r%A4-dBquj5*G&bu!gKwoO_nS;LQT^1W`?RvhSP_8$3==>+aY-PTt z>bq-vSj!54>+X4cy9uFc7n4e89$B@NcVD5A-ZJOxHgc`}0Xekmrnv zFXt>J(de%xG=HqM%#sdc`1MGQF^WDoQiWxMaI(4dHmX&4!LlBo`(Of>F#wiHG2!fZ zvB{2Q#2#f}GF24rrVMQV1q+OtDek8cd8z74b#rGk91~90FBtkjwVnDn53id&|26Z`rO1<>1bMNki zIionO>*HS1J4(aUYgwsF#kSB3LoKM6=_L4awnOEIti-PdFWHKvSHkYopzzkmO{#f! zBCp*D{8xF0vlect8R3v&sfl^TuDXSf&P%wC74{#9?N5X!pC24A7h4?)2V-9N|c{C;w5wl|z8<2X0es$`*M5j(oF{0r&32 z`U~-Q8qfbA;nM54%Pd-|nK@0LdSA=5KyqV*g)A>?W!gQiNj|kKfej`z+TWeH!`Hpg z4x)z(>^8nLqTC<9RW5iJvCjWHv7}1afGXDDjvlcDu^s2txL;E`C?VN3k?3wy4?Rg4 znmrvze0;v4z1-miFC~klv>fjZbDDi1Sb3^nk~4(v>AQ0kEgcS!BT@@JFn156+M2%+9d~_aj?sf*d7G$H=KZ+;~_5OXv~HkLZB`D1C0=ySHh6%$1n_d9W{Z z&m>oGu#UW7!b=#@N;S*cUt1_&zh6G6Pp&1MS&qW^nP8>f9Vydi7A|Q=nJs1UqHe~% zo8!0@d07eTQ)zRgq2lRbPX=U9X)}<}K~;F^6$@(xJg{M=ogF(BJK$Va())Mp;3$9P zb1zLrct_$*_$9%}3(n0%gfU}7>#&k71PXy}!LO#cR3p!xc`NR8zFQw{A$DKq6Oeuw z;ZC#iv;VMss-vmXR&ElJ5dxInx1l|}uEaG5i80LcV~4TkD%!RUD@5+~l+kiSOpS0( zJ-iwpm}JCR@Sy?BW$_tvO%K-fQUFm-UCi;NK$-MsQoWnQXO+(qUd!{zFS!JepUfxD zmmoFLB>{OkHam{gP2#GXZaq&=xio1Kop4j#`v}Qz6U1D0dc!ks4ikn{Y6ti#ZeqYgF+ z0jQIIQUvnReW)_53Z+>u>)Lw((~vxa6AFrr%d}nI!o7{spwl@ir`qH9j7o=6JXYD| zsp>X-yI}#VHc1S{c}{E|acAh>zF%*}R`4 zM+xtI9F&>Xs(IJooneFYo;l{cU*-2DT~2TUm;QwTC9RXwFSwqHS82mcZmDj8xVn(+ zhjg5e>~E9?3K-*RvJ)uCq0UIdRl~D85$B^#Nph2%)6FN1>6!u6+%oE;F=J5B=`W{` zL<6;Qu8Pq|0+tS%yP10nmIgUV^r%Hyjyo|#W0hIVR`qiw@r)O7`K*l4Ma$$u=XQc$ z^#q3KLI6#VtuIxX4b;#_lx#bieZGmNS8?8jxHeTsE52O+t4ih5iw}=p7@DZs*!jev z{i#&SO#GsN^zjC{G<~Nu|2>~?q2Z@)UnNDB&2?wHQCn?p9v7YpNRPW1 zWM9#550th&<~(gv_Sok5g3e8tnTzkV2|gxe#kE{nUT{aP8n5=}qg4mCp!JuEcz=Ht z&y3I7&uxdKU%P7D+5NV%Ok}hj@mimhKlv+R1bd8?zb|20JJD?Q?=vElsc#c2!VJmq z&W&vW+CaWx`FG1VfMsEf)`p}0TTes}|I{%_X{vj;}wDxh!zb$|D=4e756H z7dp8?Ul~60@eSwbY!+Crzr*mLMSqj6ofW&@mJB8fIGm%=B28`wnbx8F8YnigN|~sB z)ie@y57LaLin3|;u`JzFDsS0JCrG!Z4g+Nd*=-JadG7AesG5y*rMun?dHJhkCMW_% zCal ztKYWr0+ECjETkqk!9jw#hv?D8BB>sVztP<9s&fY3kg7O(65kdl!pnzWhNl>mkKBOP z9wGNuspXb&`T7gZLu#Y670KyIg|D$foZ^6CxK^NurqGjTAORgOb-D`MnNNRW8Xw=g z8)`pHz^^@&DlTfcLBTlT7>c#c{d1Rs^_EM?6rpWz{8ZrZ3&E3&F=tOC;zGnc>6#NjY1JQMZ!+8#j*!95<*U{5CE&b@6WIV= z`L8w`z0>!&Y?@c9IUIXc)WVTOpF}^_=xxWoJZGv|AT41`N;g@MZhWeGa@pxlgGji8 zR3?G5Rb3_fNj8zy!w)Nl>leQXO0(UI&kdY+N-i0G7Z%q|`!Oo^N%yZLWCBLMop?7) z`#d}b79JtI-AG(Fx@TIi!6u-D3-^!Dlae;43Yp1%MZ9XATQ^#ln*F21RntEEXZFkB z`SV+qf>QWy^~x~X!#q&<(a*gW8Npq#5?J;o^D1<$rOl;PQ2b4cBvE-R>e$@3lbK}qIv=--S zEeI|aC9>S#V3jN>JO#=lUV`ja4_n@N34a(b9DsX~5L~fhJpe=AgZbr~VX+0ZQY{x^ z(k)K(A0~mNkFt zA8e)|)*K0!nFmOg^$p@)RlWA0%f_jul)Ga}wOT-A_SHF)3v!5Ywj5XdkuSTR2s1b> z60lzNZMkjx`b~_wapzIo-Eku>H`NV#XFRgb*F@gDM&yDMiwX=D%B zmzw)_!+aX+zV8mY9at~%ev^rb^(0rwKSp(3};ZpMvxEwD2OjDaVA6Ry$0&8rtZV3pHxzf$? zzAjYXA~;b|XCc95MUR%dTT@Z>0}uY+8y=;wW1vky{pKP;cOV}6&6tV$I;>`FK z906wPfPrz9t=;&M?(Wwdm z0?&;KzLQk84srC-9#ap*I_9GregSZjm<$6oiZ>h3ACEnS7A^faq{fPmD!rT69qQG% zRVF#+RDZ(-Ue?g!$?;NT#p=8F8SV%EZ5ry{-5J)UN6Jj~-klPlw7o4w&aUp0pn@@) zM(jp3}a6rP@=sC1ZvM zV)jL-HO|elZ@x|hHXkrmGu9uS2%=Jqa zgIqpCmA+s{=XewW1!LqE)3%%mIO z(8jQbk;xApH`iS0;h7M96j^_3N=#|-xP-=*>3=obmL(W)Au>jdy3E<UjD;R zOI^Va(lW(qH`MjF&}RqCOifgKKA39SANA9=Qv4z+3Qey|4BJBzex_v%9=l5D-xJaG`?IF#?EKul!io4R+`>v>t_65&VXqROwiMr@*>SD)gNHL4^Ml5(vgCqodJjd$~XNSPzt@GziL=mgy;Y+qBZh&1qKxwm{>$kMCyH2rN?F2%^-bX#z9QBC| zNx?aIaFXEMqAKsMWDfWB@Pt3@$5LZ%DVDT70icB1BXM`F_#4rYqTkpk%wf tVgFekgZM{XhA!KlmFcR^%iaf4$rSfz)nO-hfB%&wE2$_^D)!aq{{YOB6}SKZ literal 0 HcmV?d00001 diff --git a/web/js/bootstrap.js b/web/js/bootstrap.js new file mode 100755 index 0000000..1f295a1 --- /dev/null +++ b/web/js/bootstrap.js @@ -0,0 +1 @@ +!function(a){a(function(){"use strict",a.support.transition=function(){var b=document.body||document.documentElement,c=b.style,d=c.transition!==undefined||c.WebkitTransition!==undefined||c.MozTransition!==undefined||c.MsTransition!==undefined||c.OTransition!==undefined;return d&&{end:function(){var b="TransitionEnd";return a.browser.webkit?b="webkitTransitionEnd":a.browser.mozilla?b="transitionend":a.browser.opera&&(b="oTransitionEnd"),b}()}}()})}(window.jQuery),!function(a){"use strict";var b='[data-dismiss="alert"]',c=function(c){a(c).on("click",b,this.close)};c.prototype={constructor:c,close:function(b){function f(){e.remove(),e.trigger("closed")}var c=a(this),d=c.attr("data-target"),e;d||(d=c.attr("href"),d=d&&d.replace(/.*(?=#[^\s]*$)/,"")),e=a(d),e.trigger("close"),b&&b.preventDefault(),e.length||(e=c.hasClass("alert")?c:c.parent()),e.removeClass("in"),a.support.transition&&e.hasClass("fade")?e.on(a.support.transition.end,f):f()}},a.fn.alert=function(b){return this.each(function(){var d=a(this),e=d.data("alert");e||d.data("alert",e=new c(this)),typeof b=="string"&&e[b].call(d)})},a.fn.alert.Constructor=c,a(function(){a("body").on("click.alert.data-api",b,c.prototype.close)})}(window.jQuery),!function(a){"use strict";var b=function(b,c){this.$element=a(b),this.options=a.extend({},a.fn.button.defaults,c)};b.prototype={constructor:b,setState:function(a){var b="disabled",c=this.$element,d=c.data(),e=c.is("input")?"val":"html";a+="Text",d.resetText||c.data("resetText",c[e]()),c[e](d[a]||this.options[a]),setTimeout(function(){a=="loadingText"?c.addClass(b).attr(b,b):c.removeClass(b).removeAttr(b)},0)},toggle:function(){var a=this.$element.parent('[data-toggle="buttons-radio"]');a&&a.find(".active").removeClass("active"),this.$element.toggleClass("active")}},a.fn.button=function(c){return this.each(function(){var d=a(this),e=d.data("button"),f=typeof c=="object"&&c;e||d.data("button",e=new b(this,f)),c=="toggle"?e.toggle():c&&e.setState(c)})},a.fn.button.defaults={loadingText:"loading..."},a.fn.button.Constructor=b,a(function(){a("body").on("click.button.data-api","[data-toggle^=button]",function(b){a(b.target).button("toggle")})})}(window.jQuery),!function(a){"use strict";var b=function(b,c){this.$element=a(b),this.options=a.extend({},a.fn.carousel.defaults,c),this.options.slide&&this.slide(this.options.slide)};b.prototype={cycle:function(){return this.interval=setInterval(a.proxy(this.next,this),this.options.interval),this},to:function(b){var c=this.$element.find(".active"),d=c.parent().children(),e=d.index(c),f=this;if(b>d.length-1||b<0)return;return this.sliding?this.$element.one("slid",function(){f.to(b)}):e==b?this.pause().cycle():this.slide(b>e?"next":"prev",a(d[b]))},pause:function(){return clearInterval(this.interval),this},next:function(){if(this.sliding)return;return this.slide("next")},prev:function(){if(this.sliding)return;return this.slide("prev")},slide:function(b,c){var d=this.$element.find(".active"),e=c||d[b](),f=this.interval,g=b=="next"?"left":"right",h=b=="next"?"first":"last",i=this;return this.sliding=!0,f&&this.pause(),e=e.length?e:this.$element.find(".item")[h](),!a.support.transition&&this.$element.hasClass("slide")?(this.$element.trigger("slide"),d.removeClass("active"),e.addClass("active"),this.sliding=!1,this.$element.trigger("slid")):(e.addClass(b),e[0].offsetWidth,d.addClass(g),e.addClass(g),this.$element.trigger("slide"),this.$element.one(a.support.transition.end,function(){e.removeClass([b,g].join(" ")).addClass("active"),d.removeClass(["active",g].join(" ")),i.sliding=!1,setTimeout(function(){i.$element.trigger("slid")},0)})),f&&this.cycle(),this}},a.fn.carousel=function(c){return this.each(function(){var d=a(this),e=d.data("carousel"),f=typeof c=="object"&&c;e||d.data("carousel",e=new b(this,f)),typeof c=="number"?e.to(c):typeof c=="string"||(c=f.slide)?e[c]():e.cycle()})},a.fn.carousel.defaults={interval:5e3},a.fn.carousel.Constructor=b,a(function(){a("body").on("click.carousel.data-api","[data-slide]",function(b){var c=a(this),d,e=a(c.attr("data-target")||(d=c.attr("href"))&&d.replace(/.*(?=#[^\s]+$)/,"")),f=!e.data("modal")&&a.extend({},e.data(),c.data());e.carousel(f),b.preventDefault()})})}(window.jQuery),!function(a){"use strict";var b=function(b,c){this.$element=a(b),this.options=a.extend({},a.fn.collapse.defaults,c),this.options.parent&&(this.$parent=a(this.options.parent)),this.options.toggle&&this.toggle()};b.prototype={constructor:b,dimension:function(){var a=this.$element.hasClass("width");return a?"width":"height"},show:function(){var b=this.dimension(),c=a.camelCase(["scroll",b].join("-")),d=this.$parent&&this.$parent.find(".in"),e;d&&d.length&&(e=d.data("collapse"),d.collapse("hide"),e||d.data("collapse",null)),this.$element[b](0),this.transition("addClass","show","shown"),this.$element[b](this.$element[0][c])},hide:function(){var a=this.dimension();this.reset(this.$element[a]()),this.transition("removeClass","hide","hidden"),this.$element[a](0)},reset:function(a){var b=this.dimension();this.$element.removeClass("collapse")[b](a||"auto")[0].offsetWidth,this.$element.addClass("collapse")},transition:function(b,c,d){var e=this,f=function(){c=="show"&&e.reset(),e.$element.trigger(d)};this.$element.trigger(c)[b]("in"),a.support.transition&&this.$element.hasClass("collapse")?this.$element.one(a.support.transition.end,f):f()},toggle:function(){this[this.$element.hasClass("in")?"hide":"show"]()}},a.fn.collapse=function(c){return this.each(function(){var d=a(this),e=d.data("collapse"),f=typeof c=="object"&&c;e||d.data("collapse",e=new b(this,f)),typeof c=="string"&&e[c]()})},a.fn.collapse.defaults={toggle:!0},a.fn.collapse.Constructor=b,a(function(){a("body").on("click.collapse.data-api","[data-toggle=collapse]",function(b){var c=a(this),d,e=c.attr("data-target")||b.preventDefault()||(d=c.attr("href"))&&d.replace(/.*(?=#[^\s]+$)/,""),f=a(e).data("collapse")?"toggle":c.data();a(e).collapse(f)})})}(window.jQuery),!function(a){function d(){a(b).parent().removeClass("open")}"use strict";var b='[data-toggle="dropdown"]',c=function(b){var c=a(b).on("click.dropdown.data-api",this.toggle);a("html").on("click.dropdown.data-api",function(){c.parent().removeClass("open")})};c.prototype={constructor:c,toggle:function(b){var c=a(this),e=c.attr("data-target"),f,g;return e||(e=c.attr("href"),e=e&&e.replace(/.*(?=#[^\s]*$)/,"")),f=a(e),f.length||(f=c.parent()),g=f.hasClass("open"),d(),!g&&f.toggleClass("open"),!1}},a.fn.dropdown=function(b){return this.each(function(){var d=a(this),e=d.data("dropdown");e||d.data("dropdown",e=new c(this)),typeof b=="string"&&e[b].call(d)})},a.fn.dropdown.Constructor=c,a(function(){a("html").on("click.dropdown.data-api",d),a("body").on("click.dropdown.data-api",b,c.prototype.toggle)})}(window.jQuery),!function(a){function c(){var b=this,c=setTimeout(function(){b.$element.off(a.support.transition.end),d.call(b)},500);this.$element.one(a.support.transition.end,function(){clearTimeout(c),d.call(b)})}function d(a){this.$element.hide().trigger("hidden"),e.call(this)}function e(b){var c=this,d=this.$element.hasClass("fade")?"fade":"";if(this.isShown&&this.options.backdrop){var e=a.support.transition&&d;this.$backdrop=a('