Comparar commits
832 Commits
| Autor | SHA1 | Data | |
|---|---|---|---|
| 83f1ccb8ad | |||
| cea401c513 | |||
| 6b54c1b6d4 | |||
| 193c4a953e | |||
| d389134036 | |||
| d8b140e341 | |||
| 5931e1143b | |||
| e6d0c4b79f | |||
| 44a4a18320 | |||
| 2f96506a6e | |||
| f904606b32 | |||
| 99bb14680a | |||
| 83df3dda95 | |||
| 5e4d67209c | |||
| 2798af1668 | |||
| f0e41ce4c7 | |||
| 300602dd32 | |||
| 136f6778e2 | |||
| 2fa7907054 | |||
| f3803c4c22 | |||
| 5d37927b0a | |||
| 6227f5cfa2 | |||
| 9fbb9fa5e1 | |||
| de81ee7ee6 | |||
| 5fcf971e28 | |||
| 492f70fc4b | |||
| 3ced7b7212 | |||
| 7b55867ea7 | |||
| 3e15ef1c86 | |||
| 0cdc836054 | |||
| 1aad62b95d | |||
| ed1c88920e | |||
| e6ca20c4a2 | |||
| cfa3470df6 | |||
| 479f297cf6 | |||
| 7e1b222f90 | |||
| 69baa6ea1a | |||
| 0fce93be37 | |||
| 51611ec2bb | |||
| 228a78136d | |||
| 34d10ace7a | |||
| 57bd0a5628 | |||
| 42e4860855 | |||
| ea1c9f70d7 | |||
| d3085b0450 | |||
| 63d02f1d5f | |||
| dfecbe5bfa | |||
| bcf9f00de5 | |||
| a8e8e35678 | |||
| 2ec4a8b4ce | |||
| 6c3d3dee81 | |||
| 40b3116ed7 | |||
| df7582b138 | |||
| b08dab9aa1 | |||
| da5a4804df | |||
| d12f43498e | |||
| f9f2facd76 | |||
| eaee3954c1 | |||
| eb235e11b6 | |||
| 0c5fe61ed3 | |||
| 76052b443a | |||
| 5a48248e1e | |||
| bedff3278f | |||
| 0bb48153a5 | |||
| b75246e3ca | |||
| 2d31f0d7fe | |||
| 87cfca721e | |||
| 54d36f3337 | |||
| a4a17d171a | |||
| 7c2673fc66 | |||
| c77ecbb10f | |||
| f0ba57ba23 | |||
| 893aa80361 | |||
| 59ac3d1d45 | |||
| 247a9478c8 | |||
| 1c64d858c2 | |||
| 91a1d7faaf | |||
| 214bbfa7b6 | |||
| 3ee4492b3c | |||
| 38c453b3c6 | |||
| 3d41c61991 | |||
| 1f9f47f97e | |||
| 41cc2923b1 | |||
| 4c180f3eed | |||
| 5f9a64dbea | |||
| 9d37bfdcf1 | |||
| d738c3aca6 | |||
| 836c4313ab | |||
| 91c6478add | |||
| fce527a55b | |||
| 9a1f62fc1f | |||
| 018d8656d3 | |||
| 2636e1ba45 | |||
| caed07dfc1 | |||
| 373cf3328d | |||
| 0370c3fa01 | |||
| f5143a7d63 | |||
| 346b2a27cb | |||
| 38281e6361 | |||
| 6e8413f568 | |||
| cb04364381 | |||
| 83d2555123 | |||
| 0ace97c8d4 | |||
| 9e3a0c61b8 | |||
| 2fda5c7873 | |||
| e29868c8ce | |||
| ac560be870 | |||
| 5a29cbf4a7 | |||
| dc77894f68 | |||
| 74432a6b9d | |||
| 2b2fa7e77c | |||
| c3f71b33aa | |||
| 927aae72d1 | |||
| 2a674f4e8d | |||
| 0b86ce2bda | |||
| 548a0709f4 | |||
| da84c0bb4d | |||
| 9e68a5b9ab | |||
| 6982bdaa28 | |||
| c1809232fe | |||
| 3c06c82eb7 | |||
| 1bcccb3134 | |||
| cdf49dc87e | |||
| 0a0fb8f803 | |||
| 60cfd56369 | |||
| 73050d38c5 | |||
| c50e941972 | |||
| 7c59fd21e2 | |||
| b095a4221c | |||
| 1ef39fa3b8 | |||
| fcf4530d83 | |||
| 0515e14d27 | |||
| ddbef0bd79 | |||
| bc5d31f86a | |||
| 09147a2aab | |||
| a75a119e2f | |||
| c1b3546161 | |||
| 8338432c7b | |||
| 8beae065cf | |||
| 0e7d39aa13 | |||
| 74063ad735 | |||
| 55a29808c9 | |||
| 40f3de3939 | |||
| 5d94ab09e3 | |||
| b34ff6853d | |||
| e2570be957 | |||
| 95e6fff3da | |||
| 9de12b7385 | |||
| beffe906e4 | |||
| e850d1073d | |||
| c02a2ba5ba | |||
| f92bb6690b | |||
| 5842a3bba6 | |||
| fcf691d97c | |||
| a32c7d5f07 | |||
| edfb18b75a | |||
| 5544c5d5d4 | |||
| 22100f7c3a | |||
| 8ece01ccda | |||
| 9b68039a62 | |||
| a914326e75 | |||
| 2b7dd35e05 | |||
| 15e41169ad | |||
| f8c5219c12 | |||
| c570b67dda | |||
| 456fac1700 | |||
| 41e58c394b | |||
| 5aeda5f67b | |||
| c96adf168c | |||
| 40c5bc01ff | |||
| 09dabef70c | |||
| 96edee806e | |||
| e58f4683d1 | |||
| 9750817bf0 | |||
| 7099d730e6 | |||
| 13c290e99f | |||
| cf37966e39 | |||
| 9449c4b9b5 | |||
| 046172e5d9 | |||
| c74446e38c | |||
| 7897460993 | |||
| 4b59583bca | |||
| 41f73559f2 | |||
| d3090c0965 | |||
| dc5caebbf8 | |||
| 14280dfedb | |||
| 8fdf11135a | |||
| c8baa8f349 | |||
| a6e25b46af | |||
| 395d14ab58 | |||
| 0d4b57df7a | |||
| 416e7617b3 | |||
| 838c470017 | |||
| f0a09665db | |||
| 1397fa64d5 | |||
| ac17a6afcf | |||
| 35af1bab30 | |||
| 1fe7bb271a | |||
| 774e25d944 | |||
| 6770e8541d | |||
| 70e94e4dde | |||
| 0ffbc6e345 | |||
| 25e796753f | |||
| 70d9ef4f77 | |||
| fd67c9a589 | |||
| f0917ff774 | |||
| 843281dec0 | |||
| 3a21588429 | |||
| 21eeb09a3c | |||
| 900c5ed358 | |||
| 195c8a92d1 | |||
| 7c651e6093 | |||
| 860b899f0c | |||
| 87b7524de9 | |||
| 29641ae16a | |||
| 02e32a7a9d | |||
| 3056369c6e | |||
| 3da75726d3 | |||
| b7346de47d | |||
| 7a69100b2f | |||
| 2732d2b9d3 | |||
| 87bb27cc4f | |||
| bafa300b0f | |||
| 939cf4542f | |||
| 874975da54 | |||
| f0bebd4c33 | |||
| c444a11862 | |||
| 4c0704e338 | |||
| 77f4cd5d58 | |||
| 9858d3c5a8 | |||
| 414acbc9ec | |||
| 21f904db42 | |||
| f6ca253849 | |||
| 3f989191e3 | |||
| 2bea014e89 | |||
| 84fc19a75f | |||
| d803924ef0 | |||
| 889f8d9d30 | |||
| 1dcb7eaf1f | |||
| 5f9e4ace2f | |||
| 6386330973 | |||
| fd89b9755e | |||
| 730f454278 | |||
| e2fca29c91 | |||
| f97a222cf5 | |||
| c75105e977 | |||
| 7096471d65 | |||
| cc1cee8a38 | |||
| 5f2680889f | |||
| b3ae8bb606 | |||
| 72d9f37845 | |||
| fdfccafd84 | |||
| a784f946ba | |||
| a04af6c4bd | |||
| 64b24a96c8 | |||
| a62a5d0d7b | |||
| 3043d1e839 | |||
| a3aefb6902 | |||
| a42aaa270c | |||
| f8b471c17b | |||
| 87adff44db | |||
| 767bba9c04 | |||
| 0c827521d0 | |||
| 80cec7d12f | |||
| 5f916357e9 | |||
| ea82eea9bb | |||
| 22222d7db4 | |||
| cebee369d1 | |||
| faf809851c | |||
| d05aafd76b | |||
| 8d52721873 | |||
| 66c6152cd2 | |||
| 32cd51d3a5 | |||
| 497a28401e | |||
| e46912047c | |||
| 56b573ede5 | |||
| 0276be1789 | |||
| 7db4c60fc3 | |||
| 95cc01dd91 | |||
| cce1dddf42 | |||
| 0938b57841 | |||
| aef8550e2f | |||
| 71bf0bde32 | |||
| 750623b9b1 | |||
| 1f12c53b65 | |||
| 8717ea1b95 | |||
| db2eb658ca | |||
| 70483484ce | |||
| d21afc43a1 | |||
| 7ad0803f2e | |||
| ec4bfd55f9 | |||
| f65174a0db | |||
| 8b1fcd4dd7 | |||
| 9509237958 | |||
| f20fd20a06 | |||
| 8e48120fb7 | |||
| 44cc5ac883 | |||
| 9a43cc8c4b | |||
| 8dfc0d54f4 | |||
| 77febd21de | |||
| 84a997c66b | |||
| b900a59fc2 | |||
| ca782dbc58 | |||
| 746cda27ec | |||
| 16d1651656 | |||
| 4735fb2377 | |||
| ec6b518fd2 | |||
| 580ad1b192 | |||
| 23d8810486 | |||
| d579d62eb6 | |||
| 529f785e3c | |||
| 0581ae87b4 | |||
| cda1059336 | |||
| 1aed6b5c30 | |||
| 4e68705b24 | |||
| 216cb1ea12 | |||
| 8ef6a0b432 | |||
| 5dc152c46e | |||
| c3a21437e4 | |||
| 32bc8dcbf9 | |||
| 677ca7b4aa | |||
| dfc2331104 | |||
| cd06bbfb8d | |||
| 1d596bcbac | |||
| 0cb7b9c13d | |||
| a180efeca7 | |||
| eef24ea29c | |||
| 458bb3d7f7 | |||
| ffb23d92c0 | |||
| 0ce39a4cf6 | |||
| a24c70e490 | |||
| 8d4ef6e883 | |||
| 54865ebdee | |||
| cb8e704d5c | |||
| 57f4978442 | |||
| b8833f4c9e | |||
| 5c40cbc675 | |||
| a455dcc919 | |||
| 289487cc46 | |||
| 42b28d5bd6 | |||
| 46bee05d93 | |||
| 49bed5cd5f | |||
| 624cf93e2e | |||
| 6ab76cda51 | |||
| 15beb81368 | |||
| 6a6cce03b7 | |||
| 970ff87ef6 | |||
| 8b6c4d322f | |||
| 4467a00acd | |||
| 63efc61517 | |||
| 7aa18345c8 | |||
| 34149187a8 | |||
| 2d132cd0d5 | |||
| 8d741361e0 | |||
| 2bcea3a370 | |||
| ead3cace02 | |||
| 7afa5c7b43 | |||
| 115765e1ee | |||
| d712fae2d7 | |||
| 0dba9bd87f | |||
| 2fbc495088 | |||
| 37d490cbe4 | |||
| 451679c582 | |||
| 7270a13ef7 | |||
| 16f3f90ed7 | |||
| 9ff2f81a10 | |||
| 1bc9a1af6a | |||
| 760d9e3341 | |||
| 5b28abc5ed | |||
| defb1596bb | |||
| 9c117d5875 | |||
| 1b09fce42a | |||
| 5e328830e7 | |||
| 606da1c45b | |||
| 6232c1a8f3 | |||
| 15a24e68d2 | |||
| c12ace81db | |||
| 531990b516 | |||
| b90a3dbffe | |||
| 0352b97798 | |||
| e45abe60bf | |||
| 8077481707 | |||
| 4651dd33cd | |||
| c93cfa0871 | |||
| d57cb7d411 | |||
| 81088766ee | |||
| ae59a50c28 | |||
| 4a05056393 | |||
| 71e97c815a | |||
| 8b7787aa60 | |||
| e0f94b052b | |||
| eb2c9ffd32 | |||
| 5af315c165 | |||
| e9fdfc339b | |||
| 132730acea | |||
| 405097d323 | |||
| c9d79f26c5 | |||
| 7ef13eaf0c | |||
| 2756aba21f | |||
| c01a7f57df | |||
| 33a4dd3841 | |||
| 3c150db4a2 | |||
| e31f5d2325 | |||
| f8a61c667e | |||
| e2fe60399f | |||
| 59c4e35691 | |||
| 2dc5afd018 | |||
| b5df39bc46 | |||
| f87b709923 | |||
| 671aa1036b | |||
| 4580340db3 | |||
| 31b53eae39 | |||
| 089423958d | |||
| 0d02ea43e1 | |||
| 93239ae3fb | |||
| 5d21e9364d | |||
| b9306c4cca | |||
| c07bd31f61 | |||
| fee4830669 | |||
| 9016b6778e | |||
| e05501a1ae | |||
| 23d09fcc9e | |||
| 5d11fccbf2 | |||
| 3851957dd0 | |||
| 135afb0883 | |||
| 27b27af7cd | |||
| 20fe31b803 | |||
| ebca9aab8d | |||
| 49821e0416 | |||
| b5d6f1ada5 | |||
| b40325cd7b | |||
| 3b27ec66e1 | |||
| 52b8b36d54 | |||
| 7e3106dd1b | |||
| 399e0d94fc | |||
| eef1ff9e87 | |||
| 9202c7504b | |||
| 3fd7e3b93e | |||
| b6996df86a | |||
| 4b3efd7bfa | |||
| 12c2682c34 | |||
| 2caf67b813 | |||
| fbbe13661a | |||
| 0fb102edae | |||
| b4dd90d074 | |||
| 2e3f806d07 | |||
| f76092e83e | |||
| 1e44081583 | |||
| 26b3a4259d | |||
| 8a074a7e6d | |||
| e99434582f | |||
| fec57df994 | |||
| 033cb2d20a | |||
| a5fa58e3e6 | |||
| fdc9d2714b | |||
| 824ca9b513 | |||
| a74a740964 | |||
| 2041d52495 | |||
| fb5d528eff | |||
| a2cedbd9da | |||
| 8574eddfa0 | |||
| 2ccf876929 | |||
| 28df150784 | |||
| 0950083da2 | |||
| 88c5f910da | |||
| a9d3e4798e | |||
| d85d834b23 | |||
| 5729da5a9f | |||
| 1255331160 | |||
| 0fef51b5f6 | |||
| 7cf18c0468 | |||
| b994509b9d | |||
| 6571e19e8f | |||
| 7441164db9 | |||
| 09bb10cd3f | |||
| bd6f24f4b1 | |||
| 0a174ad219 | |||
| 33d004fb9a | |||
| 652bf34c77 | |||
| 3f11bad68e | |||
| 5245fbfc6d | |||
| 68f8ee8a99 | |||
| 7b83a52dce | |||
| 5f57c82478 | |||
| 3128cbef73 | |||
| 694b2bf055 | |||
| e03a023c5f | |||
| 3ff5d3bd09 | |||
| 04841196e3 | |||
| 18dd495d99 | |||
| f714945bbb | |||
| a318ef2232 | |||
| 8ed24de9d3 | |||
| e06e50936d | |||
| d710e9cb47 | |||
| 9af3699850 | |||
| 3cc7b4d8c2 | |||
| 8b58732a1f | |||
| a2a0935343 | |||
| 2668156e53 | |||
| d67615d239 | |||
| afd70b64ed | |||
| 78a6e43667 | |||
| a65af97e43 | |||
| 637dfabe69 | |||
| ff8b3c10f3 | |||
| f442555d8f | |||
| bdf9f882ad | |||
| 1e5c5bfe00 | |||
| 0e78fdfcb4 | |||
| 4f532f86dc | |||
| 92844825cb | |||
| 6b13fc99b0 | |||
| 0f7aa26757 | |||
| a06f963263 | |||
| d31de1e654 | |||
| 46a0301022 | |||
| 73d26036ef | |||
| b9637c9d7d | |||
| 08497683bf | |||
| 44a50f6e58 | |||
| 4c2b4e5c66 | |||
| b0884ad7c3 | |||
| cf09f86df2 | |||
| d33c38f671 | |||
| fbfba21854 | |||
| b2093d6f10 | |||
| 736a0190c1 | |||
| a74c259c38 | |||
| d3dedd6312 | |||
| 920eab0ef9 | |||
| d576085e8d | |||
| 62b9a9e287 | |||
| 56ffe1d62a | |||
| 14acbcb5f1 | |||
| 9a0011bf49 | |||
| 7b9f18b097 | |||
| f8e4333157 | |||
| b5c933aa94 | |||
| c441af3f2c | |||
| e885421a67 | |||
| ce4fe24a93 | |||
| 1c0e2ceba5 | |||
| a435e8ec47 | |||
| b958393135 | |||
| a6dd697a68 | |||
| a4833e7b35 | |||
| 54fa643c10 | |||
| 6047cd4542 | |||
| 8c5f7df280 | |||
| ff1250c33c | |||
| 4297c10fe9 | |||
| ab6ad94ec3 | |||
| 759183461f | |||
| f850fa6bc9 | |||
| 5a242d35b7 | |||
| abf5d66e01 | |||
| e92360e0d4 | |||
| b5c0fdc013 | |||
| 906ef02c5e | |||
| 7550decbc0 | |||
| acd88c3c8a | |||
| a3854a2de1 | |||
| 880aca5d83 | |||
| 2d1c98a5a7 | |||
| fefa06ba21 | |||
| 18faa91c94 | |||
| 9f23c7b31a | |||
| eee6926bab | |||
| 6806f952d6 | |||
| faaef54b42 | |||
| c29852fae7 | |||
| 7d100dae97 | |||
| 206e800963 | |||
| 555a334315 | |||
| d2c5def108 | |||
| 98323c530e | |||
| b36510dbb9 | |||
| 50e533c32b | |||
| 33277c3d37 | |||
| 28f2ad0109 | |||
| 5d10ee4e61 | |||
| b4190ada0c | |||
| 60d314552d | |||
| c971d54543 | |||
| 346f019fa8 | |||
| d61c2480b6 | |||
| e30b5b86da | |||
| bd74fe24fd | |||
| b30eaa8ee3 | |||
| 75c530516a | |||
| 3b49821e82 | |||
| febf443960 | |||
| f5da81e782 | |||
| 9cb23ac584 | |||
| 6ff3100c1f | |||
| e3ea3723bf | |||
| 33443d9b41 | |||
| e0ef61659f | |||
| 655ad77bce | |||
| c09e546240 | |||
| a08b14a89e | |||
| 12e90b0eef | |||
| 7606f1bcbd | |||
| cabb43b187 | |||
| b85cda3f6e | |||
| b92f1b7497 | |||
| 273a51a335 | |||
| 212dc88b89 | |||
| ce82a91d14 | |||
| 52a08401c7 | |||
| 706c0cb033 | |||
| 1567a613a6 | |||
| d767d80d20 | |||
| 1103a9d510 | |||
| c34af4b97a | |||
| 8dcee4d338 | |||
| fc1e69ea73 | |||
| db460117c1 | |||
| 1c514df0fd | |||
| 780ec5cbec | |||
| 3721873b62 | |||
| 3bdc77ca91 | |||
| b8f54d64a4 | |||
| 90d48b320a | |||
| a797342800 | |||
| 31e608e0ae | |||
| ebcfb0e577 | |||
| 161c9f9142 | |||
| 2a099da726 | |||
| 5118397004 | |||
| 546c340656 | |||
| 3034cc7c52 | |||
| d89381ff78 | |||
| 86cac58100 | |||
| 8fc062007e | |||
| 9fd92e3306 | |||
| a4cbbe5dd4 | |||
| da7ea9655f | |||
| 00a8c642dd | |||
| e1152f6e56 | |||
| 7cf9c6842a | |||
| d375535cd9 | |||
| c5ea868f47 | |||
| 884d333805 | |||
| 3b9921a15b | |||
| 17b753d95e | |||
| 6a8781b5e2 | |||
| 0dfaf01749 | |||
| 088aa83e22 | |||
| 934adb1e9f | |||
| 50e0593de6 | |||
| f2637c6421 | |||
| a52dacea68 | |||
| f56a33d22d | |||
| 2aa858b0fb | |||
| c3b8ba4b9a | |||
| ac96b8c0e1 | |||
| a329a9f4d8 | |||
| 63cc7eb44d | |||
| 6505f32221 | |||
| 22088260f4 | |||
| d893009a8d | |||
| b62e1741be | |||
| 903c2aa642 | |||
| d0a8bb550e | |||
| f96f1423e1 | |||
| 7bf17f2f61 | |||
| 670b6cfc15 | |||
| b6d2c8479c | |||
| 5aaa114271 | |||
| e99c2791bb | |||
| 656d7754cd | |||
| 4aaf10fbfc | |||
| 85e77b77aa | |||
| f23ef09247 | |||
| 440d956438 | |||
| acd2852cf3 | |||
| 4246f43bdf | |||
| 7d33769cd2 | |||
| 31fdba80d4 | |||
| 77f8e460d0 | |||
| 51e51db9f7 | |||
| 73c506f19c | |||
| 79143f3334 | |||
| 128ba9040e | |||
| 300cfe74ad | |||
| f008af05bd | |||
| 3aa870d6bd | |||
| 25e1a8c001 | |||
| 0ba3b114b0 | |||
| c429e88e8e | |||
| c7a2fe8440 | |||
| de237e760b | |||
| 927f5c9883 | |||
| 88f461baf2 | |||
| 3251ae26a0 | |||
| e84c81633e | |||
| a2ec966ac7 | |||
| 69660fbfda | |||
| dcedca1a80 | |||
| 6455e954fc | |||
| 646904688f | |||
| 62779fc972 | |||
| fdee4cde26 | |||
| 0c18ac5adc | |||
| 6da4a942ca | |||
| 565f68d2d7 | |||
| a7cee4c889 | |||
| 4f315f9b11 | |||
| 51febfec2d | |||
| df8bd96b2e | |||
| 9da9beb342 | |||
| f93af823df | |||
| 5e74723a92 | |||
| ec77e1acea | |||
| 1afad64972 | |||
| a3365dda07 | |||
| 4c246a4cd1 | |||
| 934ffd0731 | |||
| 790e2c233d | |||
| 9e9967381c | |||
| 891ed08827 | |||
| 55d13a6f08 | |||
| 5569ea4397 | |||
| 693e37459a | |||
| 90de1fa55d | |||
| 6d6e1557ce | |||
| 2d84f16dc0 | |||
| 2f98ef9f6d | |||
| e0c07d2385 | |||
| 0b2413d8c0 | |||
| e92b01f813 | |||
| 27ff13459f | |||
| 3e80915454 | |||
| 4063d2e2c4 | |||
| ce9416857b | |||
| 575d5e8e57 | |||
| 14ceb8c046 | |||
| c6c29ca505 | |||
| c551226504 | |||
| fcbc09543c | |||
| 6d83bb0026 | |||
| 0784a513c9 | |||
| ebcdec0860 | |||
| 6805e4f28f | |||
| 962f5fa412 | |||
| 1385988ab8 | |||
| 6cc80f8c54 | |||
| d6c843962d | |||
| 247e6ad9ab | |||
| 364c131a4e | |||
| b047325033 | |||
| 0894cfe058 | |||
| f4a9d7e70b | |||
| 593634fb5d | |||
| 2932bf5006 | |||
| 4b1c39fe41 | |||
| ee91e3a139 | |||
| e287b854a2 | |||
| 5458e5dd00 | |||
| ed30f37ca9 | |||
| 9e495f6bb8 | |||
| 21566c49f0 | |||
| 0a225c13a4 | |||
| 680c07a030 | |||
| 8ee66dca22 | |||
| 60393f07ef | |||
| 34418b03fb | |||
| ce2f161f08 | |||
| d5ca1ea152 | |||
| c3fec8225a | |||
| bfff00826e | |||
| 61cb50b2bd | |||
| c8d3bfabc7 | |||
| 1ce212d33d | |||
| 3682f4f06a | |||
| 0f74408b0e | |||
| f7d7c4a8e0 | |||
| 0cb8d12796 | |||
| 9586ecfd58 | |||
| 86217867f9 | |||
| 496de51a11 | |||
| 38591d06a0 | |||
| 91805f7da4 | |||
| 7bd8340a8b | |||
| 4817007097 | |||
| d5003feb39 | |||
| f03e460ca5 | |||
| 347c8847b0 | |||
| 9f48b32329 | |||
| 2f6dfbc165 | |||
| 49cb53d735 | |||
| 54f1a51a10 | |||
| c6a2674c2b | |||
| 282a10ec83 | |||
| 50276a06df | |||
| f452250f88 | |||
| 9d44e237a5 | |||
| 9f0e5899c2 | |||
| e351a02a06 | |||
| cd167ec777 | |||
| 3290412477 | |||
| acf0128fb4 | |||
| e91ee22a89 | |||
| 45ef08cf99 | |||
| baa7d12ed6 | |||
| 822de581b3 | |||
| 3863a76fc8 | |||
| 9727fac291 | |||
| 5cadbcbbd7 | |||
| 8ee0294672 | |||
| 99789c3182 | |||
| 406a7f4fc7 | |||
| 5a11f82e0e | |||
| 33eca37ec9 | |||
| fbd9cdd11e | |||
| 6ec6657512 | |||
| 8e91d2039a | |||
| 1879648be7 | |||
| 490770171d | |||
| 621063cc18 | |||
| 821defc11b | |||
| dbc1709e0e | |||
| 4d1bda0601 | |||
| 1a9a3674c2 | |||
| 99b7e74422 | |||
| add0a43c40 | |||
| 3dc7c6a254 | |||
| e645123fbd | |||
| 4b104db212 | |||
| e823e31550 |
@@ -1 +1,9 @@
|
||||
.DS_Store
|
||||
lib-cov
|
||||
*.seed
|
||||
*.log
|
||||
*.csv
|
||||
*.dat
|
||||
*.out
|
||||
*.pid
|
||||
benchmarks/graphs
|
||||
+18
-12
@@ -1,12 +1,18 @@
|
||||
[submodule "spec/support/libxmljs"]
|
||||
path = spec/support/libxmljs
|
||||
url = git://github.com/sprsquish/libxmljs.git
|
||||
[submodule "lib/support/haml"]
|
||||
path = lib/support/haml
|
||||
url = git://github.com/creationix/haml-js.git
|
||||
[submodule "lib/support/js-oo"]
|
||||
path = lib/support/js-oo
|
||||
url = git://github.com/visionmedia/js-oo.git
|
||||
[submodule "lib/support/sass"]
|
||||
path = lib/support/sass
|
||||
url = git://github.com/visionmedia/sass.js.git
|
||||
[submodule "support/expresso"]
|
||||
path = support/expresso
|
||||
url = git://github.com/visionmedia/expresso.git
|
||||
[submodule "support/jade"]
|
||||
path = support/jade
|
||||
url = git://github.com/visionmedia/jade.git
|
||||
[submodule "support/haml"]
|
||||
path = support/haml
|
||||
url = git://github.com/visionmedia/haml.js.git
|
||||
[submodule "support/ejs"]
|
||||
path = support/ejs
|
||||
url = http://github.com/visionmedia/ejs.git
|
||||
[submodule "support/connect-form"]
|
||||
path = support/connect-form
|
||||
url = http://github.com/visionmedia/connect-form.git
|
||||
[submodule "support/connect"]
|
||||
path = support/connect
|
||||
url = http://github.com/senchalabs/connect.git
|
||||
|
||||
+21
@@ -0,0 +1,21 @@
|
||||
---
|
||||
- !ruby/object:Pomo::Task
|
||||
complete: false
|
||||
description:
|
||||
length: 25
|
||||
name: TM bundle
|
||||
- !ruby/object:Pomo::Task
|
||||
complete: false
|
||||
description:
|
||||
length: 25
|
||||
name: blog
|
||||
- !ruby/object:Pomo::Task
|
||||
complete: false
|
||||
description:
|
||||
length: 25
|
||||
name: release on nodejs group
|
||||
- !ruby/object:Pomo::Task
|
||||
complete: false
|
||||
description:
|
||||
length: 25
|
||||
name: Beta
|
||||
+237
@@ -1,4 +1,241 @@
|
||||
|
||||
1.0.0beta / 2010-07-15
|
||||
==================
|
||||
|
||||
* Re-write
|
||||
- much faster
|
||||
- much lighter
|
||||
- Check [ExpressJS.com](http://expressjs.com) for migration guide and updated docs
|
||||
|
||||
0.14.0 / 2010-06-15
|
||||
==================
|
||||
|
||||
* Utilize relative requires
|
||||
* Added Static bufferSize option [aheckmann]
|
||||
* Fixed caching of view and partial subdirectories [aheckmann]
|
||||
* Fixed mime.type() comments now that ".ext" is not supported
|
||||
* Updated haml submodule
|
||||
* Updated class submodule
|
||||
* Removed bin/express
|
||||
|
||||
0.13.0 / 2010-06-01
|
||||
==================
|
||||
|
||||
* Added node v0.1.97 compatibility
|
||||
* Added support for deleting cookies via Request#cookie('key', null)
|
||||
* Updated haml submodule
|
||||
* Fixed not-found page, now using using charset utf-8
|
||||
* Fixed show-exceptions page, now using using charset utf-8
|
||||
* Fixed view support due to fs.readFile Buffers
|
||||
* Changed; mime.type() no longer accepts ".type" due to node extname() changes
|
||||
|
||||
0.12.0 / 2010-05-22
|
||||
==================
|
||||
|
||||
* Added node v0.1.96 compatibility
|
||||
* Added view `helpers` export which act as additional local variables
|
||||
* Updated haml submodule
|
||||
* Changed ETag; removed inode, modified time only
|
||||
* Fixed LF to CRLF for setting multiple cookies
|
||||
* Fixed cookie complation; values are now urlencoded
|
||||
* Fixed cookies parsing; accepts quoted values and url escaped cookies
|
||||
|
||||
0.11.0 / 2010-05-06
|
||||
==================
|
||||
|
||||
* Added support for layouts using different engines
|
||||
- this.render('page.html.haml', { layout: 'super-cool-layout.html.ejs' })
|
||||
- this.render('page.html.haml', { layout: 'foo' }) // assumes 'foo.html.haml'
|
||||
- this.render('page.html.haml', { layout: false }) // no layout
|
||||
* Updated ext submodule
|
||||
* Updated haml submodule
|
||||
* Fixed EJS partial support by passing along the context. Issue #307
|
||||
|
||||
0.10.1 / 2010-05-03
|
||||
==================
|
||||
|
||||
* Fixed binary uploads.
|
||||
|
||||
0.10.0 / 2010-04-30
|
||||
==================
|
||||
|
||||
* Added charset support via Request#charset (automatically assigned to 'UTF-8' when respond()'s
|
||||
encoding is set to 'utf8' or 'utf-8'.
|
||||
* Added "encoding" option to Request#render(). Closes #299
|
||||
* Added "dump exceptions" setting, which is enabled by default.
|
||||
* Added simple ejs template engine support
|
||||
* Added error reponse support for text/plain, application/json. Closes #297
|
||||
* Added callback function param to Request#error()
|
||||
* Added Request#sendHead()
|
||||
* Added Request#stream()
|
||||
* Added support for Request#respond(304, null) for empty response bodies
|
||||
* Added ETag support to Request#sendfile()
|
||||
* Added options to Request#sendfile(), passed to fs.createReadStream()
|
||||
* Added filename arg to Request#download()
|
||||
* Performance enhanced due to pre-reversing plugins so that plugins.reverse() is not called on each request
|
||||
* Performance enhanced by preventing several calls to toLowerCase() in Router#match()
|
||||
* Changed; Request#sendfile() now streams
|
||||
* Changed; Renamed Request#halt() to Request#respond(). Closes #289
|
||||
* Changed; Using sys.inspect() instead of JSON.encode() for error output
|
||||
* Changed; run() returns the http.Server instance. Closes #298
|
||||
* Changed; Defaulting Server#host to null (INADDR_ANY)
|
||||
* Changed; Logger "common" format scale of 0.4f
|
||||
* Removed Logger "request" format
|
||||
* Fixed; Catching ENOENT in view caching, preventing error when "views/partials" is not found
|
||||
* Fixed several issues with http client
|
||||
* Fixed Logger Content-Length output
|
||||
* Fixed bug preventing Opera from retaining the generated session id. Closes #292
|
||||
|
||||
0.9.0 / 2010-04-14
|
||||
==================
|
||||
|
||||
* Added DSL level error() route support
|
||||
* Added DSL level notFound() route support
|
||||
* Added Request#error()
|
||||
* Added Request#notFound()
|
||||
* Added Request#render() callback function. Closes #258
|
||||
* Added "max upload size" setting
|
||||
* Added "magic" variables to collection partials (\_\_index\_\_, \_\_length\_\_, \_\_isFirst\_\_, \_\_isLast\_\_). Closes #254
|
||||
* Added [haml.js](http://github.com/visionmedia/haml.js) submodule; removed haml-js
|
||||
* Added callback function support to Request#halt() as 3rd/4th arg
|
||||
* Added preprocessing of route param wildcards using param(). Closes #251
|
||||
* Added view partial support (with collections etc)
|
||||
* Fixed bug preventing falsey params (such as ?page=0). Closes #286
|
||||
* Fixed setting of multiple cookies. Closes #199
|
||||
* Changed; view naming convention is now NAME.TYPE.ENGINE (for example page.html.haml)
|
||||
* Changed; session cookie is now httpOnly
|
||||
* Changed; Request is no longer global
|
||||
* Changed; Event is no longer global
|
||||
* Changed; "sys" module is no longer global
|
||||
* Changed; moved Request#download to Static plugin where it belongs
|
||||
* Changed; Request instance created before body parsing. Closes #262
|
||||
* Changed; Pre-caching views in memory when "cache view contents" is enabled. Closes #253
|
||||
* Changed; Pre-caching view partials in memory when "cache view partials" is enabled
|
||||
* Updated support to node --version 0.1.90
|
||||
* Updated dependencies
|
||||
* Removed set("session cookie") in favour of use(Session, { cookie: { ... }})
|
||||
* Removed utils.mixin(); use Object#mergeDeep()
|
||||
|
||||
0.8.0 / 2010-03-19
|
||||
==================
|
||||
|
||||
* Added coffeescript example app. Closes #242
|
||||
* Changed; cache api now async friendly. Closes #240
|
||||
* Removed deprecated 'express/static' support. Use 'express/plugins/static'
|
||||
|
||||
0.7.6 / 2010-03-19
|
||||
==================
|
||||
|
||||
* Added Request#isXHR. Closes #229
|
||||
* Added `make install` (for the executable)
|
||||
* Added `express` executable for setting up simple app templates
|
||||
* Added "GET /public/*" to Static plugin, defaulting to <root>/public
|
||||
* Added Static plugin
|
||||
* Fixed; Request#render() only calls cache.get() once
|
||||
* Fixed; Namespacing View caches with "view:"
|
||||
* Fixed; Namespacing Static caches with "static:"
|
||||
* Fixed; Both example apps now use the Static plugin
|
||||
* Fixed set("views"). Closes #239
|
||||
* Fixed missing space for combined log format
|
||||
* Deprecated Request#sendfile() and 'express/static'
|
||||
* Removed Server#running
|
||||
|
||||
0.7.5 / 2010-03-16
|
||||
==================
|
||||
|
||||
* Added Request#flash() support without args, now returns all flashes
|
||||
* Updated ext submodule
|
||||
|
||||
0.7.4 / 2010-03-16
|
||||
==================
|
||||
|
||||
* Fixed session reaper
|
||||
* Changed; class.js replacing js-oo Class implementation (quite a bit faster, no browser cruft)
|
||||
|
||||
0.7.3 / 2010-03-16
|
||||
==================
|
||||
|
||||
* Added package.json
|
||||
* Fixed requiring of haml / sass due to kiwi removal
|
||||
|
||||
0.7.2 / 2010-03-16
|
||||
==================
|
||||
|
||||
* Fixed GIT submodules (HAH!)
|
||||
|
||||
0.7.1 / 2010-03-16
|
||||
==================
|
||||
|
||||
* Changed; Express now using submodules again until a PM is adopted
|
||||
* Changed; chat example using millisecond conversions from ext
|
||||
|
||||
0.7.0 / 2010-03-15
|
||||
==================
|
||||
|
||||
* Added Request#pass() support (finds the next matching route, or the given path)
|
||||
* Added Logger plugin (default "common" format replaces CommonLogger)
|
||||
* Removed Profiler plugin
|
||||
* Removed CommonLogger plugin
|
||||
|
||||
0.6.0 / 2010-03-11
|
||||
==================
|
||||
|
||||
* Added seed.yml for kiwi package management support
|
||||
* Added HTTP client query string support when method is GET. Closes #205
|
||||
|
||||
* Added support for arbitrary view engines.
|
||||
For example "foo.engine.html" will now require('engine'),
|
||||
the exports from this module are cached after the first require().
|
||||
|
||||
* Added async plugin support
|
||||
|
||||
* Removed usage of RESTful route funcs as http client
|
||||
get() etc, use http.get() and friends
|
||||
|
||||
* Removed custom exceptions
|
||||
|
||||
0.5.0 / 2010-03-10
|
||||
==================
|
||||
|
||||
* Added ext dependency (library of js extensions)
|
||||
* Removed extname() / basename() utils. Use path module
|
||||
* Removed toArray() util. Use arguments.values
|
||||
* Removed escapeRegexp() util. Use RegExp.escape()
|
||||
* Removed process.mixin() dependency. Use utils.mixin()
|
||||
* Removed Collection
|
||||
* Removed ElementCollection
|
||||
* Shameless self promotion of ebook "Advanced JavaScript" (http://dev-mag.com) ;)
|
||||
|
||||
0.4.0 / 2010-02-11
|
||||
==================
|
||||
|
||||
* Added flash() example to sample upload app
|
||||
* Added high level restful http client module (express/http)
|
||||
* Changed; RESTful route functions double as HTTP clients. Closes #69
|
||||
* Changed; throwing error when routes are added at runtime
|
||||
* Changed; defaulting render() context to the current Request. Closes #197
|
||||
* Updated haml submodule
|
||||
|
||||
0.3.0 / 2010-02-11
|
||||
==================
|
||||
|
||||
* Updated haml / sass submodules. Closes #200
|
||||
* Added flash message support. Closes #64
|
||||
* Added accepts() now allows multiple args. fixes #117
|
||||
* Added support for plugins to halt. Closes #189
|
||||
* Added alternate layout support. Closes #119
|
||||
* Removed Route#run(). Closes #188
|
||||
* Fixed broken specs due to use(Cookie) missing
|
||||
|
||||
0.2.1 / 2010-02-05
|
||||
==================
|
||||
|
||||
* Added "plot" format option for Profiler (for gnuplot processing)
|
||||
* Added request number to Profiler plugin
|
||||
* Fixed binary encoding for multi-part file uploads, was previously defaulting to UTF8
|
||||
* Fixed issue with routes not firing when not files are present. Closes #184
|
||||
* Fixed process.Promise -> events.Promise
|
||||
|
||||
0.2.0 / 2010-02-03
|
||||
==================
|
||||
|
||||
|
||||
+68
-29
@@ -1,33 +1,72 @@
|
||||
|
||||
NODE = node
|
||||
PREFIX = /usr/local
|
||||
LIB_PREFIX = ~/.node_libraries
|
||||
|
||||
all: test
|
||||
DOCS = docs/index.md \
|
||||
docs/executable.md \
|
||||
docs/contrib.md \
|
||||
docs/guide.md \
|
||||
docs/migrate.md
|
||||
|
||||
init:
|
||||
@git submodule init && git submodule update
|
||||
MANPAGES =$(DOCS:.md=.1)
|
||||
HTMLDOCS =$(DOCS:.md=.html)
|
||||
|
||||
test: init spec/support/libxmljs/libxmljs.node
|
||||
@$(NODE) spec/node.js all
|
||||
|
||||
test-independant: init
|
||||
@$(NODE) spec/node.js independant
|
||||
|
||||
test-dependant: init spec/support/libxmljs/libxmljs.node
|
||||
@$(NODE) spec/node.js dependant
|
||||
|
||||
app: app-chat
|
||||
|
||||
app-chat:
|
||||
@$(NODE) examples/chat/app.js
|
||||
|
||||
app-upload:
|
||||
@$(NODE) examples/upload/app.js
|
||||
|
||||
benchmark:
|
||||
@$(NODE) benchmarks/collection.js
|
||||
@$(NODE) benchmarks/views.js
|
||||
|
||||
spec/support/libxmljs/libxmljs.node:
|
||||
@scons -C spec/support/libxmljs libxmljs.node
|
||||
|
||||
.PHONY: init test benchmark app
|
||||
install: install-docs
|
||||
@mkdir -p $(PREFIX)/bin
|
||||
cp -f bin/express $(PREFIX)/bin/express
|
||||
cp -fr lib/express $(LIB_PREFIX)/express
|
||||
|
||||
uninstall: uninstall-docs
|
||||
rm -f $(PREFIX)/bin/express
|
||||
rm -fr $(LIB_PREFIX)/express
|
||||
|
||||
install-support:
|
||||
cd support/connect && $(MAKE) install
|
||||
cd support/jade && $(MAKE) install
|
||||
|
||||
uninstall-support:
|
||||
cd support/connect && $(MAKE) uninstall
|
||||
cd support/jade && $(MAKE) uninstall
|
||||
|
||||
install-docs:
|
||||
cp -f docs/executable.1 $(PREFIX)/share/man/man1/express.1
|
||||
|
||||
uninstall-docs:
|
||||
rm -f $(PREFIX)/share/man/man1/express.1
|
||||
|
||||
test:
|
||||
@CONNECT_ENV=test ./support/expresso/bin/expresso \
|
||||
-I lib \
|
||||
-I support/connect/lib \
|
||||
-I support/haml/lib \
|
||||
-I support/jade/lib \
|
||||
-I support/ejs/lib \
|
||||
$(TESTFLAGS) \
|
||||
test/*.test.js
|
||||
test-cov:
|
||||
@TESTFLAGS=--cov $(MAKE) test
|
||||
|
||||
docs: docs/api.html $(MANPAGES) $(HTMLDOCS)
|
||||
@ echo "... generating TOC"
|
||||
@./support/toc.js docs/guide.html
|
||||
|
||||
docs/api.html: lib/express/*.js
|
||||
dox --title Express \
|
||||
--desc "High performance web framework for [node](http://nodejs.org)." \
|
||||
$(shell find lib/express/* -type f) > $@
|
||||
|
||||
%.1: %.md
|
||||
@echo "... $< -> $@"
|
||||
@ronn -r --pipe $< > $@
|
||||
|
||||
%.html: %.md
|
||||
@echo "... $< -> $@"
|
||||
@ronn -5 --pipe --fragment $< \
|
||||
| cat docs/layout/head.html - docs/layout/foot.html \
|
||||
| sed 's/NAME/Express/g' \
|
||||
> $@
|
||||
|
||||
docclean:
|
||||
rm -f docs/*.{1,html}
|
||||
|
||||
.PHONY: install uninstall install-docs install-support uninstall-support install-docs uninstall-docs test test-cov docs docclean
|
||||
+62
-101
@@ -2,128 +2,89 @@
|
||||
# Express
|
||||
|
||||
Insanely fast (and small) server-side JavaScript web development framework
|
||||
built on **node.js** and the **V8 JavaScript engine**.
|
||||
|
||||
* Visit the [Wiki](http://wiki.github.com/visionmedia/express) for documentation
|
||||
* Visit the [Google Group](http://groups.google.com/group/express-js) for discussion
|
||||
|
||||
## Features (so far)
|
||||
|
||||
* Sexy DSL with robust sinatra-like routing
|
||||
* High performance
|
||||
* Session support
|
||||
* Mime helpers
|
||||
* Redirection helpers
|
||||
* Nested parameter parsing
|
||||
* Full test coverage
|
||||
* Extremely readable specs
|
||||
* Test helpers (mock requests etc)
|
||||
* Environment based configuration
|
||||
* Light-weight JavaScript class implementation via js-oo
|
||||
* Collections and chainable iterators
|
||||
* ElementCollections / markup parsing via libxmljs and css selector traversal support via css2xpath
|
||||
* View support (ejs, haml, sass, etc)
|
||||
built on [node](http://nodejs.org) and [Connect](http://github.com/extjs/Connect).
|
||||
|
||||
var app = express.createServer();
|
||||
|
||||
app.get('/', function(req, res){
|
||||
res.send('Hello World');
|
||||
});
|
||||
|
||||
app.listen(3000);
|
||||
|
||||
## Installation
|
||||
|
||||
Currently Express must be cloned (or downloaded), you can use the following command to
|
||||
get rolling and initialize the submodule dependencies:
|
||||
npm:
|
||||
|
||||
$ git clone git://github.com/visionmedia/express.git && cd express && git submodule update --init && make app
|
||||
|
||||
Or with the [gh](http://github.com/visionmedia/gh) utility:
|
||||
$ npm install connect
|
||||
$ npm install express
|
||||
|
||||
$ gh clone visionmedia express && cd express && git submodule update --init && make app
|
||||
curl (or similar):
|
||||
|
||||
## Performance
|
||||
$ curl -# http://github.com/visionmedia/express/raw/master/install.sh | sh
|
||||
|
||||
Extensive benchmarking will wait until a development version
|
||||
has been released.
|
||||
|
||||
Currently Express can chew through a request with a two Haml views (*page and layout*)
|
||||
requested **2000** times with concurrency of **80** in **2.4** seconds and **814**
|
||||
requests per second. With no caching involved.
|
||||
|
||||
An identical Sinatra application was served with the **Thin** HTTP server
|
||||
and scored **8.3** seconds and **238** requests per second. In this situation
|
||||
Express is currently **3.5** times faster than Sinatra.
|
||||
git clone, first update the submodules:
|
||||
|
||||
## Examples
|
||||
$ git submodule update --init
|
||||
$ make install
|
||||
$ make install-support
|
||||
|
||||
Below is a minimal app example when express is already within your load path.
|
||||
## Features
|
||||
|
||||
* Robust routing
|
||||
* Redirection helpers
|
||||
* Focus on high performance
|
||||
* View rendering and partials support
|
||||
* Environment based configuration
|
||||
* Session based flash notifications
|
||||
* Built on [Connect](http://extjs.github.com/Connect)
|
||||
* Executable for generating applications quickly
|
||||
|
||||
Via Connect:
|
||||
|
||||
* Session support
|
||||
* Cache API
|
||||
* Mime helpers
|
||||
* ETag support
|
||||
* Persistent flash notifications
|
||||
* Cookie support
|
||||
* JSON-RPC
|
||||
* Logging
|
||||
* and _much_ more!
|
||||
|
||||
## Contributors
|
||||
|
||||
The following are the major contributors of Express (in no specific order).
|
||||
|
||||
* TJ Holowaychuk ([visionmedia](http://github.com/visionmedia))
|
||||
* Ciaran Jessup ([ciaranj](http://github.com/ciaranj))
|
||||
* Aaron Heckmann ([aheckmann](http://github.com/aheckmann))
|
||||
|
||||
## More Information
|
||||
|
||||
* Follow [tjholowaychuk](http://twitter.com/tjholowaychuk) on twitter for updates
|
||||
* [Google Group](http://groups.google.com/group/express-js) for discussion
|
||||
* [JavaScript Extensions & Utilities](http://github.com/visionmedia/ext.js)
|
||||
* [JavaScript Sass](http://github.com/visionmedia/sass.js)
|
||||
* [JavaScript Haml](http://github.com/visionmedia/haml.js)
|
||||
* [JavaScript Jade](http://github.com/visionmedia/jade) spiritual successor of Haml
|
||||
|
||||
require('express')
|
||||
require('express/plugins')
|
||||
|
||||
configure(function(){
|
||||
use(MethodOverride)
|
||||
use(ContentLength)
|
||||
set('root', __dirname)
|
||||
})
|
||||
|
||||
get('/hello', function(){
|
||||
this.contentType('html')
|
||||
return '<h1>World<h1>'
|
||||
})
|
||||
|
||||
get('/user/:id?', function(id) {
|
||||
this.render('user.haml.html', {
|
||||
locals: {
|
||||
name: id ? 'User ' + id : 'You'
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
run()
|
||||
|
||||
## Running Tests
|
||||
|
||||
Express uses the [JSpec](http://jspec.info) BDD JavaScript testing
|
||||
framework to write and run elegant spec suites. JSpec is frozen
|
||||
to spec/lib and does not require separate installation.
|
||||
|
||||
To run all specifications run the following command. This will ensure
|
||||
git submodules are initialized and updated, as well as building test
|
||||
related dependencies such as libxmljs.
|
||||
Express uses the [Expresso](http://github.com/visionmedia/expresso) TDD
|
||||
framework to write and run elegant test suites extremely fast. First `$ git submodule update --init`, then run:
|
||||
|
||||
$ make test
|
||||
|
||||
To run independent specs (which do not require building of external apis etc) use:
|
||||
The latest release of Express is compatible with node --version:
|
||||
|
||||
$ make test-independant
|
||||
|
||||
To run dependent specs (which require building of external apis etc) use:
|
||||
|
||||
$ make test-dependant
|
||||
|
||||
Run individual suites:
|
||||
|
||||
$ node spec/node.js core
|
||||
$ node spec/node.js mime
|
||||
$ node spec/node.js routing
|
||||
...
|
||||
|
||||
Express is currently being developed with node --version:
|
||||
v0.1.27
|
||||
|
||||
## More Information
|
||||
|
||||
* [JavaScript Extensions & Utilities](http://github.com/visionmedia/ext.js)
|
||||
* [JavaScript Sass](http://github.com/visionmedia/sass.js)
|
||||
* [Scons Build System](http://www.scons.org/) (some development dependencies rely on this, ex libxmljs)
|
||||
|
||||
## Contributors
|
||||
|
||||
* TJ Holowaychuk (visionmedia) <tj@vision-media.ca>
|
||||
* Ciaran Jessup (ciaranj) <ciaranj@gmail.com>
|
||||
* Gareth Jones (csausdev) <gareth.jones@sensis.com.au>
|
||||
* Aaron Heckmann (aheckmann) <aaron.heckmann+github@gmail.com>
|
||||
v0.1.100
|
||||
|
||||
## License
|
||||
|
||||
(The MIT License)
|
||||
|
||||
Copyright (c) 2009 TJ Holowaychuk <tj@vision-media.ca>
|
||||
Copyright (c) 2009-2010 TJ Holowaychuk <tj@vision-media.ca>
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining
|
||||
a copy of this software and associated documentation files (the
|
||||
|
||||
@@ -1,83 +0,0 @@
|
||||
|
||||
;(function(){
|
||||
var currentSuite
|
||||
|
||||
/**
|
||||
* Contents of _fn_. Strips function literal and signature.
|
||||
*
|
||||
* @param {function} fn
|
||||
* @return {string}
|
||||
* @api private
|
||||
*/
|
||||
|
||||
function contentsOf(fn) {
|
||||
return fn.toString().match(/^[^\{]*{((.*\n*)*)}/m)[1]
|
||||
}
|
||||
|
||||
/**
|
||||
* Pad _str_ to _len_.
|
||||
*
|
||||
* @param {string} str
|
||||
* @param {integer} len
|
||||
* @return {string}
|
||||
* @api private
|
||||
*/
|
||||
|
||||
function pad(str, len) {
|
||||
return str + (new Array(len - str.length)).join(' ')
|
||||
}
|
||||
|
||||
/**
|
||||
* Time the execution of _fn_
|
||||
*
|
||||
* @param {function} fn
|
||||
* @return {float}
|
||||
* @api private
|
||||
*/
|
||||
|
||||
function time(fn) {
|
||||
var start = Number(new Date)
|
||||
fn()
|
||||
return (Number(new Date) - start) / 1000
|
||||
}
|
||||
|
||||
/**
|
||||
* Benchmark _fn_ with the given _label_.
|
||||
*
|
||||
* @param {string} label
|
||||
* @param {function} fn
|
||||
* @api public
|
||||
*/
|
||||
|
||||
function benchmark(label, fn) {
|
||||
var duration = time(function(){
|
||||
for (var i = 0; i < currentSuite.times; ++i)
|
||||
fn()
|
||||
}).toFixed(3)
|
||||
print(pad(' ' + label, 50 - duration.toString().length) + duration + ' |')
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a benchmark suite with the given _label_, which
|
||||
* will run each benchmark n _times_. If _times_ is omitted
|
||||
* then it defaults to 1.
|
||||
*
|
||||
* @param {string} label
|
||||
* @param {integer, function} times
|
||||
* @param {function} fn
|
||||
* @api public
|
||||
*/
|
||||
|
||||
suite = function(label, times, fn) {
|
||||
currentSuite = this
|
||||
if (typeof times == 'function')
|
||||
this.times = 1, fn = times
|
||||
else
|
||||
this.times = times
|
||||
print('\n ' + pad(label, 42 - this.times.toString().length) + this.times + ' time(s)')
|
||||
print(' -------------------------------------------------')
|
||||
eval(contentsOf(fn))
|
||||
print('')
|
||||
}
|
||||
|
||||
})()
|
||||
@@ -1,111 +0,0 @@
|
||||
|
||||
require.paths.unshift('lib')
|
||||
require.paths.unshift('benchmarks')
|
||||
process.mixin(GLOBAL, require('sys'))
|
||||
process.mixin(GLOBAL, require('benchmark'))
|
||||
require('express')
|
||||
|
||||
print = puts
|
||||
|
||||
range = function(a, b) {
|
||||
var array = []
|
||||
while (a++ < b)
|
||||
array.push(a-1)
|
||||
return array
|
||||
}
|
||||
|
||||
suite('Collection with [0..10,000]', 100, function(){
|
||||
array = range(0, 10000)
|
||||
|
||||
benchmark('for', function(){
|
||||
for (var i = 0, len = array.length; i < len; ++i) ;
|
||||
})
|
||||
|
||||
benchmark('for uncached', function(){
|
||||
for (var i = 0; i < array.length; ++i) ;
|
||||
})
|
||||
|
||||
benchmark('forEach()', function(){
|
||||
array.forEach(function(){})
|
||||
})
|
||||
|
||||
benchmark('#each()', function(){
|
||||
$(array).each(function(){})
|
||||
})
|
||||
|
||||
benchmark('#map()', function(){
|
||||
$(array).map(function(n){ return n += 1 })
|
||||
})
|
||||
|
||||
benchmark('#map() with shorthand', function(){
|
||||
$(array).map('a += 1')
|
||||
})
|
||||
|
||||
benchmark('#find()', function(){
|
||||
$(array).find(function(n){ return n > 5000 })
|
||||
})
|
||||
|
||||
benchmark('#select()', function(){
|
||||
$(array).select(function(n){ return n % 2 })
|
||||
})
|
||||
|
||||
benchmark('#first()', function(){
|
||||
$(array).first(5000)
|
||||
})
|
||||
|
||||
benchmark('#slice()', function(){
|
||||
$(array).slice(100, 5000)
|
||||
})
|
||||
|
||||
benchmark('#drop()', function(){
|
||||
$(array).drop(5000)
|
||||
})
|
||||
|
||||
benchmark('#length()', function(){
|
||||
$(array).length()
|
||||
})
|
||||
|
||||
benchmark('#keys()', function(){
|
||||
$(array).keys()
|
||||
})
|
||||
|
||||
benchmark('#toArray()', function(){
|
||||
$(array).toArray()
|
||||
})
|
||||
|
||||
benchmark('#min()', function(){
|
||||
$(array).min()
|
||||
})
|
||||
|
||||
benchmark('#max()', function(){
|
||||
$(array).max()
|
||||
})
|
||||
|
||||
benchmark('#sum()', function(){
|
||||
$(array).sum()
|
||||
})
|
||||
|
||||
benchmark('#avg()', function(){
|
||||
$(array).avg()
|
||||
})
|
||||
|
||||
benchmark('#clone()', function(){
|
||||
$(array).clone()
|
||||
})
|
||||
|
||||
benchmark('#merge()', function(){
|
||||
$(array).merge({ foo: 'bar' })
|
||||
})
|
||||
|
||||
benchmark('#sample()', function(){
|
||||
$(array).sample()
|
||||
})
|
||||
|
||||
benchmark('#chunk()', function(){
|
||||
$(array).chunk(5)
|
||||
})
|
||||
|
||||
benchmark('#at()', function(){
|
||||
$(array).at(5000)
|
||||
})
|
||||
})
|
||||
@@ -1,56 +0,0 @@
|
||||
|
||||
require.paths.unshift('lib')
|
||||
require.paths.unshift('benchmarks')
|
||||
process.mixin(GLOBAL, require('sys'))
|
||||
process.mixin(GLOBAL, require('benchmark'))
|
||||
require('express')
|
||||
|
||||
print = puts
|
||||
|
||||
engine = {
|
||||
ejs: require('ejs'),
|
||||
haml: require('haml'),
|
||||
sass: require('sass')
|
||||
}
|
||||
|
||||
options = { locals: { article: { title: 'Foo', body: 'bar' }}}
|
||||
|
||||
ejs = ' \n\
|
||||
<div id="primary"> \n\
|
||||
<div class="block first"> \n\
|
||||
<h1><%= article.title %></h1> \n\
|
||||
<p><%= article.body %></p> \n\
|
||||
</div> \n\
|
||||
</div> \n\
|
||||
'
|
||||
|
||||
haml = ' \n\
|
||||
#primary \n\
|
||||
.block.first \n\
|
||||
%h1= article.title \n\
|
||||
%p= article.body \n\
|
||||
'
|
||||
|
||||
sass = ' \n\
|
||||
red: #ff0000 \n\
|
||||
body \n\
|
||||
ul \n\
|
||||
li \n\
|
||||
a \n\
|
||||
:color !red \n\
|
||||
:list-style none \n\
|
||||
'
|
||||
|
||||
suite('Template Engines', 1000, function(){
|
||||
benchmark('ejs', function(){
|
||||
engine.ejs.render(ejs, options)
|
||||
})
|
||||
|
||||
benchmark('haml', function(){
|
||||
engine.haml.render(haml, options)
|
||||
})
|
||||
|
||||
benchmark('sass', function(){
|
||||
engine.sass.render(sass)
|
||||
})
|
||||
})
|
||||
Arquivo executável
+183
@@ -0,0 +1,183 @@
|
||||
#!/usr/bin/env node
|
||||
|
||||
/**
|
||||
* Module dependencies.
|
||||
*/
|
||||
|
||||
var sys = require('sys'),
|
||||
fs = require('fs'),
|
||||
childProcess = require('child_process');
|
||||
|
||||
/**
|
||||
* Framework version.
|
||||
*/
|
||||
|
||||
var version = '1.0.0beta';
|
||||
|
||||
/**
|
||||
* Usage documentation.
|
||||
*/
|
||||
|
||||
var usage = ''
|
||||
+ '\x1b[1mUsage\x1b[0m: express [options] [PATH]\n'
|
||||
+ '\n'
|
||||
+ '\x1b[1mOptions\x1b[0m:\n'
|
||||
+ ' -v, --version Output framework version\n'
|
||||
+ ' -h, --help Output help information\n';
|
||||
|
||||
/**
|
||||
* Jade layout template.
|
||||
*/
|
||||
|
||||
var jadeLayout = [
|
||||
'!!!',
|
||||
'html',
|
||||
' head',
|
||||
' title= title',
|
||||
' link(rel=\'stylesheet\', href=\'/stylesheets/style.css\')',
|
||||
' body!= body'
|
||||
].join('\n');
|
||||
|
||||
/**
|
||||
* Default sass template.
|
||||
*/
|
||||
|
||||
var sass = [
|
||||
'body',
|
||||
' :padding 50px',
|
||||
' :font 14px "Lucida Grande", "Helvetica Nueue", Arial, sans-serif'
|
||||
].join('\n');
|
||||
|
||||
/**
|
||||
* Jade index template.
|
||||
*/
|
||||
|
||||
var jadeIndex = [
|
||||
'h1= title',
|
||||
'p Welcome to #{title}'
|
||||
].join('\n');
|
||||
|
||||
/**
|
||||
* App template.
|
||||
*/
|
||||
|
||||
var app = [
|
||||
'',
|
||||
'/**',
|
||||
' * Module dependencies.',
|
||||
' */',
|
||||
'',
|
||||
'var express = require(\'express\'),',
|
||||
' connect = require(\'connect\');',
|
||||
'',
|
||||
'// Create and export Express app',
|
||||
'',
|
||||
'var app = express.createServer();',
|
||||
'',
|
||||
'// Configuration',
|
||||
'',
|
||||
'app.configure(function(){',
|
||||
' app.set(\'views\', __dirname + \'/views\');',
|
||||
' app.use(\'/\', connect.bodyDecoder());',
|
||||
' app.use(\'/\', connect.methodOverride());',
|
||||
' app.use(\'/\', connect.compiler({ src: __dirname + \'/public\', enable: [\'sass\'] }));',
|
||||
' app.use(\'/\', connect.staticProvider(__dirname + \'/public\'));',
|
||||
'});',
|
||||
'',
|
||||
'app.configure(\'development\', function(){',
|
||||
' app.set(\'reload views\', 1000);',
|
||||
' app.use(\'/\', connect.errorHandler({ dumpExceptions: true, showStack: true })); ',
|
||||
'});',
|
||||
'',
|
||||
'app.configure(\'production\', function(){',
|
||||
' app.use(\'/\', connect.errorHandler()); ',
|
||||
'});',
|
||||
'',
|
||||
'// Routes',
|
||||
'',
|
||||
'app.get(\'/\', function(req, res){',
|
||||
' res.render(\'index.jade\', {',
|
||||
' locals: {',
|
||||
' title: \'Express\'',
|
||||
' }',
|
||||
' });',
|
||||
'});',
|
||||
'',
|
||||
'app.listen(3000);',
|
||||
''
|
||||
].join('\n');
|
||||
|
||||
// Parse arguments
|
||||
|
||||
var args = process.argv.slice(2),
|
||||
path = '.';
|
||||
while (args.length) {
|
||||
var arg = args.shift();
|
||||
switch (arg) {
|
||||
case '-h':
|
||||
case '--help':
|
||||
abort(usage);
|
||||
break;
|
||||
case '-v':
|
||||
case '--version':
|
||||
abort(version);
|
||||
break;
|
||||
default:
|
||||
path = arg;
|
||||
}
|
||||
}
|
||||
|
||||
// Generate application
|
||||
|
||||
(function createApplication(path) {
|
||||
mkdir(path, function(){
|
||||
mkdir(path + '/pids');
|
||||
mkdir(path + '/logs');
|
||||
mkdir(path + '/public/{javascripts,stylesheets,images}', function(){
|
||||
write(path + '/public/stylesheets/style.sass', sass);
|
||||
});
|
||||
mkdir(path + '/views/partials', function(){
|
||||
write(path + '/views/layout.jade', jadeLayout);
|
||||
write(path + '/views/index.jade', jadeIndex);
|
||||
});
|
||||
write(path + '/app.js', app);
|
||||
});
|
||||
})(path);
|
||||
|
||||
/**
|
||||
* echo str > path.
|
||||
*
|
||||
* @param {String} path
|
||||
* @param {String} str
|
||||
*/
|
||||
|
||||
function write(path, str) {
|
||||
fs.writeFile(path, str);
|
||||
sys.puts(' create : ' + path);
|
||||
}
|
||||
|
||||
/**
|
||||
* Mkdir -p.
|
||||
*
|
||||
* @param {String} path
|
||||
* @param {Function} fn
|
||||
*/
|
||||
|
||||
function mkdir(path, fn) {
|
||||
childProcess.exec('mkdir -p ' + path, function(err){
|
||||
if (err) throw err;
|
||||
sys.puts(' create : ' + path);
|
||||
fn && fn();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Exit with the given `str`.
|
||||
*
|
||||
* @param {String} str
|
||||
*/
|
||||
|
||||
function abort(str) {
|
||||
sys.error(str);
|
||||
process.exit(1);
|
||||
}
|
||||
+1032
Diferenças do arquivo suprimidas por serem muito extensas
Carregar Diff
@@ -0,0 +1,64 @@
|
||||
.\" generated with Ronn/v0.6.6
|
||||
.\" http://github.com/rtomayko/ronn/
|
||||
.
|
||||
.TH "CONTRIB" "" "July 2010" "" ""
|
||||
.
|
||||
.SS "Development Dependencies"
|
||||
Express development dependencies are stored within the \fI\./support\fR directory\. To update them execute:
|
||||
.
|
||||
.IP "" 4
|
||||
.
|
||||
.nf
|
||||
|
||||
$ git submodule update \-\-init
|
||||
.
|
||||
.fi
|
||||
.
|
||||
.IP "" 0
|
||||
.
|
||||
.SS "Running Tests"
|
||||
Express uses the Expresso \fIhttp://github\.com/visionmedia/expresso\fR TDD framework to write and run elegant test suites extremely fast\. To run all test suites simply execute:
|
||||
.
|
||||
.IP "" 4
|
||||
.
|
||||
.nf
|
||||
|
||||
$ make test
|
||||
.
|
||||
.fi
|
||||
.
|
||||
.IP "" 0
|
||||
.
|
||||
.P
|
||||
To target specific suites we may specify the files via:
|
||||
.
|
||||
.IP "" 4
|
||||
.
|
||||
.nf
|
||||
|
||||
$ make test TESTS=test/view\.test\.js
|
||||
.
|
||||
.fi
|
||||
.
|
||||
.IP "" 0
|
||||
.
|
||||
.SS "Contributions"
|
||||
To accept a contribution, you should follow these guidelines:
|
||||
.
|
||||
.IP "\(bu" 4
|
||||
All tests \fImust\fR pass
|
||||
.
|
||||
.IP "\(bu" 4
|
||||
Your alterations or additions \fImust\fR include tests
|
||||
.
|
||||
.IP "\(bu" 4
|
||||
Your commit(s) should be \fIfocused\fR, do not commit once for several changes
|
||||
.
|
||||
.IP "\(bu" 4
|
||||
Do \fInot\fR alter release information such as the \fIversion\fR, or \fIHistory\.md\fR
|
||||
.
|
||||
.IP "\(bu" 4
|
||||
Indents are \fI4\fR spaces\.
|
||||
.
|
||||
.IP "" 0
|
||||
|
||||
@@ -0,0 +1,220 @@
|
||||
<html>
|
||||
<head>
|
||||
<title>Express - node web framework</title>
|
||||
<style>
|
||||
#header {
|
||||
position: absolute;
|
||||
top: 10px;
|
||||
left: 0;
|
||||
padding: 12px 0;
|
||||
text-indent: 40px;
|
||||
width: 100%;
|
||||
border-top: 1px solid rgba(0,0,0,0.7);
|
||||
border-bottom: 1px solid rgba(0,0,0,0.7);
|
||||
background: rgba(255,255,255,0.1) url(http://www.sencha.com/favicon.ico) no-repeat 15px 50%;
|
||||
text-align: left;
|
||||
color: #fff;
|
||||
}
|
||||
#tagline {
|
||||
margin-left: 75px;
|
||||
margin-bottom: 30px;
|
||||
color: rgba(255,255,255,0.7); }
|
||||
html {
|
||||
background: #1c1c1c url(images/bg.tile.jpg); }
|
||||
|
||||
body {
|
||||
margin: 0;
|
||||
padding-bottom: 30px;
|
||||
font: 14px/1.4 "Helvetica Neue", "Lucida Grande", "Arial";
|
||||
font-size: 14px;
|
||||
line-height: 1.5;
|
||||
-webkit-text-stroke: 1px rgba(0, 0, 0, 0.1);
|
||||
-moz-text-stroke: 1px rgba(0, 0, 0, 0.1);
|
||||
background: url(images/bg.jpg) 50% 0 no-repeat;
|
||||
color: #8b8b8b; }
|
||||
|
||||
* {
|
||||
outline: none; }
|
||||
|
||||
em {
|
||||
color: white; }
|
||||
|
||||
a img {
|
||||
border: none !important; }
|
||||
|
||||
a {
|
||||
font-weight: bold;
|
||||
text-decoration: none;
|
||||
color: white;
|
||||
-webkit-transition-property: opacity, -webkit-transform, color, background-color, padding, -webkit-box-shadow;
|
||||
-webkit-transition-duration: 0.15s;
|
||||
-webkit-transition-timing-function: ease-out; }
|
||||
a:hover {
|
||||
opacity: 0.8; }
|
||||
|
||||
h1, h2, h3 {
|
||||
margin: 45px 0 0 0;
|
||||
color: white;
|
||||
text-shadow: 1px 2px 2px rgba(0,0,0,0.6); }
|
||||
|
||||
h3 {
|
||||
font-size: 18px; }
|
||||
|
||||
pre {
|
||||
margin: 20px 10px;
|
||||
padding: 25px 20px;
|
||||
background: rgba(0,0,0,0.5);
|
||||
border: 1px solid #323232;
|
||||
-webkit-box-shadow: 1px 2px 2px rgba(0,0,0,0.6);
|
||||
-moz-box-shadow: 1px 2px 2px rgba(0,0,0,0.6);
|
||||
-webkit-border-radius: 5px;
|
||||
-moz-border-radius: 5px; }
|
||||
|
||||
code {
|
||||
font-family: "Helvetica Neue", "Lucida Grande", "Arial"; }
|
||||
|
||||
ul {
|
||||
margin: 15px 0;
|
||||
padding: 0 0 0 35px; }
|
||||
ul li {
|
||||
margin: 0;
|
||||
padding: 2px 0;
|
||||
list-style: square; }
|
||||
|
||||
.sect {
|
||||
margin-left: 40px; }
|
||||
|
||||
#logo {
|
||||
display: block;
|
||||
margin-left: 30%;
|
||||
margin-bottom: 30px;
|
||||
width: 194px;
|
||||
height: 51px;
|
||||
background: url(images/logo.png) 0 0 no-repeat;
|
||||
text-indent: -99999px; }
|
||||
#logo:hover {
|
||||
opacity: 0.7; }
|
||||
#logo:active {
|
||||
opacity: 0.3; }
|
||||
|
||||
#ribbon {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
right: 0;
|
||||
z-index: 2; }
|
||||
|
||||
#wrapper {
|
||||
width: 100%;
|
||||
min-height: 800px;
|
||||
background: url(images/top.png) 0 0 repeat-x; }
|
||||
|
||||
#container {
|
||||
margin: 0 auto;
|
||||
padding-top: 110px;
|
||||
width: 550px; }
|
||||
|
||||
#toc {
|
||||
position: fixed;
|
||||
top: 60px;
|
||||
left: 0;
|
||||
margin: 0 0 0 15px;
|
||||
padding: 15px;
|
||||
height: 100%;
|
||||
background: rgba(0,0,0,0.2);
|
||||
border-right: 1px solid rgba(255,255,255,0.05);
|
||||
}
|
||||
#toc li {
|
||||
padding: 1px 0;
|
||||
list-style: none;
|
||||
}
|
||||
#toc li a {
|
||||
font-size: 11px;
|
||||
}
|
||||
#menu {
|
||||
margin-left: 65px;
|
||||
padding: 0;
|
||||
padding-bottom: 30px; }
|
||||
#menu li {
|
||||
display: inline;
|
||||
list-style: none; }
|
||||
#menu li a {
|
||||
display: block;
|
||||
float: left;
|
||||
margin: 0 2px;
|
||||
padding: 3px 15px;
|
||||
background: rgba(0,0,0,0.2);
|
||||
-webkit-border-radius: 8px;
|
||||
-moz-border-radius: 8px;
|
||||
-webkit-box-shadow: 1px 2px 2px rgba(0,0,0,0.6);
|
||||
-moz-box-shadow: 1px 2px 2px rgba(0,0,0,0.6);
|
||||
-webkit-transition-property: opacity, -webkit-transform, color, background-color, -webkit-box-shadow;
|
||||
-webkit-transition-duration: 0.15s;
|
||||
-webkit-transition-timing-function: ease-out; }
|
||||
#menu li a:hover,
|
||||
#menu li a.active {
|
||||
background: rgba(0,0,0,0.5); }
|
||||
#menu li a:active {
|
||||
background: rgba(0,0,0,0.1);
|
||||
-webkit-box-shadow: 1px 1px 1px rgba(0,0,0,0.4);
|
||||
-moz-box-shadow: 1px 1px 1px rgba(0,0,0,0.4); }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<a href='http://github.com/visionmedia/express'>
|
||||
<img alt='Fork me on GitHub' id='ribbon' src='http://s3.amazonaws.com/github/ribbons/forkme_right_white_ffffff.png' />
|
||||
</a>
|
||||
<div id="header"><strong>Sencha</strong> labs</div>
|
||||
<div id="wrapper">
|
||||
<div id="container">
|
||||
<a href='http://github.com/visionmedia/express' id='logo'>Express</a>
|
||||
<p id="tagline">
|
||||
High performance, high class web development for
|
||||
<a href="http://nodejs.org">Node.js</a>
|
||||
</p>
|
||||
<ul id="menu">
|
||||
<li><a href="index.html">Home</a></li>
|
||||
<li><a href="guide.html">Guide</a></li>
|
||||
<li><a href="contrib.html">Contributing</a></li>
|
||||
<li><a href="migrate.html">1.x Migration</a></li>
|
||||
</ul>
|
||||
<div class='mp'>
|
||||
<h3 id="Development-Dependencies">Development Dependencies</h3>
|
||||
|
||||
<p>Express development dependencies are stored within the <em>./support</em> directory. To
|
||||
update them execute:</p>
|
||||
|
||||
<pre><code>$ git submodule update --init
|
||||
</code></pre>
|
||||
|
||||
<h3 id="Running-Tests">Running Tests</h3>
|
||||
|
||||
<p>Express uses the <a href="http://github.com/visionmedia/expresso">Expresso</a> TDD
|
||||
framework to write and run elegant test suites extremely fast. To run all test suites
|
||||
simply execute:</p>
|
||||
|
||||
<pre><code>$ make test
|
||||
</code></pre>
|
||||
|
||||
<p>To target specific suites we may specify the files via:</p>
|
||||
|
||||
<pre><code>$ make test TESTS=test/view.test.js
|
||||
</code></pre>
|
||||
|
||||
<h3 id="Contributions">Contributions</h3>
|
||||
|
||||
<p>To accept a contribution, you should follow these guidelines:</p>
|
||||
|
||||
<ul>
|
||||
<li>All tests <em>must</em> pass</li>
|
||||
<li>Your alterations or additions <em>must</em> include tests</li>
|
||||
<li>Your commit(s) should be <em>focused</em>, do not commit once for several changes</li>
|
||||
<li>Do <em>not</em> alter release information such as the <em>version</em>, or <em>History.md</em></li>
|
||||
<li>Indents are <em>4</em> spaces.</li>
|
||||
</ul>
|
||||
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,29 @@
|
||||
|
||||
### Development Dependencies
|
||||
|
||||
Express development dependencies are stored within the _./support_ directory. To
|
||||
update them execute:
|
||||
|
||||
$ git submodule update --init
|
||||
|
||||
### Running Tests
|
||||
|
||||
Express uses the [Expresso](http://github.com/visionmedia/expresso) TDD
|
||||
framework to write and run elegant test suites extremely fast. To run all test suites
|
||||
simply execute:
|
||||
|
||||
$ make test
|
||||
|
||||
To target specific suites we may specify the files via:
|
||||
|
||||
$ make test TESTS=test/view.test.js
|
||||
|
||||
### Contributions
|
||||
|
||||
To accept a contribution, you should follow these guidelines:
|
||||
|
||||
* All tests _must_ pass
|
||||
* Your alterations or additions _must_ include tests
|
||||
* Your commit(s) should be _focused_, do not commit once for several changes
|
||||
* Do _not_ alter release information such as the _version_, or _History.md_
|
||||
* Indents are _4_ spaces.
|
||||
@@ -0,0 +1,25 @@
|
||||
.\" generated with Ronn/v0.6.6
|
||||
.\" http://github.com/rtomayko/ronn/
|
||||
.
|
||||
.TH "EXECUTABLE" "" "July 2010" "" ""
|
||||
.
|
||||
.SH "Synopsis"
|
||||
.
|
||||
.nf
|
||||
|
||||
express [\-h|\-\-help] [\-v|\-\-version] [PATH]
|
||||
.
|
||||
.fi
|
||||
.
|
||||
.SH "Description"
|
||||
The \fBexpress\fR executable generates apps at the given \fBPATH\fR or the current working directory\. Although Express is not bound to a specific application structure, this executable creates a maintainable base app\.
|
||||
.
|
||||
.SH "Options"
|
||||
.
|
||||
.nf
|
||||
|
||||
\-v, \-\-version Output framework version
|
||||
\-h, \-\-help Display help information
|
||||
.
|
||||
.fi
|
||||
|
||||
@@ -0,0 +1,202 @@
|
||||
<html>
|
||||
<head>
|
||||
<title>Express - node web framework</title>
|
||||
<style>
|
||||
#header {
|
||||
position: absolute;
|
||||
top: 10px;
|
||||
left: 0;
|
||||
padding: 12px 0;
|
||||
text-indent: 40px;
|
||||
width: 100%;
|
||||
border-top: 1px solid rgba(0,0,0,0.7);
|
||||
border-bottom: 1px solid rgba(0,0,0,0.7);
|
||||
background: rgba(255,255,255,0.1) url(http://www.sencha.com/favicon.ico) no-repeat 15px 50%;
|
||||
text-align: left;
|
||||
color: #fff;
|
||||
}
|
||||
#tagline {
|
||||
margin-left: 75px;
|
||||
margin-bottom: 30px;
|
||||
color: rgba(255,255,255,0.7); }
|
||||
html {
|
||||
background: #1c1c1c url(images/bg.tile.jpg); }
|
||||
|
||||
body {
|
||||
margin: 0;
|
||||
padding-bottom: 30px;
|
||||
font: 14px/1.4 "Helvetica Neue", "Lucida Grande", "Arial";
|
||||
font-size: 14px;
|
||||
line-height: 1.5;
|
||||
-webkit-text-stroke: 1px rgba(0, 0, 0, 0.1);
|
||||
-moz-text-stroke: 1px rgba(0, 0, 0, 0.1);
|
||||
background: url(images/bg.jpg) 50% 0 no-repeat;
|
||||
color: #8b8b8b; }
|
||||
|
||||
* {
|
||||
outline: none; }
|
||||
|
||||
em {
|
||||
color: white; }
|
||||
|
||||
a img {
|
||||
border: none !important; }
|
||||
|
||||
a {
|
||||
font-weight: bold;
|
||||
text-decoration: none;
|
||||
color: white;
|
||||
-webkit-transition-property: opacity, -webkit-transform, color, background-color, padding, -webkit-box-shadow;
|
||||
-webkit-transition-duration: 0.15s;
|
||||
-webkit-transition-timing-function: ease-out; }
|
||||
a:hover {
|
||||
opacity: 0.8; }
|
||||
|
||||
h1, h2, h3 {
|
||||
margin: 45px 0 0 0;
|
||||
color: white;
|
||||
text-shadow: 1px 2px 2px rgba(0,0,0,0.6); }
|
||||
|
||||
h3 {
|
||||
font-size: 18px; }
|
||||
|
||||
pre {
|
||||
margin: 20px 10px;
|
||||
padding: 25px 20px;
|
||||
background: rgba(0,0,0,0.5);
|
||||
border: 1px solid #323232;
|
||||
-webkit-box-shadow: 1px 2px 2px rgba(0,0,0,0.6);
|
||||
-moz-box-shadow: 1px 2px 2px rgba(0,0,0,0.6);
|
||||
-webkit-border-radius: 5px;
|
||||
-moz-border-radius: 5px; }
|
||||
|
||||
code {
|
||||
font-family: "Helvetica Neue", "Lucida Grande", "Arial"; }
|
||||
|
||||
ul {
|
||||
margin: 15px 0;
|
||||
padding: 0 0 0 35px; }
|
||||
ul li {
|
||||
margin: 0;
|
||||
padding: 2px 0;
|
||||
list-style: square; }
|
||||
|
||||
.sect {
|
||||
margin-left: 40px; }
|
||||
|
||||
#logo {
|
||||
display: block;
|
||||
margin-left: 30%;
|
||||
margin-bottom: 30px;
|
||||
width: 194px;
|
||||
height: 51px;
|
||||
background: url(images/logo.png) 0 0 no-repeat;
|
||||
text-indent: -99999px; }
|
||||
#logo:hover {
|
||||
opacity: 0.7; }
|
||||
#logo:active {
|
||||
opacity: 0.3; }
|
||||
|
||||
#ribbon {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
right: 0;
|
||||
z-index: 2; }
|
||||
|
||||
#wrapper {
|
||||
width: 100%;
|
||||
min-height: 800px;
|
||||
background: url(images/top.png) 0 0 repeat-x; }
|
||||
|
||||
#container {
|
||||
margin: 0 auto;
|
||||
padding-top: 110px;
|
||||
width: 550px; }
|
||||
|
||||
#toc {
|
||||
position: fixed;
|
||||
top: 60px;
|
||||
left: 0;
|
||||
margin: 0 0 0 15px;
|
||||
padding: 15px;
|
||||
height: 100%;
|
||||
background: rgba(0,0,0,0.2);
|
||||
border-right: 1px solid rgba(255,255,255,0.05);
|
||||
}
|
||||
#toc li {
|
||||
padding: 1px 0;
|
||||
list-style: none;
|
||||
}
|
||||
#toc li a {
|
||||
font-size: 11px;
|
||||
}
|
||||
#menu {
|
||||
margin-left: 65px;
|
||||
padding: 0;
|
||||
padding-bottom: 30px; }
|
||||
#menu li {
|
||||
display: inline;
|
||||
list-style: none; }
|
||||
#menu li a {
|
||||
display: block;
|
||||
float: left;
|
||||
margin: 0 2px;
|
||||
padding: 3px 15px;
|
||||
background: rgba(0,0,0,0.2);
|
||||
-webkit-border-radius: 8px;
|
||||
-moz-border-radius: 8px;
|
||||
-webkit-box-shadow: 1px 2px 2px rgba(0,0,0,0.6);
|
||||
-moz-box-shadow: 1px 2px 2px rgba(0,0,0,0.6);
|
||||
-webkit-transition-property: opacity, -webkit-transform, color, background-color, -webkit-box-shadow;
|
||||
-webkit-transition-duration: 0.15s;
|
||||
-webkit-transition-timing-function: ease-out; }
|
||||
#menu li a:hover,
|
||||
#menu li a.active {
|
||||
background: rgba(0,0,0,0.5); }
|
||||
#menu li a:active {
|
||||
background: rgba(0,0,0,0.1);
|
||||
-webkit-box-shadow: 1px 1px 1px rgba(0,0,0,0.4);
|
||||
-moz-box-shadow: 1px 1px 1px rgba(0,0,0,0.4); }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<a href='http://github.com/visionmedia/express'>
|
||||
<img alt='Fork me on GitHub' id='ribbon' src='http://s3.amazonaws.com/github/ribbons/forkme_right_white_ffffff.png' />
|
||||
</a>
|
||||
<div id="header"><strong>Sencha</strong> labs</div>
|
||||
<div id="wrapper">
|
||||
<div id="container">
|
||||
<a href='http://github.com/visionmedia/express' id='logo'>Express</a>
|
||||
<p id="tagline">
|
||||
High performance, high class web development for
|
||||
<a href="http://nodejs.org">Node.js</a>
|
||||
</p>
|
||||
<ul id="menu">
|
||||
<li><a href="index.html">Home</a></li>
|
||||
<li><a href="guide.html">Guide</a></li>
|
||||
<li><a href="contrib.html">Contributing</a></li>
|
||||
<li><a href="migrate.html">1.x Migration</a></li>
|
||||
</ul>
|
||||
<div class='mp'>
|
||||
<h2 id="Synopsis">Synopsis</h2>
|
||||
|
||||
<pre><code>express [-h|--help] [-v|--version] [PATH]
|
||||
</code></pre>
|
||||
|
||||
<h2 id="Description">Description</h2>
|
||||
|
||||
<p>The <code>express</code> executable generates apps at the given <strong>PATH</strong> or the
|
||||
current working directory. Although Express is not bound to a specific
|
||||
application structure, this executable creates a maintainable base app.</p>
|
||||
|
||||
<h2 id="Options">Options</h2>
|
||||
|
||||
<pre><code>-v, --version Output framework version
|
||||
-h, --help Display help information
|
||||
</code></pre>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,16 @@
|
||||
|
||||
## Synopsis
|
||||
|
||||
express [-h|--help] [-v|--version] [PATH]
|
||||
|
||||
## Description
|
||||
|
||||
The `express` executable generates apps at the given **PATH** or the
|
||||
current working directory. Although Express is not bound to a specific
|
||||
application structure, this executable creates a maintainable base app.
|
||||
|
||||
## Options
|
||||
|
||||
-v, --version Output framework version
|
||||
-h, --help Display help information
|
||||
|
||||
+860
@@ -0,0 +1,860 @@
|
||||
.\" generated with Ronn/v0.6.6
|
||||
.\" http://github.com/rtomayko/ronn/
|
||||
.
|
||||
.TH "GUIDE" "" "July 2010" "" ""
|
||||
.
|
||||
.SS "Installation"
|
||||
npm:
|
||||
.
|
||||
.IP "" 4
|
||||
.
|
||||
.nf
|
||||
|
||||
$ npm install connect
|
||||
$ npm install express
|
||||
.
|
||||
.fi
|
||||
.
|
||||
.IP "" 0
|
||||
.
|
||||
.P
|
||||
curl (or similar):
|
||||
.
|
||||
.IP "" 4
|
||||
.
|
||||
.nf
|
||||
|
||||
$ curl \-# http://github\.com/visionmedia/express/raw/master/install\.sh | sh
|
||||
.
|
||||
.fi
|
||||
.
|
||||
.IP "" 0
|
||||
.
|
||||
.P
|
||||
git clone, first update the submodules:
|
||||
.
|
||||
.IP "" 4
|
||||
.
|
||||
.nf
|
||||
|
||||
$ git submodule update \-\-init
|
||||
$ make install
|
||||
$ make install\-support
|
||||
.
|
||||
.fi
|
||||
.
|
||||
.IP "" 0
|
||||
.
|
||||
.SS "Creating An Application"
|
||||
The \fIexpress\.Server\fR now inherits from \fIhttp\.Server\fR, however follows the same idiom by providing \fIexpress\.createServer()\fR as shown below\. This means that you can utilize Express server\'s transparently with other libraries\.
|
||||
.
|
||||
.IP "" 4
|
||||
.
|
||||
.nf
|
||||
|
||||
var app = require(\'express\')\.createServer();
|
||||
|
||||
app\.get(\'/\', function(req, res){
|
||||
res\.send(\'hello world\');
|
||||
});
|
||||
|
||||
app\.listen(3000);
|
||||
.
|
||||
.fi
|
||||
.
|
||||
.IP "" 0
|
||||
.
|
||||
.SS "Configuration"
|
||||
Express supports arbitrary environments, such as \fIproduction\fR and \fIdevelopment\fR\. Developers can use the \fIconfigure()\fR method to setup needs required by the current environment\. When \fIconfigure()\fR is called without an environment name it will be run in \fIevery\fR environment prior to the environment specific callback\.
|
||||
.
|
||||
.P
|
||||
In the example below we only \fIdumpExceptions\fR, and respond with exception stack traces in \fIdevelopment\fR mode, however for both environments we utilize \fImethodOverride\fR and \fIbodyDecoder\fR\.
|
||||
.
|
||||
.IP "" 4
|
||||
.
|
||||
.nf
|
||||
|
||||
app\.configure(function(){
|
||||
app\.use(\'/\', connect\.methodOverride());
|
||||
app\.use(\'/\', connect\.bodyDecoder());
|
||||
});
|
||||
|
||||
app\.configure(\'development\', function(){
|
||||
app\.use(\'/\', connect\.errorHandler({ dumpExceptions: true, showStack: true }));
|
||||
});
|
||||
|
||||
app\.configure(\'production\', function(){
|
||||
app\.use(\'/\', connect\.errorHandler());
|
||||
});
|
||||
.
|
||||
.fi
|
||||
.
|
||||
.IP "" 0
|
||||
.
|
||||
.P
|
||||
For internal and arbitrary settings Express provides the \fIset(key[, val])\fR, \fIenable(key)\fR, \fIdisable(key)\fR methods:
|
||||
.
|
||||
.IP "" 4
|
||||
.
|
||||
.nf
|
||||
|
||||
app\.configure(function(){
|
||||
app\.set(\'views\', __dirname + \'/views\');
|
||||
app\.set(\'views\');
|
||||
// => \"\.\.\. views directory \.\.\.\"
|
||||
|
||||
app\.enable(\'some feature\');
|
||||
// same as app\.set(\'some feature\', true);
|
||||
|
||||
app\.disable(\'some feature\');
|
||||
// same as app\.set(\'some feature\', false);
|
||||
});
|
||||
.
|
||||
.fi
|
||||
.
|
||||
.IP "" 0
|
||||
.
|
||||
.P
|
||||
To alter the environment we can set the \fICONNECT_ENV\fR environment variable, or more specifically \fIEXPRESS_ENV\fR, for example:
|
||||
.
|
||||
.IP "" 4
|
||||
.
|
||||
.nf
|
||||
|
||||
$ EXPRESS_ENV=production node app\.js
|
||||
.
|
||||
.fi
|
||||
.
|
||||
.IP "" 0
|
||||
.
|
||||
.SS "Settings"
|
||||
Express supports the following settings out of the box:
|
||||
.
|
||||
.IP "\(bu" 4
|
||||
\fIenv\fR Application environment set internally, use \fIapp\.set(\'env\')\fR to utilize
|
||||
.
|
||||
.IP "\(bu" 4
|
||||
\fIhome\fR Application base path used with \fIres\.redirect()\fR
|
||||
.
|
||||
.IP "\(bu" 4
|
||||
\fIviews\fR Root views directory defaulting to \fBCWD/views\fR
|
||||
.
|
||||
.IP "\(bu" 4
|
||||
\fIview engine\fR Default view engine name for views rendered without extensions
|
||||
.
|
||||
.IP "\(bu" 4
|
||||
\fIreload views\fR Reloads altered views, by default watches for \fImtime\fR changes with with a 5 minute interval\. Example: \fIapp\.set(\'reload views\', 60000);\fR
|
||||
.
|
||||
.IP "" 0
|
||||
.
|
||||
.SS "Routing"
|
||||
Express utilizes the HTTP verbs to provide a meaningful, expressive routing API\. For example we may want to render a user\'s account for the path \fI/user/12\fR, this can be done by defining the route below\. The values associated to the named placeholders, are passed as the \fIthird\fR argument, which here we name \fIparams\fR\.
|
||||
.
|
||||
.IP "" 4
|
||||
.
|
||||
.nf
|
||||
|
||||
app\.get(\'/user/:id\', function(req, res, params){
|
||||
res\.send(\'user \' + params\.id);
|
||||
});
|
||||
.
|
||||
.fi
|
||||
.
|
||||
.IP "" 0
|
||||
.
|
||||
.P
|
||||
A route is simple a string which is compiled to a \fIRegExp\fR internally\. For example when \fI/user/:id\fR is compiled, a simplified version of the regexp may look similar to:
|
||||
.
|
||||
.IP "" 4
|
||||
.
|
||||
.nf
|
||||
|
||||
\\/user\\/([^\\/]+)\\/?
|
||||
.
|
||||
.fi
|
||||
.
|
||||
.IP "" 0
|
||||
.
|
||||
.P
|
||||
Literal regular expressions may also be passed for complex uses:
|
||||
.
|
||||
.IP "" 4
|
||||
.
|
||||
.nf
|
||||
|
||||
app\.get(/^\\/foo(bar)?$/, function(){});
|
||||
.
|
||||
.fi
|
||||
.
|
||||
.IP "" 0
|
||||
.
|
||||
.P
|
||||
Below are some route examples, and the associated paths that they may consume:
|
||||
.
|
||||
.IP "" 4
|
||||
.
|
||||
.nf
|
||||
|
||||
\"/user/:id\"
|
||||
/user/12
|
||||
|
||||
\"/users/:id?\"
|
||||
/users/5
|
||||
/users
|
||||
|
||||
\"/files/*\"
|
||||
/files/jquery\.js
|
||||
/files/javascripts/jquery\.js
|
||||
|
||||
\"/file/*\.*\"
|
||||
/files/jquery\.js
|
||||
/files/javascripts/jquery\.js
|
||||
|
||||
\"/user/:id/:operation?\"
|
||||
/user/1
|
||||
/user/1/edit
|
||||
|
||||
\"/products\.:format\"
|
||||
/products\.json
|
||||
/products\.xml
|
||||
|
||||
\"/products\.:format?\"
|
||||
/products\.json
|
||||
/products\.xml
|
||||
/products
|
||||
.
|
||||
.fi
|
||||
.
|
||||
.IP "" 0
|
||||
.
|
||||
.SS "Passing Route Control"
|
||||
We may pass control to the next \fImatching\fR route, by calling the \fIfourth\fR parameter, the \fInext()\fR function\. When a match cannot be made, control is passed back to Connect\.
|
||||
.
|
||||
.IP "" 4
|
||||
.
|
||||
.nf
|
||||
|
||||
app\.get(\'/users/:id?\', function(req, res, params){
|
||||
if (params\.id) {
|
||||
// do something
|
||||
} else {
|
||||
next();
|
||||
}
|
||||
});
|
||||
|
||||
app\.get(\'/users\', function(req, res, params){
|
||||
// do something else
|
||||
});
|
||||
.
|
||||
.fi
|
||||
.
|
||||
.IP "" 0
|
||||
.
|
||||
.SS "Middleware"
|
||||
The Express \fIPlugin\fR is no more! middleware via Connect \fIhttp://github\.com/extjs/Connect\fR can be passed to \fIexpress\.createServer()\fR as you would with a regular Connect server\. For example:
|
||||
.
|
||||
.IP "" 4
|
||||
.
|
||||
.nf
|
||||
|
||||
var connect = require(\'connect\'),
|
||||
express = require(\'express\');
|
||||
|
||||
var app = express\.createServer(
|
||||
connect\.logger(),
|
||||
connect\.bodyDecoder()
|
||||
);
|
||||
.
|
||||
.fi
|
||||
.
|
||||
.IP "" 0
|
||||
.
|
||||
.P
|
||||
Alternatively we can \fIuse()\fR them which is useful when adding middleware within \fIconfigure()\fR blocks:
|
||||
.
|
||||
.IP "" 4
|
||||
.
|
||||
.nf
|
||||
|
||||
app\.use(\'/\', connect\.logger({ format: \':method :uri\' }));
|
||||
.
|
||||
.fi
|
||||
.
|
||||
.IP "" 0
|
||||
.
|
||||
.SS "Error Handling"
|
||||
Express provides the \fIapp\.error()\fR method which receives exceptions thrown within a route, or passed to \fInext(err)\fR\. Below is an example which serves different pages based on our ad\-hoc \fINotFound\fR exception:
|
||||
.
|
||||
.IP "" 4
|
||||
.
|
||||
.nf
|
||||
|
||||
function NotFound(msg){
|
||||
this\.name = \'NotFound\';
|
||||
Error\.call(this, msg);
|
||||
Error\.captureStackTrace(this, arguments\.callee);
|
||||
}
|
||||
|
||||
sys\.inherits(NotFound, Error);
|
||||
|
||||
app\.get(\'/404\', function(req, res){
|
||||
throw new NotFound;
|
||||
});
|
||||
|
||||
app\.get(\'/500\', function(req, res){
|
||||
throw new Error(\'keyboard cat!\');
|
||||
});
|
||||
.
|
||||
.fi
|
||||
.
|
||||
.IP "" 0
|
||||
.
|
||||
.P
|
||||
We can call \fIapp\.error()\fR several times as shown below\. Here we check for an instanceof \fINotFound\fR and show the 404 page, or we pass on to the next error handler\.
|
||||
.
|
||||
.IP "" 4
|
||||
.
|
||||
.nf
|
||||
|
||||
app\.error(function(err, req, res, next){
|
||||
if (err instanceof NotFound) {
|
||||
res\.render(\'404\.jade\');
|
||||
} else {
|
||||
next(err);
|
||||
}
|
||||
});
|
||||
.
|
||||
.fi
|
||||
.
|
||||
.IP "" 0
|
||||
.
|
||||
.P
|
||||
Here we assume all errors as 500 for the simplicity of this demo, however you can choose whatever you like
|
||||
.
|
||||
.IP "" 4
|
||||
.
|
||||
.nf
|
||||
|
||||
app\.error(function(err, req, res){
|
||||
res\.render(\'500\.jade\', {
|
||||
locals: {
|
||||
error: err
|
||||
}
|
||||
});
|
||||
});
|
||||
.
|
||||
.fi
|
||||
.
|
||||
.IP "" 0
|
||||
.
|
||||
.P
|
||||
Our apps could also utilize the Connect \fIerrorHandler\fR middleware to report on exceptions\. For example if we wish to output exceptions in \"development\" mode to \fIstderr\fR we can use:
|
||||
.
|
||||
.IP "" 4
|
||||
.
|
||||
.nf
|
||||
|
||||
app\.use(\'/\', connect\.errorHandler({ dumpExceptions: true }));
|
||||
.
|
||||
.fi
|
||||
.
|
||||
.IP "" 0
|
||||
.
|
||||
.P
|
||||
Also during development we may want fancy html pages to show exceptions that are passed or thrown, so we can set \fIshowStack\fR to true:
|
||||
.
|
||||
.IP "" 4
|
||||
.
|
||||
.nf
|
||||
|
||||
app\.use(\'/\', connect\.errorHandler({ showStack: true, dumpExceptions: true }));
|
||||
.
|
||||
.fi
|
||||
.
|
||||
.IP "" 0
|
||||
.
|
||||
.P
|
||||
The \fIerrorHandler\fR middleware also responds with \fIjson\fR if \fIAccept: application/json\fR is present, which is useful for developing apps that rely heavily on client\-side JavaScript\.
|
||||
.
|
||||
.SS "View Rendering"
|
||||
View filenames take the form \fINAME\fR\.\fIENGINE\fR, where \fIENGINE\fR is the name of the module that will be required\. For example the view \fIlayout\.ejs\fR will tell the view system to \fIrequire(\'ejs\')\fR, the module being loaded must (currently) export the method \fIexports\.render(str, options)\fR to comply with Express, however with will likely be extensible in the future\.
|
||||
.
|
||||
.P
|
||||
Below is an example using Haml\.js \fIhttp://github\.com/visionmedia/haml\.js\fR to render \fIindex\.html\fR, and since we do not use \fIlayout: false\fR the rendered contents of \fIindex\.html\fR will be passed as the \fIbody\fR local variable in \fIlayout\.haml\fR\.
|
||||
.
|
||||
.IP "" 4
|
||||
.
|
||||
.nf
|
||||
|
||||
app\.get(\'/\', function(req, res){
|
||||
res\.render(\'index\.haml\', {
|
||||
locals: { title: \'My Site\' }
|
||||
});
|
||||
});
|
||||
.
|
||||
.fi
|
||||
.
|
||||
.IP "" 0
|
||||
.
|
||||
.SS "View Partials"
|
||||
The Express view system has built\-in support for partials and collections, which are sort of \"mini\" views representing a document fragment\. For example rather than iterating in a view to display comments, we would use a partial with collection support:
|
||||
.
|
||||
.IP "" 4
|
||||
.
|
||||
.nf
|
||||
|
||||
partial(\'comment\.haml\', { collection: comments });
|
||||
.
|
||||
.fi
|
||||
.
|
||||
.IP "" 0
|
||||
.
|
||||
.P
|
||||
To make things even less verbose we can assume the extension as \fI\.haml\fR when omitted, however if we wished we could use an ejs partial, within a haml view for example\.
|
||||
.
|
||||
.IP "" 4
|
||||
.
|
||||
.nf
|
||||
|
||||
partial(\'comment\', { collection: comments });
|
||||
.
|
||||
.fi
|
||||
.
|
||||
.IP "" 0
|
||||
.
|
||||
.P
|
||||
And once again even further, when rendering a collection we can simply pass an array, if no other options are desired:
|
||||
.
|
||||
.IP "" 4
|
||||
.
|
||||
.nf
|
||||
|
||||
partial(\'comments\', comments);
|
||||
.
|
||||
.fi
|
||||
.
|
||||
.IP "" 0
|
||||
.
|
||||
.SS "Template Engines"
|
||||
Below are a few template engines commonly used with Express:
|
||||
.
|
||||
.IP "\(bu" 4
|
||||
Jade \fIhttp://github\.com/visionmedia/jade\fR haml\.js successor
|
||||
.
|
||||
.IP "\(bu" 4
|
||||
Haml \fIhttp://github\.com/visionmedia/haml\.js\fR indented templates
|
||||
.
|
||||
.IP "\(bu" 4
|
||||
EJS \fIhttp://github\.com/visionmedia/ejs\fR Embedded JavaScript
|
||||
.
|
||||
.IP "" 0
|
||||
.
|
||||
.SS "req\.header(key[, defaultValue])"
|
||||
Get the case\-insensitive request header \fIkey\fR, with optional \fIdefaultValue\fR:
|
||||
.
|
||||
.IP "" 4
|
||||
.
|
||||
.nf
|
||||
|
||||
req\.header(\'Host\');
|
||||
req\.header(\'host\');
|
||||
req\.header(\'Accept\', \'*/*\');
|
||||
.
|
||||
.fi
|
||||
.
|
||||
.IP "" 0
|
||||
.
|
||||
.SS "req\.accepts(type)"
|
||||
Check if the \fIAccept\fR header is present, and includes the given \fItype\fR\.
|
||||
.
|
||||
.P
|
||||
When the \fIAccept\fR header is not present \fItrue\fR is returned\. Otherwise the given \fItype\fR is matched by an exact match, and then subtypes\. You may pass the subtype such as \"html\" which is then converted internally to \"text/html\" using the mime lookup table\.
|
||||
.
|
||||
.IP "" 4
|
||||
.
|
||||
.nf
|
||||
|
||||
// Accept: text/html
|
||||
req\.accepts(\'html\');
|
||||
// => true
|
||||
|
||||
// Accept: text/*; application/json
|
||||
req\.accepts(\'html\');
|
||||
req\.accepts(\'text/html\');
|
||||
req\.accepts(\'text/plain\');
|
||||
req\.accepts(\'application/json\');
|
||||
// => true
|
||||
|
||||
req\.accepts(\'image/png\');
|
||||
req\.accepts(\'png\');
|
||||
// => false
|
||||
.
|
||||
.fi
|
||||
.
|
||||
.IP "" 0
|
||||
.
|
||||
.SS "req\.param(name)"
|
||||
Return the value of param \fIname\fR when present\.
|
||||
.
|
||||
.IP "\(bu" 4
|
||||
Checks route placeholders, ex: /user/:id
|
||||
.
|
||||
.IP "\(bu" 4
|
||||
Checks query string params, ex: ?id=12
|
||||
.
|
||||
.IP "\(bu" 4
|
||||
Checks urlencoded body params, ex: id=12
|
||||
.
|
||||
.IP "" 0
|
||||
.
|
||||
.P
|
||||
To utilize urlencoded request bodies, \fIreq\.body\fR should be an object\. This can be done by using the \fIconnect\.bodyDecoder\fR middleware\.
|
||||
.
|
||||
.SS "req\.flash(type[, msg])"
|
||||
Queue flash \fImsg\fR of the given \fItype\fR\.
|
||||
.
|
||||
.IP "" 4
|
||||
.
|
||||
.nf
|
||||
|
||||
req\.flash(\'info\', \'email sent\');
|
||||
req\.flash(\'error\', \'email delivery failed\');
|
||||
req\.flash(\'info\', \'email re\-sent\');
|
||||
// => 2
|
||||
|
||||
req\.flash(\'info\');
|
||||
// => [\'email sent\', \'email re\-sent\']
|
||||
|
||||
req\.flash(\'info\');
|
||||
// => []
|
||||
|
||||
req\.flash();
|
||||
// => { error: [\'email delivery failed\'], info: [] }
|
||||
.
|
||||
.fi
|
||||
.
|
||||
.IP "" 0
|
||||
.
|
||||
.SS "req\.isXMLHttpRequest"
|
||||
Also aliased as \fIreq\.xhr\fR, this getter checks the \fIX\-Requested\-With\fR header to see if it was issued by an \fIXMLHttpRequest\fR:
|
||||
.
|
||||
.IP "" 4
|
||||
.
|
||||
.nf
|
||||
|
||||
req\.xhr
|
||||
req\.isXMLHttpRequest
|
||||
.
|
||||
.fi
|
||||
.
|
||||
.IP "" 0
|
||||
.
|
||||
.SS "res\.header(key[, val])"
|
||||
Get or set the response header \fIkey\fR\.
|
||||
.
|
||||
.IP "" 4
|
||||
.
|
||||
.nf
|
||||
|
||||
res\.header(\'Content\-Length\');
|
||||
// => undefined
|
||||
|
||||
res\.header(\'Content\-Length\', 123);
|
||||
// => 123
|
||||
|
||||
res\.header(\'Content\-Length\');
|
||||
// => 123
|
||||
.
|
||||
.fi
|
||||
.
|
||||
.IP "" 0
|
||||
.
|
||||
.SS "res\.contentType(type)"
|
||||
Sets the \fIContent\-Type\fR response header to the given \fItype\fR\.
|
||||
.
|
||||
.IP "" 4
|
||||
.
|
||||
.nf
|
||||
|
||||
var filename = \'path/to/image\.png\';
|
||||
res\.contentType(filename);
|
||||
// res\.headers[\'Content\-Type\'] is now \"image/png\"
|
||||
.
|
||||
.fi
|
||||
.
|
||||
.IP "" 0
|
||||
.
|
||||
.SS "res\.attachment([filename])"
|
||||
Sets the \fIContent\-Disposition\fR response header to \"attachment\", with optional \fIfilename\fR\.
|
||||
.
|
||||
.IP "" 4
|
||||
.
|
||||
.nf
|
||||
|
||||
res\.attachment(\'path/to/my/image\.png\');
|
||||
.
|
||||
.fi
|
||||
.
|
||||
.IP "" 0
|
||||
.
|
||||
.SS "res\.sendfile(path)"
|
||||
Used by \fBres\.download()\fR to transfer an arbitrary file\.
|
||||
.
|
||||
.IP "" 4
|
||||
.
|
||||
.nf
|
||||
|
||||
res\.sendfile(\'path/to/my\.file\');
|
||||
.
|
||||
.fi
|
||||
.
|
||||
.IP "" 0
|
||||
.
|
||||
.P
|
||||
\fBNOTE\fR: this is \fInot\fR a replacement for Connect\'s \fIstaticProvider\fR middleware, nor does it perform any security checks, use with caution when using in a dynamic manor\.
|
||||
.
|
||||
.SS "res\.download(file[, filename])"
|
||||
Transfer the given \fIfile\fR as an attachment with optional alternative \fIfilename\fR\.
|
||||
.
|
||||
.IP "" 4
|
||||
.
|
||||
.nf
|
||||
|
||||
res\.download(\'path/to/image\.png\');
|
||||
res\.download(\'path/to/image\.png\', \'foo\.png\');
|
||||
.
|
||||
.fi
|
||||
.
|
||||
.IP "" 0
|
||||
.
|
||||
.P
|
||||
This is equivalent to:
|
||||
.
|
||||
.IP "" 4
|
||||
.
|
||||
.nf
|
||||
|
||||
res\.attachment(file);
|
||||
res\.sendfile(file);
|
||||
.
|
||||
.fi
|
||||
.
|
||||
.IP "" 0
|
||||
.
|
||||
.SS "res\.send(body|status[, headers|status[, status]])"
|
||||
The \fBres\.send()\fR method is a high level response utility allowing you to pass objects to respond with json, strings for html, arbitrary _Buffer_s or numbers for status code based responses\. The following are all valid uses:
|
||||
.
|
||||
.IP "" 4
|
||||
.
|
||||
.nf
|
||||
|
||||
res\.send(new Buffer(\'wahoo\'));
|
||||
res\.send({ some: \'json\' });
|
||||
res\.send(\'<p>some html</p>\');
|
||||
res\.send(\'Sorry, cant find that\', 404);
|
||||
res\.send(\'text\', { \'Content\-Type\': \'text/plain\' }, 201);
|
||||
res\.send(404);
|
||||
.
|
||||
.fi
|
||||
.
|
||||
.IP "" 0
|
||||
.
|
||||
.P
|
||||
By default the \fIContent\-Type\fR response header is set, however if explicitly assigned through \fBres\.send()\fR or previously with \fBres\.header()\fR or \fBres\.contentType()\fR it will not be set again\.
|
||||
.
|
||||
.SS "res\.redirect(url[, status])"
|
||||
Redirect to the given \fIurl\fR with a default response \fIstatus\fR of 302\.
|
||||
.
|
||||
.IP "" 4
|
||||
.
|
||||
.nf
|
||||
|
||||
res\.redirect(\'/\', 301);
|
||||
res\.redirect(\'/account\');
|
||||
res\.redirect(\'http://google\.com\');
|
||||
res\.redirect(\'home\');
|
||||
res\.redirect(\'back\');
|
||||
.
|
||||
.fi
|
||||
.
|
||||
.IP "" 0
|
||||
.
|
||||
.P
|
||||
Express supports \"redirect mapping\", which by default provides \fIhome\fR, and \fIback\fR\. The \fIback\fR map checks the \fIReferrer\fR and \fIReferer\fR headers, while \fIhome\fR utilizes the \"home\" setting and defaults to \"/\"\.
|
||||
.
|
||||
.SS "app\.set(name[, val])"
|
||||
Apply an application level setting \fIname\fR to \fIval\fR, or get the value of \fIname\fR when \fIval\fR is not present:
|
||||
.
|
||||
.IP "" 4
|
||||
.
|
||||
.nf
|
||||
|
||||
app\.set(\'reload views\', 200);
|
||||
app\.set(\'reload views\');
|
||||
// => 200
|
||||
.
|
||||
.fi
|
||||
.
|
||||
.IP "" 0
|
||||
.
|
||||
.SS "app\.enable(name)"
|
||||
Enable the given setting \fIname\fR:
|
||||
.
|
||||
.IP "" 4
|
||||
.
|
||||
.nf
|
||||
|
||||
app\.enable(\'some arbitrary setting\');
|
||||
app\.set(\'some arbitrary setting\');
|
||||
// => true
|
||||
.
|
||||
.fi
|
||||
.
|
||||
.IP "" 0
|
||||
.
|
||||
.SS "app\.disable(name)"
|
||||
Disable the given setting \fIname\fR:
|
||||
.
|
||||
.IP "" 4
|
||||
.
|
||||
.nf
|
||||
|
||||
app\.disable(\'some setting\');
|
||||
app\.set(\'some setting\');
|
||||
// => false
|
||||
.
|
||||
.fi
|
||||
.
|
||||
.IP "" 0
|
||||
.
|
||||
.SS "app\.configure(env|function[, function])"
|
||||
Define a callback function for the given \fIenv\fR (or all environments) with callback \fIfunction\fR:
|
||||
.
|
||||
.IP "" 4
|
||||
.
|
||||
.nf
|
||||
|
||||
app\.configure(function(){
|
||||
// executed for each env
|
||||
});
|
||||
|
||||
app\.configure(\'development\', function(){
|
||||
// executed for \'development\' only
|
||||
});
|
||||
.
|
||||
.fi
|
||||
.
|
||||
.IP "" 0
|
||||
.
|
||||
.SS "app\.redirect(name, val)"
|
||||
For use with \fBres\.redirect()\fR we can map redirects at the application level as shown below:
|
||||
.
|
||||
.IP "" 4
|
||||
.
|
||||
.nf
|
||||
|
||||
app\.redirect(\'google\', \'http://google\.com\');
|
||||
.
|
||||
.fi
|
||||
.
|
||||
.IP "" 0
|
||||
.
|
||||
.P
|
||||
Now in a route we may call:
|
||||
.
|
||||
.P
|
||||
res\.redirect(\'google\');
|
||||
.
|
||||
.P
|
||||
We may also map dynamic redirects:
|
||||
.
|
||||
.IP "" 4
|
||||
.
|
||||
.nf
|
||||
|
||||
app\.redirect(\'comments\', function(req, res, params){
|
||||
return \'/post/\' + params\.id + \'/comments\';
|
||||
});
|
||||
.
|
||||
.fi
|
||||
.
|
||||
.IP "" 0
|
||||
.
|
||||
.P
|
||||
So now we may do the following, and the redirect will dynamically adjust to the context of the request\. If we called this route with \fIGET /post/12\fR our redirect \fILocation\fR would be \fI/post/12/comments\fR\.
|
||||
.
|
||||
.IP "" 4
|
||||
.
|
||||
.nf
|
||||
|
||||
app\.get(\'/post/:id\', function(req, res){
|
||||
res\.redirect(\'comments\');
|
||||
});
|
||||
.
|
||||
.fi
|
||||
.
|
||||
.IP "" 0
|
||||
.
|
||||
.SS "app\.error(function)"
|
||||
Adds an error handler \fIfunction\fR which will receive the exception as the first parameter as shown below\. Note that we may set several error handlers by making several calls to this method, however the handler should call \fInext(err)\fR if it does not wish to deal with the exception:
|
||||
.
|
||||
.IP "" 4
|
||||
.
|
||||
.nf
|
||||
|
||||
app\.error(function(err, req, res, next){
|
||||
res\.send(err\.message, 500);
|
||||
});
|
||||
.
|
||||
.fi
|
||||
.
|
||||
.IP "" 0
|
||||
.
|
||||
.SS "app\.listen([port[, host]])"
|
||||
Bind the app server to the given \fIport\fR, which defaults to 3000\. When \fIhost\fR is omitted all connections will be accepted via \fIINADDR_ANY\fR\.
|
||||
.
|
||||
.IP "" 4
|
||||
.
|
||||
.nf
|
||||
|
||||
app\.listen();
|
||||
app\.listen(3000);
|
||||
app\.listen(3000, \'n\.n\.n\.n\');
|
||||
.
|
||||
.fi
|
||||
.
|
||||
.IP "" 0
|
||||
.
|
||||
.P
|
||||
The \fIport\fR argument may also be a string representing the path to a unix domain socket:
|
||||
.
|
||||
.IP "" 4
|
||||
.
|
||||
.nf
|
||||
|
||||
app\.listen(\'/tmp/express\.sock\');
|
||||
.
|
||||
.fi
|
||||
.
|
||||
.IP "" 0
|
||||
.
|
||||
.P
|
||||
Then try it out:
|
||||
.
|
||||
.IP "" 4
|
||||
.
|
||||
.nf
|
||||
|
||||
$ telnet /tmp/express\.sock
|
||||
GET / HTTP/1\.1
|
||||
|
||||
HTTP/1\.1 200 OK
|
||||
Content\-Type: text/plain
|
||||
Content\-Length: 11
|
||||
|
||||
Hello World
|
||||
.
|
||||
.fi
|
||||
.
|
||||
.IP "" 0
|
||||
|
||||
@@ -0,0 +1,787 @@
|
||||
<html>
|
||||
<head>
|
||||
<title>Express - node web framework</title>
|
||||
<style>
|
||||
#header {
|
||||
position: absolute;
|
||||
top: 10px;
|
||||
left: 0;
|
||||
padding: 12px 0;
|
||||
text-indent: 40px;
|
||||
width: 100%;
|
||||
border-top: 1px solid rgba(0,0,0,0.7);
|
||||
border-bottom: 1px solid rgba(0,0,0,0.7);
|
||||
background: rgba(255,255,255,0.1) url(http://www.sencha.com/favicon.ico) no-repeat 15px 50%;
|
||||
text-align: left;
|
||||
color: #fff;
|
||||
}
|
||||
#tagline {
|
||||
margin-left: 75px;
|
||||
margin-bottom: 30px;
|
||||
color: rgba(255,255,255,0.7); }
|
||||
html {
|
||||
background: #1c1c1c url(images/bg.tile.jpg); }
|
||||
|
||||
body {
|
||||
margin: 0;
|
||||
padding-bottom: 30px;
|
||||
font: 14px/1.4 "Helvetica Neue", "Lucida Grande", "Arial";
|
||||
font-size: 14px;
|
||||
line-height: 1.5;
|
||||
-webkit-text-stroke: 1px rgba(0, 0, 0, 0.1);
|
||||
-moz-text-stroke: 1px rgba(0, 0, 0, 0.1);
|
||||
background: url(images/bg.jpg) 50% 0 no-repeat;
|
||||
color: #8b8b8b; }
|
||||
|
||||
* {
|
||||
outline: none; }
|
||||
|
||||
em {
|
||||
color: white; }
|
||||
|
||||
a img {
|
||||
border: none !important; }
|
||||
|
||||
a {
|
||||
font-weight: bold;
|
||||
text-decoration: none;
|
||||
color: white;
|
||||
-webkit-transition-property: opacity, -webkit-transform, color, background-color, padding, -webkit-box-shadow;
|
||||
-webkit-transition-duration: 0.15s;
|
||||
-webkit-transition-timing-function: ease-out; }
|
||||
a:hover {
|
||||
opacity: 0.8; }
|
||||
|
||||
h1, h2, h3 {
|
||||
margin: 45px 0 0 0;
|
||||
color: white;
|
||||
text-shadow: 1px 2px 2px rgba(0,0,0,0.6); }
|
||||
|
||||
h3 {
|
||||
font-size: 18px; }
|
||||
|
||||
pre {
|
||||
margin: 20px 10px;
|
||||
padding: 25px 20px;
|
||||
background: rgba(0,0,0,0.5);
|
||||
border: 1px solid #323232;
|
||||
-webkit-box-shadow: 1px 2px 2px rgba(0,0,0,0.6);
|
||||
-moz-box-shadow: 1px 2px 2px rgba(0,0,0,0.6);
|
||||
-webkit-border-radius: 5px;
|
||||
-moz-border-radius: 5px; }
|
||||
|
||||
code {
|
||||
font-family: "Helvetica Neue", "Lucida Grande", "Arial"; }
|
||||
|
||||
ul {
|
||||
margin: 15px 0;
|
||||
padding: 0 0 0 35px; }
|
||||
ul li {
|
||||
margin: 0;
|
||||
padding: 2px 0;
|
||||
list-style: square; }
|
||||
|
||||
.sect {
|
||||
margin-left: 40px; }
|
||||
|
||||
#logo {
|
||||
display: block;
|
||||
margin-left: 30%;
|
||||
margin-bottom: 30px;
|
||||
width: 194px;
|
||||
height: 51px;
|
||||
background: url(images/logo.png) 0 0 no-repeat;
|
||||
text-indent: -99999px; }
|
||||
#logo:hover {
|
||||
opacity: 0.7; }
|
||||
#logo:active {
|
||||
opacity: 0.3; }
|
||||
|
||||
#ribbon {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
right: 0;
|
||||
z-index: 2; }
|
||||
|
||||
#wrapper {
|
||||
width: 100%;
|
||||
min-height: 800px;
|
||||
background: url(images/top.png) 0 0 repeat-x; }
|
||||
|
||||
#container {
|
||||
margin: 0 auto;
|
||||
padding-top: 110px;
|
||||
width: 550px; }
|
||||
|
||||
#toc {
|
||||
position: fixed;
|
||||
top: 60px;
|
||||
left: 0;
|
||||
margin: 0 0 0 15px;
|
||||
padding: 15px;
|
||||
height: 100%;
|
||||
background: rgba(0,0,0,0.2);
|
||||
border-right: 1px solid rgba(255,255,255,0.05);
|
||||
}
|
||||
#toc li {
|
||||
padding: 1px 0;
|
||||
list-style: none;
|
||||
}
|
||||
#toc li a {
|
||||
font-size: 11px;
|
||||
}
|
||||
#menu {
|
||||
margin-left: 65px;
|
||||
padding: 0;
|
||||
padding-bottom: 30px; }
|
||||
#menu li {
|
||||
display: inline;
|
||||
list-style: none; }
|
||||
#menu li a {
|
||||
display: block;
|
||||
float: left;
|
||||
margin: 0 2px;
|
||||
padding: 3px 15px;
|
||||
background: rgba(0,0,0,0.2);
|
||||
-webkit-border-radius: 8px;
|
||||
-moz-border-radius: 8px;
|
||||
-webkit-box-shadow: 1px 2px 2px rgba(0,0,0,0.6);
|
||||
-moz-box-shadow: 1px 2px 2px rgba(0,0,0,0.6);
|
||||
-webkit-transition-property: opacity, -webkit-transform, color, background-color, -webkit-box-shadow;
|
||||
-webkit-transition-duration: 0.15s;
|
||||
-webkit-transition-timing-function: ease-out; }
|
||||
#menu li a:hover,
|
||||
#menu li a.active {
|
||||
background: rgba(0,0,0,0.5); }
|
||||
#menu li a:active {
|
||||
background: rgba(0,0,0,0.1);
|
||||
-webkit-box-shadow: 1px 1px 1px rgba(0,0,0,0.4);
|
||||
-moz-box-shadow: 1px 1px 1px rgba(0,0,0,0.4); }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<a href='http://github.com/visionmedia/express'>
|
||||
<img alt='Fork me on GitHub' id='ribbon' src='http://s3.amazonaws.com/github/ribbons/forkme_right_white_ffffff.png' />
|
||||
</a>
|
||||
<div id="header"><strong>Sencha</strong> labs</div>
|
||||
<div id="wrapper">
|
||||
<div id="container"><ul id="toc">
|
||||
<li><a href="#Installation">Installation</a></li>
|
||||
<li><a href="#Creating-An-Application">Creating An Application</a></li>
|
||||
<li><a href="#Configuration">Configuration</a></li>
|
||||
<li><a href="#Settings">Settings</a></li>
|
||||
<li><a href="#Routing">Routing</a></li>
|
||||
<li><a href="#Passing-Route-Control">Passing Route Control</a></li>
|
||||
<li><a href="#Middleware">Middleware</a></li>
|
||||
<li><a href="#Error-Handling">Error Handling</a></li>
|
||||
<li><a href="#View-Rendering">View Rendering</a></li>
|
||||
<li><a href="#View-Partials">View Partials</a></li>
|
||||
<li><a href="#Template-Engines">Template Engines</a></li>
|
||||
<li><a href="#req-header-key-defaultValue-">req.header()</a></li>
|
||||
<li><a href="#req-accepts-type-">req.accepts()</a></li>
|
||||
<li><a href="#req-param-name-">req.param()</a></li>
|
||||
<li><a href="#req-flash-type-msg-">req.flash()</a></li>
|
||||
<li><a href="#req-isXMLHttpRequest">req.isXMLHttpRequest</a></li>
|
||||
<li><a href="#res-header-key-val-">res.header()</a></li>
|
||||
<li><a href="#res-contentType-type-">res.contentType()</a></li>
|
||||
<li><a href="#res-attachment-filename-">res.attachment()</a></li>
|
||||
<li><a href="#res-sendfile-path-">res.sendfile()</a></li>
|
||||
<li><a href="#res-download-file-filename-">res.download()</a></li>
|
||||
<li><a href="#res-send-body-status-headers-status-status-">res.send()</a></li>
|
||||
<li><a href="#res-redirect-url-status-">res.redirect()</a></li>
|
||||
<li><a href="#app-set-name-val-">app.set()</a></li>
|
||||
<li><a href="#app-enable-name-">app.enable()</a></li>
|
||||
<li><a href="#app-disable-name-">app.disable()</a></li>
|
||||
<li><a href="#app-configure-env-function-function-">app.configure()</a></li>
|
||||
<li><a href="#app-redirect-name-val-">app.redirect()</a></li>
|
||||
<li><a href="#app-error-function-">app.error()</a></li>
|
||||
<li><a href="#app-listen-port-host-">app.listen()</a></li>
|
||||
</ul>
|
||||
<a href='http://github.com/visionmedia/express' id='logo'>Express</a>
|
||||
<p id="tagline">
|
||||
High performance, high class web development for
|
||||
<a href="http://nodejs.org">Node.js</a>
|
||||
</p>
|
||||
<ul id="menu">
|
||||
<li><a href="index.html">Home</a></li>
|
||||
<li><a href="guide.html">Guide</a></li>
|
||||
<li><a href="contrib.html">Contributing</a></li>
|
||||
<li><a href="migrate.html">1.x Migration</a></li>
|
||||
</ul>
|
||||
<div class='mp'>
|
||||
<h3 id="Installation">Installation</h3>
|
||||
|
||||
<p>npm:</p>
|
||||
|
||||
<pre><code>$ npm install connect
|
||||
$ npm install express
|
||||
</code></pre>
|
||||
|
||||
<p>curl (or similar):</p>
|
||||
|
||||
<pre><code>$ curl -# http://github.com/visionmedia/express/raw/master/install.sh | sh
|
||||
</code></pre>
|
||||
|
||||
<p>git clone, first update the submodules:</p>
|
||||
|
||||
<pre><code>$ git submodule update --init
|
||||
$ make install
|
||||
$ make install-support
|
||||
</code></pre>
|
||||
|
||||
<h3 id="Creating-An-Application">Creating An Application</h3>
|
||||
|
||||
<p>The <em>express.Server</em> now inherits from <em>http.Server</em>, however
|
||||
follows the same idiom by providing <em>express.createServer()</em> as shown below. This means
|
||||
that you can utilize Express server's transparently with other libraries.</p>
|
||||
|
||||
<pre><code>var app = require('express').createServer();
|
||||
|
||||
app.get('/', function(req, res){
|
||||
res.send('hello world');
|
||||
});
|
||||
|
||||
app.listen(3000);
|
||||
</code></pre>
|
||||
|
||||
<h3 id="Configuration">Configuration</h3>
|
||||
|
||||
<p>Express supports arbitrary environments, such as <em>production</em> and <em>development</em>. Developers
|
||||
can use the <em>configure()</em> method to setup needs required by the current environment. When
|
||||
<em>configure()</em> is called without an environment name it will be run in <em>every</em> environment
|
||||
prior to the environment specific callback.</p>
|
||||
|
||||
<p>In the example below we only <em>dumpExceptions</em>, and respond with exception stack traces
|
||||
in <em>development</em> mode, however for both environments we utilize <em>methodOverride</em> and <em>bodyDecoder</em>.</p>
|
||||
|
||||
<pre><code>app.configure(function(){
|
||||
app.use('/', connect.methodOverride());
|
||||
app.use('/', connect.bodyDecoder());
|
||||
});
|
||||
|
||||
app.configure('development', function(){
|
||||
app.use('/', connect.errorHandler({ dumpExceptions: true, showStack: true }));
|
||||
});
|
||||
|
||||
app.configure('production', function(){
|
||||
app.use('/', connect.errorHandler());
|
||||
});
|
||||
</code></pre>
|
||||
|
||||
<p>For internal and arbitrary settings Express provides the <em>set(key[, val])</em>, <em>enable(key)</em>, <em>disable(key)</em> methods:</p>
|
||||
|
||||
<pre><code>app.configure(function(){
|
||||
app.set('views', __dirname + '/views');
|
||||
app.set('views');
|
||||
// => "... views directory ..."
|
||||
|
||||
app.enable('some feature');
|
||||
// same as app.set('some feature', true);
|
||||
|
||||
app.disable('some feature');
|
||||
// same as app.set('some feature', false);
|
||||
});
|
||||
</code></pre>
|
||||
|
||||
<p>To alter the environment we can set the <em>CONNECT_ENV</em> environment variable,
|
||||
or more specifically <em>EXPRESS_ENV</em>, for example:</p>
|
||||
|
||||
<pre><code>$ EXPRESS_ENV=production node app.js
|
||||
</code></pre>
|
||||
|
||||
<h3 id="Settings">Settings</h3>
|
||||
|
||||
<p>Express supports the following settings out of the box:</p>
|
||||
|
||||
<ul>
|
||||
<li><em>env</em> Application environment set internally, use <em>app.set('env')</em> to utilize</li>
|
||||
<li><em>home</em> Application base path used with <em>res.redirect()</em></li>
|
||||
<li><em>views</em> Root views directory defaulting to <strong>CWD/views</strong></li>
|
||||
<li><em>view engine</em> Default view engine name for views rendered without extensions</li>
|
||||
<li><em>reload views</em> Reloads altered views, by default watches for <em>mtime</em> changes with
|
||||
with a 5 minute interval. Example: <em>app.set('reload views', 60000);</em></li>
|
||||
</ul>
|
||||
|
||||
|
||||
<h3 id="Routing">Routing</h3>
|
||||
|
||||
<p>Express utilizes the HTTP verbs to provide a meaningful, expressive routing API.
|
||||
For example we may want to render a user's account for the path <em>/user/12</em>, this
|
||||
can be done by defining the route below. The values associated to the named placeholders,
|
||||
are passed as the <em>third</em> argument, which here we name <em>params</em>.</p>
|
||||
|
||||
<pre><code>app.get('/user/:id', function(req, res, params){
|
||||
res.send('user ' + params.id);
|
||||
});
|
||||
</code></pre>
|
||||
|
||||
<p>A route is simple a string which is compiled to a <em>RegExp</em> internally. For example
|
||||
when <em>/user/:id</em> is compiled, a simplified version of the regexp may look similar to:</p>
|
||||
|
||||
<pre><code>\/user\/([^\/]+)\/?
|
||||
</code></pre>
|
||||
|
||||
<p>Literal regular expressions may also be passed for complex uses:</p>
|
||||
|
||||
<pre><code>app.get(/^\/foo(bar)?$/, function(){});
|
||||
</code></pre>
|
||||
|
||||
<p>Below are some route examples, and the associated paths that they
|
||||
may consume:</p>
|
||||
|
||||
<pre><code> "/user/:id"
|
||||
/user/12
|
||||
|
||||
"/users/:id?"
|
||||
/users/5
|
||||
/users
|
||||
|
||||
"/files/*"
|
||||
/files/jquery.js
|
||||
/files/javascripts/jquery.js
|
||||
|
||||
"/file/*.*"
|
||||
/files/jquery.js
|
||||
/files/javascripts/jquery.js
|
||||
|
||||
"/user/:id/:operation?"
|
||||
/user/1
|
||||
/user/1/edit
|
||||
|
||||
"/products.:format"
|
||||
/products.json
|
||||
/products.xml
|
||||
|
||||
"/products.:format?"
|
||||
/products.json
|
||||
/products.xml
|
||||
/products
|
||||
</code></pre>
|
||||
|
||||
<h3 id="Passing-Route-Control">Passing Route Control</h3>
|
||||
|
||||
<p>We may pass control to the next <em>matching</em> route, by calling the <em>fourth</em> parameter,
|
||||
the <em>next()</em> function. When a match cannot be made, control is passed back to Connect.</p>
|
||||
|
||||
<pre><code>app.get('/users/:id?', function(req, res, params){
|
||||
if (params.id) {
|
||||
// do something
|
||||
} else {
|
||||
next();
|
||||
}
|
||||
});
|
||||
|
||||
app.get('/users', function(req, res, params){
|
||||
// do something else
|
||||
});
|
||||
</code></pre>
|
||||
|
||||
<h3 id="Middleware">Middleware</h3>
|
||||
|
||||
<p>The Express <em>Plugin</em> is no more! middleware via <a href="http://github.com/extjs/Connect">Connect</a> can be
|
||||
passed to <em>express.createServer()</em> as you would with a regular Connect server. For example:</p>
|
||||
|
||||
<pre><code>var connect = require('connect'),
|
||||
express = require('express');
|
||||
|
||||
var app = express.createServer(
|
||||
connect.logger(),
|
||||
connect.bodyDecoder()
|
||||
);
|
||||
</code></pre>
|
||||
|
||||
<p>Alternatively we can <em>use()</em> them which is useful when adding middleware within <em>configure()</em> blocks:</p>
|
||||
|
||||
<pre><code>app.use('/', connect.logger({ format: ':method :uri' }));
|
||||
</code></pre>
|
||||
|
||||
<h3 id="Error-Handling">Error Handling</h3>
|
||||
|
||||
<p>Express provides the <em>app.error()</em> method which receives exceptions thrown within a route,
|
||||
or passed to <em>next(err)</em>. Below is an example which serves different pages based on our
|
||||
ad-hoc <em>NotFound</em> exception:</p>
|
||||
|
||||
<pre><code>function NotFound(msg){
|
||||
this.name = 'NotFound';
|
||||
Error.call(this, msg);
|
||||
Error.captureStackTrace(this, arguments.callee);
|
||||
}
|
||||
|
||||
sys.inherits(NotFound, Error);
|
||||
|
||||
app.get('/404', function(req, res){
|
||||
throw new NotFound;
|
||||
});
|
||||
|
||||
app.get('/500', function(req, res){
|
||||
throw new Error('keyboard cat!');
|
||||
});
|
||||
</code></pre>
|
||||
|
||||
<p>We can call <em>app.error()</em> several times as shown below.
|
||||
Here we check for an instanceof <em>NotFound</em> and show the
|
||||
404 page, or we pass on to the next error handler.</p>
|
||||
|
||||
<pre><code>app.error(function(err, req, res, next){
|
||||
if (err instanceof NotFound) {
|
||||
res.render('404.jade');
|
||||
} else {
|
||||
next(err);
|
||||
}
|
||||
});
|
||||
</code></pre>
|
||||
|
||||
<p>Here we assume all errors as 500 for the simplicity of
|
||||
this demo, however you can choose whatever you like</p>
|
||||
|
||||
<pre><code>app.error(function(err, req, res){
|
||||
res.render('500.jade', {
|
||||
locals: {
|
||||
error: err
|
||||
}
|
||||
});
|
||||
});
|
||||
</code></pre>
|
||||
|
||||
<p>Our apps could also utilize the Connect <em>errorHandler</em> middleware
|
||||
to report on exceptions. For example if we wish to output exceptions
|
||||
in "development" mode to <em>stderr</em> we can use:</p>
|
||||
|
||||
<pre><code>app.use('/', connect.errorHandler({ dumpExceptions: true }));
|
||||
</code></pre>
|
||||
|
||||
<p>Also during development we may want fancy html pages to show exceptions
|
||||
that are passed or thrown, so we can set <em>showStack</em> to true:</p>
|
||||
|
||||
<pre><code>app.use('/', connect.errorHandler({ showStack: true, dumpExceptions: true }));
|
||||
</code></pre>
|
||||
|
||||
<p>The <em>errorHandler</em> middleware also responds with <em>json</em> if <em>Accept: application/json</em>
|
||||
is present, which is useful for developing apps that rely heavily on client-side JavaScript.</p>
|
||||
|
||||
<h3 id="View-Rendering">View Rendering</h3>
|
||||
|
||||
<p>View filenames take the form <em>Express</em>.<em>ENGINE</em>, where <em>ENGINE</em> is the name
|
||||
of the module that will be required. For example the view <em>layout.ejs</em> will
|
||||
tell the view system to <em>require('ejs')</em>, the module being loaded must (currently)
|
||||
export the method <em>exports.render(str, options)</em> to comply with Express, however
|
||||
with will likely be extensible in the future.</p>
|
||||
|
||||
<p>Below is an example using <a href="http://github.com/visionmedia/haml.js">Haml.js</a> to render <em>index.html</em>,
|
||||
and since we do not use <em>layout: false</em> the rendered contents of <em>index.html</em> will be passed as
|
||||
the <em>body</em> local variable in <em>layout.haml</em>.</p>
|
||||
|
||||
<pre><code>app.get('/', function(req, res){
|
||||
res.render('index.haml', {
|
||||
locals: { title: 'My Site' }
|
||||
});
|
||||
});
|
||||
</code></pre>
|
||||
|
||||
<h3 id="View-Partials">View Partials</h3>
|
||||
|
||||
<p>The Express view system has built-in support for partials and collections, which are
|
||||
sort of "mini" views representing a document fragment. For example rather than iterating
|
||||
in a view to display comments, we would use a partial with collection support:</p>
|
||||
|
||||
<pre><code>partial('comment.haml', { collection: comments });
|
||||
</code></pre>
|
||||
|
||||
<p>To make things even less verbose we can assume the extension as <em>.haml</em> when omitted,
|
||||
however if we wished we could use an ejs partial, within a haml view for example.</p>
|
||||
|
||||
<pre><code>partial('comment', { collection: comments });
|
||||
</code></pre>
|
||||
|
||||
<p>And once again even further, when rendering a collection we can simply pass
|
||||
an array, if no other options are desired:</p>
|
||||
|
||||
<pre><code>partial('comments', comments);
|
||||
</code></pre>
|
||||
|
||||
<h3 id="Template-Engines">Template Engines</h3>
|
||||
|
||||
<p>Below are a few template engines commonly used with Express:</p>
|
||||
|
||||
<ul>
|
||||
<li><a href="http://github.com/visionmedia/jade">Jade</a> haml.js successor</li>
|
||||
<li><a href="http://github.com/visionmedia/haml.js">Haml</a> indented templates</li>
|
||||
<li><a href="http://github.com/visionmedia/ejs">EJS</a> Embedded JavaScript</li>
|
||||
</ul>
|
||||
|
||||
|
||||
<h3 id="req-header-key-defaultValue-">req.header(key[, defaultValue])</h3>
|
||||
|
||||
<p>Get the case-insensitive request header <em>key</em>, with optional <em>defaultValue</em>:</p>
|
||||
|
||||
<pre><code>req.header('Host');
|
||||
req.header('host');
|
||||
req.header('Accept', '*/*');
|
||||
</code></pre>
|
||||
|
||||
<h3 id="req-accepts-type-">req.accepts(type)</h3>
|
||||
|
||||
<p>Check if the <em>Accept</em> header is present, and includes the given <em>type</em>.</p>
|
||||
|
||||
<p>When the <em>Accept</em> header is not present <em>true</em> is returned. Otherwise
|
||||
the given <em>type</em> is matched by an exact match, and then subtypes. You
|
||||
may pass the subtype such as "html" which is then converted internally
|
||||
to "text/html" using the mime lookup table.</p>
|
||||
|
||||
<pre><code>// Accept: text/html
|
||||
req.accepts('html');
|
||||
// => true
|
||||
|
||||
// Accept: text/*; application/json
|
||||
req.accepts('html');
|
||||
req.accepts('text/html');
|
||||
req.accepts('text/plain');
|
||||
req.accepts('application/json');
|
||||
// => true
|
||||
|
||||
req.accepts('image/png');
|
||||
req.accepts('png');
|
||||
// => false
|
||||
</code></pre>
|
||||
|
||||
<h3 id="req-param-name-">req.param(name)</h3>
|
||||
|
||||
<p>Return the value of param <em>name</em> when present.</p>
|
||||
|
||||
<ul>
|
||||
<li>Checks route placeholders, ex: /user/:id</li>
|
||||
<li>Checks query string params, ex: ?id=12</li>
|
||||
<li>Checks urlencoded body params, ex: id=12</li>
|
||||
</ul>
|
||||
|
||||
|
||||
<p>To utilize urlencoded request bodies, <em>req.body</em>
|
||||
should be an object. This can be done by using
|
||||
the <em>connect.bodyDecoder</em> middleware.</p>
|
||||
|
||||
<h3 id="req-flash-type-msg-">req.flash(type[, msg])</h3>
|
||||
|
||||
<p>Queue flash <em>msg</em> of the given <em>type</em>.</p>
|
||||
|
||||
<pre><code>req.flash('info', 'email sent');
|
||||
req.flash('error', 'email delivery failed');
|
||||
req.flash('info', 'email re-sent');
|
||||
// => 2
|
||||
|
||||
req.flash('info');
|
||||
// => ['email sent', 'email re-sent']
|
||||
|
||||
req.flash('info');
|
||||
// => []
|
||||
|
||||
req.flash();
|
||||
// => { error: ['email delivery failed'], info: [] }
|
||||
</code></pre>
|
||||
|
||||
<h3 id="req-isXMLHttpRequest">req.isXMLHttpRequest</h3>
|
||||
|
||||
<p>Also aliased as <em>req.xhr</em>, this getter checks the <em>X-Requested-With</em> header
|
||||
to see if it was issued by an <em>XMLHttpRequest</em>:</p>
|
||||
|
||||
<pre><code>req.xhr
|
||||
req.isXMLHttpRequest
|
||||
</code></pre>
|
||||
|
||||
<h3 id="res-header-key-val-">res.header(key[, val])</h3>
|
||||
|
||||
<p>Get or set the response header <em>key</em>.</p>
|
||||
|
||||
<pre><code>res.header('Content-Length');
|
||||
// => undefined
|
||||
|
||||
res.header('Content-Length', 123);
|
||||
// => 123
|
||||
|
||||
res.header('Content-Length');
|
||||
// => 123
|
||||
</code></pre>
|
||||
|
||||
<h3 id="res-contentType-type-">res.contentType(type)</h3>
|
||||
|
||||
<p>Sets the <em>Content-Type</em> response header to the given <em>type</em>.</p>
|
||||
|
||||
<pre><code> var filename = 'path/to/image.png';
|
||||
res.contentType(filename);
|
||||
// res.headers['Content-Type'] is now "image/png"
|
||||
</code></pre>
|
||||
|
||||
<h3 id="res-attachment-filename-">res.attachment([filename])</h3>
|
||||
|
||||
<p>Sets the <em>Content-Disposition</em> response header to "attachment", with optional <em>filename</em>.</p>
|
||||
|
||||
<pre><code> res.attachment('path/to/my/image.png');
|
||||
</code></pre>
|
||||
|
||||
<h3 id="res-sendfile-path-">res.sendfile(path)</h3>
|
||||
|
||||
<p>Used by <code>res.download()</code> to transfer an arbitrary file.</p>
|
||||
|
||||
<pre><code>res.sendfile('path/to/my.file');
|
||||
</code></pre>
|
||||
|
||||
<p><strong>NOTE</strong>: this is <em>not</em> a replacement for Connect's <em>staticProvider</em> middleware,
|
||||
nor does it perform any security checks, use with caution when using in a dynamic manor.</p>
|
||||
|
||||
<h3 id="res-download-file-filename-">res.download(file[, filename])</h3>
|
||||
|
||||
<p>Transfer the given <em>file</em> as an attachment with optional alternative <em>filename</em>.</p>
|
||||
|
||||
<pre><code>res.download('path/to/image.png');
|
||||
res.download('path/to/image.png', 'foo.png');
|
||||
</code></pre>
|
||||
|
||||
<p>This is equivalent to:</p>
|
||||
|
||||
<pre><code>res.attachment(file);
|
||||
res.sendfile(file);
|
||||
</code></pre>
|
||||
|
||||
<h3 id="res-send-body-status-headers-status-status-">res.send(body|status[, headers|status[, status]])</h3>
|
||||
|
||||
<p>The <code>res.send()</code> method is a high level response utility allowing you to pass
|
||||
objects to respond with json, strings for html, arbitrary _Buffer_s or numbers for status
|
||||
code based responses. The following are all valid uses:</p>
|
||||
|
||||
<pre><code> res.send(new Buffer('wahoo'));
|
||||
res.send({ some: 'json' });
|
||||
res.send('<p>some html</p>');
|
||||
res.send('Sorry, cant find that', 404);
|
||||
res.send('text', { 'Content-Type': 'text/plain' }, 201);
|
||||
res.send(404);
|
||||
</code></pre>
|
||||
|
||||
<p>By default the <em>Content-Type</em> response header is set, however if explicitly
|
||||
assigned through <code>res.send()</code> or previously with <code>res.header()</code> or <code>res.contentType()</code>
|
||||
it will not be set again.</p>
|
||||
|
||||
<h3 id="res-redirect-url-status-">res.redirect(url[, status])</h3>
|
||||
|
||||
<p>Redirect to the given <em>url</em> with a default response <em>status</em> of 302.</p>
|
||||
|
||||
<pre><code>res.redirect('/', 301);
|
||||
res.redirect('/account');
|
||||
res.redirect('http://google.com');
|
||||
res.redirect('home');
|
||||
res.redirect('back');
|
||||
</code></pre>
|
||||
|
||||
<p>Express supports "redirect mapping", which by default provides <em>home</em>, and <em>back</em>.
|
||||
The <em>back</em> map checks the <em>Referrer</em> and <em>Referer</em> headers, while <em>home</em> utilizes
|
||||
the "home" setting and defaults to "/".</p>
|
||||
|
||||
<h3 id="app-set-name-val-">app.set(name[, val])</h3>
|
||||
|
||||
<p>Apply an application level setting <em>name</em> to <em>val</em>, or
|
||||
get the value of <em>name</em> when <em>val</em> is not present:</p>
|
||||
|
||||
<pre><code>app.set('reload views', 200);
|
||||
app.set('reload views');
|
||||
// => 200
|
||||
</code></pre>
|
||||
|
||||
<h3 id="app-enable-name-">app.enable(name)</h3>
|
||||
|
||||
<p>Enable the given setting <em>name</em>:</p>
|
||||
|
||||
<pre><code>app.enable('some arbitrary setting');
|
||||
app.set('some arbitrary setting');
|
||||
// => true
|
||||
</code></pre>
|
||||
|
||||
<h3 id="app-disable-name-">app.disable(name)</h3>
|
||||
|
||||
<p>Disable the given setting <em>name</em>:</p>
|
||||
|
||||
<pre><code>app.disable('some setting');
|
||||
app.set('some setting');
|
||||
// => false
|
||||
</code></pre>
|
||||
|
||||
<h3 id="app-configure-env-function-function-">app.configure(env|function[, function])</h3>
|
||||
|
||||
<p>Define a callback function for the given <em>env</em> (or all environments) with callback <em>function</em>:</p>
|
||||
|
||||
<pre><code>app.configure(function(){
|
||||
// executed for each env
|
||||
});
|
||||
|
||||
app.configure('development', function(){
|
||||
// executed for 'development' only
|
||||
});
|
||||
</code></pre>
|
||||
|
||||
<h3 id="app-redirect-name-val-">app.redirect(name, val)</h3>
|
||||
|
||||
<p>For use with <code>res.redirect()</code> we can map redirects at the application level as shown below:</p>
|
||||
|
||||
<pre><code>app.redirect('google', 'http://google.com');
|
||||
</code></pre>
|
||||
|
||||
<p>Now in a route we may call:</p>
|
||||
|
||||
<p> res.redirect('google');</p>
|
||||
|
||||
<p>We may also map dynamic redirects:</p>
|
||||
|
||||
<pre><code>app.redirect('comments', function(req, res, params){
|
||||
return '/post/' + params.id + '/comments';
|
||||
});
|
||||
</code></pre>
|
||||
|
||||
<p>So now we may do the following, and the redirect will dynamically adjust to
|
||||
the context of the request. If we called this route with <em>GET /post/12</em> our
|
||||
redirect <em>Location</em> would be <em>/post/12/comments</em>.</p>
|
||||
|
||||
<pre><code>app.get('/post/:id', function(req, res){
|
||||
res.redirect('comments');
|
||||
});
|
||||
</code></pre>
|
||||
|
||||
<h3 id="app-error-function-">app.error(function)</h3>
|
||||
|
||||
<p>Adds an error handler <em>function</em> which will receive the exception as the first parameter as shown below.
|
||||
Note that we may set several error handlers by making several calls to this method, however the handler
|
||||
should call <em>next(err)</em> if it does not wish to deal with the exception:</p>
|
||||
|
||||
<pre><code>app.error(function(err, req, res, next){
|
||||
res.send(err.message, 500);
|
||||
});
|
||||
</code></pre>
|
||||
|
||||
<h3 id="app-listen-port-host-">app.listen([port[, host]])</h3>
|
||||
|
||||
<p>Bind the app server to the given <em>port</em>, which defaults to 3000. When <em>host</em> is omitted all
|
||||
connections will be accepted via <em>INADDR_ANY</em>.</p>
|
||||
|
||||
<pre><code>app.listen();
|
||||
app.listen(3000);
|
||||
app.listen(3000, 'n.n.n.n');
|
||||
</code></pre>
|
||||
|
||||
<p>The <em>port</em> argument may also be a string representing the path to a unix domain socket:</p>
|
||||
|
||||
<pre><code>app.listen('/tmp/express.sock');
|
||||
</code></pre>
|
||||
|
||||
<p>Then try it out:</p>
|
||||
|
||||
<pre><code>$ telnet /tmp/express.sock
|
||||
GET / HTTP/1.1
|
||||
|
||||
HTTP/1.1 200 OK
|
||||
Content-Type: text/plain
|
||||
Content-Length: 11
|
||||
|
||||
Hello World
|
||||
</code></pre>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
+516
@@ -0,0 +1,516 @@
|
||||
|
||||
### Installation
|
||||
|
||||
npm:
|
||||
|
||||
$ npm install connect
|
||||
$ npm install express
|
||||
|
||||
curl (or similar):
|
||||
|
||||
$ curl -# http://github.com/visionmedia/express/raw/master/install.sh | sh
|
||||
|
||||
git clone, first update the submodules:
|
||||
|
||||
$ git submodule update --init
|
||||
$ make install
|
||||
$ make install-support
|
||||
|
||||
### Creating An Application
|
||||
|
||||
The _express.Server_ now inherits from _http.Server_, however
|
||||
follows the same idiom by providing _express.createServer()_ as shown below. This means
|
||||
that you can utilize Express server's transparently with other libraries.
|
||||
|
||||
var app = require('express').createServer();
|
||||
|
||||
app.get('/', function(req, res){
|
||||
res.send('hello world');
|
||||
});
|
||||
|
||||
app.listen(3000);
|
||||
|
||||
### Configuration
|
||||
|
||||
Express supports arbitrary environments, such as _production_ and _development_. Developers
|
||||
can use the _configure()_ method to setup needs required by the current environment. When
|
||||
_configure()_ is called without an environment name it will be run in _every_ environment
|
||||
prior to the environment specific callback.
|
||||
|
||||
In the example below we only _dumpExceptions_, and respond with exception stack traces
|
||||
in _development_ mode, however for both environments we utilize _methodOverride_ and _bodyDecoder_.
|
||||
|
||||
app.configure(function(){
|
||||
app.use('/', connect.methodOverride());
|
||||
app.use('/', connect.bodyDecoder());
|
||||
});
|
||||
|
||||
app.configure('development', function(){
|
||||
app.use('/', connect.errorHandler({ dumpExceptions: true, showStack: true }));
|
||||
});
|
||||
|
||||
app.configure('production', function(){
|
||||
app.use('/', connect.errorHandler());
|
||||
});
|
||||
|
||||
For internal and arbitrary settings Express provides the _set(key[, val])_, _enable(key)_, _disable(key)_ methods:
|
||||
|
||||
app.configure(function(){
|
||||
app.set('views', __dirname + '/views');
|
||||
app.set('views');
|
||||
// => "... views directory ..."
|
||||
|
||||
app.enable('some feature');
|
||||
// same as app.set('some feature', true);
|
||||
|
||||
app.disable('some feature');
|
||||
// same as app.set('some feature', false);
|
||||
});
|
||||
|
||||
To alter the environment we can set the _CONNECT_ENV_ environment variable,
|
||||
or more specifically _EXPRESS_ENV_, for example:
|
||||
|
||||
$ EXPRESS_ENV=production node app.js
|
||||
|
||||
### Settings
|
||||
|
||||
Express supports the following settings out of the box:
|
||||
|
||||
* _env_ Application environment set internally, use _app.set('env')_ to utilize
|
||||
* _home_ Application base path used with _res.redirect()_
|
||||
* _views_ Root views directory defaulting to **CWD/views**
|
||||
* _view engine_ Default view engine name for views rendered without extensions
|
||||
* _reload views_ Reloads altered views, by default watches for _mtime_ changes with
|
||||
with a 5 minute interval. Example: _app.set('reload views', 60000);_
|
||||
|
||||
### Routing
|
||||
|
||||
Express utilizes the HTTP verbs to provide a meaningful, expressive routing API.
|
||||
For example we may want to render a user's account for the path _/user/12_, this
|
||||
can be done by defining the route below. The values associated to the named placeholders,
|
||||
are passed as the _third_ argument, which here we name _params_.
|
||||
|
||||
app.get('/user/:id', function(req, res, params){
|
||||
res.send('user ' + params.id);
|
||||
});
|
||||
|
||||
A route is simple a string which is compiled to a _RegExp_ internally. For example
|
||||
when _/user/:id_ is compiled, a simplified version of the regexp may look similar to:
|
||||
|
||||
\/user\/([^\/]+)\/?
|
||||
|
||||
Literal regular expressions may also be passed for complex uses:
|
||||
|
||||
app.get(/^\/foo(bar)?$/, function(){});
|
||||
|
||||
Below are some route examples, and the associated paths that they
|
||||
may consume:
|
||||
|
||||
"/user/:id"
|
||||
/user/12
|
||||
|
||||
"/users/:id?"
|
||||
/users/5
|
||||
/users
|
||||
|
||||
"/files/*"
|
||||
/files/jquery.js
|
||||
/files/javascripts/jquery.js
|
||||
|
||||
"/file/*.*"
|
||||
/files/jquery.js
|
||||
/files/javascripts/jquery.js
|
||||
|
||||
"/user/:id/:operation?"
|
||||
/user/1
|
||||
/user/1/edit
|
||||
|
||||
"/products.:format"
|
||||
/products.json
|
||||
/products.xml
|
||||
|
||||
"/products.:format?"
|
||||
/products.json
|
||||
/products.xml
|
||||
/products
|
||||
|
||||
### Passing Route Control
|
||||
|
||||
We may pass control to the next _matching_ route, by calling the _fourth_ parameter,
|
||||
the _next()_ function. When a match cannot be made, control is passed back to Connect.
|
||||
|
||||
app.get('/users/:id?', function(req, res, params){
|
||||
if (params.id) {
|
||||
// do something
|
||||
} else {
|
||||
next();
|
||||
}
|
||||
});
|
||||
|
||||
app.get('/users', function(req, res, params){
|
||||
// do something else
|
||||
});
|
||||
|
||||
### Middleware
|
||||
|
||||
The Express _Plugin_ is no more! middleware via [Connect](http://github.com/extjs/Connect) can be
|
||||
passed to _express.createServer()_ as you would with a regular Connect server. For example:
|
||||
|
||||
var connect = require('connect'),
|
||||
express = require('express');
|
||||
|
||||
var app = express.createServer(
|
||||
connect.logger(),
|
||||
connect.bodyDecoder()
|
||||
);
|
||||
|
||||
Alternatively we can _use()_ them which is useful when adding middleware within _configure()_ blocks:
|
||||
|
||||
app.use('/', connect.logger({ format: ':method :uri' }));
|
||||
|
||||
### Error Handling
|
||||
|
||||
Express provides the _app.error()_ method which receives exceptions thrown within a route,
|
||||
or passed to _next(err)_. Below is an example which serves different pages based on our
|
||||
ad-hoc _NotFound_ exception:
|
||||
|
||||
function NotFound(msg){
|
||||
this.name = 'NotFound';
|
||||
Error.call(this, msg);
|
||||
Error.captureStackTrace(this, arguments.callee);
|
||||
}
|
||||
|
||||
sys.inherits(NotFound, Error);
|
||||
|
||||
app.get('/404', function(req, res){
|
||||
throw new NotFound;
|
||||
});
|
||||
|
||||
app.get('/500', function(req, res){
|
||||
throw new Error('keyboard cat!');
|
||||
});
|
||||
|
||||
We can call _app.error()_ several times as shown below.
|
||||
Here we check for an instanceof _NotFound_ and show the
|
||||
404 page, or we pass on to the next error handler.
|
||||
|
||||
app.error(function(err, req, res, next){
|
||||
if (err instanceof NotFound) {
|
||||
res.render('404.jade');
|
||||
} else {
|
||||
next(err);
|
||||
}
|
||||
});
|
||||
|
||||
Here we assume all errors as 500 for the simplicity of
|
||||
this demo, however you can choose whatever you like
|
||||
|
||||
app.error(function(err, req, res){
|
||||
res.render('500.jade', {
|
||||
locals: {
|
||||
error: err
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
Our apps could also utilize the Connect _errorHandler_ middleware
|
||||
to report on exceptions. For example if we wish to output exceptions
|
||||
in "development" mode to _stderr_ we can use:
|
||||
|
||||
app.use('/', connect.errorHandler({ dumpExceptions: true }));
|
||||
|
||||
Also during development we may want fancy html pages to show exceptions
|
||||
that are passed or thrown, so we can set _showStack_ to true:
|
||||
|
||||
app.use('/', connect.errorHandler({ showStack: true, dumpExceptions: true }));
|
||||
|
||||
The _errorHandler_ middleware also responds with _json_ if _Accept: application/json_
|
||||
is present, which is useful for developing apps that rely heavily on client-side JavaScript.
|
||||
|
||||
### View Rendering
|
||||
|
||||
View filenames take the form _NAME_._ENGINE_, where _ENGINE_ is the name
|
||||
of the module that will be required. For example the view _layout.ejs_ will
|
||||
tell the view system to _require('ejs')_, the module being loaded must (currently)
|
||||
export the method _exports.render(str, options)_ to comply with Express, however
|
||||
with will likely be extensible in the future.
|
||||
|
||||
Below is an example using [Haml.js](http://github.com/visionmedia/haml.js) to render _index.html_,
|
||||
and since we do not use _layout: false_ the rendered contents of _index.html_ will be passed as
|
||||
the _body_ local variable in _layout.haml_.
|
||||
|
||||
app.get('/', function(req, res){
|
||||
res.render('index.haml', {
|
||||
locals: { title: 'My Site' }
|
||||
});
|
||||
});
|
||||
|
||||
### View Partials
|
||||
|
||||
The Express view system has built-in support for partials and collections, which are
|
||||
sort of "mini" views representing a document fragment. For example rather than iterating
|
||||
in a view to display comments, we would use a partial with collection support:
|
||||
|
||||
partial('comment.haml', { collection: comments });
|
||||
|
||||
To make things even less verbose we can assume the extension as _.haml_ when omitted,
|
||||
however if we wished we could use an ejs partial, within a haml view for example.
|
||||
|
||||
partial('comment', { collection: comments });
|
||||
|
||||
And once again even further, when rendering a collection we can simply pass
|
||||
an array, if no other options are desired:
|
||||
|
||||
partial('comments', comments);
|
||||
|
||||
### Template Engines
|
||||
|
||||
Below are a few template engines commonly used with Express:
|
||||
|
||||
* [Jade](http://github.com/visionmedia/jade) haml.js successor
|
||||
* [Haml](http://github.com/visionmedia/haml.js) indented templates
|
||||
* [EJS](http://github.com/visionmedia/ejs) Embedded JavaScript
|
||||
|
||||
### req.header(key[, defaultValue])
|
||||
|
||||
Get the case-insensitive request header _key_, with optional _defaultValue_:
|
||||
|
||||
req.header('Host');
|
||||
req.header('host');
|
||||
req.header('Accept', '*/*');
|
||||
|
||||
### req.accepts(type)
|
||||
|
||||
Check if the _Accept_ header is present, and includes the given _type_.
|
||||
|
||||
When the _Accept_ header is not present _true_ is returned. Otherwise
|
||||
the given _type_ is matched by an exact match, and then subtypes. You
|
||||
may pass the subtype such as "html" which is then converted internally
|
||||
to "text/html" using the mime lookup table.
|
||||
|
||||
// Accept: text/html
|
||||
req.accepts('html');
|
||||
// => true
|
||||
|
||||
// Accept: text/*; application/json
|
||||
req.accepts('html');
|
||||
req.accepts('text/html');
|
||||
req.accepts('text/plain');
|
||||
req.accepts('application/json');
|
||||
// => true
|
||||
|
||||
req.accepts('image/png');
|
||||
req.accepts('png');
|
||||
// => false
|
||||
|
||||
### req.param(name)
|
||||
|
||||
Return the value of param _name_ when present.
|
||||
|
||||
- Checks route placeholders, ex: /user/:id
|
||||
- Checks query string params, ex: ?id=12
|
||||
- Checks urlencoded body params, ex: id=12
|
||||
|
||||
To utilize urlencoded request bodies, _req.body_
|
||||
should be an object. This can be done by using
|
||||
the _connect.bodyDecoder_ middleware.
|
||||
|
||||
### req.flash(type[, msg])
|
||||
|
||||
Queue flash _msg_ of the given _type_.
|
||||
|
||||
req.flash('info', 'email sent');
|
||||
req.flash('error', 'email delivery failed');
|
||||
req.flash('info', 'email re-sent');
|
||||
// => 2
|
||||
|
||||
req.flash('info');
|
||||
// => ['email sent', 'email re-sent']
|
||||
|
||||
req.flash('info');
|
||||
// => []
|
||||
|
||||
req.flash();
|
||||
// => { error: ['email delivery failed'], info: [] }
|
||||
|
||||
### req.isXMLHttpRequest
|
||||
|
||||
Also aliased as _req.xhr_, this getter checks the _X-Requested-With_ header
|
||||
to see if it was issued by an _XMLHttpRequest_:
|
||||
|
||||
req.xhr
|
||||
req.isXMLHttpRequest
|
||||
|
||||
### res.header(key[, val])
|
||||
|
||||
Get or set the response header _key_.
|
||||
|
||||
res.header('Content-Length');
|
||||
// => undefined
|
||||
|
||||
res.header('Content-Length', 123);
|
||||
// => 123
|
||||
|
||||
res.header('Content-Length');
|
||||
// => 123
|
||||
|
||||
### res.contentType(type)
|
||||
|
||||
Sets the _Content-Type_ response header to the given _type_.
|
||||
|
||||
var filename = 'path/to/image.png';
|
||||
res.contentType(filename);
|
||||
// res.headers['Content-Type'] is now "image/png"
|
||||
|
||||
### res.attachment([filename])
|
||||
|
||||
Sets the _Content-Disposition_ response header to "attachment", with optional _filename_.
|
||||
|
||||
res.attachment('path/to/my/image.png');
|
||||
|
||||
### res.sendfile(path)
|
||||
|
||||
Used by `res.download()` to transfer an arbitrary file.
|
||||
|
||||
res.sendfile('path/to/my.file');
|
||||
|
||||
**NOTE**: this is _not_ a replacement for Connect's _staticProvider_ middleware,
|
||||
nor does it perform any security checks, use with caution when using in a dynamic manor.
|
||||
|
||||
### res.download(file[, filename])
|
||||
|
||||
Transfer the given _file_ as an attachment with optional alternative _filename_.
|
||||
|
||||
res.download('path/to/image.png');
|
||||
res.download('path/to/image.png', 'foo.png');
|
||||
|
||||
This is equivalent to:
|
||||
|
||||
res.attachment(file);
|
||||
res.sendfile(file);
|
||||
|
||||
### res.send(body|status[, headers|status[, status]])
|
||||
|
||||
The `res.send()` method is a high level response utility allowing you to pass
|
||||
objects to respond with json, strings for html, arbitrary _Buffer_s or numbers for status
|
||||
code based responses. The following are all valid uses:
|
||||
|
||||
res.send(new Buffer('wahoo'));
|
||||
res.send({ some: 'json' });
|
||||
res.send('<p>some html</p>');
|
||||
res.send('Sorry, cant find that', 404);
|
||||
res.send('text', { 'Content-Type': 'text/plain' }, 201);
|
||||
res.send(404);
|
||||
|
||||
By default the _Content-Type_ response header is set, however if explicitly
|
||||
assigned through `res.send()` or previously with `res.header()` or `res.contentType()`
|
||||
it will not be set again.
|
||||
|
||||
### res.redirect(url[, status])
|
||||
|
||||
Redirect to the given _url_ with a default response _status_ of 302.
|
||||
|
||||
res.redirect('/', 301);
|
||||
res.redirect('/account');
|
||||
res.redirect('http://google.com');
|
||||
res.redirect('home');
|
||||
res.redirect('back');
|
||||
|
||||
Express supports "redirect mapping", which by default provides _home_, and _back_.
|
||||
The _back_ map checks the _Referrer_ and _Referer_ headers, while _home_ utilizes
|
||||
the "home" setting and defaults to "/".
|
||||
|
||||
### app.set(name[, val])
|
||||
|
||||
Apply an application level setting _name_ to _val_, or
|
||||
get the value of _name_ when _val_ is not present:
|
||||
|
||||
app.set('reload views', 200);
|
||||
app.set('reload views');
|
||||
// => 200
|
||||
|
||||
### app.enable(name)
|
||||
|
||||
Enable the given setting _name_:
|
||||
|
||||
app.enable('some arbitrary setting');
|
||||
app.set('some arbitrary setting');
|
||||
// => true
|
||||
|
||||
### app.disable(name)
|
||||
|
||||
Disable the given setting _name_:
|
||||
|
||||
app.disable('some setting');
|
||||
app.set('some setting');
|
||||
// => false
|
||||
|
||||
### app.configure(env|function[, function])
|
||||
|
||||
Define a callback function for the given _env_ (or all environments) with callback _function_:
|
||||
|
||||
app.configure(function(){
|
||||
// executed for each env
|
||||
});
|
||||
|
||||
app.configure('development', function(){
|
||||
// executed for 'development' only
|
||||
});
|
||||
|
||||
### app.redirect(name, val)
|
||||
|
||||
For use with `res.redirect()` we can map redirects at the application level as shown below:
|
||||
|
||||
app.redirect('google', 'http://google.com');
|
||||
|
||||
Now in a route we may call:
|
||||
|
||||
res.redirect('google');
|
||||
|
||||
We may also map dynamic redirects:
|
||||
|
||||
app.redirect('comments', function(req, res, params){
|
||||
return '/post/' + params.id + '/comments';
|
||||
});
|
||||
|
||||
So now we may do the following, and the redirect will dynamically adjust to
|
||||
the context of the request. If we called this route with _GET /post/12_ our
|
||||
redirect _Location_ would be _/post/12/comments_.
|
||||
|
||||
app.get('/post/:id', function(req, res){
|
||||
res.redirect('comments');
|
||||
});
|
||||
|
||||
### app.error(function)
|
||||
|
||||
Adds an error handler _function_ which will receive the exception as the first parameter as shown below.
|
||||
Note that we may set several error handlers by making several calls to this method, however the handler
|
||||
should call _next(err)_ if it does not wish to deal with the exception:
|
||||
|
||||
app.error(function(err, req, res, next){
|
||||
res.send(err.message, 500);
|
||||
});
|
||||
|
||||
### app.listen([port[, host]])
|
||||
|
||||
Bind the app server to the given _port_, which defaults to 3000. When _host_ is omitted all
|
||||
connections will be accepted via _INADDR_ANY_.
|
||||
|
||||
app.listen();
|
||||
app.listen(3000);
|
||||
app.listen(3000, 'n.n.n.n');
|
||||
|
||||
The _port_ argument may also be a string representing the path to a unix domain socket:
|
||||
|
||||
app.listen('/tmp/express.sock');
|
||||
|
||||
Then try it out:
|
||||
|
||||
$ telnet /tmp/express.sock
|
||||
GET / HTTP/1.1
|
||||
|
||||
HTTP/1.1 200 OK
|
||||
Content-Type: text/plain
|
||||
Content-Length: 11
|
||||
|
||||
Hello World
|
||||
Arquivo binário não exibido.
|
Depois Largura: | Altura: | Tamanho: 108 KiB |
Arquivo binário não exibido.
|
Depois Largura: | Altura: | Tamanho: 7.0 KiB |
Arquivo binário não exibido.
|
Depois Largura: | Altura: | Tamanho: 7.2 KiB |
Arquivo binário não exibido.
|
Depois Largura: | Altura: | Tamanho: 143 B |
@@ -0,0 +1,85 @@
|
||||
.\" generated with Ronn/v0.6.6
|
||||
.\" http://github.com/rtomayko/ronn/
|
||||
.
|
||||
.TH "INDEX" "" "July 2010" "" ""
|
||||
.
|
||||
.IP "" 4
|
||||
.
|
||||
.nf
|
||||
|
||||
var app = express\.createServer();
|
||||
|
||||
app\.get(\'/\', function(req, res){
|
||||
res\.send(\'Hello World\');
|
||||
});
|
||||
|
||||
app\.listen(3000);
|
||||
.
|
||||
.fi
|
||||
.
|
||||
.IP "" 0
|
||||
.
|
||||
.SH "Features"
|
||||
.
|
||||
.IP "\(bu" 4
|
||||
Robust routing
|
||||
.
|
||||
.IP "\(bu" 4
|
||||
Redirection helpers
|
||||
.
|
||||
.IP "\(bu" 4
|
||||
Focus on high performance
|
||||
.
|
||||
.IP "\(bu" 4
|
||||
View rendering and partials support
|
||||
.
|
||||
.IP "\(bu" 4
|
||||
Environment based configuration
|
||||
.
|
||||
.IP "\(bu" 4
|
||||
Session based flash notifications
|
||||
.
|
||||
.IP "\(bu" 4
|
||||
Built on Connect \fIhttp://extjs\.github\.com/Connect\fR
|
||||
.
|
||||
.IP "\(bu" 4
|
||||
Executable \fIexecutable\.html\fR for generating applications quickly
|
||||
.
|
||||
.IP "" 0
|
||||
.
|
||||
.SH "Contributors"
|
||||
The following are the major contributors of Express (in no specific order)\.
|
||||
.
|
||||
.IP "\(bu" 4
|
||||
TJ Holowaychuk (visionmedia \fIhttp://github\.com/visionmedia\fR)
|
||||
.
|
||||
.IP "\(bu" 4
|
||||
Ciaran Jessup (ciaranj \fIhttp://github\.com/ciaranj\fR)
|
||||
.
|
||||
.IP "\(bu" 4
|
||||
Aaron Heckmann (aheckmann \fIhttp://github\.com/aheckmann\fR)
|
||||
.
|
||||
.IP "" 0
|
||||
.
|
||||
.SH "More Information"
|
||||
.
|
||||
.IP "\(bu" 4
|
||||
Google Group \fIhttp://groups\.google\.com/group/express\-js\fR for discussion
|
||||
.
|
||||
.IP "\(bu" 4
|
||||
Follow tjholowaychuk \fIhttp://twitter\.com/tjholowaychuk\fR on twitter for updates
|
||||
.
|
||||
.IP "\(bu" 4
|
||||
Annotated source documentation \fIapi\.html\fR
|
||||
.
|
||||
.IP "\(bu" 4
|
||||
View the source \fIhttp://github\.com/visionmedia/express\fR
|
||||
.
|
||||
.IP "\(bu" 4
|
||||
View the Connect \fIhttp://github\.com/extjs/Connect\fR repo for middleware usage
|
||||
.
|
||||
.IP "\(bu" 4
|
||||
View the Connect Wiki \fIhttp://wiki\.github\.com/extjs/Connect/\fR for contrib middleware
|
||||
.
|
||||
.IP "" 0
|
||||
|
||||
@@ -0,0 +1,231 @@
|
||||
<html>
|
||||
<head>
|
||||
<title>Express - node web framework</title>
|
||||
<style>
|
||||
#header {
|
||||
position: absolute;
|
||||
top: 10px;
|
||||
left: 0;
|
||||
padding: 12px 0;
|
||||
text-indent: 40px;
|
||||
width: 100%;
|
||||
border-top: 1px solid rgba(0,0,0,0.7);
|
||||
border-bottom: 1px solid rgba(0,0,0,0.7);
|
||||
background: rgba(255,255,255,0.1) url(http://www.sencha.com/favicon.ico) no-repeat 15px 50%;
|
||||
text-align: left;
|
||||
color: #fff;
|
||||
}
|
||||
#tagline {
|
||||
margin-left: 75px;
|
||||
margin-bottom: 30px;
|
||||
color: rgba(255,255,255,0.7); }
|
||||
html {
|
||||
background: #1c1c1c url(images/bg.tile.jpg); }
|
||||
|
||||
body {
|
||||
margin: 0;
|
||||
padding-bottom: 30px;
|
||||
font: 14px/1.4 "Helvetica Neue", "Lucida Grande", "Arial";
|
||||
font-size: 14px;
|
||||
line-height: 1.5;
|
||||
-webkit-text-stroke: 1px rgba(0, 0, 0, 0.1);
|
||||
-moz-text-stroke: 1px rgba(0, 0, 0, 0.1);
|
||||
background: url(images/bg.jpg) 50% 0 no-repeat;
|
||||
color: #8b8b8b; }
|
||||
|
||||
* {
|
||||
outline: none; }
|
||||
|
||||
em {
|
||||
color: white; }
|
||||
|
||||
a img {
|
||||
border: none !important; }
|
||||
|
||||
a {
|
||||
font-weight: bold;
|
||||
text-decoration: none;
|
||||
color: white;
|
||||
-webkit-transition-property: opacity, -webkit-transform, color, background-color, padding, -webkit-box-shadow;
|
||||
-webkit-transition-duration: 0.15s;
|
||||
-webkit-transition-timing-function: ease-out; }
|
||||
a:hover {
|
||||
opacity: 0.8; }
|
||||
|
||||
h1, h2, h3 {
|
||||
margin: 45px 0 0 0;
|
||||
color: white;
|
||||
text-shadow: 1px 2px 2px rgba(0,0,0,0.6); }
|
||||
|
||||
h3 {
|
||||
font-size: 18px; }
|
||||
|
||||
pre {
|
||||
margin: 20px 10px;
|
||||
padding: 25px 20px;
|
||||
background: rgba(0,0,0,0.5);
|
||||
border: 1px solid #323232;
|
||||
-webkit-box-shadow: 1px 2px 2px rgba(0,0,0,0.6);
|
||||
-moz-box-shadow: 1px 2px 2px rgba(0,0,0,0.6);
|
||||
-webkit-border-radius: 5px;
|
||||
-moz-border-radius: 5px; }
|
||||
|
||||
code {
|
||||
font-family: "Helvetica Neue", "Lucida Grande", "Arial"; }
|
||||
|
||||
ul {
|
||||
margin: 15px 0;
|
||||
padding: 0 0 0 35px; }
|
||||
ul li {
|
||||
margin: 0;
|
||||
padding: 2px 0;
|
||||
list-style: square; }
|
||||
|
||||
.sect {
|
||||
margin-left: 40px; }
|
||||
|
||||
#logo {
|
||||
display: block;
|
||||
margin-left: 30%;
|
||||
margin-bottom: 30px;
|
||||
width: 194px;
|
||||
height: 51px;
|
||||
background: url(images/logo.png) 0 0 no-repeat;
|
||||
text-indent: -99999px; }
|
||||
#logo:hover {
|
||||
opacity: 0.7; }
|
||||
#logo:active {
|
||||
opacity: 0.3; }
|
||||
|
||||
#ribbon {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
right: 0;
|
||||
z-index: 2; }
|
||||
|
||||
#wrapper {
|
||||
width: 100%;
|
||||
min-height: 800px;
|
||||
background: url(images/top.png) 0 0 repeat-x; }
|
||||
|
||||
#container {
|
||||
margin: 0 auto;
|
||||
padding-top: 110px;
|
||||
width: 550px; }
|
||||
|
||||
#toc {
|
||||
position: fixed;
|
||||
top: 60px;
|
||||
left: 0;
|
||||
margin: 0 0 0 15px;
|
||||
padding: 15px;
|
||||
height: 100%;
|
||||
background: rgba(0,0,0,0.2);
|
||||
border-right: 1px solid rgba(255,255,255,0.05);
|
||||
}
|
||||
#toc li {
|
||||
padding: 1px 0;
|
||||
list-style: none;
|
||||
}
|
||||
#toc li a {
|
||||
font-size: 11px;
|
||||
}
|
||||
#menu {
|
||||
margin-left: 65px;
|
||||
padding: 0;
|
||||
padding-bottom: 30px; }
|
||||
#menu li {
|
||||
display: inline;
|
||||
list-style: none; }
|
||||
#menu li a {
|
||||
display: block;
|
||||
float: left;
|
||||
margin: 0 2px;
|
||||
padding: 3px 15px;
|
||||
background: rgba(0,0,0,0.2);
|
||||
-webkit-border-radius: 8px;
|
||||
-moz-border-radius: 8px;
|
||||
-webkit-box-shadow: 1px 2px 2px rgba(0,0,0,0.6);
|
||||
-moz-box-shadow: 1px 2px 2px rgba(0,0,0,0.6);
|
||||
-webkit-transition-property: opacity, -webkit-transform, color, background-color, -webkit-box-shadow;
|
||||
-webkit-transition-duration: 0.15s;
|
||||
-webkit-transition-timing-function: ease-out; }
|
||||
#menu li a:hover,
|
||||
#menu li a.active {
|
||||
background: rgba(0,0,0,0.5); }
|
||||
#menu li a:active {
|
||||
background: rgba(0,0,0,0.1);
|
||||
-webkit-box-shadow: 1px 1px 1px rgba(0,0,0,0.4);
|
||||
-moz-box-shadow: 1px 1px 1px rgba(0,0,0,0.4); }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<a href='http://github.com/visionmedia/express'>
|
||||
<img alt='Fork me on GitHub' id='ribbon' src='http://s3.amazonaws.com/github/ribbons/forkme_right_white_ffffff.png' />
|
||||
</a>
|
||||
<div id="header"><strong>Sencha</strong> labs</div>
|
||||
<div id="wrapper">
|
||||
<div id="container">
|
||||
<a href='http://github.com/visionmedia/express' id='logo'>Express</a>
|
||||
<p id="tagline">
|
||||
High performance, high class web development for
|
||||
<a href="http://nodejs.org">Node.js</a>
|
||||
</p>
|
||||
<ul id="menu">
|
||||
<li><a href="index.html">Home</a></li>
|
||||
<li><a href="guide.html">Guide</a></li>
|
||||
<li><a href="contrib.html">Contributing</a></li>
|
||||
<li><a href="migrate.html">1.x Migration</a></li>
|
||||
</ul>
|
||||
<div class='mp'>
|
||||
<pre><code>var app = express.createServer();
|
||||
|
||||
app.get('/', function(req, res){
|
||||
res.send('Hello World');
|
||||
});
|
||||
|
||||
app.listen(3000);
|
||||
</code></pre>
|
||||
|
||||
<h2 id="Features">Features</h2>
|
||||
|
||||
<ul>
|
||||
<li>Robust routing</li>
|
||||
<li>Redirection helpers</li>
|
||||
<li>Focus on high performance</li>
|
||||
<li>View rendering and partials support</li>
|
||||
<li>Environment based configuration</li>
|
||||
<li>Session based flash notifications</li>
|
||||
<li>Built on <a href="http://extjs.github.com/Connect">Connect</a></li>
|
||||
<li><a href="executable.html">Executable</a> for generating applications quickly</li>
|
||||
</ul>
|
||||
|
||||
|
||||
<h2 id="Contributors">Contributors</h2>
|
||||
|
||||
<p>The following are the major contributors of Express (in no specific order).</p>
|
||||
|
||||
<ul>
|
||||
<li>TJ Holowaychuk (<a href="http://github.com/visionmedia">visionmedia</a>)</li>
|
||||
<li>Ciaran Jessup (<a href="http://github.com/ciaranj">ciaranj</a>)</li>
|
||||
<li>Aaron Heckmann (<a href="http://github.com/aheckmann">aheckmann</a>)</li>
|
||||
</ul>
|
||||
|
||||
|
||||
<h2 id="More-Information">More Information</h2>
|
||||
|
||||
<ul>
|
||||
<li><a href="http://groups.google.com/group/express-js">Google Group</a> for discussion</li>
|
||||
<li>Follow <a href="http://twitter.com/tjholowaychuk">tjholowaychuk</a> on twitter for updates</li>
|
||||
<li>Annotated source <a href="api.html">documentation</a></li>
|
||||
<li>View the <a href="http://github.com/visionmedia/express">source</a></li>
|
||||
<li>View the <a href="http://github.com/extjs/Connect">Connect</a> repo for middleware usage</li>
|
||||
<li>View the <a href="http://wiki.github.com/extjs/Connect/">Connect Wiki</a> for contrib middleware</li>
|
||||
</ul>
|
||||
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,36 @@
|
||||
|
||||
var app = express.createServer();
|
||||
|
||||
app.get('/', function(req, res){
|
||||
res.send('Hello World');
|
||||
});
|
||||
|
||||
app.listen(3000);
|
||||
|
||||
## Features
|
||||
|
||||
* Robust routing
|
||||
* Redirection helpers
|
||||
* Focus on high performance
|
||||
* View rendering and partials support
|
||||
* Environment based configuration
|
||||
* Session based flash notifications
|
||||
* Built on [Connect](http://extjs.github.com/Connect)
|
||||
* [Executable](executable.html) for generating applications quickly
|
||||
|
||||
## Contributors
|
||||
|
||||
The following are the major contributors of Express (in no specific order).
|
||||
|
||||
* TJ Holowaychuk ([visionmedia](http://github.com/visionmedia))
|
||||
* Ciaran Jessup ([ciaranj](http://github.com/ciaranj))
|
||||
* Aaron Heckmann ([aheckmann](http://github.com/aheckmann))
|
||||
|
||||
## More Information
|
||||
|
||||
* [Google Group](http://groups.google.com/group/express-js) for discussion
|
||||
* Follow [tjholowaychuk](http://twitter.com/tjholowaychuk) on twitter for updates
|
||||
* Annotated source [documentation](api.html)
|
||||
* View the [source](http://github.com/visionmedia/express)
|
||||
* View the [Connect](http://github.com/extjs/Connect) repo for middleware usage
|
||||
* View the [Connect Wiki](http://wiki.github.com/extjs/Connect/) for contrib middleware
|
||||
@@ -0,0 +1,4 @@
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,179 @@
|
||||
<html>
|
||||
<head>
|
||||
<title>Express - node web framework</title>
|
||||
<style>
|
||||
#header {
|
||||
position: absolute;
|
||||
top: 10px;
|
||||
left: 0;
|
||||
padding: 12px 0;
|
||||
text-indent: 40px;
|
||||
width: 100%;
|
||||
border-top: 1px solid rgba(0,0,0,0.7);
|
||||
border-bottom: 1px solid rgba(0,0,0,0.7);
|
||||
background: rgba(255,255,255,0.1) url(http://www.sencha.com/favicon.ico) no-repeat 15px 50%;
|
||||
text-align: left;
|
||||
color: #fff;
|
||||
}
|
||||
#tagline {
|
||||
margin-left: 75px;
|
||||
margin-bottom: 30px;
|
||||
color: rgba(255,255,255,0.7); }
|
||||
html {
|
||||
background: #1c1c1c url(images/bg.tile.jpg); }
|
||||
|
||||
body {
|
||||
margin: 0;
|
||||
padding-bottom: 30px;
|
||||
font: 14px/1.4 "Helvetica Neue", "Lucida Grande", "Arial";
|
||||
font-size: 14px;
|
||||
line-height: 1.5;
|
||||
-webkit-text-stroke: 1px rgba(0, 0, 0, 0.1);
|
||||
-moz-text-stroke: 1px rgba(0, 0, 0, 0.1);
|
||||
background: url(images/bg.jpg) 50% 0 no-repeat;
|
||||
color: #8b8b8b; }
|
||||
|
||||
* {
|
||||
outline: none; }
|
||||
|
||||
em {
|
||||
color: white; }
|
||||
|
||||
a img {
|
||||
border: none !important; }
|
||||
|
||||
a {
|
||||
font-weight: bold;
|
||||
text-decoration: none;
|
||||
color: white;
|
||||
-webkit-transition-property: opacity, -webkit-transform, color, background-color, padding, -webkit-box-shadow;
|
||||
-webkit-transition-duration: 0.15s;
|
||||
-webkit-transition-timing-function: ease-out; }
|
||||
a:hover {
|
||||
opacity: 0.8; }
|
||||
|
||||
h1, h2, h3 {
|
||||
margin: 45px 0 0 0;
|
||||
color: white;
|
||||
text-shadow: 1px 2px 2px rgba(0,0,0,0.6); }
|
||||
|
||||
h3 {
|
||||
font-size: 18px; }
|
||||
|
||||
pre {
|
||||
margin: 20px 10px;
|
||||
padding: 25px 20px;
|
||||
background: rgba(0,0,0,0.5);
|
||||
border: 1px solid #323232;
|
||||
-webkit-box-shadow: 1px 2px 2px rgba(0,0,0,0.6);
|
||||
-moz-box-shadow: 1px 2px 2px rgba(0,0,0,0.6);
|
||||
-webkit-border-radius: 5px;
|
||||
-moz-border-radius: 5px; }
|
||||
|
||||
code {
|
||||
font-family: "Helvetica Neue", "Lucida Grande", "Arial"; }
|
||||
|
||||
ul {
|
||||
margin: 15px 0;
|
||||
padding: 0 0 0 35px; }
|
||||
ul li {
|
||||
margin: 0;
|
||||
padding: 2px 0;
|
||||
list-style: square; }
|
||||
|
||||
.sect {
|
||||
margin-left: 40px; }
|
||||
|
||||
#logo {
|
||||
display: block;
|
||||
margin-left: 30%;
|
||||
margin-bottom: 30px;
|
||||
width: 194px;
|
||||
height: 51px;
|
||||
background: url(images/logo.png) 0 0 no-repeat;
|
||||
text-indent: -99999px; }
|
||||
#logo:hover {
|
||||
opacity: 0.7; }
|
||||
#logo:active {
|
||||
opacity: 0.3; }
|
||||
|
||||
#ribbon {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
right: 0;
|
||||
z-index: 2; }
|
||||
|
||||
#wrapper {
|
||||
width: 100%;
|
||||
min-height: 800px;
|
||||
background: url(images/top.png) 0 0 repeat-x; }
|
||||
|
||||
#container {
|
||||
margin: 0 auto;
|
||||
padding-top: 110px;
|
||||
width: 550px; }
|
||||
|
||||
#toc {
|
||||
position: fixed;
|
||||
top: 60px;
|
||||
left: 0;
|
||||
margin: 0 0 0 15px;
|
||||
padding: 15px;
|
||||
height: 100%;
|
||||
background: rgba(0,0,0,0.2);
|
||||
border-right: 1px solid rgba(255,255,255,0.05);
|
||||
}
|
||||
#toc li {
|
||||
padding: 1px 0;
|
||||
list-style: none;
|
||||
}
|
||||
#toc li a {
|
||||
font-size: 11px;
|
||||
}
|
||||
#menu {
|
||||
margin-left: 65px;
|
||||
padding: 0;
|
||||
padding-bottom: 30px; }
|
||||
#menu li {
|
||||
display: inline;
|
||||
list-style: none; }
|
||||
#menu li a {
|
||||
display: block;
|
||||
float: left;
|
||||
margin: 0 2px;
|
||||
padding: 3px 15px;
|
||||
background: rgba(0,0,0,0.2);
|
||||
-webkit-border-radius: 8px;
|
||||
-moz-border-radius: 8px;
|
||||
-webkit-box-shadow: 1px 2px 2px rgba(0,0,0,0.6);
|
||||
-moz-box-shadow: 1px 2px 2px rgba(0,0,0,0.6);
|
||||
-webkit-transition-property: opacity, -webkit-transform, color, background-color, -webkit-box-shadow;
|
||||
-webkit-transition-duration: 0.15s;
|
||||
-webkit-transition-timing-function: ease-out; }
|
||||
#menu li a:hover,
|
||||
#menu li a.active {
|
||||
background: rgba(0,0,0,0.5); }
|
||||
#menu li a:active {
|
||||
background: rgba(0,0,0,0.1);
|
||||
-webkit-box-shadow: 1px 1px 1px rgba(0,0,0,0.4);
|
||||
-moz-box-shadow: 1px 1px 1px rgba(0,0,0,0.4); }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<a href='http://github.com/visionmedia/express'>
|
||||
<img alt='Fork me on GitHub' id='ribbon' src='http://s3.amazonaws.com/github/ribbons/forkme_right_white_ffffff.png' />
|
||||
</a>
|
||||
<div id="header"><strong>Sencha</strong> labs</div>
|
||||
<div id="wrapper">
|
||||
<div id="container">
|
||||
<a href='http://github.com/visionmedia/express' id='logo'>Express</a>
|
||||
<p id="tagline">
|
||||
High performance, high class web development for
|
||||
<a href="http://nodejs.org">Node.js</a>
|
||||
</p>
|
||||
<ul id="menu">
|
||||
<li><a href="index.html">Home</a></li>
|
||||
<li><a href="guide.html">Guide</a></li>
|
||||
<li><a href="contrib.html">Contributing</a></li>
|
||||
<li><a href="migrate.html">1.x Migration</a></li>
|
||||
</ul>
|
||||
@@ -0,0 +1,347 @@
|
||||
.\" generated with Ronn/v0.6.6
|
||||
.\" http://github.com/rtomayko/ronn/
|
||||
.
|
||||
.TH "MIGRATE" "" "July 2010" "" ""
|
||||
.
|
||||
.SS "Built On Connect"
|
||||
Express 1\.x is written to run on\-top of the Connect \fIhttp://extjs\.github\.com/Connect\fR middlware framework, thus the \fIPlugin\fR has been replaced by Connect\'s middleware\. By abstracting our middleware to Connect we allow additional community frameworks to develop robust, high\-level frameworks using the same technologies as Express\.
|
||||
.
|
||||
.SS "Creating Applications"
|
||||
Previously due to legacy code implemented in the early days of node, Express unfortunately had some globals\. The DSL would previously be accessed as shown below:
|
||||
.
|
||||
.IP "" 4
|
||||
.
|
||||
.nf
|
||||
|
||||
require(\'express\');
|
||||
|
||||
configure(function(){
|
||||
// app configuration
|
||||
});
|
||||
|
||||
get(\'/\', function(){
|
||||
return \'hello world\';
|
||||
});
|
||||
.
|
||||
.fi
|
||||
.
|
||||
.IP "" 0
|
||||
.
|
||||
.P
|
||||
Now we utilize the CommonJS module system appropriately, and introduce \fIexpress\.createServer()\fR which accepts the same arguments as \fIhttp\.createServer()\fR:
|
||||
.
|
||||
.IP "" 4
|
||||
.
|
||||
.nf
|
||||
|
||||
var express = require(\'express\'),
|
||||
app = express\.createServer();
|
||||
|
||||
app\.configure(function(){
|
||||
// app configuration
|
||||
});
|
||||
|
||||
app\.get(\'/\', function(req, res){
|
||||
res\.send(\'hello world\');
|
||||
});
|
||||
.
|
||||
.fi
|
||||
.
|
||||
.IP "" 0
|
||||
.
|
||||
.P
|
||||
Express 1\.x does \fInot\fR currently allow returning of a string\.
|
||||
.
|
||||
.SS "Plugins vs Middleware"
|
||||
Previously Express was bundled with plugins, which were essentially what are now Connect middleware\. Previously plugins would be utilized in a manor similar to below:
|
||||
.
|
||||
.IP "" 4
|
||||
.
|
||||
.nf
|
||||
|
||||
use(Logger);
|
||||
use(MethodOverride);
|
||||
use(Cookie);
|
||||
.
|
||||
.fi
|
||||
.
|
||||
.IP "" 0
|
||||
.
|
||||
.P
|
||||
Which we can now \fIuse()\fR within our app, or pass to the \fIexpress\.createServer()\fR method:
|
||||
.
|
||||
.IP "" 4
|
||||
.
|
||||
.nf
|
||||
|
||||
var connect = require(\'connect\');
|
||||
|
||||
var app = express\.createServer(
|
||||
connect\.logger(),
|
||||
connect\.methodOverride(),
|
||||
connect\.cookie()
|
||||
);
|
||||
.
|
||||
.fi
|
||||
.
|
||||
.IP "" 0
|
||||
.
|
||||
.P
|
||||
or:
|
||||
.
|
||||
.IP "" 4
|
||||
.
|
||||
.nf
|
||||
|
||||
var connect = require(\'connect\');
|
||||
var app = express\.createServer();
|
||||
|
||||
app\.use(\'/\', connect\.logger());
|
||||
app\.use(\'/\', connect\.methodOverride());
|
||||
app\.use(\'/\', connect\.cookieDecoder());
|
||||
.
|
||||
.fi
|
||||
.
|
||||
.IP "" 0
|
||||
.
|
||||
.P
|
||||
For documentation on creating Connect middleware visit Middleware Authoring \fIhttp://extjs\.github\.com/Connect/#Middleware\-Authoring\fR\.
|
||||
.
|
||||
.SS "Running Applications"
|
||||
Previously a global function \fIrun()\fR, was available:
|
||||
.
|
||||
.IP "" 4
|
||||
.
|
||||
.nf
|
||||
|
||||
run();
|
||||
.
|
||||
.fi
|
||||
.
|
||||
.IP "" 0
|
||||
.
|
||||
.P
|
||||
The new \fIexpress\.Server\fR has the same API as \fIhttp\.Server\fR, so we can do things like:
|
||||
.
|
||||
.IP "" 4
|
||||
.
|
||||
.nf
|
||||
|
||||
app\.listen();
|
||||
app\.listen(3000);
|
||||
.
|
||||
.fi
|
||||
.
|
||||
.IP "" 0
|
||||
.
|
||||
.SS "Route Parameters"
|
||||
Previously we could use \fIthis\.param()\fR to attempt fetching a route, query string, or request body parameter:
|
||||
.
|
||||
.IP "" 4
|
||||
.
|
||||
.nf
|
||||
|
||||
get(\'/user/:id\', function(){
|
||||
this\.param(\'id\');
|
||||
});
|
||||
.
|
||||
.fi
|
||||
.
|
||||
.IP "" 0
|
||||
.
|
||||
.P
|
||||
Polymorphic parameter access can be done using \fBreq\.param()\fR:
|
||||
.
|
||||
.IP "" 4
|
||||
.
|
||||
.nf
|
||||
|
||||
app\.get(\'/user/:id\', function(){
|
||||
req\.param(\'id\');
|
||||
});
|
||||
.
|
||||
.fi
|
||||
.
|
||||
.IP "" 0
|
||||
.
|
||||
.P
|
||||
Route parameters are also passed as the third argument:
|
||||
.
|
||||
.IP "" 4
|
||||
.
|
||||
.nf
|
||||
|
||||
app\.get(\'/user/:id\', function(req, res, params){
|
||||
params\.id;
|
||||
});
|
||||
.
|
||||
.fi
|
||||
.
|
||||
.IP "" 0
|
||||
.
|
||||
.SS "Passing Route Control"
|
||||
Old express had a weak notion of route passing, which did not support async, and was never properly implemented for practical use:
|
||||
.
|
||||
.IP "" 4
|
||||
.
|
||||
.nf
|
||||
|
||||
get(\'/\', function(){
|
||||
this\.pass(\'/foobar\');
|
||||
});
|
||||
.
|
||||
.fi
|
||||
.
|
||||
.IP "" 0
|
||||
.
|
||||
.P
|
||||
Now Express has access to Connect\'s \fInext()\fR function, which is passed as the fourth and final argument\. Calling \fInext()\fR will pass control to the next \fImatching route\fR, or continue down the stack of Connect middleware\.
|
||||
.
|
||||
.IP "" 4
|
||||
.
|
||||
.nf
|
||||
|
||||
app\.get(\'/user/:id?\', function(req, res, params, next){
|
||||
next();
|
||||
});
|
||||
|
||||
app\.get(\'/user\', function(){
|
||||
// \.\.\. respond
|
||||
});
|
||||
.
|
||||
.fi
|
||||
.
|
||||
.IP "" 0
|
||||
.
|
||||
.SS "View Rendering"
|
||||
View filenames no longer take the form \fINAME\fR\.\fITYPE\fR\.\fIENGINE\fR, the \fIContent\-Type\fR can be set via \fIres\.contentType()\fR or \fIres\.header()\fR\. For example what was previously \fIlayout\.html\.haml\fR, should now be \fIlayout\.haml\fR\.
|
||||
.
|
||||
.P
|
||||
Previously a view render looked something like this:
|
||||
.
|
||||
.IP "" 4
|
||||
.
|
||||
.nf
|
||||
|
||||
get(\'/\', function(){
|
||||
this\.render(\'index\.html\.haml\', {
|
||||
locals: { title: \'My Site\' }
|
||||
});
|
||||
});
|
||||
.
|
||||
.fi
|
||||
.
|
||||
.IP "" 0
|
||||
.
|
||||
.P
|
||||
We now have \fIres\.render()\fR, however the options passed to haml \fIhttp://github\.com/visionmedia/haml\.js\fR, jade \fIhttp://github\.com/visionmedia/jade\fR, and others remain the same\.
|
||||
.
|
||||
.IP "" 4
|
||||
.
|
||||
.nf
|
||||
|
||||
app\.get(\'/\', function(req, res){
|
||||
res\.render(\'index\.haml\', {
|
||||
locals: { title: \'My Site\' }
|
||||
});
|
||||
});
|
||||
.
|
||||
.fi
|
||||
.
|
||||
.IP "" 0
|
||||
.
|
||||
.P
|
||||
Previously rendering of a collection via \fIpartial()\fR would look something like this:
|
||||
.
|
||||
.IP "" 4
|
||||
.
|
||||
.nf
|
||||
|
||||
this\.partial(\'comment\.html\.haml\', { collection: comments });
|
||||
.
|
||||
.fi
|
||||
.
|
||||
.IP "" 0
|
||||
.
|
||||
.P
|
||||
Although this worked just fine, it was generally to verbose, the similar but new API looks like this, as \fIpartial()\fR is \fIalways\fR passed as a local variable:
|
||||
.
|
||||
.IP "" 4
|
||||
.
|
||||
.nf
|
||||
|
||||
partial(\'comment\.haml\', { collection: comments });
|
||||
.
|
||||
.fi
|
||||
.
|
||||
.IP "" 0
|
||||
.
|
||||
.P
|
||||
To make things even less verbose we can assume the extension when omitted:
|
||||
.
|
||||
.IP "" 4
|
||||
.
|
||||
.nf
|
||||
|
||||
partial(\'comment\', { collection: comments });
|
||||
.
|
||||
.fi
|
||||
.
|
||||
.IP "" 0
|
||||
.
|
||||
.P
|
||||
And once again even further, when rendering a collection we can simply pass an array, if no other options are desired:
|
||||
.
|
||||
.IP "" 4
|
||||
.
|
||||
.nf
|
||||
|
||||
partial(\'comments\', comments);
|
||||
.
|
||||
.fi
|
||||
.
|
||||
.IP "" 0
|
||||
.
|
||||
.SS "Redirecting"
|
||||
Previously you would
|
||||
.
|
||||
.IP "" 4
|
||||
.
|
||||
.nf
|
||||
|
||||
this\.redirect(\'/somewhere\');
|
||||
.
|
||||
.fi
|
||||
.
|
||||
.IP "" 0
|
||||
.
|
||||
.P
|
||||
However you would now:
|
||||
.
|
||||
.IP "" 4
|
||||
.
|
||||
.nf
|
||||
|
||||
res\.redirect(\'/somewhere\');
|
||||
res\.redirect(\'/somewhere\', 301);
|
||||
.
|
||||
.fi
|
||||
.
|
||||
.IP "" 0
|
||||
.
|
||||
.SS "HTTP Client"
|
||||
Previously Express provided a high level http client, this library is no more as it does not belong in Express, however it may be resurrected as a separate module\.
|
||||
.
|
||||
.SS "Core Extensions"
|
||||
Express is no longer dependent on the JavaScript Extensions \fIhttp://github\.com/visionmedia/ext\.js\fR library, so those of you using the methods provided by it such as \fBObject\.merge(a, b)\fR will need to roll your own, or install the module via:
|
||||
.
|
||||
.IP "" 4
|
||||
.
|
||||
.nf
|
||||
|
||||
$ npm install ext
|
||||
.
|
||||
.fi
|
||||
.
|
||||
.IP "" 0
|
||||
|
||||
@@ -0,0 +1,398 @@
|
||||
<html>
|
||||
<head>
|
||||
<title>Express - node web framework</title>
|
||||
<style>
|
||||
#header {
|
||||
position: absolute;
|
||||
top: 10px;
|
||||
left: 0;
|
||||
padding: 12px 0;
|
||||
text-indent: 40px;
|
||||
width: 100%;
|
||||
border-top: 1px solid rgba(0,0,0,0.7);
|
||||
border-bottom: 1px solid rgba(0,0,0,0.7);
|
||||
background: rgba(255,255,255,0.1) url(http://www.sencha.com/favicon.ico) no-repeat 15px 50%;
|
||||
text-align: left;
|
||||
color: #fff;
|
||||
}
|
||||
#tagline {
|
||||
margin-left: 75px;
|
||||
margin-bottom: 30px;
|
||||
color: rgba(255,255,255,0.7); }
|
||||
html {
|
||||
background: #1c1c1c url(images/bg.tile.jpg); }
|
||||
|
||||
body {
|
||||
margin: 0;
|
||||
padding-bottom: 30px;
|
||||
font: 14px/1.4 "Helvetica Neue", "Lucida Grande", "Arial";
|
||||
font-size: 14px;
|
||||
line-height: 1.5;
|
||||
-webkit-text-stroke: 1px rgba(0, 0, 0, 0.1);
|
||||
-moz-text-stroke: 1px rgba(0, 0, 0, 0.1);
|
||||
background: url(images/bg.jpg) 50% 0 no-repeat;
|
||||
color: #8b8b8b; }
|
||||
|
||||
* {
|
||||
outline: none; }
|
||||
|
||||
em {
|
||||
color: white; }
|
||||
|
||||
a img {
|
||||
border: none !important; }
|
||||
|
||||
a {
|
||||
font-weight: bold;
|
||||
text-decoration: none;
|
||||
color: white;
|
||||
-webkit-transition-property: opacity, -webkit-transform, color, background-color, padding, -webkit-box-shadow;
|
||||
-webkit-transition-duration: 0.15s;
|
||||
-webkit-transition-timing-function: ease-out; }
|
||||
a:hover {
|
||||
opacity: 0.8; }
|
||||
|
||||
h1, h2, h3 {
|
||||
margin: 45px 0 0 0;
|
||||
color: white;
|
||||
text-shadow: 1px 2px 2px rgba(0,0,0,0.6); }
|
||||
|
||||
h3 {
|
||||
font-size: 18px; }
|
||||
|
||||
pre {
|
||||
margin: 20px 10px;
|
||||
padding: 25px 20px;
|
||||
background: rgba(0,0,0,0.5);
|
||||
border: 1px solid #323232;
|
||||
-webkit-box-shadow: 1px 2px 2px rgba(0,0,0,0.6);
|
||||
-moz-box-shadow: 1px 2px 2px rgba(0,0,0,0.6);
|
||||
-webkit-border-radius: 5px;
|
||||
-moz-border-radius: 5px; }
|
||||
|
||||
code {
|
||||
font-family: "Helvetica Neue", "Lucida Grande", "Arial"; }
|
||||
|
||||
ul {
|
||||
margin: 15px 0;
|
||||
padding: 0 0 0 35px; }
|
||||
ul li {
|
||||
margin: 0;
|
||||
padding: 2px 0;
|
||||
list-style: square; }
|
||||
|
||||
.sect {
|
||||
margin-left: 40px; }
|
||||
|
||||
#logo {
|
||||
display: block;
|
||||
margin-left: 30%;
|
||||
margin-bottom: 30px;
|
||||
width: 194px;
|
||||
height: 51px;
|
||||
background: url(images/logo.png) 0 0 no-repeat;
|
||||
text-indent: -99999px; }
|
||||
#logo:hover {
|
||||
opacity: 0.7; }
|
||||
#logo:active {
|
||||
opacity: 0.3; }
|
||||
|
||||
#ribbon {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
right: 0;
|
||||
z-index: 2; }
|
||||
|
||||
#wrapper {
|
||||
width: 100%;
|
||||
min-height: 800px;
|
||||
background: url(images/top.png) 0 0 repeat-x; }
|
||||
|
||||
#container {
|
||||
margin: 0 auto;
|
||||
padding-top: 110px;
|
||||
width: 550px; }
|
||||
|
||||
#toc {
|
||||
position: fixed;
|
||||
top: 60px;
|
||||
left: 0;
|
||||
margin: 0 0 0 15px;
|
||||
padding: 15px;
|
||||
height: 100%;
|
||||
background: rgba(0,0,0,0.2);
|
||||
border-right: 1px solid rgba(255,255,255,0.05);
|
||||
}
|
||||
#toc li {
|
||||
padding: 1px 0;
|
||||
list-style: none;
|
||||
}
|
||||
#toc li a {
|
||||
font-size: 11px;
|
||||
}
|
||||
#menu {
|
||||
margin-left: 65px;
|
||||
padding: 0;
|
||||
padding-bottom: 30px; }
|
||||
#menu li {
|
||||
display: inline;
|
||||
list-style: none; }
|
||||
#menu li a {
|
||||
display: block;
|
||||
float: left;
|
||||
margin: 0 2px;
|
||||
padding: 3px 15px;
|
||||
background: rgba(0,0,0,0.2);
|
||||
-webkit-border-radius: 8px;
|
||||
-moz-border-radius: 8px;
|
||||
-webkit-box-shadow: 1px 2px 2px rgba(0,0,0,0.6);
|
||||
-moz-box-shadow: 1px 2px 2px rgba(0,0,0,0.6);
|
||||
-webkit-transition-property: opacity, -webkit-transform, color, background-color, -webkit-box-shadow;
|
||||
-webkit-transition-duration: 0.15s;
|
||||
-webkit-transition-timing-function: ease-out; }
|
||||
#menu li a:hover,
|
||||
#menu li a.active {
|
||||
background: rgba(0,0,0,0.5); }
|
||||
#menu li a:active {
|
||||
background: rgba(0,0,0,0.1);
|
||||
-webkit-box-shadow: 1px 1px 1px rgba(0,0,0,0.4);
|
||||
-moz-box-shadow: 1px 1px 1px rgba(0,0,0,0.4); }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<a href='http://github.com/visionmedia/express'>
|
||||
<img alt='Fork me on GitHub' id='ribbon' src='http://s3.amazonaws.com/github/ribbons/forkme_right_white_ffffff.png' />
|
||||
</a>
|
||||
<div id="header"><strong>Sencha</strong> labs</div>
|
||||
<div id="wrapper">
|
||||
<div id="container">
|
||||
<a href='http://github.com/visionmedia/express' id='logo'>Express</a>
|
||||
<p id="tagline">
|
||||
High performance, high class web development for
|
||||
<a href="http://nodejs.org">Node.js</a>
|
||||
</p>
|
||||
<ul id="menu">
|
||||
<li><a href="index.html">Home</a></li>
|
||||
<li><a href="guide.html">Guide</a></li>
|
||||
<li><a href="contrib.html">Contributing</a></li>
|
||||
<li><a href="migrate.html">1.x Migration</a></li>
|
||||
</ul>
|
||||
<div class='mp'>
|
||||
<h3 id="Built-On-Connect">Built On Connect</h3>
|
||||
|
||||
<p>Express 1.x is written to run on-top of the <a href="http://extjs.github.com/Connect">Connect</a> middlware
|
||||
framework, thus the <em>Plugin</em> has been replaced by Connect's middleware. By abstracting our middleware
|
||||
to Connect we allow additional community frameworks to develop robust, high-level frameworks using
|
||||
the same technologies as Express.</p>
|
||||
|
||||
<h3 id="Creating-Applications">Creating Applications</h3>
|
||||
|
||||
<p>Previously due to legacy code implemented in the early days of node,
|
||||
Express unfortunately had some globals. The DSL would previously be
|
||||
accessed as shown below:</p>
|
||||
|
||||
<pre><code>require('express');
|
||||
|
||||
configure(function(){
|
||||
// app configuration
|
||||
});
|
||||
|
||||
get('/', function(){
|
||||
return 'hello world';
|
||||
});
|
||||
</code></pre>
|
||||
|
||||
<p>Now we utilize the CommonJS module system appropriately, and
|
||||
introduce <em>express.createServer()</em> which accepts the same arguments
|
||||
as <em>http.createServer()</em>:</p>
|
||||
|
||||
<pre><code>var express = require('express'),
|
||||
app = express.createServer();
|
||||
|
||||
app.configure(function(){
|
||||
// app configuration
|
||||
});
|
||||
|
||||
app.get('/', function(req, res){
|
||||
res.send('hello world');
|
||||
});
|
||||
</code></pre>
|
||||
|
||||
<p>Express 1.x does <em>not</em> currently allow returning of a string.</p>
|
||||
|
||||
<h3 id="Plugins-vs-Middleware">Plugins vs Middleware</h3>
|
||||
|
||||
<p>Previously Express was bundled with plugins, which were essentially what
|
||||
are now Connect middleware. Previously plugins would be utilized in a manor
|
||||
similar to below:</p>
|
||||
|
||||
<pre><code>use(Logger);
|
||||
use(MethodOverride);
|
||||
use(Cookie);
|
||||
</code></pre>
|
||||
|
||||
<p>Which we can now <em>use()</em> within our app, or pass to the <em>express.createServer()</em> method:</p>
|
||||
|
||||
<pre><code>var connect = require('connect');
|
||||
|
||||
var app = express.createServer(
|
||||
connect.logger(),
|
||||
connect.methodOverride(),
|
||||
connect.cookie()
|
||||
);
|
||||
</code></pre>
|
||||
|
||||
<p>or:</p>
|
||||
|
||||
<pre><code>var connect = require('connect');
|
||||
var app = express.createServer();
|
||||
|
||||
app.use('/', connect.logger());
|
||||
app.use('/', connect.methodOverride());
|
||||
app.use('/', connect.cookieDecoder());
|
||||
</code></pre>
|
||||
|
||||
<p>For documentation on creating Connect middleware visit <a href="http://extjs.github.com/Connect/#Middleware-Authoring">Middleware Authoring</a>.</p>
|
||||
|
||||
<h3 id="Running-Applications">Running Applications</h3>
|
||||
|
||||
<p>Previously a global function <em>run()</em>, was available:</p>
|
||||
|
||||
<pre><code>run();
|
||||
</code></pre>
|
||||
|
||||
<p>The new <em>express.Server</em> has the same API as <em>http.Server</em>,
|
||||
so we can do things like:</p>
|
||||
|
||||
<pre><code>app.listen();
|
||||
app.listen(3000);
|
||||
</code></pre>
|
||||
|
||||
<h3 id="Route-Parameters">Route Parameters</h3>
|
||||
|
||||
<p>Previously we could use <em>this.param()</em> to attempt
|
||||
fetching a route, query string, or request body parameter:</p>
|
||||
|
||||
<pre><code>get('/user/:id', function(){
|
||||
this.param('id');
|
||||
});
|
||||
</code></pre>
|
||||
|
||||
<p>Polymorphic parameter access can be done using <code>req.param()</code>:</p>
|
||||
|
||||
<pre><code>app.get('/user/:id', function(){
|
||||
req.param('id');
|
||||
});
|
||||
</code></pre>
|
||||
|
||||
<p>Route parameters are also passed as the third argument:</p>
|
||||
|
||||
<pre><code>app.get('/user/:id', function(req, res, params){
|
||||
params.id;
|
||||
});
|
||||
</code></pre>
|
||||
|
||||
<h3 id="Passing-Route-Control">Passing Route Control</h3>
|
||||
|
||||
<p>Old express had a weak notion of route passing,
|
||||
which did not support async, and was never properly
|
||||
implemented for practical use:</p>
|
||||
|
||||
<pre><code>get('/', function(){
|
||||
this.pass('/foobar');
|
||||
});
|
||||
</code></pre>
|
||||
|
||||
<p>Now Express has access to Connect's <em>next()</em> function,
|
||||
which is passed as the fourth and final argument. Calling <em>next()</em> will
|
||||
pass control to the next <em>matching route</em>, or continue down the stack
|
||||
of Connect middleware.</p>
|
||||
|
||||
<pre><code>app.get('/user/:id?', function(req, res, params, next){
|
||||
next();
|
||||
});
|
||||
|
||||
app.get('/user', function(){
|
||||
// ... respond
|
||||
});
|
||||
</code></pre>
|
||||
|
||||
<h3 id="View-Rendering">View Rendering</h3>
|
||||
|
||||
<p>View filenames no longer take the form <em>Express</em>.<em>TYPE</em>.<em>ENGINE</em>,
|
||||
the <em>Content-Type</em> can be set via <em>res.contentType()</em> or
|
||||
<em>res.header()</em>. For example what was previously <em>layout.html.haml</em>,
|
||||
should now be <em>layout.haml</em>.</p>
|
||||
|
||||
<p>Previously a view render looked something like this:</p>
|
||||
|
||||
<pre><code>get('/', function(){
|
||||
this.render('index.html.haml', {
|
||||
locals: { title: 'My Site' }
|
||||
});
|
||||
});
|
||||
</code></pre>
|
||||
|
||||
<p>We now have <em>res.render()</em>, however the options passed to <a href="http://github.com/visionmedia/haml.js">haml</a>, <a href="http://github.com/visionmedia/jade">jade</a>, and others
|
||||
remain the same.</p>
|
||||
|
||||
<pre><code>app.get('/', function(req, res){
|
||||
res.render('index.haml', {
|
||||
locals: { title: 'My Site' }
|
||||
});
|
||||
});
|
||||
</code></pre>
|
||||
|
||||
<p>Previously rendering of a collection via <em>partial()</em> would look something like this:</p>
|
||||
|
||||
<pre><code>this.partial('comment.html.haml', { collection: comments });
|
||||
</code></pre>
|
||||
|
||||
<p>Although this worked just fine, it was generally to verbose, the similar but new API
|
||||
looks like this, as <em>partial()</em> is <em>always</em> passed as a local variable:</p>
|
||||
|
||||
<pre><code>partial('comment.haml', { collection: comments });
|
||||
</code></pre>
|
||||
|
||||
<p>To make things even less verbose we can assume the extension when omitted:</p>
|
||||
|
||||
<pre><code>partial('comment', { collection: comments });
|
||||
</code></pre>
|
||||
|
||||
<p>And once again even further, when rendering a collection we can simply pass
|
||||
an array, if no other options are desired:</p>
|
||||
|
||||
<pre><code>partial('comments', comments);
|
||||
</code></pre>
|
||||
|
||||
<h3 id="Redirecting">Redirecting</h3>
|
||||
|
||||
<p>Previously you would</p>
|
||||
|
||||
<pre><code>this.redirect('/somewhere');
|
||||
</code></pre>
|
||||
|
||||
<p>However you would now:</p>
|
||||
|
||||
<pre><code>res.redirect('/somewhere');
|
||||
res.redirect('/somewhere', 301);
|
||||
</code></pre>
|
||||
|
||||
<h3 id="HTTP-Client">HTTP Client</h3>
|
||||
|
||||
<p>Previously Express provided a high level http client, this library is no more
|
||||
as it does not belong in Express, however it may be resurrected as a separate module.</p>
|
||||
|
||||
<h3 id="Core-Extensions">Core Extensions</h3>
|
||||
|
||||
<p>Express is no longer dependent on the <a href="http://github.com/visionmedia/ext.js">JavaScript Extensions</a> library, so those of you using the methods provided by it such as <code>Object.merge(a, b)</code> will need to
|
||||
roll your own, or install the module via:</p>
|
||||
|
||||
<pre><code>$ npm install ext
|
||||
</code></pre>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,192 @@
|
||||
|
||||
### Built On Connect
|
||||
|
||||
Express 1.x is written to run on-top of the [Connect](http://extjs.github.com/Connect) middlware
|
||||
framework, thus the _Plugin_ has been replaced by Connect's middleware. By abstracting our middleware
|
||||
to Connect we allow additional community frameworks to develop robust, high-level frameworks using
|
||||
the same technologies as Express.
|
||||
|
||||
### Creating Applications
|
||||
|
||||
Previously due to legacy code implemented in the early days of node,
|
||||
Express unfortunately had some globals. The DSL would previously be
|
||||
accessed as shown below:
|
||||
|
||||
require('express');
|
||||
|
||||
configure(function(){
|
||||
// app configuration
|
||||
});
|
||||
|
||||
get('/', function(){
|
||||
return 'hello world';
|
||||
});
|
||||
|
||||
Now we utilize the CommonJS module system appropriately, and
|
||||
introduce _express.createServer()_ which accepts the same arguments
|
||||
as _http.createServer()_:
|
||||
|
||||
var express = require('express'),
|
||||
app = express.createServer();
|
||||
|
||||
app.configure(function(){
|
||||
// app configuration
|
||||
});
|
||||
|
||||
app.get('/', function(req, res){
|
||||
res.send('hello world');
|
||||
});
|
||||
|
||||
Express 1.x does _not_ currently allow returning of a string.
|
||||
|
||||
### Plugins vs Middleware
|
||||
|
||||
Previously Express was bundled with plugins, which were essentially what
|
||||
are now Connect middleware. Previously plugins would be utilized in a manor
|
||||
similar to below:
|
||||
|
||||
use(Logger);
|
||||
use(MethodOverride);
|
||||
use(Cookie);
|
||||
|
||||
Which we can now _use()_ within our app, or pass to the _express.createServer()_ method:
|
||||
|
||||
var connect = require('connect');
|
||||
|
||||
var app = express.createServer(
|
||||
connect.logger(),
|
||||
connect.methodOverride(),
|
||||
connect.cookie()
|
||||
);
|
||||
|
||||
or:
|
||||
|
||||
var connect = require('connect');
|
||||
var app = express.createServer();
|
||||
|
||||
app.use('/', connect.logger());
|
||||
app.use('/', connect.methodOverride());
|
||||
app.use('/', connect.cookieDecoder());
|
||||
|
||||
For documentation on creating Connect middleware visit [Middleware Authoring](http://extjs.github.com/Connect/#Middleware-Authoring).
|
||||
|
||||
### Running Applications
|
||||
|
||||
Previously a global function _run()_, was available:
|
||||
|
||||
run();
|
||||
|
||||
The new _express.Server_ has the same API as _http.Server_,
|
||||
so we can do things like:
|
||||
|
||||
app.listen();
|
||||
app.listen(3000);
|
||||
|
||||
### Route Parameters
|
||||
|
||||
Previously we could use _this.param()_ to attempt
|
||||
fetching a route, query string, or request body parameter:
|
||||
|
||||
get('/user/:id', function(){
|
||||
this.param('id');
|
||||
});
|
||||
|
||||
Polymorphic parameter access can be done using `req.param()`:
|
||||
|
||||
app.get('/user/:id', function(){
|
||||
req.param('id');
|
||||
});
|
||||
|
||||
Route parameters are also passed as the third argument:
|
||||
|
||||
app.get('/user/:id', function(req, res, params){
|
||||
params.id;
|
||||
});
|
||||
|
||||
### Passing Route Control
|
||||
|
||||
Old express had a weak notion of route passing,
|
||||
which did not support async, and was never properly
|
||||
implemented for practical use:
|
||||
|
||||
get('/', function(){
|
||||
this.pass('/foobar');
|
||||
});
|
||||
|
||||
Now Express has access to Connect's _next()_ function,
|
||||
which is passed as the fourth and final argument. Calling _next()_ will
|
||||
pass control to the next _matching route_, or continue down the stack
|
||||
of Connect middleware.
|
||||
|
||||
app.get('/user/:id?', function(req, res, params, next){
|
||||
next();
|
||||
});
|
||||
|
||||
app.get('/user', function(){
|
||||
// ... respond
|
||||
});
|
||||
|
||||
### View Rendering
|
||||
|
||||
View filenames no longer take the form _NAME_._TYPE_._ENGINE_,
|
||||
the _Content-Type_ can be set via _res.contentType()_ or
|
||||
_res.header()_. For example what was previously _layout.html.haml_,
|
||||
should now be _layout.haml_.
|
||||
|
||||
Previously a view render looked something like this:
|
||||
|
||||
get('/', function(){
|
||||
this.render('index.html.haml', {
|
||||
locals: { title: 'My Site' }
|
||||
});
|
||||
});
|
||||
|
||||
We now have _res.render()_, however the options passed to [haml](http://github.com/visionmedia/haml.js), [jade](http://github.com/visionmedia/jade), and others
|
||||
remain the same.
|
||||
|
||||
app.get('/', function(req, res){
|
||||
res.render('index.haml', {
|
||||
locals: { title: 'My Site' }
|
||||
});
|
||||
});
|
||||
|
||||
Previously rendering of a collection via _partial()_ would look something like this:
|
||||
|
||||
this.partial('comment.html.haml', { collection: comments });
|
||||
|
||||
Although this worked just fine, it was generally to verbose, the similar but new API
|
||||
looks like this, as _partial()_ is _always_ passed as a local variable:
|
||||
|
||||
partial('comment.haml', { collection: comments });
|
||||
|
||||
To make things even less verbose we can assume the extension when omitted:
|
||||
|
||||
partial('comment', { collection: comments });
|
||||
|
||||
And once again even further, when rendering a collection we can simply pass
|
||||
an array, if no other options are desired:
|
||||
|
||||
partial('comments', comments);
|
||||
|
||||
### Redirecting
|
||||
|
||||
Previously you would
|
||||
|
||||
this.redirect('/somewhere');
|
||||
|
||||
However you would now:
|
||||
|
||||
res.redirect('/somewhere');
|
||||
res.redirect('/somewhere', 301);
|
||||
|
||||
### HTTP Client
|
||||
|
||||
Previously Express provided a high level http client, this library is no more
|
||||
as it does not belong in Express, however it may be resurrected as a separate module.
|
||||
|
||||
### Core Extensions
|
||||
|
||||
Express is no longer dependent on the [JavaScript Extensions](http://github.com/visionmedia/ext.js) library, so those of you using the methods provided by it such as `Object.merge(a, b)` will need to
|
||||
roll your own, or install the module via:
|
||||
|
||||
$ npm install ext
|
||||
@@ -1,81 +0,0 @@
|
||||
|
||||
require.paths.unshift('lib')
|
||||
require('express')
|
||||
require('express/plugins')
|
||||
|
||||
configure(function(){
|
||||
var fiveMinutes = 300000,
|
||||
oneMinute = 60000
|
||||
use(MethodOverride)
|
||||
use(ContentLength)
|
||||
use(CommonLogger)
|
||||
use(Cookie)
|
||||
use(Cache, { lifetime: fiveMinutes, reapInterval: oneMinute })
|
||||
use(Session, { lifetime: fiveMinutes, reapInterval: oneMinute })
|
||||
set('root', __dirname)
|
||||
})
|
||||
|
||||
var messages = [],
|
||||
utils = require('express/utils')
|
||||
|
||||
get('/', function(){
|
||||
this.redirect('/chat')
|
||||
})
|
||||
|
||||
get('/chat', function(){
|
||||
this.render('chat.haml.html', {
|
||||
locals: {
|
||||
title: 'Chat',
|
||||
messages: messages,
|
||||
name: this.session.name,
|
||||
usersOnline: Session.store.length()
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
post('/chat', function(){
|
||||
this.session.name = this.param('name')
|
||||
messages
|
||||
.push(utils.escape(this.param('name')) + ': ' + utils.escape(this.param('message'))
|
||||
.replace(/(http:\/\/[^\s]+)/g, '<a href="$1" target="express-chat">$1</a>')
|
||||
.replace(/:\)/g, '<img src="http://icons3.iconfinder.netdna-cdn.com/data/icons/ledicons/emoticon_smile.png">'))
|
||||
this.halt(200)
|
||||
})
|
||||
|
||||
get('/chat/messages', function(){
|
||||
var self = this,
|
||||
previousLength = messages.length,
|
||||
timer = setInterval(function(){
|
||||
if (messages.length > previousLength)
|
||||
self.contentType('json'),
|
||||
previousLength = messages.length,
|
||||
self.halt(200, JSON.encode(messages)),
|
||||
clearInterval(timer)
|
||||
}, 100)
|
||||
})
|
||||
|
||||
get('/public/*', function(file){
|
||||
this.sendfile(__dirname + '/public/' + file)
|
||||
})
|
||||
|
||||
get('/*.css', function(file){
|
||||
this.render(file + '.sass.css', { layout: false })
|
||||
})
|
||||
|
||||
get('/error/view', function(){
|
||||
this.render('does.not.exist')
|
||||
})
|
||||
|
||||
get('/error', function(){
|
||||
throw new Error('oh noes!')
|
||||
})
|
||||
|
||||
get('/simple', function(){
|
||||
return 'Hello :)'
|
||||
})
|
||||
|
||||
get('/favicon.ico', function(){
|
||||
this.halt()
|
||||
})
|
||||
|
||||
run()
|
||||
Arquivo binário não exibido.
|
Antes Largura: | Altura: | Tamanho: 5.2 KiB |
@@ -1,28 +0,0 @@
|
||||
|
||||
$(function(){
|
||||
// Send message
|
||||
$('form').submit(function(){
|
||||
var message = $('input[name=message]'),
|
||||
name = $('input[name=name]')
|
||||
if (message.val())
|
||||
$.post('/chat', { name: name.val(), message: message.val() }, function(){
|
||||
message.val('')
|
||||
})
|
||||
else
|
||||
message.css('border', '1px solid red')
|
||||
return false
|
||||
})
|
||||
|
||||
// Longpoll
|
||||
;(function poll(){
|
||||
$.getJSON('/chat/messages', function(messages){
|
||||
$('#messages').empty()
|
||||
$.each(messages, function(i, msg){
|
||||
$('#messages')
|
||||
.append('<li>' + msg + '</li>')
|
||||
.get(0).scrollTop = $('#messages').get(0).scrollHeight
|
||||
})
|
||||
poll()
|
||||
})
|
||||
})()
|
||||
})
|
||||
Diff do arquivo suprimido porque uma ou mais linhas são muito longas
@@ -1,10 +0,0 @@
|
||||
%h1 Chat
|
||||
%img.bubble{ src: '/public/images/bubble.png' }
|
||||
%ul#messages
|
||||
:each msg in messages
|
||||
%li= msg
|
||||
%form{ method: 'post' }
|
||||
%input{ type: 'hidden', name: '_method', value: 'put' }
|
||||
%input{ type: 'text', name: 'name', value: name || 'guest' }
|
||||
%input{ type: 'text', name: 'message' }
|
||||
%input{ type: 'submit', value: 'Send' }
|
||||
@@ -1,11 +0,0 @@
|
||||
%html
|
||||
%head
|
||||
%title= title
|
||||
%script{ src: '/public/javascripts/jquery.js' }
|
||||
%script{ src: '/public/javascripts/app.js' }
|
||||
%link{ rel: 'stylesheet', href: '/style.css' }
|
||||
%body
|
||||
#wrapper= body
|
||||
#online
|
||||
Online:
|
||||
%strong= usersOnline
|
||||
@@ -1,81 +0,0 @@
|
||||
body
|
||||
:font-family "Helvetica Neue", "Lucida Grande", "Arial"
|
||||
:font-size 13px
|
||||
:text-align center
|
||||
=text-stroke 1px rgba(255, 255, 255, 0.1)
|
||||
:color #555
|
||||
|
||||
h1, h2
|
||||
:margin 0
|
||||
:font-size 22px
|
||||
:color #343434
|
||||
h1
|
||||
:text-shadow 1px 2px 2px #ddd
|
||||
:font-size 60px
|
||||
|
||||
img.bubble
|
||||
:position absolute
|
||||
:top -25px
|
||||
:left 120px
|
||||
|
||||
#wrapper
|
||||
:position relative
|
||||
:margin 100px auto
|
||||
:width 500px
|
||||
:text-align left
|
||||
|
||||
ul
|
||||
:margin 0
|
||||
:padding 0
|
||||
:max-height 300px
|
||||
:overflow-x hidden
|
||||
li
|
||||
:margin 5px 0
|
||||
:padding 3px 8px
|
||||
:list-style none
|
||||
:border 1px solid #eee
|
||||
=border-radius 3px
|
||||
=border-radius 3px
|
||||
li:hover
|
||||
:cursor pointer
|
||||
:color #2E2E2E
|
||||
|
||||
input[type=text]
|
||||
:padding 5px
|
||||
:border 1px solid #ddd
|
||||
:outline none
|
||||
=border-radius 2px
|
||||
input[type=text]:focus
|
||||
:border-color #00C3FF
|
||||
|
||||
input[type=submit]
|
||||
=border-radius 2px
|
||||
=box-shadow 0 1px 2px #ddd
|
||||
:padding 6px 10px
|
||||
:border solid 1px #999
|
||||
:background -webkit-gradient(linear, 0% 0%, 0% 100%, from(#fff), to(#ddd))
|
||||
:color #333
|
||||
:text-decoration none
|
||||
:cursor pointer
|
||||
:display inline-block
|
||||
:text-align center
|
||||
:text-shadow 0px 1px 1px #fff
|
||||
:line-height 1
|
||||
input[type=submit]:hover
|
||||
:background -webkit-gradient(linear, 0% 0%, 0% 100%, from(#fff), to(#E6E4E4))
|
||||
input[type=submit]:active
|
||||
:background -webkit-gradient(linear, 0% 0%, 0% 100%, from(#fff), to(#c7c7c7))
|
||||
input[name=name]
|
||||
:width 80px
|
||||
|
||||
a
|
||||
:color #1ABFF1
|
||||
=transition-property padding
|
||||
=transition-duration 0.15s
|
||||
a:hover
|
||||
:padding 0 5px
|
||||
a:hover:before
|
||||
:content 'visit: '
|
||||
|
||||
#online
|
||||
:font-size 12px
|
||||
@@ -0,0 +1,29 @@
|
||||
|
||||
/**
|
||||
* Module dependencies.
|
||||
*/
|
||||
|
||||
var express = require('./../../lib/express');
|
||||
|
||||
var app = express.createServer();
|
||||
|
||||
// Optional since express defaults to CWD/views
|
||||
|
||||
app.set('views', __dirname + '/views');
|
||||
|
||||
// Dummy users
|
||||
var users = [
|
||||
{ name: 'tj', email: 'tj@sencha.com' },
|
||||
{ name: 'ciaran', email: 'ciaranj@gmail.com' },
|
||||
{ name: 'aaron', email: 'aaron.heckmann+github@gmail.com' }
|
||||
];
|
||||
|
||||
app.get('/', function(req, res){
|
||||
res.render('users.ejs', {
|
||||
locals: {
|
||||
users: users
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
app.listen(3000);
|
||||
@@ -0,0 +1,6 @@
|
||||
<html>
|
||||
<body>
|
||||
<h1>Users</h1>
|
||||
<%- body %>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1 @@
|
||||
<li><%= user.name %> <<%= user.email %>></li>
|
||||
@@ -0,0 +1,3 @@
|
||||
<ul id="users">
|
||||
<%- partial('user', users) %>
|
||||
</ul>
|
||||
@@ -0,0 +1,26 @@
|
||||
|
||||
/**
|
||||
* Module dependencies.
|
||||
*/
|
||||
|
||||
var express = require('./../../lib/express'),
|
||||
connect = require('connect');
|
||||
|
||||
var app = express.createServer();
|
||||
|
||||
app.get('/', function(req, res){
|
||||
// Caught and passed down to the errorHandler middleware
|
||||
throw new Error('something broke!');
|
||||
});
|
||||
|
||||
app.get('/next', function(req, res, params, next){
|
||||
// We can also pass exceptions to next()
|
||||
next(new Error('oh no!'))
|
||||
});
|
||||
|
||||
// The errorHandler middleware in this case will dump exceptions to stderr
|
||||
// as well as show the stack trace in responses, currently handles text/plain,
|
||||
// text/html, and application/json responses to aid in development
|
||||
app.use('/', connect.errorHandler({ dumpExceptions: true, showStack: true }));
|
||||
|
||||
app.listen(3000);
|
||||
@@ -0,0 +1,50 @@
|
||||
|
||||
/**
|
||||
* Module dependencies.
|
||||
*/
|
||||
|
||||
var express = require('./../../lib/express'),
|
||||
connect = require('connect'),
|
||||
sys = require('sys');
|
||||
|
||||
var app = express.createServer(
|
||||
// Here we use the bodyDecoder middleware
|
||||
// to parse urlencoded request bodies
|
||||
// which populates req.body
|
||||
connect.bodyDecoder(),
|
||||
|
||||
// The methodOverride middleware allows us
|
||||
// to set a hidden input of _method to an arbitrary
|
||||
// HTTP method to support app.put(), app.del() etc
|
||||
connect.methodOverride()
|
||||
);
|
||||
|
||||
app.get('/', function(req, res){
|
||||
// get ?name=foo
|
||||
var name = req.param('name') || '';
|
||||
|
||||
// Switch the button label based if we have a name
|
||||
var label = name ? 'Update' : 'Save';
|
||||
|
||||
// If we have a name, we are updating,
|
||||
// so add the hidden _method input
|
||||
res.send('<form method="post">'
|
||||
+ (name ? '<input type="hidden" value="put" name="_method" />' : '')
|
||||
+ 'Name: <input type="text" name="name" value="' + name + '" />'
|
||||
+ '<input type="submit" value="' + label + '" />'
|
||||
+ '</form>');
|
||||
});
|
||||
|
||||
app.post('/', function(req, res){
|
||||
// Typically here we would create a resource
|
||||
sys.puts('saved ' + req.body.name);
|
||||
res.redirect('/?name=' + req.body.name);
|
||||
});
|
||||
|
||||
app.put('/', function(req, res){
|
||||
// Typically here we would update a resource
|
||||
sys.puts('updated ' + req.body.name);
|
||||
res.redirect('/?name=' + req.body.name);
|
||||
});
|
||||
|
||||
app.listen(3000);
|
||||
@@ -0,0 +1,103 @@
|
||||
|
||||
/**
|
||||
* Module dependencies.
|
||||
*/
|
||||
|
||||
var express = require('./../../lib/express'),
|
||||
connect = require('connect'),
|
||||
http = require('http');
|
||||
|
||||
var app = express.createServer();
|
||||
|
||||
// Expose our views
|
||||
|
||||
app.set('views', __dirname + '/views');
|
||||
|
||||
/**
|
||||
* Request github json api `path`.
|
||||
*
|
||||
* @param {String} path
|
||||
* @param {Function} fn
|
||||
* @api public
|
||||
*/
|
||||
|
||||
function request(path, fn){
|
||||
var client = http.createClient(80, 'github.com'),
|
||||
req = client.request('GET', '/api/v2/json' + path, { Host: 'github.com' });
|
||||
req.addListener('response', function(res){
|
||||
res.body = '';
|
||||
res.addListener('data', function(chunk){ res.body += chunk; });
|
||||
res.addListener('end', function(){
|
||||
try {
|
||||
fn(null, JSON.parse(res.body));
|
||||
} catch (err) {
|
||||
fn(err);
|
||||
}
|
||||
});
|
||||
});
|
||||
req.end();
|
||||
}
|
||||
|
||||
/**
|
||||
* Sort repositories by watchers desc.
|
||||
*
|
||||
* @param {Array} repos
|
||||
* @api public
|
||||
*/
|
||||
|
||||
function sort(repos){
|
||||
return repos.sort(function(a, b){
|
||||
if (a.watchers == b.watchers) return 0;
|
||||
if (a.watchers > b.watchers) return -1;
|
||||
if (a.watchers < b.watchers) return 1;
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Tally up total watchers.
|
||||
*
|
||||
* @param {Array} repos
|
||||
* @return {Number}
|
||||
* @api public
|
||||
*/
|
||||
|
||||
function totalWatchers(repos) {
|
||||
return repos.reduce(function(sum, repo){
|
||||
return sum + repo.watchers;
|
||||
}, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Default to my user name :)
|
||||
*/
|
||||
|
||||
app.get('/', function(req, res){
|
||||
res.redirect('/repos/visionmedia');
|
||||
});
|
||||
|
||||
/**
|
||||
* Display repos.
|
||||
*/
|
||||
|
||||
app.get('/repos/:user', function(req, res, params, next){
|
||||
var name = params.user;
|
||||
request('/repos/show/' + name, function(err, user){
|
||||
if (err) {
|
||||
next(err)
|
||||
} else {
|
||||
res.render('index.jade', {
|
||||
locals: {
|
||||
totalWatchers: totalWatchers(user.repositories),
|
||||
repos: sort(user.repositories),
|
||||
name: name
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
// Serve statics from ./public
|
||||
app.use('/', connect.staticProvider(__dirname + '/public'));
|
||||
|
||||
// Listen on port 3000
|
||||
app.listen(3000);
|
||||
@@ -0,0 +1,11 @@
|
||||
body {
|
||||
padding: 30px 50px;
|
||||
font: 12px/1.4 "Lucida Grande", "Helvetica Nueue", Arial, sans-serif;
|
||||
}
|
||||
a {
|
||||
color: #00AAFF;
|
||||
text-decoration: none;
|
||||
}
|
||||
a:hover {
|
||||
text-decoration: underline;
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
h1= name
|
||||
p.summary
|
||||
| <a href="http://github.com/#{name}">#{name}</a> has <strong>#{repos.length}</strong> repositories
|
||||
| with a total of <strong>#{totalWatchers}</strong> watchers.
|
||||
table#repos!= partial('repo', repos)
|
||||
@@ -0,0 +1,7 @@
|
||||
!!!
|
||||
html
|
||||
head
|
||||
title Github Example
|
||||
link(rel="stylesheet", href="/style.css")
|
||||
body
|
||||
#container!= body
|
||||
@@ -0,0 +1,5 @@
|
||||
tr.repo
|
||||
td.name
|
||||
a(href: repo.homepage || repo.url)= repo.name
|
||||
td.watchers
|
||||
= repo.watchers
|
||||
@@ -0,0 +1,14 @@
|
||||
|
||||
/**
|
||||
* Module dependencies.
|
||||
*/
|
||||
|
||||
var express = require('./../../lib/express');
|
||||
|
||||
var app = express.createServer();
|
||||
|
||||
app.get('/', function(req, res){
|
||||
res.send('Hello World');
|
||||
});
|
||||
|
||||
app.listen(3000);
|
||||
@@ -0,0 +1,50 @@
|
||||
|
||||
/**
|
||||
* Module dependencies.
|
||||
*/
|
||||
|
||||
var express = require('./../../lib/express'),
|
||||
connect = require('connect');
|
||||
|
||||
// Path to our public directory
|
||||
|
||||
var pub = __dirname + '/public';
|
||||
|
||||
// Auto-compile sass to css with "compiler"
|
||||
// and then serve with connect's staticProvider
|
||||
|
||||
var app = express.createServer(
|
||||
connect.compiler({ src: pub, enable: ['sass'] }),
|
||||
connect.staticProvider(pub)
|
||||
);
|
||||
|
||||
// Optional since express defaults to CWD/views
|
||||
|
||||
app.set('views', __dirname + '/views');
|
||||
|
||||
// Enable auto-reloading of view contents when changed
|
||||
// with an interval of 1000 milliseconds. Start the app
|
||||
// with $ node examples/jade/app.js
|
||||
// then alter some views :)
|
||||
|
||||
app.set('reload views', 1000);
|
||||
// or app.enable('reload views'); for defaults
|
||||
|
||||
// Re-compile
|
||||
|
||||
// Dummy users
|
||||
var users = [
|
||||
{ name: 'tj', email: 'tj@sencha.com' },
|
||||
{ name: 'ciaran', email: 'ciaranj@gmail.com' },
|
||||
{ name: 'aaron', email: 'aaron.heckmann+github@gmail.com' }
|
||||
];
|
||||
|
||||
app.get('/', function(req, res){
|
||||
res.render('users.jade', {
|
||||
locals: {
|
||||
users: users
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
app.listen(3000);
|
||||
@@ -0,0 +1,3 @@
|
||||
body {
|
||||
padding: 50px 80px;
|
||||
font: 14px "Helvetica Nueue", "Lucida Grande", Arial, sans-serif;}
|
||||
@@ -0,0 +1,3 @@
|
||||
body
|
||||
:padding 50px 80px
|
||||
:font 14px "Helvetica Nueue", "Lucida Grande", Arial, sans-serif
|
||||
@@ -0,0 +1,6 @@
|
||||
!!!
|
||||
html
|
||||
head
|
||||
title Jade Example
|
||||
link(rel="stylesheet", href="/stylesheets/style.css")
|
||||
body!= body
|
||||
@@ -0,0 +1,3 @@
|
||||
.user
|
||||
h2= user.name
|
||||
.email= user.email
|
||||
@@ -0,0 +1,3 @@
|
||||
- if (users.length)
|
||||
h1 Users
|
||||
#users!= partial('user', users)
|
||||
@@ -0,0 +1,47 @@
|
||||
|
||||
/**
|
||||
* Module dependencies.
|
||||
*/
|
||||
|
||||
var express = require('./../../lib/express'),
|
||||
form = require('./../../support/connect-form'),
|
||||
connect = require('connect'),
|
||||
sys = require('sys');
|
||||
|
||||
var app = express.createServer(
|
||||
// connect-form (http://github.com/visionmedia/connect-form)
|
||||
// middleware uses the formidable middleware to parse urlencoded
|
||||
// and multipart form data
|
||||
form()
|
||||
);
|
||||
|
||||
app.get('/', function(req, res){
|
||||
res.send('<form method="post" enctype="form-data/multipart">'
|
||||
+ '<p>Image: <input type="file" name="image" /></p>'
|
||||
+ '<p><input type="submit" value="Upload" /></p>'
|
||||
+ '</form>');
|
||||
});
|
||||
|
||||
app.post('/', function(req, res, params, next){
|
||||
|
||||
// connect-form adds the req.form object
|
||||
// we can (optionally) define onComplete, passing
|
||||
// the exception (if any) fields parsed, and files parsed
|
||||
req.form.complete(function(err, fields, files){
|
||||
if (err) {
|
||||
next(err);
|
||||
} else {
|
||||
sys.puts('\nuploaded ' + files.image.filename);
|
||||
res.redirect('back');
|
||||
}
|
||||
});
|
||||
|
||||
// We can add listeners for several form
|
||||
// events such as "progress"
|
||||
req.form.addListener('progress', function(bytesReceived, bytesExpected){
|
||||
var percent = (bytesReceived / bytesExpected * 100) | 0;
|
||||
sys.print('Uploading: %' + percent + '\r');
|
||||
});
|
||||
});
|
||||
|
||||
app.listen(3000);
|
||||
@@ -0,0 +1,58 @@
|
||||
|
||||
/**
|
||||
* Module dependencies.
|
||||
*/
|
||||
|
||||
var express = require('./../../lib/express');
|
||||
|
||||
var app = express.createServer(),
|
||||
sys = require('sys');
|
||||
|
||||
app.set('views', __dirname + '/views');
|
||||
|
||||
// Provide our app with the notion of NotFound exceptions
|
||||
|
||||
function NotFound(msg){
|
||||
this.name = 'NotFound';
|
||||
Error.call(this, msg);
|
||||
Error.captureStackTrace(this, arguments.callee);
|
||||
}
|
||||
|
||||
sys.inherits(NotFound, Error);
|
||||
|
||||
app.get('/', function(req, res){
|
||||
res.render('index.jade');
|
||||
});
|
||||
|
||||
app.get('/404', function(req, res){
|
||||
throw new NotFound;
|
||||
});
|
||||
|
||||
app.get('/500', function(req, res){
|
||||
throw new Error('keyboard cat!');
|
||||
});
|
||||
|
||||
// We can call app.error() several times as shown below.
|
||||
// Here we check for an instanceof NotFound and show the
|
||||
// 404 page, or we pass on to the next error handler.
|
||||
|
||||
app.error(function(err, req, res, next){
|
||||
if (err instanceof NotFound) {
|
||||
res.render('404.jade');
|
||||
} else {
|
||||
next(err);
|
||||
}
|
||||
});
|
||||
|
||||
// Here we assume all errors as 500 for the simplicity of
|
||||
// this demo, however you can choose whatever you like
|
||||
|
||||
app.error(function(err, req, res){
|
||||
res.render('500.jade', {
|
||||
locals: {
|
||||
error: err
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
app.listen(3000);
|
||||
@@ -0,0 +1 @@
|
||||
h2 Page Not Found
|
||||
@@ -0,0 +1,2 @@
|
||||
h1 Error: #{error.message}
|
||||
pre= error.stack
|
||||
@@ -0,0 +1,8 @@
|
||||
h2 Pages Example
|
||||
ul
|
||||
li
|
||||
| visit
|
||||
a(href="/500") 500
|
||||
li
|
||||
| visit
|
||||
a(href="/404") 404
|
||||
@@ -0,0 +1,6 @@
|
||||
html
|
||||
head
|
||||
title Custom Pages Example
|
||||
body
|
||||
h1 My Site
|
||||
!= body
|
||||
@@ -1,36 +0,0 @@
|
||||
|
||||
require.paths.unshift('lib')
|
||||
require('express')
|
||||
require('express/plugins')
|
||||
|
||||
configure(function(){
|
||||
use(MethodOverride)
|
||||
use(ContentLength)
|
||||
use(CommonLogger)
|
||||
set('root', __dirname)
|
||||
})
|
||||
|
||||
get('/', function(){
|
||||
this.redirect('/upload')
|
||||
})
|
||||
|
||||
get('/upload', function(){
|
||||
this.render('upload.haml.html')
|
||||
})
|
||||
|
||||
post('/upload', function(){
|
||||
$(this.param('images')).each(function(image){
|
||||
puts('uploaded ' + image.filename + ' to ' + image.tempfile)
|
||||
})
|
||||
this.redirect('/upload')
|
||||
})
|
||||
|
||||
get('/public/*', function(file){
|
||||
this.sendfile(__dirname + '/public/' + file)
|
||||
})
|
||||
|
||||
get('/*.css', function(file){
|
||||
this.render(file + '.sass.css', { layout: false })
|
||||
})
|
||||
|
||||
run()
|
||||
Diff do arquivo suprimido porque uma ou mais linhas são muito longas
@@ -1,8 +0,0 @@
|
||||
%html
|
||||
%head
|
||||
%title Upload
|
||||
%script{ src: '/public/javascripts/jquery.js' }
|
||||
%script{ src: '/public/javascripts/app.js' }
|
||||
%link{ rel: 'stylesheet', href: '/style.css' }
|
||||
%body
|
||||
#wrapper= body
|
||||
@@ -1,59 +0,0 @@
|
||||
body
|
||||
:font-family "Helvetica Neue", "Lucida Grande", "Arial"
|
||||
:font-size 13px
|
||||
:text-align center
|
||||
:-webkit-text-stroke 1px rgba(255, 255, 255, 0.1)
|
||||
:color #555
|
||||
|
||||
h1, h2
|
||||
:margin 0 0 15px 0
|
||||
:font-size 22px
|
||||
:color #343434
|
||||
h1
|
||||
:text-shadow 1px 2px 2px #ddd
|
||||
:font-size 60px
|
||||
h2
|
||||
:margin-top 15px
|
||||
|
||||
#wrapper
|
||||
:position relative
|
||||
:margin 100px auto
|
||||
:width 500px
|
||||
:text-align left
|
||||
|
||||
input[type=file]
|
||||
:padding 5px
|
||||
:border 1px solid #ddd
|
||||
:outline none
|
||||
:-webkit-border-radius 2px
|
||||
:-moz-border-radius 2px
|
||||
input[type=file]:focus
|
||||
:border-color #00C3FF
|
||||
|
||||
input[type=submit]
|
||||
:-webkit-border-radius 2px
|
||||
:-moz-border-radius 2px
|
||||
:-webkit-box-shadow 0 1px 2px #ddd
|
||||
:-moz-box-shadow 0 1px 2px #ddd
|
||||
:padding 6px 10px
|
||||
:border solid 1px #999
|
||||
:background -webkit-gradient(linear, 0% 0%, 0% 100%, from(#fff), to(#ddd))
|
||||
:color #333
|
||||
:text-decoration none
|
||||
:cursor pointer
|
||||
:display inline-block
|
||||
:text-align center
|
||||
:text-shadow 0px 1px 1px #fff
|
||||
:line-height 1
|
||||
input[type=submit]:hover
|
||||
:background -webkit-gradient(linear, 0% 0%, 0% 100%, from(#fff), to(#E6E4E4))
|
||||
input[type=submit]:active
|
||||
:background -webkit-gradient(linear, 0% 0%, 0% 100%, from(#fff), to(#c7c7c7))
|
||||
input[name=name]
|
||||
:width 80px
|
||||
|
||||
form
|
||||
.panel
|
||||
:float left
|
||||
:width 100%
|
||||
:margin-bottom 15px
|
||||
@@ -1,19 +0,0 @@
|
||||
%h1 Upload
|
||||
:if typeof images !== 'undefined'
|
||||
.images
|
||||
:each img in images
|
||||
%img{ src: img }
|
||||
|
||||
%h2 Singles
|
||||
%form{ method: 'post', enctype: 'multipart/form-data' }
|
||||
%input{ type: 'file', name: 'images[0]' }
|
||||
%input{ type: 'file', name: 'images[1]' }
|
||||
%input{ type: 'file', name: 'images[2]' }
|
||||
.panel
|
||||
%input{ type: 'submit', value: 'Upload' }
|
||||
|
||||
%h2 Multiple
|
||||
%form{ method: 'post', enctype: 'multipart/form-data' }
|
||||
%input{ type: 'file', name: 'images[]', multiple: 'multiple' }
|
||||
.panel
|
||||
%input{ type: 'submit', value: 'Upload' }
|
||||
@@ -0,0 +1,2 @@
|
||||
|
||||
module.exports = require('./lib/express');
|
||||
Arquivo executável
+14
@@ -0,0 +1,14 @@
|
||||
|
||||
install() {
|
||||
mkdir -p /tmp/$2 \
|
||||
&& cd /tmp/$2 \
|
||||
&& echo "... installing $2" \
|
||||
&& curl -# -L "http://github.com/$1/$2/tarball/master" \
|
||||
| tar xz --strip 1 \
|
||||
&& cp -fr lib/$2 ~/.node_libraries/$2
|
||||
}
|
||||
|
||||
install visionmedia express \
|
||||
&& install senchalabs connect \
|
||||
&& cp -f /tmp/express/bin/express /usr/local/bin/express \
|
||||
&& echo "... installation complete"
|
||||
@@ -1,8 +0,0 @@
|
||||
|
||||
var path = require('path')
|
||||
require.paths.unshift(__dirname + '/support/js-oo/lib')
|
||||
require.paths.unshift(__dirname + '/support/ejs/lib')
|
||||
require.paths.unshift(__dirname + '/support/haml/lib')
|
||||
require.paths.unshift(__dirname + '/support/sass/lib')
|
||||
require('oo')
|
||||
require('express/core')
|
||||
@@ -1,543 +0,0 @@
|
||||
|
||||
// Express - Collection - Copyright TJ Holowaychuk <tj@vision-media.ca> (MIT Licensed)
|
||||
|
||||
/**
|
||||
* Throw $break in order to stop iteration.
|
||||
*/
|
||||
|
||||
$break = '__break__'
|
||||
|
||||
var property = /^\w+$/,
|
||||
method = /^\w+\(/
|
||||
|
||||
/**
|
||||
* Normalize callback _fn_. When a string is
|
||||
* passed convert the shorthand expr to a function.
|
||||
*
|
||||
* - Functions are passed through un-touched
|
||||
* - Strings with length of < 4 are considered operators between a and b
|
||||
* - Single words are considered properties on a
|
||||
* - Single functions are considered method calls on a
|
||||
* - Larger strings are considered return expressions
|
||||
*
|
||||
* @param {string, function} fn
|
||||
* @return {function}
|
||||
* @api private
|
||||
*/
|
||||
|
||||
function callback(fn) {
|
||||
if (fn === undefined) return
|
||||
if (fn instanceof Function) return fn
|
||||
if (fn.length < 4) return Function('a, b, c', 'return a ' + fn + ' b')
|
||||
if (property.test(fn) || method.test(fn)) fn = 'a.' + fn
|
||||
return Function('a, b, c', 'return ' + fn)
|
||||
}
|
||||
|
||||
// --- Collection
|
||||
|
||||
Collection = Class({
|
||||
|
||||
/**
|
||||
* Initialize collection with an array-like object.
|
||||
*
|
||||
* @param {object} arr
|
||||
* @api private
|
||||
*/
|
||||
|
||||
init: function(arr) {
|
||||
this.arr = arr || []
|
||||
},
|
||||
|
||||
/**
|
||||
* Return the value of _index_ or null.
|
||||
*
|
||||
* @param {int} index
|
||||
* @return {mixed}
|
||||
* @api public
|
||||
*/
|
||||
|
||||
at: function(index) {
|
||||
if ('length' in this.arr)
|
||||
return this.arr[index]
|
||||
var result, i = 0
|
||||
this.each(function(val){
|
||||
if (i++ == index) {
|
||||
result = val
|
||||
throw $break
|
||||
}
|
||||
})
|
||||
return result
|
||||
},
|
||||
|
||||
/**
|
||||
* Iterate collection using callback _fn_,
|
||||
* passing both the value and index.
|
||||
*
|
||||
* @param {function} fn
|
||||
* @return {Collection}
|
||||
* @api public
|
||||
*/
|
||||
|
||||
each: function(fn) {
|
||||
try {
|
||||
if (this.arr.forEach)
|
||||
this.arr.forEach(fn)
|
||||
else
|
||||
for (var key in this.arr)
|
||||
if (this.arr.hasOwnProperty(key))
|
||||
fn(this.arr[key], key)
|
||||
}
|
||||
catch (e) {
|
||||
if (e != $break) throw e
|
||||
}
|
||||
return this
|
||||
},
|
||||
|
||||
/**
|
||||
* Reverse a collection.
|
||||
*
|
||||
* @return {Collection}
|
||||
* @api public
|
||||
*/
|
||||
|
||||
reverse: function() {
|
||||
if (this.arr.reverse)
|
||||
return $(this.arr.reverse())
|
||||
return this
|
||||
},
|
||||
|
||||
/**
|
||||
* Join a collection with the given _str_ or ''.
|
||||
*
|
||||
* @param {string} str
|
||||
* @return {string}
|
||||
* @api public
|
||||
*/
|
||||
|
||||
join: function(str) {
|
||||
return this.toArray().join(str || '')
|
||||
},
|
||||
|
||||
/**
|
||||
* Sort collection with optional _fn_.
|
||||
*
|
||||
* @param {function} fn
|
||||
* @return {Collection}
|
||||
* @warn converts to array
|
||||
* @api public
|
||||
*/
|
||||
|
||||
sort: function(fn) {
|
||||
fn = callback(fn)
|
||||
return $(this.toArray().sort(fn))
|
||||
},
|
||||
|
||||
/**
|
||||
* Iterate with _memo_ using callback _fn_.
|
||||
* The _memo_ object is passed as the first
|
||||
* argument, and the return value of _fn_ becomes
|
||||
* the value of _memo_ providing a functional
|
||||
* approach to reducing a collection.
|
||||
*
|
||||
* @param {mixed} memo
|
||||
* @param {function} fn
|
||||
* @return {mixed}
|
||||
* @api public
|
||||
*/
|
||||
|
||||
reduce: function(memo, fn) {
|
||||
fn = callback(fn)
|
||||
if (this.arr.reduce)
|
||||
return this.arr.reduce(fn, memo)
|
||||
this.each(function(val, key){
|
||||
memo = fn(memo, val, key)
|
||||
})
|
||||
return memo
|
||||
},
|
||||
|
||||
/**
|
||||
* Map using callback _fn_, returning a
|
||||
* new collection of return values.
|
||||
*
|
||||
* @param {function} fn
|
||||
* @return {Collection}
|
||||
* @api public
|
||||
*/
|
||||
|
||||
map: function(fn) {
|
||||
fn = callback(fn)
|
||||
if (this.arr.map)
|
||||
return $(this.arr.map(fn))
|
||||
return $(this.reduce([], function(array, val, key){
|
||||
array.push(fn(val, key))
|
||||
return array
|
||||
}))
|
||||
},
|
||||
|
||||
/**
|
||||
* Return collection of values when _fn_ evaluates
|
||||
* to true.
|
||||
*
|
||||
* @param {function} fn
|
||||
* @return {Collection}
|
||||
* @api public
|
||||
*/
|
||||
|
||||
select: function(fn) {
|
||||
fn = callback(fn)
|
||||
return $(this.reduce([], function(array, val, key){
|
||||
if (fn(val, key))
|
||||
array.push(val)
|
||||
return array
|
||||
}))
|
||||
},
|
||||
|
||||
/**
|
||||
* Return collection of values when _fn_ evaluates
|
||||
* to false.
|
||||
*
|
||||
* @param {function} fn
|
||||
* @return {Collection}
|
||||
* @api public
|
||||
*/
|
||||
|
||||
reject: function(fn) {
|
||||
fn = callback(fn)
|
||||
return $(this.reduce([], function(array, val, key){
|
||||
if (!fn(val, key))
|
||||
array.push(val)
|
||||
return array
|
||||
}))
|
||||
},
|
||||
|
||||
/**
|
||||
* Check if all arguments passed are found within
|
||||
* the collection using the === operator.
|
||||
*
|
||||
* @return {bool}
|
||||
* @api public
|
||||
*/
|
||||
|
||||
includes: function() {
|
||||
var self = this
|
||||
return $(arguments).all(function(arg){
|
||||
return self.any(function(val){
|
||||
return val === arg
|
||||
})
|
||||
})
|
||||
},
|
||||
|
||||
/**
|
||||
* Return the first _n_ value(s), defaults to 1.
|
||||
*
|
||||
* @param {int} n
|
||||
* @return {mixed}
|
||||
* @api public
|
||||
*/
|
||||
|
||||
first: function(n) {
|
||||
return n ?
|
||||
this.slice(0, n) :
|
||||
this.at(0)
|
||||
},
|
||||
|
||||
/**
|
||||
* Return the last _n_ value(s), defaults to -1.
|
||||
*
|
||||
* @param {int} n
|
||||
* @return {mixed}
|
||||
* @api public
|
||||
*/
|
||||
|
||||
last: function(n) {
|
||||
var len = this.length()
|
||||
return n ?
|
||||
this.slice(len - n, len) :
|
||||
this.at(--len)
|
||||
},
|
||||
|
||||
/**
|
||||
* Drop the first _n_ values, returning the others.
|
||||
*
|
||||
* @param {int} n
|
||||
* @return {Collection}
|
||||
* @api public
|
||||
*/
|
||||
|
||||
drop: function(n) {
|
||||
return this.slice(n, this.length())
|
||||
},
|
||||
|
||||
/**
|
||||
* Return a slice of values from _start_ to _end_.
|
||||
*
|
||||
* @param {int} start
|
||||
* @param {int} end
|
||||
* @return {Collection}
|
||||
* @api public
|
||||
*/
|
||||
|
||||
slice: function(start, end) {
|
||||
if (this.arr.slice)
|
||||
return $(this.arr.slice(start, end))
|
||||
var i = 0
|
||||
return $(this.reduce([], function(array, val){
|
||||
if (i++ >= start)
|
||||
if (i <= end)
|
||||
array.push(val)
|
||||
else
|
||||
throw $break
|
||||
return array
|
||||
}))
|
||||
},
|
||||
|
||||
/**
|
||||
* Iterate until _fn_ evaluates to true, then
|
||||
* return the matching value.
|
||||
*
|
||||
* @param {function} fn
|
||||
* @return {mixed}
|
||||
* @api public
|
||||
*/
|
||||
|
||||
find: function(fn) {
|
||||
var result, fn = callback(fn)
|
||||
this.each(function(val, key){
|
||||
if (fn(val, key)) {
|
||||
result = val
|
||||
throw $break
|
||||
}
|
||||
})
|
||||
return result
|
||||
},
|
||||
|
||||
/**
|
||||
* Return true if _fn_ ever evaluates to true, otherwise
|
||||
* returns false.
|
||||
*
|
||||
* @param {function} fn
|
||||
* @return {bool}
|
||||
* @api public
|
||||
*/
|
||||
|
||||
any: function(fn) {
|
||||
fn = callback(fn)
|
||||
if (this.arr.some)
|
||||
return this.arr.some(fn)
|
||||
return !! this.find(fn)
|
||||
},
|
||||
|
||||
/**
|
||||
* Returns true if _fn_ always evaluates to true, otherwise
|
||||
* returns false.
|
||||
*
|
||||
* @param {function} fn
|
||||
* @return {bool}
|
||||
* @api public
|
||||
*/
|
||||
|
||||
all: function(fn) {
|
||||
fn = callback(fn)
|
||||
if (this.arr.every)
|
||||
return this.arr.every(fn)
|
||||
return this.reduce(true, function(state, val, key){
|
||||
if (!state) throw $break
|
||||
return !! fn(val, key)
|
||||
})
|
||||
},
|
||||
|
||||
/**
|
||||
* Select values matching _pattern_
|
||||
*
|
||||
* @param {regexp} pattern
|
||||
* @return {Collection}
|
||||
* @api public
|
||||
*/
|
||||
|
||||
grep: function(pattern) {
|
||||
return this.select(function(val){
|
||||
return pattern.exec(val)
|
||||
})
|
||||
},
|
||||
|
||||
/**
|
||||
* Return keys as a collection.
|
||||
*
|
||||
* @return {Collection}
|
||||
* @api public
|
||||
*/
|
||||
|
||||
keys: function() {
|
||||
return $(Object.keys(this.arr))
|
||||
},
|
||||
|
||||
/**
|
||||
* Convert a collection to a true array.
|
||||
* Works recursively
|
||||
*
|
||||
* @return {array}
|
||||
* @api public
|
||||
*/
|
||||
|
||||
toArray: function() {
|
||||
return this.reduce([], function(array, val){
|
||||
val instanceof Collection ?
|
||||
array.push(val.toArray()) :
|
||||
array.push(val)
|
||||
return array
|
||||
})
|
||||
},
|
||||
|
||||
/**
|
||||
* Return the lowest number in the collection.
|
||||
*
|
||||
* @return {number}
|
||||
* @api public
|
||||
*/
|
||||
|
||||
min: function() {
|
||||
return this.reduce(null, function(min, val){
|
||||
return min === null ? val :
|
||||
val < min ? val :
|
||||
min
|
||||
})
|
||||
},
|
||||
|
||||
/**
|
||||
* Return the largest number in the collection.
|
||||
*
|
||||
* @return {number}
|
||||
* @api public
|
||||
*/
|
||||
|
||||
max: function() {
|
||||
return this.reduce(null, function(max, val){
|
||||
return max === null ? val :
|
||||
val > max ? val :
|
||||
max
|
||||
})
|
||||
},
|
||||
|
||||
/**
|
||||
* Group collection into several collections grouped by _size_.
|
||||
*
|
||||
* @param {int} size
|
||||
* @return {Collection}
|
||||
* @api public
|
||||
*/
|
||||
|
||||
chunk: function(size) {
|
||||
var len = this.arr.length,
|
||||
chunks = [], chunk = [], i = 0
|
||||
this.each(function(val, key){
|
||||
chunk.push(val)
|
||||
if (i++ > 0 && (i % size == 0 || i == len))
|
||||
chunks.push($(chunk)),
|
||||
chunk = []
|
||||
})
|
||||
return $(chunks)
|
||||
},
|
||||
|
||||
/**
|
||||
* Return the length of this collection.
|
||||
*
|
||||
* @return {int}
|
||||
* @api public
|
||||
*/
|
||||
|
||||
length: function() {
|
||||
if ('length' in this.arr)
|
||||
return this.arr.length
|
||||
return this.reduce(0, function(len){
|
||||
return ++len
|
||||
})
|
||||
},
|
||||
|
||||
/**
|
||||
* Return the sum of numeric values in this collection.
|
||||
*
|
||||
* @return {number}
|
||||
* @api public
|
||||
*/
|
||||
|
||||
sum: function() {
|
||||
return this.reduce(0, function(sum, n){
|
||||
return sum + n
|
||||
})
|
||||
},
|
||||
|
||||
/**
|
||||
* Return the average of numeric values in this collection.
|
||||
*
|
||||
* @return {number}
|
||||
* @api public
|
||||
*/
|
||||
|
||||
avg: function() {
|
||||
return this.sum() / this.length()
|
||||
},
|
||||
|
||||
/**
|
||||
* Merge _other_ with this collection.
|
||||
*
|
||||
* @param {mixed} other
|
||||
* @return {Collection}
|
||||
* @api public
|
||||
*/
|
||||
|
||||
merge: function(other) {
|
||||
if (this.arr instanceof Array)
|
||||
return $($(other).reduce(this.arr, function(array, val){
|
||||
array.push(val)
|
||||
return array
|
||||
}))
|
||||
else
|
||||
return $(process.mixin(this.arr, $(other).arr))
|
||||
},
|
||||
|
||||
/**
|
||||
* Clone the collection.
|
||||
*
|
||||
* @return {Collection}
|
||||
* @api public
|
||||
*/
|
||||
|
||||
clone: function() {
|
||||
return this.merge({})
|
||||
},
|
||||
|
||||
/**
|
||||
* Return the value of a random index.
|
||||
*
|
||||
* @return {mixed}
|
||||
* @api public
|
||||
*/
|
||||
|
||||
sample: function() {
|
||||
return this.at(Math.floor(Math.random() * this.length()))
|
||||
},
|
||||
|
||||
/**
|
||||
* Convert collection to a string.
|
||||
*
|
||||
* @return {string}
|
||||
* @api public
|
||||
*/
|
||||
|
||||
toString: function() {
|
||||
return '[Collection ' + this.arr + ']'
|
||||
}
|
||||
})
|
||||
|
||||
/**
|
||||
* Create a new collection from an array-like object.
|
||||
*
|
||||
* @param {object} arr
|
||||
* @return {Collection}
|
||||
* @api public
|
||||
*/
|
||||
|
||||
var $ = exports.$ = function(arr) {
|
||||
if (arr instanceof Collection) return arr
|
||||
return new Collection(arr)
|
||||
}
|
||||
@@ -1,334 +0,0 @@
|
||||
|
||||
// Express - Core - Copyright TJ Holowaychuk <tj@vision-media.ca> (MIT Licensed)
|
||||
|
||||
process.mixin(require('sys'))
|
||||
process.mixin(require('express/exceptions'))
|
||||
process.mixin(require('express/collection'))
|
||||
process.mixin(require('express/event'))
|
||||
process.mixin(require('express/request'))
|
||||
process.mixin(require('express/plugin'))
|
||||
process.mixin(require('express/dsl'))
|
||||
|
||||
/**
|
||||
* Module dependencies.
|
||||
*/
|
||||
|
||||
var multipart = require('multipart'),
|
||||
events = require('events'),
|
||||
File = require('file').File,
|
||||
utils = require('express/utils')
|
||||
|
||||
// --- Route
|
||||
|
||||
Route = Class({
|
||||
|
||||
/**
|
||||
* Initialize a route with the given _method_,
|
||||
* _path_, and callback _fn_.
|
||||
*
|
||||
* The given _path_ becomes #originalPath,
|
||||
* #path is then a normalized version converted
|
||||
* to a regular expression for routing.
|
||||
*
|
||||
* @param {string} method
|
||||
* @param {string} path
|
||||
* @param {function} fn
|
||||
* @param {hash} options
|
||||
* @api private
|
||||
*/
|
||||
|
||||
init: function(method, path, fn, options){
|
||||
this.method = method
|
||||
this.originalPath = path
|
||||
this.path = this.normalize(path)
|
||||
this.fn = fn
|
||||
},
|
||||
|
||||
/**
|
||||
* Execute this route's #fn with _args_,
|
||||
* against _context_ or GLOBAL.
|
||||
*
|
||||
* @param {array} args
|
||||
* @return {mixed}
|
||||
* @api private
|
||||
*/
|
||||
|
||||
run: function(args, context) {
|
||||
return this.fn.apply(context || GLOBAL, args)
|
||||
},
|
||||
|
||||
/**
|
||||
* Normalize _path_. When a RegExp it is simply returned,
|
||||
* otherwise a string is converted to a regular expression
|
||||
* surrounded by ^$. So /user/:id/edit would become:
|
||||
*
|
||||
* /^\/user\/([^\/]+)\/edit$/i
|
||||
*
|
||||
* Each param key (:id) will be captured and placed in the
|
||||
* params array, so param('id') would give the string captured.
|
||||
*
|
||||
* The following are valid routes:
|
||||
*
|
||||
* - /user/:id Ex: '/user/12'
|
||||
* - /user/:id? Ex: '/user', '/user/12'
|
||||
* - /report.:format Ex: '/report.pdf', 'report.csv'
|
||||
* - /public/* Ex: '/public/app.js', '/public/javascripts/app.js'
|
||||
* - /public/*.* Ex: '/public/app.js', '/public/javascripts/app.js'
|
||||
*
|
||||
* @param {string} path
|
||||
* @return {regexp}
|
||||
* @api private
|
||||
*/
|
||||
|
||||
normalize: function(path) {
|
||||
var self = this
|
||||
this.keys = []
|
||||
if (path instanceof RegExp) return path
|
||||
return new RegExp('^' + utils.escapeRegexp(normalizePath(path), '.')
|
||||
.replace(/\*/g, '(.+)')
|
||||
.replace(/(\/|\\\.):(\w+)\?/g, function(_, c, key){
|
||||
self.keys.push(key)
|
||||
return '(?:' + c + '([^\/]+))?'
|
||||
})
|
||||
.replace(/:(\w+)/g, function(_, key){
|
||||
self.keys.push(key)
|
||||
return '([^\/]+)'
|
||||
}) + '$', 'i')
|
||||
}
|
||||
})
|
||||
|
||||
// --- Router
|
||||
|
||||
Router = Class({
|
||||
|
||||
/**
|
||||
* Initialize with _request_ and parse url.
|
||||
*
|
||||
* @param {Request} request
|
||||
* @api private
|
||||
*/
|
||||
|
||||
init: function(request) {
|
||||
this.request = request
|
||||
},
|
||||
|
||||
/**
|
||||
* Evaluate the matched route against #request.
|
||||
*
|
||||
* @return {mixed}
|
||||
* @api private
|
||||
*/
|
||||
|
||||
route: function(){
|
||||
var route = this.matchingRoute()
|
||||
if (route)
|
||||
return route.run(this.request.captures.slice(1), this.request)
|
||||
else if (this.request.accepts('html') && set('helpful 404'))
|
||||
this.request.halt(404, require('express/pages/not-found').render(this.request))
|
||||
else
|
||||
this.request.halt()
|
||||
},
|
||||
|
||||
/**
|
||||
* Attempt to match a route from Express.routes.
|
||||
*
|
||||
* @return {Route}
|
||||
* @api private
|
||||
*/
|
||||
|
||||
matchingRoute: function(){
|
||||
var self = this
|
||||
return $(Express.routes).find(function(route){
|
||||
return self.match(route)
|
||||
})
|
||||
},
|
||||
|
||||
/**
|
||||
* Check if _route_ matches the current request.
|
||||
* If so populate #captures and #params.
|
||||
*
|
||||
* @param {object} route
|
||||
* @return {bool}
|
||||
* @api private
|
||||
*/
|
||||
|
||||
match: function(route) {
|
||||
if (this.request.method.toLowerCase() == route.method)
|
||||
if (this.request.captures = this.request.url.pathname.match(route.path)) {
|
||||
this.mapParams(route)
|
||||
return true
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Map #request.captures to #request.params.path based on the
|
||||
* given _route_ keys.
|
||||
*
|
||||
* @param {Route} route
|
||||
* @api private
|
||||
*/
|
||||
|
||||
mapParams: function(route) {
|
||||
var self = this
|
||||
$(route.keys).each(function(key, i){
|
||||
self.request.params.path[key] = self.request.captures[++i]
|
||||
})
|
||||
}
|
||||
})
|
||||
|
||||
// --- Server
|
||||
|
||||
Server = Class({
|
||||
|
||||
/**
|
||||
* Default port number.
|
||||
*/
|
||||
|
||||
port: 3000,
|
||||
|
||||
/**
|
||||
* Default host ip, when null node will accept requests on
|
||||
* all network addresses.
|
||||
*/
|
||||
|
||||
host: 'localhost',
|
||||
|
||||
/**
|
||||
* Maximum number of queued connections.
|
||||
*/
|
||||
|
||||
backlog: 128,
|
||||
|
||||
/**
|
||||
* Run Express.
|
||||
*
|
||||
* - Buffers request bodies
|
||||
* - Calls #route() once the request is complete
|
||||
*
|
||||
* @param {int} port
|
||||
* @param {string} host
|
||||
* @param {int} backlog
|
||||
* @see run()
|
||||
* @api private
|
||||
*/
|
||||
|
||||
run: function(port, host, backlog){
|
||||
var self = this
|
||||
if (host !== undefined) this.host = host
|
||||
if (port !== undefined) this.port = port
|
||||
if (backlog !== undefined) this.backlog = backlog
|
||||
require('http')
|
||||
.createServer(function(request, response){
|
||||
request.body = ''
|
||||
request.setBodyEncoding('binary')
|
||||
if (request.headers['content-type'] &&
|
||||
request.headers['content-type'].indexOf('multipart/form-data') !== -1) {
|
||||
var stream = new multipart.Stream(request),
|
||||
promise = new events.Promise,
|
||||
pendingFiles = 0
|
||||
request.params = { post: {}}
|
||||
promise.timeout(set('upload timeout') || 5000)
|
||||
stream.addListener('part', function(part){
|
||||
if (part.filename) {
|
||||
var file = new File(part.tempfile = '/tmp/express-' + Number(new Date) + utils.uid(), 'w', { encoding: 'binary' })
|
||||
++pendingFiles
|
||||
part.pos = 0
|
||||
part.addListener('body', function(chunk){
|
||||
file.write(chunk, part.pos, 'binary').addErrback(function(){
|
||||
promise.emitError.apply(promise, arguments)
|
||||
})
|
||||
part.pos += chunk.length
|
||||
})
|
||||
.addListener('complete', function(){
|
||||
file.close().addCallback(function(){
|
||||
if (!--pendingFiles)
|
||||
promise.emitSuccess()
|
||||
}).addErrback(function(){
|
||||
promise.emitError.apply(promise, arguments)
|
||||
})
|
||||
utils.mergeParam(part.name, part, request.params.post)
|
||||
})
|
||||
}
|
||||
else
|
||||
part.buf = '',
|
||||
part
|
||||
.addListener('body', function(chunk){ part.buf += chunk })
|
||||
.addListener('complete', function(){
|
||||
if (part.buf.length)
|
||||
utils.mergeParam(part.name, part.buf, request.params.post)
|
||||
})
|
||||
}).addListener('complete', function(){
|
||||
if (!pendingFiles) promise.emitSuccess()
|
||||
promise.addCallback(function(){ self.route(request, response) })
|
||||
})
|
||||
}
|
||||
else
|
||||
request
|
||||
.addListener('body', function(chunk){ request.body += chunk })
|
||||
.addListener('complete', function(){ self.route(request, response) })
|
||||
})
|
||||
.listen(this.port, this.host, this.backlog)
|
||||
puts('Express started at http://' + this.host + ':' + this.port + '/ in ' + Express.environment + ' mode')
|
||||
},
|
||||
|
||||
/**
|
||||
* Route the given _request_ and _response_.
|
||||
*
|
||||
* @param {object} request
|
||||
* @param {object} response
|
||||
* @api private
|
||||
*/
|
||||
|
||||
route: function(request, response){
|
||||
request = new Request(request, response)
|
||||
request.trigger('request')
|
||||
try {
|
||||
if (typeof (body = (new Router(request)).route()) == 'string')
|
||||
request.halt(200, body)
|
||||
}
|
||||
catch (e) {
|
||||
if (e instanceof ExpressError)
|
||||
throw e
|
||||
if (request.accepts('html') && set('show exceptions'))
|
||||
request.halt(500, require('express/pages/show-exceptions').render(request, e))
|
||||
else
|
||||
request.halt(500)
|
||||
if (set('throw exceptions'))
|
||||
throw e
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
// --- Express
|
||||
|
||||
Express = {
|
||||
version: '0.2.0',
|
||||
config: [],
|
||||
routes: [],
|
||||
plugins: [],
|
||||
settings: {},
|
||||
server: new Server
|
||||
}
|
||||
|
||||
// --- Defaults
|
||||
|
||||
configure(function(){
|
||||
use(require('express/plugins/view').View)
|
||||
use(require('express/plugins/cache').Cache)
|
||||
use(require('express/plugins/redirect').Redirect)
|
||||
use(require('express/plugins/body-decoder').BodyDecoder)
|
||||
})
|
||||
|
||||
configure('development', function(){
|
||||
enable('helpful 404')
|
||||
enable('show exceptions')
|
||||
})
|
||||
|
||||
configure('test', function(){
|
||||
enable('throw exceptions')
|
||||
})
|
||||
|
||||
configure('production', function(){
|
||||
enable('cache view contents')
|
||||
enable('cache static files')
|
||||
})
|
||||
@@ -1,122 +0,0 @@
|
||||
|
||||
// Express - DSL - Copyright TJ Holowaychuk <tj@vision-media.ca> (MIT Licensed)
|
||||
|
||||
/**
|
||||
* Return a routing function for _method_.
|
||||
*
|
||||
* @param {string} method
|
||||
* @return {function}
|
||||
* @api private
|
||||
*/
|
||||
|
||||
function route(method) {
|
||||
return function(path, options, fn){
|
||||
if (options instanceof Function)
|
||||
fn = options, options = {}
|
||||
Express.routes.push(new Route(method, path, fn, options))
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Set _option_ to _val_. When only _option_ is
|
||||
* present its current value will be returned.
|
||||
*
|
||||
* @param {string} option
|
||||
* @param {mixed} val
|
||||
* @return {mixed}
|
||||
* @api public
|
||||
*/
|
||||
|
||||
exports.set = function(option, val) {
|
||||
return val === undefined ?
|
||||
Express.settings[option] instanceof Function ?
|
||||
Express.settings[option]() :
|
||||
Express.settings[option] :
|
||||
Express.settings[option] = val
|
||||
}
|
||||
|
||||
/**
|
||||
* Enable _options_.
|
||||
*
|
||||
* @param {string} option
|
||||
* @api public
|
||||
*/
|
||||
|
||||
exports.enable = function(option) {
|
||||
set(option, true)
|
||||
}
|
||||
|
||||
/**
|
||||
* Disable _option_.
|
||||
*
|
||||
* @param {string} option
|
||||
* @api public
|
||||
*/
|
||||
|
||||
exports.disable = function(option) {
|
||||
set(option, false)
|
||||
}
|
||||
|
||||
/**
|
||||
* Run Express, view Server#run() for parameters.
|
||||
*
|
||||
* All configuration handlers for EXPRESS_ENV or 'development'
|
||||
* are called before starting the server.
|
||||
*
|
||||
* @see Server#run()
|
||||
* @api public
|
||||
*/
|
||||
|
||||
exports.run = function() {
|
||||
configure(Express.environment = process.ENV.EXPRESS_ENV || 'development')
|
||||
$(Express.plugins).each(function(plugin){
|
||||
if ('init' in plugin.klass)
|
||||
plugin.klass.init(plugin.options)
|
||||
})
|
||||
Express.server.run.apply(Express.server, arguments)
|
||||
}
|
||||
|
||||
/**
|
||||
* Configure _environment_ with _fn_.
|
||||
*
|
||||
* Global configuration, disregards which
|
||||
* environment is active:
|
||||
*
|
||||
* configure(function(){
|
||||
* // ...
|
||||
* })
|
||||
*
|
||||
* Environment specific configuration:
|
||||
*
|
||||
* configure('development', function(){
|
||||
* // ...
|
||||
* })
|
||||
*
|
||||
* Running configurations:
|
||||
*
|
||||
* configure('development')
|
||||
*
|
||||
* @param {string, function} environment
|
||||
* @param {function} fn
|
||||
* @api public
|
||||
*/
|
||||
|
||||
exports.configure = function(environment, fn) {
|
||||
if (environment instanceof Function)
|
||||
fn = environment, environment = 'all'
|
||||
if (fn instanceof Function)
|
||||
return Express.config.push([environment, fn])
|
||||
if (typeof environment != 'string')
|
||||
throw new Error('environment required')
|
||||
for (var i = 0, len = Express.config.length; i < len; ++i)
|
||||
if (Express.config[i][0] == environment ||
|
||||
Express.config[i][0] == 'all')
|
||||
Express.config[i][1].call(Express)
|
||||
}
|
||||
|
||||
// --- Routing API
|
||||
|
||||
exports.get = exports.view = route('get')
|
||||
exports.post = exports.create = route('post')
|
||||
exports.del = exports.destroy = route('delete')
|
||||
exports.put = exports.update = route('put')
|
||||
@@ -1,179 +0,0 @@
|
||||
|
||||
// Express - ElementCollection - Copyright TJ Holowaychuk <tj@vision-media.ca> (MIT Licensed)
|
||||
|
||||
/**
|
||||
* Module dependencies.
|
||||
*/
|
||||
|
||||
var libxml = require('libxmljs')
|
||||
|
||||
// --- css2xpath
|
||||
|
||||
/** version 0.1 2009-04-30
|
||||
* @author Andrea Giammarchi
|
||||
* @license Mit Style License
|
||||
* @project http://code.google.com/p/css2xpath/
|
||||
*/
|
||||
var css2xpath=(function(){var b=[/\[([^\]~\$\*\^\|\!]+)(=[^\]]+)?\]/g,"[@$1$2]",/\s*,\s*/g,"|",/\s*(\+|~|>)\s*/g,"$1",/([a-zA-Z0-9\_\-\*])~([a-zA-Z0-9\_\-\*])/g,"$1/following-sibling::$2",/([a-zA-Z0-9\_\-\*])\+([a-zA-Z0-9\_\-\*])/g,"$1/following-sibling::*[1]/self::$2",/([a-zA-Z0-9\_\-\*])>([a-zA-Z0-9\_\-\*])/g,"$1/$2",/\[([^=]+)=([^'|"][^\]]*)\]/g,"[$1='$2']",/(^|[^a-zA-Z0-9\_\-\*])(#|\.)([a-zA-Z0-9\_\-]+)/g,"$1*$2$3",/([\>\+\|\~\,\s])([a-zA-Z\*]+)/g,"$1//$2",/\s+\/\//g,"//",/([a-zA-Z0-9\_\-\*]+):first-child/g,"*[1]/self::$1",/([a-zA-Z0-9\_\-\*]+):last-child/g,"$1[not(following-sibling::*)]",/([a-zA-Z0-9\_\-\*]+):only-child/g,"*[last()=1]/self::$1",/([a-zA-Z0-9\_\-\*]+):empty/g,"$1[not(*) and not(normalize-space())]",/([a-zA-Z0-9\_\-\*]+):not\(([^\)]*)\)/g,function(f,e,d){return e.concat("[not(",a(d).replace(/^[^\[]+\[([^\]]*)\].*$/g,"$1"),")]")},/([a-zA-Z0-9\_\-\*]+):nth-child\(([^\)]*)\)/g,function(f,e,d){switch(d){case"n":return e;case"even":return"*[position() mod 2=0 and position()>=0]/self::"+e;case"odd":return e+"[(count(preceding-sibling::*) + 1) mod 2=1]";default:d=(d||"0").replace(/^([0-9]*)n.*?([0-9]*)$/,"$1+$2").split("+");d[1]=d[1]||"0";return"*[(position()-".concat(d[1],") mod ",d[0],"=0 and position()>=",d[1],"]/self::",e)}},/:contains\(([^\)]*)\)/g,function(e,d){return"[contains(string(.),'"+d+"')]"},/\[([a-zA-Z0-9\_\-]+)\|=([^\]]+)\]/g,"[@$1=$2 or starts-with(@$1,concat($2,'-'))]",/\[([a-zA-Z0-9\_\-]+)\*=([^\]]+)\]/g,"[contains(@$1,$2)]",/\[([a-zA-Z0-9\_\-]+)~=([^\]]+)\]/g,"[contains(concat(' ',normalize-space(@$1),' '),concat(' ',$2,' '))]",/\[([a-zA-Z0-9\_\-]+)\^=([^\]]+)\]/g,"[starts-with(@$1,$2)]",/\[([a-zA-Z0-9\_\-]+)\$=([^\]]+)\]/g,function(f,e,d){return"[substring(@".concat(e,",string-length(@",e,")-",d.length-3,")=",d,"]")},/\[([a-zA-Z0-9\_\-]+)\!=([^\]]+)\]/g,"[not(@$1) or @$1!=$2]",/#([a-zA-Z0-9\_\-]+)/g,"[@id='$1']",/\.([a-zA-Z0-9\_\-]+)/g,"[contains(concat(' ',normalize-space(@class),' '),' $1 ')]",/\]\[([^\]]+)/g," and ($1)"],c=b.length;return function a(e){var d=0;while(d<c){e=e.replace(b[d++],b[d++])}return"//"+e}})();
|
||||
|
||||
// --- ElementCollection
|
||||
|
||||
ElementCollection = Collection.extend({
|
||||
|
||||
/**
|
||||
* Initialize with string of _markup_.
|
||||
*
|
||||
* @param {string} markup
|
||||
* @api private
|
||||
*/
|
||||
|
||||
init: function(markup) {
|
||||
if (typeof markup != 'string')
|
||||
return this.__super__(markup)
|
||||
if (!(/<html>/.test(markup)))
|
||||
markup = '<html><body>' + markup + '</body></html>'
|
||||
this.document = libxml.parseHtmlString(markup)
|
||||
this.arr = [this.document.root()]
|
||||
},
|
||||
|
||||
/**
|
||||
* Return the first element's name.
|
||||
*
|
||||
* @return {string}
|
||||
* @api public
|
||||
*/
|
||||
|
||||
name: function() {
|
||||
return this.at(0).name()
|
||||
},
|
||||
|
||||
/**
|
||||
* Search child elements with the given _xpath_.
|
||||
*
|
||||
* @param {string} xpath
|
||||
* @return {ElementCollection}
|
||||
* @api public
|
||||
*/
|
||||
|
||||
xpath: function(xpath) {
|
||||
// TODO: refactor with flatten()
|
||||
return $(this.reduce([], function(array, e){
|
||||
$(e.find(xpath)).each(function(child){
|
||||
array.push(child)
|
||||
})
|
||||
return array
|
||||
}))
|
||||
},
|
||||
|
||||
/**
|
||||
* Search child elements with the given css _selector_
|
||||
*
|
||||
* @param {string} selector
|
||||
* @return {ElementCollection}
|
||||
* @api public
|
||||
*/
|
||||
|
||||
search: function(selector) {
|
||||
return this.xpath(css2xpath(selector))
|
||||
},
|
||||
|
||||
/**
|
||||
* Return collection of children.
|
||||
*
|
||||
* @return {ElementCollection}
|
||||
* @api public
|
||||
*/
|
||||
|
||||
children: function() {
|
||||
// TODO: refactor with flatten()
|
||||
return $(this.reduce([], function(array, e){
|
||||
$(e.children()).each(function(child){
|
||||
array.push(child)
|
||||
})
|
||||
return array
|
||||
}))
|
||||
},
|
||||
|
||||
/**
|
||||
* Return collection of parents.
|
||||
*
|
||||
* @return {ElementCollection}
|
||||
* @api public
|
||||
*/
|
||||
|
||||
parents: function() {
|
||||
return this.map(function(e){
|
||||
return e.parent()
|
||||
})
|
||||
},
|
||||
|
||||
/**
|
||||
* Return the first element's parent.
|
||||
*
|
||||
* @return {ElementCollection}
|
||||
* @api public
|
||||
*/
|
||||
|
||||
parent: function() {
|
||||
return $([this.at(0).parent()])
|
||||
},
|
||||
|
||||
/**
|
||||
* Return the prev element.
|
||||
*
|
||||
* @return {ElementCollection}
|
||||
* @api public
|
||||
*/
|
||||
|
||||
prev: function() {
|
||||
return $([this.at(0).prevSibling()])
|
||||
},
|
||||
|
||||
/**
|
||||
* Return the next element.
|
||||
*
|
||||
* @return {ElementCollection}
|
||||
* @api public
|
||||
*/
|
||||
|
||||
next: function() {
|
||||
return $([this.at(0).nextSibling()])
|
||||
},
|
||||
|
||||
/**
|
||||
* Return the first element's text content.
|
||||
*
|
||||
* @return {string}
|
||||
* @api public
|
||||
*/
|
||||
|
||||
text: function() {
|
||||
return this.at(0).text()
|
||||
},
|
||||
|
||||
/**
|
||||
* Convert collection to a string.
|
||||
*
|
||||
* @return {string}
|
||||
* @api public
|
||||
*/
|
||||
|
||||
toString: function() {
|
||||
if (this.at(0) && this.at(0).doc)
|
||||
return '[Collection <elements>]'
|
||||
return this.__super__()
|
||||
}
|
||||
})
|
||||
|
||||
/**
|
||||
* Add markup support to $().
|
||||
*
|
||||
* @param {object} arr
|
||||
* @return {Collection, ElementCollection}
|
||||
* @api public
|
||||
*/
|
||||
|
||||
var $ = exports.$ = function(arr) {
|
||||
if (arr instanceof Collection) return arr
|
||||
return new ElementCollection(arr)
|
||||
}
|
||||
@@ -1,29 +0,0 @@
|
||||
|
||||
// Express - Event - Copyright TJ Holowaychuk <tj@vision-media.ca> (MIT Licensed)
|
||||
|
||||
exports.Event = Class({
|
||||
|
||||
/**
|
||||
* Initialize with event _name_ and optional _data_.
|
||||
*
|
||||
* @param {string} name
|
||||
* @param {hash} data
|
||||
* @api private
|
||||
*/
|
||||
|
||||
init: function(name, data) {
|
||||
this.name = name
|
||||
process.mixin(this, data)
|
||||
},
|
||||
|
||||
/**
|
||||
* Return event string.
|
||||
*
|
||||
* @return {string}
|
||||
* @api public
|
||||
*/
|
||||
|
||||
toString: function() {
|
||||
return '[Event ' + this.name + ']'
|
||||
}
|
||||
})
|
||||
@@ -1,14 +0,0 @@
|
||||
|
||||
// Express - Exceptions - Copyright TJ Holowaychuk <tj@vision-media.ca> (MIT Licensed)
|
||||
|
||||
// --- ExpressError
|
||||
|
||||
ExpressError = Class({
|
||||
name: 'ExpressError',
|
||||
init: function(message) {
|
||||
this.message = message
|
||||
},
|
||||
toString: function() {
|
||||
return this.name + ': ' + this.message
|
||||
}
|
||||
})
|
||||
@@ -0,0 +1,38 @@
|
||||
|
||||
/*!
|
||||
* Express
|
||||
* Copyright(c) 2010 TJ Holowaychuk <tj@vision-media.ca>
|
||||
* MIT Licensed
|
||||
*/
|
||||
|
||||
/**
|
||||
* Framework version.
|
||||
*/
|
||||
|
||||
exports.version = '1.0.0beta';
|
||||
|
||||
/**
|
||||
* Module dependencies.
|
||||
*/
|
||||
|
||||
var Server = require('./server');
|
||||
|
||||
/**
|
||||
* Shortcut for `new Server(...)`.
|
||||
*
|
||||
* @param {Function} ...
|
||||
* @return {Server}
|
||||
* @api public
|
||||
*/
|
||||
|
||||
exports.createServer = function(){
|
||||
return new Server(Array.prototype.slice.call(arguments));
|
||||
};
|
||||
|
||||
/**
|
||||
* View extensions.
|
||||
*/
|
||||
|
||||
require('./view');
|
||||
require('./request');
|
||||
require('./response');
|
||||
@@ -1,370 +0,0 @@
|
||||
|
||||
// Express - Mime - Copyright TJ Holowaychuk <tj@vision-media.ca> (MIT Licensed)
|
||||
|
||||
/**
|
||||
* Module dependencies.
|
||||
*/
|
||||
|
||||
var utils = require('express/utils')
|
||||
|
||||
/**
|
||||
* Mime type lookup table.
|
||||
*/
|
||||
|
||||
exports.types = {
|
||||
'323' : 'text/h323',
|
||||
'3gp' : 'video/3gpp',
|
||||
'a' : 'application/octet-stream',
|
||||
'acx' : 'application/internet-property-stream',
|
||||
'ai' : 'application/postscript',
|
||||
'aif' : 'audio/x-aiff',
|
||||
'aifc' : 'audio/x-aiff',
|
||||
'aiff' : 'audio/x-aiff',
|
||||
'asc' : 'application/pgp-signature',
|
||||
'asf' : 'video/x-ms-asf',
|
||||
'asr' : 'video/x-ms-asf',
|
||||
'asm' : 'text/x-asm',
|
||||
'asx' : 'video/x-ms-asf',
|
||||
'atom' : 'application/atom+xml',
|
||||
'au' : 'audio/basic',
|
||||
'avi' : 'video/x-msvideo',
|
||||
'axs' : 'application/olescript',
|
||||
'bas' : 'text/plain',
|
||||
'bat' : 'application/x-msdownload',
|
||||
'bcpio' : 'application/x-bcpio',
|
||||
'bin' : 'application/octet-stream',
|
||||
'bmp' : 'image/bmp',
|
||||
'bz2' : 'application/x-bzip2',
|
||||
'c' : 'text/x-c',
|
||||
'cab' : 'application/vnd.ms-cab-compressed',
|
||||
'cat' : 'application/vnd.ms-pkiseccat',
|
||||
'cc' : 'text/x-c',
|
||||
'cdf' : 'application/x-netcdf',
|
||||
'cer' : 'application/x-x509-ca-cert',
|
||||
'cgm' : 'image/cgm',
|
||||
'chm' : 'application/vnd.ms-htmlhelp',
|
||||
'class' : 'application/octet-stream',
|
||||
'clp' : 'application/x-msclip',
|
||||
'cmx' : 'image/x-cmx',
|
||||
'cod' : 'image/cis-cod',
|
||||
'com' : 'application/x-msdownload',
|
||||
'conf' : 'text/plain',
|
||||
'cpio' : 'application/x-cpio',
|
||||
'cpp' : 'text/x-c',
|
||||
'cpt' : 'application/mac-compactpro',
|
||||
'crd' : 'application/x-mscardfile',
|
||||
'crl' : 'application/pkix-crl',
|
||||
'crt' : 'application/x-x509-ca-cert',
|
||||
'csh' : 'application/x-csh',
|
||||
'css' : 'text/css',
|
||||
'csv' : 'text/csv',
|
||||
'cxx' : 'text/x-c',
|
||||
'dcr' : 'application/x-director',
|
||||
'deb' : 'application/x-debian-package',
|
||||
'der' : 'application/x-x509-ca-cert',
|
||||
'diff' : 'text/x-diff',
|
||||
'dir' : 'application/x-director',
|
||||
'djv' : 'image/vnd.djvu',
|
||||
'djvu' : 'image/vnd.djvu',
|
||||
'dll' : 'application/x-msdownload',
|
||||
'dmg' : 'application/octet-stream',
|
||||
'dms' : 'application/octet-stream',
|
||||
'doc' : 'application/msword',
|
||||
'dot' : 'application/msword',
|
||||
'dtd' : 'application/xml-dtd',
|
||||
'dv' : 'video/x-dv',
|
||||
'dvi' : 'application/x-dvi',
|
||||
'dxr' : 'application/x-director',
|
||||
'ear' : 'application/java-archive',
|
||||
'eml' : 'message/rfc822',
|
||||
'eps' : 'application/postscript',
|
||||
'etx' : 'text/x-setext',
|
||||
'evy' : 'application/envoy',
|
||||
'exe' : 'application/x-msdownload',
|
||||
'ez' : 'application/andrew-inset',
|
||||
'f' : 'text/x-fortran',
|
||||
'f77' : 'text/x-fortran',
|
||||
'f90' : 'text/x-fortran',
|
||||
'fif' : 'application/fractals',
|
||||
'flr' : 'x-world/x-vrml',
|
||||
'flv' : 'video/x-flv',
|
||||
'for' : 'text/x-fortran',
|
||||
'gem' : 'application/octet-stream',
|
||||
'gemspec' : 'text/x-script.ruby',
|
||||
'gif' : 'image/gif',
|
||||
'gram' : 'application/srgs',
|
||||
'grxml' : 'application/srgs+xml',
|
||||
'gtar' : 'application/x-gtar',
|
||||
'gz' : 'application/x-gzip',
|
||||
'h' : 'text/x-c',
|
||||
'hdf' : 'application/x-hdf',
|
||||
'hh' : 'text/x-c',
|
||||
'hlp' : 'application/winhlp',
|
||||
'hqx' : 'application/mac-binhex40',
|
||||
'hta' : 'application/hta',
|
||||
'htc' : 'text/x-component',
|
||||
'htm' : 'text/html',
|
||||
'html' : 'text/html',
|
||||
'htt' : 'text/webviewhtml',
|
||||
'ice' : 'x-conference/x-cooltalk',
|
||||
'ico' : 'image/vnd.microsoft.icon',
|
||||
'ics' : 'text/calendar',
|
||||
'ief' : 'image/ief',
|
||||
'ifb' : 'text/calendar',
|
||||
'iges' : 'model/iges',
|
||||
'igs' : 'model/iges',
|
||||
'iii' : 'application/x-iphone',
|
||||
'ins' : 'application/x-internet-signup',
|
||||
'isp' : 'application/x-internet-signup',
|
||||
'iso' : 'application/octet-stream',
|
||||
'jar' : 'application/java-archive',
|
||||
'java' : 'text/x-java-source',
|
||||
'jfif' : 'image/pipeg',
|
||||
'jnlp' : 'application/x-java-jnlp-file',
|
||||
'jp2' : 'image/jp2',
|
||||
'jpe' : 'image/jpeg',
|
||||
'jpeg' : 'image/jpeg',
|
||||
'jpg' : 'image/jpeg',
|
||||
'js' : 'application/javascript',
|
||||
'json' : 'application/json',
|
||||
'kar' : 'audio/midi',
|
||||
'latex' : 'application/x-latex',
|
||||
'lha' : 'application/octet-stream',
|
||||
'lsf' : 'video/x-la-asf',
|
||||
'lsx' : 'video/x-la-asf',
|
||||
'lzh' : 'application/octet-stream',
|
||||
'log' : 'text/plain',
|
||||
'm13' : 'application/x-msmediaview',
|
||||
'm14' : 'application/x-msmediaview',
|
||||
'm3u' : 'audio/x-mpegurl',
|
||||
'm4a' : 'audio/mp4a-latm',
|
||||
'm4b' : 'audio/mp4a-latm',
|
||||
'm4p' : 'audio/mp4a-latm',
|
||||
'm4u' : 'video/vnd.mpegurl',
|
||||
'm4v' : 'video/mp4',
|
||||
'mac' : 'image/x-macpaint',
|
||||
'man' : 'text/troff',
|
||||
'mathml' : 'application/mathml+xml',
|
||||
'mbox' : 'application/mbox',
|
||||
'mdb' : 'application/x-msaccess',
|
||||
'mdoc' : 'text/troff',
|
||||
'me' : 'text/troff',
|
||||
'mesh' : 'model/mesh',
|
||||
'mht' : 'message/rfc822',
|
||||
'mhtml' : 'message/rfc822',
|
||||
'mid' : 'audio/midi',
|
||||
'midi' : 'audio/midi',
|
||||
'mif' : 'application/vnd.mif',
|
||||
'mime' : 'message/rfc822',
|
||||
'mml' : 'application/mathml+xml',
|
||||
'mng' : 'video/x-mng',
|
||||
'mny' : 'application/x-msmoney',
|
||||
'mov' : 'video/quicktime',
|
||||
'movie' : 'video/x-sgi-movie',
|
||||
'mp2' : 'video/mpeg',
|
||||
'mp3' : 'audio/mpeg',
|
||||
'mp4' : 'video/mp4',
|
||||
'mp4v' : 'video/mp4',
|
||||
'mpa' : 'video/mpeg',
|
||||
'mpe' : 'video/mpeg',
|
||||
'mpeg' : 'video/mpeg',
|
||||
'mpg' : 'video/mpeg',
|
||||
'mpga' : 'audio/mpeg',
|
||||
'mpp' : 'application/vnd.ms-project',
|
||||
'mpv2' : 'video/mpeg',
|
||||
'ms' : 'text/troff',
|
||||
'msh' : 'model/mesh',
|
||||
'msi' : 'application/x-msdownload',
|
||||
'mvb' : 'application/x-msmediaview',
|
||||
'mxu' : 'video/vnd.mpegurl',
|
||||
'nc' : 'application/x-netcdf',
|
||||
'nws' : 'message/rfc822',
|
||||
'oda' : 'application/oda',
|
||||
'odp' : 'application/vnd.oasis.opendocument.presentation',
|
||||
'ods' : 'application/vnd.oasis.opendocument.spreadsheet',
|
||||
'odt' : 'application/vnd.oasis.opendocument.text',
|
||||
'ogg' : 'application/ogg',
|
||||
'p' : 'text/x-pascal',
|
||||
'p10' : 'application/pkcs10',
|
||||
'p12' : 'application/x-pkcs12',
|
||||
'p7b' : 'application/x-pkcs7-certificates',
|
||||
'p7c' : 'application/x-pkcs7-mime',
|
||||
'p7m' : 'application/x-pkcs7-mime',
|
||||
'p7r' : 'application/x-pkcs7-certreqresp',
|
||||
'p7s' : 'application/x-pkcs7-signature',
|
||||
'pas' : 'text/x-pascal',
|
||||
'pbm' : 'image/x-portable-bitmap',
|
||||
'pct' : 'image/pict',
|
||||
'pdb' : 'chemical/x-pdb',
|
||||
'pdf' : 'application/pdf',
|
||||
'pem' : 'application/x-x509-ca-cert',
|
||||
'pfx' : 'application/x-pkcs12',
|
||||
'pgm' : 'image/x-portable-graymap',
|
||||
'pgn' : 'application/x-chess-pgn',
|
||||
'pgp' : 'application/pgp-encrypted',
|
||||
'pic' : 'image/pict',
|
||||
'pict' : 'image/pict',
|
||||
'pkg' : 'application/octet-stream',
|
||||
'pko' : 'application/ynd.ms-pkipko',
|
||||
'pl' : 'text/x-script.perl',
|
||||
'pm' : 'text/x-script.perl-module',
|
||||
'pma' : 'application/x-perfmon',
|
||||
'pmc' : 'application/x-perfmon',
|
||||
'pml' : 'application/x-perfmon',
|
||||
'pmr' : 'application/x-perfmon',
|
||||
'pmw' : 'application/x-perfmon',
|
||||
'png' : 'image/png',
|
||||
'pnm' : 'image/x-portable-anymap',
|
||||
'pnt' : 'image/x-macpaint',
|
||||
'pntg' : 'image/x-macpaint',
|
||||
'pot' : 'application/vnd.ms-powerpoint',
|
||||
'ppm' : 'image/x-portable-pixmap',
|
||||
'pps' : 'application/vnd.ms-powerpoint',
|
||||
'ppt' : 'application/vnd.ms-powerpoint',
|
||||
'prf' : 'application/pics-rules',
|
||||
'ps' : 'application/postscript',
|
||||
'psd' : 'image/vnd.adobe.photoshop',
|
||||
'pub' : 'application/x-mspublisher',
|
||||
'py' : 'text/x-script.python',
|
||||
'qt' : 'video/quicktime',
|
||||
'qti' : 'image/x-quicktime',
|
||||
'qtif' : 'image/x-quicktime',
|
||||
'ra' : 'audio/x-pn-realaudio',
|
||||
'rake' : 'text/x-script.ruby',
|
||||
'ram' : 'audio/x-pn-realaudio',
|
||||
'rar' : 'application/x-rar-compressed',
|
||||
'ras' : 'image/x-cmu-raster',
|
||||
'rb' : 'text/x-script.ruby',
|
||||
'rdf' : 'application/rdf+xml',
|
||||
'rgb' : 'image/x-rgb',
|
||||
'rm' : 'application/vnd.rn-realmedia',
|
||||
'rmi' : 'audio/mid',
|
||||
'roff' : 'text/troff',
|
||||
'rpm' : 'application/x-redhat-package-manager',
|
||||
'rss' : 'application/rss+xml',
|
||||
'rtf' : 'application/rtf',
|
||||
'rtx' : 'text/richtext',
|
||||
'ru' : 'text/x-script.ruby',
|
||||
's' : 'text/x-asm',
|
||||
'scd' : 'application/x-msschedule',
|
||||
'sct' : 'text/scriptlet',
|
||||
'setpay' : 'application/set-payment-initiation',
|
||||
'setreg' : 'application/set-registration-initiation',
|
||||
'sgm' : 'text/sgml',
|
||||
'sgml' : 'text/sgml',
|
||||
'sh' : 'application/x-sh',
|
||||
'shar' : 'application/x-shar',
|
||||
'sig' : 'application/pgp-signature',
|
||||
'silo' : 'model/mesh',
|
||||
'sit' : 'application/x-stuffit',
|
||||
'skd' : 'application/x-koan',
|
||||
'skm' : 'application/x-koan',
|
||||
'skp' : 'application/x-koan',
|
||||
'skt' : 'application/x-koan',
|
||||
'smi' : 'application/smil',
|
||||
'smil' : 'application/smil',
|
||||
'snd' : 'audio/basic',
|
||||
'so' : 'application/octet-stream',
|
||||
'spc' : 'application/x-pkcs7-certificates',
|
||||
'spl' : 'application/x-futuresplash',
|
||||
'src' : 'application/x-wais-source',
|
||||
'sst' : 'application/vnd.ms-pkicertstore',
|
||||
'stl' : 'application/vnd.ms-pkistl',
|
||||
'stm' : 'text/html',
|
||||
'sv4cpio' : 'application/x-sv4cpio',
|
||||
'sv4crc' : 'application/x-sv4crc',
|
||||
'svg' : 'image/svg+xml',
|
||||
'svgz' : 'image/svg+xml',
|
||||
'swf' : 'application/x-shockwave-flash',
|
||||
't' : 'text/troff',
|
||||
'tar' : 'application/x-tar',
|
||||
'tbz' : 'application/x-bzip-compressed-tar',
|
||||
'tcl' : 'application/x-tcl',
|
||||
'tex' : 'application/x-tex',
|
||||
'texi' : 'application/x-texinfo',
|
||||
'texinfo' : 'application/x-texinfo',
|
||||
'text' : 'text/plain',
|
||||
'tgz' : 'application/x-compressed',
|
||||
'tif' : 'image/tiff',
|
||||
'tiff' : 'image/tiff',
|
||||
'torrent' : 'application/x-bittorrent',
|
||||
'tr' : 'text/troff',
|
||||
'trm' : 'application/x-msterminal',
|
||||
'tsv' : 'text/tab-seperated-values',
|
||||
'txt' : 'text/plain',
|
||||
'uls' : 'text/iuls',
|
||||
'ustar' : 'application/x-ustar',
|
||||
'vcd' : 'application/x-cdlink',
|
||||
'vcf' : 'text/x-vcard',
|
||||
'vcs' : 'text/x-vcalendar',
|
||||
'vrml' : 'model/vrml',
|
||||
'vxml' : 'application/voicexml+xml',
|
||||
'war' : 'application/java-archive',
|
||||
'wav' : 'audio/x-wav',
|
||||
'wbmp' : 'image/vnd.wap.wbmp',
|
||||
'wbxml' : 'application/vnd.wap.wbxml',
|
||||
'wcm' : 'application/vnd.ms-works',
|
||||
'wdb' : 'application/vnd.ms-works',
|
||||
'wks' : 'application/vnd.ms-works',
|
||||
'wma' : 'audio/x-ms-wma',
|
||||
'wmf' : 'application/x-msmetafile',
|
||||
'wml' : 'text/vnd.wap.wml',
|
||||
'wmls' : 'text/vnd.wap.wmlscript',
|
||||
'wmlsc' : 'application/vnd.wap.wmlscriptc',
|
||||
'wmv' : 'video/x-ms-wmv',
|
||||
'wmx' : 'video/x-ms-wmx',
|
||||
'wps' : 'application/vnd.ms-works',
|
||||
'wri' : 'application/x-mswrite',
|
||||
'wrl' : 'model/vrml',
|
||||
'wrz' : 'x-world/x-vrml',
|
||||
'wsdl' : 'application/wsdl+xml',
|
||||
'xaf' : 'x-world/x-vrml',
|
||||
'xbm' : 'image/x-xbitmap',
|
||||
'xht' : 'application/xhtml+xml',
|
||||
'xhtml' : 'application/xhtml+xml',
|
||||
'xla' : 'application/vnd.ms-excel',
|
||||
'xlc' : 'application/vnd.ms-excel',
|
||||
'xlm' : 'application/vnd.ms-excel',
|
||||
'xls' : 'application/vnd.ms-excel',
|
||||
'xlt' : 'application/vnd.ms-excel',
|
||||
'xml' : 'application/xml',
|
||||
'xof' : 'x-world/x-vrml',
|
||||
'xpm' : 'image/x-xpixmap',
|
||||
'xsl' : 'application/xml',
|
||||
'xslt' : 'application/xslt+xml',
|
||||
'xul' : 'application/vnd.mozilla.xul+xml',
|
||||
'xwd' : 'image/x-xwindowdump',
|
||||
'xyz' : 'chemical/x-xyz',
|
||||
'yaml' : 'text/yaml',
|
||||
'yml' : 'text/yaml',
|
||||
'z' : 'application/x-compress',
|
||||
'zip' : 'application/zip'
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the mime type associated with _path_,
|
||||
* where _path_ may be an extension, or full
|
||||
* file path.
|
||||
*
|
||||
* By default 'application/octet-stream' is returned,
|
||||
* however this can be altered using the 'default mime type'
|
||||
* setting.
|
||||
*
|
||||
* var mime = require('express/mime')
|
||||
* mime.type('png') // => 'image/png'
|
||||
* mime.type('.png') // => 'image/png'
|
||||
* mime.type('image.png') // => 'image/png'
|
||||
* mime.type('path/to/image.png') // => 'image/png'
|
||||
*
|
||||
* @param {string} path
|
||||
* @return {string}
|
||||
* @settings 'default mime type'
|
||||
* @api public
|
||||
*/
|
||||
|
||||
exports.type = function(path) {
|
||||
return exports.types[path] ||
|
||||
exports.types[utils.extname(path)] ||
|
||||
set('default mime type') ||
|
||||
'application/octet-stream'
|
||||
}
|
||||
@@ -1,29 +0,0 @@
|
||||
|
||||
// Express - Pages - Not Found - Copyright TJ Holowaychuk <tj@vision-media.ca> (MIT Licensed)
|
||||
|
||||
var style = require('express/pages/style').style
|
||||
|
||||
exports.render = function(request) {
|
||||
request.contentType('html')
|
||||
var method = request.method.toLowerCase(),
|
||||
path = request.url.pathname || '/'
|
||||
return '<html> \n\
|
||||
<head> \n\
|
||||
<title>Express -- Not Found</title> \n\
|
||||
' + style + ' \n\
|
||||
</head> \n\
|
||||
<body> \n\
|
||||
<div id="wrapper"> \n\
|
||||
<h1>Express</h1> \n\
|
||||
<h2><em>404</em> Not Found</h2> \n\
|
||||
<p>No routes were matched, try the route below.\n\
|
||||
For restful services ensure that you use(<strong>MethodOverride</strong>).</p> \n\
|
||||
<pre><code> \n\
|
||||
' + method + '(\'' + path + '\', function(){ \n\
|
||||
// Response logic \n\
|
||||
}) \n\
|
||||
</code></pre> \n\
|
||||
</div> \n\
|
||||
</body> \n\
|
||||
</html>'
|
||||
}
|
||||
@@ -1,78 +0,0 @@
|
||||
|
||||
// Express - Pages - Show Exceptions - Copyright TJ Holowaychuk <tj@vision-media.ca> (MIT Licensed)
|
||||
|
||||
var style = require('express/pages/style').style
|
||||
|
||||
/**
|
||||
* Return list items for exception _e_'s stack.
|
||||
*
|
||||
* @param {object} e
|
||||
* @return {string}
|
||||
* @api private
|
||||
*/
|
||||
|
||||
function stack(e) {
|
||||
if (e.stack)
|
||||
return $(e.stack.split('\n').slice(1)).map(function(val, i){
|
||||
if (!i)
|
||||
return '<li>' + val.replace(/^(.*?):/, '<span class="path">$1</span>:') + '</li>'
|
||||
return '<li>' + val
|
||||
.replace(/\(([^:]+)/, '(<span class="path">$1</span>')
|
||||
.replace(/(:\d+:\d+)/, '<span class="line">$1</span>') +
|
||||
'</li>'
|
||||
}).join('\n')
|
||||
}
|
||||
|
||||
/**
|
||||
* Return table rows for _hash_.
|
||||
*
|
||||
* @param {hash} hash
|
||||
* @return {string}
|
||||
* @api private
|
||||
*/
|
||||
|
||||
function hash(hash) {
|
||||
if (!$(hash).length()) return '<tr><td class="empty" colspan="2">Empty</td></tr>'
|
||||
return $(hash).map(function(val, key){
|
||||
return '<tr><td>' + key + ':</td><td>' + JSON.encode(val) + '</td></tr>'
|
||||
}).join('\n')
|
||||
}
|
||||
|
||||
exports.render = function(request, e) {
|
||||
request.contentType('html')
|
||||
return '<html> \n\
|
||||
<head> \n\
|
||||
<title>Express -- ' + e + '</title> \n\
|
||||
' + style + ' \n\
|
||||
</head> \n\
|
||||
<body> \n\
|
||||
<div id="wrapper"> \n\
|
||||
<h1>Express</h1> \n\
|
||||
<h2><em>500</em> ' + e + '</h2> \n\
|
||||
<ul id="stacktrace"> \n\
|
||||
' + stack(e) + ' \n\
|
||||
</ul> \n\
|
||||
<h3>Request</h3> \n\
|
||||
<table id="request-headers"> \n\
|
||||
' + hash(request.headers) + ' \n\
|
||||
</table> \n\
|
||||
<h3>Response</h3> \n\
|
||||
<table id="response-headers"> \n\
|
||||
' + hash(request.response.headers) + ' \n\
|
||||
</table> \n\
|
||||
<h3>Params</h3> \n\
|
||||
<table id="route-params"> \n\
|
||||
' + hash(request.params) + ' \n\
|
||||
</table> \n\
|
||||
<h3>GET</h3> \n\
|
||||
<table id="get-params"> \n\
|
||||
' + hash(request.url.params) + ' \n\
|
||||
</table> \n\
|
||||
<h3>POST</h3> \n\
|
||||
<table id="post-params"> \n\
|
||||
' + hash(request.url.post) + ' \n\
|
||||
</table> \n\
|
||||
</div> \n\
|
||||
</body> \n\
|
||||
</html>'
|
||||
}
|
||||
@@ -1,99 +0,0 @@
|
||||
|
||||
// Express - Pages - Style - Copyright TJ Holowaychuk <tj@vision-media.ca> (MIT Licensed)
|
||||
|
||||
exports.style = '<style> \n\
|
||||
body { \n\
|
||||
font-family: "Helvetica Neue", "Lucida Grande", "Arial"; \n\
|
||||
font-size: 13px; \n\
|
||||
text-align: center; \n\
|
||||
background: -webkit-gradient(linear, 0% 0%, 0% 100%, from(#fff), to(#ECE9E9)); \n\
|
||||
color: #555; \n\
|
||||
-webkit-text-stroke: 1px rgba(255, 255, 255, 0.1); \n\
|
||||
} \n\
|
||||
h1, h2, h3 { \n\
|
||||
margin: 0; \n\
|
||||
font-size: 22px; \n\
|
||||
color: #343434; \n\
|
||||
} \n\
|
||||
h1 em, h2 em { \n\
|
||||
padding: 0 5px; \n\
|
||||
font-weight: normal; \n\
|
||||
} \n\
|
||||
h1 { \n\
|
||||
text-shadow: 1px 2px 2px #ddd; \n\
|
||||
font-size: 60px; \n\
|
||||
} \n\
|
||||
h3 { \n\
|
||||
margin: 5px 0 10px 0; \n\
|
||||
padding-bottom: 5px; \n\
|
||||
border-bottom: 1px solid #eee; \n\
|
||||
font-size: 18px; \n\
|
||||
} \n\
|
||||
#wrapper { \n\
|
||||
margin: 50px auto; \n\
|
||||
width: 750px; \n\
|
||||
text-align: left; \n\
|
||||
} \n\
|
||||
ul { \n\
|
||||
margin: 0; \n\
|
||||
padding: 0; \n\
|
||||
} \n\
|
||||
ul li { \n\
|
||||
margin: 5px 0; \n\
|
||||
padding: 3px 8px; \n\
|
||||
list-style: none; \n\
|
||||
border: 1px solid #eee; \n\
|
||||
-webkit-border-radius: 3px; \n\
|
||||
-mox-border-radius: 3px; \n\
|
||||
-webkit-transition-property: color, padding; \n\
|
||||
-webkit-transition-duration: 0.1s; \n\
|
||||
} \n\
|
||||
ul li:hover { \n\
|
||||
padding: 3px 20px; \n\
|
||||
cursor: pointer; \n\
|
||||
color: #2E2E2E; \n\
|
||||
} \n\
|
||||
ul li .path { \n\
|
||||
padding-left: 5px; \n\
|
||||
font-weight: bold; \n\
|
||||
} \n\
|
||||
ul li .line { \n\
|
||||
padding-right: 5px; \n\
|
||||
font-style: italic; \n\
|
||||
} \n\
|
||||
ul li:first-child .path { \n\
|
||||
padding-left: 0; \n\
|
||||
} \n\
|
||||
p { \n\
|
||||
line-height: 1.5; \n\
|
||||
} \n\
|
||||
pre { \n\
|
||||
margin: 0; \n\
|
||||
padding: 10px; \n\
|
||||
border: 1px solid #eee; \n\
|
||||
-webkit-border-radius: 10px; \n\
|
||||
-moz-border-radius: 10px; \n\
|
||||
-webkit-box-shadow: 1px 1px 6px #ddd; \n\
|
||||
-moz-box-shadow: 1px 1px 6px #ddd; \n\
|
||||
} \n\
|
||||
table { \n\
|
||||
margin-bottom: 35px; \n\
|
||||
width: 100%; \n\
|
||||
border-collapse: collapse; \n\
|
||||
} \n\
|
||||
table td { \n\
|
||||
padding: 5px 10px; \n\
|
||||
font-size: 14px; \n\
|
||||
} \n\
|
||||
table tr { \n\
|
||||
border-bottom: 1px solid #fff; \n\
|
||||
} \n\
|
||||
table tr:last-child { \n\
|
||||
border-bottom: none; \n\
|
||||
} \n\
|
||||
table td:first-child { \n\
|
||||
width: 150px; \n\
|
||||
color: #343434; \n\
|
||||
} \n\
|
||||
</style> \n\
|
||||
'
|
||||
@@ -1,55 +0,0 @@
|
||||
|
||||
// Express - Plugin - Copyright TJ Holowaychuk <tj@vision-media.ca> (MIT Licensed)
|
||||
|
||||
/**
|
||||
* Push _plugin_ with _options_ to the plugin stack.
|
||||
* If _plugin_ has already been pushed, then it's options
|
||||
* will override any previously set.
|
||||
*
|
||||
* @param {Plugin} plugin
|
||||
* @param {hash} options
|
||||
* @api public
|
||||
*/
|
||||
|
||||
exports.use = function(plugin, options) {
|
||||
if (Express.environment === 'test' && 'init' in plugin)
|
||||
plugin.init(options)
|
||||
$(Express.plugins).each(function(other, i){
|
||||
if (other.klass === plugin)
|
||||
delete Express.plugins[i]
|
||||
})
|
||||
Express.plugins.push({
|
||||
klass: plugin,
|
||||
options: options
|
||||
})
|
||||
}
|
||||
|
||||
// --- Plugin
|
||||
|
||||
exports.Plugin = Class({
|
||||
|
||||
/**
|
||||
* Initialize with _options_.
|
||||
*
|
||||
* @param {hash} options
|
||||
* @api private
|
||||
*/
|
||||
|
||||
init: function(options) {
|
||||
if (options)
|
||||
process.mixin(this, options)
|
||||
},
|
||||
|
||||
/**
|
||||
* Trigger handler for the given _event_.
|
||||
*
|
||||
* @param {Event} event
|
||||
* @api private
|
||||
*/
|
||||
|
||||
trigger: function(event) {
|
||||
if ('on' in this)
|
||||
if (event.name in this.on)
|
||||
this.on[event.name].call(this, event)
|
||||
}
|
||||
})
|
||||
@@ -1,11 +0,0 @@
|
||||
|
||||
// Express - Plugins - Copyright TJ Holowaychuk <tj@vision-media.ca> (MIT Licensed)
|
||||
|
||||
process.mixin(require('express/plugins/hooks'))
|
||||
process.mixin(require('express/plugins/cache'))
|
||||
process.mixin(require('express/plugins/cookie'))
|
||||
process.mixin(require('express/plugins/session'))
|
||||
process.mixin(require('express/plugins/profiler'))
|
||||
process.mixin(require('express/plugins/common-logger'))
|
||||
process.mixin(require('express/plugins/content-length'))
|
||||
process.mixin(require('express/plugins/method-override'))
|
||||
@@ -1,22 +0,0 @@
|
||||
|
||||
// Express - BodyDecoder - Copyright TJ Holowaychuk <tj@vision-media.ca> (MIT Licensed)
|
||||
|
||||
var queryString = require('querystring')
|
||||
|
||||
exports.BodyDecoder = Plugin.extend({
|
||||
on: {
|
||||
|
||||
/**
|
||||
* Decodes common content-types, currently handles:
|
||||
*
|
||||
* - application/x-www-form-urlencoded
|
||||
*/
|
||||
|
||||
request: function(event) {
|
||||
var request = event.request
|
||||
if (request.header('content-type') &&
|
||||
request.header('content-type').indexOf('application/x-www-form-urlencoded') > -1)
|
||||
request.params.post = queryString.parseQuery(request.body)
|
||||
}
|
||||
}
|
||||
})
|
||||
@@ -1,218 +0,0 @@
|
||||
|
||||
// Express - Cache - Copyright TJ Holowaychuk <tj@vision-media.ca> (MIT Licensed)
|
||||
|
||||
// --- Cache
|
||||
|
||||
var Cache = Class({
|
||||
|
||||
/**
|
||||
* Initialize cache with _key_ and _val_.
|
||||
*/
|
||||
|
||||
init: function(key, val) {
|
||||
this.key = key
|
||||
this.val = val
|
||||
this.created = Number(new Date)
|
||||
}
|
||||
})
|
||||
|
||||
// --- Store
|
||||
|
||||
exports.Store = Class({
|
||||
|
||||
/**
|
||||
* Ensure that the given _key_ is a string.
|
||||
* Override in subclass to provide data-store specific functionality.
|
||||
*
|
||||
* @param {string} key
|
||||
* @param {string} val
|
||||
* @api public
|
||||
*/
|
||||
|
||||
set: function(key, val) {
|
||||
if (typeof key !== 'string') throw new Error(this.name + ' store #set() key must be a string')
|
||||
},
|
||||
|
||||
/**
|
||||
* Ensure that the given _key_ is a string.
|
||||
* Override in subclass to provide data-store specific functionality.
|
||||
*
|
||||
* @param {string} key
|
||||
* @api public
|
||||
*/
|
||||
|
||||
get: function(key) {
|
||||
if (typeof key !== 'string') throw new Error(this.name + 'store #get() key must be a string')
|
||||
},
|
||||
|
||||
/**
|
||||
* Convert to '[NAME Store]'.
|
||||
*
|
||||
* @return {string}
|
||||
* @api public
|
||||
*/
|
||||
|
||||
toString: function() {
|
||||
return '[' + this.name + ' Store]'
|
||||
}
|
||||
})
|
||||
|
||||
// --- Store.Memory
|
||||
|
||||
exports.Store.Memory = exports.Store.extend({
|
||||
|
||||
/**
|
||||
* Datastore name.
|
||||
*/
|
||||
|
||||
name: 'Memory',
|
||||
|
||||
/**
|
||||
* Initialize data.
|
||||
*/
|
||||
|
||||
init: function() {
|
||||
this.data = {}
|
||||
},
|
||||
|
||||
/**
|
||||
* Set the given _key_ to _val_, returning _val_.
|
||||
*
|
||||
* @param {string} key
|
||||
* @param {string} val
|
||||
* @return {string}
|
||||
* @api public
|
||||
*/
|
||||
|
||||
set: function(key, val) {
|
||||
this.__super__(key, val)
|
||||
return this.data[key] = new Cache(key, val), val
|
||||
},
|
||||
|
||||
/**
|
||||
* Get data found matching the given _key_.
|
||||
*
|
||||
* Examples:
|
||||
*
|
||||
* cache.get('page:front')
|
||||
* // => '<html>...</html>'
|
||||
*
|
||||
* cache.get('page:*')
|
||||
* // => { 'page:front': '<html>...</html>',
|
||||
* 'page:users': '<html>...</html>',
|
||||
* ... }
|
||||
*
|
||||
* @param {string} key
|
||||
* @return {mixed}
|
||||
* @api public
|
||||
*/
|
||||
|
||||
get: function(key) {
|
||||
this.__super__(key)
|
||||
if (key.indexOf('*') === -1)
|
||||
return this.data[key] instanceof Cache ?
|
||||
this.data[key].val :
|
||||
null
|
||||
var regexp = this.normalize(key)
|
||||
return $(this.data).reduce({}, function(vals, cache){
|
||||
if (regexp.test(cache.key))
|
||||
vals[cache.key] = cache.val
|
||||
return vals
|
||||
})
|
||||
},
|
||||
|
||||
/**
|
||||
* Clear data matching the given _key_.
|
||||
*
|
||||
* Examples:
|
||||
*
|
||||
* cache.clear('page:front')
|
||||
* cache.clear('page:*')
|
||||
*
|
||||
* @param {string} key
|
||||
* @api public
|
||||
*/
|
||||
|
||||
clear: function(key) {
|
||||
if (key.indexOf('*') === -1)
|
||||
return delete this.data[key]
|
||||
var regexp = this.normalize(key)
|
||||
for (var key in this.data)
|
||||
if (this.data.hasOwnProperty(key))
|
||||
if (regexp.test(key))
|
||||
delete this.data[key]
|
||||
},
|
||||
|
||||
/**
|
||||
* Reap caches older than _ms_.
|
||||
*
|
||||
* @param {int} ms
|
||||
* @api private
|
||||
*/
|
||||
|
||||
reap: function(ms) {
|
||||
var self = this,
|
||||
threshold = Number(new Date(Number(new Date) - ms))
|
||||
$(this.data).each(function(cache){
|
||||
if (cache.created < threshold)
|
||||
self.clear(cache.key)
|
||||
})
|
||||
},
|
||||
|
||||
/**
|
||||
* Convert the given key matching _pattern_
|
||||
* into a RegExp.
|
||||
*
|
||||
* - * is converted to (.*?)
|
||||
*
|
||||
* @param {string} pattern
|
||||
* @return {regexp}
|
||||
* @api private
|
||||
*/
|
||||
|
||||
normalize: function(pattern) {
|
||||
return new RegExp('^' + pattern.replace(/[*]/g, '(.*?)') + '$')
|
||||
}
|
||||
})
|
||||
|
||||
// --- Cache
|
||||
|
||||
exports.Cache = Plugin.extend({
|
||||
extend: {
|
||||
|
||||
/**
|
||||
* Initialize memory store and start reaper.
|
||||
*
|
||||
* Options:
|
||||
*
|
||||
* - dataStore constructor name of cache data store, defaults to Store.Memory
|
||||
* - lifetime lifetime of cache in milliseconds, defaults to one day
|
||||
* - reapInterval, reapEvery interval in milliseconds in which to reap old caches, defaults to one hour
|
||||
*
|
||||
* @param {hash} options
|
||||
* @api private
|
||||
*/
|
||||
|
||||
init: function(options) {
|
||||
process.mixin(this, options)
|
||||
this.store = new (this.dataStore || exports.Store.Memory)(options)
|
||||
Request.include({ cache: this.store })
|
||||
this.startReaper()
|
||||
},
|
||||
|
||||
/**
|
||||
* Start reaper.
|
||||
*
|
||||
* @api private
|
||||
*/
|
||||
|
||||
startReaper: function() {
|
||||
var self = this,
|
||||
oneDay = 86400000,
|
||||
oneHour = 3600000
|
||||
setInterval(function(){
|
||||
self.store.reap(self.lifetime || oneDay)
|
||||
}, self.reapInterval || self.reapEvery || oneHour)
|
||||
}
|
||||
}
|
||||
})
|
||||
@@ -1,50 +0,0 @@
|
||||
|
||||
// Express - CommonLogger - Copyright TJ Holowaychuk <tj@vision-media.ca> (MIT Licensed)
|
||||
|
||||
/**
|
||||
* Months.
|
||||
*/
|
||||
|
||||
var months = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec']
|
||||
|
||||
/**
|
||||
* Format _date_.
|
||||
*
|
||||
* @param {Date} date
|
||||
* @return {string}
|
||||
* @api private
|
||||
*/
|
||||
|
||||
function format(date) {
|
||||
var d = date.getDate(),
|
||||
m = months[date.getMonth()],
|
||||
y = date.getFullYear(),
|
||||
h = date.getHours(),
|
||||
mi = date.getMinutes(),
|
||||
s = date.getSeconds()
|
||||
return (d < 10 ? '0' : '') + d + '/' + m + '/' + y + ' ' +
|
||||
(h < 10 ? '0' : '') + h + ':' + (mi < 10 ? '0' : '') +
|
||||
mi + ':' + (s < 10 ? '0' : '') + s
|
||||
}
|
||||
|
||||
// --- CommonLogger
|
||||
|
||||
exports.CommonLogger = Plugin.extend({
|
||||
on: {
|
||||
|
||||
/**
|
||||
* Output log data.
|
||||
*/
|
||||
|
||||
response: function(event) {
|
||||
puts([event.request.headers.host,
|
||||
'-',
|
||||
'-',
|
||||
'[' + format(new Date) + ']',
|
||||
'"' + event.request.method.toUpperCase() + ' ' + (event.request.url.pathname || '/') +
|
||||
' HTTP/' + event.request.httpVersion + '"',
|
||||
event.request.response.status,
|
||||
event.request.response.headers['content-length'] || 0].join(' '))
|
||||
}
|
||||
}
|
||||
})
|
||||
@@ -1,18 +0,0 @@
|
||||
|
||||
// Express - ContentLength - Copyright TJ Holowaychuk <tj@vision-media.ca> (MIT Licensed)
|
||||
|
||||
exports.ContentLength = Plugin.extend({
|
||||
on: {
|
||||
|
||||
/**
|
||||
* Assign Content-Length header unless present.
|
||||
*/
|
||||
|
||||
response: function(event) {
|
||||
var response = event.request.response
|
||||
response.headers['content-length'] =
|
||||
response.headers['content-length'] ||
|
||||
response.body.length
|
||||
}
|
||||
}
|
||||
})
|
||||
@@ -1,110 +0,0 @@
|
||||
|
||||
// Express - Cookie - Copyright TJ Holowaychuk <tj@vision-media.ca> (MIT Licensed)
|
||||
|
||||
/**
|
||||
* Parse an HTTP _cookie_ string into a hash.
|
||||
*
|
||||
* @param {string} cookie
|
||||
* @return {hash}
|
||||
* @api public
|
||||
*/
|
||||
|
||||
exports.parseCookie = function(cookie) {
|
||||
return $(cookie.replace(/^ *| *$/g, '').split(/ *; */)).reduce({}, function(hash, pair){
|
||||
var parts = pair.split(/ *= */)
|
||||
hash[parts[0]] = parts[1]
|
||||
return hash
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* Compile cookie _name_, _val_ and _options_ to a string.
|
||||
*
|
||||
* @param {string} name
|
||||
* @param {string} val
|
||||
* @param {hash} options
|
||||
* @return {string}
|
||||
* @api public
|
||||
*/
|
||||
|
||||
exports.compileCookie = function(name, val, options) {
|
||||
if (!options) return name + '=' + val
|
||||
return name + '=' + val + '; ' + $(options).map(function(val, key){
|
||||
if (val instanceof Date)
|
||||
val = val.toString()
|
||||
.replace(/^(\w+)/, '$1,')
|
||||
.replace(/(\w+) (\d+) (\d+)/, '$2-$1-$3')
|
||||
.replace(/GMT.*$/, 'GMT')
|
||||
return val === true ? key : key + '=' + val
|
||||
}).toArray().join('; ')
|
||||
}
|
||||
|
||||
// --- Cookie
|
||||
|
||||
exports.Cookie = Plugin.extend({
|
||||
extend: {
|
||||
|
||||
/**
|
||||
* Initialize extensions.
|
||||
*/
|
||||
|
||||
init: function() {
|
||||
Request.include({
|
||||
|
||||
/**
|
||||
* Get or set cookie values.
|
||||
*
|
||||
* Options:
|
||||
*
|
||||
* - path Cookie path, defaults to '/'
|
||||
* - domain Tail matched domain name such as 'vision-media.ca' or 'blog.vision-media.ca' etc
|
||||
* - expires Date object converted to 'Wdy, DD-Mon-YYYY HH:MM:SS GMT'
|
||||
* when undefined the cookie will last the duration of a the
|
||||
* client's session.
|
||||
* - secure When true the cookie will be sent by the client only when transfering data via HTTPS
|
||||
* - httpOnly When true the cookie will be sent to the server only and will not be accessable via
|
||||
* client-side scripting.
|
||||
*
|
||||
* @param {string} name
|
||||
* @param {string} val
|
||||
* @param {hash} options
|
||||
* @return {string}
|
||||
* @api public
|
||||
*/
|
||||
|
||||
cookie: function(name, val, options) {
|
||||
options = options || {}
|
||||
options.path = options.path || '/'
|
||||
return val ?
|
||||
this.response.cookies.push(exports.compileCookie(name, val, options)) :
|
||||
this.cookies[name]
|
||||
}
|
||||
})
|
||||
}
|
||||
},
|
||||
|
||||
// --- Events
|
||||
|
||||
on: {
|
||||
|
||||
/**
|
||||
* Parse request cookie data.
|
||||
*/
|
||||
|
||||
request: function(event) {
|
||||
event.request.response.cookies = []
|
||||
event.request.cookies = event.request.headers.cookie ?
|
||||
exports.parseCookie(event.request.headers.cookie) :
|
||||
{}
|
||||
},
|
||||
|
||||
/**
|
||||
* Set the Set-Cookie header when response cookies are available.
|
||||
*/
|
||||
|
||||
response: function(event) {
|
||||
if (event.response.cookies.length)
|
||||
event.request.header('set-cookie', event.response.cookies.join(', '))
|
||||
}
|
||||
}
|
||||
})
|
||||
@@ -1,67 +0,0 @@
|
||||
|
||||
// Express - Hooks - Copyright TJ Holowaychuk <tj@vision-media.ca> (MIT Licensed)
|
||||
|
||||
var before = [],
|
||||
after = []
|
||||
|
||||
/**
|
||||
* Add a _fn_ to be excuted before a request.
|
||||
*
|
||||
* @param {function} fn
|
||||
* @api public
|
||||
*/
|
||||
|
||||
exports.before = function(fn) {
|
||||
before.push(fn)
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a _fn_ to be excuted after a request.
|
||||
*
|
||||
* @param {function} fn
|
||||
* @api public
|
||||
*/
|
||||
|
||||
exports.after = function(fn) {
|
||||
after.push(fn)
|
||||
}
|
||||
|
||||
// --- Hooks
|
||||
|
||||
exports.Hooks = Plugin.extend({
|
||||
extend: {
|
||||
|
||||
/**
|
||||
* Initialize extensions.
|
||||
*/
|
||||
|
||||
init: function() {
|
||||
process.mixin(GLOBAL, exports)
|
||||
}
|
||||
},
|
||||
|
||||
// --- Events
|
||||
|
||||
on: {
|
||||
|
||||
/**
|
||||
* Invoke all before handlers.
|
||||
*/
|
||||
|
||||
request: function(event) {
|
||||
$(before).each(function(fn){
|
||||
fn.call(event.request, event.request)
|
||||
})
|
||||
},
|
||||
|
||||
/**
|
||||
* Invoke all after handlers.
|
||||
*/
|
||||
|
||||
response: function(event) {
|
||||
$(after).each(function(fn){
|
||||
fn.call(event.request, event.request)
|
||||
})
|
||||
}
|
||||
}
|
||||
})
|
||||
@@ -1,16 +0,0 @@
|
||||
|
||||
// Express - MethodOverride - Copyright TJ Holowaychuk <tj@vision-media.ca> (MIT Licensed)
|
||||
|
||||
exports.MethodOverride = Plugin.extend({
|
||||
on: {
|
||||
|
||||
/**
|
||||
* Set request method to _method param when present.
|
||||
*/
|
||||
|
||||
request: function(event) {
|
||||
if (event.request.param('_method'))
|
||||
event.request.method = event.request.param('_method').toLowerCase()
|
||||
}
|
||||
}
|
||||
})
|
||||
@@ -1,51 +0,0 @@
|
||||
|
||||
// Express - Profiler - Copyright TJ Holowaychuk <tj@vision-media.ca> (MIT Licensed)
|
||||
|
||||
var n = 0
|
||||
|
||||
exports.Profiler = Plugin.extend({
|
||||
extend: {
|
||||
|
||||
/**
|
||||
* Initialize profiler options.
|
||||
*
|
||||
* Options:
|
||||
*
|
||||
* - format 'plot' outputs request duration in milliseconds only
|
||||
*
|
||||
* @param {hash} options
|
||||
* @api private
|
||||
*/
|
||||
|
||||
init: function(options) {
|
||||
process.mixin(this, options)
|
||||
}
|
||||
},
|
||||
|
||||
// --- Events
|
||||
|
||||
on: {
|
||||
|
||||
/**
|
||||
* Start timer.
|
||||
*/
|
||||
|
||||
request: function(event) {
|
||||
this.start = Number(new Date)
|
||||
},
|
||||
|
||||
/**
|
||||
* Output duration.
|
||||
*/
|
||||
|
||||
response: function(event) {
|
||||
if (exports.Profiler.format === 'plot')
|
||||
puts(Number(new Date) - this.start)
|
||||
else
|
||||
puts(event.request.method + ' ' +
|
||||
event.request.url.pathname + ': ' +
|
||||
(Number(new Date) - this.start) + ' ms' +
|
||||
' #' + ++n)
|
||||
}
|
||||
}
|
||||
})
|
||||
@@ -1,57 +0,0 @@
|
||||
|
||||
// Express - Redirect - Copyright TJ Holowaychuk <tj@vision-media.ca> (MIT Licensed)
|
||||
|
||||
exports.Redirect = Plugin.extend({
|
||||
extend: {
|
||||
|
||||
/**
|
||||
* Initialize extensions.
|
||||
*/
|
||||
|
||||
init: function() {
|
||||
Request.include({
|
||||
|
||||
/**
|
||||
* Redirect to _url_ with optional status _code_,
|
||||
* defaulting to 303 "See Other".
|
||||
*
|
||||
* When using redirect('home') the resolution
|
||||
* is as follows:
|
||||
*
|
||||
* - set('home')
|
||||
* - set('basepath')
|
||||
* - '/'
|
||||
*
|
||||
* When using redirect('back') the HTTP referrer
|
||||
* header is used. Commonly misspelled as 'referer'
|
||||
* which is supported as well.
|
||||
*
|
||||
* @param {string} url
|
||||
* @settings 'home', 'basepath'
|
||||
* @api public
|
||||
*/
|
||||
|
||||
redirect: function(url, code) {
|
||||
if (url == 'back' || url == 'home') url = this[url]
|
||||
this.header('location', url)
|
||||
this.halt(code || 303)
|
||||
}
|
||||
})
|
||||
}
|
||||
},
|
||||
|
||||
// --- Events
|
||||
|
||||
on: {
|
||||
|
||||
/**
|
||||
* Assign home to 'home', 'basepath' or '/'.
|
||||
* Assign back to 'referrer' or 'referer' headers.
|
||||
*/
|
||||
|
||||
request: function(event) {
|
||||
event.request.home = set('home') || set('basepath') || '/'
|
||||
event.request.back = event.request.header('referrer') || event.request.header('referer')
|
||||
}
|
||||
}
|
||||
})
|
||||
@@ -1,205 +0,0 @@
|
||||
|
||||
// Express - Session - Copyright TJ Holowaychuk <tj@vision-media.ca> (MIT Licensed)
|
||||
|
||||
/**
|
||||
* Module dependencies.
|
||||
*/
|
||||
|
||||
var utils = require('express/utils')
|
||||
|
||||
// --- Session
|
||||
|
||||
var Session = Class({
|
||||
|
||||
/**
|
||||
* Initialize session _sid_.
|
||||
*/
|
||||
|
||||
init: function(sid) {
|
||||
this.id = sid
|
||||
this.touch()
|
||||
},
|
||||
|
||||
/**
|
||||
* Update last access time.
|
||||
*
|
||||
* @api private
|
||||
*/
|
||||
|
||||
touch: function() {
|
||||
this.lastAccess = Number(new Date)
|
||||
}
|
||||
})
|
||||
|
||||
// --- Store
|
||||
|
||||
exports.Store = Class({
|
||||
|
||||
/**
|
||||
* Convert to '[NAME Store]'.
|
||||
*
|
||||
* @return {string}
|
||||
* @api public
|
||||
*/
|
||||
|
||||
toString: function() {
|
||||
return '[' + this.name + ' Store]'
|
||||
}
|
||||
})
|
||||
|
||||
// --- Store.Memory
|
||||
|
||||
exports.Store.Memory = exports.Store.extend({
|
||||
|
||||
/**
|
||||
* Datastore name.
|
||||
*/
|
||||
|
||||
name: 'Memory',
|
||||
|
||||
/**
|
||||
* Initialize in-memory session store.
|
||||
*/
|
||||
|
||||
init: function() {
|
||||
this.store = {}
|
||||
},
|
||||
|
||||
/**
|
||||
* Fetch session with the given _sid_ or
|
||||
* a new Session is returned.
|
||||
*
|
||||
* @param {int} sid
|
||||
* @return {Session}
|
||||
* @api private
|
||||
*/
|
||||
|
||||
fetch: function(sid) {
|
||||
return this.store[sid] || new Session(sid)
|
||||
},
|
||||
|
||||
/**
|
||||
* Commit _session_ data.
|
||||
*
|
||||
* @param {Session} session
|
||||
* @api private
|
||||
*/
|
||||
|
||||
commit: function(session) {
|
||||
return this.store[session.id] = session
|
||||
},
|
||||
|
||||
/**
|
||||
* Clear all sessions.
|
||||
*
|
||||
* @api public
|
||||
*/
|
||||
|
||||
clear: function() {
|
||||
this.store = {}
|
||||
},
|
||||
|
||||
/**
|
||||
* Destroy session using the given _sid_.
|
||||
*
|
||||
* @param {int} sid
|
||||
* @api public
|
||||
*/
|
||||
|
||||
destroy: function(sid) {
|
||||
delete this.store[sid]
|
||||
},
|
||||
|
||||
/**
|
||||
* Return the number of sessions currently stored.
|
||||
*
|
||||
* @return {int}
|
||||
* @api public
|
||||
*/
|
||||
|
||||
length: function() {
|
||||
return $(this.store).length()
|
||||
},
|
||||
|
||||
/**
|
||||
* Reap sessions older than _ms_.
|
||||
*
|
||||
* @param {int} ms
|
||||
* @api private
|
||||
*/
|
||||
|
||||
reap: function(ms) {
|
||||
var self = this,
|
||||
threshold = Number(new Date(Number(new Date) - ms))
|
||||
$(this.store).each(function(session, sid){
|
||||
if (session.lastAccess < threshold)
|
||||
self.destroy(sid)
|
||||
})
|
||||
}
|
||||
})
|
||||
|
||||
// --- Session
|
||||
|
||||
exports.Session = Plugin.extend({
|
||||
extend: {
|
||||
|
||||
/**
|
||||
* Initialize memory store and start reaper.
|
||||
*
|
||||
* Options:
|
||||
*
|
||||
* - dataStore constructor name of session data store, defaults to Store.Memory
|
||||
* - lifetime lifetime of session in milliseconds, defaults to one day
|
||||
* - reapInterval, reapEvery interval in milliseconds in which to reap old sessions, defaults to one hour
|
||||
*
|
||||
* @param {hash} options
|
||||
* @api private
|
||||
*/
|
||||
|
||||
init: function(options) {
|
||||
process.mixin(this, options)
|
||||
this.store = new (this.dataStore || exports.Store.Memory)(options)
|
||||
this.startReaper()
|
||||
},
|
||||
|
||||
/**
|
||||
* Start reaper.
|
||||
*
|
||||
* @api private
|
||||
*/
|
||||
|
||||
startReaper: function() {
|
||||
var self = this,
|
||||
oneDay = 86400000,
|
||||
oneHour = 3600000
|
||||
setInterval(function(){
|
||||
self.store.reap(self.lifetime || oneDay)
|
||||
}, self.reapInterval || self.reapEvery || oneHour)
|
||||
}
|
||||
},
|
||||
|
||||
// --- Events
|
||||
|
||||
on: {
|
||||
|
||||
/**
|
||||
* Create session id when not found; delegate to store.
|
||||
*/
|
||||
|
||||
request: function(event) {
|
||||
var sid
|
||||
if (!(sid = event.request.cookie('sid')))
|
||||
event.request.cookie('sid', sid = utils.uid(), set('session cookie'))
|
||||
event.request.session = exports.Session.store.fetch(sid)
|
||||
event.request.session.touch()
|
||||
},
|
||||
|
||||
/**
|
||||
* Delegate to store, allowing it to save sessions changes.
|
||||
*/
|
||||
|
||||
response: function(event) {
|
||||
exports.Session.store.commit(event.request.session)
|
||||
}
|
||||
}
|
||||
})
|
||||
@@ -1,99 +0,0 @@
|
||||
|
||||
// Express - View - Copyright TJ Holowaychuk <tj@vision-media.ca> (MIT Licensed)
|
||||
|
||||
/**
|
||||
* Module dependencies.
|
||||
*/
|
||||
|
||||
var posix = require('posix'),
|
||||
utils = require('express/utils')
|
||||
|
||||
/**
|
||||
* Supported template engines.
|
||||
*/
|
||||
|
||||
var engine = {
|
||||
ejs: require('ejs'),
|
||||
haml: require('haml'),
|
||||
sass: require('sass')
|
||||
}
|
||||
|
||||
// --- View
|
||||
|
||||
exports.View = Plugin.extend({
|
||||
extend: {
|
||||
|
||||
/**
|
||||
* Initialize extensions.
|
||||
*/
|
||||
|
||||
init: function() {
|
||||
|
||||
// Settings
|
||||
|
||||
set('views', function(){ return set('root') + '/views' })
|
||||
|
||||
// Request
|
||||
|
||||
Request.include({
|
||||
|
||||
/**
|
||||
* Render _view_ with _options_.
|
||||
*
|
||||
* Views are looked up relative to the'views' path setting.
|
||||
* View filenames should conform to ANY.ENGINE.TYPE so for example
|
||||
* 'layout.ejs.html', 'ejs' represents the template engine, 'html'
|
||||
* represents the type of content being rendered, which is then passed
|
||||
* to contentType().
|
||||
*
|
||||
* Engines must export a render() method accepting the template string
|
||||
* and a hash of options.
|
||||
*
|
||||
* Options:
|
||||
*
|
||||
* - layout: Whether or not to use a layout. Defaults to true
|
||||
* - context: Most engines support an evaluation context (the 'this' keyword)
|
||||
* - locals: Most engines support a hash of local variable names / values.
|
||||
*
|
||||
* @param {string} view
|
||||
* @param {hash} options
|
||||
* @settings 'views', 'cache view contents'
|
||||
* @api public
|
||||
*/
|
||||
|
||||
render: function(view, options) {
|
||||
var self = this,
|
||||
options = options || {},
|
||||
path = set('views') + '/' + view,
|
||||
type = path.split('.').slice(-2)[0],
|
||||
ext = utils.extname(path),
|
||||
layout = options.layout === undefined ? true : options.layout
|
||||
self.contentType(ext)
|
||||
function render(content) {
|
||||
content = engine[type].render(content, options)
|
||||
if (layout)
|
||||
self.render('layout.' + type + '.' + ext, process.mixin(true, options, {
|
||||
layout: false,
|
||||
locals: {
|
||||
body: content
|
||||
}
|
||||
}))
|
||||
else
|
||||
self.halt(200, content)
|
||||
}
|
||||
if (set('cache view contents') && self.cache.get(path))
|
||||
render(self.cache.get(path))
|
||||
else
|
||||
posix.cat(path).addCallback(function(content){
|
||||
set('cache view contents') ?
|
||||
render(self.cache.set(path, content)) :
|
||||
render(content)
|
||||
}).addErrback(function(e){
|
||||
throw e
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
+165
-247
@@ -1,262 +1,180 @@
|
||||
|
||||
// Express - Request - Copyright TJ Holowaychuk <tj@vision-media.ca> (MIT Licensed)
|
||||
/*!
|
||||
* Express - request
|
||||
* Copyright(c) 2010 TJ Holowaychuk <tj@vision-media.ca>
|
||||
* MIT Licensed
|
||||
*/
|
||||
|
||||
/**
|
||||
* Module dependencies.
|
||||
*/
|
||||
|
||||
var StaticFile = require('express/static').File,
|
||||
statusBodies = require('http').STATUS_CODES,
|
||||
queryString = require('querystring'),
|
||||
mime = require('express/mime'),
|
||||
url = require('url')
|
||||
|
||||
// --- InvalidStatusCode
|
||||
|
||||
InvalidStatusCode = ExpressError.extend({
|
||||
name: 'InvalidStatusCode',
|
||||
init: function(status) {
|
||||
this.message = status + ' is an invalid HTTP response code'
|
||||
}
|
||||
})
|
||||
|
||||
// --- InvalidResponseBody
|
||||
|
||||
InvalidResponseBody = ExpressError.extend({
|
||||
name: 'InvalidResponseBody',
|
||||
init: function(request) {
|
||||
this.message = request.method + ' ' + JSON.encode(request.url.pathname) + ' did not respond with a body string'
|
||||
}
|
||||
})
|
||||
|
||||
// --- Helpers
|
||||
var http = require('http'),
|
||||
utils = require('./utils'),
|
||||
mime = require('connect/utils').mime;
|
||||
|
||||
/**
|
||||
* Normalize the given _path_.
|
||||
* Strips trailing slashes and whitespace.
|
||||
* Return request header or optional default.
|
||||
*
|
||||
* @param {string} path
|
||||
* @return {string}
|
||||
* Examples:
|
||||
*
|
||||
* req.header('Content-Type');
|
||||
* // => "text/plain"
|
||||
*
|
||||
* req.header('content-type');
|
||||
* // => "text/plain"
|
||||
*
|
||||
* req.header('Accept');
|
||||
* // => undefined
|
||||
*
|
||||
* req.header('Accept', 'text/html');
|
||||
* // => "text/html"
|
||||
*
|
||||
* @param {String} name
|
||||
* @param {String} defaultValue
|
||||
* @return {String}
|
||||
* @api public
|
||||
*/
|
||||
|
||||
exports.normalizePath = function(path) {
|
||||
return path.replace(/[\s\/]*$/g, '')
|
||||
http.IncomingMessage.prototype.header = function(name, defaultValue){
|
||||
return this.headers[name.toLowerCase()] || defaultValue;
|
||||
};
|
||||
|
||||
/**
|
||||
* Check if the _Accept_ header is present, and includes the given `type`.
|
||||
*
|
||||
* When the _Accept_ header is not present `true` is returned. Otherwise
|
||||
* the given `type` is matched by an exact match, and then subtypes. You
|
||||
* may pass the subtype such as "html" which is then converted internally
|
||||
* to "text/html" using the mime lookup table.
|
||||
*
|
||||
* Examples:
|
||||
*
|
||||
* // Accept: text/html
|
||||
* req.accepts('html');
|
||||
* // => true
|
||||
*
|
||||
* // Accept: text/*; application/json
|
||||
* req.accepts('html');
|
||||
* req.accepts('text/html');
|
||||
* req.accepts('text/plain');
|
||||
* req.accepts('application/json');
|
||||
* // => true
|
||||
*
|
||||
* req.accepts('image/png');
|
||||
* req.accepts('png');
|
||||
* // => false
|
||||
*
|
||||
* @param {String} type
|
||||
* @return {Boolean}
|
||||
* @api public
|
||||
*/
|
||||
|
||||
http.IncomingMessage.prototype.accepts = function(type){
|
||||
var accept = this.header('Accept');
|
||||
if (!accept || accept === '*/*') {
|
||||
return true;
|
||||
} else if (type) {
|
||||
// Allow "html" vs "text/html" etc
|
||||
if (type.indexOf('/') < 0) {
|
||||
type = mime.types['.' + type];
|
||||
}
|
||||
// Check if we have a direct match
|
||||
if (accept.indexOf(type) >= 0) {
|
||||
return true;
|
||||
// Check if we have type/*
|
||||
} else {
|
||||
type = type.split('/')[0] + '/*';
|
||||
return accept.indexOf(type) >= 0;
|
||||
}
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Return the value of param `name` when present.
|
||||
*
|
||||
* - Checks route placeholders, ex: _/user/:id_
|
||||
* - Checks query string params, ex: ?id=12
|
||||
* - Checks urlencoded body params, ex: id=12
|
||||
*
|
||||
* To utilize urlencoded request bodies, `req.body`
|
||||
* should be an object. This can be done by using
|
||||
* the `connect.bodyDecoder` middleware.
|
||||
*
|
||||
* @param {String} name
|
||||
* @return {String}
|
||||
* @api public
|
||||
*/
|
||||
|
||||
http.IncomingMessage.prototype.param = function(name){
|
||||
// Route params like /user/:id
|
||||
if (this.params.path[name] !== undefined) {
|
||||
return this.params.path[name];
|
||||
}
|
||||
// Query string params
|
||||
if (this.params.get[name] !== undefined) {
|
||||
return this.params.get[name];
|
||||
}
|
||||
// Request body params via connect.bodyDecoder
|
||||
if (this.body && this.body[name] !== undefined) {
|
||||
return this.body[name];
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Queue flash `msg` of the given `type`.
|
||||
*
|
||||
* Examples:
|
||||
*
|
||||
* req.flash('info', 'email sent');
|
||||
* req.flash('error', 'email delivery failed');
|
||||
* req.flash('info', 'email re-sent');
|
||||
* // => 2
|
||||
*
|
||||
* req.flash('info');
|
||||
* // => ['email sent', 'email re-sent']
|
||||
*
|
||||
* req.flash('info');
|
||||
* // => []
|
||||
*
|
||||
* req.flash();
|
||||
* // => { error: ['email delivery failed'], info: [] }
|
||||
*
|
||||
* @param {String} type
|
||||
* @param {String} msg
|
||||
* @return {Array|Object|Number}
|
||||
* @api public
|
||||
*/
|
||||
|
||||
http.IncomingMessage.prototype.flash = function(type, msg){
|
||||
var msgs = this.session.flash = this.session.flash || {};
|
||||
if (type && msg) {
|
||||
msg = utils.miniMarkdown(utils.htmlEscape(msg));
|
||||
return (msgs[type] = msgs[type] || []).push(msg);
|
||||
} else if (type) {
|
||||
var arr = msgs[type];
|
||||
delete msgs[type];
|
||||
return arr || [];
|
||||
} else {
|
||||
this.session.flash = {};
|
||||
return msgs;
|
||||
}
|
||||
};
|
||||
|
||||
// Callback for isXMLHttpRequest / xhr
|
||||
|
||||
function isxhr() {
|
||||
return this.header('X-Requested-With', '').toLowerCase() === 'xmlhttprequest';
|
||||
}
|
||||
|
||||
// --- Request
|
||||
/**
|
||||
* Check if the request was an _XMLHttpRequest_.
|
||||
*
|
||||
* @return {Boolean}
|
||||
* @api public
|
||||
*/
|
||||
|
||||
exports.Request = Class({
|
||||
|
||||
/**
|
||||
* Initialize with node's _request_ and _response_ objects.
|
||||
*
|
||||
* - Defaults headers to {}
|
||||
* - Parses request url
|
||||
* - Normalizes url pathname
|
||||
* - Parses GET params when available
|
||||
* - Initializes plugins
|
||||
*
|
||||
* @param {object} request
|
||||
* @param {object} response
|
||||
* @api private
|
||||
*/
|
||||
|
||||
init: function(request, response) {
|
||||
process.mixin(true, this, request)
|
||||
response.headers = {}
|
||||
this.response = response
|
||||
this.url = url.parse(this.url)
|
||||
this.url.pathname = exports.normalizePath(this.url.pathname)
|
||||
this.params = this.params || {}
|
||||
this.params.path = {}
|
||||
this.params.get = this.url.query ? queryString.parseQuery(this.url.query) : {}
|
||||
this.params.post = this.params.post || {}
|
||||
this.plugins = $(Express.plugins).map(function(plugin){
|
||||
return new plugin.klass(plugin.options)
|
||||
})
|
||||
},
|
||||
|
||||
/**
|
||||
* Set response header _key_ and _val_ or get
|
||||
* request header value by _key_.
|
||||
*
|
||||
* @param {string} key
|
||||
* @param {string} val
|
||||
* @return {string}
|
||||
* @api public
|
||||
*/
|
||||
|
||||
header: function(key, val) {
|
||||
return val === undefined ?
|
||||
this.headers[key.toLowerCase()] :
|
||||
this.response.headers[key.toLowerCase()] = val
|
||||
},
|
||||
|
||||
/**
|
||||
* Return a param _key_ value or null.
|
||||
*
|
||||
* - Checks route populated path parameters
|
||||
* - Checks POST parameters
|
||||
* - Checks GET parameters
|
||||
*
|
||||
* @param {string} key
|
||||
* @return {string}
|
||||
* @api public
|
||||
*/
|
||||
|
||||
param: function(key) {
|
||||
return this.params.get[key] ||
|
||||
this.params.post[key] ||
|
||||
this.params.path[key]
|
||||
},
|
||||
|
||||
/**
|
||||
* Check if Accept header includes the mime type
|
||||
* for the given _path_, which calls mime.type().
|
||||
*
|
||||
* When no Accept header is present true will be
|
||||
* returned as stated in the HTTP specification.
|
||||
*
|
||||
* @param {string} path
|
||||
* @return {bool}
|
||||
* @api public
|
||||
*/
|
||||
|
||||
accepts: function(path) {
|
||||
return this.header('accept') ?
|
||||
this.header('accept').indexOf(mime.type(path)) !== -1 :
|
||||
true
|
||||
},
|
||||
|
||||
/**
|
||||
* Set response status to _code_.
|
||||
*
|
||||
* @param {int} code
|
||||
* @return {Request)
|
||||
* @api public
|
||||
*/
|
||||
|
||||
status: function(code) {
|
||||
this.response.status = code
|
||||
return this
|
||||
},
|
||||
|
||||
/**
|
||||
* Immediately respond with response _code_, _body_ and optional _encoding_.
|
||||
* The status _code_ defaults to to 404, and _body_ will
|
||||
* default to the default body associated with the response
|
||||
* _code_.
|
||||
*
|
||||
* When an invalid status _code_ is passed, InvalidStatusCode
|
||||
* will be thrown.
|
||||
*
|
||||
* @param {int} code
|
||||
* @param {string} body
|
||||
* @param {string} encoding
|
||||
* @see statusBodies
|
||||
* @api public
|
||||
*/
|
||||
|
||||
halt: function(code, body, encoding) {
|
||||
this.status(code = code || 404)
|
||||
if (body = body || statusBodies[code])
|
||||
return this.respond(body, encoding)
|
||||
throw new InvalidStatusCode(code)
|
||||
},
|
||||
|
||||
/**
|
||||
* Respond with _body_ and optional _encoding_.
|
||||
*
|
||||
* @param {string} body
|
||||
* @param {string} encoding
|
||||
* @see Request#halt()
|
||||
* @api private
|
||||
*/
|
||||
|
||||
respond: function(body, encoding) {
|
||||
this.response.body = body
|
||||
this.trigger('response')
|
||||
if (typeof this.response.body != 'string') throw new InvalidResponseBody(this)
|
||||
if (typeof this.response.status != 'number') throw new InvalidStatusCode(this.response.status)
|
||||
this.response.sendHeader(this.response.status, this.response.headers)
|
||||
this.response.sendBody(this.response.body, encoding)
|
||||
this.response.finish()
|
||||
},
|
||||
|
||||
/**
|
||||
* Set Content-Type header to the mime type
|
||||
* for the given _path_, which calls mime.type().
|
||||
*
|
||||
* @param {string} path
|
||||
* @return {Request}
|
||||
* @api public
|
||||
*/
|
||||
|
||||
contentType: function(path) {
|
||||
this.header('content-type', mime.type(path))
|
||||
return this
|
||||
},
|
||||
|
||||
/**
|
||||
* Trigger even _name_ with optional _data_.
|
||||
*
|
||||
* @param {string} name
|
||||
* @param {hash} data
|
||||
* @return {Request}
|
||||
* @api public
|
||||
*/
|
||||
|
||||
trigger: function(name, data) {
|
||||
data = process.mixin(data || {}, {
|
||||
request: this,
|
||||
response: this.response
|
||||
})
|
||||
this.plugins.each(function(plugin){
|
||||
plugin.trigger(new Event(name, data))
|
||||
})
|
||||
return this
|
||||
},
|
||||
|
||||
/**
|
||||
* Transfer static file at the given _path_ as an attachment.
|
||||
* The basename of _path_ is used as the attachment filename.
|
||||
*
|
||||
* @param {string} path
|
||||
* @return {Request}
|
||||
* @api public
|
||||
*/
|
||||
|
||||
download: function(path) {
|
||||
return this.attachment(basename(path)).sendfile(path)
|
||||
},
|
||||
|
||||
/**
|
||||
* Set Content-Disposition header to 'attachment',
|
||||
* with optional file _path_.
|
||||
*
|
||||
* @param {string} path
|
||||
* @return {Request}
|
||||
* @api public
|
||||
*/
|
||||
|
||||
attachment: function(path) {
|
||||
this.header('content-disposition', path ?
|
||||
'attachment; filename="' + path + '"' :
|
||||
'attachment')
|
||||
return this
|
||||
},
|
||||
|
||||
/**
|
||||
* Transfer static file at the given _path_.
|
||||
*
|
||||
* @param {string} path
|
||||
* @return {Request}
|
||||
* @api public
|
||||
*/
|
||||
|
||||
sendfile: function(path) {
|
||||
(new StaticFile(path)).send(this)
|
||||
return this
|
||||
}
|
||||
})
|
||||
http.IncomingMessage.prototype.__defineGetter__('isXMLHttpRequest', isxhr);
|
||||
http.IncomingMessage.prototype.__defineGetter__('xhr', isxhr);
|
||||
@@ -0,0 +1,235 @@
|
||||
|
||||
/*!
|
||||
* Express - response
|
||||
* Copyright(c) 2010 TJ Holowaychuk <tj@vision-media.ca>
|
||||
* MIT Licensed
|
||||
*/
|
||||
|
||||
/**
|
||||
* Module dependencies.
|
||||
*/
|
||||
|
||||
var fs = require('fs'),
|
||||
http = require('http'),
|
||||
path = require('path'),
|
||||
utils = require('connect/utils'),
|
||||
mime = require('connect/utils').mime,
|
||||
Buffer = require('buffer').Buffer;
|
||||
|
||||
/**
|
||||
* Send a response with the given `body` and optional `headers` and `status` code.
|
||||
*
|
||||
* Examples:
|
||||
*
|
||||
* res.send(new Buffer('wahoo'));
|
||||
* res.send({ some: 'json' });
|
||||
* res.send('<p>some html</p>');
|
||||
* res.send('Sorry, cant find that', 404);
|
||||
* res.send('text', { 'Content-Type': 'text/plain' }, 201);
|
||||
* res.send(404);
|
||||
*
|
||||
* @param {String|Object|Number|Buffer} body or status
|
||||
* @param {Object|Number} headers or status
|
||||
* @param {Number} status
|
||||
* @return {ServerResponse}
|
||||
* @api public
|
||||
*/
|
||||
|
||||
http.ServerResponse.prototype.send = function(body, headers, status){
|
||||
// Allow status as second arg
|
||||
if (typeof headers === 'number') {
|
||||
status = headers,
|
||||
headers = null;
|
||||
}
|
||||
|
||||
// Defaults
|
||||
status = status || 200;
|
||||
headers = headers || {};
|
||||
|
||||
// Determine content type
|
||||
switch (typeof body) {
|
||||
case 'number':
|
||||
if (!this.headers['Content-Type']) {
|
||||
this.contentType('.txt');
|
||||
}
|
||||
body = http.STATUS_CODES[status = body];
|
||||
break;
|
||||
case 'string':
|
||||
if (!this.headers['Content-Type']) {
|
||||
this.contentType('.html');
|
||||
}
|
||||
break;
|
||||
case 'object':
|
||||
if (body instanceof Buffer) {
|
||||
if (!this.headers['Content-Type']) {
|
||||
this.contentType('.bin');
|
||||
}
|
||||
} else {
|
||||
if (!this.headers['Content-Type']) {
|
||||
this.contentType('.json');
|
||||
}
|
||||
body = JSON.stringify(body);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
// Populate Content-Length
|
||||
if (!this.headers['Content-Length']) {
|
||||
this.header('Content-Length', body instanceof Buffer
|
||||
? body.length
|
||||
: Buffer.byteLength(body));
|
||||
}
|
||||
|
||||
// Merge headers passed
|
||||
utils.merge(this.headers, headers);
|
||||
|
||||
// Respond
|
||||
this.writeHead(status, this.headers);
|
||||
this.end(body);
|
||||
};
|
||||
|
||||
/**
|
||||
* Transfer the given `file`. Automatically sets the _Content-Type_
|
||||
* response header, and responds with 404 / 500 appropriately.
|
||||
*
|
||||
* @param {String} file
|
||||
* @api public
|
||||
*/
|
||||
|
||||
http.ServerResponse.prototype.sendfile = function(file){
|
||||
var self = this;
|
||||
fs.readFile(file, function(err, buf){
|
||||
if (err) {
|
||||
self.send(err.errno === process.ENOENT
|
||||
? 404
|
||||
: 500);
|
||||
} else {
|
||||
self.contentType(file);
|
||||
self.send(buf);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Set _Content-Type_ response header passed through `mime.type()`.
|
||||
*
|
||||
* Examples:
|
||||
*
|
||||
* var filename = 'path/to/image.png';
|
||||
* res.contentType(filename);
|
||||
* // res.headers['Content-Type'] is now "image/png"
|
||||
*
|
||||
* @param {String} type
|
||||
* @return {String} the resolved mime type
|
||||
* @api public
|
||||
*/
|
||||
|
||||
http.ServerResponse.prototype.contentType = function(type){
|
||||
return this.header('Content-Type', mime.type(type));
|
||||
};
|
||||
|
||||
/**
|
||||
* Set _Content-Disposition_ header to _attachment_ with optional `filename`.
|
||||
*
|
||||
* @param {String} filename
|
||||
* @return {ServerResponse}
|
||||
* @api public
|
||||
*/
|
||||
|
||||
http.ServerResponse.prototype.attachment = function(filename){
|
||||
this.header('Content-Disposition', filename
|
||||
? 'attachment; filename="' + path.basename(filename) + '"'
|
||||
: 'attachment');
|
||||
return this;
|
||||
};
|
||||
|
||||
/**
|
||||
* Transfer the given `file` with optional `filename` as an attachment.
|
||||
*
|
||||
* @param {String} file
|
||||
* @param {String} filename
|
||||
* @return {Type}
|
||||
* @api public
|
||||
*/
|
||||
|
||||
http.ServerResponse.prototype.download = function(file, filename){
|
||||
this.attachment(filename || file).sendfile(file);
|
||||
};
|
||||
|
||||
/**
|
||||
* Set or get response header `name` with optional `val`.
|
||||
*
|
||||
* @param {String} name
|
||||
* @param {String} val
|
||||
* @return {String}
|
||||
* @api public
|
||||
*/
|
||||
|
||||
http.ServerResponse.prototype.header = function(name, val){
|
||||
return val === undefined
|
||||
? this.headers[name]
|
||||
: this.headers[name] = val;
|
||||
};
|
||||
|
||||
/**
|
||||
* Redirect to the given `url` with optional response `status`
|
||||
* defauling to 302.
|
||||
*
|
||||
* The given `url` can also be the name of a mapped url, for
|
||||
* example by default express supports "back" which redirects
|
||||
* to the _Referrer_ or _Referer_ headers or the application's
|
||||
* "home" setting. Express also supports "home" out of the box,
|
||||
* which can be set via `app.set('home', '/blog');`, and defaults
|
||||
* to '/'.
|
||||
*
|
||||
* Redirect Mapping:
|
||||
*
|
||||
* To extend the redirect mapping capabilities that Express provides,
|
||||
* we may use the `app.redirect()` method:
|
||||
*
|
||||
* app.redirect('google', 'http://google.com');
|
||||
*
|
||||
* Now in a route we may call:
|
||||
*
|
||||
* res.redirect('google');
|
||||
*
|
||||
* We may also map dynamic redirects:
|
||||
*
|
||||
* app.redirect('comments', function(req, res, params){
|
||||
* return '/post/' + params.id + '/comments';
|
||||
* });
|
||||
*
|
||||
* So now we may do the following, and the redirect will dynamically adjust to
|
||||
* the context of the request. If we called this route with _GET /post/12_ our
|
||||
* redirect _Location_ would be _/post/12/comments_.
|
||||
*
|
||||
* app.get('/post/:id', function(req, res){
|
||||
* res.redirect('comments');
|
||||
* });
|
||||
*
|
||||
* @param {String} url
|
||||
* @param {Number} code
|
||||
* @api public
|
||||
*/
|
||||
|
||||
http.ServerResponse.prototype.redirect = function(url, status){
|
||||
var basePath = this.app.set('home') || '/';
|
||||
|
||||
// Setup redirect map
|
||||
var map = {
|
||||
back: this.req.headers.referrer || this.req.headers.referer || basePath,
|
||||
home: basePath
|
||||
};
|
||||
|
||||
// Support custom redirect map
|
||||
map.__proto__ = this.app.redirects;
|
||||
|
||||
// Attempt mapped redirect
|
||||
var mapped = typeof map[url] === 'function'
|
||||
? map[url](this.req, this, this.req.params.path)
|
||||
: map[url];
|
||||
|
||||
// Perform redirect
|
||||
this.writeHead(status || 302, { 'Location': mapped || url });
|
||||
this.end();
|
||||
};
|
||||
@@ -0,0 +1,196 @@
|
||||
|
||||
/*!
|
||||
* Express - Server
|
||||
* Copyright(c) 2010 TJ Holowaychuk <tj@vision-media.ca>
|
||||
* MIT Licensed
|
||||
*/
|
||||
|
||||
/**
|
||||
* Module dependencies.
|
||||
*/
|
||||
|
||||
var sys = require('sys'),
|
||||
url = require('url'),
|
||||
view = require('./view'),
|
||||
connect = require('connect'),
|
||||
queryString = require('querystring'),
|
||||
router = require('connect/middleware/router');
|
||||
|
||||
/**
|
||||
* Initialize a new `Server` with optional `middleware`.
|
||||
*
|
||||
* @param {Array} middleware
|
||||
* @api public
|
||||
*/
|
||||
|
||||
var Server = exports = module.exports = function Server(middleware){
|
||||
var self = this;
|
||||
this.config = {};
|
||||
this.settings = {};
|
||||
this.redirects = {};
|
||||
connect.Server.call(this, middleware || []);
|
||||
|
||||
// Expose objects to each other
|
||||
this.use('/', function(req, res, next){
|
||||
req.params = req.params || {};
|
||||
req.params.get = {};
|
||||
res.headers = {};
|
||||
req.app = res.app = self;
|
||||
req.res = res;
|
||||
res.req = req;
|
||||
// Assign req.params.get
|
||||
if (req.url.indexOf('?') > 0) {
|
||||
// TODO: consider simple substr to increase performance
|
||||
var query = url.parse(req.url).query;
|
||||
req.params.get = queryString.parse(query);
|
||||
}
|
||||
next();
|
||||
});
|
||||
|
||||
// Use router, expose as app.get(), etc
|
||||
this.use('/', router(function(app){
|
||||
self.routes = app;
|
||||
}));
|
||||
};
|
||||
|
||||
/**
|
||||
* Inherit from `connect.Server`.
|
||||
*/
|
||||
|
||||
sys.inherits(Server, connect.Server);
|
||||
|
||||
/**
|
||||
* Start listening on the given `port` / `host`.
|
||||
*
|
||||
* @param {Number} port
|
||||
* @param {String} host
|
||||
* @api public
|
||||
*/
|
||||
|
||||
Server.prototype.listen = function(port, host){
|
||||
this.set('env', process.env.EXPRESS_ENV || process.connectEnv.name);
|
||||
this.runConfig('any', this.set('env'));
|
||||
|
||||
// Setup view reloading
|
||||
if (this.set('reload views')) {
|
||||
view.watcher.call(this, this.set('reload views'));
|
||||
}
|
||||
|
||||
connect.Server.prototype.listen.call(this, port, host);
|
||||
};
|
||||
|
||||
/**
|
||||
* Run config callbacks for the given environment(s);
|
||||
*
|
||||
* @param {String} env ...
|
||||
* @return {Server} for chaining
|
||||
* @api private
|
||||
*/
|
||||
|
||||
Server.prototype.runConfig = function(){
|
||||
for (var i = 0, len = arguments.length; i < len; ++i) {
|
||||
var env = arguments[i];
|
||||
if (env in this.config) {
|
||||
var config = this.config[env];
|
||||
config.forEach(function(fn){
|
||||
fn.call(this);
|
||||
}, this);
|
||||
}
|
||||
}
|
||||
return this;
|
||||
};
|
||||
|
||||
/**
|
||||
* Assign a custom exception handler callback `fn`.
|
||||
*
|
||||
* @param {Function} fn
|
||||
* @return {Server} for chaining
|
||||
* @api public
|
||||
*/
|
||||
|
||||
Server.prototype.error = function(fn){
|
||||
this.use('/', function(err, req, res, next){
|
||||
fn.apply(this, arguments);
|
||||
});
|
||||
return this;
|
||||
};
|
||||
|
||||
/**
|
||||
* Assign `setting` to `val`, or return `setting`'s value.
|
||||
*
|
||||
* @param {String} setting
|
||||
* @param {String} val
|
||||
* @return {Server|Mixed} for chaining, or the setting value
|
||||
* @api public
|
||||
*/
|
||||
|
||||
Server.prototype.set = function(setting, val){
|
||||
if (val === undefined) {
|
||||
return this.settings[setting];
|
||||
} else {
|
||||
this.settings[setting] = val;
|
||||
return this;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Enable `setting`.
|
||||
*
|
||||
* @param {String} setting
|
||||
* @return {Server} for chaining
|
||||
* @api public
|
||||
*/
|
||||
|
||||
Server.prototype.enable = function(setting){
|
||||
return this.set(setting, true);
|
||||
};
|
||||
|
||||
/**
|
||||
* Disable `setting`.
|
||||
*
|
||||
* @param {String} setting
|
||||
* @return {Server} for chaining
|
||||
* @api public
|
||||
*/
|
||||
|
||||
Server.prototype.disable = function(setting){
|
||||
return this.set(setting, false);
|
||||
};
|
||||
|
||||
/**
|
||||
* Redirect `key` to `url`.
|
||||
*
|
||||
* @param {String} key
|
||||
* @param {String} url
|
||||
* @return {Server} for chaining
|
||||
* @api public
|
||||
*/
|
||||
|
||||
Server.prototype.redirect = function(key, url){
|
||||
this.redirects[key] = url;
|
||||
return this;
|
||||
};
|
||||
|
||||
/**
|
||||
* Disable `setting`.
|
||||
*
|
||||
* @param {String} setting
|
||||
* @return {Server} for chaining
|
||||
* @api public
|
||||
*/
|
||||
|
||||
Server.prototype.configure = function(env, fn){
|
||||
if (typeof env === 'function') fn = env, env = 'any';
|
||||
(this.config[env] = this.config[env] || []).push(fn);
|
||||
return this;
|
||||
};
|
||||
|
||||
// Generate routing methods
|
||||
|
||||
(function(method){
|
||||
Server.prototype[method] = function(path, fn){
|
||||
this.routes[method](path, fn);
|
||||
return this;
|
||||
};
|
||||
return arguments.callee;
|
||||
})('get')('post')('put')('del');
|
||||
@@ -1,2 +0,0 @@
|
||||
|
||||
require('express/spec/mocks')
|
||||
Alguns arquivos não foram exibidos porque demasiados arquivos foram alterados neste diff Mostrar Mais
Referência em uma Nova Issue
Bloquear um usuário