├── .htaccess ├── LICENSE ├── README.md ├── api ├── OAuth.php ├── callback.php ├── clearsessions.php ├── comment.php ├── config.php ├── functions.php ├── gc.php ├── getpost.php ├── index.htm ├── indexmaker.php ├── login.php ├── logout.php ├── mysql.php ├── post.php ├── twa.php ├── ustatus.php └── vote.php ├── assets ├── app.js ├── bootstrap.min.css ├── handlebars-v1.3.0.js ├── index.htm └── main.css ├── db.php └── index.htm /.htaccess: -------------------------------------------------------------------------------- 1 | RewriteEngine on 2 | RewriteCond %{REQUEST_FILENAME} !-f 3 | RewriteCond %{REQUEST_FILENAME} !-d 4 | RewriteRule ^(.*)$ /index.htm#$1 [L] 5 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2014 Rukshan Ranatunge 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | 23 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Helius - A HackerNews Clone 2 | ======= 3 | ![enter image description here](https://lh5.googleusercontent.com/-d7_mhr-F9k4/VE5vTuGmeFI/AAAAAAAAIXg/xPBheWCTvi8/w910-h460/Screenshot+from+2014-10-27+21:42:29.png) 4 | 5 | If you are interested in becoming a contributer let me know 6 | 7 | Introduction 8 | ------------ 9 | A simple HackerNews clone written using PHP, and EmberJS. It requires users signing in using a Twitter account. Inspired by Product Hunt and HackerNews and all the HackerNews clones focusing on different sub categories (HackerNews for Bitcoin etc) 10 | 11 | This was made as a weekend project while learning EmberJS, you can easily customize this and host it as your own HackerNews for your favourite topic. 12 | 13 | It uses Twitter Bootstrap therefor responsive in design. 14 | 15 | Installation 16 | ------------ 17 | The source code is available at [https://github.com/rukshn/helius.git](https://github.com/rukshn/helius.git). Clone the repository by: 18 | 19 | [root@localhost ~]# git clone https://github.com/rukshn/helius.git 20 | 21 | How to use 22 | ------------------ 23 | Here is a brief introduction to the directories in the repository. 24 | 25 | **API :** Contains all the files that handle the backend functions. 26 | 27 | **Assets:** Contains the CSS and image files. 28 | 29 | **After installation,** 30 | 31 | 1. Create a Twitter app from developer page and replace the consumer key, consumer secret and callback URL variables in the **/api/config.php** file. 32 | 2. Add your database database name, username, password in the **/api/mysql.php** file. 33 | 3. Add the same database name, username, password to db.php file in the root folder and run it to create necessary tables and delete it after that. 34 | 4. Modify the index.htm according to your project by replacing the names of my project and links to Facebook, Twitter, Newsletter etc. 35 | 5. You are good to go after that. 36 | 37 | Before you finish reading 38 | ------- 39 | 40 | 1. It was a weekend project so the coding was in a rush and it might not follow your best practices in coding. 41 | 2. The documentation might be little. 42 | 3. There is lot of room for improvement, please improve on this, fork it, correct errors. 43 | 4. If you like contribute to the project. 44 | 5. If you hate PHP don't blame me, it may not be perfect but it works. 45 | 46 | Dependencies 47 | ------- 48 | 49 | - Jquery 50 | - Twitter Bootstrap 51 | - Handlebars JS 52 | - Ember JS 53 | - Moment JS 54 | 55 | -------------------------------------------------------------------------------- /api/OAuth.php: -------------------------------------------------------------------------------- 1 | key = $key; 18 | $this->secret = $secret; 19 | $this->callback_url = $callback_url; 20 | } 21 | 22 | function __toString() { 23 | return "OAuthConsumer[key=$this->key,secret=$this->secret]"; 24 | } 25 | } 26 | 27 | class OAuthToken { 28 | // access tokens and request tokens 29 | public $key; 30 | public $secret; 31 | 32 | /** 33 | * key = the token 34 | * secret = the token secret 35 | */ 36 | function __construct($key, $secret) { 37 | $this->key = $key; 38 | $this->secret = $secret; 39 | } 40 | 41 | /** 42 | * generates the basic string serialization of a token that a server 43 | * would respond to request_token and access_token calls with 44 | */ 45 | function to_string() { 46 | return "oauth_token=" . 47 | OAuthUtil::urlencode_rfc3986($this->key) . 48 | "&oauth_token_secret=" . 49 | OAuthUtil::urlencode_rfc3986($this->secret); 50 | } 51 | 52 | function __toString() { 53 | return $this->to_string(); 54 | } 55 | } 56 | 57 | /** 58 | * A class for implementing a Signature Method 59 | * See section 9 ("Signing Requests") in the spec 60 | */ 61 | abstract class OAuthSignatureMethod { 62 | /** 63 | * Needs to return the name of the Signature Method (ie HMAC-SHA1) 64 | * @return string 65 | */ 66 | abstract public function get_name(); 67 | 68 | /** 69 | * Build up the signature 70 | * NOTE: The output of this function MUST NOT be urlencoded. 71 | * the encoding is handled in OAuthRequest when the final 72 | * request is serialized 73 | * @param OAuthRequest $request 74 | * @param OAuthConsumer $consumer 75 | * @param OAuthToken $token 76 | * @return string 77 | */ 78 | abstract public function build_signature($request, $consumer, $token); 79 | 80 | /** 81 | * Verifies that a given signature is correct 82 | * @param OAuthRequest $request 83 | * @param OAuthConsumer $consumer 84 | * @param OAuthToken $token 85 | * @param string $signature 86 | * @return bool 87 | */ 88 | public function check_signature($request, $consumer, $token, $signature) { 89 | $built = $this->build_signature($request, $consumer, $token); 90 | return $built == $signature; 91 | } 92 | } 93 | 94 | /** 95 | * The HMAC-SHA1 signature method uses the HMAC-SHA1 signature algorithm as defined in [RFC2104] 96 | * where the Signature Base String is the text and the key is the concatenated values (each first 97 | * encoded per Parameter Encoding) of the Consumer Secret and Token Secret, separated by an '&' 98 | * character (ASCII code 38) even if empty. 99 | * - Chapter 9.2 ("HMAC-SHA1") 100 | */ 101 | class OAuthSignatureMethod_HMAC_SHA1 extends OAuthSignatureMethod { 102 | function get_name() { 103 | return "HMAC-SHA1"; 104 | } 105 | 106 | public function build_signature($request, $consumer, $token) { 107 | $base_string = $request->get_signature_base_string(); 108 | $request->base_string = $base_string; 109 | 110 | $key_parts = array( 111 | $consumer->secret, 112 | ($token) ? $token->secret : "" 113 | ); 114 | 115 | $key_parts = OAuthUtil::urlencode_rfc3986($key_parts); 116 | $key = implode('&', $key_parts); 117 | 118 | return base64_encode(hash_hmac('sha1', $base_string, $key, true)); 119 | } 120 | } 121 | 122 | /** 123 | * The PLAINTEXT method does not provide any security protection and SHOULD only be used 124 | * over a secure channel such as HTTPS. It does not use the Signature Base String. 125 | * - Chapter 9.4 ("PLAINTEXT") 126 | */ 127 | class OAuthSignatureMethod_PLAINTEXT extends OAuthSignatureMethod { 128 | public function get_name() { 129 | return "PLAINTEXT"; 130 | } 131 | 132 | /** 133 | * oauth_signature is set to the concatenated encoded values of the Consumer Secret and 134 | * Token Secret, separated by a '&' character (ASCII code 38), even if either secret is 135 | * empty. The result MUST be encoded again. 136 | * - Chapter 9.4.1 ("Generating Signatures") 137 | * 138 | * Please note that the second encoding MUST NOT happen in the SignatureMethod, as 139 | * OAuthRequest handles this! 140 | */ 141 | public function build_signature($request, $consumer, $token) { 142 | $key_parts = array( 143 | $consumer->secret, 144 | ($token) ? $token->secret : "" 145 | ); 146 | 147 | $key_parts = OAuthUtil::urlencode_rfc3986($key_parts); 148 | $key = implode('&', $key_parts); 149 | $request->base_string = $key; 150 | 151 | return $key; 152 | } 153 | } 154 | 155 | /** 156 | * The RSA-SHA1 signature method uses the RSASSA-PKCS1-v1_5 signature algorithm as defined in 157 | * [RFC3447] section 8.2 (more simply known as PKCS#1), using SHA-1 as the hash function for 158 | * EMSA-PKCS1-v1_5. It is assumed that the Consumer has provided its RSA public key in a 159 | * verified way to the Service Provider, in a manner which is beyond the scope of this 160 | * specification. 161 | * - Chapter 9.3 ("RSA-SHA1") 162 | */ 163 | abstract class OAuthSignatureMethod_RSA_SHA1 extends OAuthSignatureMethod { 164 | public function get_name() { 165 | return "RSA-SHA1"; 166 | } 167 | 168 | // Up to the SP to implement this lookup of keys. Possible ideas are: 169 | // (1) do a lookup in a table of trusted certs keyed off of consumer 170 | // (2) fetch via http using a url provided by the requester 171 | // (3) some sort of specific discovery code based on request 172 | // 173 | // Either way should return a string representation of the certificate 174 | protected abstract function fetch_public_cert(&$request); 175 | 176 | // Up to the SP to implement this lookup of keys. Possible ideas are: 177 | // (1) do a lookup in a table of trusted certs keyed off of consumer 178 | // 179 | // Either way should return a string representation of the certificate 180 | protected abstract function fetch_private_cert(&$request); 181 | 182 | public function build_signature($request, $consumer, $token) { 183 | $base_string = $request->get_signature_base_string(); 184 | $request->base_string = $base_string; 185 | 186 | // Fetch the private key cert based on the request 187 | $cert = $this->fetch_private_cert($request); 188 | 189 | // Pull the private key ID from the certificate 190 | $privatekeyid = openssl_get_privatekey($cert); 191 | 192 | // Sign using the key 193 | $ok = openssl_sign($base_string, $signature, $privatekeyid); 194 | 195 | // Release the key resource 196 | openssl_free_key($privatekeyid); 197 | 198 | return base64_encode($signature); 199 | } 200 | 201 | public function check_signature($request, $consumer, $token, $signature) { 202 | $decoded_sig = base64_decode($signature); 203 | 204 | $base_string = $request->get_signature_base_string(); 205 | 206 | // Fetch the public key cert based on the request 207 | $cert = $this->fetch_public_cert($request); 208 | 209 | // Pull the public key ID from the certificate 210 | $publickeyid = openssl_get_publickey($cert); 211 | 212 | // Check the computed signature against the one passed in the query 213 | $ok = openssl_verify($base_string, $decoded_sig, $publickeyid); 214 | 215 | // Release the key resource 216 | openssl_free_key($publickeyid); 217 | 218 | return $ok == 1; 219 | } 220 | } 221 | 222 | class OAuthRequest { 223 | private $parameters; 224 | private $http_method; 225 | private $http_url; 226 | // for debug purposes 227 | public $base_string; 228 | public static $version = '1.0'; 229 | public static $POST_INPUT = 'php://input'; 230 | 231 | function __construct($http_method, $http_url, $parameters=NULL) { 232 | @$parameters or $parameters = array(); 233 | $parameters = array_merge( OAuthUtil::parse_parameters(parse_url($http_url, PHP_URL_QUERY)), $parameters); 234 | $this->parameters = $parameters; 235 | $this->http_method = $http_method; 236 | $this->http_url = $http_url; 237 | } 238 | 239 | 240 | /** 241 | * attempt to build up a request from what was passed to the server 242 | */ 243 | public static function from_request($http_method=NULL, $http_url=NULL, $parameters=NULL) { 244 | $scheme = (!isset($_SERVER['HTTPS']) || $_SERVER['HTTPS'] != "on") 245 | ? 'http' 246 | : 'https'; 247 | @$http_url or $http_url = $scheme . 248 | '://' . $_SERVER['HTTP_HOST'] . 249 | ':' . 250 | $_SERVER['SERVER_PORT'] . 251 | $_SERVER['REQUEST_URI']; 252 | @$http_method or $http_method = $_SERVER['REQUEST_METHOD']; 253 | 254 | // We weren't handed any parameters, so let's find the ones relevant to 255 | // this request. 256 | // If you run XML-RPC or similar you should use this to provide your own 257 | // parsed parameter-list 258 | if (!$parameters) { 259 | // Find request headers 260 | $request_headers = OAuthUtil::get_headers(); 261 | 262 | // Parse the query-string to find GET parameters 263 | $parameters = OAuthUtil::parse_parameters($_SERVER['QUERY_STRING']); 264 | 265 | // It's a POST request of the proper content-type, so parse POST 266 | // parameters and add those overriding any duplicates from GET 267 | if ($http_method == "POST" 268 | && @strstr($request_headers["Content-Type"], 269 | "application/x-www-form-urlencoded") 270 | ) { 271 | $post_data = OAuthUtil::parse_parameters( 272 | file_get_contents(self::$POST_INPUT) 273 | ); 274 | $parameters = array_merge($parameters, $post_data); 275 | } 276 | 277 | // We have a Authorization-header with OAuth data. Parse the header 278 | // and add those overriding any duplicates from GET or POST 279 | if (@substr($request_headers['Authorization'], 0, 6) == "OAuth ") { 280 | $header_parameters = OAuthUtil::split_header( 281 | $request_headers['Authorization'] 282 | ); 283 | $parameters = array_merge($parameters, $header_parameters); 284 | } 285 | 286 | } 287 | 288 | return new OAuthRequest($http_method, $http_url, $parameters); 289 | } 290 | 291 | /** 292 | * pretty much a helper function to set up the request 293 | */ 294 | public static function from_consumer_and_token($consumer, $token, $http_method, $http_url, $parameters=NULL) { 295 | @$parameters or $parameters = array(); 296 | $defaults = array("oauth_version" => OAuthRequest::$version, 297 | "oauth_nonce" => OAuthRequest::generate_nonce(), 298 | "oauth_timestamp" => OAuthRequest::generate_timestamp(), 299 | "oauth_consumer_key" => $consumer->key); 300 | if ($token) 301 | $defaults['oauth_token'] = $token->key; 302 | 303 | $parameters = array_merge($defaults, $parameters); 304 | 305 | return new OAuthRequest($http_method, $http_url, $parameters); 306 | } 307 | 308 | public function set_parameter($name, $value, $allow_duplicates = true) { 309 | if ($allow_duplicates && isset($this->parameters[$name])) { 310 | // We have already added parameter(s) with this name, so add to the list 311 | if (is_scalar($this->parameters[$name])) { 312 | // This is the first duplicate, so transform scalar (string) 313 | // into an array so we can add the duplicates 314 | $this->parameters[$name] = array($this->parameters[$name]); 315 | } 316 | 317 | $this->parameters[$name][] = $value; 318 | } else { 319 | $this->parameters[$name] = $value; 320 | } 321 | } 322 | 323 | public function get_parameter($name) { 324 | return isset($this->parameters[$name]) ? $this->parameters[$name] : null; 325 | } 326 | 327 | public function get_parameters() { 328 | return $this->parameters; 329 | } 330 | 331 | public function unset_parameter($name) { 332 | unset($this->parameters[$name]); 333 | } 334 | 335 | /** 336 | * The request parameters, sorted and concatenated into a normalized string. 337 | * @return string 338 | */ 339 | public function get_signable_parameters() { 340 | // Grab all parameters 341 | $params = $this->parameters; 342 | 343 | // Remove oauth_signature if present 344 | // Ref: Spec: 9.1.1 ("The oauth_signature parameter MUST be excluded.") 345 | if (isset($params['oauth_signature'])) { 346 | unset($params['oauth_signature']); 347 | } 348 | 349 | return OAuthUtil::build_http_query($params); 350 | } 351 | 352 | /** 353 | * Returns the base string of this request 354 | * 355 | * The base string defined as the method, the url 356 | * and the parameters (normalized), each urlencoded 357 | * and the concated with &. 358 | */ 359 | public function get_signature_base_string() { 360 | $parts = array( 361 | $this->get_normalized_http_method(), 362 | $this->get_normalized_http_url(), 363 | $this->get_signable_parameters() 364 | ); 365 | 366 | $parts = OAuthUtil::urlencode_rfc3986($parts); 367 | 368 | return implode('&', $parts); 369 | } 370 | 371 | /** 372 | * just uppercases the http method 373 | */ 374 | public function get_normalized_http_method() { 375 | return strtoupper($this->http_method); 376 | } 377 | 378 | /** 379 | * parses the url and rebuilds it to be 380 | * scheme://host/path 381 | */ 382 | public function get_normalized_http_url() { 383 | $parts = parse_url($this->http_url); 384 | 385 | $port = @$parts['port']; 386 | $scheme = $parts['scheme']; 387 | $host = $parts['host']; 388 | $path = @$parts['path']; 389 | 390 | $port or $port = ($scheme == 'https') ? '443' : '80'; 391 | 392 | if (($scheme == 'https' && $port != '443') 393 | || ($scheme == 'http' && $port != '80')) { 394 | $host = "$host:$port"; 395 | } 396 | return "$scheme://$host$path"; 397 | } 398 | 399 | /** 400 | * builds a url usable for a GET request 401 | */ 402 | public function to_url() { 403 | $post_data = $this->to_postdata(); 404 | $out = $this->get_normalized_http_url(); 405 | if ($post_data) { 406 | $out .= '?'.$post_data; 407 | } 408 | return $out; 409 | } 410 | 411 | /** 412 | * builds the data one would send in a POST request 413 | */ 414 | public function to_postdata() { 415 | return OAuthUtil::build_http_query($this->parameters); 416 | } 417 | 418 | /** 419 | * builds the Authorization: header 420 | */ 421 | public function to_header($realm=null) { 422 | $first = true; 423 | if($realm) { 424 | $out = 'Authorization: OAuth realm="' . OAuthUtil::urlencode_rfc3986($realm) . '"'; 425 | $first = false; 426 | } else 427 | $out = 'Authorization: OAuth'; 428 | 429 | $total = array(); 430 | foreach ($this->parameters as $k => $v) { 431 | if (substr($k, 0, 5) != "oauth") continue; 432 | if (is_array($v)) { 433 | throw new OAuthException('Arrays not supported in headers'); 434 | } 435 | $out .= ($first) ? ' ' : ','; 436 | $out .= OAuthUtil::urlencode_rfc3986($k) . 437 | '="' . 438 | OAuthUtil::urlencode_rfc3986($v) . 439 | '"'; 440 | $first = false; 441 | } 442 | return $out; 443 | } 444 | 445 | public function __toString() { 446 | return $this->to_url(); 447 | } 448 | 449 | 450 | public function sign_request($signature_method, $consumer, $token) { 451 | $this->set_parameter( 452 | "oauth_signature_method", 453 | $signature_method->get_name(), 454 | false 455 | ); 456 | $signature = $this->build_signature($signature_method, $consumer, $token); 457 | $this->set_parameter("oauth_signature", $signature, false); 458 | } 459 | 460 | public function build_signature($signature_method, $consumer, $token) { 461 | $signature = $signature_method->build_signature($this, $consumer, $token); 462 | return $signature; 463 | } 464 | 465 | /** 466 | * util function: current timestamp 467 | */ 468 | private static function generate_timestamp() { 469 | return time(); 470 | } 471 | 472 | /** 473 | * util function: current nonce 474 | */ 475 | private static function generate_nonce() { 476 | $mt = microtime(); 477 | $rand = mt_rand(); 478 | 479 | return md5($mt . $rand); // md5s look nicer than numbers 480 | } 481 | } 482 | 483 | class OAuthServer { 484 | protected $timestamp_threshold = 300; // in seconds, five minutes 485 | protected $version = '1.0'; // hi blaine 486 | protected $signature_methods = array(); 487 | 488 | protected $data_store; 489 | 490 | function __construct($data_store) { 491 | $this->data_store = $data_store; 492 | } 493 | 494 | public function add_signature_method($signature_method) { 495 | $this->signature_methods[$signature_method->get_name()] = 496 | $signature_method; 497 | } 498 | 499 | // high level functions 500 | 501 | /** 502 | * process a request_token request 503 | * returns the request token on success 504 | */ 505 | public function fetch_request_token(&$request) { 506 | $this->get_version($request); 507 | 508 | $consumer = $this->get_consumer($request); 509 | 510 | // no token required for the initial token request 511 | $token = NULL; 512 | 513 | $this->check_signature($request, $consumer, $token); 514 | 515 | // Rev A change 516 | $callback = $request->get_parameter('oauth_callback'); 517 | $new_token = $this->data_store->new_request_token($consumer, $callback); 518 | 519 | return $new_token; 520 | } 521 | 522 | /** 523 | * process an access_token request 524 | * returns the access token on success 525 | */ 526 | public function fetch_access_token(&$request) { 527 | $this->get_version($request); 528 | 529 | $consumer = $this->get_consumer($request); 530 | 531 | // requires authorized request token 532 | $token = $this->get_token($request, $consumer, "request"); 533 | 534 | $this->check_signature($request, $consumer, $token); 535 | 536 | // Rev A change 537 | $verifier = $request->get_parameter('oauth_verifier'); 538 | $new_token = $this->data_store->new_access_token($token, $consumer, $verifier); 539 | 540 | return $new_token; 541 | } 542 | 543 | /** 544 | * verify an api call, checks all the parameters 545 | */ 546 | public function verify_request(&$request) { 547 | $this->get_version($request); 548 | $consumer = $this->get_consumer($request); 549 | $token = $this->get_token($request, $consumer, "access"); 550 | $this->check_signature($request, $consumer, $token); 551 | return array($consumer, $token); 552 | } 553 | 554 | // Internals from here 555 | /** 556 | * version 1 557 | */ 558 | private function get_version(&$request) { 559 | $version = $request->get_parameter("oauth_version"); 560 | if (!$version) { 561 | // Service Providers MUST assume the protocol version to be 1.0 if this parameter is not present. 562 | // Chapter 7.0 ("Accessing Protected Ressources") 563 | $version = '1.0'; 564 | } 565 | if ($version !== $this->version) { 566 | throw new OAuthException("OAuth version '$version' not supported"); 567 | } 568 | return $version; 569 | } 570 | 571 | /** 572 | * figure out the signature with some defaults 573 | */ 574 | private function get_signature_method(&$request) { 575 | $signature_method = 576 | @$request->get_parameter("oauth_signature_method"); 577 | 578 | if (!$signature_method) { 579 | // According to chapter 7 ("Accessing Protected Ressources") the signature-method 580 | // parameter is required, and we can't just fallback to PLAINTEXT 581 | throw new OAuthException('No signature method parameter. This parameter is required'); 582 | } 583 | 584 | if (!in_array($signature_method, 585 | array_keys($this->signature_methods))) { 586 | throw new OAuthException( 587 | "Signature method '$signature_method' not supported " . 588 | "try one of the following: " . 589 | implode(", ", array_keys($this->signature_methods)) 590 | ); 591 | } 592 | return $this->signature_methods[$signature_method]; 593 | } 594 | 595 | /** 596 | * try to find the consumer for the provided request's consumer key 597 | */ 598 | private function get_consumer(&$request) { 599 | $consumer_key = @$request->get_parameter("oauth_consumer_key"); 600 | if (!$consumer_key) { 601 | throw new OAuthException("Invalid consumer key"); 602 | } 603 | 604 | $consumer = $this->data_store->lookup_consumer($consumer_key); 605 | if (!$consumer) { 606 | throw new OAuthException("Invalid consumer"); 607 | } 608 | 609 | return $consumer; 610 | } 611 | 612 | /** 613 | * try to find the token for the provided request's token key 614 | */ 615 | private function get_token(&$request, $consumer, $token_type="access") { 616 | $token_field = @$request->get_parameter('oauth_token'); 617 | $token = $this->data_store->lookup_token( 618 | $consumer, $token_type, $token_field 619 | ); 620 | if (!$token) { 621 | throw new OAuthException("Invalid $token_type token: $token_field"); 622 | } 623 | return $token; 624 | } 625 | 626 | /** 627 | * all-in-one function to check the signature on a request 628 | * should guess the signature method appropriately 629 | */ 630 | private function check_signature(&$request, $consumer, $token) { 631 | // this should probably be in a different method 632 | $timestamp = @$request->get_parameter('oauth_timestamp'); 633 | $nonce = @$request->get_parameter('oauth_nonce'); 634 | 635 | $this->check_timestamp($timestamp); 636 | $this->check_nonce($consumer, $token, $nonce, $timestamp); 637 | 638 | $signature_method = $this->get_signature_method($request); 639 | 640 | $signature = $request->get_parameter('oauth_signature'); 641 | $valid_sig = $signature_method->check_signature( 642 | $request, 643 | $consumer, 644 | $token, 645 | $signature 646 | ); 647 | 648 | if (!$valid_sig) { 649 | throw new OAuthException("Invalid signature"); 650 | } 651 | } 652 | 653 | /** 654 | * check that the timestamp is new enough 655 | */ 656 | private function check_timestamp($timestamp) { 657 | if( ! $timestamp ) 658 | throw new OAuthException( 659 | 'Missing timestamp parameter. The parameter is required' 660 | ); 661 | 662 | // verify that timestamp is recentish 663 | $now = time(); 664 | if (abs($now - $timestamp) > $this->timestamp_threshold) { 665 | throw new OAuthException( 666 | "Expired timestamp, yours $timestamp, ours $now" 667 | ); 668 | } 669 | } 670 | 671 | /** 672 | * check that the nonce is not repeated 673 | */ 674 | private function check_nonce($consumer, $token, $nonce, $timestamp) { 675 | if( ! $nonce ) 676 | throw new OAuthException( 677 | 'Missing nonce parameter. The parameter is required' 678 | ); 679 | 680 | // verify that the nonce is uniqueish 681 | $found = $this->data_store->lookup_nonce( 682 | $consumer, 683 | $token, 684 | $nonce, 685 | $timestamp 686 | ); 687 | if ($found) { 688 | throw new OAuthException("Nonce already used: $nonce"); 689 | } 690 | } 691 | 692 | } 693 | 694 | class OAuthDataStore { 695 | function lookup_consumer($consumer_key) { 696 | // implement me 697 | } 698 | 699 | function lookup_token($consumer, $token_type, $token) { 700 | // implement me 701 | } 702 | 703 | function lookup_nonce($consumer, $token, $nonce, $timestamp) { 704 | // implement me 705 | } 706 | 707 | function new_request_token($consumer, $callback = null) { 708 | // return a new token attached to this consumer 709 | } 710 | 711 | function new_access_token($token, $consumer, $verifier = null) { 712 | // return a new access token attached to this consumer 713 | // for the user associated with this token if the request token 714 | // is authorized 715 | // should also invalidate the request token 716 | } 717 | 718 | } 719 | 720 | class OAuthUtil { 721 | public static function urlencode_rfc3986($input) { 722 | if (is_array($input)) { 723 | return array_map(array('OAuthUtil', 'urlencode_rfc3986'), $input); 724 | } else if (is_scalar($input)) { 725 | return str_replace( 726 | '+', 727 | ' ', 728 | str_replace('%7E', '~', rawurlencode($input)) 729 | ); 730 | } else { 731 | return ''; 732 | } 733 | } 734 | 735 | 736 | // This decode function isn't taking into consideration the above 737 | // modifications to the encoding process. However, this method doesn't 738 | // seem to be used anywhere so leaving it as is. 739 | public static function urldecode_rfc3986($string) { 740 | return urldecode($string); 741 | } 742 | 743 | // Utility function for turning the Authorization: header into 744 | // parameters, has to do some unescaping 745 | // Can filter out any non-oauth parameters if needed (default behaviour) 746 | public static function split_header($header, $only_allow_oauth_parameters = true) { 747 | $pattern = '/(([-_a-z]*)=("([^"]*)"|([^,]*)),?)/'; 748 | $offset = 0; 749 | $params = array(); 750 | while (preg_match($pattern, $header, $matches, PREG_OFFSET_CAPTURE, $offset) > 0) { 751 | $match = $matches[0]; 752 | $header_name = $matches[2][0]; 753 | $header_content = (isset($matches[5])) ? $matches[5][0] : $matches[4][0]; 754 | if (preg_match('/^oauth_/', $header_name) || !$only_allow_oauth_parameters) { 755 | $params[$header_name] = OAuthUtil::urldecode_rfc3986($header_content); 756 | } 757 | $offset = $match[1] + strlen($match[0]); 758 | } 759 | 760 | if (isset($params['realm'])) { 761 | unset($params['realm']); 762 | } 763 | 764 | return $params; 765 | } 766 | 767 | // helper to try to sort out headers for people who aren't running apache 768 | public static function get_headers() { 769 | if (function_exists('apache_request_headers')) { 770 | // we need this to get the actual Authorization: header 771 | // because apache tends to tell us it doesn't exist 772 | $headers = apache_request_headers(); 773 | 774 | // sanitize the output of apache_request_headers because 775 | // we always want the keys to be Cased-Like-This and arh() 776 | // returns the headers in the same case as they are in the 777 | // request 778 | $out = array(); 779 | foreach( $headers AS $key => $value ) { 780 | $key = str_replace( 781 | " ", 782 | "-", 783 | ucwords(strtolower(str_replace("-", " ", $key))) 784 | ); 785 | $out[$key] = $value; 786 | } 787 | } else { 788 | // otherwise we don't have apache and are just going to have to hope 789 | // that $_SERVER actually contains what we need 790 | $out = array(); 791 | if( isset($_SERVER['CONTENT_TYPE']) ) 792 | $out['Content-Type'] = $_SERVER['CONTENT_TYPE']; 793 | if( isset($_ENV['CONTENT_TYPE']) ) 794 | $out['Content-Type'] = $_ENV['CONTENT_TYPE']; 795 | 796 | foreach ($_SERVER as $key => $value) { 797 | if (substr($key, 0, 5) == "HTTP_") { 798 | // this is chaos, basically it is just there to capitalize the first 799 | // letter of every word that is not an initial HTTP and strip HTTP 800 | // code from przemek 801 | $key = str_replace( 802 | " ", 803 | "-", 804 | ucwords(strtolower(str_replace("_", " ", substr($key, 5)))) 805 | ); 806 | $out[$key] = $value; 807 | } 808 | } 809 | } 810 | return $out; 811 | } 812 | 813 | // This function takes a input like a=b&a=c&d=e and returns the parsed 814 | // parameters like this 815 | // array('a' => array('b','c'), 'd' => 'e') 816 | public static function parse_parameters( $input ) { 817 | if (!isset($input) || !$input) return array(); 818 | 819 | $pairs = explode('&', $input); 820 | 821 | $parsed_parameters = array(); 822 | foreach ($pairs as $pair) { 823 | $split = explode('=', $pair, 2); 824 | $parameter = OAuthUtil::urldecode_rfc3986($split[0]); 825 | $value = isset($split[1]) ? OAuthUtil::urldecode_rfc3986($split[1]) : ''; 826 | 827 | if (isset($parsed_parameters[$parameter])) { 828 | // We have already recieved parameter(s) with this name, so add to the list 829 | // of parameters with this name 830 | 831 | if (is_scalar($parsed_parameters[$parameter])) { 832 | // This is the first duplicate, so transform scalar (string) into an array 833 | // so we can add the duplicates 834 | $parsed_parameters[$parameter] = array($parsed_parameters[$parameter]); 835 | } 836 | 837 | $parsed_parameters[$parameter][] = $value; 838 | } else { 839 | $parsed_parameters[$parameter] = $value; 840 | } 841 | } 842 | return $parsed_parameters; 843 | } 844 | 845 | public static function build_http_query($params) { 846 | if (!$params) return ''; 847 | 848 | // Urlencode both keys and values 849 | $keys = OAuthUtil::urlencode_rfc3986(array_keys($params)); 850 | $values = OAuthUtil::urlencode_rfc3986(array_values($params)); 851 | $params = array_combine($keys, $values); 852 | 853 | // Parameters are sorted by name, using lexicographical byte value ordering. 854 | // Ref: Spec: 9.1.1 (1) 855 | uksort($params, 'strcmp'); 856 | 857 | $pairs = array(); 858 | foreach ($params as $parameter => $value) { 859 | if (is_array($value)) { 860 | // If two or more parameters share the same name, they are sorted by their value 861 | // Ref: Spec: 9.1.1 (1) 862 | natsort($value); 863 | foreach ($value as $duplicate_value) { 864 | $pairs[] = $parameter . '=' . $duplicate_value; 865 | } 866 | } else { 867 | $pairs[] = $parameter . '=' . $value; 868 | } 869 | } 870 | // For each parameter, the name is separated from the corresponding value by an '=' character (ASCII code 61) 871 | // Each name-value pair is separated by an '&' character (ASCII code 38) 872 | return implode('&', $pairs); 873 | } 874 | } 875 | -------------------------------------------------------------------------------- /api/callback.php: -------------------------------------------------------------------------------- 1 | getAccessToken($_REQUEST['oauth_verifier']); 23 | $content = $connection->get('account/verify_credentials'); 24 | /* Save the access tokens. Normally these would be saved in a database for future use. */ 25 | $_SESSION['access_token'] = $access_token; 26 | /* Remove no longer needed request tokens */ 27 | unset($_SESSION['oauth_token']); 28 | unset($_SESSION['oauth_token_secret']); 29 | /* If HTTP response is 200 continue otherwise send to connect page to retry */ 30 | if (200 == $connection->http_code) { 31 | /* The user has been verified and the access tokens can be saved for future use */ 32 | $_SESSION['status'] = 'verified'; 33 | 34 | $arrays = objectToArray($content); 35 | 36 | $user_id = $arrays['id']; 37 | $name = $arrays['name']; 38 | $propic = $arrays['profile_image_url']; 39 | 40 | $token = sha1(generateRandomString(10).$userid); 41 | $twitter_token = $access_token["oauth_token"]; 42 | $twitter_secret = $access_token["oauth_token_secret"]; 43 | 44 | //check if the user exists 45 | $query = $conn->prepare("SELECT * FROM users WHERE userid = :userid LIMIT 1"); 46 | $query->bindValue(':userid', $user_id); 47 | $query->execute(); 48 | 49 | $row = $query->fetchAll(PDO::FETCH_ASSOC); 50 | 51 | if($row==true) 52 | { 53 | $_SESSION['user_id'] = $user_id; 54 | $_SESSION['propic'] = $propic; 55 | $_SESSION['myname'] = $name; 56 | $_SESSION['user_status'] = true; 57 | 58 | $query = $conn->prepare("UPDATE users 59 | SET token = :token 60 | , propic = :propic WHERE userid = :userid"); 61 | $query->bindValue(':token', $token); 62 | $query->bindValue(':propic', $propic); 63 | $query->bindValue(':userid', $user_id); 64 | 65 | $query->execute(); 66 | 67 | $cookie_name = 'info'; 68 | $cookie_value = $token; 69 | setcookie($cookie_name, $cookie_value, time() + (86400 * 30), '/'); // 86400 = 1 day 70 | header('Location: /'); 71 | 72 | } 73 | else 74 | { 75 | $_SESSION['user_id'] = $user_id; 76 | $_SESSION['propic'] = $propic; 77 | $_SESSION['myname'] = $name; 78 | $_SESSION['user_status'] = true; 79 | 80 | $query = $conn->prepare("INSERT into users (name,userid,propic,token,twitter_token,twitter_secret) VALUES (:name, :userid, :propic, :token, :twtoken, :twsecret)"); 81 | 82 | $query->execute(array(':name' => $name, ':userid' => $user_id, ':propic' => $propic, ':token' => $token, ':twtoken' => $twitter_token, ':twsecret' => $twitter_secret)); 83 | 84 | $cookie_name = 'info'; 85 | $cookie_value = $token; 86 | setcookie($cookie_name, $cookie_value, time() + (86400 * 30), '/'); // 86400 = 1 day 87 | 88 | header('Location: /'); 89 | } 90 | 91 | 92 | } else { 93 | /* Save HTTP status for error dialog on connnect page.*/ 94 | header('Location: ./clearsessions.php'); 95 | } 96 | -------------------------------------------------------------------------------- /api/clearsessions.php: -------------------------------------------------------------------------------- 1 | prepare("SELECT 1 FROM comments WHERE commentid = :rand"); 13 | do { 14 | $random_string = generateRandomString(5); 15 | $query->bindValue(':rand', $random_string); 16 | $query->execute(); 17 | } 18 | while 19 | ( 20 | $query->rowCount() > 0 21 | 22 | ); 23 | return $random_string; 24 | } 25 | 26 | 27 | if($_SESSION['user_status']==true) 28 | { 29 | $user = $_SESSION['user_id']; 30 | $comment_id = postex($conn); 31 | 32 | $query = $conn->prepare("INSERT INTO comments (comment, post_id, user_id, commentid, ctime) VALUES (:comment, :post, :user, :commentid, :time)"); 33 | $query->execute(array(':comment' => $comment, ':time' => $time, ':user' => $user, ':post' => $post, ":commentid" => $comment_id)); 34 | echo $comment_id; 35 | } 36 | else 37 | { 38 | echo "nouser"; 39 | } 40 | 41 | 42 | ?> 43 | -------------------------------------------------------------------------------- /api/config.php: -------------------------------------------------------------------------------- 1 | prepare("SELECT * FROM votes WHERE user_id = :user AND post_id = :id LIMIT 1"); 41 | $query->bindValue(':user', $cuser); 42 | $query->bindValue(':id', $id); 43 | $query->execute(); 44 | $getcuser = $query->fetchAll(PDO::FETCH_ASSOC); 45 | 46 | if($getcuser == true) 47 | { 48 | return true; 49 | } 50 | else 51 | { 52 | return false; 53 | } 54 | } 55 | else 56 | { 57 | return false; 58 | } 59 | 60 | } 61 | 62 | function totalvotes($id,$conn) 63 | { 64 | $getvotes = $conn->prepare("SELECT post_id FROM votes WHERE post_id = :id"); 65 | $getvotes->bindValue(':id', $id); 66 | $getvotes->execute(); 67 | $getvotes = $getvotes->rowCount(); 68 | 69 | return $getvotes; 70 | } 71 | 72 | function totalcomments($id, $conn) 73 | { 74 | $query = $conn->prepare("SELECT COUNT(*) FROM comments WHERE post_id = :id"); 75 | $query->bindValue(':id', $id); 76 | $query->execute(); 77 | 78 | $row = $query->fetch(PDO::FETCH_ASSOC); 79 | $gc = $row["COUNT(*)"]; 80 | return $gc; 81 | } 82 | -------------------------------------------------------------------------------- /api/gc.php: -------------------------------------------------------------------------------- 1 | prepare("SELECT * FROM comments WHERE post_id = :id"); 11 | $query->bindValue(':id', $id); 12 | $query->execute(); 13 | 14 | 15 | while ($row = $query->fetch(PDO::FETCH_ASSOC)) { 16 | $user_id = $row['user_id']; 17 | $post_id = $row['post_id']; 18 | $comment = $row['comment']; 19 | $comment_id = $row['commentid']; 20 | $ctime = $row['ctime']; 21 | 22 | $gu = $conn->prepare("SELECT * FROM users WHERE userid = :user LIMIT 1"); 23 | $gu->bindValue(':user', $user_id); 24 | $gu->execute(); 25 | $ru = $gu->fetchAll(PDO::FETCH_ASSOC); 26 | 27 | $user_name = $ru[0]['name']; 28 | $propic = $ru[0]['propic']; 29 | 30 | $commentz[] = [comment => $comment, 31 | commentid => $comment_id, 32 | ctime => $ctime, 33 | post_id => $post_id, 34 | user_id => $user_id, 35 | user_name => $user_name, 36 | propic => $propic 37 | ]; 38 | 39 | } 40 | 41 | $jcom = json_encode($commentz); 42 | print $jcom; 43 | 44 | 45 | 46 | ?> 47 | -------------------------------------------------------------------------------- /api/getpost.php: -------------------------------------------------------------------------------- 1 | prepare("SELECT * FROM posts WHERE postid = :id LIMIT 1"); 11 | $query->execute(array(':id' => $id)); 12 | $row = $query->fetch(PDO::FETCH_ASSOC); 13 | 14 | if( $row == true) 15 | { 16 | $title = $row['title']; 17 | $tagline = $row['tagline']; 18 | $link = $row['link']; 19 | $time = $row['post_time']; 20 | $user = $row['user']; 21 | 22 | $query = $conn->prepare("SELECT * FROM users WHERE userid = :user LIMIT 1"); 23 | $query->execute(array(':user' => $user)); 24 | $getuser = $query->fetch(PDO::FETCH_ASSOC); 25 | 26 | $user_name = $getuser['name']; 27 | $pro_pic = $getuser['propic']; 28 | 29 | $isvoted = isuservoted($id,$conn); 30 | $total_votes = totalvotes($id,$conn); 31 | 32 | $post_status = [ 33 | 'title' => $title, 34 | 'tagline' => $tagline, 35 | 'isVoted' => $isvoted, 36 | 'datalink' => $id, 37 | 'user' => $user_name, 38 | 'upropic' => $pro_pic, 39 | 'votecount' => $total_votes, 40 | 'post_date' => $time, 41 | 'post_url' => $link 42 | 43 | ]; 44 | $post_status = json_encode($post_status); 45 | print $post_status; 46 | } 47 | else 48 | { 49 | print "nopost"; 50 | } 51 | 52 | ?> 53 | -------------------------------------------------------------------------------- /api/index.htm: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /api/indexmaker.php: -------------------------------------------------------------------------------- 1 | 0) 13 | { 14 | $sign = 1; 15 | } 16 | elseif($s < 0) 17 | { 18 | $sign = -1; 19 | } 20 | else 21 | { 22 | $sign = 0; 23 | } 24 | 25 | $seconds = $time - 1412433234; 26 | $signxorder = $sign * $order; 27 | $signxorderpseconds = $signxorder + $seconds; 28 | $score = round($signxorderpseconds / 45000 ,7); 29 | return $score; 30 | 31 | } 32 | 33 | $query = $conn->prepare("SELECT * FROM posts ORDER BY id DESC LIMIT 100"); 34 | $query->execute(); 35 | 36 | while ($row = $query->fetch(PDO::FETCH_ASSOC)) 37 | { 38 | $postid = $row['postid']; 39 | $userid = $row['user']; 40 | $title = $row['title']; 41 | $tagling = $row['tagline']; 42 | $link = $row['link']; 43 | $post_time = $row['post_time']; 44 | 45 | $total_votes = totalvotes($postid,$conn); 46 | $hotrank = getscore($total_votes, $post_time); 47 | $isvoted = isuservoted($postid,$conn); 48 | 49 | $gu = $conn->prepare("SELECT * FROM users WHERE userid = :id LIMIT 1"); 50 | $gu->execute(array(':id' => $userid)); 51 | 52 | $gur = $gu->fetch(PDO::FETCH_ASSOC); 53 | $user_name = $gur['name']; 54 | $user_propic = $gur['propic']; 55 | 56 | $gcoms = totalcomments($postid,$conn); 57 | 58 | $allposts[] = ['datalink' => $postid, 59 | 'user_id' => $userid, 60 | 'upropic' => $user_propic, 61 | 'user' => $user_name, 62 | 'title' => $title, 63 | 'tag_line' => $tagling, 64 | 'post_url' => $link, 65 | 'votecount' => $total_votes, 66 | 'hot_rank' => $hotrank, 67 | 'isVoted' => $isvoted, 68 | 'post_time' => $post_time, 69 | 'num_comments' => $gcoms]; 70 | } 71 | 72 | usort($allposts, function($a, $b) { 73 | return ($a['hot_rank'] < $b['hot_rank']) ? -1 : 1; 74 | // return $a['hot_rank'] - $b['hot_rank']; 75 | }); 76 | 77 | $rsl = array_reverse($allposts); 78 | $sl = array_slice($rsl, 0, 20); 79 | 80 | if(isset($_REQUEST['pre'])) 81 | { 82 | echo "
";
83 |   var_dump($sl);
84 |   echo "
"; 85 | } 86 | else 87 | { 88 | print(json_encode($sl)); 89 | } 90 | ?> 91 | -------------------------------------------------------------------------------- /api/login.php: -------------------------------------------------------------------------------- 1 | getRequestToken(OAUTH_CALLBACK); 10 | /* Save temporary credentials to session. */ 11 | $_SESSION['oauth_token'] = $token = $request_token['oauth_token']; 12 | $_SESSION['oauth_token_secret'] = $request_token['oauth_token_secret']; 13 | /* If last connection failed don't display authorization link. */ 14 | switch ($connection->http_code) { 15 | case 200: 16 | /* Build authorize URL and redirect user to Twitter. */ 17 | $url = $connection->getAuthorizeURL($token); 18 | header('Location: ' . $url); 19 | break; 20 | default: 21 | /* Show notification if something went wrong. */ 22 | echo 'Could not connect to Twitter. Refresh the page or try again later.'; 23 | } 24 | -------------------------------------------------------------------------------- /api/logout.php: -------------------------------------------------------------------------------- 1 | prepare("SELECT 1 FROM posts WHERE postid = :rand"); 13 | do { 14 | $random_string = generateRandomString(5); 15 | $query->bindValue(':rand', $random_string); 16 | $query->execute(); 17 | } while 18 | ( 19 | $query->rowCount() > 0 20 | 21 | ); 22 | return $random_string; 23 | } 24 | 25 | if($_SESSION['user_status']== true) 26 | { 27 | $url = strip_tags($_POST['purl']); 28 | $title = strip_tags($_POST['ptitle']); 29 | $tagling = strip_tags($_POST['ptagline']); 30 | 31 | $query = $conn->prepare("SELECT postid FROM posts WHERE link = :url LIMIT 1"); 32 | $query->execute(array(':url' => $url)); 33 | $row = $query->fetchAll(PDO::FETCH_ASSOC); 34 | 35 | if($row == true) 36 | { 37 | print $row[0]['postid']; 38 | } 39 | else 40 | { 41 | $randid = postex($conn); 42 | $query = $conn->prepare("INSERT INTO posts (postid,title,tagline,link,user,post_time) 43 | VALUES (:raid, :title, :tagline, :url, :userid, :timest)"); 44 | $query->execute(array(':raid' => $randid, ':title' => $title, ':tagline' => $tagling, ':url' => $url, ':userid' => $_SESSION['user_id'], ':timest' => time())); 45 | 46 | $query = $conn->prepare("INSERT INTO votes (post_id,user_id, op_id) VALUES (:post, :user, :opid)"); 47 | $query->execute(array(':post'=>$randid, ':user' => $_SESSION['user_id'], ':opid' => $_SESSION['user_id'])); 48 | print $randid; 49 | 50 | } 51 | } 52 | else 53 | { 54 | echo "User out"; 55 | } 56 | ?> 57 | -------------------------------------------------------------------------------- /api/twa.php: -------------------------------------------------------------------------------- 1 | http_status; } 54 | function lastAPICall() { return $this->last_api_call; } 55 | 56 | /** 57 | * construct TwitterOAuth object 58 | */ 59 | function __construct($consumer_key, $consumer_secret, $oauth_token = NULL, $oauth_token_secret = NULL) { 60 | $this->sha1_method = new OAuthSignatureMethod_HMAC_SHA1(); 61 | $this->consumer = new OAuthConsumer($consumer_key, $consumer_secret); 62 | if (!empty($oauth_token) && !empty($oauth_token_secret)) { 63 | $this->token = new OAuthConsumer($oauth_token, $oauth_token_secret); 64 | } else { 65 | $this->token = NULL; 66 | } 67 | } 68 | 69 | 70 | /** 71 | * Get a request_token from Twitter 72 | * 73 | * @returns a key/value array containing oauth_token and oauth_token_secret 74 | */ 75 | function getRequestToken($oauth_callback) { 76 | $parameters = array(); 77 | $parameters['oauth_callback'] = $oauth_callback; 78 | $request = $this->oAuthRequest($this->requestTokenURL(), 'GET', $parameters); 79 | $token = OAuthUtil::parse_parameters($request); 80 | $this->token = new OAuthConsumer($token['oauth_token'], $token['oauth_token_secret']); 81 | return $token; 82 | } 83 | 84 | /** 85 | * Get the authorize URL 86 | * 87 | * @returns a string 88 | */ 89 | function getAuthorizeURL($token, $sign_in_with_twitter = TRUE) { 90 | if (is_array($token)) { 91 | $token = $token['oauth_token']; 92 | } 93 | if (empty($sign_in_with_twitter)) { 94 | return $this->authorizeURL() . "?oauth_token={$token}"; 95 | } else { 96 | return $this->authenticateURL() . "?oauth_token={$token}"; 97 | } 98 | } 99 | 100 | /** 101 | * Exchange request token and secret for an access token and 102 | * secret, to sign API calls. 103 | * 104 | * @returns array("oauth_token" => "the-access-token", 105 | * "oauth_token_secret" => "the-access-secret", 106 | * "user_id" => "9436992", 107 | * "screen_name" => "abraham") 108 | */ 109 | function getAccessToken($oauth_verifier) { 110 | $parameters = array(); 111 | $parameters['oauth_verifier'] = $oauth_verifier; 112 | $request = $this->oAuthRequest($this->accessTokenURL(), 'GET', $parameters); 113 | $token = OAuthUtil::parse_parameters($request); 114 | $this->token = new OAuthConsumer($token['oauth_token'], $token['oauth_token_secret']); 115 | return $token; 116 | } 117 | 118 | /** 119 | * One time exchange of username and password for access token and secret. 120 | * 121 | * @returns array("oauth_token" => "the-access-token", 122 | * "oauth_token_secret" => "the-access-secret", 123 | * "user_id" => "9436992", 124 | * "screen_name" => "abraham", 125 | * "x_auth_expires" => "0") 126 | */ 127 | function getXAuthToken($username, $password) { 128 | $parameters = array(); 129 | $parameters['x_auth_username'] = $username; 130 | $parameters['x_auth_password'] = $password; 131 | $parameters['x_auth_mode'] = 'client_auth'; 132 | $request = $this->oAuthRequest($this->accessTokenURL(), 'POST', $parameters); 133 | $token = OAuthUtil::parse_parameters($request); 134 | $this->token = new OAuthConsumer($token['oauth_token'], $token['oauth_token_secret']); 135 | return $token; 136 | } 137 | 138 | /** 139 | * GET wrapper for oAuthRequest. 140 | */ 141 | function get($url, $parameters = array()) { 142 | $response = $this->oAuthRequest($url, 'GET', $parameters); 143 | if ($this->format === 'json' && $this->decode_json) { 144 | return json_decode($response); 145 | } 146 | return $response; 147 | } 148 | 149 | /** 150 | * POST wrapper for oAuthRequest. 151 | */ 152 | function post($url, $parameters = array()) { 153 | $response = $this->oAuthRequest($url, 'POST', $parameters); 154 | if ($this->format === 'json' && $this->decode_json) { 155 | return json_decode($response); 156 | } 157 | return $response; 158 | } 159 | 160 | /** 161 | * DELETE wrapper for oAuthReqeust. 162 | */ 163 | function delete($url, $parameters = array()) { 164 | $response = $this->oAuthRequest($url, 'DELETE', $parameters); 165 | if ($this->format === 'json' && $this->decode_json) { 166 | return json_decode($response); 167 | } 168 | return $response; 169 | } 170 | 171 | /** 172 | * Format and sign an OAuth / API request 173 | */ 174 | function oAuthRequest($url, $method, $parameters) { 175 | if (strrpos($url, 'https://') !== 0 && strrpos($url, 'http://') !== 0) { 176 | $url = "{$this->host}{$url}.{$this->format}"; 177 | } 178 | $request = OAuthRequest::from_consumer_and_token($this->consumer, $this->token, $method, $url, $parameters); 179 | $request->sign_request($this->sha1_method, $this->consumer, $this->token); 180 | switch ($method) { 181 | case 'GET': 182 | return $this->http($request->to_url(), 'GET'); 183 | default: 184 | return $this->http($request->get_normalized_http_url(), $method, $request->to_postdata()); 185 | } 186 | } 187 | 188 | /** 189 | * Make an HTTP request 190 | * 191 | * @return API results 192 | */ 193 | function http($url, $method, $postfields = NULL) { 194 | $this->http_info = array(); 195 | $ci = curl_init(); 196 | /* Curl settings */ 197 | curl_setopt($ci, CURLOPT_USERAGENT, $this->useragent); 198 | curl_setopt($ci, CURLOPT_CONNECTTIMEOUT, $this->connecttimeout); 199 | curl_setopt($ci, CURLOPT_TIMEOUT, $this->timeout); 200 | curl_setopt($ci, CURLOPT_RETURNTRANSFER, TRUE); 201 | curl_setopt($ci, CURLOPT_HTTPHEADER, array('Expect:')); 202 | curl_setopt($ci, CURLOPT_SSL_VERIFYPEER, $this->ssl_verifypeer); 203 | curl_setopt($ci, CURLOPT_HEADERFUNCTION, array($this, 'getHeader')); 204 | curl_setopt($ci, CURLOPT_HEADER, FALSE); 205 | 206 | switch ($method) { 207 | case 'POST': 208 | curl_setopt($ci, CURLOPT_POST, TRUE); 209 | if (!empty($postfields)) { 210 | curl_setopt($ci, CURLOPT_POSTFIELDS, $postfields); 211 | } 212 | break; 213 | case 'DELETE': 214 | curl_setopt($ci, CURLOPT_CUSTOMREQUEST, 'DELETE'); 215 | if (!empty($postfields)) { 216 | $url = "{$url}?{$postfields}"; 217 | } 218 | } 219 | 220 | curl_setopt($ci, CURLOPT_URL, $url); 221 | $response = curl_exec($ci); 222 | $this->http_code = curl_getinfo($ci, CURLINFO_HTTP_CODE); 223 | $this->http_info = array_merge($this->http_info, curl_getinfo($ci)); 224 | $this->url = $url; 225 | curl_close ($ci); 226 | return $response; 227 | } 228 | 229 | /** 230 | * Get the header info to store. 231 | */ 232 | function getHeader($ch, $header) { 233 | $i = strpos($header, ':'); 234 | if (!empty($i)) { 235 | $key = str_replace('-', '_', strtolower(substr($header, 0, $i))); 236 | $value = trim(substr($header, $i + 2)); 237 | $this->http_header[$key] = $value; 238 | } 239 | return strlen($header); 240 | } 241 | } 242 | -------------------------------------------------------------------------------- /api/ustatus.php: -------------------------------------------------------------------------------- 1 | $user_status]; 20 | return json_encode($array); 21 | 22 | } 23 | else 24 | { 25 | $cookie_name = "info"; 26 | $cvalue = $_COOKIE[$cookie_name]; 27 | 28 | $query = $conn->prepare("SELECT * FROM users WHERE token = :token LIMIT 1"); 29 | $query->bindValue(':token', $cvalue, PDO::PARAM_STR); 30 | $query->execute(); 31 | $rows = $query->fetchAll(PDO::FETCH_ASSOC); 32 | 33 | if($rows) 34 | { 35 | $token = $rows[0]['twitter_token']; 36 | $secret = $rows[0]['twitter_secret']; 37 | $user_id = $rows[0]['userid']; 38 | 39 | 40 | $connection = new TwitterOAuth(CONSUMER_KEY, CONSUMER_SECRET, $token , $secret); 41 | $content = $connection->get('account/verify_credentials'); 42 | 43 | $arrays = objectToArray($content); 44 | $userid = $arrays['id']; 45 | $name = $arrays['name']; 46 | $propic = $arrays['profile_image_url']; 47 | 48 | if($user_id==$userid) 49 | { 50 | 51 | $_SESSION['user_status'] = $user_status = true; 52 | $_SESSION['user_id'] = $user_id; 53 | $_SESSION['myname'] = $name; 54 | $_SESSION['propic'] = $propic; 55 | 56 | $array = [ 57 | user_id => $_SESSION['user_id'], 58 | user_status => $_SESSION['user_status'], 59 | name => $name, 60 | propic => $propic]; 61 | 62 | $query = $conn->prepare("UPDATE users 63 | SET name = :name 64 | , propic = :propic WHERE userid= :usserid"); 65 | 66 | $query->bindValue(':name', $name); 67 | $query->bindValue(':propic', $propic); 68 | $query->bindValue(':usserid', $userid); 69 | return json_encode($array); 70 | } 71 | else 72 | { 73 | session_destroy(); 74 | $user_status = false; 75 | $array = [user_status => $user_status]; 76 | return json_encode($array); 77 | 78 | } 79 | } 80 | else 81 | { 82 | session_destroy(); 83 | $user_status = false; 84 | $array = [user_status => $user_status]; 85 | return json_encode($array); 86 | } 87 | } 88 | 89 | } 90 | elseif($_SESSION['user_status'] == true) 91 | { 92 | 93 | $array = [ 94 | 'user_id' => $_SESSION['user_id'], 95 | 'user_status' => $_SESSION['user_status'], 96 | 'name' => $_SESSION['myname'], 97 | 'propic' => $_SESSION['propic'] 98 | ]; 99 | return json_encode($array); 100 | } 101 | } 102 | 103 | function checkuser() 104 | { 105 | if($_SESSION['user_status'] == true) 106 | { 107 | $array = [ 108 | 'user_id' => $_SESSION['user_id'], 109 | 'user_status' => $_SESSION['user_status'], 110 | 'name' => $_SESSION['myname'], 111 | 'propic' => $_SESSION['propic'] 112 | ]; 113 | return json_encode($array); 114 | } 115 | else 116 | { 117 | userlogin(); 118 | $array = [ 119 | 'user_id' => $_SESSION['user_id'], 120 | 'user_status' => $_SESSION['user_status'], 121 | 'name' => $myname, 122 | 'propic' => $propic 123 | ]; 124 | return json_encode($array); 125 | } 126 | 127 | } 128 | 129 | print(userlogin($conn)); 130 | 131 | ?> 132 | -------------------------------------------------------------------------------- /api/vote.php: -------------------------------------------------------------------------------- 1 | prepare("SELECT * FROM posts WHERE postid = :id LIMIT 1"); 16 | $query->execute(array(':id' => $post)); 17 | $result = $query -> fetch(PDO::FETCH_ASSOC); 18 | $user_id = $result['user']; 19 | 20 | if($user_id == $user) 21 | { 22 | echo "X"; 23 | } 24 | elseif($user_id != $user) 25 | { 26 | $gp = $conn->prepare("SELECT * FROM votes WHERE post_id = :post AND user_id = :user LIMIT 1") or die(); 27 | $gp->execute(array(':post' => $post , ':user' => $user)); 28 | $result = $gp->rowCount(); 29 | 30 | if($result > 0) 31 | { 32 | $dq = $conn->prepare("DELETE FROM votes WHERE post_id = :post AND user_id = :user"); 33 | $dq->execute(array(':post'=> $post, ':user' => $user)); 34 | 35 | $vc = $conn->prepare("SELECT * FROM votes WHERE post_id = :post"); 36 | $vc->execute(array(':post' => $post)); 37 | print $vc->rowCount(); 38 | } 39 | else 40 | { 41 | $aq = $conn->prepare("INSERT INTO votes (post_id,user_id, op_id) VALUES (:post, :user, :op)"); 42 | $aq->execute(array(':post' => $post, ':user' => $user, ':op' => $user_id)); 43 | 44 | $vc = $conn->prepare("SELECT * FROM votes WHERE post_id = :post"); 45 | $vc->execute(array(':post' => $post)); 46 | print $vc->rowCount(); 47 | } 48 | } 49 | } 50 | else 51 | { 52 | echo "L"; 53 | } 54 | ?> 55 | -------------------------------------------------------------------------------- /assets/app.js: -------------------------------------------------------------------------------- 1 | App = Ember.Application.create(); 2 | 3 | //Routers 4 | 5 | App.Router.map(function() { 6 | this.resource('index', {path : '/'} , function() { 7 | this.resource('post', { path: '/p/:post_id' }); 8 | this.resource('login'); 9 | this.resource('about'); 10 | }); 11 | }); 12 | 13 | //Location API, keep auto for browser history API and none for # URL, hashbang for #! URLs to bettter SEO 14 | App.Router.reopen({ 15 | // location: 'auto' 16 | location : 'hashbang' 17 | }); 18 | 19 | //Controllers 20 | App.IndexController = Ember.ObjectController.extend({ 21 | showSubmit: false, 22 | actions : { 23 | toggleSubmit : function() { //showing new submit 24 | this.set('showSubmit', !this.get('showSubmit')); 25 | } 26 | }, 27 | 28 | submit : function(){ //submitting new story 29 | this.set('onsubmit' , true); 30 | var url = this.get("purl"); 31 | var tagline = this.get("ptagline"); 32 | var title = this.get("ptitle"); 33 | this.set("ptitle" , ''); 34 | this.set("ptagline",""); 35 | this.set("purl",""); 36 | 37 | var self= this; 38 | this.set('onsubmit', false); 39 | $.post( "/api/post.php", { purl: url, ptagline: tagline, ptitle: title }) 40 | .done(function(data) { 41 | self.set('showSubmit', !self.get('showSubmit')); 42 | self.transitionTo('post', data); 43 | }); 44 | } 45 | 46 | }); 47 | 48 | App.BookController = Ember.ObjectController.extend({ 49 | 50 | islg : false, 51 | 52 | actions: { 53 | voteup: function(e) { //upvoting function 54 | this.set('isVoted', false); 55 | 56 | var self = this; 57 | $.post('/api/vote.php', {post_id : e}) 58 | .done(function(data){ 59 | if(data == "L") 60 | { 61 | self.set('islg' , true); 62 | } 63 | else 64 | { 65 | self.set('votecount', data); 66 | } 67 | }); 68 | console.log(e); 69 | }, 70 | 71 | votedown: function(e) { //votedown function 72 | this.set('isVoted', true); 73 | var self = this; 74 | $.post('/api/vote.php', {post_id : e}) 75 | .done(function(data){ 76 | if(data == "L") 77 | { 78 | self.set('islg', true); 79 | } 80 | else 81 | { 82 | self.set('votecount', data); 83 | } 84 | }); 85 | console.log(e); 86 | } 87 | } 88 | }); 89 | 90 | App.PostController = Ember.ObjectController.extend({ 91 | voop : false, 92 | islg : false, 93 | actions: { 94 | voteup: function(e) { //vote up fucntion 95 | this.set('uposts.isVoted', false); 96 | var self = this; 97 | $.post('/api/vote.php', {post_id : e}) 98 | .done(function(data){ 99 | if(data == "X") 100 | { 101 | self.set('voop' , true); 102 | } 103 | else if(dara == "L") 104 | { 105 | self.set('islg', true); 106 | } 107 | else 108 | { 109 | self.set('uposts.votecount', data); 110 | } 111 | }); 112 | console.log(e); 113 | }, 114 | 115 | votedown: function(e) { //vote down function 116 | this.set('uposts.isVoted', true); 117 | var self = this; 118 | $.post('/api/vote.php', {post_id : e}) 119 | .done(function(data){ 120 | if(data == "X") 121 | { 122 | self.set('voop' , true); 123 | } 124 | else if(data == "L") 125 | { 126 | self.set('islg', true); 127 | } 128 | else 129 | { 130 | self.set('uposts.votecount', data); 131 | } 132 | }); 133 | console.log(e); 134 | }, 135 | report : function(e){ //report story 136 | $.post('/api/report.php' , {postid : e}) 137 | .done(function(data){ 138 | alert('reported' + data); 139 | }); 140 | }, 141 | 142 | twshare: function(e){ //twitter share 143 | var url = e; 144 | window.location.href = 'http://twitter.com/share?url=http://picnicy.com/p/' + e + '&via=picnicyco&hashtags=tech'; 145 | }, 146 | 147 | fbshare : function(e){ //facebook share 148 | var url = e; 149 | window.location.href = 'https://www.facebook.com/sharer/sharer.php?u=http://picnicy.com/p/' + e; 150 | }, 151 | 152 | submit: function(e){ 153 | var comment = this.get("ncomment"); 154 | var datalink = e; 155 | this.set("ncomment",""); 156 | var self = this; 157 | 158 | $.post('/api/comment.php' , {comment : comment, postid : datalink }) 159 | .done(function(){ 160 | self.send('track'); 161 | }); 162 | } 163 | } 164 | }); 165 | 166 | //Routes 167 | 168 | App.PostRoute = Ember.Route.extend({ //post route 169 | model: function(params) { 170 | return Em.RSVP.hash({ 171 | uposts: $.getJSON('/api/getpost.php' , {postid : params.post_id}), 172 | ustat: $.getJSON('/api/ustatus.php'), 173 | comment : $.getJSON('/api/gc.php', {postid : params.post_id}) 174 | }); 175 | // return posts.findBy('datalink', params.post_id); 176 | }, 177 | actions: { 178 | track: function() { 179 | this.refresh(); 180 | } 181 | } 182 | 183 | 184 | }); 185 | 186 | 187 | 188 | 189 | App.IndexRoute = Ember.Route.extend({ //index route 190 | 191 | model: function(params) { 192 | return Em.RSVP.hash({ 193 | uposts: $.getJSON("/api/indexmaker.php"), 194 | ustat: $.getJSON("/api/ustatus.php") 195 | }); 196 | } 197 | }); 198 | 199 | //Custom text fields 200 | 201 | Ember.TextField.reopen({ //custom text input 202 | attributeBindings: ['required'] 203 | }); 204 | 205 | Ember.LinkView.reopen({ //custom links 206 | attributeBindings: ['class'] 207 | }); 208 | 209 | Ember.TextArea.reopen({ //custom textarea 210 | attributeBindings: ['required' , 'class', 'rows'] 211 | }); 212 | 213 | 214 | //helpers 215 | 216 | Ember.Handlebars.helper('format-date', function(datem) { 217 | return moment(datem, 'X').fromNow(); 218 | }); 219 | 220 | Ember.Handlebars.helper('format-url', function(url) { 221 | pathArray = String(url).split( '/' ); 222 | return pathArray[2]; 223 | }); 224 | 225 | var loading = {"loading":true}; 226 | 227 | 228 | $("img").error(function () { 229 | $(this).unbind("error").attr("src", "http://www.piraten-oberpfalz.de/files/2009/08/orange-twitter-egg.jpg"); 230 | }); 231 | 232 | //The hashbang for better Goolge crawling 233 | 234 | Ember.Location.registerImplementation('hashbang', Ember.HashLocation.extend({ 235 | getURL: function () { 236 | return Ember.get(this, 'location').hash.substr(2); 237 | }, 238 | 239 | setURL: function (path) { 240 | Ember.get(this, 'location').hash = '!' + path; 241 | Ember.set(this, 'lastSetURL', '!' + path); 242 | }, 243 | 244 | onUpdateURL: function (callback) { 245 | var self = this; 246 | var guid = Ember.guidFor(this); 247 | 248 | Ember.$(window).bind('hashchange.ember-location-' + guid, function () { 249 | Ember.run(function() { 250 | var path = location.hash.substr(2); 251 | if (Ember.get(self, 'lastSetURL') === path) { return; } 252 | Ember.set(self, 'lastSetURL', null); 253 | callback(path); 254 | }); 255 | }); 256 | }, 257 | 258 | formatURL: function (url) { 259 | return '#!' + url; 260 | } 261 | })); 262 | 263 | -------------------------------------------------------------------------------- /assets/handlebars-v1.3.0.js: -------------------------------------------------------------------------------- 1 | /*! 2 | 3 | handlebars v1.3.0 4 | 5 | Copyright (C) 2011 by Yehuda Katz 6 | 7 | Permission is hereby granted, free of charge, to any person obtaining a copy 8 | of this software and associated documentation files (the "Software"), to deal 9 | in the Software without restriction, including without limitation the rights 10 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | copies of the Software, and to permit persons to whom the Software is 12 | furnished to do so, subject to the following conditions: 13 | 14 | The above copyright notice and this permission notice shall be included in 15 | all copies or substantial portions of the Software. 16 | 17 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 23 | THE SOFTWARE. 24 | 25 | @license 26 | */ 27 | /* exported Handlebars */ 28 | var Handlebars = (function() { 29 | // handlebars/safe-string.js 30 | var __module4__ = (function() { 31 | "use strict"; 32 | var __exports__; 33 | // Build out our basic SafeString type 34 | function SafeString(string) { 35 | this.string = string; 36 | } 37 | 38 | SafeString.prototype.toString = function() { 39 | return "" + this.string; 40 | }; 41 | 42 | __exports__ = SafeString; 43 | return __exports__; 44 | })(); 45 | 46 | // handlebars/utils.js 47 | var __module3__ = (function(__dependency1__) { 48 | "use strict"; 49 | var __exports__ = {}; 50 | /*jshint -W004 */ 51 | var SafeString = __dependency1__; 52 | 53 | var escape = { 54 | "&": "&", 55 | "<": "<", 56 | ">": ">", 57 | '"': """, 58 | "'": "'", 59 | "`": "`" 60 | }; 61 | 62 | var badChars = /[&<>"'`]/g; 63 | var possible = /[&<>"'`]/; 64 | 65 | function escapeChar(chr) { 66 | return escape[chr] || "&"; 67 | } 68 | 69 | function extend(obj, value) { 70 | for(var key in value) { 71 | if(Object.prototype.hasOwnProperty.call(value, key)) { 72 | obj[key] = value[key]; 73 | } 74 | } 75 | } 76 | 77 | __exports__.extend = extend;var toString = Object.prototype.toString; 78 | __exports__.toString = toString; 79 | // Sourced from lodash 80 | // https://github.com/bestiejs/lodash/blob/master/LICENSE.txt 81 | var isFunction = function(value) { 82 | return typeof value === 'function'; 83 | }; 84 | // fallback for older versions of Chrome and Safari 85 | if (isFunction(/x/)) { 86 | isFunction = function(value) { 87 | return typeof value === 'function' && toString.call(value) === '[object Function]'; 88 | }; 89 | } 90 | var isFunction; 91 | __exports__.isFunction = isFunction; 92 | var isArray = Array.isArray || function(value) { 93 | return (value && typeof value === 'object') ? toString.call(value) === '[object Array]' : false; 94 | }; 95 | __exports__.isArray = isArray; 96 | 97 | function escapeExpression(string) { 98 | // don't escape SafeStrings, since they're already safe 99 | if (string instanceof SafeString) { 100 | return string.toString(); 101 | } else if (!string && string !== 0) { 102 | return ""; 103 | } 104 | 105 | // Force a string conversion as this will be done by the append regardless and 106 | // the regex test will do this transparently behind the scenes, causing issues if 107 | // an object's to string has escaped characters in it. 108 | string = "" + string; 109 | 110 | if(!possible.test(string)) { return string; } 111 | return string.replace(badChars, escapeChar); 112 | } 113 | 114 | __exports__.escapeExpression = escapeExpression;function isEmpty(value) { 115 | if (!value && value !== 0) { 116 | return true; 117 | } else if (isArray(value) && value.length === 0) { 118 | return true; 119 | } else { 120 | return false; 121 | } 122 | } 123 | 124 | __exports__.isEmpty = isEmpty; 125 | return __exports__; 126 | })(__module4__); 127 | 128 | // handlebars/exception.js 129 | var __module5__ = (function() { 130 | "use strict"; 131 | var __exports__; 132 | 133 | var errorProps = ['description', 'fileName', 'lineNumber', 'message', 'name', 'number', 'stack']; 134 | 135 | function Exception(message, node) { 136 | var line; 137 | if (node && node.firstLine) { 138 | line = node.firstLine; 139 | 140 | message += ' - ' + line + ':' + node.firstColumn; 141 | } 142 | 143 | var tmp = Error.prototype.constructor.call(this, message); 144 | 145 | // Unfortunately errors are not enumerable in Chrome (at least), so `for prop in tmp` doesn't work. 146 | for (var idx = 0; idx < errorProps.length; idx++) { 147 | this[errorProps[idx]] = tmp[errorProps[idx]]; 148 | } 149 | 150 | if (line) { 151 | this.lineNumber = line; 152 | this.column = node.firstColumn; 153 | } 154 | } 155 | 156 | Exception.prototype = new Error(); 157 | 158 | __exports__ = Exception; 159 | return __exports__; 160 | })(); 161 | 162 | // handlebars/base.js 163 | var __module2__ = (function(__dependency1__, __dependency2__) { 164 | "use strict"; 165 | var __exports__ = {}; 166 | var Utils = __dependency1__; 167 | var Exception = __dependency2__; 168 | 169 | var VERSION = "1.3.0"; 170 | __exports__.VERSION = VERSION;var COMPILER_REVISION = 4; 171 | __exports__.COMPILER_REVISION = COMPILER_REVISION; 172 | var REVISION_CHANGES = { 173 | 1: '<= 1.0.rc.2', // 1.0.rc.2 is actually rev2 but doesn't report it 174 | 2: '== 1.0.0-rc.3', 175 | 3: '== 1.0.0-rc.4', 176 | 4: '>= 1.0.0' 177 | }; 178 | __exports__.REVISION_CHANGES = REVISION_CHANGES; 179 | var isArray = Utils.isArray, 180 | isFunction = Utils.isFunction, 181 | toString = Utils.toString, 182 | objectType = '[object Object]'; 183 | 184 | function HandlebarsEnvironment(helpers, partials) { 185 | this.helpers = helpers || {}; 186 | this.partials = partials || {}; 187 | 188 | registerDefaultHelpers(this); 189 | } 190 | 191 | __exports__.HandlebarsEnvironment = HandlebarsEnvironment;HandlebarsEnvironment.prototype = { 192 | constructor: HandlebarsEnvironment, 193 | 194 | logger: logger, 195 | log: log, 196 | 197 | registerHelper: function(name, fn, inverse) { 198 | if (toString.call(name) === objectType) { 199 | if (inverse || fn) { throw new Exception('Arg not supported with multiple helpers'); } 200 | Utils.extend(this.helpers, name); 201 | } else { 202 | if (inverse) { fn.not = inverse; } 203 | this.helpers[name] = fn; 204 | } 205 | }, 206 | 207 | registerPartial: function(name, str) { 208 | if (toString.call(name) === objectType) { 209 | Utils.extend(this.partials, name); 210 | } else { 211 | this.partials[name] = str; 212 | } 213 | } 214 | }; 215 | 216 | function registerDefaultHelpers(instance) { 217 | instance.registerHelper('helperMissing', function(arg) { 218 | if(arguments.length === 2) { 219 | return undefined; 220 | } else { 221 | throw new Exception("Missing helper: '" + arg + "'"); 222 | } 223 | }); 224 | 225 | instance.registerHelper('blockHelperMissing', function(context, options) { 226 | var inverse = options.inverse || function() {}, fn = options.fn; 227 | 228 | if (isFunction(context)) { context = context.call(this); } 229 | 230 | if(context === true) { 231 | return fn(this); 232 | } else if(context === false || context == null) { 233 | return inverse(this); 234 | } else if (isArray(context)) { 235 | if(context.length > 0) { 236 | return instance.helpers.each(context, options); 237 | } else { 238 | return inverse(this); 239 | } 240 | } else { 241 | return fn(context); 242 | } 243 | }); 244 | 245 | instance.registerHelper('each', function(context, options) { 246 | var fn = options.fn, inverse = options.inverse; 247 | var i = 0, ret = "", data; 248 | 249 | if (isFunction(context)) { context = context.call(this); } 250 | 251 | if (options.data) { 252 | data = createFrame(options.data); 253 | } 254 | 255 | if(context && typeof context === 'object') { 256 | if (isArray(context)) { 257 | for(var j = context.length; i 0) { 691 | throw new Exception("Invalid path: " + original, this); 692 | } else if (part === "..") { 693 | depth++; 694 | } else { 695 | this.isScoped = true; 696 | } 697 | } else { 698 | dig.push(part); 699 | } 700 | } 701 | 702 | this.original = original; 703 | this.parts = dig; 704 | this.string = dig.join('.'); 705 | this.depth = depth; 706 | 707 | // an ID is simple if it only has one part, and that part is not 708 | // `..` or `this`. 709 | this.isSimple = parts.length === 1 && !this.isScoped && depth === 0; 710 | 711 | this.stringModeValue = this.string; 712 | }, 713 | 714 | PartialNameNode: function(name, locInfo) { 715 | LocationInfo.call(this, locInfo); 716 | this.type = "PARTIAL_NAME"; 717 | this.name = name.original; 718 | }, 719 | 720 | DataNode: function(id, locInfo) { 721 | LocationInfo.call(this, locInfo); 722 | this.type = "DATA"; 723 | this.id = id; 724 | }, 725 | 726 | StringNode: function(string, locInfo) { 727 | LocationInfo.call(this, locInfo); 728 | this.type = "STRING"; 729 | this.original = 730 | this.string = 731 | this.stringModeValue = string; 732 | }, 733 | 734 | IntegerNode: function(integer, locInfo) { 735 | LocationInfo.call(this, locInfo); 736 | this.type = "INTEGER"; 737 | this.original = 738 | this.integer = integer; 739 | this.stringModeValue = Number(integer); 740 | }, 741 | 742 | BooleanNode: function(bool, locInfo) { 743 | LocationInfo.call(this, locInfo); 744 | this.type = "BOOLEAN"; 745 | this.bool = bool; 746 | this.stringModeValue = bool === "true"; 747 | }, 748 | 749 | CommentNode: function(comment, locInfo) { 750 | LocationInfo.call(this, locInfo); 751 | this.type = "comment"; 752 | this.comment = comment; 753 | } 754 | }; 755 | 756 | // Must be exported as an object rather than the root of the module as the jison lexer 757 | // most modify the object to operate properly. 758 | __exports__ = AST; 759 | return __exports__; 760 | })(__module5__); 761 | 762 | // handlebars/compiler/parser.js 763 | var __module9__ = (function() { 764 | "use strict"; 765 | var __exports__; 766 | /* jshint ignore:start */ 767 | /* Jison generated parser */ 768 | var handlebars = (function(){ 769 | var parser = {trace: function trace() { }, 770 | yy: {}, 771 | symbols_: {"error":2,"root":3,"statements":4,"EOF":5,"program":6,"simpleInverse":7,"statement":8,"openInverse":9,"closeBlock":10,"openBlock":11,"mustache":12,"partial":13,"CONTENT":14,"COMMENT":15,"OPEN_BLOCK":16,"sexpr":17,"CLOSE":18,"OPEN_INVERSE":19,"OPEN_ENDBLOCK":20,"path":21,"OPEN":22,"OPEN_UNESCAPED":23,"CLOSE_UNESCAPED":24,"OPEN_PARTIAL":25,"partialName":26,"partial_option0":27,"sexpr_repetition0":28,"sexpr_option0":29,"dataName":30,"param":31,"STRING":32,"INTEGER":33,"BOOLEAN":34,"OPEN_SEXPR":35,"CLOSE_SEXPR":36,"hash":37,"hash_repetition_plus0":38,"hashSegment":39,"ID":40,"EQUALS":41,"DATA":42,"pathSegments":43,"SEP":44,"$accept":0,"$end":1}, 772 | terminals_: {2:"error",5:"EOF",14:"CONTENT",15:"COMMENT",16:"OPEN_BLOCK",18:"CLOSE",19:"OPEN_INVERSE",20:"OPEN_ENDBLOCK",22:"OPEN",23:"OPEN_UNESCAPED",24:"CLOSE_UNESCAPED",25:"OPEN_PARTIAL",32:"STRING",33:"INTEGER",34:"BOOLEAN",35:"OPEN_SEXPR",36:"CLOSE_SEXPR",40:"ID",41:"EQUALS",42:"DATA",44:"SEP"}, 773 | productions_: [0,[3,2],[3,1],[6,2],[6,3],[6,2],[6,1],[6,1],[6,0],[4,1],[4,2],[8,3],[8,3],[8,1],[8,1],[8,1],[8,1],[11,3],[9,3],[10,3],[12,3],[12,3],[13,4],[7,2],[17,3],[17,1],[31,1],[31,1],[31,1],[31,1],[31,1],[31,3],[37,1],[39,3],[26,1],[26,1],[26,1],[30,2],[21,1],[43,3],[43,1],[27,0],[27,1],[28,0],[28,2],[29,0],[29,1],[38,1],[38,2]], 774 | performAction: function anonymous(yytext,yyleng,yylineno,yy,yystate,$$,_$) { 775 | 776 | var $0 = $$.length - 1; 777 | switch (yystate) { 778 | case 1: return new yy.ProgramNode($$[$0-1], this._$); 779 | break; 780 | case 2: return new yy.ProgramNode([], this._$); 781 | break; 782 | case 3:this.$ = new yy.ProgramNode([], $$[$0-1], $$[$0], this._$); 783 | break; 784 | case 4:this.$ = new yy.ProgramNode($$[$0-2], $$[$0-1], $$[$0], this._$); 785 | break; 786 | case 5:this.$ = new yy.ProgramNode($$[$0-1], $$[$0], [], this._$); 787 | break; 788 | case 6:this.$ = new yy.ProgramNode($$[$0], this._$); 789 | break; 790 | case 7:this.$ = new yy.ProgramNode([], this._$); 791 | break; 792 | case 8:this.$ = new yy.ProgramNode([], this._$); 793 | break; 794 | case 9:this.$ = [$$[$0]]; 795 | break; 796 | case 10: $$[$0-1].push($$[$0]); this.$ = $$[$0-1]; 797 | break; 798 | case 11:this.$ = new yy.BlockNode($$[$0-2], $$[$0-1].inverse, $$[$0-1], $$[$0], this._$); 799 | break; 800 | case 12:this.$ = new yy.BlockNode($$[$0-2], $$[$0-1], $$[$0-1].inverse, $$[$0], this._$); 801 | break; 802 | case 13:this.$ = $$[$0]; 803 | break; 804 | case 14:this.$ = $$[$0]; 805 | break; 806 | case 15:this.$ = new yy.ContentNode($$[$0], this._$); 807 | break; 808 | case 16:this.$ = new yy.CommentNode($$[$0], this._$); 809 | break; 810 | case 17:this.$ = new yy.MustacheNode($$[$0-1], null, $$[$0-2], stripFlags($$[$0-2], $$[$0]), this._$); 811 | break; 812 | case 18:this.$ = new yy.MustacheNode($$[$0-1], null, $$[$0-2], stripFlags($$[$0-2], $$[$0]), this._$); 813 | break; 814 | case 19:this.$ = {path: $$[$0-1], strip: stripFlags($$[$0-2], $$[$0])}; 815 | break; 816 | case 20:this.$ = new yy.MustacheNode($$[$0-1], null, $$[$0-2], stripFlags($$[$0-2], $$[$0]), this._$); 817 | break; 818 | case 21:this.$ = new yy.MustacheNode($$[$0-1], null, $$[$0-2], stripFlags($$[$0-2], $$[$0]), this._$); 819 | break; 820 | case 22:this.$ = new yy.PartialNode($$[$0-2], $$[$0-1], stripFlags($$[$0-3], $$[$0]), this._$); 821 | break; 822 | case 23:this.$ = stripFlags($$[$0-1], $$[$0]); 823 | break; 824 | case 24:this.$ = new yy.SexprNode([$$[$0-2]].concat($$[$0-1]), $$[$0], this._$); 825 | break; 826 | case 25:this.$ = new yy.SexprNode([$$[$0]], null, this._$); 827 | break; 828 | case 26:this.$ = $$[$0]; 829 | break; 830 | case 27:this.$ = new yy.StringNode($$[$0], this._$); 831 | break; 832 | case 28:this.$ = new yy.IntegerNode($$[$0], this._$); 833 | break; 834 | case 29:this.$ = new yy.BooleanNode($$[$0], this._$); 835 | break; 836 | case 30:this.$ = $$[$0]; 837 | break; 838 | case 31:$$[$0-1].isHelper = true; this.$ = $$[$0-1]; 839 | break; 840 | case 32:this.$ = new yy.HashNode($$[$0], this._$); 841 | break; 842 | case 33:this.$ = [$$[$0-2], $$[$0]]; 843 | break; 844 | case 34:this.$ = new yy.PartialNameNode($$[$0], this._$); 845 | break; 846 | case 35:this.$ = new yy.PartialNameNode(new yy.StringNode($$[$0], this._$), this._$); 847 | break; 848 | case 36:this.$ = new yy.PartialNameNode(new yy.IntegerNode($$[$0], this._$)); 849 | break; 850 | case 37:this.$ = new yy.DataNode($$[$0], this._$); 851 | break; 852 | case 38:this.$ = new yy.IdNode($$[$0], this._$); 853 | break; 854 | case 39: $$[$0-2].push({part: $$[$0], separator: $$[$0-1]}); this.$ = $$[$0-2]; 855 | break; 856 | case 40:this.$ = [{part: $$[$0]}]; 857 | break; 858 | case 43:this.$ = []; 859 | break; 860 | case 44:$$[$0-1].push($$[$0]); 861 | break; 862 | case 47:this.$ = [$$[$0]]; 863 | break; 864 | case 48:$$[$0-1].push($$[$0]); 865 | break; 866 | } 867 | }, 868 | table: [{3:1,4:2,5:[1,3],8:4,9:5,11:6,12:7,13:8,14:[1,9],15:[1,10],16:[1,12],19:[1,11],22:[1,13],23:[1,14],25:[1,15]},{1:[3]},{5:[1,16],8:17,9:5,11:6,12:7,13:8,14:[1,9],15:[1,10],16:[1,12],19:[1,11],22:[1,13],23:[1,14],25:[1,15]},{1:[2,2]},{5:[2,9],14:[2,9],15:[2,9],16:[2,9],19:[2,9],20:[2,9],22:[2,9],23:[2,9],25:[2,9]},{4:20,6:18,7:19,8:4,9:5,11:6,12:7,13:8,14:[1,9],15:[1,10],16:[1,12],19:[1,21],20:[2,8],22:[1,13],23:[1,14],25:[1,15]},{4:20,6:22,7:19,8:4,9:5,11:6,12:7,13:8,14:[1,9],15:[1,10],16:[1,12],19:[1,21],20:[2,8],22:[1,13],23:[1,14],25:[1,15]},{5:[2,13],14:[2,13],15:[2,13],16:[2,13],19:[2,13],20:[2,13],22:[2,13],23:[2,13],25:[2,13]},{5:[2,14],14:[2,14],15:[2,14],16:[2,14],19:[2,14],20:[2,14],22:[2,14],23:[2,14],25:[2,14]},{5:[2,15],14:[2,15],15:[2,15],16:[2,15],19:[2,15],20:[2,15],22:[2,15],23:[2,15],25:[2,15]},{5:[2,16],14:[2,16],15:[2,16],16:[2,16],19:[2,16],20:[2,16],22:[2,16],23:[2,16],25:[2,16]},{17:23,21:24,30:25,40:[1,28],42:[1,27],43:26},{17:29,21:24,30:25,40:[1,28],42:[1,27],43:26},{17:30,21:24,30:25,40:[1,28],42:[1,27],43:26},{17:31,21:24,30:25,40:[1,28],42:[1,27],43:26},{21:33,26:32,32:[1,34],33:[1,35],40:[1,28],43:26},{1:[2,1]},{5:[2,10],14:[2,10],15:[2,10],16:[2,10],19:[2,10],20:[2,10],22:[2,10],23:[2,10],25:[2,10]},{10:36,20:[1,37]},{4:38,8:4,9:5,11:6,12:7,13:8,14:[1,9],15:[1,10],16:[1,12],19:[1,11],20:[2,7],22:[1,13],23:[1,14],25:[1,15]},{7:39,8:17,9:5,11:6,12:7,13:8,14:[1,9],15:[1,10],16:[1,12],19:[1,21],20:[2,6],22:[1,13],23:[1,14],25:[1,15]},{17:23,18:[1,40],21:24,30:25,40:[1,28],42:[1,27],43:26},{10:41,20:[1,37]},{18:[1,42]},{18:[2,43],24:[2,43],28:43,32:[2,43],33:[2,43],34:[2,43],35:[2,43],36:[2,43],40:[2,43],42:[2,43]},{18:[2,25],24:[2,25],36:[2,25]},{18:[2,38],24:[2,38],32:[2,38],33:[2,38],34:[2,38],35:[2,38],36:[2,38],40:[2,38],42:[2,38],44:[1,44]},{21:45,40:[1,28],43:26},{18:[2,40],24:[2,40],32:[2,40],33:[2,40],34:[2,40],35:[2,40],36:[2,40],40:[2,40],42:[2,40],44:[2,40]},{18:[1,46]},{18:[1,47]},{24:[1,48]},{18:[2,41],21:50,27:49,40:[1,28],43:26},{18:[2,34],40:[2,34]},{18:[2,35],40:[2,35]},{18:[2,36],40:[2,36]},{5:[2,11],14:[2,11],15:[2,11],16:[2,11],19:[2,11],20:[2,11],22:[2,11],23:[2,11],25:[2,11]},{21:51,40:[1,28],43:26},{8:17,9:5,11:6,12:7,13:8,14:[1,9],15:[1,10],16:[1,12],19:[1,11],20:[2,3],22:[1,13],23:[1,14],25:[1,15]},{4:52,8:4,9:5,11:6,12:7,13:8,14:[1,9],15:[1,10],16:[1,12],19:[1,11],20:[2,5],22:[1,13],23:[1,14],25:[1,15]},{14:[2,23],15:[2,23],16:[2,23],19:[2,23],20:[2,23],22:[2,23],23:[2,23],25:[2,23]},{5:[2,12],14:[2,12],15:[2,12],16:[2,12],19:[2,12],20:[2,12],22:[2,12],23:[2,12],25:[2,12]},{14:[2,18],15:[2,18],16:[2,18],19:[2,18],20:[2,18],22:[2,18],23:[2,18],25:[2,18]},{18:[2,45],21:56,24:[2,45],29:53,30:60,31:54,32:[1,57],33:[1,58],34:[1,59],35:[1,61],36:[2,45],37:55,38:62,39:63,40:[1,64],42:[1,27],43:26},{40:[1,65]},{18:[2,37],24:[2,37],32:[2,37],33:[2,37],34:[2,37],35:[2,37],36:[2,37],40:[2,37],42:[2,37]},{14:[2,17],15:[2,17],16:[2,17],19:[2,17],20:[2,17],22:[2,17],23:[2,17],25:[2,17]},{5:[2,20],14:[2,20],15:[2,20],16:[2,20],19:[2,20],20:[2,20],22:[2,20],23:[2,20],25:[2,20]},{5:[2,21],14:[2,21],15:[2,21],16:[2,21],19:[2,21],20:[2,21],22:[2,21],23:[2,21],25:[2,21]},{18:[1,66]},{18:[2,42]},{18:[1,67]},{8:17,9:5,11:6,12:7,13:8,14:[1,9],15:[1,10],16:[1,12],19:[1,11],20:[2,4],22:[1,13],23:[1,14],25:[1,15]},{18:[2,24],24:[2,24],36:[2,24]},{18:[2,44],24:[2,44],32:[2,44],33:[2,44],34:[2,44],35:[2,44],36:[2,44],40:[2,44],42:[2,44]},{18:[2,46],24:[2,46],36:[2,46]},{18:[2,26],24:[2,26],32:[2,26],33:[2,26],34:[2,26],35:[2,26],36:[2,26],40:[2,26],42:[2,26]},{18:[2,27],24:[2,27],32:[2,27],33:[2,27],34:[2,27],35:[2,27],36:[2,27],40:[2,27],42:[2,27]},{18:[2,28],24:[2,28],32:[2,28],33:[2,28],34:[2,28],35:[2,28],36:[2,28],40:[2,28],42:[2,28]},{18:[2,29],24:[2,29],32:[2,29],33:[2,29],34:[2,29],35:[2,29],36:[2,29],40:[2,29],42:[2,29]},{18:[2,30],24:[2,30],32:[2,30],33:[2,30],34:[2,30],35:[2,30],36:[2,30],40:[2,30],42:[2,30]},{17:68,21:24,30:25,40:[1,28],42:[1,27],43:26},{18:[2,32],24:[2,32],36:[2,32],39:69,40:[1,70]},{18:[2,47],24:[2,47],36:[2,47],40:[2,47]},{18:[2,40],24:[2,40],32:[2,40],33:[2,40],34:[2,40],35:[2,40],36:[2,40],40:[2,40],41:[1,71],42:[2,40],44:[2,40]},{18:[2,39],24:[2,39],32:[2,39],33:[2,39],34:[2,39],35:[2,39],36:[2,39],40:[2,39],42:[2,39],44:[2,39]},{5:[2,22],14:[2,22],15:[2,22],16:[2,22],19:[2,22],20:[2,22],22:[2,22],23:[2,22],25:[2,22]},{5:[2,19],14:[2,19],15:[2,19],16:[2,19],19:[2,19],20:[2,19],22:[2,19],23:[2,19],25:[2,19]},{36:[1,72]},{18:[2,48],24:[2,48],36:[2,48],40:[2,48]},{41:[1,71]},{21:56,30:60,31:73,32:[1,57],33:[1,58],34:[1,59],35:[1,61],40:[1,28],42:[1,27],43:26},{18:[2,31],24:[2,31],32:[2,31],33:[2,31],34:[2,31],35:[2,31],36:[2,31],40:[2,31],42:[2,31]},{18:[2,33],24:[2,33],36:[2,33],40:[2,33]}], 869 | defaultActions: {3:[2,2],16:[2,1],50:[2,42]}, 870 | parseError: function parseError(str, hash) { 871 | throw new Error(str); 872 | }, 873 | parse: function parse(input) { 874 | var self = this, stack = [0], vstack = [null], lstack = [], table = this.table, yytext = "", yylineno = 0, yyleng = 0, recovering = 0, TERROR = 2, EOF = 1; 875 | this.lexer.setInput(input); 876 | this.lexer.yy = this.yy; 877 | this.yy.lexer = this.lexer; 878 | this.yy.parser = this; 879 | if (typeof this.lexer.yylloc == "undefined") 880 | this.lexer.yylloc = {}; 881 | var yyloc = this.lexer.yylloc; 882 | lstack.push(yyloc); 883 | var ranges = this.lexer.options && this.lexer.options.ranges; 884 | if (typeof this.yy.parseError === "function") 885 | this.parseError = this.yy.parseError; 886 | function popStack(n) { 887 | stack.length = stack.length - 2 * n; 888 | vstack.length = vstack.length - n; 889 | lstack.length = lstack.length - n; 890 | } 891 | function lex() { 892 | var token; 893 | token = self.lexer.lex() || 1; 894 | if (typeof token !== "number") { 895 | token = self.symbols_[token] || token; 896 | } 897 | return token; 898 | } 899 | var symbol, preErrorSymbol, state, action, a, r, yyval = {}, p, len, newState, expected; 900 | while (true) { 901 | state = stack[stack.length - 1]; 902 | if (this.defaultActions[state]) { 903 | action = this.defaultActions[state]; 904 | } else { 905 | if (symbol === null || typeof symbol == "undefined") { 906 | symbol = lex(); 907 | } 908 | action = table[state] && table[state][symbol]; 909 | } 910 | if (typeof action === "undefined" || !action.length || !action[0]) { 911 | var errStr = ""; 912 | if (!recovering) { 913 | expected = []; 914 | for (p in table[state]) 915 | if (this.terminals_[p] && p > 2) { 916 | expected.push("'" + this.terminals_[p] + "'"); 917 | } 918 | if (this.lexer.showPosition) { 919 | errStr = "Parse error on line " + (yylineno + 1) + ":\n" + this.lexer.showPosition() + "\nExpecting " + expected.join(", ") + ", got '" + (this.terminals_[symbol] || symbol) + "'"; 920 | } else { 921 | errStr = "Parse error on line " + (yylineno + 1) + ": Unexpected " + (symbol == 1?"end of input":"'" + (this.terminals_[symbol] || symbol) + "'"); 922 | } 923 | this.parseError(errStr, {text: this.lexer.match, token: this.terminals_[symbol] || symbol, line: this.lexer.yylineno, loc: yyloc, expected: expected}); 924 | } 925 | } 926 | if (action[0] instanceof Array && action.length > 1) { 927 | throw new Error("Parse Error: multiple actions possible at state: " + state + ", token: " + symbol); 928 | } 929 | switch (action[0]) { 930 | case 1: 931 | stack.push(symbol); 932 | vstack.push(this.lexer.yytext); 933 | lstack.push(this.lexer.yylloc); 934 | stack.push(action[1]); 935 | symbol = null; 936 | if (!preErrorSymbol) { 937 | yyleng = this.lexer.yyleng; 938 | yytext = this.lexer.yytext; 939 | yylineno = this.lexer.yylineno; 940 | yyloc = this.lexer.yylloc; 941 | if (recovering > 0) 942 | recovering--; 943 | } else { 944 | symbol = preErrorSymbol; 945 | preErrorSymbol = null; 946 | } 947 | break; 948 | case 2: 949 | len = this.productions_[action[1]][1]; 950 | yyval.$ = vstack[vstack.length - len]; 951 | yyval._$ = {first_line: lstack[lstack.length - (len || 1)].first_line, last_line: lstack[lstack.length - 1].last_line, first_column: lstack[lstack.length - (len || 1)].first_column, last_column: lstack[lstack.length - 1].last_column}; 952 | if (ranges) { 953 | yyval._$.range = [lstack[lstack.length - (len || 1)].range[0], lstack[lstack.length - 1].range[1]]; 954 | } 955 | r = this.performAction.call(yyval, yytext, yyleng, yylineno, this.yy, action[1], vstack, lstack); 956 | if (typeof r !== "undefined") { 957 | return r; 958 | } 959 | if (len) { 960 | stack = stack.slice(0, -1 * len * 2); 961 | vstack = vstack.slice(0, -1 * len); 962 | lstack = lstack.slice(0, -1 * len); 963 | } 964 | stack.push(this.productions_[action[1]][0]); 965 | vstack.push(yyval.$); 966 | lstack.push(yyval._$); 967 | newState = table[stack[stack.length - 2]][stack[stack.length - 1]]; 968 | stack.push(newState); 969 | break; 970 | case 3: 971 | return true; 972 | } 973 | } 974 | return true; 975 | } 976 | }; 977 | 978 | 979 | function stripFlags(open, close) { 980 | return { 981 | left: open.charAt(2) === '~', 982 | right: close.charAt(0) === '~' || close.charAt(1) === '~' 983 | }; 984 | } 985 | 986 | /* Jison generated lexer */ 987 | var lexer = (function(){ 988 | var lexer = ({EOF:1, 989 | parseError:function parseError(str, hash) { 990 | if (this.yy.parser) { 991 | this.yy.parser.parseError(str, hash); 992 | } else { 993 | throw new Error(str); 994 | } 995 | }, 996 | setInput:function (input) { 997 | this._input = input; 998 | this._more = this._less = this.done = false; 999 | this.yylineno = this.yyleng = 0; 1000 | this.yytext = this.matched = this.match = ''; 1001 | this.conditionStack = ['INITIAL']; 1002 | this.yylloc = {first_line:1,first_column:0,last_line:1,last_column:0}; 1003 | if (this.options.ranges) this.yylloc.range = [0,0]; 1004 | this.offset = 0; 1005 | return this; 1006 | }, 1007 | input:function () { 1008 | var ch = this._input[0]; 1009 | this.yytext += ch; 1010 | this.yyleng++; 1011 | this.offset++; 1012 | this.match += ch; 1013 | this.matched += ch; 1014 | var lines = ch.match(/(?:\r\n?|\n).*/g); 1015 | if (lines) { 1016 | this.yylineno++; 1017 | this.yylloc.last_line++; 1018 | } else { 1019 | this.yylloc.last_column++; 1020 | } 1021 | if (this.options.ranges) this.yylloc.range[1]++; 1022 | 1023 | this._input = this._input.slice(1); 1024 | return ch; 1025 | }, 1026 | unput:function (ch) { 1027 | var len = ch.length; 1028 | var lines = ch.split(/(?:\r\n?|\n)/g); 1029 | 1030 | this._input = ch + this._input; 1031 | this.yytext = this.yytext.substr(0, this.yytext.length-len-1); 1032 | //this.yyleng -= len; 1033 | this.offset -= len; 1034 | var oldLines = this.match.split(/(?:\r\n?|\n)/g); 1035 | this.match = this.match.substr(0, this.match.length-1); 1036 | this.matched = this.matched.substr(0, this.matched.length-1); 1037 | 1038 | if (lines.length-1) this.yylineno -= lines.length-1; 1039 | var r = this.yylloc.range; 1040 | 1041 | this.yylloc = {first_line: this.yylloc.first_line, 1042 | last_line: this.yylineno+1, 1043 | first_column: this.yylloc.first_column, 1044 | last_column: lines ? 1045 | (lines.length === oldLines.length ? this.yylloc.first_column : 0) + oldLines[oldLines.length - lines.length].length - lines[0].length: 1046 | this.yylloc.first_column - len 1047 | }; 1048 | 1049 | if (this.options.ranges) { 1050 | this.yylloc.range = [r[0], r[0] + this.yyleng - len]; 1051 | } 1052 | return this; 1053 | }, 1054 | more:function () { 1055 | this._more = true; 1056 | return this; 1057 | }, 1058 | less:function (n) { 1059 | this.unput(this.match.slice(n)); 1060 | }, 1061 | pastInput:function () { 1062 | var past = this.matched.substr(0, this.matched.length - this.match.length); 1063 | return (past.length > 20 ? '...':'') + past.substr(-20).replace(/\n/g, ""); 1064 | }, 1065 | upcomingInput:function () { 1066 | var next = this.match; 1067 | if (next.length < 20) { 1068 | next += this._input.substr(0, 20-next.length); 1069 | } 1070 | return (next.substr(0,20)+(next.length > 20 ? '...':'')).replace(/\n/g, ""); 1071 | }, 1072 | showPosition:function () { 1073 | var pre = this.pastInput(); 1074 | var c = new Array(pre.length + 1).join("-"); 1075 | return pre + this.upcomingInput() + "\n" + c+"^"; 1076 | }, 1077 | next:function () { 1078 | if (this.done) { 1079 | return this.EOF; 1080 | } 1081 | if (!this._input) this.done = true; 1082 | 1083 | var token, 1084 | match, 1085 | tempMatch, 1086 | index, 1087 | col, 1088 | lines; 1089 | if (!this._more) { 1090 | this.yytext = ''; 1091 | this.match = ''; 1092 | } 1093 | var rules = this._currentRules(); 1094 | for (var i=0;i < rules.length; i++) { 1095 | tempMatch = this._input.match(this.rules[rules[i]]); 1096 | if (tempMatch && (!match || tempMatch[0].length > match[0].length)) { 1097 | match = tempMatch; 1098 | index = i; 1099 | if (!this.options.flex) break; 1100 | } 1101 | } 1102 | if (match) { 1103 | lines = match[0].match(/(?:\r\n?|\n).*/g); 1104 | if (lines) this.yylineno += lines.length; 1105 | this.yylloc = {first_line: this.yylloc.last_line, 1106 | last_line: this.yylineno+1, 1107 | first_column: this.yylloc.last_column, 1108 | last_column: lines ? lines[lines.length-1].length-lines[lines.length-1].match(/\r?\n?/)[0].length : this.yylloc.last_column + match[0].length}; 1109 | this.yytext += match[0]; 1110 | this.match += match[0]; 1111 | this.matches = match; 1112 | this.yyleng = this.yytext.length; 1113 | if (this.options.ranges) { 1114 | this.yylloc.range = [this.offset, this.offset += this.yyleng]; 1115 | } 1116 | this._more = false; 1117 | this._input = this._input.slice(match[0].length); 1118 | this.matched += match[0]; 1119 | token = this.performAction.call(this, this.yy, this, rules[index],this.conditionStack[this.conditionStack.length-1]); 1120 | if (this.done && this._input) this.done = false; 1121 | if (token) return token; 1122 | else return; 1123 | } 1124 | if (this._input === "") { 1125 | return this.EOF; 1126 | } else { 1127 | return this.parseError('Lexical error on line '+(this.yylineno+1)+'. Unrecognized text.\n'+this.showPosition(), 1128 | {text: "", token: null, line: this.yylineno}); 1129 | } 1130 | }, 1131 | lex:function lex() { 1132 | var r = this.next(); 1133 | if (typeof r !== 'undefined') { 1134 | return r; 1135 | } else { 1136 | return this.lex(); 1137 | } 1138 | }, 1139 | begin:function begin(condition) { 1140 | this.conditionStack.push(condition); 1141 | }, 1142 | popState:function popState() { 1143 | return this.conditionStack.pop(); 1144 | }, 1145 | _currentRules:function _currentRules() { 1146 | return this.conditions[this.conditionStack[this.conditionStack.length-1]].rules; 1147 | }, 1148 | topState:function () { 1149 | return this.conditionStack[this.conditionStack.length-2]; 1150 | }, 1151 | pushState:function begin(condition) { 1152 | this.begin(condition); 1153 | }}); 1154 | lexer.options = {}; 1155 | lexer.performAction = function anonymous(yy,yy_,$avoiding_name_collisions,YY_START) { 1156 | 1157 | 1158 | function strip(start, end) { 1159 | return yy_.yytext = yy_.yytext.substr(start, yy_.yyleng-end); 1160 | } 1161 | 1162 | 1163 | var YYSTATE=YY_START 1164 | switch($avoiding_name_collisions) { 1165 | case 0: 1166 | if(yy_.yytext.slice(-2) === "\\\\") { 1167 | strip(0,1); 1168 | this.begin("mu"); 1169 | } else if(yy_.yytext.slice(-1) === "\\") { 1170 | strip(0,1); 1171 | this.begin("emu"); 1172 | } else { 1173 | this.begin("mu"); 1174 | } 1175 | if(yy_.yytext) return 14; 1176 | 1177 | break; 1178 | case 1:return 14; 1179 | break; 1180 | case 2: 1181 | this.popState(); 1182 | return 14; 1183 | 1184 | break; 1185 | case 3:strip(0,4); this.popState(); return 15; 1186 | break; 1187 | case 4:return 35; 1188 | break; 1189 | case 5:return 36; 1190 | break; 1191 | case 6:return 25; 1192 | break; 1193 | case 7:return 16; 1194 | break; 1195 | case 8:return 20; 1196 | break; 1197 | case 9:return 19; 1198 | break; 1199 | case 10:return 19; 1200 | break; 1201 | case 11:return 23; 1202 | break; 1203 | case 12:return 22; 1204 | break; 1205 | case 13:this.popState(); this.begin('com'); 1206 | break; 1207 | case 14:strip(3,5); this.popState(); return 15; 1208 | break; 1209 | case 15:return 22; 1210 | break; 1211 | case 16:return 41; 1212 | break; 1213 | case 17:return 40; 1214 | break; 1215 | case 18:return 40; 1216 | break; 1217 | case 19:return 44; 1218 | break; 1219 | case 20:// ignore whitespace 1220 | break; 1221 | case 21:this.popState(); return 24; 1222 | break; 1223 | case 22:this.popState(); return 18; 1224 | break; 1225 | case 23:yy_.yytext = strip(1,2).replace(/\\"/g,'"'); return 32; 1226 | break; 1227 | case 24:yy_.yytext = strip(1,2).replace(/\\'/g,"'"); return 32; 1228 | break; 1229 | case 25:return 42; 1230 | break; 1231 | case 26:return 34; 1232 | break; 1233 | case 27:return 34; 1234 | break; 1235 | case 28:return 33; 1236 | break; 1237 | case 29:return 40; 1238 | break; 1239 | case 30:yy_.yytext = strip(1,2); return 40; 1240 | break; 1241 | case 31:return 'INVALID'; 1242 | break; 1243 | case 32:return 5; 1244 | break; 1245 | } 1246 | }; 1247 | lexer.rules = [/^(?:[^\x00]*?(?=(\{\{)))/,/^(?:[^\x00]+)/,/^(?:[^\x00]{2,}?(?=(\{\{|\\\{\{|\\\\\{\{|$)))/,/^(?:[\s\S]*?--\}\})/,/^(?:\()/,/^(?:\))/,/^(?:\{\{(~)?>)/,/^(?:\{\{(~)?#)/,/^(?:\{\{(~)?\/)/,/^(?:\{\{(~)?\^)/,/^(?:\{\{(~)?\s*else\b)/,/^(?:\{\{(~)?\{)/,/^(?:\{\{(~)?&)/,/^(?:\{\{!--)/,/^(?:\{\{![\s\S]*?\}\})/,/^(?:\{\{(~)?)/,/^(?:=)/,/^(?:\.\.)/,/^(?:\.(?=([=~}\s\/.)])))/,/^(?:[\/.])/,/^(?:\s+)/,/^(?:\}(~)?\}\})/,/^(?:(~)?\}\})/,/^(?:"(\\["]|[^"])*")/,/^(?:'(\\[']|[^'])*')/,/^(?:@)/,/^(?:true(?=([~}\s)])))/,/^(?:false(?=([~}\s)])))/,/^(?:-?[0-9]+(?=([~}\s)])))/,/^(?:([^\s!"#%-,\.\/;->@\[-\^`\{-~]+(?=([=~}\s\/.)]))))/,/^(?:\[[^\]]*\])/,/^(?:.)/,/^(?:$)/]; 1248 | lexer.conditions = {"mu":{"rules":[4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32],"inclusive":false},"emu":{"rules":[2],"inclusive":false},"com":{"rules":[3],"inclusive":false},"INITIAL":{"rules":[0,1,32],"inclusive":true}}; 1249 | return lexer;})() 1250 | parser.lexer = lexer; 1251 | function Parser () { this.yy = {}; }Parser.prototype = parser;parser.Parser = Parser; 1252 | return new Parser; 1253 | })();__exports__ = handlebars; 1254 | /* jshint ignore:end */ 1255 | return __exports__; 1256 | })(); 1257 | 1258 | // handlebars/compiler/base.js 1259 | var __module8__ = (function(__dependency1__, __dependency2__) { 1260 | "use strict"; 1261 | var __exports__ = {}; 1262 | var parser = __dependency1__; 1263 | var AST = __dependency2__; 1264 | 1265 | __exports__.parser = parser; 1266 | 1267 | function parse(input) { 1268 | // Just return if an already-compile AST was passed in. 1269 | if(input.constructor === AST.ProgramNode) { return input; } 1270 | 1271 | parser.yy = AST; 1272 | return parser.parse(input); 1273 | } 1274 | 1275 | __exports__.parse = parse; 1276 | return __exports__; 1277 | })(__module9__, __module7__); 1278 | 1279 | // handlebars/compiler/compiler.js 1280 | var __module10__ = (function(__dependency1__) { 1281 | "use strict"; 1282 | var __exports__ = {}; 1283 | var Exception = __dependency1__; 1284 | 1285 | function Compiler() {} 1286 | 1287 | __exports__.Compiler = Compiler;// the foundHelper register will disambiguate helper lookup from finding a 1288 | // function in a context. This is necessary for mustache compatibility, which 1289 | // requires that context functions in blocks are evaluated by blockHelperMissing, 1290 | // and then proceed as if the resulting value was provided to blockHelperMissing. 1291 | 1292 | Compiler.prototype = { 1293 | compiler: Compiler, 1294 | 1295 | disassemble: function() { 1296 | var opcodes = this.opcodes, opcode, out = [], params, param; 1297 | 1298 | for (var i=0, l=opcodes.length; i 0) { 1904 | this.source[1] = this.source[1] + ", " + locals.join(", "); 1905 | } 1906 | 1907 | // Generate minimizer alias mappings 1908 | if (!this.isChild) { 1909 | for (var alias in this.context.aliases) { 1910 | if (this.context.aliases.hasOwnProperty(alias)) { 1911 | this.source[1] = this.source[1] + ', ' + alias + '=' + this.context.aliases[alias]; 1912 | } 1913 | } 1914 | } 1915 | 1916 | if (this.source[1]) { 1917 | this.source[1] = "var " + this.source[1].substring(2) + ";"; 1918 | } 1919 | 1920 | // Merge children 1921 | if (!this.isChild) { 1922 | this.source[1] += '\n' + this.context.programs.join('\n') + '\n'; 1923 | } 1924 | 1925 | if (!this.environment.isSimple) { 1926 | this.pushSource("return buffer;"); 1927 | } 1928 | 1929 | var params = this.isChild ? ["depth0", "data"] : ["Handlebars", "depth0", "helpers", "partials", "data"]; 1930 | 1931 | for(var i=0, l=this.environment.depths.list.length; i this.stackVars.length) { this.stackVars.push("stack" + this.stackSlot); } 2524 | return this.topStackName(); 2525 | }, 2526 | topStackName: function() { 2527 | return "stack" + this.stackSlot; 2528 | }, 2529 | flushInline: function() { 2530 | var inlineStack = this.inlineStack; 2531 | if (inlineStack.length) { 2532 | this.inlineStack = []; 2533 | for (var i = 0, len = inlineStack.length; i < len; i++) { 2534 | var entry = inlineStack[i]; 2535 | if (entry instanceof Literal) { 2536 | this.compileStack.push(entry); 2537 | } else { 2538 | this.pushStack(entry); 2539 | } 2540 | } 2541 | } 2542 | }, 2543 | isInline: function() { 2544 | return this.inlineStack.length; 2545 | }, 2546 | 2547 | popStack: function(wrapped) { 2548 | var inline = this.isInline(), 2549 | item = (inline ? this.inlineStack : this.compileStack).pop(); 2550 | 2551 | if (!wrapped && (item instanceof Literal)) { 2552 | return item.value; 2553 | } else { 2554 | if (!inline) { 2555 | if (!this.stackSlot) { 2556 | throw new Exception('Invalid stack pop'); 2557 | } 2558 | this.stackSlot--; 2559 | } 2560 | return item; 2561 | } 2562 | }, 2563 | 2564 | topStack: function(wrapped) { 2565 | var stack = (this.isInline() ? this.inlineStack : this.compileStack), 2566 | item = stack[stack.length - 1]; 2567 | 2568 | if (!wrapped && (item instanceof Literal)) { 2569 | return item.value; 2570 | } else { 2571 | return item; 2572 | } 2573 | }, 2574 | 2575 | quotedString: function(str) { 2576 | return '"' + str 2577 | .replace(/\\/g, '\\\\') 2578 | .replace(/"/g, '\\"') 2579 | .replace(/\n/g, '\\n') 2580 | .replace(/\r/g, '\\r') 2581 | .replace(/\u2028/g, '\\u2028') // Per Ecma-262 7.3 + 7.8.4 2582 | .replace(/\u2029/g, '\\u2029') + '"'; 2583 | }, 2584 | 2585 | setupHelper: function(paramSize, name, missingParams) { 2586 | var params = [], 2587 | paramsInit = this.setupParams(paramSize, params, missingParams); 2588 | var foundHelper = this.nameLookup('helpers', name, 'helper'); 2589 | 2590 | return { 2591 | params: params, 2592 | paramsInit: paramsInit, 2593 | name: foundHelper, 2594 | callParams: ["depth0"].concat(params).join(", "), 2595 | helperMissingParams: missingParams && ["depth0", this.quotedString(name)].concat(params).join(", ") 2596 | }; 2597 | }, 2598 | 2599 | setupOptions: function(paramSize, params) { 2600 | var options = [], contexts = [], types = [], param, inverse, program; 2601 | 2602 | options.push("hash:" + this.popStack()); 2603 | 2604 | if (this.options.stringParams) { 2605 | options.push("hashTypes:" + this.popStack()); 2606 | options.push("hashContexts:" + this.popStack()); 2607 | } 2608 | 2609 | inverse = this.popStack(); 2610 | program = this.popStack(); 2611 | 2612 | // Avoid setting fn and inverse if neither are set. This allows 2613 | // helpers to do a check for `if (options.fn)` 2614 | if (program || inverse) { 2615 | if (!program) { 2616 | this.context.aliases.self = "this"; 2617 | program = "self.noop"; 2618 | } 2619 | 2620 | if (!inverse) { 2621 | this.context.aliases.self = "this"; 2622 | inverse = "self.noop"; 2623 | } 2624 | 2625 | options.push("inverse:" + inverse); 2626 | options.push("fn:" + program); 2627 | } 2628 | 2629 | for(var i=0; i 2 | -------------------------------------------------------------------------------- /assets/main.css: -------------------------------------------------------------------------------- 1 | .voteb 2 | { 3 | color: #BDC3C7; 4 | border: 2px solid #BDC3C7; 5 | border-radius: 3px; 6 | text-align: center; 7 | height: 48px; 8 | width: 40px; 9 | background-color: #ffffff; 10 | text-decoration: none; 11 | padding-top: 5px; 12 | } 13 | .voteb:hover 14 | { 15 | color: #2ECC71; 16 | border: 2px solid #2ECC71; 17 | text-decoration: none; 18 | 19 | } 20 | .voteb:active 21 | { 22 | color: #ffffff; 23 | border: 2px solid #26C281; 24 | background-color: #26C281; 25 | text-decoration: none; 26 | } 27 | 28 | .linkt 29 | { 30 | text-decoration: none; 31 | font-size: 20px; 32 | color: rgba(4, 70, 86, 0.92); 33 | font-weight: 700; 34 | } 35 | .linkt:hover 36 | { 37 | color: #025E98; 38 | text-decoration: none; 39 | cursor: pointer; 40 | } 41 | #votec 42 | { 43 | font-size: 12px; 44 | font-weight: bold; 45 | } 46 | #linkbox 47 | { 48 | padding-bottom: 15px; 49 | } 50 | #commetico 51 | { 52 | vertical-align: middle; 53 | color: #ccc; 54 | font-size: 24px; 55 | } 56 | #submitli 57 | { 58 | color: #ffffff; 59 | } 60 | #dropdown 61 | { 62 | margin-top: 10px; 63 | } 64 | 65 | #submitb 66 | { 67 | padding-bottom: 15px; 68 | } 69 | #title 70 | { 71 | color: #6C7A89; 72 | font-weight: 700; 73 | } 74 | #iconloading 75 | { 76 | color: #26A65B; 77 | } 78 | .rsidebar 79 | { 80 | top: 0px; 81 | right: 0px; 82 | bottom: 0px; 83 | z-index: 1000; 84 | box-shadow: 0px 0px 20px rgba(0, 0, 0, 0.25); 85 | border-left: 1px solid rgba(0, 0, 0, 0.1); 86 | background-color: white; 87 | transition: all 0s ease 0s, all 0.2s ease 0s; 88 | padding-left: 0px; 89 | padding-right: 0px; 90 | } 91 | 92 | @media only screen 93 | and (min-width : 400px) { 94 | .rsidebar 95 | { 96 | position: fixed; 97 | } 98 | } 99 | 100 | #top 101 | { 102 | padding-right: 20px; 103 | padding-bottom: 20px; 104 | padding-top: 25px; 105 | padding-left: 30px; 106 | height: 85px; 107 | border-bottom: 1px solid #ccc; 108 | } 109 | #top-contain 110 | { 111 | background: none repeat scroll 0% 0% #ECF0F1; 112 | 113 | } 114 | #bottom-contain 115 | { 116 | padding-right: 20px; 117 | padding-bottom: 20px; 118 | padding-top: 20px; 119 | padding-left: 30px; 120 | } 121 | #posted 122 | { 123 | font-weight: 300; 124 | color: #2B2B2B; 125 | font-size: 12px; 126 | } 127 | .comments 128 | { 129 | padding-left: 30px; 130 | padding-top: 20px; 131 | padding-right: 20px; 132 | } 133 | .commentarea 134 | { 135 | resize: none; 136 | } 137 | .submitform 138 | { 139 | margin-top: 60px; 140 | } 141 | 142 | 143 | .commentpic { 144 | padding-right: 30px; 145 | } 146 | .navtitle { 147 | color: #26A65B; 148 | font-weight: 600; 149 | font-size: 24px; 150 | } 151 | .navtitle:hover 152 | { 153 | color: #EEEEEE; 154 | } 155 | 156 | img:before 157 | { 158 | content:" "; 159 | background:url('http://newton.physics.uiowa.edu/~sbaalrud/empty_profile.gif'); 160 | display: block; 161 | position:absolute; 162 | } 163 | 164 | .icon-bar { 165 | background-color: #ffffff; 166 | } 167 | 168 | .navbar-toggle { 169 | border: 1px solid #FFF; 170 | } 171 | 172 | #taglinetxt { 173 | font-size: 16px; 174 | font-weight: 300; 175 | } 176 | 177 | .nwcomment { 178 | padding-top: 20px; 179 | } 180 | .tagline 181 | { 182 | font-size : 14px; 183 | color : #6C7A89; 184 | } 185 | .footer 186 | { 187 | margin-bottom : 0px; 188 | margin-top : 20px; 189 | 190 | } 191 | 192 | 193 | 194 | .loading-bar 195 | { 196 | -webkit-animation: shift-rightwards 1s ease-in-out infinite; 197 | -moz-animation: shift-rightwards 1s ease-in-out infinite; 198 | -ms-animation: shift-rightwards 1s ease-in-out infinite; 199 | -o-animation: shift-rightwards 1s ease-in-out infinite; 200 | animation: shift-rightwards 1s ease-in-out infinite; 201 | -webkit-animation-delay: .4s; 202 | -moz-animation-delay: .4s; 203 | -o-animation-delay: .4s; 204 | animation-delay: .4s; 205 | 206 | position: fixed; 207 | top: 0; 208 | left: 0; 209 | right: 0; 210 | height: 4px; 211 | z-index: 2000; 212 | background: #e74c3c; 213 | -webkit-transform: translateX(100%); 214 | -moz-transform: translateX(100%); 215 | -o-transform: translateX(100%); 216 | transform: translateX(100%); 217 | } 218 | 219 | @-webkit-keyframes shift-rightwards 220 | { 221 | 0% 222 | { 223 | -webkit-transform:translateX(-100%); 224 | -moz-transform:translateX(-100%); 225 | -o-transform:translateX(-100%); 226 | transform:translateX(-100%); 227 | } 228 | 229 | 40% 230 | { 231 | -webkit-transform:translateX(0%); 232 | -moz-transform:translateX(0%); 233 | -o-transform:translateX(0%); 234 | transform:translateX(0%); 235 | } 236 | 237 | 60% 238 | { 239 | -webkit-transform:translateX(0%); 240 | -moz-transform:translateX(0%); 241 | -o-transform:translateX(0%); 242 | transform:translateX(0%); 243 | } 244 | 245 | 100% 246 | { 247 | -webkit-transform:translateX(100%); 248 | -moz-transform:translateX(100%); 249 | -o-transform:translateX(100%); 250 | transform:translateX(100%); 251 | } 252 | 253 | } 254 | @-moz-keyframes shift-rightwards 255 | { 256 | 0% 257 | { 258 | -webkit-transform:translateX(-100%); 259 | -moz-transform:translateX(-100%); 260 | -o-transform:translateX(-100%); 261 | transform:translateX(-100%); 262 | } 263 | 264 | 40% 265 | { 266 | -webkit-transform:translateX(0%); 267 | -moz-transform:translateX(0%); 268 | -o-transform:translateX(0%); 269 | transform:translateX(0%); 270 | } 271 | 272 | 60% 273 | { 274 | -webkit-transform:translateX(0%); 275 | -moz-transform:translateX(0%); 276 | -o-transform:translateX(0%); 277 | transform:translateX(0%); 278 | } 279 | 280 | 100% 281 | { 282 | -webkit-transform:translateX(100%); 283 | -moz-transform:translateX(100%); 284 | -o-transform:translateX(100%); 285 | transform:translateX(100%); 286 | } 287 | 288 | } 289 | @-o-keyframes shift-rightwards 290 | { 291 | 0% 292 | { 293 | -webkit-transform:translateX(-100%); 294 | -moz-transform:translateX(-100%); 295 | -o-transform:translateX(-100%); 296 | transform:translateX(-100%); 297 | } 298 | 299 | 40% 300 | { 301 | -webkit-transform:translateX(0%); 302 | -moz-transform:translateX(0%); 303 | -o-transform:translateX(0%); 304 | transform:translateX(0%); 305 | } 306 | 307 | 60% 308 | { 309 | -webkit-transform:translateX(0%); 310 | -moz-transform:translateX(0%); 311 | -o-transform:translateX(0%); 312 | transform:translateX(0%); 313 | } 314 | 315 | 100% 316 | { 317 | -webkit-transform:translateX(100%); 318 | -moz-transform:translateX(100%); 319 | -o-transform:translateX(100%); 320 | transform:translateX(100%); 321 | } 322 | 323 | } 324 | @keyframes shift-rightwards 325 | { 326 | 0% 327 | { 328 | -webkit-transform:translateX(-100%); 329 | -moz-transform:translateX(-100%); 330 | -o-transform:translateX(-100%); 331 | transform:translateX(-100%); 332 | } 333 | 334 | 40% 335 | { 336 | -webkit-transform:translateX(0%); 337 | -moz-transform:translateX(0%); 338 | -o-transform:translateX(0%); 339 | transform:translateX(0%); 340 | } 341 | 342 | 60% 343 | { 344 | -webkit-transform:translateX(0%); 345 | -moz-transform:translateX(0%); 346 | -o-transform:translateX(0%); 347 | transform:translateX(0%); 348 | } 349 | 350 | 100% 351 | { 352 | -webkit-transform:translateX(100%); 353 | -moz-transform:translateX(100%); 354 | -o-transform:translateX(100%); 355 | transform:translateX(100%); 356 | } 357 | } 358 | -------------------------------------------------------------------------------- /db.php: -------------------------------------------------------------------------------- 1 | 60 | -------------------------------------------------------------------------------- /index.htm: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | Picnicy 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 27 | 28 | 29 |
30 | 37 | 38 | 137 | 161 | 162 | 233 | 234 | 237 | 238 | 247 | 248 | 266 | 267 | 291 | 292 | 293 | 294 | 295 | 296 | 297 | 298 | 299 | 300 | 301 | 302 | --------------------------------------------------------------------------------