├── .gitignore
├── Procfile
├── README.md
├── app.js
├── app
├── client
│ ├── anonications.js
│ ├── behaviors
│ │ └── home
│ │ │ └── a_button.js
│ ├── burtle_storm.js
│ ├── chat.js
│ ├── cordova.js
│ ├── drawing.js
│ ├── emojies.js
│ ├── favorite_boards.js
│ ├── imgur.js
│ ├── john.js
│ ├── newthread.js
│ ├── notif.js
│ ├── post_utils.js
│ ├── prelude.js
│ ├── prelude.json
│ ├── profanity.js
│ ├── settings.js
│ ├── sidebar.js
│ ├── sinners.js
│ ├── sticky_post.js
│ ├── storage.js
│ ├── summarize.js
│ ├── text.js
│ ├── tripcode.js
│ └── voyeur.js
├── controllers
│ ├── archives
│ │ ├── client.js
│ │ ├── server.js
│ │ └── test
│ │ │ └── server.js
│ ├── boards
│ │ ├── client.js
│ │ ├── server.js
│ │ └── test
│ │ │ └── server.js
│ ├── data
│ │ ├── client.js
│ │ ├── server.js
│ │ └── test
│ │ │ └── server.js
│ ├── home
│ │ ├── client.js
│ │ ├── server.js
│ │ └── test
│ │ │ ├── client.js
│ │ │ └── server.js
│ ├── icons
│ │ ├── client.js
│ │ ├── server.js
│ │ └── test
│ │ │ └── server.js
│ ├── posts
│ │ ├── client.js
│ │ ├── server.js
│ │ └── test
│ │ │ └── server.js
│ ├── profiles
│ │ ├── client.js
│ │ ├── server.js
│ │ └── test
│ │ │ └── server.js
│ ├── rss
│ │ ├── server.js
│ │ └── test
│ │ │ └── server.js
│ └── search
│ │ ├── client.js
│ │ ├── server.js
│ │ └── test
│ │ └── server.js
├── fakedata.js
├── main.js
├── models
│ ├── action.js
│ ├── archived_post.js
│ ├── ban.js
│ ├── board.js
│ ├── board_claim.js
│ ├── board_config.js
│ ├── ip.js
│ ├── link.js
│ ├── model.js
│ ├── post.js
│ ├── trophy.js
│ └── user.js
├── server
│ ├── anon_pocket.js
│ ├── anonications.js
│ ├── board_migrations.js
│ ├── board_names.js
│ ├── board_utils.js
│ ├── chat.js
│ ├── client_api.js
│ ├── hidden_boards.js
│ ├── makeme_store.js
│ ├── md5.js
│ ├── mod.js
│ ├── post_links.js
│ ├── posting.js
│ ├── snorkel_api.js
│ ├── sponsored_content.js
│ ├── tripcode.js
│ └── worship_boards.js
└── static
│ ├── favicon.ico
│ ├── fonts
│ ├── glyphicons-halflings-regular.woff
│ ├── webhostinghub-glyphs.eot
│ ├── webhostinghub-glyphs.svg
│ └── webhostinghub-glyphs.ttf
│ ├── images
│ ├── atob-logo-404.png
│ ├── atobi.png
│ ├── atobn.png
│ └── bootstrap
│ │ ├── glyphicons-halflings-regular.eot
│ │ ├── glyphicons-halflings-regular.svg
│ │ ├── glyphicons-halflings-regular.ttf
│ │ ├── glyphicons-halflings-white.png
│ │ └── glyphicons-halflings.png
│ ├── styles
│ ├── RubberBand.css
│ ├── archive.css
│ ├── board.css
│ ├── bootstrap
│ │ ├── bootstrap-editable.css
│ │ ├── bootstrap.cosmo.css
│ │ ├── bootstrap.css
│ │ ├── bootstrap.theme.css
│ │ ├── overrides.css
│ │ └── responsive.css
│ ├── box.css
│ ├── chat.css
│ ├── definitions.less
│ ├── home.css
│ ├── jquery.fullPage.css
│ ├── jquery.sidr.light.css
│ ├── links.css
│ ├── logo_and_links.css
│ ├── post.css
│ ├── profile.css
│ ├── scrollers.css
│ ├── search.css
│ ├── searchbar.css
│ ├── settings.css
│ ├── sponsored_content.css
│ └── whhg.css
│ ├── templates
│ ├── controllers
│ │ ├── about.html.erb
│ │ ├── archives.html.erb
│ │ ├── boards
│ │ │ ├── list.html.erb
│ │ │ └── show.html.erb
│ │ ├── chat.html.erb
│ │ ├── data
│ │ │ └── data.html.erb
│ │ ├── home.html.erb
│ │ ├── links.html.erb
│ │ ├── mods.html.erb
│ │ ├── posts
│ │ │ ├── posts.html.erb
│ │ │ └── show.html.erb
│ │ ├── profiles
│ │ │ └── profiles.html.erb
│ │ ├── recent.html.erb
│ │ └── search
│ │ │ └── search.html.erb
│ └── partials
│ │ ├── boards
│ │ └── shared.html.erb
│ │ ├── home
│ │ ├── 404.html.erb
│ │ ├── board_links.html.erb
│ │ ├── header.html.erb
│ │ ├── icons.html.erb
│ │ ├── index.html.erb
│ │ ├── link.html.erb
│ │ ├── recent_posts.html.erb
│ │ └── rules.html.erb
│ │ ├── posts
│ │ └── shared.html.erb
│ │ ├── profiles
│ │ └── stats.html.erb
│ │ ├── rss.html.erb
│ │ └── shared
│ │ ├── benjamin_buttons.html.erb
│ │ ├── chat.html.erb
│ │ ├── favorites.html.erb
│ │ ├── loading.html.erb
│ │ ├── logo_and_links.html.erb
│ │ ├── new_post.html.erb
│ │ ├── search.html.erb
│ │ └── settings.html.erb
│ └── vendor
│ ├── RubberBand.js
│ ├── Sortable.js
│ ├── bootstrap.min.js
│ ├── cordova
│ ├── cordova.js
│ ├── cordova_plugins.js
│ └── plugins
│ │ ├── de.appplant.cordova.plugin.background-mode
│ │ └── www
│ │ │ └── background-mode.js
│ │ ├── de.appplant.cordova.plugin.local-notification
│ │ └── www
│ │ │ └── local-notification.js
│ │ ├── org.apache.cordova.device
│ │ └── www
│ │ │ └── device.js
│ │ └── org.apache.cordova.inappbrowser
│ │ └── www
│ │ └── inappbrowser.js
│ ├── fastclick.js
│ ├── hex-rgb.src.js
│ ├── is_mobile.js
│ ├── jquery-deparam.js
│ ├── jquery.chroma-hash.js
│ ├── jquery.cookie.js
│ ├── jquery.deserialize.js
│ ├── jquery.fullPage.js
│ ├── jquery.scrollTo.js
│ ├── jquery.sidr.js
│ ├── jquery.tagcloud.js
│ ├── jquery.textcomplete.js
│ ├── jquery.timeago.js
│ ├── lunicode.js
│ ├── marked.js
│ ├── md5.js
│ ├── notify.js
│ ├── plotly-latest.min.js
│ ├── strtotime.js
│ ├── useractions.js
│ ├── useractions_stub.js
│ └── velocity.js
├── components
├── board_admin_panel
│ ├── board_admin_panel.css
│ ├── board_admin_panel.html.erb
│ ├── board_admin_panel.js
│ ├── events.js
│ ├── package.json
│ └── test
│ │ ├── client.js
│ │ └── server.js
├── board_claim_panel
│ ├── board_claim_panel.css
│ ├── board_claim_panel.html.erb
│ ├── board_claim_panel.js
│ ├── events.js
│ ├── package.json
│ └── test
│ │ ├── client.js
│ │ └── server.js
├── button
│ ├── button.css
│ ├── button.html.erb
│ ├── button.js
│ ├── events.js
│ ├── package.json
│ └── test
│ │ ├── client.js
│ │ └── server.js
├── component.js
├── delete_post_modal
│ ├── delete_post_modal.css
│ ├── delete_post_modal.html.erb
│ ├── delete_post_modal.js
│ ├── events.js
│ └── package.json
├── markdown_dialog
│ ├── events.js
│ ├── markdown_dialog.css
│ ├── markdown_dialog.html.erb
│ ├── markdown_dialog.js
│ ├── package.json
│ └── test
│ │ ├── client.js
│ │ └── server.js
├── post
│ ├── events.js
│ ├── package.json
│ ├── post.css
│ ├── post.html.erb
│ ├── post.js
│ └── test
│ │ ├── client.js
│ │ └── server.js
├── stats_dialog
│ ├── events.js
│ ├── markdown_dialog.css
│ ├── package.json
│ ├── stats_dialog.html.erb
│ └── stats_dialog.js
├── template
│ ├── events.js
│ ├── package.json
│ ├── template.css
│ ├── template.html.erb
│ ├── template.js
│ └── test
│ │ ├── client.js
│ │ └── server.js
└── upeye
│ ├── events.js
│ ├── package.json
│ ├── test
│ ├── client.js
│ └── server.js
│ ├── upeye.css
│ ├── upeye.html.erb
│ └── upeye.js
├── config
├── config.js
├── config.json
├── heroku.js
├── localhost.js
├── override.js
└── users.htpasswd
├── migrations
├── 20140318213854-add_votes_to_posts.js
├── 20140318231555-add_bumped_at_to_posts.js
├── 20140320182742-add_bumped_index_to_posts.js
├── 20140320213029-add_bans_table.js
├── 20140320214332-add_ips_table.js
├── 20140410104732-add_archives_table.js
├── 20140520201926-add_links_table.js
├── 20150304120553-add_burtles_to_post.js
├── 20150416212010-add_actions_table.js
├── 20151005130358-add_board_config_table.js
├── 20151005132417-add_board_claims_table.js
├── 20151114192529-add_trophies_table.js
└── 20151116104703-add_anon_id_to_trophies.js
├── package.json
├── routes.json
└── scripts
├── add_user.sh
├── clean_old_posts.sh
├── create_component.sh
├── create_controller.sh
├── create_partial.sh
├── create_react_component.sh
├── garbage_collection.js
├── generate_unit_tests.sh
├── grab_all_archives.sh
├── ip_conversion.js
├── link_collection.js
├── pocket_collection.js
├── run_client_tests.sh
├── run_component_tests.sh
├── run_controller_tests.sh
├── run_tests.sh
├── run_unit_tests.sh
└── setup_certificates.sh
/.gitignore:
--------------------------------------------------------------------------------
1 | *.sw?
2 | db.sqlite
3 | ab.sqlite
4 | node_modules
5 | garbage_collector.log
6 | bak
7 |
--------------------------------------------------------------------------------
/Procfile:
--------------------------------------------------------------------------------
1 | web: node app.js
2 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 |
2 |
3 | WHAT
4 | ====
5 |
6 | atob is a realtime textboard. this is the source code for it.
7 |
8 |
9 |
10 | HOWTO
11 | =====
12 |
13 | INSTALL ATOB
14 | ------------
15 |
16 | make sure node / npm are installed. then run `npm install`
17 |
18 |
19 | RUNNING ATOB
20 | ------------
21 |
22 | The first time you run atob, you need to create the database by specifying
23 | RESET=true on the command line. After the first run, you should NOT use
24 | the RESET parameter again, unless you want to delete your data.
25 |
26 |
27 | The two relevant commands are:
28 |
29 |
30 | # RUN THIS COMMAND ONCE (THE FIRST TIME)
31 | PORT=8001 RESET=1 node app
32 |
33 | # RUN THIS COMMAND ANY OTHER TIME
34 | PORT=8001 node app
35 |
36 | PRUNING ATOB
37 | ------------
38 |
39 | run scripts/clean\_old\_posts.sh from the main directory to delete and archive
40 | old posts and remove old IPs. This should be done regularly in a cronjob
41 |
42 | SETTING UP NGINX
43 | ----------------
44 |
45 |
46 | An example NGINX sites-available config might look like:
47 |
48 | server {
49 | listen 80;
50 |
51 | # using SSL (OPTIONAL)
52 | listen 443 ssl;
53 | ssl_certificate /home/dev/atob/config/bak/signed.crt;
54 | ssl_certificate_key /home/dev/atob/config/bak/server.key;
55 | # end SSL
56 |
57 | server_name atob.xyz;
58 | server_name atob.cc;
59 |
60 | location / {
61 | # assume the app is running on 8001
62 | proxy_pass http://127.0.0.1:8001;
63 |
64 |
65 | # web socket and proxy header stuff
66 | proxy_http_version 1.1;
67 | proxy_set_header Upgrade $http_upgrade;
68 | proxy_set_header Connection "upgrade";
69 | proxy_redirect off;
70 | proxy_set_header Host $host;
71 | proxy_set_header X-Real-IP $remote_addr;
72 | proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
73 |
74 | client_max_body_size 10m;
75 | client_body_buffer_size 2k;
76 | proxy_buffers 32 4k;
77 | }
78 | }
79 |
80 |
81 | ADDING ADMIN USER
82 | -----------------
83 |
84 | go to the atob root directory and start a new node process, then run:
85 |
86 | ```
87 | > require("superfluous");
88 | > User = require_app("models/user");
89 | > User.create({"tripname" : "name", "tripcode": "tripcode" });
90 | ```
91 |
92 | To get the tripcode, go to your atob instance and enter your details in
93 | the settings pane, then click the "report" button on a post. That will show you
94 | the tripname/tripcode for your current user.
95 |
96 | The tripcode will be a hash, not plaintext. Do not insert a plaintext password
97 | into the DB, the tripcode should look like: "c4b20b4880b3efe567b06760e7edd8bb"
98 |
99 |
100 | To verify the user was created:
101 |
102 | ```
103 | sqlite db.sqlite
104 | > SELECT * FROM USERS;
105 | ```
106 |
107 | If your user is in the db, the next time you click the "report" button, you
108 | should get an admin panel instead of the normal report dialog.
109 |
--------------------------------------------------------------------------------
/app.js:
--------------------------------------------------------------------------------
1 | var server = require('superfluous');
2 | server.run();
3 |
--------------------------------------------------------------------------------
/app/client/anonications.js:
--------------------------------------------------------------------------------
1 |
2 | var ICON_LOOKUP = {
3 | e: "icon-atob",
4 | t: "icon-keyboardalt",
5 | f: "icon-glassesalt",
6 | u: "icon-glassesalt",
7 | d: "icon-ducky",
8 | b: "icon-comedy",
9 | n: "icon-toast",
10 | s: "icon-ghost"
11 | };
12 |
13 | function get_anonicator_for(c) {
14 | if (!c) {
15 | return "";
16 | }
17 |
18 | var ret = ICON_LOOKUP[c[0]];
19 | if (!ret) {
20 | c = c.replace(/:/g, "");
21 | if (c.indexOf("_") !== -1) { // glyphicons
22 | ret = "glyphicon glyphicon-" + c.replace(/_/, "");
23 | } else { // default icon set
24 | ret = "icon-" + c;
25 | }
26 | }
27 | return ret;
28 | }
29 |
30 | var actions = {
31 | ducky: "kited",
32 | reddit: "snooing",
33 | comedy: "ducking"
34 |
35 | };
36 |
37 | module.exports = {
38 | check: function(targetEl, anon_id, tripcode) {
39 | var ret = false;
40 | _.each(actions, function(action, index) {
41 | if (targetEl.hasClass("icon-" + index)) {
42 | SF.socket().emit("stalking", {
43 | what: action,
44 | anon: anon_id,
45 | mytrip: tripcode
46 | });
47 |
48 | _ET.global("anonicator", action);
49 |
50 | ret = true;
51 | }
52 |
53 | });
54 |
55 | return ret;
56 | },
57 | get_anonicator_for: get_anonicator_for
58 | };
59 |
--------------------------------------------------------------------------------
/app/client/behaviors/home/a_button.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | events: {
3 | "click" : "sample_click"
4 | },
5 |
6 | sample_click: function() {
7 | $("#clickit")
8 | .html("nice job :)")
9 | .fadeIn();
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/app/client/chat.js:
--------------------------------------------------------------------------------
1 | var HIDDEN = true;
2 | var COUNT = 0;
3 | module.exports = {
4 | add_socket_subscriptions: function(s) {
5 | s.on("new_chat", function(reply) {
6 | if (HIDDEN) {
7 | COUNT++;
8 | $(".chat .chat_header .counter").text("(" + COUNT + ")");
9 | }
10 |
11 | if (window._POSTS.chat) {
12 | window._POSTS.chat.add_reply(reply);
13 | }
14 | });
15 |
16 | },
17 | controller_events: {
18 | "click .chat_header" : "toggle_chat"
19 | },
20 | show_chat_popup: function() {
21 | _ET.local("chat", "show_popup");
22 | this.$el.find(".chat_content").slideDown();
23 | this.$el.find(".chat").addClass("visible");
24 | var repliesEl = this.$el.find(".chat .replies");
25 | if (repliesEl.length) {
26 | repliesEl.scrollTop(repliesEl[0].scrollHeight);
27 | }
28 | COUNT = 0;
29 | $(".chat .chat_header .counter").empty();
30 |
31 |
32 | },
33 | hide_chat_popup: function() {
34 | _ET.local("chat", "hide_popup");
35 | this.$el.find(".chat_content").slideUp();
36 | this.$el.find(".chat").removeClass("visible");
37 |
38 | },
39 | toggle_chat: function() {
40 | HIDDEN = !HIDDEN;
41 | if (HIDDEN) {
42 | this.hide_chat_popup();
43 | } else {
44 | this.show_chat_popup();
45 | }
46 | },
47 | show_chat: function(post_id) {
48 | var tries = 0;
49 | function try_again() {
50 | tries++;
51 | _.delay(function() {
52 | try {
53 | window._POSTS.chat = window._POSTS[post_id];
54 | } catch (e) {
55 | if (tries < 20) {
56 | try_again();
57 | }
58 | }
59 | }, 300);
60 | }
61 |
62 | try_again();
63 |
64 | // This is where we can show and hide chat?
65 | $(".chat").removeClass("hidden");
66 | var repliesEl = $(".chat .replies");
67 | if (repliesEl.length) {
68 | repliesEl.scrollTop(repliesEl[0].scrollHeight);
69 | }
70 |
71 | },
72 | };
73 |
--------------------------------------------------------------------------------
/app/client/drawing.js:
--------------------------------------------------------------------------------
1 | var iframe;
2 | var insertedFrame = false;
3 | var clearFrameTimer;
4 | var showing = false;
5 |
6 | function toggler(e) {
7 | if (e.keyCode === 192 && e.ctrlKey) {
8 | module.exports.toggle();
9 |
10 | }
11 | }
12 |
13 | toggler = _.throttle(toggler, 100);
14 |
15 | $("html").keyup(toggler);
16 | $(window).on("message", function(msg) {
17 | if (msg.originalEvent.data == "tilde") {
18 | module.exports.hide();
19 | }
20 | });
21 |
22 |
23 | function maybehideframe() {
24 | clearTimeout(clearFrameTimer);
25 | clearFrameTimer = setTimeout(function() {
26 | if (showing) {
27 | return;
28 | }
29 |
30 | iframe.remove();
31 | iframe = null;
32 | insertedFrame = false;
33 |
34 | }, 30000);
35 |
36 |
37 | }
38 |
39 | function checkInstall() {
40 | if (!iframe) {
41 | module.exports.install();
42 | }
43 |
44 | if (!insertedFrame) {
45 | $("html").append(iframe);
46 | insertedFrame = true;
47 | }
48 |
49 | }
50 | module.exports = {
51 | install: function() {
52 | if (!iframe) {
53 |
54 | iframe = $("");
55 |
56 | // Need to figure out if we are on HTTPS or on HTTP...
57 | // TODO: remove these dependencies on external URLS...
58 | if (document.location.protocol == "https:") {
59 | iframe.attr("src", "https://atob.xyz:444");
60 | } else {
61 | iframe.attr("src", "http://gtg.kthxb.ai");
62 | }
63 |
64 | iframe.css({
65 | width: "100%",
66 | height: "100%",
67 | position: "fixed",
68 | top: 0,
69 | left: 0,
70 | display: "none",
71 | zIndex: 9999
72 | });
73 |
74 | var toggleEl = $("
");
75 | var ICONS = [ "icon-edit", "icon-poop", "icon-acorn", "icon-koala", "icon-onion", "icon-heart", "icon-grave", "icon-circlepencil" ];
76 | var icon_str = ICONS[_.random(0, ICONS.length-1)];
77 |
78 | toggleEl.addClass(icon_str);
79 | toggleEl.css({
80 | position: "fixed",
81 | cursor: "pointer",
82 | bottom: "10px",
83 | left: "18px",
84 | display: "block",
85 | zIndex: 10000,
86 | width: "20px",
87 | height: "20px"
88 | });
89 |
90 | toggleEl.on("click", module.exports.toggle);
91 | $("body").append(toggleEl);
92 |
93 |
94 | }
95 |
96 | },
97 | show: function() {
98 | showing = true;
99 | checkInstall();
100 | iframe.fadeIn();
101 | iframe.focus();
102 |
103 | },
104 | hide: function() {
105 | showing = false;
106 | checkInstall();
107 | iframe.fadeOut();
108 | $(window).focus();
109 |
110 | maybehideframe();
111 | },
112 | toggle: function() {
113 | checkInstall();
114 | showing = !showing;
115 |
116 | if (showing) {
117 | _ET.global("drawing_board", "show");
118 | } else {
119 | _ET.global("drawing_board", "hide");
120 | }
121 |
122 | iframe.fadeToggle();
123 | maybehideframe();
124 |
125 | }
126 | };
127 |
128 | module.exports.install();
129 |
--------------------------------------------------------------------------------
/app/client/imgur.js:
--------------------------------------------------------------------------------
1 |
2 | function handle_imgur_upload(textareaEl, file, cb) {
3 | // add feedback to indiciate its uploading
4 |
5 | function set_upload_state() {
6 | textareaEl.attr("disabled", true);
7 |
8 | }
9 |
10 | function end_upload_state() {
11 | textareaEl.attr("disabled", false);
12 | setTimeout(function() {
13 | textareaEl.focus();
14 | moveCaretToEnd(textareaEl[0]);
15 | setTimeout(function() {
16 | moveCaretToEnd(textareaEl[0]);
17 | textareaEl.focus();
18 | });
19 | });
20 | }
21 |
22 | // from http://stackoverflow.com/questions/4715762/javascript-move-caret-to-last-character
23 | function moveCaretToEnd(el) {
24 | if (typeof el.selectionStart == "number") {
25 | el.selectionStart = el.selectionEnd = el.value.length;
26 | } else if (typeof el.createTextRange != "undefined") {
27 | el.focus();
28 | var range = el.createTextRange();
29 | range.collapse(false);
30 | range.select();
31 | }
32 | }
33 |
34 |
35 | function set_textarea_val(stub) {
36 | var val = textareaEl.val();
37 | textareaEl.val('').focus().val(val + stub);
38 | }
39 |
40 | function stub_imgur_request() {
41 | setTimeout(function() {
42 | var link = "/images/atobi.png";
43 |
44 | var textareaval = "  ";
45 |
46 | set_textarea_val(textareaval);
47 | end_upload_state();
48 |
49 | if (cb) {
50 | cb();
51 | }
52 |
53 | }, 1000);
54 | }
55 |
56 | function real_imgur_request() {
57 | /* Lets build a FormData object*/
58 | var fd = new FormData(); // I wrote about it: https://hacks.mozilla.org/2011/01/how-to-develop-a-html5-image-uploader/
59 | fd.append("image", file); // Append the file
60 | var xhr = new XMLHttpRequest(); // Create the XHR (Cross-Domain XHR FTW!!!) Thank you sooooo much imgur.com
61 | xhr.open("POST", "https://api.imgur.com/3/image.json"); // Boooom!
62 | xhr.onload = function() {
63 | var response = JSON.parse(xhr.responseText);
64 | var link = response.data.link;
65 |
66 | var textareaval = "  ";
67 | set_textarea_val(textareaval);
68 | end_upload_state();
69 |
70 | if (cb) {
71 | cb();
72 | }
73 | };
74 | xhr.setRequestHeader('Authorization', "Client-ID " + IMGUR_KEY); // Get your own key http://api.imgur.com/
75 | xhr.send(fd);
76 | }
77 |
78 | set_upload_state();
79 | real_imgur_request();
80 | // stub_imgur_request();
81 |
82 |
83 | }
84 |
85 | module.exports = handle_imgur_upload;
86 |
--------------------------------------------------------------------------------
/app/client/john.js:
--------------------------------------------------------------------------------
1 |
2 | var SWEAR_REs = {
3 | "JOHN" : /\bjohn\b/ig,
4 | "SARAH" : /\bsarah\b/ig
5 | };
6 |
7 | var Luni;
8 | function sweep_text(el) {
9 | var context = el.nodeValue;
10 |
11 | // test for john...
12 | _.each(SWEAR_REs, function(swr, rep) {
13 | if (swr.test(context)) {
14 | var replaced = Luni.tools.creepify.encode(rep);
15 | context = context.replace(swr, replaced);
16 | }
17 | });
18 |
19 | el.nodeValue = context;
20 |
21 | }
22 |
23 | function clean_element(element) {
24 | element = $(element)[0];
25 | if (!element || !element.childNodes) {
26 | return;
27 | }
28 | recurse(element);
29 | }
30 |
31 | function recurse(element)
32 | {
33 | if (element.childNodes.length > 0)
34 | for (var i = 0; i < element.childNodes.length; i++)
35 | recurse(element.childNodes[i]);
36 |
37 | if (element.nodeType == Node.TEXT_NODE && /\S/.test(element.nodeValue)) {
38 | sweep_text(element);
39 | }
40 | }
41 |
42 | module.exports = function(el) {
43 | bootloader.require("app/static/vendor/lunicode", function(Lunicode) {
44 | Luni = new Lunicode();
45 |
46 | el = $(el)[0];
47 | recurse(el);
48 |
49 | });
50 | }
51 |
--------------------------------------------------------------------------------
/app/client/post_utils.js:
--------------------------------------------------------------------------------
1 | require("app/client/cordova");
2 |
3 | module.exports = {
4 | update_post: function(post_id, text) {
5 | var replyEl = $("#reply" + post_id).children("small.text");
6 | if (!text) {
7 | replyEl.parent().fadeOut();
8 | if (window._POSTS[post_id]) {
9 | window._POSTS[post_id].$el.fadeOut();
10 | }
11 |
12 | return;
13 | }
14 |
15 | require("app/client/text", function(formatter) {
16 | var replyContainer = $("
");
17 |
18 | // Update our in memory text for this post
19 | var reply = window._REPLIES[post_id];
20 | if (reply) {
21 | reply.text = text;
22 | }
23 |
24 | replyEl.data("text", text);
25 |
26 | replyEl.fadeOut(1000, function() {
27 | replyEl.empty();
28 | replyEl.text(text);
29 | formatter.format_text(replyEl);
30 | replyEl.fadeIn();
31 | });
32 | });
33 | },
34 | freshen_links: function(post_id, links) {
35 | var post = window._POSTS && window._POSTS[post_id];
36 | SF.do_when(post, 'post' + post_id, function() {
37 | var postEl = $(".post[data-post-id=" + post_id + "]");
38 |
39 | _.each(links, function(link) {
40 | var textEl;
41 | if (link.post_id === post_id) {
42 | // Look for the link in postEls .op.text
43 | textEl = postEl.find(".op.text");
44 | } else {
45 | textEl = postEl.find("#reply" + link.post_id + " .text");
46 | }
47 |
48 | var a_candidates = textEl.find(".upboat");
49 |
50 | a_candidates.each(function() {
51 | var $this = $(this);
52 | var href = $this.data('href');
53 | var title = $this.data('text');
54 |
55 | if (href === link.href && title === link.title) {
56 | setTimeout(function() {
57 | $this.fadeOut(function() {
58 | $this.addClass('icon-arrow-up upboat');
59 | $this.removeClass('icon-coffee');
60 | $this.fadeIn();
61 | });
62 | }, link.remaining);
63 |
64 | $this.fadeOut(function() {
65 | $this.removeClass('icon-arrow-up');
66 | $this.removeClass("upboat");
67 | $this.addClass('icon-coffee');
68 | $this.fadeIn();
69 |
70 | });
71 | }
72 | });
73 | });
74 |
75 | });
76 |
77 | }
78 | };
79 |
--------------------------------------------------------------------------------
/app/client/prelude.js:
--------------------------------------------------------------------------------
1 | // i dont like that clicking links that are just #href causes me history
2 | // problems.
3 | $(document).on("click", "a[href^='#']", function(event) {
4 | if (!event.altKey && !event.ctrlKey && !event.metaKey && !event.shiftKey) {
5 | event.preventDefault();
6 | }
7 | });
8 |
9 | window.bootloader.modules.jquery = $;
10 | window.bootloader.defs.jquery = $;
11 |
--------------------------------------------------------------------------------
/app/client/prelude.json:
--------------------------------------------------------------------------------
1 | {
2 | "files" : [
3 | "core/client/prelude/hide_addressbar.js"
4 | ],
5 | "vendor" : [
6 | "app/static/vendor/bootstrap.min.js",
7 | "app/static/vendor/jquery.deserialize.js",
8 | "app/static/vendor/md5.js",
9 | "app/static/vendor/marked.js",
10 | "app/static/vendor/jquery-deparam.js",
11 | "app/static/vendor/jquery.scrollTo.js",
12 | "app/static/vendor/jquery.cookie.js",
13 | "app/static/vendor/jquery.timeago.js",
14 | "app/static/vendor/notify.js",
15 | "app/static/vendor/fastclick.js",
16 | "app/static/vendor/strtotime.js",
17 | "app/static/vendor/useractions_stub.js",
18 | "app/static/vendor/hex-rgb.src.js"
19 | ],
20 | "styles" : [
21 | "app/static/styles/bootstrap/bootstrap.css",
22 | "app/static/styles/bootstrap/bootstrap.theme.css",
23 | "app/static/styles/bootstrap/responsive.css",
24 | "app/static/styles/box.css",
25 | "app/static/styles/scrollers.css",
26 | "app/static/styles/whhg.css",
27 | "app/static/styles/bootstrap/overrides.css"
28 | ]
29 | }
30 |
--------------------------------------------------------------------------------
/app/client/sinners.js:
--------------------------------------------------------------------------------
1 | var board_names = require("app/server/board_names");
2 | var SINNERS;
3 | function check_and_replace_trip(el, tripcode) {
4 | tripcode = tripcode || $(el).data("tripcode");
5 | if (SINNERS[tripcode]) {
6 | module.exports.poopcode(el, tripcode);
7 | }
8 |
9 |
10 | }
11 | module.exports = {
12 | poopcode: function(el, tripcode) {
13 | var tripcodeEl = $(el);
14 | var sinner_data = SINNERS[tripcode];
15 |
16 | if ($(el).hasClass("poop")) {
17 | return;
18 | }
19 |
20 | tripcodeEl.css("height", "20px");
21 | tripcodeEl.addClass("poop");
22 |
23 | var iconClass = "icon-poop";
24 | // for SARAH!
25 | if (sinner_data.board_id === board_names.CLERETICS) {
26 | iconClass = "icon-securityalt-shieldalt";
27 | }
28 | // for JOHN!
29 | if (sinner_data.board_id === board_names.APOSTLES) {
30 | iconClass = "icon-crackedegg";
31 | }
32 |
33 |
34 | tripcodeEl.children().each(function() {
35 | $(this).html(" ");
36 | var color = $(this).css("backgroundColor");
37 | $(this).css({
38 | "color" : color,
39 | "backgroundColor" : "inherit"
40 | });
41 | });
42 |
43 | },
44 | punish: function(sinners) {
45 | SINNERS = SINNERS || {};
46 | _.each(sinners, function(sinner) {
47 | SINNERS[sinner.tripcode] = sinner;
48 | });
49 |
50 | SF.trigger("sinners");
51 | },
52 | check_reply: function(replyEl, tripcode) {
53 | SF.do_when(SINNERS, "sinners", function() {
54 | if (replyEl.hasClass("tripcode")) {
55 | check_and_replace_trip($(replyEl)[0], tripcode);
56 | } else {
57 | replyEl.find(".tripcode").each(function() {
58 | check_and_replace_trip(this);
59 | });
60 | }
61 |
62 | });
63 |
64 | bootloader.require("app/client/john", function(john) {
65 | john(replyEl);
66 | });
67 | },
68 | };
69 |
--------------------------------------------------------------------------------
/app/client/sticky_post.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | set_starred: function(post_id, no_load) {
3 | if (!post_id) {
4 | return;
5 | }
6 |
7 | if (!window._POSTS) {
8 | var self = this;
9 | setTimeout(function() {
10 | module.exports.set_starred(post_id, no_load);
11 | }, 100);
12 | return;
13 | }
14 |
15 | if (!window._POSTS[post_id]) {
16 |
17 | if (no_load) {
18 | setTimeout(function() { module.exports.set_starred(post_id, no_load); }, 1000);
19 | } else {
20 | SF.socket().emit("get_post", post_id, function(post) {
21 | post.post_id = post.id;
22 | $C("post", post, function(cmp) {
23 | $(".posts").prepend(cmp.$el);
24 | cmp.star();
25 | cmp.add_markdown();
26 | cmp.gen_tripcodes();
27 | cmp.$el.fadeIn();
28 | });
29 | });
30 | }
31 | } else {
32 | window._POSTS[post_id].star();
33 | }
34 |
35 |
36 |
37 | },
38 | };
39 |
--------------------------------------------------------------------------------
/app/client/storage.js:
--------------------------------------------------------------------------------
1 | "use strict";
2 |
3 | function get_from_storage(key) {
4 |
5 | var val = window.bootloader.storage.get(key);
6 | if (!val) {
7 | val = $.cookie(key);
8 | }
9 |
10 | return val;
11 | }
12 |
13 | function set_in_storage(key, value) {
14 | window.bootloader.storage.set(key, value);
15 | $.removeCookie(key);
16 |
17 | }
18 |
19 | module.exports = {
20 | get: get_from_storage,
21 | set: set_in_storage
22 | };
23 |
--------------------------------------------------------------------------------
/app/client/summarize.js:
--------------------------------------------------------------------------------
1 | function summarize_post(post, archive, title_only) {
2 | archive = archive || "p";
3 |
4 | var div = $("
");
5 | var first_a = $(" ");
6 | var second_a = $(" ");
7 | var url = "/" + archive + "/" + post.id;
8 | first_a.attr("href", url);
9 | second_a.attr("href", url);
10 | var small = $(" ");
11 | first_a.text("#" + post.id);
12 | var b = $(" ").html(post.title);
13 |
14 | second_a.append(b);
15 | if (!title_only) {
16 | var span = $(" ").html(post.text);
17 | second_a.append(span);
18 | }
19 |
20 | small.append(first_a);
21 | if (archive === "p") {
22 | small.append(" /" + post.board_id + " ");
23 | } else {
24 | small.append(" ");
25 | }
26 |
27 | small.append(second_a);
28 | div.append(small);
29 |
30 | var outer = $("
");
31 | outer.append(div);
32 |
33 | var escaped = outer.text();
34 | if (escaped.length > 300) {
35 | small.addClass("hideContent");
36 | small.addClass("truncable");
37 | }
38 |
39 | return outer.html();
40 | }
41 |
42 | module.exports = summarize_post;
43 |
--------------------------------------------------------------------------------
/app/client/voyeur.js:
--------------------------------------------------------------------------------
1 | var Instrumentation = require("app/static/vendor/useractions");
2 |
3 | module.exports = {
4 | init: function() {
5 | this.init = function() { };
6 |
7 |
8 | var storage = window.bootloader.storage;
9 | var last_send = Date.now();
10 | var throttled_send = function() {
11 | if (Date.now() - last_send < 10000) {
12 | setTimeout(throttled_send, 500);
13 | return;
14 | }
15 |
16 | last_send = Date.now();
17 |
18 | var samples = JSON.parse(storage.get("__samples") || "[]");
19 | if (samples && samples.length > 0) {
20 | if (SF.socket()) {
21 | SF.socket().emit("samples", { samples: samples}, function() {
22 | storage.set("__samples", "");
23 | });
24 | } else if (samples.length > 30) {
25 | $.post("/d/s", { samples: samples }, function() {
26 | // Empty the local storage if the POST was successful
27 | storage.set("__samples", "");
28 | });
29 | }
30 | }
31 | };
32 | throttled_send();
33 |
34 |
35 | Instrumentation.Sample.__send = function(sample, meta) {
36 | var samples = JSON.parse(storage.get("__samples") || "[]");
37 | if (!samples) {
38 | samples = [];
39 | }
40 |
41 | samples.push(sample);
42 |
43 | // Hold the UTC timestamp in the sample, too
44 | if (!sample.__ts) {
45 | sample.__ts = new Date(Date.now()).toISOString();
46 | }
47 |
48 | storage.set("__samples", JSON.stringify(samples));
49 |
50 | // only post timespent data like every 15 minutes worth or so
51 | if (meta.dataset !== "timespent" || samples.length > 15) {
52 | throttled_send();
53 | }
54 |
55 | };
56 |
57 | Instrumentation.init();
58 | }
59 | }
60 |
--------------------------------------------------------------------------------
/app/controllers/archives/client.js:
--------------------------------------------------------------------------------
1 | "use strict";
2 | var settings = require("app/client/settings");
3 |
4 | module.exports = {
5 | events: {
6 |
7 | },
8 | init: function() {
9 | this.init_tripcodes();
10 | }
11 | };
12 |
13 | _.extend(module.exports, settings);
14 |
--------------------------------------------------------------------------------
/app/controllers/archives/server.js:
--------------------------------------------------------------------------------
1 | "use strict";
2 |
3 | var controller = require_core("server/controller");
4 | var gen_md5 = require_app("server/md5");
5 | var Board = require_app("models/board");
6 | var ArchivedPost = require_app("models/archived_post");
7 | module.exports = {
8 | // If the controller has assets in its subdirs, set is_package to true
9 | is_package: false,
10 | routes: {
11 | "/:id" : "get",
12 | },
13 |
14 | get: function(ctx, api) {
15 | this.set_title("atob");
16 | api.template.add_stylesheet("post");
17 | api.template.add_stylesheet("archive");
18 |
19 | var board_utils = require_app("server/board_utils");
20 | var render_boards = board_utils.render_boards();
21 | var render_post = api.page.async(function(flush) {
22 | function render_posting(result, highlight_id) {
23 | var post_data = result.dataValues;
24 | post_data = result.dataValues;
25 | post_data.post_id = post_data.id;
26 | post_data.highlight_id = highlight_id;
27 | post_data.maximized = true;
28 | post_data.collapsed = false;
29 | delete post_data.id;
30 |
31 | post_data.replies = _.map(result.children, function(c) { return c.dataValues; } );
32 | post_data.replies = _.sortBy(post_data.replies, function(d) {
33 | return new Date(d.created_at);
34 | });
35 |
36 | post_data.client_options = _.clone(post_data);
37 | post_data.archived = true;
38 |
39 | var postCmp = $C("post", post_data);
40 | var text_formatter = require_root("app/client/text");
41 | postCmp.add_markdown(text_formatter);
42 | var tripcode_gen = require_app("server/tripcode");
43 | postCmp.gen_tripcodes(tripcode_gen.gen_tripcode);
44 |
45 | flush(postCmp.toString());
46 | }
47 |
48 | ArchivedPost.find({
49 | where: { id: ctx.req.params.id},
50 | include: [
51 | {model: ArchivedPost, as: "Children" },
52 | ]})
53 | .success(function(result) {
54 | if (!result) {
55 | var upeye = $C("upeye", { title: "something's not right here..."});
56 | return flush(upeye.toString());
57 | }
58 |
59 | var post_data = result.dataValues;
60 |
61 | // If it's a child post, get its thread
62 | if (post_data.parent_id) {
63 | ArchivedPost.find({
64 | where: { id: post_data.thread_id },
65 | include: [
66 | {model: ArchivedPost, as: "Children" },
67 | ]
68 | }).success(function(parent) {
69 | if (!parent) {
70 | render_posting(result);
71 | } else {
72 | api.bridge.controller("posts", "focus_post", post_data.id);
73 | render_posting(parent, post_data.id);
74 | }
75 | });
76 |
77 | } else {
78 | render_posting(result);
79 | }
80 | });
81 |
82 | });
83 |
84 | var template_str = api.template.render("controllers/posts/show.html.erb",
85 | { render_post: render_post, render_boards: render_boards, tripcode: gen_md5(Math.random()) });
86 | api.page.render({ content: template_str, socket: true });
87 | },
88 |
89 | socket: function() {}
90 | };
91 |
--------------------------------------------------------------------------------
/app/controllers/archives/test/server.js:
--------------------------------------------------------------------------------
1 | describe("A blank test for archives", function() {
2 | it("should work", function(done) {
3 | SF.controller("archives", function(ctrl) {
4 | assert.notEqual(ctrl, null);
5 |
6 | done();
7 | });
8 | });
9 | });
10 |
--------------------------------------------------------------------------------
/app/controllers/boards/test/server.js:
--------------------------------------------------------------------------------
1 | describe("A blank test for boards", function() {
2 | it("should work", function(done) {
3 | SF.controller("boards", function(ctrl) {
4 | assert.notEqual(ctrl, null);
5 |
6 | done();
7 | });
8 | });
9 | });
10 |
--------------------------------------------------------------------------------
/app/controllers/data/test/server.js:
--------------------------------------------------------------------------------
1 | describe("A blank test for data", function() {
2 | it("should work", function(done) {
3 | SF.controller("data", function(ctrl) {
4 | assert.notEqual(ctrl, null);
5 |
6 | done();
7 | });
8 | });
9 | });
10 |
--------------------------------------------------------------------------------
/app/controllers/home/test/client.js:
--------------------------------------------------------------------------------
1 | describe("A blank test", function() {
2 | it("should work", function(done) {
3 | SF.controller("home", function(ctrl) {
4 | assert.notEqual(ctrl, null);
5 |
6 | done();
7 | });
8 | });
9 | });
10 |
--------------------------------------------------------------------------------
/app/controllers/home/test/server.js:
--------------------------------------------------------------------------------
1 | "use strict";
2 |
3 | var test_helper = require("superfluous").test_helper;
4 | test_helper.init();
5 | var assert = require("assert");
6 |
7 | describe("Home Controller", function() {
8 | it("should render the index page", function(done) {
9 | test_helper.test_route("home", "index", [], function(rendered_page) {
10 | // we've only verified that it contains the words 'html' and 'superfluous', so
11 | // far...
12 | assert.notEqual(rendered_page.indexOf("html"), -1);
13 | assert.notEqual(rendered_page.indexOf("superfluous"), -1);
14 | done();
15 | });
16 | });
17 | });
18 |
--------------------------------------------------------------------------------
/app/controllers/icons/client.js:
--------------------------------------------------------------------------------
1 | "use strict";
2 |
3 | module.exports = {
4 | events: {
5 |
6 | },
7 | init: function() {
8 |
9 | }
10 | };
11 |
--------------------------------------------------------------------------------
/app/controllers/icons/server.js:
--------------------------------------------------------------------------------
1 | "use strict";
2 |
3 | var controller = require_core("server/controller");
4 | // Helpers for serialized form elements
5 | var value_of = controller.value_of,
6 | array_of = controller.array_of;
7 |
8 |
9 | module.exports = {
10 | // If the controller has assets in its subdirs, set is_package to true
11 | is_package: false,
12 | routes: {
13 | "" : "index",
14 | },
15 |
16 | index: function(ctx, api) {
17 | var template_str = api.template.render("controllers/icons/icons.html.erb", {});
18 | api.page.render({ content: template_str});
19 | },
20 |
21 | socket: function() {}
22 | };
23 |
--------------------------------------------------------------------------------
/app/controllers/icons/test/server.js:
--------------------------------------------------------------------------------
1 | describe("A blank test for icons", function() {
2 | it("should work", function(done) {
3 | SF.controller("icons", function(ctrl) {
4 | assert.notEqual(ctrl, null);
5 |
6 | done();
7 | });
8 | });
9 | });
10 |
--------------------------------------------------------------------------------
/app/controllers/posts/client.js:
--------------------------------------------------------------------------------
1 | "use strict";
2 |
3 | var settings = require("app/client/settings");
4 | var post_utils = require("app/client/post_utils");
5 | var notif = require("app/client/notif");
6 | var chat = require("app/client/chat");
7 |
8 | require("app/client/drawing");
9 |
10 | var newthread = require("app/client/newthread");
11 |
12 | window._POSTS = window._POSTS || {};
13 | module.exports = {
14 | events: {
15 | "click .post .title h4" : "post_title_click"
16 |
17 | },
18 | post_title_click: function(e) {
19 | var linklink = $(e.target).closest(".linklink, .titlelink");
20 | if (linklink.length) {
21 | return;
22 | }
23 |
24 | e.preventDefault();
25 | e.stopPropagation();
26 |
27 | return;
28 |
29 | },
30 | init: function() {
31 | this.init_tripcodes();
32 |
33 |
34 | var current_location = window.location.pathname;
35 | SF.subscribe("popstate", function() {
36 | if (window.location.pathname !== current_location) {
37 | window.location.reload();
38 | }
39 | });
40 | },
41 | goto_chat: function() {
42 | window.location = "/chat";
43 | },
44 | focus_post: function(id) {
45 | var self = this;
46 | var params = $.deparam(window.location.search.substr(1));
47 |
48 | setTimeout(function() {
49 | var dest = $("#reply" + id).filter(":visible");
50 | if (params.e) {
51 | dest = $("form.replyform");
52 | $("form.replyform textarea").focus();
53 | }
54 |
55 | if (!dest.length) {
56 | self.focus_post(id);
57 | } else {
58 | $("body").scrollTo(dest, { duration: 400, offset: { top: -100 } });
59 | }
60 |
61 | }, 50);
62 | },
63 | socket: function(s) {
64 | var self = this;
65 | notif.subscribe(s);
66 |
67 | chat.add_socket_subscriptions(s);
68 | settings.add_socket_subscriptions(s);
69 | s.on("doings", function(data) {
70 | var post = window._POSTS[data.post_id];
71 | if (post) {
72 | post.update_counts(data.counts, data.last_seen);
73 | }
74 |
75 | });
76 |
77 | s.on("new_reply", function(data) {
78 | var post = window._POSTS[data.parent_id];
79 | if (post) {
80 | post.add_reply(data);
81 | }
82 | });
83 |
84 | s.on("joined", function(c) {
85 | console.log("Joined the board", c);
86 | });
87 |
88 | s.on("shake_post", function(post_id, duration) {
89 | var post = window._POSTS[post_id];
90 | if (post) {
91 | post.shake(duration);
92 | }
93 | });
94 |
95 | s.on("update_post", function(post_id, text) {
96 | post_utils.update_post(post_id, text);
97 | });
98 |
99 | s.on("notif", function(msg, type, options) {
100 | notif.handle_notif(msg, type, options);
101 | });
102 |
103 | var self = this;
104 | self.do_when(self.board, "set_board", function() {
105 | s.emit("join", self.board);
106 | });
107 |
108 | var post_id = $(".post").data("post-id");
109 | s.emit("isdoing", { what: "focused", post_id: post_id });
110 | },
111 | goto: function(url) {
112 | // redirecting
113 | window.location = url;
114 | },
115 | hide_loading: function() {
116 | $(".loading").fadeOut();
117 | }
118 | };
119 |
120 | _.extend(module.exports, chat);
121 | _.extend(module.exports.events, chat.controller_events);
122 |
123 | _.extend(module.exports, settings);
124 | _.extend(module.exports.events, settings.controller_events);
125 |
126 | _.extend(module.exports, newthread);
127 | _.extend(module.exports.events, newthread.controller_events);
128 |
--------------------------------------------------------------------------------
/app/controllers/posts/test/server.js:
--------------------------------------------------------------------------------
1 | describe("A blank test for posts", function() {
2 | it("should work", function(done) {
3 | SF.controller("posts", function(ctrl) {
4 | assert.notEqual(ctrl, null);
5 |
6 | done();
7 | });
8 | });
9 | });
10 |
--------------------------------------------------------------------------------
/app/controllers/profiles/test/server.js:
--------------------------------------------------------------------------------
1 | describe("A blank test for profiles", function() {
2 | it("should work", function(done) {
3 | SF.controller("profiles", function(ctrl) {
4 | assert.notEqual(ctrl, null);
5 |
6 | done();
7 | });
8 | });
9 | });
10 |
--------------------------------------------------------------------------------
/app/controllers/rss/server.js:
--------------------------------------------------------------------------------
1 | "use strict";
2 |
3 | var context = require_core("server/context");
4 | var controller = require_core("server/controller");
5 | var Post = require_app("models/post");
6 |
7 | var marked = require_app("static/vendor/marked");
8 | global.marked = marked;
9 |
10 | var client_text = require_app("client/text");
11 |
12 | var RSS = require('rss');
13 |
14 | var FEEDS = {};
15 | var TIMERS = {};
16 | var FEED_TIMEOUT = 60 * 1000;
17 |
18 | function clear_feed(board) {
19 | if (!TIMERS[board]) {
20 | TIMERS[board] = setTimeout(function() {
21 | console.log("Clearing feed for board /" + board);
22 | delete FEEDS[board];
23 | delete TIMERS[board];
24 |
25 | }, FEED_TIMEOUT);
26 | }
27 | }
28 |
29 | function refresh_feed(board, cb) {
30 | if (FEEDS[board]) {
31 | cb(FEEDS[board]);
32 | return;
33 | }
34 |
35 | console.log("Generating feed for board /" + board);
36 |
37 | var where = {
38 | thread_id: null,
39 | parent_id: null
40 |
41 | };
42 |
43 | if (board !== "*") {
44 | where.board_id = board;
45 | } else {
46 | where.board_id = [ "a", "b" ];
47 | }
48 |
49 | if (!FEEDS[board]) {
50 | Post.findAll({
51 | order: "bumped_at DESC",
52 | where: where,
53 | limit: 20
54 | }).success(function(results) {
55 | var feed = new RSS({
56 | title: "atob/" + board,
57 | feed_url: "http://atob.xyz/r/" + board,
58 | site_url: "http://atob.xyz"
59 | });
60 |
61 | _.each(results, function(post) {
62 | if (!post.bumped_at) {
63 | return;
64 | }
65 |
66 | var div = $("
");
67 | div.text(post.title);
68 |
69 | var title = client_text.format_text(div);
70 | title = div.text();
71 | feed.item({
72 | title: title + " [" + (post.replies || 0) + " replies]",
73 | description: post.text,
74 | url: "_ATOB_HOST_" + "/p/" + post.id,
75 | categories: [ post.board ],
76 | author: "anon",
77 | date: post.bumped_at
78 | });
79 | });
80 |
81 | FEEDS[board] = feed;
82 | clear_feed(board);
83 |
84 | cb(feed);
85 | });
86 | }
87 | }
88 |
89 | module.exports = {
90 | // If the controller has assets in its subdirs, set is_package to true
91 | is_package: false,
92 | routes: {
93 | "/:id" : "index",
94 | },
95 |
96 | index: function(ctx, api) {
97 | var board_id = ctx.req.params.id;
98 |
99 | refresh_feed(board_id, function(feed) {
100 | var hostname = ctx.req.headers.host;
101 | var xml_data = feed.xml();
102 | var proto = ctx.req.proto || "https";
103 | xml_data = xml_data.replace(/_ATOB_HOST_/g, proto + "://" + hostname);
104 | ctx.res.end(xml_data);
105 | });
106 | },
107 |
108 | socket: function() {}
109 | };
110 |
--------------------------------------------------------------------------------
/app/controllers/rss/test/server.js:
--------------------------------------------------------------------------------
1 | describe("A blank test for rss", function() {
2 | it("should work", function(done) {
3 | SF.controller("rss", function(ctrl) {
4 | assert.notEqual(ctrl, null);
5 |
6 | done();
7 | });
8 | });
9 | });
10 |
--------------------------------------------------------------------------------
/app/controllers/search/client.js:
--------------------------------------------------------------------------------
1 | "use strict";
2 |
3 | require("app/client/summarize");
4 |
5 | var last_update = null;
6 | var last_query = null;
7 |
8 | module.exports = {
9 | events: {
10 | "keydown .searchinput" : "handle_search_keydown"
11 |
12 | },
13 | init: function() {
14 |
15 | },
16 | display_results: function(results) {
17 | // we need to bump the queue to display results, i guess
18 | var self = this;
19 | self.latest_results = results;
20 | self.throttled_display_results();
21 | },
22 | throttled_display_results: _.throttle(function() {
23 | var self = this;
24 | var div = $("
");
25 |
26 | var summarize = require("app/client/summarize");
27 | if (!self.latest_results.length) {
28 | div.append("you shout, but there is no answer");
29 | }
30 |
31 | _.each(self.latest_results, function(r) {
32 | var el = summarize(r);
33 | var elel = $("
").append(el);
34 |
35 | div.append(elel);
36 | });
37 |
38 | var results_hash = window.md5(div.html());
39 |
40 | if (results_hash !== self.previous_results_hash) {
41 | var resultsEl = $(".results");
42 | resultsEl.empty();
43 | resultsEl.append(div);
44 | }
45 | self.previous_results_hash = results_hash;
46 |
47 |
48 | }, 1000),
49 | socket: function(s) {
50 | var self = this;
51 | s.on("queryresults", function(results, q, ts) {
52 | if (!last_update || ts > last_update) {
53 | // now we display the results
54 | self.display_results(results);
55 |
56 | SF.replace("/s/?q=" + q);
57 |
58 | last_update = ts;
59 | }
60 |
61 | });
62 |
63 | },
64 | handle_search_keydown: _.throttle(function() {
65 | var query = $(".searchinput").val();
66 | if (query === last_query) {
67 | return;
68 | }
69 |
70 | SF.socket().emit("query", query);
71 | last_query = query;
72 |
73 | }, 500)
74 | };
75 |
--------------------------------------------------------------------------------
/app/controllers/search/server.js:
--------------------------------------------------------------------------------
1 | "use strict";
2 |
3 | var controller = require_core("server/controller");
4 | // Helpers for serialized form elements
5 | var value_of = controller.value_of,
6 | array_of = controller.array_of;
7 |
8 | var escape_html = require("escape-html");
9 | var Post = require_app("models/post");
10 | var HIDDEN_BOARDS = require_app("server/hidden_boards");
11 |
12 | function find_posts(q, cb) {
13 | // turn query into escaped query...
14 | q = q.replace(/ /g, "%").replace("+", " ");
15 | var likeq = "%" + escape_html(q) + "%";
16 |
17 | Post.findAll({where: [ "(title like ? or text like ?)", likeq, likeq ], limit: 100, order: "ID DESC"})
18 | .success(function(results) {
19 | results = _.filter(results, function(r) {
20 | var is_hidden = false;
21 | _.each(HIDDEN_BOARDS, function(board) {
22 | is_hidden = is_hidden || board === r.board_id;
23 | });
24 | return !is_hidden;
25 | });
26 |
27 | if (results) {
28 | cb(_.last(results, 30));
29 | }
30 | });
31 |
32 |
33 | }
34 |
35 | module.exports = {
36 | // If the controller has assets in its subdirs, set is_package to true
37 | is_package: false,
38 | routes: {
39 | "" : "index",
40 | },
41 |
42 | index: function(ctx, api) {
43 | var query = ctx.req.query.q;
44 | var render_search_results = api.page.async(function(flush) {
45 | if (query) {
46 | find_posts(query, function(posts) {
47 | var div = $("
");
48 | var summarize = require_app("client/summarize");
49 | _.each(posts, function(p) {
50 | div.append(summarize(p));
51 | });
52 |
53 | flush(div.html());
54 | });
55 | } else {
56 | flush("");
57 | }
58 |
59 | });
60 |
61 | var template_str = api.template.render("controllers/search/search.html.erb", { query: query, render_search_results: render_search_results });
62 | // maybe load the search query results, too
63 | api.page.render({ content: template_str, socket: true});
64 | },
65 |
66 | socket: function(s) {
67 | s.on("query", function(q) {
68 | find_posts(q, function(results) {
69 | s.emit("queryresults", _.last(results, 30), q, Date.now());
70 |
71 | });
72 | });
73 |
74 | }
75 | };
76 |
--------------------------------------------------------------------------------
/app/controllers/search/test/server.js:
--------------------------------------------------------------------------------
1 | describe("A blank test for search", function() {
2 | it("should work", function(done) {
3 | SF.controller("search", function(ctrl) {
4 | assert.notEqual(ctrl, null);
5 |
6 | done();
7 | });
8 | });
9 | });
10 |
--------------------------------------------------------------------------------
/app/fakedata.js:
--------------------------------------------------------------------------------
1 | var faker = require("Faker");
2 | var crypto = require("crypto");
3 |
4 | module.exports = {
5 | generate: function() {
6 |
7 | var Board = require_app("models/board");
8 | var Post = require_app("models/post");
9 |
10 | var boards = [];
11 | var board_ids = {};
12 | var board_id;
13 | for (var i = 0; i < 5; i++) {
14 | board_id = parseInt(Math.random() * 26, 10);
15 | var board_code = String.fromCharCode(97 + board_id);
16 |
17 | if (board_ids[board_code]) {
18 | i--;
19 | continue;
20 | }
21 |
22 | board_ids[board_code] = 1;
23 | var board = Board.create({
24 | name: board_code,
25 | title: faker.Company.catchPhrase()
26 | });
27 | boards.push(board);
28 | }
29 |
30 | var posts = [];
31 | var board_codes = _.keys(board_ids);
32 | var density = 0.90;
33 | for (var i = 0; i < 100; i++) {
34 | board_id = board_codes[_.random(0, board_codes.length)];
35 |
36 | var parent_index = null;
37 | if (Math.random() < density) {
38 | parent_index = _.random(0, posts.length);
39 | }
40 |
41 | var parent = posts[parent_index];
42 | if (parent && parent_index != null) {
43 | parent.success(function(p) {
44 | var post_data = {
45 | title: faker.Lorem.sentence(),
46 | text: faker.Lorem.sentences(),
47 | board_id: board_id,
48 | author: faker.Name.firstName()
49 | };
50 |
51 | post_data.thread_id = p.thread_id;
52 | post_data.parent_id = p.id;
53 | post_data.board_id = p.board_id;
54 |
55 | var hash = crypto.Hash("md5");
56 | hash.update("" + faker.Lorem.words());
57 | post_data.tripcode = hash.digest('hex');
58 | var post = Post.create(post_data);
59 | posts.push(post);
60 | });
61 | } else {
62 | var post_data = {
63 | title: faker.Lorem.sentence(),
64 | text: faker.Lorem.sentences(),
65 | board_id: board_id,
66 | };
67 |
68 | var post = Post.create(post_data);
69 | posts.push(post);
70 | }
71 |
72 | }
73 | }
74 | };
75 |
--------------------------------------------------------------------------------
/app/models/action.js:
--------------------------------------------------------------------------------
1 | var sequelize = require_app("models/model");
2 |
3 | var Action = sequelize.instance.define('Action', {
4 | id: { type: sequelize.INTEGER, primaryKey: true},
5 | object: sequelize.STRING,
6 | actor: sequelize.STRING,
7 | action: sequelize.STRING,
8 | count: {
9 | type: sequelize.INTEGER,
10 | defaultValue: 1
11 | }
12 | });
13 |
14 | module.exports = Action;
15 |
--------------------------------------------------------------------------------
/app/models/archived_post.js:
--------------------------------------------------------------------------------
1 | var sequelize = require_app("models/model");
2 |
3 |
4 | var ArchivedPost = sequelize.archive.define('ArchivedPost', {
5 | title: sequelize.STRING,
6 | text: sequelize.TEXT,
7 | thread_id: sequelize.INTEGER,
8 | tripcode: sequelize.STRING,
9 | author: sequelize.STRING,
10 | replies: sequelize.INTEGER,
11 | downs: sequelize.INTEGER,
12 | ups: sequelize.INTEGER,
13 | bumped_at: sequelize.DATE
14 | });
15 |
16 | ArchivedPost.hasOne(ArchivedPost, { as: 'Thread', foreignKey: 'thread_id', through: null });
17 | ArchivedPost.hasMany(ArchivedPost, { as: 'Children', foreignKey: 'parent_id', through: null });
18 |
19 | module.exports = ArchivedPost;
20 |
21 |
--------------------------------------------------------------------------------
/app/models/ban.js:
--------------------------------------------------------------------------------
1 | var sequelize = require_app("models/model");
2 |
3 | var Post = require_app("models/post");
4 | var Ban = sequelize.instance.define('Ban', {
5 | ip: sequelize.STRING,
6 | from: sequelize.STRING,
7 | post_id: sequelize.INTEGER,
8 | reason_id: sequelize.INTEGER,
9 | tripcode: sequelize.STRING,
10 | hours: sequelize.INTEGER,
11 | board: sequelize.STRING
12 | });
13 |
14 | Ban.belongsTo(Post);
15 | Ban.belongsTo(Post, { as: 'Reason', foreignKey: 'reason_id'});
16 |
17 | module.exports = Ban;
18 |
--------------------------------------------------------------------------------
/app/models/board.js:
--------------------------------------------------------------------------------
1 | var sequelize = require_app("models/model");
2 |
3 | var Board = sequelize.instance.define('Board', {
4 | title: sequelize.STRING,
5 | rules: sequelize.TEXT,
6 | longname: sequelize.STRING,
7 | name: {
8 | type: sequelize.STRING,
9 | primaryKey: true
10 | },
11 | description: sequelize.TEXT
12 | });
13 |
14 | module.exports = Board;
15 |
--------------------------------------------------------------------------------
/app/models/board_claim.js:
--------------------------------------------------------------------------------
1 | var sequelize = require_app("models/model");
2 |
3 | var BoardClaim = sequelize.instance.define('BoardClaims', {
4 | board_id: sequelize.STRING,
5 | accepted: sequelize.BOOLEAN,
6 | author: sequelize.STRING,
7 | tripcode: sequelize.STRING
8 | });
9 |
10 | module.exports = BoardClaim;
11 |
--------------------------------------------------------------------------------
/app/models/board_config.js:
--------------------------------------------------------------------------------
1 | var sequelize = require_app("models/model");
2 |
3 | var BoardConfig = sequelize.instance.define('BoardConfigs', {
4 | board_id: {
5 | type: sequelize.STRING,
6 | primaryKey: true
7 | },
8 | config: {
9 | type: sequelize.TEXT,
10 | get: function() {
11 | var title = JSON.parse(this.getDataValue('config') || "{}");
12 | return title;
13 | },
14 | set: function(val) {
15 | this.setDataValue('config', JSON.stringify(val));
16 |
17 | }
18 | },
19 | author: sequelize.STRING,
20 | tripcode: sequelize.STRING
21 | }, {
22 | instanceMethods: {
23 | getSetting: function(key) {
24 | var config = JSON.parse(this.getDataValue('config') || "{}");
25 | return config[key];
26 | },
27 | setSetting: function(key, val) {
28 | var config = JSON.parse(this.getDataValue('config') || "{}");
29 | config[key] = val;
30 | this.config = config;
31 | }
32 |
33 | }
34 | });
35 |
36 | module.exports = BoardConfig;
37 |
--------------------------------------------------------------------------------
/app/models/ip.js:
--------------------------------------------------------------------------------
1 | var sequelize = require_app("models/model");
2 |
3 | var Post = require_app("models/post");
4 | var IP = sequelize.instance.define('IP', {
5 | ip: sequelize.STRING,
6 | browser: sequelize.STRING,
7 | });
8 |
9 | IP.belongsTo(Post);
10 |
11 | var gen_md5 = require_app("server/md5");
12 | IP.toHash = function(ip) {
13 | return gen_md5("atob:" + ip);
14 | };
15 |
16 | module.exports = IP;
17 |
18 |
--------------------------------------------------------------------------------
/app/models/link.js:
--------------------------------------------------------------------------------
1 |
2 | var sequelize = require_app("models/model");
3 |
4 | var Post = require_app("models/post");
5 | var Link = sequelize.instance.define('Links', {
6 | id: sequelize.INTEGER,
7 | ip: sequelize.STRING,
8 | ups: sequelize.INTEGER,
9 | downs: sequelize.INTEGER,
10 | author: sequelize.STRING,
11 | tripcode: sequelize.STRING,
12 | href: sequelize.STRING,
13 | title: sequelize.STRING,
14 | image: sequelize.BOOLEAN,
15 | created_at: sequelize.DATE,
16 | updated_at: sequelize.DATE,
17 | post_id: sequelize.INTEGER,
18 | board: sequelize.STRING
19 | });
20 |
21 | Link.belongsTo(Post, {foreignKey: 'post_id'});
22 | Post.hasMany(Link);
23 |
24 | module.exports = Link;
25 |
--------------------------------------------------------------------------------
/app/models/model.js:
--------------------------------------------------------------------------------
1 | var Sequelize = require("sequelize");
2 |
3 | var archive = new Sequelize('database', 'username', 'password', {
4 | dialect: 'sqlite',
5 | storage: 'ab.sqlite',
6 | logging: function() { },
7 | define: {
8 | sync: { force: true },
9 | underscored: true
10 | }
11 | });
12 |
13 | var sequelize = new Sequelize('database', 'username', 'password', {
14 | dialect: 'sqlite',
15 | storage: 'db.sqlite',
16 | logging: function() { },
17 | define: {
18 | sync: { force: true },
19 | underscored: true
20 | }
21 | });
22 |
23 |
24 | module.exports = Sequelize;
25 | module.exports.instance = sequelize;
26 | module.exports.archive = archive;
27 |
28 | // all models defined need to be required somewhere before the main setup is called
29 | require_app("models/post");
30 | require_app("models/user");
31 | require_app("models/board");
32 | require_app("models/board_config");
33 | require_app("models/action");
34 |
35 | // If we are in RESET mode, we should load all models off the disk before finishing loading this file...
36 | if (process.env.RESET) {
37 | // assume we are in root dir
38 | var cwd = process.cwd();
39 | var fs = require("fs");
40 | var paths = fs.readdirSync(cwd + "/app/models");
41 |
42 | console.log("LOADING ALL MODELS BECAUSE OF FIRST RUN");
43 |
44 | _.each(paths, function(path) {
45 | try {
46 | require_app("models/" + path.replace(/\.js/g, ''));
47 |
48 | } catch(e) {
49 | console.log(e);
50 |
51 | }
52 |
53 | });
54 |
55 | }
56 |
--------------------------------------------------------------------------------
/app/models/post.js:
--------------------------------------------------------------------------------
1 | var sequelize = require_app("models/model");
2 |
3 | var User = require_app("models/user");
4 | var Board = require_app("models/board");
5 |
6 | var Post = sequelize.instance.define('Post', {
7 | title: sequelize.STRING,
8 | text: sequelize.TEXT,
9 | thread_id: sequelize.INTEGER,
10 | tripcode: sequelize.STRING,
11 | author: sequelize.STRING,
12 | replies: sequelize.INTEGER,
13 | downs: sequelize.INTEGER,
14 | ups: sequelize.INTEGER,
15 | burtles: sequelize.INTEGER,
16 | bumped_at: sequelize.DATE
17 | });
18 |
19 | Post.hasOne(Post, { as: 'Thread', foreignKey: 'thread_id', through: null });
20 | Post.hasMany(Post, { as: 'Children', foreignKey: 'parent_id', through: null });
21 | Board.hasMany(Post);
22 |
23 | module.exports = Post;
24 |
--------------------------------------------------------------------------------
/app/models/trophy.js:
--------------------------------------------------------------------------------
1 | var sequelize = require_app("models/model");
2 |
3 | var Trophy = sequelize.instance.define('Trophy', {
4 | id: { type: sequelize.INTEGER, primaryKey: true},
5 | actor: sequelize.STRING,
6 | anon: sequelize.STRING,
7 | trophy: sequelize.STRING,
8 | anon_id: {
9 | type: sequelize.INTEGER,
10 | defaultValue: 1
11 | },
12 | post_id: {
13 | type: sequelize.INTEGER,
14 | defaultValue: 1
15 | }
16 | }, {
17 | tableName: "Trophies"
18 | });
19 |
20 | module.exports = Trophy;
21 |
--------------------------------------------------------------------------------
/app/models/user.js:
--------------------------------------------------------------------------------
1 | var sequelize = require_app("models/model");
2 |
3 | var User = sequelize.instance.define('User', {
4 | tripcode: sequelize.STRING,
5 | tripname: sequelize.STRING
6 | });
7 |
8 | module.exports = User;
9 |
--------------------------------------------------------------------------------
/app/server/anon_pocket.js:
--------------------------------------------------------------------------------
1 | var Trophy = require_app("models/trophy");
2 | var Post = require_app("models/post");
3 | var HIDDEN_BOARDS = require_app("server/hidden_boards");
4 |
5 | var POCKET_RE = /:hand-right:\s*(:[\w-]*:\s*)+(?:\(.?\(\s*)?>>\s*(\d*)/;
6 | module.exports = {
7 | find_and_create_items: function(post) {
8 | if (post.dataValues) {
9 | post = post.dataValues;
10 | }
11 |
12 | var post_id = post.post_id || post.id;
13 | if (post_id) {
14 | Trophy.findAll({where: { anon_id: post_id }}).success(function (results) {
15 | _.each(results, function(r) { r.destroy(); });
16 | });
17 | }
18 |
19 | if (!post.text) {
20 | return;
21 | }
22 |
23 | var text = post.text;
24 | var groups = text.match(POCKET_RE);
25 | var trophies = [];
26 | while (groups && groups[2]) {
27 | var trophy = groups[1].trim();
28 | var parent_id = groups[2];
29 |
30 | if (!parent_id || !trophy) {
31 | break;
32 | }
33 |
34 | trophies.push([trophy, parent_id]);
35 | var indexOf = text.lastIndexOf(trophy);
36 | text = text.substring(0, indexOf) + text.substring(indexOf + trophy.length);
37 | groups = text.match(POCKET_RE);
38 | }
39 |
40 | var index = 0;
41 | function make_trophy() {
42 | if (index >= trophies.length) { return; }
43 |
44 | var trophy = trophies[trophies.length - 1 - index];
45 | console.log("Adding trophy", trophy, "in post", parent_id);
46 |
47 | Post.find({ where: { id: trophy[1] }, order: "id ASC"}).success(function(pocket_post) {
48 | if (pocket_post) {
49 | // we found a post, therefore we can make this action!
50 | Trophy.create({
51 | actor: post.tripcode,
52 | updated_at: pocket_post.created_at + (index * 1000),
53 | created_at: pocket_post.created_at + (index * 1000),
54 | anon: pocket_post.dataValues.tripcode,
55 | trophy: trophy[0],
56 | post_id: parent_id,
57 | anon_id: post_id
58 | });
59 | }
60 | });
61 |
62 |
63 |
64 | index++;
65 | setTimeout(make_trophy, 1500);
66 | }
67 |
68 | make_trophy();
69 | }
70 | };
71 |
--------------------------------------------------------------------------------
/app/server/board_migrations.js:
--------------------------------------------------------------------------------
1 | var BOARDS = require_app("server/board_names");
2 | var HIDDEN_BOARDS = require_app("server/hidden_boards");
3 | var WORSHIP_BOARDS = require_app("server/worship_boards").boards;
4 |
5 | var migrations = {};
6 | migrations["heretics"] = "_heretics";
7 | migrations["cleretics"] = "_cleretics";
8 | migrations["apostles"] = "_apostles";
9 |
10 |
11 | module.exports = {
12 | run: function() {
13 | var Post = require_app("models/post");
14 | _.each(migrations, function(new_board, old_board) {
15 | Post.findAll({where: {board_id: old_board }}).success(function(results) {
16 | _.each(results, function(r) {
17 | r.board_id = new_board;
18 | r.save();
19 | });
20 |
21 | });
22 |
23 | if (_.contains(HIDDEN_BOARDS, old_board)) {
24 | HIDDEN_BOARDS.push(new_board);
25 | }
26 |
27 | if (_.contains(WORSHIP_BOARDS, old_board)) {
28 | WORSHIP_BOARDS.push(new_board);
29 | }
30 |
31 | _.each(BOARDS, function(name, key){
32 | if (migrations[name]) {
33 | if (BOARDS[key]) {
34 | BOARDS[key] = migrations[name];
35 | }
36 | }
37 | });
38 | });
39 |
40 |
41 | }
42 | };
43 |
--------------------------------------------------------------------------------
/app/server/board_names.js:
--------------------------------------------------------------------------------
1 | var boards = {
2 | HERETICS: "_heretics",
3 | CLERETICS: "_cleretics",
4 | APOSTLES: "_apostles",
5 | LOG: "log",
6 | CHAT: "chat",
7 | ADS: "ads",
8 | FAQ: "faq",
9 | BUGS: "bugs",
10 | MOD: "mod",
11 | COP: "cop",
12 | BAN: "ban",
13 | TEST: "test",
14 | };
15 |
16 |
17 | module.exports = boards;
18 |
--------------------------------------------------------------------------------
/app/server/board_utils.js:
--------------------------------------------------------------------------------
1 |
2 | var bridge = require_core("server/bridge");
3 |
4 | var page = require_core("server/page");
5 | var template = require_core("server/template");
6 |
7 | module.exports = {
8 | render_boards: function() {
9 | var render_boards = page.async(function(flush) {
10 | var boards = [ "a", "to", "b", "links", "gifs" ];
11 |
12 | var template_str = template.partial("home/board_links.html.erb", {
13 | boards: boards
14 | });
15 |
16 | flush(template_str);
17 |
18 | });
19 |
20 | return render_boards;
21 | }
22 | };
23 |
--------------------------------------------------------------------------------
/app/server/chat.js:
--------------------------------------------------------------------------------
1 | var Post = require_app("models/post");
2 | var posting = require_app("server/posting");
3 | var render_posting = posting.render_posting;
4 | var board_names = require_app("server/board_names");
5 |
6 | module.exports = {
7 | render_recent: function(api) {
8 | return api.page.async(function(flush) {
9 | Post.findAll({
10 | where: {
11 | board_id: {
12 | eq: board_names.CHAT
13 | },
14 | },
15 | order: "id DESC",
16 | limit: 30
17 | }).success(function(posts) {
18 | // Find the most recent thread
19 | var parent = null;
20 | _.each(posts, function(post) {
21 | if (post && !post.dataValues.parent_id && !parent) {
22 | parent = post;
23 | }
24 | });
25 |
26 |
27 | if (!parent && posts.length) {
28 | parent = posts[posts.length - 1];
29 | }
30 |
31 | if (!parent) {
32 | return flush("");
33 | }
34 |
35 | parent.children = posts;
36 | render_posting(api, flush, parent, null /* highlight_id */, true /* nothreading! */);
37 |
38 | api.bridge.call("app/client/chat", "show_chat", parent.dataValues.post_id);
39 | });
40 | });
41 | }
42 | };
43 |
--------------------------------------------------------------------------------
/app/server/hidden_boards.js:
--------------------------------------------------------------------------------
1 | var BoardConfig = require_app("models/board_config");
2 |
3 | module.exports = [
4 | "cleretics",
5 | "apostles",
6 | "heretics",
7 | "faq",
8 | "bugs",
9 | "log",
10 | "mod",
11 | "cop",
12 | "ban",
13 | "test",
14 | "chat",
15 | "ads"
16 | ];
17 |
--------------------------------------------------------------------------------
/app/server/md5.js:
--------------------------------------------------------------------------------
1 | "use strict";
2 |
3 | var crypto = require("crypto");
4 | var gen_md5 = function(h) {
5 | var hash = crypto.Hash("md5");
6 | hash.update("" + h);
7 | return hash.digest("hex");
8 | };
9 |
10 | module.exports = gen_md5;
11 |
--------------------------------------------------------------------------------
/app/server/sponsored_content.js:
--------------------------------------------------------------------------------
1 | var Post = require_app("models/post");
2 | var board_names = require_app("server/board_names");
3 |
4 | module.exports = {
5 | render: function(api) {
6 | api.template.add_stylesheet("sponsored_content");
7 | var ad;
8 | return api.page.async(function(flush) {
9 | Post.findAll({
10 | where: { board_id: board_names.ADS, parent_id: null },
11 | }).success(function(results) {
12 |
13 | function wrap_in_divs(inner) {
14 | var outer = $("
");
15 | outer.addClass("atobd mtl");
16 | if (ad) {
17 | outer.attr("data-adid", ad.dataValues.id);
18 | outer.append($("sponsored content "));
19 | } else {
20 | outer.append($("sponsored content "));
21 | }
22 |
23 |
24 | outer.append($("
").append(inner));
25 |
26 | var outerouter = $("
");
27 | outerouter.append(outer);
28 |
29 | return outerouter.html();
30 |
31 | }
32 |
33 | if (!results || !results.length || !_.random(7)) {
34 | flush(wrap_in_divs("post in /ads to put your own message here"));
35 | } else {
36 | // Pick a random ad...
37 | //
38 | ad = results[_.random(0, results.length - 1)];
39 |
40 | var postCmp = $C("post", ad.dataValues);
41 | var text_formatter = require_root("app/client/text");
42 | postCmp.add_markdown(text_formatter);
43 |
44 | var container = $("
");
45 | container.append(postCmp.$el.find(".title .text").html());
46 | container.append(postCmp.$el.find(".op.text").html());
47 |
48 | flush(wrap_in_divs(container));
49 |
50 | }
51 |
52 | });
53 |
54 |
55 | });
56 | }
57 | };
58 |
59 |
--------------------------------------------------------------------------------
/app/server/tripcode.js:
--------------------------------------------------------------------------------
1 | // WARNING: this file is duplicatesing code with app/client/tripcode. be careful
2 |
3 | // http://stackoverflow.com/a/10075654/442652
4 | function padDigits(number, digits) {
5 | return Array(Math.max(digits - String(number).length + 1, 0)).join(0) + number;
6 | }
7 |
8 | var md5 = require_app("server/md5");
9 | function get_colors_for_hash(hashed) {
10 | hashed = hashed || md5(hashed);
11 | var colors = hashed.match(/([\dABCDEF]{6})/ig);
12 |
13 | var hexes = [];
14 | for (var i = 0; i < 4; i++) {
15 | var color = parseInt(colors[i], 16);
16 | var red = (color >> 16) & 255;
17 | var green = (color >> 8) & 255;
18 | var blue = color & 255;
19 | var hex = _.map([red, green, blue], function(c, i) {
20 | return padDigits(((c >> 4) * 0x10).toString(16), 2);
21 | }).join('');
22 |
23 | hexes.push(hex);
24 | }
25 |
26 | return hexes;
27 | }
28 |
29 |
30 | var cached = {};
31 | function gen_tripcode(el) {
32 | // Now that we have our tripcodes, do other things...
33 | var tripcode = $(el).attr("data-tripcode");
34 | if (cached[tripcode]) {
35 | $(el).html(cached[tripcode]);
36 | } else {
37 | var colors = get_colors_for_hash(tripcode);
38 |
39 | var div = $(el);
40 | _.each(colors, function(color) {
41 | var colorDiv = $("
").css({
42 | "background-color": "#" + color,
43 | });
44 | div.append(colorDiv);
45 |
46 | $(el).css({
47 | position: "relative"
48 | });
49 | });
50 |
51 | cached[tripcode] = div.html();
52 | }
53 | }
54 |
55 |
56 | module.exports = {
57 | gen_tripcode: gen_tripcode
58 | };
59 |
--------------------------------------------------------------------------------
/app/server/worship_boards.js:
--------------------------------------------------------------------------------
1 | var board_names = require_app("server/board_names");
2 |
3 | module.exports = {
4 | contains: function(b) {
5 | return _.contains(module.exports.boards, b);
6 | },
7 | boards: [ board_names.CLERETICS, board_names.HERETICS, board_names.APOSTLES ]
8 | }
9 |
--------------------------------------------------------------------------------
/app/static/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/atobs/atob/HEAD/app/static/favicon.ico
--------------------------------------------------------------------------------
/app/static/fonts/glyphicons-halflings-regular.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/atobs/atob/HEAD/app/static/fonts/glyphicons-halflings-regular.woff
--------------------------------------------------------------------------------
/app/static/fonts/webhostinghub-glyphs.eot:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/atobs/atob/HEAD/app/static/fonts/webhostinghub-glyphs.eot
--------------------------------------------------------------------------------
/app/static/fonts/webhostinghub-glyphs.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/atobs/atob/HEAD/app/static/fonts/webhostinghub-glyphs.ttf
--------------------------------------------------------------------------------
/app/static/images/atob-logo-404.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/atobs/atob/HEAD/app/static/images/atob-logo-404.png
--------------------------------------------------------------------------------
/app/static/images/atobi.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/atobs/atob/HEAD/app/static/images/atobi.png
--------------------------------------------------------------------------------
/app/static/images/atobn.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/atobs/atob/HEAD/app/static/images/atobn.png
--------------------------------------------------------------------------------
/app/static/images/bootstrap/glyphicons-halflings-regular.eot:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/atobs/atob/HEAD/app/static/images/bootstrap/glyphicons-halflings-regular.eot
--------------------------------------------------------------------------------
/app/static/images/bootstrap/glyphicons-halflings-regular.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/atobs/atob/HEAD/app/static/images/bootstrap/glyphicons-halflings-regular.ttf
--------------------------------------------------------------------------------
/app/static/images/bootstrap/glyphicons-halflings-white.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/atobs/atob/HEAD/app/static/images/bootstrap/glyphicons-halflings-white.png
--------------------------------------------------------------------------------
/app/static/images/bootstrap/glyphicons-halflings.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/atobs/atob/HEAD/app/static/images/bootstrap/glyphicons-halflings.png
--------------------------------------------------------------------------------
/app/static/styles/RubberBand.css:
--------------------------------------------------------------------------------
1 |
2 | #RubberBandjs {
3 | background: #ccc;
4 | height: 90px;
5 | position: absolute;
6 | top: -80px;
7 | margin-top: -10px;
8 | z-index: 1040;
9 | width: 100%;
10 | text-align: center;
11 | }
12 | .rband {
13 | color: #444;;
14 | font-size: 14px;
15 | margin: 30px 0 0 0;
16 | text-align: center;
17 | text-shadow: 0px -1px 0px rgba(0,0,0,0.4);
18 | }
19 | .rband .text {
20 | display: inline-block;
21 | font-weight: 200;
22 | margin: 0;
23 | padding: 0;
24 | }
25 | .rband img {
26 | height: 16px;
27 | margin-right: 20px;
28 | position: relative;
29 | top: 3px;
30 | width: 16px;
31 | }
32 | .rband .load { display: none; }
33 |
--------------------------------------------------------------------------------
/app/static/styles/archive.css:
--------------------------------------------------------------------------------
1 | .identity_tripcode,
2 | .tripcode {
3 | filter: url("data:image/svg+xml;utf8, #grayscale"); /* Firefox 10+, Firefox on Android */
4 | filter: gray; /* IE6-9 */
5 | -webkit-filter: grayscale(90%); /* Chrome 19+, Safari 6+, Safari 6+ iOS */
6 | }
7 |
8 | .loading {
9 | opacity: 0.6;
10 | margin-top: 20px;
11 | overflow-x: hidden;
12 | max-width: 100%;
13 |
14 | position: relative;
15 | margin: 0 auto;
16 | margin-top: 50px;
17 | margin-bottom: 400px;
18 | text-align: center;
19 |
20 | #fountainG {
21 | margin: auto;
22 | }
23 |
24 | h4 {
25 | font-size: 32px;
26 | }
27 | }
28 |
29 |
30 |
--------------------------------------------------------------------------------
/app/static/styles/board.css:
--------------------------------------------------------------------------------
1 | .posts {
2 | margin-bottom: 100px;
3 | min-height: 100%;
4 | }
5 |
6 | .post_preview {
7 | opacity: 0.65;
8 |
9 | @media @device {
10 | display: none !important;
11 | }
12 | }
13 |
14 |
15 | .loading {
16 | opacity: 0.6;
17 | margin-top: 20px;
18 | overflow-x: hidden;
19 | max-width: 100%;
20 |
21 | position: relative;
22 | margin: 0 auto;
23 | margin-top: 50px;
24 | margin-bottom: 400px;
25 | text-align: center;
26 |
27 | #fountainG {
28 | margin: auto;
29 | }
30 |
31 | h4 {
32 | font-size: 32px;
33 | }
34 | }
35 |
36 | .board_slogan {
37 | @media @device {
38 | position: absolute;
39 | left: 15px;
40 | top: 65px;
41 | }
42 | }
43 |
44 | .boardview {
45 | position: relative;
46 | top: -40px;
47 | left: -45%;
48 | height: 0px;
49 |
50 | a {
51 | color: #000;
52 | opacity: 0.3;
53 | text-decoration: none;
54 |
55 | &.active {
56 | opacity: 1;
57 | }
58 | &:hover {
59 | opacity: 0.8;
60 | }
61 | }
62 |
63 |
64 | @media @device {
65 | position: inherit;
66 | margin-top: -5px;
67 | right: 0px;
68 | top: 0px;
69 | left: 0px;
70 | }
71 | }
72 |
73 | .searchform input {
74 | padding: 5px;
75 | }
76 |
--------------------------------------------------------------------------------
/app/static/styles/bootstrap/overrides.css:
--------------------------------------------------------------------------------
1 | body {
2 | .notifyjs-bootstrap-base {
3 | background-image: none;
4 | padding-left: 15px;
5 | }
6 | }
7 |
8 | body.modal-open {
9 | overflow-y: auto;
10 | overflow-x: auto;
11 | }
12 |
13 |
14 | .desaturate {
15 | transition: 1s;
16 | -webkit-filter: grayscale(100%);
17 | -moz-filter: grayscale(100%);
18 | filter: grayscale(100%);
19 | filter: url("data:image/svg+xml;utf8, #grayscale"); /* Firefox 3.5+ */
20 |
21 | &:hover {
22 | -webkit-filter: none;
23 | -moz-filter: none;
24 | filter: grayscale(0);
25 | filter: none;
26 | transition: 1s;
27 | }
28 | }
29 |
30 | .icon-atob {
31 | background-image: url("/images/atobn.png");
32 | background-size: cover;
33 | width: 1.25em;
34 | height: 1.25em;
35 | overflow: visible;
36 | display: inline-block;
37 | position: relative;
38 | top: 2px;
39 | };
40 |
41 |
42 | .identity_tripcode, .burtle_tripcode {
43 | div.tripcolor {
44 | -webkit-transition: background-color 400ms linear;
45 | -moz-transition: background-color 400ms linear;
46 | -o-transition: background-color 400ms linear;
47 | -ms-transition: background-color 400ms linear;
48 | transition: background-color 400ms linear;
49 | }
50 | }
51 |
52 |
53 | .popover {
54 | min-width: 250px;
55 | max-width: inherit;
56 | z-index: 1070;
57 | }
58 |
59 | .popover.reply {
60 | max-width: 550px;
61 | }
62 |
63 | .content .container {
64 | padding: 0px;
65 | }
66 |
--------------------------------------------------------------------------------
/app/static/styles/bootstrap/responsive.css:
--------------------------------------------------------------------------------
1 | /*!
2 | * Bootstrap v2.3.1
3 | *
4 | * Copyright 2012 Twitter, Inc
5 | * Licensed under the Apache License v2.0
6 | * http://www.apache.org/licenses/LICENSE-2.0
7 | *
8 | * Designed and built with all the love in the world @twitter by @mdo and @fat.
9 | */
10 | .clearfix {
11 | *zoom: 1;
12 | }
13 | .clearfix:before,
14 | .clearfix:after {
15 | display: table;
16 | content: "";
17 | line-height: 0;
18 | }
19 | .clearfix:after {
20 | clear: both;
21 | }
22 | .hide-text {
23 | font: 0/0 a;
24 | color: transparent;
25 | text-shadow: none;
26 | background-color: transparent;
27 | border: 0;
28 | }
29 | .input-block-level {
30 | display: block;
31 | width: 100%;
32 | min-height: 30px;
33 | -webkit-box-sizing: border-box;
34 | -moz-box-sizing: border-box;
35 | box-sizing: border-box;
36 | }
37 | @-ms-viewport {
38 | width: device-width;
39 | }
40 | .hidden {
41 | display: none;
42 | visibility: hidden;
43 | }
44 | .visible-phone {
45 | display: none !important;
46 | }
47 | .visible-tablet {
48 | display: none !important;
49 | }
50 | .hidden-desktop {
51 | display: none !important;
52 | }
53 | .visible-desktop {
54 | display: inherit !important;
55 | }
56 | @media (min-width: 768px) and (max-width: 979px) {
57 | .hidden-desktop {
58 | display: inherit !important;
59 | }
60 | .visible-desktop {
61 | display: none !important ;
62 | }
63 | .visible-tablet {
64 | display: inherit !important;
65 | }
66 | .hidden-tablet {
67 | display: none !important;
68 | }
69 | }
70 | @media (max-width: 767px) {
71 | .hidden-desktop {
72 | display: inherit !important;
73 | }
74 | .visible-desktop {
75 | display: none !important;
76 | }
77 | .visible-phone {
78 | display: inherit !important;
79 | }
80 | .hidden-phone {
81 | display: none !important;
82 | }
83 | }
84 | .visible-print {
85 | display: none !important;
86 | }
87 | @media print {
88 | .visible-print {
89 | display: inherit !important;
90 | }
91 | .hidden-print {
92 | display: none !important;
93 | }
94 | }
95 |
--------------------------------------------------------------------------------
/app/static/styles/box.css:
--------------------------------------------------------------------------------
1 | .mts {
2 | margin-top: 3px;
3 | }
4 |
5 | .mtm {
6 | margin-top: 5px;
7 | }
8 |
9 | .mlm {
10 | margin-left: 5px;
11 | }
12 |
13 | .mam {
14 | margin: 5px;
15 | }
16 |
17 | .mbm {
18 | margin-bottom: 5px;
19 | }
20 |
21 | .mth {
22 | margin-top: 15px;
23 | }
24 |
25 | .mtl {
26 | margin-top: 10px;
27 | }
28 |
29 | .mll {
30 | margin-left: 10px
31 | }
32 |
33 | .mrl {
34 | margin-right: 10px;
35 | }
36 |
37 | .mal {
38 | margin: 10px;
39 | }
40 |
41 | .mbl {
42 | margin-bottom: 10px;
43 | }
44 |
45 | .lfloat {
46 | float: left;
47 | }
48 |
49 | .rfloat {
50 | float: right;
51 | }
52 |
53 | .pal {
54 | padding: 10px;
55 | }
56 |
57 | .pll {
58 | padding-left: 10px;
59 | }
60 |
61 | .prl {
62 | padding-right: 10px;
63 | }
64 |
65 | .ptl {
66 | padding-top: 10px;
67 | }
68 |
69 | .pam {
70 | padding: 5px;
71 | }
72 |
73 | .pas {
74 | padding: 2px;
75 | }
76 |
77 | .fwb {
78 | font-weight: bold;
79 | }
80 |
81 | .fsi {
82 | font-style: italic;
83 | }
84 |
85 | .eom {
86 | @media (max-width: 767px) {
87 | width: 95% !important
88 | }
89 | }
90 | .com {
91 | @media (max-width: 767px) {
92 | display: none !important
93 | }
94 | }
95 |
96 | body {
97 | background-color: #fafafa;
98 | }
99 |
100 | html, body {
101 | min-height: 600px;
102 | }
103 |
104 | a {
105 | color: #428bca;
106 | }
107 |
108 | .container {
109 | padding: 0px;
110 | }
111 |
112 | .noselect {
113 | -webkit-touch-callout: none;
114 | -webkit-user-select: none;
115 | -khtml-user-select: none;
116 | -moz-user-select: none;
117 | -ms-user-select: none;
118 | user-select: none;
119 | }
120 |
121 | @-webkit-keyframes pulse_animation_big {
122 | 0% { -webkit-transform: scale(1); }
123 | 5% { -webkit-transform: scale(2); }
124 | 10% { -webkit-transform: scale(1); }
125 | 15% { -webkit-transform: scale(2); }
126 | 20% { -webkit-transform: scale(1); }
127 | 100% { -webkit-transform: scale(1); }
128 | }
129 |
130 | .pulse {
131 | -webkit-animation-name: 'pulse_animation_big';
132 | -webkit-animation-duration: 6000ms;
133 | -webkit-transform-origin:50% 50%;
134 | -webkit-animation-iteration-count: infinite;
135 | -webkit-animation-timing-function: linear;
136 |
137 | }
138 |
139 | #fontBombConfirmation {
140 | display: none !important;
141 | }
142 |
--------------------------------------------------------------------------------
/app/static/styles/chat.css:
--------------------------------------------------------------------------------
1 | .chat.visible {
2 | .chat_header {
3 | opacity: 1;
4 | }
5 | }
6 |
7 | .chat {
8 | position: fixed;
9 | bottom: 0px;
10 | z-index: 1099;
11 |
12 | right: 3em;
13 |
14 | @media @device {
15 | left: 0;
16 | right: 0;
17 | }
18 |
19 | .chat_header {
20 | z-index: 1040;
21 | padding: 5px;
22 | background-color: #adadad;
23 | display: block;
24 | margin-right: 25px;
25 |
26 | max-width: 200px;
27 | min-width: 100px;
28 |
29 |
30 | a {
31 | color: white;
32 | }
33 |
34 | position: absolute;
35 | right: 25px;
36 | top: -2em;
37 |
38 | opacity: 0.5;
39 | &:hover {
40 | opacity: 1;
41 | }
42 |
43 | color: white;
44 | cursor: pointer;
45 | text-align: center;
46 |
47 | }
48 |
49 | .chat_content {
50 | background-color: white;
51 |
52 | border: 4px solid #ddd;
53 | border-bottom: none;
54 | padding: 10px;
55 |
56 | max-width: 500px;
57 |
58 | @media @device {
59 | max-width: none;
60 | }
61 | }
62 |
63 | .cmp-post {
64 | min-height: 400px;
65 | max-height: 400px !important;
66 | .post {
67 | .title {
68 | display: none;
69 | }
70 | .op.text, .upboat {
71 | display: none;
72 | }
73 |
74 | .deletereply, .restore, .see_replies, .ups, .downs {
75 | display: none !important;
76 | }
77 |
78 | .addreply {
79 | opacity: 0.1;
80 |
81 | &:hover {
82 | opacity: 0.7;
83 | }
84 | }
85 |
86 | .replies {
87 | max-height: 300px !important;
88 | }
89 |
90 | .infobar {
91 | display: none;
92 | }
93 |
94 | .replypreview {
95 | display: none !important;
96 | }
97 | }
98 |
99 | }
100 | }
101 |
102 | /* homepage chat overrides */
103 | .home_chat_container .chat {
104 | position: inherit;
105 | bottom: auto;
106 | left: auto;
107 | right: auto;
108 | top: auto;
109 |
110 | .chat_header {
111 | display: none;
112 | }
113 | .chat_content {
114 | .cmp-post {
115 | min-height: 200px !important;
116 | max-height: 200px !important;
117 | }
118 |
119 | max-width: none;
120 | padding: 0px;
121 | display: block !important;
122 | background-color: inherit;
123 | border: none;
124 |
125 | .replies {
126 | max-height: 150px !important;
127 | }
128 | .post {
129 | background-color: inherit;
130 | }
131 |
132 | padding-bottom: 30px;
133 | }
134 |
135 | }
136 |
--------------------------------------------------------------------------------
/app/static/styles/definitions.less:
--------------------------------------------------------------------------------
1 | @large_screen: ~"only screen and (min-width: 1900px)";
2 | @container: ~"only screen and (min-width: 1200px)";
3 |
--------------------------------------------------------------------------------
/app/static/styles/home.css:
--------------------------------------------------------------------------------
1 | .hidey {
2 | opacity: 0.01;
3 | transition: opacity 2s;
4 |
5 | &:hover {
6 | opacity: 0.9;
7 | transition: opacity 5s;
8 | }
9 | }
10 |
11 | .ruleslink,
12 | .replylink,
13 | .imglink {
14 | color: #ff43ca;
15 | cursor: pointer;
16 |
17 | &:hover {
18 | color: #000;
19 | }
20 | }
21 |
22 | .linklink {
23 | text-decoration: underline;
24 | }
25 |
26 | #anons {
27 | position: absolute;
28 | z-index: 1090;
29 | margin-right: 10px;
30 | right: 5px;
31 | top: 85px;
32 | cursor: pointer;
33 |
34 | @media @device {
35 | float: right;
36 | position: absolute;
37 | left: 10px;
38 | margin-right: 10px;
39 |
40 | zoom: 1.4;
41 | top: 82px;
42 | }
43 |
44 | }
45 |
46 | body {
47 | @media @device {
48 | padding-bottom: 200px;
49 | }
50 | }
51 |
52 | .boardlink {
53 | font-size: 125%;
54 | min-width: 50px;
55 | a {
56 | display: block;
57 | }
58 | }
59 |
60 | .hideContent {
61 | height: 6em;
62 | display: inline-block;
63 | overflow: hidden;
64 | }
65 |
66 | .post_id,
67 | .link_text {
68 | font-size: 85%;
69 | margin-top: 5px;
70 | }
71 |
72 | h1, h2, h3 {
73 | margin-top: 10px;
74 | margin-bottom: 10px;
75 | }
76 |
77 | .tripcode {
78 | cursor: pointer;
79 | float: none;
80 | }
81 |
82 | .anons .tripcode {
83 | float: left;
84 | }
85 |
86 | .recent_container {
87 | padding-top: 10px;
88 | }
89 |
90 | #fullpage .section.active {
91 | overflow: visible;
92 | }
93 |
94 | .stayput.header {
95 | position: fixed;
96 | top: 0px;
97 | margin: auto;
98 | width: 1170px;
99 | background-color: #fafafa;
100 | z-index: 1030;
101 | padding-top: 20px;
102 |
103 | }
104 |
105 | .logo .burtle_tripcode {
106 | max-height: 5px;
107 | height: 5px;
108 | width: 68px;
109 | position: absolute !important;
110 | bottom: 10px;
111 | left: 2px !important;
112 | overflow: hidden;
113 | zoom: 1 !important;
114 |
115 | div {
116 | height: 10px;
117 | width: 25%;
118 | float: left;
119 | }
120 |
121 | }
122 |
123 | .logo.burtled {
124 | .burtle_tripcode {
125 | left: 0px;
126 | }
127 | }
128 |
129 | .truncable {
130 | word-break: break-all;
131 | }
132 |
133 | .identity_tripcode {
134 | cursor: pointer;
135 | }
136 |
137 |
138 | .text, .replypreview {
139 | .spoiler {
140 | opacity: 0.5;
141 | background-color: #aaaaaa;
142 | color: #aaaaaa;
143 | cursor: pointer;
144 |
145 | -webkit-transition: color 0.5s ease-in-out;
146 | -moz-transition: color 0.5s ease-in-out;
147 | transition: color 0.5s ease-in-out;
148 |
149 | -webkit-transition: background-color 0.5s ease-in-out;
150 | -moz-transition: background-color 0.5s ease-in-out;
151 | transition: background-color 0.5s ease-in-out;
152 |
153 | -webkit-transition: opacity 0.5s ease-in-out;
154 | -moz-transition: opacity 0.5s ease-in-out;
155 | transition: opacity 0.5s ease-in-out;
156 |
157 |
158 | }
159 |
160 | &:hover {
161 | .spoiler {
162 | opacity: 1;
163 | color: #666666;
164 | background-color: inherit;
165 |
166 | }
167 | }
168 | }
169 |
170 |
171 |
--------------------------------------------------------------------------------
/app/static/styles/jquery.sidr.light.css:
--------------------------------------------------------------------------------
1 | /* line 3, ../../src/scss/sidr/_base.scss */
2 | .sidr {
3 | /* Default Settings */
4 | display: none;
5 | position: absolute;
6 | position: fixed;
7 | top: 0;
8 | height: 100%;
9 | z-index: 1030;
10 | width: 260px;
11 | overflow-x: none;
12 | overflow-y: auto;
13 | background: #fafafa;
14 | }
15 |
16 | .sidr {
17 | .boardlink {
18 |
19 | display: block;
20 | width: 100%;
21 | clear: both;
22 | padding-top: 5px;
23 | padding-bottom: 5px;
24 | border-bottom: 2px dotted #e8e8e8;
25 |
26 | a {
27 | display: block;
28 | text-align: left;
29 | padding-left: 15px;
30 |
31 | &:hover {
32 | text-decoration: none;
33 |
34 | }
35 | }
36 |
37 |
38 | &:last-child {
39 | border-bottom: 0;
40 | }
41 | }
42 | }
43 |
44 | /* line 15, ../../src/scss/sidr/_base.scss */
45 | .sidr .sidr-inner {
46 | padding: 0 0 15px;
47 | }
48 | /* line 18, ../../src/scss/sidr/_base.scss */
49 | .sidr .sidr-inner > p {
50 | margin-left: 15px;
51 | margin-right: 15px;
52 | }
53 | /* line 24, ../../src/scss/sidr/_base.scss */
54 | .sidr.right {
55 | left: auto;
56 | right: -260px;
57 | }
58 | /* line 29, ../../src/scss/sidr/_base.scss */
59 | .sidr.left {
60 | left: -260px;
61 | right: auto;
62 | }
63 |
64 | .sidr {
65 | padding: 10px;
66 |
67 | h1, h2, h3, h4, h5, h6 {
68 | margin: 0px;
69 | }
70 |
71 | .btn {
72 | width: 100%;
73 | margin-left: 0px;
74 | margin-right: 0px;
75 | }
76 | }
77 |
--------------------------------------------------------------------------------
/app/static/styles/links.css:
--------------------------------------------------------------------------------
1 | .link:hover {
2 | background-color: #dedede;
3 | }
4 |
5 | .link_text {
6 | a {
7 | word-break: break-all;
8 | word-wrap: break-word;
9 | white-space: pre-wrap;
10 |
11 | &.image {
12 | color: #ff43ca;
13 | }
14 | }
15 |
16 | margin-right: 200px;
17 | padding-left: 30px;
18 |
19 | @media @device {
20 | margin-right: 90px;
21 | }
22 |
23 | &.condensed {
24 | margin-right: 0px;
25 | padding-left: 0px;
26 | }
27 | }
28 |
29 | .condensed .post_id {
30 | margin-top: 0px;
31 | float: left;
32 | }
33 |
34 | .post_id {
35 | min-width: 50px;
36 | float: right;
37 | margin-top: 10px;
38 |
39 | @media @device {
40 | min-width: 40px;
41 | clear: both;
42 | }
43 | }
44 |
45 | .tripcode {
46 | width: 100px;
47 | float: left;
48 | margin-right: 10px;
49 |
50 | @media @device {
51 | margin-top: 10px;
52 | zoom: 0.7;
53 | margin-right: 5px;
54 | margin-bottom: 3px !important;
55 | }
56 |
57 | }
58 |
59 | .condensed {
60 | .tripcode {
61 | display: none;
62 | }
63 | }
64 |
65 | .container {
66 | margin: auto;
67 | }
68 |
69 | .indicator {
70 | position: absolute;
71 | margin-left: -30px;
72 | margin-right: 2px;
73 | }
74 |
75 |
76 | .numboats {
77 | position: relative;
78 | left: -15px;
79 | top: 15px;
80 | font-size: 90%;
81 | }
82 |
83 |
84 | .upboat {
85 | opacity: 0.5;
86 | &:hover {
87 | opacity: 0.8;
88 | }
89 |
90 | cursor: pointer;
91 | }
92 |
93 | .link {
94 | margin-bottom: 10px;
95 | padding-top: 10px;
96 | padding-bottom: 10px;
97 | border-bottom: 1px solid #ddd;
98 | }
99 | .board_slogan {
100 | @media @device {
101 | position: absolute;
102 | left: 15px;
103 | top: 75px;
104 | }
105 | }
106 |
--------------------------------------------------------------------------------
/app/static/styles/logo_and_links.css:
--------------------------------------------------------------------------------
1 | .identity_tripcode {
2 | cursor: pointer;
3 | }
4 |
5 | .tripcode_holder a {
6 | color: #aaa;
7 | }
8 |
9 | .logo .identity_tripcode {
10 | max-height: 5px;
11 | height: 4px;
12 | width: 40px;
13 | position: relative;
14 | top: -8px;
15 | left: 0px;
16 | overflow: hidden;
17 | }
18 | .navbar {
19 | padding-top: 10px;
20 | background-color: #fafafa;
21 | border: 0px;
22 | }
23 |
24 | #anons {
25 | position: absolute;
26 | top: 25px;
27 | right: 0px;
28 | opacity: 0.4;
29 | zoom: 0.8;
30 | cursor: pointer;
31 |
32 | @media only screen and (min-width: 767px) {
33 | right: 36px;
34 | }
35 |
36 | @media @device {
37 | zoom: 1.3;
38 | top: 0px;
39 | left: 40px;
40 | direction: rtl;
41 | margin-right: 20px;
42 | overflow: hidden;
43 | max-height: 20px;
44 | right: inherit;
45 |
46 | }
47 | }
48 |
49 | .navbar_helper {
50 | @media @device {
51 | .settingslink, .toptop {
52 | zoom: 1.5;
53 | }
54 | }
55 | }
56 |
57 | body:not(:-moz-handler-blocked) #anons {
58 | top: 20px;
59 | right: 26px;
60 | }
61 |
62 | #anons:hover {
63 | opacity: 0.8;
64 | }
65 | #page_content {
66 | margin-top: 70px;
67 | }
68 |
69 | #logobar.navbar {
70 | position: fixed;
71 | top: 0;
72 | z-index: 1000;
73 |
74 | @media @device {
75 | right: 15px;
76 | left: 15px;
77 | }
78 | }
79 |
80 | .flying_burtle {
81 | z-index: 3000 !important;
82 | }
83 |
84 | #logobar .navbar_helper {
85 | margin-left: 0px;
86 | margin-right: 0px;
87 | padding-left: 0;
88 |
89 | padding-right: 0px;
90 |
91 | @media only screen and (min-width: 767px) {
92 | padding-right: 30px;
93 | }
94 |
95 | .logo {
96 | position: relative;
97 |
98 | img {
99 | z-index: 10000;
100 | }
101 | }
102 | }
103 |
104 |
105 | .boardlink a {
106 | text-align: center;
107 | cursor: pointer;
108 | display: block;
109 | min-width: 50px;
110 | padding-top: 5px;
111 | padding-bottom: 5px;
112 |
113 | &:hover {
114 | background-color: #ddd;
115 | }
116 | }
117 |
118 |
119 | .hue {
120 | -webkit-animation: hue 10s infinite;
121 | color: #ff0;
122 | }
123 |
124 | @-webkit-keyframes hue {
125 | 0%, 100% { -webkit-filter: hue-rotate(0deg); }
126 | 50% { -webkit-filter: hue-rotate(360deg); }
127 | }
128 |
129 | .anonicator {
130 | padding: 5px;
131 | }
132 |
133 | body .sidr {
134 | padding: 0px;
135 | padding-top: 17px;
136 | margin-top: 78px;
137 | width: 100%;
138 |
139 | }
140 |
141 | body .sidr.right {
142 | padding: 15px;
143 | }
144 |
--------------------------------------------------------------------------------
/app/static/styles/post.css:
--------------------------------------------------------------------------------
1 | body {
2 | border: 0px;
3 | padding-bottom: 60px;
4 | }
5 |
6 | .loading {
7 | opacity: 0.6;
8 | margin-top: 20px;
9 | overflow-x: hidden;
10 | max-width: 100%;
11 |
12 | position: relative;
13 | margin: 0 auto;
14 | margin-top: 50px;
15 | margin-bottom: 400px;
16 | text-align: center;
17 |
18 | #fountainG {
19 | margin: auto;
20 | }
21 |
22 | h4 {
23 | font-size: 32px;
24 | }
25 | }
26 |
27 | .cmp-post {
28 | min-height: 500px;
29 | }
30 |
--------------------------------------------------------------------------------
/app/static/styles/profile.css:
--------------------------------------------------------------------------------
1 | .profile_container {
2 |
3 | .tripcontainer {
4 | padding: 20px;
5 | }
6 |
7 | .tripcode {
8 | width: 100px;
9 | float: left;
10 | padding-right: 15px;
11 | padding-left: 15px;
12 |
13 | @media @device {
14 | margin-top: 10px;
15 | margin-right: 5px;
16 | }
17 |
18 |
19 | }
20 |
21 | .tripcolor {
22 | width: 25%;
23 | height: 100%;
24 | display: inline-block;
25 | }
26 |
27 | .profile_photo {
28 | border-radius: 100px;
29 | background-color: #fff;
30 | border: 4px solid #ddd;
31 | display: block;
32 | height: 200px;
33 | width: 200px;
34 | position: absolute;
35 | bottom: -50px;
36 | left: 25px;
37 | overflow: hidden;
38 | z-index: 2;
39 |
40 | @media @mobile {
41 | height: 100px;
42 | width: 100px;
43 | border-radius: 50px;
44 | bottom: -25px;
45 |
46 | }
47 | }
48 |
49 | .profile_photo.noob {
50 | border: 40px solid #ddd;
51 |
52 | @media @mobile {
53 | border: 20px solid #ddd;
54 | }
55 | }
56 |
57 | .profile_photo_right {
58 | right: 25px;
59 | left: inherit;
60 | }
61 |
62 | .anoninfo {
63 | font-size: 200%;
64 | vertical-align: bottom;
65 | height: 30px;
66 | }
67 |
68 | .profile_about {
69 | vertical-align: bottom;
70 | height: 30px;
71 | margin-top: 1em;
72 | margin-left: 1em;
73 | }
74 |
75 | .cover_photo img {
76 | width: 100%;
77 | }
78 |
79 | .cover_photo .profile_code {
80 | clear: both;
81 | }
82 |
83 | .cover_photo .profile_code div {
84 | height: 300px !important;
85 |
86 | @media @mobile {
87 | height: 200px !important;
88 |
89 | }
90 | }
91 |
92 | .cover_photo_bottom .profile_code div {
93 | height: 150px !important;
94 | @media @mobile {
95 | height: 100px !important;
96 |
97 | }
98 | }
99 |
100 | .cover {
101 | position: relative;
102 | margin-bottom: 50px;
103 | }
104 |
105 | .profile_photo .profile_code div {
106 | width: 50% !important;
107 | height: 50% !important;
108 | float: left !important;
109 | }
110 |
111 | .profile_photo_block {
112 | width: 50%;
113 | height: 50%;
114 | float: left;
115 | }
116 |
117 | .cover_bg {
118 | background-color: #000;
119 | width: 100%;
120 | display: block;
121 | height: 300px;
122 | }
123 |
124 |
125 | // spinning from http://stackoverflow.com/questions/14859322/css3-spin-animation
126 | // and http://jsfiddle.net/gionaf/Ugc5g/
127 | .spin {
128 | -webkit-animation:spin 3s linear infinite;
129 | -moz-animation:spin 3s linear infinite;
130 | animation:spin 3s linear infinite;
131 | }
132 |
133 |
134 | @-moz-keyframes spin {
135 | 80% { -moz-transform: rotate(0deg); }
136 | 100% { -moz-transform: rotate(360deg); }
137 | }
138 | @-webkit-keyframes spin {
139 | 80% { -webkit-transform: rotate(0deg); }
140 | 100% { -webkit-transform: rotate(360deg); }
141 | }
142 | @keyframes spin {
143 | 80% { -webkit-transform: rotate(0deg); transform: rotate(0deg); }
144 | 100% { -webkit-transform: rotate(360deg); transform:rotate(360deg); }
145 | }
146 | }
147 |
--------------------------------------------------------------------------------
/app/static/styles/scrollers.css:
--------------------------------------------------------------------------------
1 | ::-webkit-scrollbar {
2 | width: 12px;
3 | }
4 |
5 | ::-webkit-scrollbar-thumb {
6 | background-color: #ddd;
7 | }
8 |
9 |
--------------------------------------------------------------------------------
/app/static/styles/search.css:
--------------------------------------------------------------------------------
1 | .searchinput {
2 | width: 100%;
3 | padding: 10px;
4 | }
5 |
6 | .results {
7 | overflow-y: auto;
8 | min-height: 600px;
9 | margin-top: 20px;
10 | }
11 |
--------------------------------------------------------------------------------
/app/static/styles/searchbar.css:
--------------------------------------------------------------------------------
1 | #searchform {
2 |
3 | @media @mobile {
4 | width: 100%;
5 | margin-top: 10px;
6 | }
7 |
8 | .searchform {
9 | float: right;
10 | margin-right: 10px;
11 |
12 | .searchinput {
13 | padding-left: 5px;
14 | }
15 |
16 | @media @mobile {
17 | float: none;
18 | clear: both;
19 | margin-right: 0px;
20 | .searchinput {
21 | width: 100%;
22 | }
23 |
24 | input[type=submit] {
25 | float: right;
26 | position: relative;
27 | top: -27px;
28 | }
29 | }
30 |
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/app/static/styles/settings.css:
--------------------------------------------------------------------------------
1 | .beeper {
2 | cursor: pointer;
3 |
4 | &:hover {
5 | text-decoration: underline;
6 | }
7 | }
8 |
9 | .sidr {
10 | .beeper {
11 | margin-bottom: 5px;
12 |
13 | display: block;
14 | clear: both;
15 | width: 100%;
16 |
17 | }
18 | }
19 |
20 | .tripcode_history, .regen_tripcode {
21 | @media @mobile {
22 | width: 100%;
23 | clear: both;
24 | margin: 0px;
25 | margin-top: 5px;
26 | margin-bottom: 5px;
27 | }
28 | }
29 |
30 | .tripcode_done {
31 | @media @mobile {
32 | display: none;
33 | }
34 | }
35 |
36 | #identity_container {
37 | .checkbox {
38 | margin-top: 5px;
39 | clear: both;
40 | }
41 | }
42 |
43 | .tripcode_wrapper {
44 |
45 | .tripcode_control {
46 | opacity: 0;
47 | border-radius: 100%;
48 | color: #428bca;
49 | padding: 4px;
50 |
51 | @media @device {
52 | border-radius: 0%;
53 | margin-top: 6px;
54 | opacity: 1;
55 | zoom: 1;
56 | background-color: rgba(255, 255, 255, 0.8);
57 | padding: 5px;
58 | }
59 | }
60 |
61 | &:hover {
62 | .tripcode_control {
63 | opacity: 1;
64 |
65 | color: #428bca;
66 | background-color: #fafafa;
67 | opacity: 0.9;
68 | zoom: 1.5;
69 | margin-top: 0px;
70 |
71 | @media @device {
72 | margin-top: 6px;
73 | zoom: 1;
74 | }
75 |
76 | }
77 | }
78 | }
79 |
80 |
81 | .benjamin_button {
82 | @media @device {
83 | padding-bottom: 100px;
84 | }
85 | }
86 |
87 | .btn.newthread {
88 | width: 100%;
89 | border: 3px dashed #ddd;
90 | margin-top: 10px;
91 | margin-bottom: 0px;
92 | color: #428bca;
93 | font-size: 0.8em;
94 |
95 | &:hover {
96 | background-color: #eaeaea;
97 | }
98 | }
99 |
100 | .favorite_board {
101 | height: 100px;
102 | line-height: 80px;
103 | cursor: pointer;
104 | vertical-align: center;
105 | text-align: center;
106 | padding: 10px 0px;
107 | font-size: 1.5em;
108 | overflow-x: hidden;
109 | overflow-y: hidden;
110 | position: relative;
111 |
112 | .del, .add {
113 | color: #fafafa;
114 | }
115 |
116 | @media @desktop {
117 | .del, .add {
118 | opacity: 0;
119 | }
120 |
121 | &:hover {
122 | background-color: #fafaee;
123 | .del, .add {
124 | opacity: 0.5;
125 |
126 | &:hover {
127 | opacity: 0.8;
128 | }
129 | }
130 | }
131 | }
132 |
133 | :last-child {
134 | margin-right: 0px;
135 | }
136 |
137 | }
138 |
139 | .brighten {
140 | filter: brightness(1.5) grayscale(80%);
141 | -webkit-filter: brightness(1.2) grayscale(80%);
142 | }
143 |
144 | .drag-handle {
145 | cursor: move;
146 | cursor: -webkit-grabbing;
147 | }
148 |
149 | .toptop {
150 | cursor: pointer;
151 | }
152 |
153 | #settings_dropdown .panel {
154 | border: 0px;
155 | box-shadow: 0px;
156 | -webkit-box-shadow: 0px;
157 | margin-top: 0px;
158 | max-height: 500px;
159 | overflow-y: auto;
160 | margin: 0px;
161 | }
162 |
--------------------------------------------------------------------------------
/app/static/styles/sponsored_content.css:
--------------------------------------------------------------------------------
1 |
2 | .atobd {
3 | padding-top: 5px;
4 | padding-left: 0px;
5 | float: left;
6 |
7 | font-size: 14px !important;
8 | width: 80%;
9 | max-height: 6em;
10 | overflow: hidden;
11 | max-width: 600px;
12 |
13 | @media @mobile {
14 | width: 100%;
15 | background-color: #fafaee;
16 | padding: 5px;
17 | margin-top: 20px;
18 | margin-bottom: 10px;
19 | max-height: 120px;
20 | }
21 |
22 | .upboat {
23 | display: none;
24 | }
25 |
26 | .imglink {
27 | color: #f4ca3a;
28 | }
29 |
30 | &:hover {
31 | opacity: 1;
32 | }
33 | }
34 |
35 | .atobsps a {
36 | position: relative;
37 | color: #999;
38 | }
39 |
--------------------------------------------------------------------------------
/app/static/templates/controllers/about.html.erb:
--------------------------------------------------------------------------------
1 | <% set_default("use_header", false) %>
2 | <% set_default("slogan", "") %>
3 |
4 |
5 | <% if (use_header) { %>
6 | <%= render_partial("home/header.html.erb", { slogan: slogan, show_search: false}) %>
7 | <% } %>
8 |
9 |
10 |
11 |
overview
12 |
13 | Have you found yourself wondering about the wonders of JAMES? Do you
14 | need more hulk in your life? a to b is here for yuo. some anons
15 | might describe atob as a bastard child of reddit, 4chan and hubski.
16 | they don't know how genetics work, but we still love them.
17 |
18 |
19 |
20 | who or what is anon, you ask? meet
21 | anon . anon is the voice of atob. when one shouts,
22 | they are shouting to anon. when one whispers, they are probably being
23 | ignored by anon.
24 |
25 |
26 |
when anon is done shouting, the loudest of the topics resound in the
27 | hall of archives .
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
testimonials
36 |
37 | "anon listens, when no one else does"
38 | -anon
39 |
40 |
41 |
42 | "its like shouting into the void"
43 | -anon
44 |
45 |
46 |
47 | "atob restored my eyesight"
48 | -anon (probably lying)
49 |
50 |
51 |
52 |
activity stats
53 | do you like pretty graphs? are you curious about how active the atobeans are? you're in luck! atob supports
54 |
burtle-veillance
55 |
source code
56 | while atob may not be free speech, it is
57 |
free software
58 |
59 |
60 |
61 |
62 |
--------------------------------------------------------------------------------
/app/static/templates/controllers/archives.html.erb:
--------------------------------------------------------------------------------
1 | <% set_default("tripcode", "") %>
2 |
3 |
4 |
5 | <%= render_partial("shared/logo_and_links.html.erb", { tripcode: tripcode }) %>
6 |
7 |
8 |
9 |
10 |
the archives
11 | where the noisiest posts come to settle down
12 |
13 |
14 | <%= render_archives() %>
15 |
16 |
17 |
18 |
19 |
--------------------------------------------------------------------------------
/app/static/templates/controllers/boards/list.html.erb:
--------------------------------------------------------------------------------
1 | <% add_stylesheet("home.css") %>
2 | <% set_default("tripcode", ""); %>
3 |
4 |
5 |
6 | <%= render_partial("home/header.html.erb", { show_hr: true}) %>
7 |
8 |
9 |
10 | <%= render_boards() %>
11 |
12 |
13 |
14 |
15 |
--------------------------------------------------------------------------------
/app/static/templates/controllers/boards/show.html.erb:
--------------------------------------------------------------------------------
1 | <% set_default("title", "a to b to ") %>
2 | <% set_default("posts", [] ) %>
3 | <% set_default("tripcode", "") %>
4 | <% set_default("board_slogan", "") %>
5 |
6 | <% add_stylesheet("board.css") %>
7 |
8 |
9 |
10 | <%= render_partial("shared/logo_and_links.html.erb", { tripcode: tripcode }) %>
11 |
12 |
13 |
14 |
15 |
16 | <% if (board_slogan) { %>
17 |
18 | <% } %>
19 | new thread
20 |
21 |
22 |
23 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 | <%= render_partial("shared/new_post.html.erb") %>
38 |
39 |
40 |
41 |
42 | <%= render_posts() %>
43 |
44 |
Loading
45 |
<%= render_partial("shared/loading.html.erb") %>
46 |
47 |
48 | <%= render_partial("shared/chat.html.erb", { render_recent_chats: render_recent_chats }) %>
49 |
50 | <%= render_sponsored_content() %>
51 |
52 |
53 |
54 |
55 |
56 |
57 |
--------------------------------------------------------------------------------
/app/static/templates/controllers/chat.html.erb:
--------------------------------------------------------------------------------
1 | <% set_default("tripcode", "a") %>
2 |
3 | <%= add_stylesheet("chat") %>
4 |
5 |
6 |
7 |
8 | <%= render_partial("shared/logo_and_links.html.erb", { tripcode: tripcode, desaturate: true }) %>
9 |
10 |
11 | <%= render_recent_posts() %>
12 |
13 |
14 |
15 |
--------------------------------------------------------------------------------
/app/static/templates/controllers/data/data.html.erb:
--------------------------------------------------------------------------------
1 | <% set_default("recent", false) %>
2 |
3 |
4 |
13 |
14 |
15 |
16 |
Loading
17 |
18 | <%= render_partial("shared/loading.html.erb") %>
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
--------------------------------------------------------------------------------
/app/static/templates/controllers/home.html.erb:
--------------------------------------------------------------------------------
1 | <% set_default("title", "a new borad approaches") %>
2 | <% set_default("boards", []) %>
3 | <% set_default("slogan", "something something something") %>
4 | <% set_default("render_recent_chats", function() { }) %>
5 |
6 | <% add_stylesheet("home.css") %>
7 |
8 |
9 | <%= render_partial("home/header.html.erb", { slogan: slogan }) %>
10 |
11 | <%= render_anons() %>
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
a practical guide
20 |
21 |
22 |
23 | a to b is a real time textboard . a textboard is like a
24 | forum, but there's no registration and post authors are anonymous.
25 | sound interesting? join a board and speak whats on your mind.
26 |
27 | reading between the letters is encouraged.
28 |
29 | so is reading the rules and faq .
30 |
31 |
32 |
33 | if you spot some bugs, plz to be reporting them
34 |
35 |
36 |
37 | need even more lore? check out the guide or the wiki
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 | <%= render_partial("shared/chat.html.erb", { render_recent_chats: render_recent_chats }) %>
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 | <%= render_boards() %>
57 |
58 |
59 |
60 |
61 |
62 |
63 |
recent threads
64 |
65 | (moariarty)
66 |
67 |
68 | <%= render_partial("shared/new_post.html.erb", { show_tripcode: true }) %>
69 |
70 |
71 |
72 |
73 | <%= render_recent_threads() %>
74 |
75 |
76 |
77 |
78 |
recent replies
79 | <%= render_recent_posts() %>
80 |
81 |
82 |
85 |
86 |
87 |
88 |
89 |
90 |
91 |
92 |
--------------------------------------------------------------------------------
/app/static/templates/controllers/links.html.erb:
--------------------------------------------------------------------------------
1 | <% set_default("board_slogan", "") %>
2 |
3 |
4 |
5 | <%= render_partial("shared/logo_and_links.html.erb", { tripcode: tripcode, hide_settings: true }) %>
6 |
7 |
/<%- images ? "gifs" : "links" %>
8 | <% if (board_slogan) { %>
9 | <%= board_slogan %>
10 | <% } %>
11 |
12 |
13 |
14 | <%= render_links() %>
15 |
16 |
17 |
--------------------------------------------------------------------------------
/app/static/templates/controllers/mods.html.erb:
--------------------------------------------------------------------------------
1 | <% set_default("use_header", false) %>
2 | <% set_default("slogan", "") %>
3 |
4 |
5 | <% if (use_header) { %>
6 | <%= render_partial("home/header.html.erb", { slogan: slogan, show_search: false}) %>
7 | <% } %>
8 |
9 |
10 | <%= mods %>
11 |
12 |
13 |
--------------------------------------------------------------------------------
/app/static/templates/controllers/posts/posts.html.erb:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
Welcome to posts 's controller template
5 |
6 |
7 | (This main layout is located in app/static//templates/controllers/posts/posts.html.erb )
8 |
9 |
10 |
11 |
12 | <%= render_partial("posts/shared.html.erb") %>
13 |
14 |
15 |
16 |
--------------------------------------------------------------------------------
/app/static/templates/controllers/posts/show.html.erb:
--------------------------------------------------------------------------------
1 | <% set_default("render_sponsored_content", function() { }) %>
2 | <% set_default("render_recent_chats", function() { }) %>
3 |
4 | <% set_default("tripcode", "") %>
5 |
6 |
7 |
8 | <%= render_partial("shared/logo_and_links.html.erb", { tripcode: tripcode }) %>
9 |
10 |
11 |
12 |
13 |
21 |
22 |
Loading
23 |
<%= render_partial("shared/loading.html.erb") %>
24 |
25 |
26 |
27 | <%= render_partial("shared/new_post.html.erb") %>
28 |
29 |
30 |
31 |
32 | <%= render_post() %>
33 |
34 |
35 | <%= render_partial("shared/chat.html.erb", { render_recent_chats: render_recent_chats }) %>
36 |
37 | <%= render_sponsored_content() %>
38 |
39 |
40 |
41 |
42 |
43 |
--------------------------------------------------------------------------------
/app/static/templates/controllers/profiles/profiles.html.erb:
--------------------------------------------------------------------------------
1 |
2 |
3 |
spawning doppelnon...
4 |
5 | <%= render_partial("shared/loading.html.erb") %>
6 |
7 |
8 |
9 |
10 |
11 |
12 | <%= render_partial("shared/logo_and_links.html.erb", { tripcode: tripcode, hide_settings: true }) %>
13 |
14 |
15 |
18 |
19 |
22 |
23 |
26 |
27 |
28 |
29 |
30 | <%= render_about() %>
31 |
32 |
33 |
34 |
35 | <%= render_ships() %>
36 |
37 | <%= render_burtles() %>
38 |
39 | <%= render_trophies() %>
40 |
41 |
42 |
43 |
44 |
45 |
46 |
53 |
54 |
55 |
56 |
--------------------------------------------------------------------------------
/app/static/templates/controllers/recent.html.erb:
--------------------------------------------------------------------------------
1 | <%= add_stylesheet("home") %>
2 | <% set_default("use_header", false) %>
3 |
4 |
5 |
6 | <% if (use_header) { %>
7 | <%= render_partial("home/header.html.erb", { slogan: slogan, show_search: false }) %>
8 | <% } %>
9 |
10 |
11 |
12 |
yet some more posts
13 | <%= render_recent_threads() %>
14 |
15 |
16 |
and even more replies
17 | <%= render_recent_posts() %>
18 |
19 |
20 |
21 |
22 |
--------------------------------------------------------------------------------
/app/static/templates/controllers/search/search.html.erb:
--------------------------------------------------------------------------------
1 | <% add_stylesheet("search") %>
2 | <% add_stylesheet("home") %>
3 | <% set_default("slogan", "") %>
4 | <% set_default("query", "") %>
5 |
6 |
7 | <%= render_partial("home/header.html.erb", { show_search: false}) %>
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 | <%= render_search_results() %>
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
--------------------------------------------------------------------------------
/app/static/templates/partials/boards/shared.html.erb:
--------------------------------------------------------------------------------
1 |
2 | This is a partial
3 |
4 | It's your friend :-) It's located in
5 | app/static/templates/partials/boards/shared.html.erb
6 |
7 |
--------------------------------------------------------------------------------
/app/static/templates/partials/home/404.html.erb:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
404
5 | you broke the atob
6 |
7 |
8 |
9 | <%= render_upeye() %>
10 |
11 |
12 |
13 |
--------------------------------------------------------------------------------
/app/static/templates/partials/home/board_links.html.erb:
--------------------------------------------------------------------------------
1 | <% var icons = {
2 | 'to' : 'circletwo',
3 | 'links' : 'link',
4 | 'gifs' : 'picture'
5 | } %>
6 |
7 | <% var width = 60 / (boards.length+1) %>
8 | <% set_default("add_new_thread", false) %>
9 |
10 |
11 | <% _.each(boards, function(board) { %>
12 |
13 | ">'
14 |
15 | <% }) %>
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
--------------------------------------------------------------------------------
/app/static/templates/partials/home/header.html.erb:
--------------------------------------------------------------------------------
1 | <% set_default("slogan", "something something something") %>
2 | <% set_default("tripcode", "abcdef") %>
3 | <% set_default("show_hr", true) %>
4 | <% set_default("show_search", true) %>
5 |
6 |
7 | <%
8 | set_default("render_tripcode", function() {
9 | var tripcode_gen = require_app("server/tripcode");
10 | var hashEl = $("");
11 | hashEl.attr("data-tripcode", ANON_OF_THE_NOW);
12 | tripcode_gen.gen_tripcode(hashEl);
13 |
14 |
15 | return hashEl.html();
16 | })
17 | %>
18 |
19 |
47 |
48 |
--------------------------------------------------------------------------------
/app/static/templates/partials/home/icons.html.erb:
--------------------------------------------------------------------------------
1 |
2 |
holy shit... icons
3 |
4 |
5 | thanks to WHGH, these icons are from http://www.webhostinghub.com/glyphs/bootstrap/
6 |
7 |
8 |
9 | <%= render_icons() %>
10 |
11 |
12 |
13 |
--------------------------------------------------------------------------------
/app/static/templates/partials/home/index.html.erb:
--------------------------------------------------------------------------------
1 |
2 |
3 |
This is a partial
4 | It's your friend :-)
5 |
6 |
7 |
--------------------------------------------------------------------------------
/app/static/templates/partials/home/link.html.erb:
--------------------------------------------------------------------------------
1 | <% set_default("image", false) %>
2 | <% set_default("uppable", false) %>
3 | <% set_default("condensed", false) %>
4 |
5 |
"
6 | data-linkid="<%= id %>">
7 |
11 |
12 |
13 | <% if (!condensed) { %>
14 | <% if (uppable) { %>
15 |
16 | " >
17 | <%= ups || "" %>
18 |
19 | <% } else { %>
20 |
21 |
26 | <%= ups || "" %>
27 |
28 | <% } %>
29 | <% } %>
30 |
" href=<%= href %>><%= title || href %>
31 | <% if (image) { %>
32 |
target="_blank">[link]
33 | <% } %>
34 |
<%= domain %>
35 |
36 |
37 |
38 |
--------------------------------------------------------------------------------
/app/static/templates/partials/home/recent_posts.html.erb:
--------------------------------------------------------------------------------
1 | <% set_default("posts", []) %>
2 | <% set_default("archive", "p") %>
3 | <% set_default("title_only", false) %>
4 | <% set_default("class_", "posts") %>
5 |
6 |
7 | <% _.each(posts, function(post) { %>
8 | <%= summarize(post.dataValues, archive, title_only) %>
9 | <% }) %>
10 |
11 |
--------------------------------------------------------------------------------
/app/static/templates/partials/home/rules.html.erb:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
rules
5 |
6 | do not post anything that violates local or US law
7 | spamming, advertising and flooding is bad times
8 | posting or aksing for dox is is ban times
9 | any and all shitposting belongs in /b
10 |
11 |
12 |
13 |
--------------------------------------------------------------------------------
/app/static/templates/partials/posts/shared.html.erb:
--------------------------------------------------------------------------------
1 |
2 |
This is a partial
3 |
4 | It's your friend :-) It's located in
5 |
app/static/templates/partials/posts/shared.html.erb
6 |
7 |
--------------------------------------------------------------------------------
/app/static/templates/partials/profiles/stats.html.erb:
--------------------------------------------------------------------------------
1 |
2 |
3 | posts: <%= post_count %>
4 | replies: <%= reply_count %> ,
5 | first posted
6 |
7 |
8 |
9 | this is not a profile page
10 |
11 |
12 |
13 |
14 |
15 |
--------------------------------------------------------------------------------
/app/static/templates/partials/rss.html.erb:
--------------------------------------------------------------------------------
1 |
2 |
3 |
--------------------------------------------------------------------------------
/app/static/templates/partials/shared/benjamin_buttons.html.erb:
--------------------------------------------------------------------------------
1 |
5 |
6 |
7 |
--------------------------------------------------------------------------------
/app/static/templates/partials/shared/chat.html.erb:
--------------------------------------------------------------------------------
1 | <% add_stylesheet("chat.css") %>
2 |
3 |
4 |
5 |
6 | <%= render_recent_chats() %>
7 |
8 |
9 |
10 |
--------------------------------------------------------------------------------
/app/static/templates/partials/shared/favorites.html.erb:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/app/static/templates/partials/shared/logo_and_links.html.erb:
--------------------------------------------------------------------------------
1 | <% set_default("tripcode", null) %>
2 | <% set_default("hide_settings", false) %>
3 | <% set_default("desaturate", false) %>
4 |
5 | <% add_stylesheet("logo_and_links") %>
6 |
7 |
8 |
9 |
10 |
11 |
12 |
24 | <% if (!hide_settings) { %>
25 |
26 |
27 |
28 | <% } %>
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 | <%= render_partial("shared/settings.html.erb", { tripcode: tripcode }) %>
46 |
47 |
48 | <%= render_partial("shared/favorites.html.erb", { tripcode: tripcode }) %>
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
--------------------------------------------------------------------------------
/app/static/templates/partials/shared/new_post.html.erb:
--------------------------------------------------------------------------------
1 | <%= set_default("show_tripcode", false) %>
2 |
3 |
39 |
--------------------------------------------------------------------------------
/app/static/templates/partials/shared/search.html.erb:
--------------------------------------------------------------------------------
1 | <% add_stylesheet("searchbar") %>
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/app/static/templates/partials/shared/settings.html.erb:
--------------------------------------------------------------------------------
1 | <% add_stylesheet("settings.css") %>
2 |
3 |
4 |
5 |
6 |
19 |
20 |
79 |
80 |
81 |
82 | <%= render_partial("shared/benjamin_buttons.html.erb") %>
83 |
84 |
85 |
86 |
87 |
--------------------------------------------------------------------------------
/app/static/vendor/cordova/cordova_plugins.js:
--------------------------------------------------------------------------------
1 | cordova.define('cordova/plugin_list', function(require, exports, module) {
2 | module.exports = [
3 | {
4 | "file": "plugins/de.appplant.cordova.plugin.local-notification/www/local-notification.js",
5 | "id": "de.appplant.cordova.plugin.local-notification.LocalNotification",
6 | "clobbers": [
7 | "plugin.notification.local"
8 | ]
9 | },
10 | {
11 | "file": "plugins/de.appplant.cordova.plugin.background-mode/www/background-mode.js",
12 | "id": "de.appplant.cordova.plugin.background-mode.BackgroundMode",
13 | "clobbers": [
14 | "cordova.plugins.backgroundMode",
15 | "plugin.backgroundMode"
16 | ]
17 | },
18 | {
19 | "file": "plugins/org.apache.cordova.inappbrowser/www/inappbrowser.js",
20 | "id": "org.apache.cordova.inappbrowser.inappbrowser",
21 | "clobbers": [
22 | "window.open"
23 | ]
24 | },
25 | {
26 | "file": "plugins/org.apache.cordova.device/www/device.js",
27 | "id": "org.apache.cordova.device.device",
28 | "clobbers": [
29 | "device"
30 | ]
31 | }
32 | ];
33 | module.exports.metadata =
34 | // TOP OF METADATA
35 | {
36 | "de.appplant.cordova.plugin.local-notification": "0.7.6",
37 | "de.appplant.cordova.plugin.background-mode": "0.6.2",
38 | "org.apache.cordova.inappbrowser": "0.5.4",
39 | "org.apache.cordova.device": "0.2.14-dev"
40 | }
41 | // BOTTOM OF METADATA
42 | });
--------------------------------------------------------------------------------
/app/static/vendor/cordova/plugins/org.apache.cordova.device/www/device.js:
--------------------------------------------------------------------------------
1 | cordova.define("org.apache.cordova.device.device", function(require, exports, module) { /*
2 | *
3 | * Licensed to the Apache Software Foundation (ASF) under one
4 | * or more contributor license agreements. See the NOTICE file
5 | * distributed with this work for additional information
6 | * regarding copyright ownership. The ASF licenses this file
7 | * to you under the Apache License, Version 2.0 (the
8 | * "License"); you may not use this file except in compliance
9 | * with the License. You may obtain a copy of the License at
10 | *
11 | * http://www.apache.org/licenses/LICENSE-2.0
12 | *
13 | * Unless required by applicable law or agreed to in writing,
14 | * software distributed under the License is distributed on an
15 | * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
16 | * KIND, either express or implied. See the License for the
17 | * specific language governing permissions and limitations
18 | * under the License.
19 | *
20 | */
21 |
22 | var argscheck = require('cordova/argscheck'),
23 | channel = require('cordova/channel'),
24 | utils = require('cordova/utils'),
25 | exec = require('cordova/exec'),
26 | cordova = require('cordova');
27 |
28 | channel.createSticky('onCordovaInfoReady');
29 | // Tell cordova channel to wait on the CordovaInfoReady event
30 | channel.waitForInitialization('onCordovaInfoReady');
31 |
32 | /**
33 | * This represents the mobile device, and provides properties for inspecting the model, version, UUID of the
34 | * phone, etc.
35 | * @constructor
36 | */
37 | function Device() {
38 | this.available = false;
39 | this.platform = null;
40 | this.version = null;
41 | this.uuid = null;
42 | this.cordova = null;
43 | this.model = null;
44 |
45 | var me = this;
46 |
47 | channel.onCordovaReady.subscribe(function() {
48 | me.getInfo(function(info) {
49 | //ignoring info.cordova returning from native, we should use value from cordova.version defined in cordova.js
50 | //TODO: CB-5105 native implementations should not return info.cordova
51 | var buildLabel = cordova.version;
52 | me.available = true;
53 | me.platform = info.platform;
54 | me.version = info.version;
55 | me.uuid = info.uuid;
56 | me.cordova = buildLabel;
57 | me.model = info.model;
58 | channel.onCordovaInfoReady.fire();
59 | },function(e) {
60 | me.available = false;
61 | utils.alert("[ERROR] Error initializing Cordova: " + e);
62 | });
63 | });
64 | }
65 |
66 | /**
67 | * Get device info
68 | *
69 | * @param {Function} successCallback The function to call when the heading data is available
70 | * @param {Function} errorCallback The function to call when there is an error getting the heading data. (OPTIONAL)
71 | */
72 | Device.prototype.getInfo = function(successCallback, errorCallback) {
73 | argscheck.checkArgs('fF', 'Device.getInfo', arguments);
74 | exec(successCallback, errorCallback, "Device", "getDeviceInfo", []);
75 | };
76 |
77 | module.exports = new Device();
78 |
79 | });
80 |
--------------------------------------------------------------------------------
/app/static/vendor/hex-rgb.src.js:
--------------------------------------------------------------------------------
1 | /*!
2 | * HEX <=> RGB Conversion
3 | * Copyright(c) 2011 Daniel Lamb
4 | * MIT Licensed
5 | */
6 |
7 | (function (context) {
8 |
9 | context['toRGB'] = function (/* String */ color) {
10 | // summary:
11 | // Converts a 6 digit Hexadecimal string value to an RGB integer array.
12 | // Important! input must be a 6 digit Hexadecimal string "bad" will
13 | // not convert correctly but "bbaadd" will. To keep the function as
14 | // light as possible there is no idiot-proofing, if you pass in bad
15 | // data I'm not fixing it for you :-)
16 | //
17 | // color: String
18 | // 6 digit Hexadecimal string value
19 | //
20 | // returns: Array
21 | // An array containing the RGB integers in the following format [red, green, blue]
22 | //
23 | // example:
24 | // Convert the Hexadecimal value "c0ffee" (blue color) to RGB integers.
25 | // The variable "rgb" will be equal to [192, 255, 238]
26 | //
27 | // var rgb = toRGB("c0ffee");
28 |
29 | //convert string to base 16 number
30 | var num = parseInt(color, 16);
31 |
32 | //return the red, green and blue values as a new array
33 | return [num >> 16, num >> 8 & 255, num & 255];
34 | };
35 |
36 | context['toHex'] = function (/* Number */ red, /* Number */ green, /* Number */ blue) {
37 | // summary:
38 | // Converts 3 RGB integer values into a Hexadecimal string.
39 | // Important! input must be integers with a range of 0 to 255.
40 | // To keep the function as light as possible there is no idiot-proofing,
41 | // if you pass in bad data I'm not fixing it for you :-)
42 | //
43 | // red: Number
44 | // number ranging from 0 to 255 indicating the amount of red
45 | // green: Number
46 | // number ranging from 0 to 255 indicating the amount of green
47 | // blue: Number
48 | // number ranging from 0 to 255 indicating the amount of blue
49 | //
50 | // returns: String
51 | // 6 digit Hexadecimal string value
52 | //
53 | // example:
54 | // Convert the RGB values [192, 255, 238] (blue color) to Hexadecimal string.
55 | // The variable "hex" will be equal to "c0ffee"
56 | //
57 | // var hex = toHex(192, 255, 238);
58 |
59 | //return 6 digit Hexadecimal string
60 | return ((blue | green << 8 | red << 16) | 1 << 24).toString(16).slice(1);
61 | };
62 |
63 | })(this);
64 |
65 |
--------------------------------------------------------------------------------
/app/static/vendor/is_mobile.js:
--------------------------------------------------------------------------------
1 | "use strict";
2 |
3 | var isMobile = {
4 | Android: function() {
5 | return navigator.userAgent.match(/Android/i);
6 | },
7 | BlackBerry: function() {
8 | return navigator.userAgent.match(/BlackBerry/i);
9 | },
10 | iOS: function() {
11 | return navigator.userAgent.match(/iPhone|iPad|iPod/i);
12 | },
13 | Opera: function() {
14 | return navigator.userAgent.match(/Opera Mini/i);
15 | },
16 | Windows: function() {
17 | return navigator.userAgent.match(/IEMobile/i);
18 | },
19 | any: function() {
20 | return (isMobile.Android() || isMobile.BlackBerry() || isMobile.iOS() || isMobile.Opera() || isMobile.Windows());
21 | }
22 | };
23 |
24 | module.exports = isMobile;
25 |
--------------------------------------------------------------------------------
/app/static/vendor/jquery.deserialize.js:
--------------------------------------------------------------------------------
1 | /**
2 | * jQuery Deserialize plugin
3 | *
4 | * Deserializes a query string (taken for example from window.location.hash string) into the appropriate form elements.
5 | *
6 | * Usage
7 | * $("form").deserialize(string);
8 | *
9 | * do not trigger change events on elements
10 | * $("form").deserialize(string, {noEvents: true});
11 | *
12 | * expect checkboxes to be serialized as boolean (true/false) rather than standard (present/missing)
13 | * $("form").deserialize(string, {checkboxesAsBools: true});
14 | **/
15 | (function($) {
16 | $.fn.deserialize = function(s, options) {
17 | function optionallyTrigger(element,event) {
18 | if (options.noEvents)
19 | return;
20 | element.trigger(event);
21 | }
22 |
23 | function changeChecked($input, newState) {
24 | var oldState = $input.is(":checked");
25 | $input.attr("checked", newState);
26 | if (oldState != newState)
27 | optionallyTrigger($input, 'change');
28 | }
29 |
30 | options = options || {};
31 | var data = {};
32 | var parts = s.split("&");
33 |
34 | for (var i = 0; i < parts.length; i++) {
35 | var pair = $.map(parts[i].replace(/\+/g, '%20').split("="), function(d) {
36 | return decodeURIComponent(d);
37 | });
38 |
39 | //collect data for checkbox handling
40 | data[pair[0]] = pair[1];
41 |
42 | var $input = $("[name='" + pair[0] + "']", this);
43 | var type = $input.attr('type');
44 |
45 | if (type == 'radio') {
46 | $input = $input.filter("[value='" + pair[1] + "']");
47 | changeChecked($input, true);
48 | } else if (type == 'checkbox') {
49 | // see below
50 | } else {
51 | var oldVal = $input.val();
52 | var newVal = pair[1];
53 | $input.val(newVal);
54 | if (oldVal != newVal)
55 | optionallyTrigger($input, 'change');
56 | }
57 | }
58 |
59 | $("input[type=checkbox]", this).each(function() {
60 | var $name = this["name"];
61 | var $input = $(this);
62 | if (options.checkboxesAsBools) {
63 | //checkboxes are serialized as non-standard true/false, so only change value if provided (as explicit
64 | // boolean) in the data. (so checkboxes behave like other fields - unspecified fields are unchanged)
65 | if (data[$name] == 'true')
66 | changeChecked($input, true);
67 | else if (data[$name] == 'false')
68 | changeChecked($input, false);
69 | }
70 | else {
71 | //standard serialization, so checkboxes are not serialized -> ANY missing value means unchecked
72 | // (no difference betwen "missing" and "false").
73 | changeChecked($input, ($input.attr("name") in data));
74 | }
75 | });
76 | };
77 | })(jQuery);
78 |
--------------------------------------------------------------------------------
/app/static/vendor/jquery.tagcloud.js:
--------------------------------------------------------------------------------
1 | /*!
2 | * jquery.tagcloud.js
3 | * A Simple Tag Cloud Plugin for JQuery
4 | *
5 | * https://github.com/addywaddy/jquery.tagcloud.js
6 | * created by Adam Groves
7 | */
8 | (function($) {
9 |
10 | /*global jQuery*/
11 | "use strict";
12 |
13 | var compareWeights = function(a, b)
14 | {
15 | return a - b;
16 | };
17 |
18 | // Converts hex to an RGB array
19 | var toRGB = function(code) {
20 | if (code.length === 4) {
21 | code = code.replace(/(\w)(\w)(\w)/gi, "\$1\$1\$2\$2\$3\$3");
22 | }
23 | var hex = /(\w{2})(\w{2})(\w{2})/.exec(code);
24 | return [parseInt(hex[1], 16), parseInt(hex[2], 16), parseInt(hex[3], 16)];
25 | };
26 |
27 | // Converts an RGB array to hex
28 | var toHex = function(ary) {
29 | return "#" + jQuery.map(ary, function(i) {
30 | var hex = i.toString(16);
31 | hex = (hex.length === 1) ? "0" + hex : hex;
32 | return hex;
33 | }).join("");
34 | };
35 |
36 | var colorIncrement = function(color, range) {
37 | return jQuery.map(toRGB(color.end), function(n, i) {
38 | return (n - toRGB(color.start)[i])/range;
39 | });
40 | };
41 |
42 | var tagColor = function(color, increment, weighting) {
43 | var rgb = jQuery.map(toRGB(color.start), function(n, i) {
44 | var ref = Math.round(n + (increment[i] * weighting));
45 | if (ref > 255) {
46 | ref = 255;
47 | } else {
48 | if (ref < 0) {
49 | ref = 0;
50 | }
51 | }
52 | return ref;
53 | });
54 | return toHex(rgb);
55 | };
56 |
57 | $.fn.tagcloud = function(options) {
58 |
59 | var opts = $.extend({}, $.fn.tagcloud.defaults, options);
60 | var tagWeights = this.map(function(){
61 | return $(this).attr("rel");
62 | });
63 | tagWeights = jQuery.makeArray(tagWeights).sort(compareWeights);
64 | var lowest = tagWeights[0];
65 | var highest = tagWeights.pop();
66 | var range = highest - lowest;
67 | if(range === 0) {range = 1;}
68 | // Sizes
69 | var fontIncr, colorIncr;
70 | if (opts.size) {
71 | fontIncr = (opts.size.end - opts.size.start)/range;
72 | }
73 | // Colors
74 | if (opts.color) {
75 | colorIncr = colorIncrement (opts.color, range);
76 | }
77 | return this.each(function() {
78 | var weighting = $(this).attr("rel") - lowest;
79 | if (opts.size) {
80 | $(this).css({"font-size": opts.size.start + (weighting * fontIncr) + opts.size.unit});
81 | }
82 | if (opts.color) {
83 | $(this).css({"color": tagColor(opts.color, colorIncr, weighting)});
84 | }
85 | });
86 | };
87 |
88 | $.fn.tagcloud.defaults = {
89 | size: {start: 14, end: 18, unit: "pt"}
90 | };
91 |
92 | })(jQuery);
93 |
--------------------------------------------------------------------------------
/app/static/vendor/lunicode.js:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/atobs/atob/HEAD/app/static/vendor/lunicode.js
--------------------------------------------------------------------------------
/app/static/vendor/useractions_stub.js:
--------------------------------------------------------------------------------
1 | if (typeof window !== "undefined") {
2 | window._ET = {
3 | stack: [],
4 | start: +new Date(),
5 | local: function() {
6 | this.stack.push(['local', _.toArray(arguments), +new Date()]);
7 | this.stack.length = Math.min(100, this.stack.length);
8 | },
9 | global: function() {
10 | this.stack.push(['global', _.toArray(arguments), +new Date()]);
11 | this.stack.length = Math.min(100, this.stack.length);
12 | }
13 | };
14 | }
15 |
--------------------------------------------------------------------------------
/components/board_admin_panel/board_admin_panel.css:
--------------------------------------------------------------------------------
1 | // mobile devices & portrait
2 | // 200 - 767px
3 | @media @device {
4 |
5 | }
6 |
7 | // netbooks, mobile devices in landscape
8 | // laptops, desktops
9 | // 768 - 1900px
10 | @media @computer {
11 |
12 | }
13 |
14 | // huge screens
15 | // 1900px
16 | @media @large_screen {
17 |
18 | }
19 |
--------------------------------------------------------------------------------
/components/board_admin_panel/board_admin_panel.html.erb:
--------------------------------------------------------------------------------
1 | <%= set_default("author", "") %>
2 | <%= set_default("tripcode", "") %>
3 |
4 |
5 |
6 |
7 |
12 |
13 |
14 | You are the mod fuckoff of this great and wonderful bread. That
15 | means you have some extra powers to keep this bread toasty. if you
16 | report any posts on this board, they will be deleted and the
17 | action will be reported to the mod log
18 |
19 |
20 |
21 |
22 | more powers and config will be coming in the future
23 |
24 |
25 |
26 |
31 |
32 |
33 |
34 |
--------------------------------------------------------------------------------
/components/board_admin_panel/board_admin_panel.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | tagName: "div",
3 | className: "",
4 | defaults: {
5 | content: "default content"
6 | },
7 | initialize: function(options) {
8 | },
9 | client: function(options) {
10 | $(document.body).append(this.$el);
11 | this.$el.find(".modal").modal();
12 |
13 | console.log("MADE ADMIN PANEL", this.$el);
14 | }
15 | };
16 |
--------------------------------------------------------------------------------
/components/board_admin_panel/events.js:
--------------------------------------------------------------------------------
1 | "use strict";
2 |
3 | module.exports = {
4 | // Component event handling goes here
5 | // This is purposefully kept separate from
6 | // the main component file, since it has code
7 | // that is generally not relevant to the server.
8 | events: {
9 | "click .save" : "handle_click_save",
10 | "click .cancel" : "handle_click_cancel"
11 | },
12 |
13 | handle_click_save: function() {
14 |
15 | var triphash = SF.controller().get_triphash();
16 | var tripname = SF.controller().get_handle();
17 | var board = SF.controller().board;
18 |
19 | SF.socket().emit("update_board_config", {
20 | tripcode: triphash,
21 | tripname: tripname,
22 | board: board
23 | });
24 |
25 | this.$el.find(".modal").modal("hide");
26 |
27 | },
28 | handle_click_cancel: function() {
29 | this.$el.find(".modal").modal("hide");
30 |
31 | }
32 | };
33 |
--------------------------------------------------------------------------------
/components/board_admin_panel/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "main" : "board_admin_panel",
3 | "style": "board_admin_panel.css",
4 | "styles": [],
5 |
6 | "template": "board_admin_panel.html.erb",
7 | "helpers": [],
8 | "no_redefine" : false
9 | }
10 |
--------------------------------------------------------------------------------
/components/board_admin_panel/test/client.js:
--------------------------------------------------------------------------------
1 | "use strict";
2 |
3 | var component_name = "TEMPLATE";
4 |
5 | describe(component_name, function() {
6 | it("builds on the client", function(done) {
7 | $C(component_name, { name: "first_button"}, function(cmp) {
8 | assert.notEqual(cmp, null);
9 | assert.notEqual(cmp.$el, null);
10 | done();
11 | });
12 | });
13 | it("renders on the client", function(done) {
14 | $C(component_name, { name: "first_button"}, function(cmp) {
15 | assert.notEqual(cmp, null);
16 | assert.notEqual(cmp.$el, null);
17 | done();
18 | });
19 | });
20 | });
21 |
--------------------------------------------------------------------------------
/components/board_admin_panel/test/server.js:
--------------------------------------------------------------------------------
1 | "use strict"
2 |
3 | var test_helper = require("superfluous").test_helper;
4 | test_helper.init();
5 |
6 | var assert = require("assert");
7 | var component = require_core("server/component");
8 |
9 | describe('TEMPLATE', function(){
10 | test_helper.setup_server(function() {
11 | component.build('TEMPLATE', {}, function(cmp) {
12 | describe('#initialize()', function(){
13 | it('should initialize the component', function(){
14 | assert.notEqual(cmp, null);
15 | });
16 | });
17 | });
18 |
19 | component.build('TEMPLATE', {}, function(cmp) {
20 | describe('#client()', function(){
21 | it('should initialize the component on the client', function(){
22 | assert.notEqual(cmp.$el.html(), null);
23 | });
24 | });
25 | });
26 | });
27 | });
28 |
--------------------------------------------------------------------------------
/components/board_claim_panel/board_claim_panel.css:
--------------------------------------------------------------------------------
1 | // mobile devices & portrait
2 | // 200 - 767px
3 | @media @device {
4 |
5 | }
6 |
7 | // netbooks, mobile devices in landscape
8 | // laptops, desktops
9 | // 768 - 1900px
10 | @media @computer {
11 |
12 | }
13 |
14 | // huge screens
15 | // 1900px
16 | @media @large_screen {
17 |
18 | }
19 |
--------------------------------------------------------------------------------
/components/board_claim_panel/board_claim_panel.html.erb:
--------------------------------------------------------------------------------
1 | <%= set_default("author", "") %>
2 | <%= set_default("tripcode", "") %>
3 | <%= set_default("moderated", false) %>
4 |
5 |
6 |
7 |
8 |
13 |
14 |
15 | <% if (moderated) { %>
16 | This board (/<%=board%> ) is currently
17 | moderated , but you can also moderate this board
18 | and make it all it can be. To become a janitor, you submit your
19 | claim for it with this dialog. All claims go through the admin
20 | before being approved.
21 | <% } else { %>
22 | This board (/<%=board%> ) is currently
23 | unmoderated , meaning that you can moderate this board
24 | and make it all it can be. To become a moderator, you submit your
25 | claim for it with this dialog. All claims go through the admin
26 | before being approved.
27 | <% } %>
28 |
29 |
30 |
31 | Thanks for being a responsible janitor, burtle loves you!
32 |
33 |
34 |
35 | NOTE: SAVE YOUR TRIPCODE AND TRIPNAME OR YOU WONT BE ABLE TO ADMIN YOUR BOARD!
36 |
37 |
38 |
64 |
65 |
66 |
71 |
72 |
73 |
74 |
--------------------------------------------------------------------------------
/components/board_claim_panel/board_claim_panel.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | tagName: "div",
3 | className: "",
4 | defaults: {
5 | content: "default content"
6 | },
7 | initialize: function(options) {
8 | },
9 | client: function(options) {
10 | var client_options = options.client_options;
11 |
12 | $(document.body).append(this.$el);
13 | this.$el.find(".modal").modal();
14 |
15 | this.$el.find("input[name='author']").val(SF.controller().get_handle());
16 | this.$el.find("input[name='tripcode']").val(SF.controller().get_tripcode());
17 | this.$el.find("input[name='triphash']").val(SF.controller().get_triphash());
18 | this.$el.find("input[name='author']").attr("disabled", true);
19 | this.$el.find("input[name='tripcode']").attr("disabled", true);
20 |
21 | var tripbar = $(".identity_tripcode.tripbar").clone();
22 | tripbar.removeClass("identity_tripcode");
23 | this.$el.find(".form-horizontal").prepend(tripbar);
24 | }
25 | };
26 |
--------------------------------------------------------------------------------
/components/board_claim_panel/events.js:
--------------------------------------------------------------------------------
1 | "use strict";
2 |
3 | module.exports = {
4 | // Component event handling goes here
5 | // This is purposefully kept separate from
6 | // the main component file, since it has code
7 | // that is generally not relevant to the server.
8 | events: {
9 | "click .save" : "handle_click_save",
10 | "click .cancel" : "handle_click_cancel"
11 | },
12 |
13 | handle_click_save: function() {
14 |
15 | var triphash = this.$el.find("input[name='triphash']").val();
16 | var tripname = this.$el.find("input[name='author']").val();
17 | var desc = this.$el.find("textarea[name='reason']").val();
18 |
19 | var board = SF.controller().board;
20 |
21 | // Save this tripcode forever, just in case...
22 | SF.controller().remember_tripcode_forever(tripname, SF.controller().get_tripcode());
23 | SF.socket().emit("try_claim_board", {
24 | tripcode: triphash,
25 | tripname: tripname,
26 | reason: desc,
27 | board: board
28 | });
29 |
30 | this.$el.find(".modal").modal("hide");
31 |
32 | },
33 | handle_click_cancel: function() {
34 | this.$el.find(".modal").modal("hide");
35 |
36 | }
37 |
38 | };
39 |
--------------------------------------------------------------------------------
/components/board_claim_panel/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "main" : "board_claim_panel",
3 | "style": "board_claim_panel.css",
4 | "styles": [],
5 |
6 | "template": "board_claim_panel.html.erb",
7 | "helpers": [],
8 | "no_redefine" : false
9 | }
10 |
--------------------------------------------------------------------------------
/components/board_claim_panel/test/client.js:
--------------------------------------------------------------------------------
1 | "use strict";
2 |
3 | var component_name = "TEMPLATE";
4 |
5 | describe(component_name, function() {
6 | it("builds on the client", function(done) {
7 | $C(component_name, { name: "first_button"}, function(cmp) {
8 | assert.notEqual(cmp, null);
9 | assert.notEqual(cmp.$el, null);
10 | done();
11 | });
12 | });
13 | it("renders on the client", function(done) {
14 | $C(component_name, { name: "first_button"}, function(cmp) {
15 | assert.notEqual(cmp, null);
16 | assert.notEqual(cmp.$el, null);
17 | done();
18 | });
19 | });
20 | });
21 |
--------------------------------------------------------------------------------
/components/board_claim_panel/test/server.js:
--------------------------------------------------------------------------------
1 | "use strict"
2 |
3 | var test_helper = require("superfluous").test_helper;
4 | test_helper.init();
5 |
6 | var assert = require("assert");
7 | var component = require_core("server/component");
8 |
9 | describe('TEMPLATE', function(){
10 | test_helper.setup_server(function() {
11 | component.build('TEMPLATE', {}, function(cmp) {
12 | describe('#initialize()', function(){
13 | it('should initialize the component', function(){
14 | assert.notEqual(cmp, null);
15 | });
16 | });
17 | });
18 |
19 | component.build('TEMPLATE', {}, function(cmp) {
20 | describe('#client()', function(){
21 | it('should initialize the component on the client', function(){
22 | assert.notEqual(cmp.$el.html(), null);
23 | });
24 | });
25 | });
26 | });
27 | });
28 |
--------------------------------------------------------------------------------
/components/button/button.css:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/components/button/button.html.erb:
--------------------------------------------------------------------------------
1 | <% set_default("title", "") %>
2 |
3 |
4 | <%= name %>
5 |
6 |
--------------------------------------------------------------------------------
/components/button/button.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | tagName: "div",
3 | className: "btn",
4 |
5 | set_title: function(title) {
6 | this.$el.attr('title', title);
7 | }
8 | };
9 |
--------------------------------------------------------------------------------
/components/button/events.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | events: {
3 | "click" : "handle_button_click"
4 | },
5 |
6 | handle_button_click: function() {
7 | }
8 | };
9 |
--------------------------------------------------------------------------------
/components/button/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "main" : "button",
3 | "helpers": [],
4 | "style": "button.css",
5 | "styles": [ "box" ],
6 | "template": "button.html.erb"
7 | }
8 |
--------------------------------------------------------------------------------
/components/button/test/client.js:
--------------------------------------------------------------------------------
1 | "use strict";
2 |
3 | var component_name = "button";
4 |
5 | describe(component_name, function() {
6 | it("builds on the client", function(done) {
7 | $C(component_name, { name: "first_button"}, function(cmp) {
8 | assert.notEqual(cmp, null);
9 | assert.notEqual(cmp.$el, null);
10 | done();
11 | });
12 | });
13 | it("renders on the client", function(done) {
14 | $C(component_name, { name: "first_button"}, function(cmp) {
15 | assert.notEqual(cmp, null);
16 | assert.notEqual(cmp.$el, null);
17 | done();
18 | });
19 | });
20 | });
21 |
--------------------------------------------------------------------------------
/components/button/test/server.js:
--------------------------------------------------------------------------------
1 | "use strict";
2 |
3 | var test_helper = require("superfluous").test_helper;
4 | test_helper.init();
5 |
6 | var assert = require("assert");
7 | var component_name = "button";
8 |
9 | describe(component_name, function() {
10 | test_helper.setup_server(function() {
11 | var component = require_core("server/component");
12 |
13 | it("builds on the server", test_helper.wrap(function(done) {
14 | component.build(component_name, { name: "first_button"}, function(cmp) {
15 | assert.notEqual(cmp, null);
16 | assert.notEqual(cmp.$el, null);
17 | done();
18 | });
19 | }));
20 |
21 | it("renders on the server", test_helper.wrap(function(done) {
22 | component.build(component_name, {name: "first_button"}, function(cmp) {
23 | assert.notEqual(cmp.$el.html(), null);
24 |
25 | done();
26 | });
27 | }));
28 | });
29 | });
30 |
--------------------------------------------------------------------------------
/components/component.js:
--------------------------------------------------------------------------------
1 | // Assume underscore and Backbone are already defined
2 | var Component = Backbone.View.extend({
3 | tagName: 'div',
4 |
5 | className: 'cmp',
6 |
7 | init: function(options) {
8 | Backbone.View.init(options);
9 | this.__id = options.id;
10 | },
11 |
12 | dispose: function() {
13 |
14 | },
15 |
16 | appendTo: function(parent) {
17 | return this.$el.appendTo(parent);
18 | },
19 |
20 | prependTo: function(parent) {
21 | return this.$el.prependTo(parent);
22 | },
23 |
24 | append: function(content) {
25 | return this.$el.append(content);
26 | },
27 |
28 | prepend: function() {
29 | return this.$el.prepend(content);
30 | },
31 |
32 | parent: function(selector) {
33 | return this.$el.parent(selector);
34 | },
35 |
36 | html: function(content) {
37 | return this.$el.html(content);
38 | },
39 |
40 | render: function() {
41 | var modeled = this.$el.find("[data-model]");
42 |
43 | _.each(modeled, function(el) {
44 | var par = Backbone.$(el).parent("[data-cmp]");
45 | });
46 | // TODO: Fill these out with modeled values on updates
47 | },
48 |
49 | toString: function() {
50 | // yuck
51 | var outer = Backbone.$("
");
52 | outer.append(this.$el.clone());
53 |
54 | var out_html = outer.html();
55 | return out_html;
56 | }
57 | });
58 |
59 | module.exports = Component;
60 |
--------------------------------------------------------------------------------
/components/delete_post_modal/delete_post_modal.css:
--------------------------------------------------------------------------------
1 | .modal {
2 | .modal-body {
3 | width: 100%;
4 | }
5 |
6 | .span2 { width: 100%; }
7 |
8 | .form-horizontal .controls {
9 | float: left;
10 | width: 100%;
11 | margin-left: 0px;
12 | margin-right: 0px;
13 | display: block; }
14 |
15 |
16 | .form-horizontal input,
17 | .form-horizontal textarea {
18 | width: 90%;
19 | padding: 5px;
20 |
21 | }
22 | .form-horizontal .control-label {
23 | padding-top: 5px;
24 | margin-left: 0px;
25 | text-align: left;
26 | font-weight: bold;
27 | width: 100%; }
28 | }
29 |
30 | .replypreview {
31 | opacity: 0.6;
32 |
33 | border: 3px dotted #ddd;
34 | padding: 5px;
35 | margin-top: 5px;
36 | width: 90%;
37 |
38 | @media @device {
39 | display: none !important;
40 | }
41 | }
42 |
43 |
44 | .starrable,
45 | .deletable,
46 | .editable,
47 | .reportable {
48 | display: none;
49 | }
50 |
--------------------------------------------------------------------------------
/components/delete_post_modal/delete_post_modal.html.erb:
--------------------------------------------------------------------------------
1 | <% set_default("author", ""); %>
2 | <% set_default("tripcode", ""); %>
3 | <% set_default("text", ""); %>
4 |
5 |
6 |
7 |
8 |
15 |
16 |
17 |
18 | this post will be reported to /log for review.
19 |
20 |
21 |
22 |
23 |
45 |
46 |
47 |
59 |
60 |
61 |
62 |
--------------------------------------------------------------------------------
/components/delete_post_modal/delete_post_modal.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | module.exports = {
4 | tagName: "div",
5 | className: "",
6 | defaults: {
7 | content: "default content"
8 | },
9 | show: function() {
10 | this.$el.find(".modal").modal('show');
11 | },
12 | hide: function() {
13 | this.$el.find(".modal").modal('hide');
14 | },
15 | initialize: function(options) {
16 | this.reply_id = options.reply_id;
17 | },
18 | client: function(options) {
19 | this.$el.find(".modal").modal();
20 |
21 | if (!window._REPLIES) {
22 | return;
23 | }
24 |
25 | var reply = window._REPLIES[this.reply_id];
26 | var tripcode = SF.controller().get_triphash();
27 | var author = SF.controller().get_handle();
28 |
29 | var tripdone = window.md5(author + ":" + tripcode);
30 | var self = this;
31 | if (reply) {
32 | if (reply.tripcode === tripdone) {
33 | self.$el.find(".reportable").fadeOut();
34 | self.$el.find(".editable").fadeIn().css("display", "inline-block");
35 | } else {
36 | self.$el.find(".editable").fadeOut();
37 | self.$el.find(".reportable").fadeIn().css("display", "inline-block");
38 | }
39 | }
40 |
41 | var board = SF.controller().board;
42 |
43 | SF.controller().emit("adminme", board, author, tripcode, function(isclaimed, isowner) {
44 | if (isowner) {
45 | self.$el.find(".reportable, .editable").hide();
46 | self.$el.find(".deletable").css("display", "inline-block").show();
47 |
48 | if (window._POSTS[self.reply_id]) {
49 | self.$el.find(".starrable").css("display", "inline-block").show();
50 | var post = window._POSTS[self.reply_id];
51 | if (post.is_starred()) {
52 | self.$el.find(".starrable").text("unstick");
53 | } else {
54 | self.$el.find(".starrable").text("sticky");
55 | }
56 | }
57 |
58 | }
59 |
60 | });
61 |
62 | $(document.body).append(this.$el);
63 | }
64 | };
65 |
--------------------------------------------------------------------------------
/components/delete_post_modal/events.js:
--------------------------------------------------------------------------------
1 | "use strict";
2 |
3 | module.exports = {
4 | events: {
5 | "click .delete" : "handle_delete_post",
6 | "click .ban" : "handle_ban_post",
7 | "click .star" : "handle_star_post",
8 | "click .edit" : "handle_edit_post",
9 | "click .update" : "handle_update_post",
10 | "keydown textarea" : "handle_typing"
11 | },
12 | post_to_controller: function(msg, options) {
13 | var author = this.$el.find("input[name=author]").val();
14 | var tripcode = this.$el.find("input[name=tripcode]").val();
15 |
16 | var post_id = this.options.reply_id;
17 |
18 | this.$el.find('.modal').modal('hide');
19 | SF.socket().emit(msg, _.extend({
20 | id: post_id,
21 | tripcode: tripcode,
22 | board: SF.controller().board,
23 | author: author
24 | }, options));
25 |
26 |
27 | },
28 |
29 |
30 | handle_update_post: function() {
31 | var text = this.$el.find("textarea[name=text]").val();
32 | this.post_to_controller("update_post", { text: text });
33 | },
34 |
35 | handle_ban_post: function() {
36 | this.post_to_controller("ban_post");
37 | },
38 | handle_delete_post: function() {
39 | this.post_to_controller("delete_post");
40 | },
41 |
42 | handle_star_post: function() {
43 | this.post_to_controller("star_post");
44 | },
45 | handle_typing: _.throttle(function() {
46 |
47 | // Update our preview with markdwon, too
48 | var replyInput = this.$el.find(".edit-post textarea");
49 | var reply = replyInput.val().trim();
50 | var escaped_reply = $("
").text(reply).html();
51 |
52 | var replyPreview = this.$el.find(".replypreview");
53 | if (replyPreview.is(":visible")) {
54 | replyPreview.empty();
55 | var replyContainer = $("
");
56 | replyContainer.text(escaped_reply);
57 | this.helpers['app/client/text'].format_text(replyContainer);
58 | replyPreview.append(replyContainer);
59 | }
60 |
61 |
62 | }, 100),
63 |
64 | handle_edit_post: function() {
65 | this.$el.find(".edit-post").fadeIn();
66 | this.$el.find(".edit")
67 | .text("done")
68 | .addClass("update")
69 | .removeClass("collapse")
70 | .removeClass("edit");
71 | },
72 |
73 | };
74 |
--------------------------------------------------------------------------------
/components/delete_post_modal/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "main" : "delete_post_modal",
3 | "helpers": [
4 | "app/client/text"
5 | ],
6 | "style": "delete_post_modal.css",
7 | "styles": [],
8 | "template": "delete_post_modal.html.erb"
9 | }
10 |
--------------------------------------------------------------------------------
/components/markdown_dialog/events.js:
--------------------------------------------------------------------------------
1 | "use strict";
2 |
3 | module.exports = {
4 | // Component event handling goes here
5 | // This is purposefully kept separate from
6 | // the main component file, since it has code
7 | // that is generally not relevant to the server.
8 | events: {
9 | "click" : "handle_template_click"
10 | },
11 |
12 | handle_template_click: function() {
13 | console.log(this.id, "clicked");
14 | }
15 | };
16 |
--------------------------------------------------------------------------------
/components/markdown_dialog/markdown_dialog.css:
--------------------------------------------------------------------------------
1 | // mobile devices & portrait
2 | // 200 - 767px
3 | @media @device {
4 |
5 | }
6 |
7 | // netbooks, mobile devices in landscape
8 | // laptops, desktops
9 | // 768 - 1900px
10 | @media @computer {
11 |
12 | }
13 |
14 | // huge screens
15 | // 1900px
16 | @media @large_screen {
17 |
18 | }
19 |
20 | .row {
21 | padding-top: 10px;
22 | padding-bottom: 10px;
23 | border-bottom: 1px dotted #ddd;
24 | }
25 |
26 | .modal-header,
27 | .modal-footer {
28 | border-top: 0px;
29 | border-bottom: 0px;
30 | }
31 |
32 | .tripcode .tripcolor {
33 | width: 20px;
34 | height: 20px;
35 | float: left;
36 | }
37 |
--------------------------------------------------------------------------------
/components/markdown_dialog/markdown_dialog.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | tagName: "div",
3 | className: "",
4 | defaults: {
5 | content: "default content"
6 | },
7 | initialize: function(options) {
8 | console.log("Loaded", this, options);
9 | },
10 | client: function(options) {
11 | var client_options = options.client_options;
12 |
13 | this.$el.find(".modal").modal();
14 | $(document.body).append(this.$el);
15 | }
16 | };
17 |
--------------------------------------------------------------------------------
/components/markdown_dialog/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "main" : "markdown_dialog",
3 | "style": "markdown_dialog.css",
4 | "styles": [],
5 |
6 | "template": "markdown_dialog.html.erb",
7 | "helpers": [],
8 | "no_redefine" : false
9 | }
10 |
--------------------------------------------------------------------------------
/components/markdown_dialog/test/client.js:
--------------------------------------------------------------------------------
1 | "use strict";
2 |
3 | var component_name = "TEMPLATE";
4 |
5 | describe(component_name, function() {
6 | it("builds on the client", function(done) {
7 | $C(component_name, { name: "first_button"}, function(cmp) {
8 | assert.notEqual(cmp, null);
9 | assert.notEqual(cmp.$el, null);
10 | done();
11 | });
12 | });
13 | it("renders on the client", function(done) {
14 | $C(component_name, { name: "first_button"}, function(cmp) {
15 | assert.notEqual(cmp, null);
16 | assert.notEqual(cmp.$el, null);
17 | done();
18 | });
19 | });
20 | });
21 |
--------------------------------------------------------------------------------
/components/markdown_dialog/test/server.js:
--------------------------------------------------------------------------------
1 | "use strict"
2 |
3 | var test_helper = require("superfluous").test_helper;
4 | test_helper.init();
5 |
6 | var assert = require("assert");
7 | var component = require_core("server/component");
8 |
9 | describe('TEMPLATE', function(){
10 | test_helper.setup_server(function() {
11 | component.build('TEMPLATE', {}, function(cmp) {
12 | describe('#initialize()', function(){
13 | it('should initialize the component', function(){
14 | assert.notEqual(cmp, null);
15 | });
16 | });
17 | });
18 |
19 | component.build('TEMPLATE', {}, function(cmp) {
20 | describe('#client()', function(){
21 | it('should initialize the component on the client', function(){
22 | assert.notEqual(cmp.$el.html(), null);
23 | });
24 | });
25 | });
26 | });
27 | });
28 |
--------------------------------------------------------------------------------
/components/post/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "main" : "post",
3 | "style": "post.css",
4 | "styles": [
5 | ],
6 |
7 | "template": "post.html.erb",
8 | "helpers": [
9 | "app/client/anonications",
10 | "app/client/tripcode",
11 | "app/client/text" ],
12 | "no_redefine" : false
13 | }
14 |
--------------------------------------------------------------------------------
/components/post/test/client.js:
--------------------------------------------------------------------------------
1 | "use strict";
2 |
3 | var component_name = "TEMPLATE";
4 |
5 | describe(component_name, function() {
6 | it("builds on the client", function(done) {
7 | $C(component_name, { name: "first_button"}, function(cmp) {
8 | assert.notEqual(cmp, null);
9 | assert.notEqual(cmp.$el, null);
10 | done();
11 | });
12 | });
13 | it("renders on the client", function(done) {
14 | $C(component_name, { name: "first_button"}, function(cmp) {
15 | assert.notEqual(cmp, null);
16 | assert.notEqual(cmp.$el, null);
17 | done();
18 | });
19 | });
20 | });
21 |
--------------------------------------------------------------------------------
/components/post/test/server.js:
--------------------------------------------------------------------------------
1 | "use strict"
2 |
3 | var test_helper = require("superfluous").test_helper;
4 | test_helper.init();
5 |
6 | var assert = require("assert");
7 | var component = require_core("server/component");
8 |
9 | describe('TEMPLATE', function(){
10 | test_helper.setup_server(function() {
11 | component.build('TEMPLATE', {}, function(cmp) {
12 | describe('#initialize()', function(){
13 | it('should initialize the component', function(){
14 | assert.notEqual(cmp, null);
15 | });
16 | });
17 | });
18 |
19 | component.build('TEMPLATE', {}, function(cmp) {
20 | describe('#client()', function(){
21 | it('should initialize the component on the client', function(){
22 | assert.notEqual(cmp.$el.html(), null);
23 | });
24 | });
25 | });
26 | });
27 | });
28 |
--------------------------------------------------------------------------------
/components/stats_dialog/events.js:
--------------------------------------------------------------------------------
1 | "use strict";
2 |
3 | module.exports = {
4 | // Component event handling goes here
5 | // This is purposefully kept separate from
6 | // the main component file, since it has code
7 | // that is generally not relevant to the server.
8 | events: {
9 | "click" : "handle_template_click"
10 | },
11 |
12 | handle_template_click: function() {
13 | console.log(this.id, "clicked");
14 | }
15 | };
16 |
--------------------------------------------------------------------------------
/components/stats_dialog/markdown_dialog.css:
--------------------------------------------------------------------------------
1 | // mobile devices & portrait
2 | // 200 - 767px
3 | @media @device {
4 |
5 | }
6 |
7 | // netbooks, mobile devices in landscape
8 | // laptops, desktops
9 | // 768 - 1900px
10 | @media @computer {
11 |
12 | }
13 |
14 | // huge screens
15 | // 1900px
16 | @media @large_screen {
17 |
18 | }
19 |
20 | .row {
21 | padding-top: 10px;
22 | padding-bottom: 10px;
23 | border-bottom: 1px dotted #ddd;
24 | }
25 |
26 | .modal-header,
27 | .modal-footer {
28 | border-top: 0px;
29 | border-bottom: 0px;
30 | }
31 |
32 | .tripcode .tripcolor {
33 | width: 20px;
34 | height: 20px;
35 | float: left;
36 | }
37 |
--------------------------------------------------------------------------------
/components/stats_dialog/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "main" : "stats_dialog",
3 | "style": "stats_dialog.css",
4 | "styles": [],
5 |
6 | "template": "stats_dialog.html.erb",
7 | "helpers": [],
8 | "no_redefine" : false
9 | }
10 |
--------------------------------------------------------------------------------
/components/stats_dialog/stats_dialog.html.erb:
--------------------------------------------------------------------------------
1 | <% set_default("author", ""); %>
2 | <% set_default("tripcode", ""); %>
3 | <% set_default("text", ""); %>
4 |
5 |
6 |
7 |
8 |
12 |
13 |
14 |
15 |
18 |
19 |
20 |
21 |
22 |
--------------------------------------------------------------------------------
/components/stats_dialog/stats_dialog.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | tagName: "div",
3 | className: "",
4 | defaults: {
5 | content: "default content"
6 | },
7 | initialize: function(options) {
8 | console.log("Loaded", this, options);
9 | },
10 | client: function(options) {
11 | var client_options = options.client_options;
12 |
13 | this.$el.find(".modal").modal();
14 | var iframe = $("");
15 | iframe.css({
16 | "width" : "100%",
17 | "height" : "500px",
18 | "overflow-x" : "hidden"
19 | });
20 |
21 | this.$el.find(".modal-body").append(iframe);
22 | $(document.body).append(this.$el);
23 | }
24 | };
25 |
--------------------------------------------------------------------------------
/components/template/events.js:
--------------------------------------------------------------------------------
1 | "use strict";
2 |
3 | module.exports = {
4 | // Component event handling goes here
5 | // This is purposefully kept separate from
6 | // the main component file, since it has code
7 | // that is generally not relevant to the server.
8 | events: {
9 | "click" : "handle_template_click"
10 | },
11 |
12 | handle_template_click: function() {
13 | console.log(this.id, "clicked");
14 | }
15 | };
16 |
--------------------------------------------------------------------------------
/components/template/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "main" : "TEMPLATE",
3 | "style": "TEMPLATE.css",
4 | "styles": [],
5 |
6 | "template": "TEMPLATE.html.erb",
7 | "helpers": [],
8 | "no_redefine" : false
9 | }
10 |
--------------------------------------------------------------------------------
/components/template/template.css:
--------------------------------------------------------------------------------
1 | // mobile devices & portrait
2 | // 200 - 767px
3 | @media @device {
4 |
5 | }
6 |
7 | // netbooks, mobile devices in landscape
8 | // laptops, desktops
9 | // 768 - 1900px
10 | @media @computer {
11 |
12 | }
13 |
14 | // huge screens
15 | // 1900px
16 | @media @large_screen {
17 |
18 | }
19 |
--------------------------------------------------------------------------------
/components/template/template.html.erb:
--------------------------------------------------------------------------------
1 |
2 | <% if (content) { %>
3 | <%= content %>
4 | <% } %>
5 |
6 |
--------------------------------------------------------------------------------
/components/template/template.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | tagName: "div",
3 | className: "",
4 | defaults: {
5 | content: "default content"
6 | },
7 | initialize: function(options) {
8 | console.log("Loaded", this, options);
9 | },
10 | client: function(options) {
11 | var client_options = options.client_options;
12 |
13 | console.log("Client loaded", this, client_options);
14 | }
15 | };
16 |
--------------------------------------------------------------------------------
/components/template/test/client.js:
--------------------------------------------------------------------------------
1 | "use strict";
2 |
3 | var component_name = "TEMPLATE";
4 |
5 | describe(component_name, function() {
6 | it("builds on the client", function(done) {
7 | $C(component_name, { name: "first_button"}, function(cmp) {
8 | assert.notEqual(cmp, null);
9 | assert.notEqual(cmp.$el, null);
10 | done();
11 | });
12 | });
13 | it("renders on the client", function(done) {
14 | $C(component_name, { name: "first_button"}, function(cmp) {
15 | assert.notEqual(cmp, null);
16 | assert.notEqual(cmp.$el, null);
17 | done();
18 | });
19 | });
20 | });
21 |
--------------------------------------------------------------------------------
/components/template/test/server.js:
--------------------------------------------------------------------------------
1 | "use strict"
2 |
3 | var test_helper = require("superfluous").test_helper;
4 | test_helper.init();
5 |
6 | var assert = require("assert");
7 | var component = require_core("server/component");
8 |
9 | describe('TEMPLATE', function(){
10 | test_helper.setup_server(function() {
11 | component.build('TEMPLATE', {}, function(cmp) {
12 | describe('#initialize()', function(){
13 | it('should initialize the component', function(){
14 | assert.notEqual(cmp, null);
15 | });
16 | });
17 | });
18 |
19 | component.build('TEMPLATE', {}, function(cmp) {
20 | describe('#client()', function(){
21 | it('should initialize the component on the client', function(){
22 | assert.notEqual(cmp.$el.html(), null);
23 | });
24 | });
25 | });
26 | });
27 | });
28 |
--------------------------------------------------------------------------------
/components/upeye/events.js:
--------------------------------------------------------------------------------
1 | "use strict";
2 |
3 | module.exports = {
4 | // Component event handling goes here
5 | // This is purposefully kept separate from
6 | // the main component file, since it has code
7 | // that is generally not relevant to the server.
8 | events: {
9 | "click" : "handle_template_click"
10 | },
11 |
12 | handle_template_click: function() {
13 | console.log(this.id, "clicked");
14 | }
15 | };
16 |
--------------------------------------------------------------------------------
/components/upeye/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "main" : "upeye",
3 | "style": "upeye.css",
4 | "styles": [],
5 |
6 | "template": "upeye.html.erb",
7 | "helpers": [],
8 | "no_redefine" : false
9 | }
10 |
--------------------------------------------------------------------------------
/components/upeye/test/client.js:
--------------------------------------------------------------------------------
1 | "use strict";
2 |
3 | var component_name = "TEMPLATE";
4 |
5 | describe(component_name, function() {
6 | it("builds on the client", function(done) {
7 | $C(component_name, { name: "first_button"}, function(cmp) {
8 | assert.notEqual(cmp, null);
9 | assert.notEqual(cmp.$el, null);
10 | done();
11 | });
12 | });
13 | it("renders on the client", function(done) {
14 | $C(component_name, { name: "first_button"}, function(cmp) {
15 | assert.notEqual(cmp, null);
16 | assert.notEqual(cmp.$el, null);
17 | done();
18 | });
19 | });
20 | });
21 |
--------------------------------------------------------------------------------
/components/upeye/test/server.js:
--------------------------------------------------------------------------------
1 | "use strict"
2 |
3 | var test_helper = require("superfluous").test_helper;
4 | test_helper.init();
5 |
6 | var assert = require("assert");
7 | var component = require_core("server/component");
8 |
9 | describe('TEMPLATE', function(){
10 | test_helper.setup_server(function() {
11 | component.build('TEMPLATE', {}, function(cmp) {
12 | describe('#initialize()', function(){
13 | it('should initialize the component', function(){
14 | assert.notEqual(cmp, null);
15 | });
16 | });
17 | });
18 |
19 | component.build('TEMPLATE', {}, function(cmp) {
20 | describe('#client()', function(){
21 | it('should initialize the component on the client', function(){
22 | assert.notEqual(cmp.$el.html(), null);
23 | });
24 | });
25 | });
26 | });
27 | });
28 |
--------------------------------------------------------------------------------
/components/upeye/upeye.css:
--------------------------------------------------------------------------------
1 | div[id="404-container"]{
2 | padding: 200px 0;
3 | display: block;
4 | text-align: center;
5 | margin: 0 auto;
6 | margin-top: -5px;
7 | position: relative;
8 | }
9 | div[id="404-logo"]{
10 | margin: 0 auto;
11 | position: relative;
12 | display: block;
13 | width: 300px;
14 | height: 200px;
15 | background: url('/images/atob-logo-404.png') center center no-repeat;
16 | -webkit-animation: wobble 4s infinite;
17 | animation: wobble 4s infinite;
18 | }
19 | div[id="eye"]{
20 |
21 | position: absolute;
22 | left: 50%;
23 | top: 50%;
24 | margin-left: -50px;
25 | margin-top: -50px;
26 | display: block;
27 | width: 0;
28 | height: 0;
29 | border-width: 50px;
30 | border-style: solid;
31 | border-color: #000;
32 | border-radius: 50px;
33 | -webkit-border-radius: 50px;
34 | -moz-border-radius: 50px;
35 | -o-border-radius: 50px;
36 | -ms-border-radius: 50px;
37 |
38 | }
39 | /* animations */
40 | /* Chrome, Safari, Opera */
41 | @-webkit-keyframes colorchange{
42 | 0% {background: #333;}
43 | 50% {background: #99b;}
44 | 100% {background: #333;}
45 | }
46 | /* Standard syntax */
47 | @keyframes colorchange{
48 | 0% {background: #333;}
49 | 50% {background: #cf00fc;}
50 | 100% {background: #333;}
51 | }
52 | /*wobble*/
53 | @-webkit-keyframes wobble{
54 | 0% {
55 | transform: rotate(0deg);
56 | -ms-transform: rotate(0deg);
57 | -webkit-transform: rotate(0deg);
58 | }
59 | 25% {
60 | transform: rotate(7deg);
61 | -ms-transform: rotate(7deg);
62 | -webkit-transform: rotate(7deg);
63 | animation-timing-function:linear;
64 | -webkit-animation-timing-function:linear;
65 | }
66 | 50% {
67 | transform: rotate(0deg);
68 | -ms-transform: rotate(0deg);
69 | -webkit-transform: rotate(0deg);
70 | }
71 | 75% {
72 | transform: rotate(-7deg);
73 | -ms-transform: rotate(-7deg);
74 | -webkit-transform: rotate(-7deg);
75 | animation-timing-function:linear;
76 | -webkit-animation-timing-function:linear;
77 | }
78 | 100% {
79 | transform: rotate(0deg);
80 | -ms-transform: rotate(0deg);
81 | -webkit-transform: rotate(0deg);
82 | }
83 | }
84 | @keyframes wobble{
85 | 0% {
86 | transform: rotate(0deg);
87 | -ms-transform: rotate(0deg);
88 | -webkit-transform: rotate(0deg);
89 | }
90 | 25% {
91 | transform: rotate(7deg);
92 | -ms-transform: rotate(7deg);
93 | -webkit-transform: rotate(7deg);
94 | animation-timing-function:linear;
95 | -webkit-animation-timing-function:linear;
96 | }
97 | 50% {
98 | transform: rotate(0deg);
99 | -ms-transform: rotate(0deg);
100 | -webkit-transform: rotate(0deg);
101 | }
102 | 75% {
103 | transform: rotate(-7deg);
104 | -ms-transform: rotate(-7deg);
105 | -webkit-transform: rotate(-7deg);
106 | animation-timing-function:linear;
107 | -webkit-animation-timing-function:linear;
108 | }
109 | 100% {
110 | transform: rotate(0deg);
111 | -ms-transform: rotate(0deg);
112 | -webkit-transform: rotate(0deg);
113 |
114 | }
115 | }
116 |
--------------------------------------------------------------------------------
/components/upeye/upeye.html.erb:
--------------------------------------------------------------------------------
1 | <% set_default("title", "") %>
2 |
3 |
8 |
9 | <%= title %>
10 |
--------------------------------------------------------------------------------
/components/upeye/upeye.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | tagName: "div",
3 | className: "",
4 | defaults: {
5 | content: "default content"
6 | },
7 | initialize: function(options) {
8 | },
9 | client: function(options) {
10 | function eyeLoop(){
11 | var randPosX = (Math.floor(Math.random()*100))+120;
12 | var randPosY = (Math.floor(Math.random()*50))+70;
13 | var randDelay = (Math.floor(Math.random()*1000));
14 | $('#eye').delay(randDelay).animate({
15 | left: randPosX,
16 | top: randPosY
17 | }, {
18 | duration: 1000,
19 | complete: function(){
20 | eyeLoop();
21 | }
22 | });
23 | }
24 | eyeLoop();
25 | }
26 | };
27 |
--------------------------------------------------------------------------------
/config/config.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | sockets: true,
3 | primus_transformer: "engine.io",
4 | ssl: {
5 | key: "config/certs/server.key",
6 | certificate: "config/certs/server.crt"
7 | },
8 | backend: {
9 |
10 | },
11 | authorized_users: "config/users.htpasswd",
12 | http_port: process.env.PORT || 3000,
13 | https_port: process.env.HTTPS_PORT || 3443,
14 | max_http_sockets: 1000,
15 | max_https_sockets: 1000,
16 | refresh_on_restart: true,
17 | require_https: true,
18 | use_cls: false
19 | };
20 |
--------------------------------------------------------------------------------
/config/config.json:
--------------------------------------------------------------------------------
1 | {
2 | "development": {
3 | "username": "root",
4 | "storage": "db.sqlite",
5 | "password": null,
6 | "database": "database_development",
7 | "host": "127.0.0.1",
8 | "dialect": "sqlite"
9 | },
10 | "test": {
11 | "username": "root",
12 | "storage": "db.sqlite",
13 | "password": null,
14 | "database": "database_test",
15 | "host": "127.0.0.1",
16 | "dialect": "sqlite"
17 | },
18 | "production": {
19 | "username": "root",
20 | "password": null,
21 | "database": "database_production",
22 | "storage": "db.sqlite",
23 | "host": "127.0.0.1",
24 | "dialect": "sqlite"
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/config/heroku.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | sockets: true,
3 | // setting to true will make the UDP collector and the web server run as
4 | // different processes, for stability reasons
5 | http_port: process.env.PORT || 3000,
6 | max_http_sockets: 1000,
7 | max_https_sockets: 1000,
8 | hostname: process.env.HTTPHOST || "localhost",
9 | behind_proxy: true
10 | };
11 |
--------------------------------------------------------------------------------
/config/localhost.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | hostname: 'localhost',
3 | behind_proxy: false
4 | };
5 |
--------------------------------------------------------------------------------
/config/override.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | sockets: true,
3 | behind_proxy: true,
4 | http_port: process.env.PORT || 3300,
5 | https_port: process.env.HTTP_PORT || 3343,
6 | imgur_key: "3a1eea615d14b34"
7 | };
8 |
--------------------------------------------------------------------------------
/config/users.htpasswd:
--------------------------------------------------------------------------------
1 | okay:{SHA}99xtFqyU5l2pI9xBVfRusY0Zy8k=
2 |
--------------------------------------------------------------------------------
/migrations/20140318213854-add_votes_to_posts.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | up: function(migration, DataTypes, done) {
3 | // add altering commands here, calling 'done' when finished
4 | migration.addColumn("posts", "downs", DataTypes.INTEGER);
5 | migration.addColumn("posts", "ups", DataTypes.INTEGER);
6 | done()
7 | },
8 | down: function(migration, DataTypes, done) {
9 | // add reverting commands here, calling 'done' when finished
10 | migration.removeColumn("posts", "downs");
11 | migration.removeColumn("posts", "ups");
12 | done();
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/migrations/20140318231555-add_bumped_at_to_posts.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | up: function(migration, DataTypes, done) {
3 | // add altering commands here, calling 'done' when finished
4 | migration.addColumn("posts", "bumped_at", DataTypes.DATE);
5 | migration.addIndex('posts', ['bumped_at']);
6 | done();
7 | },
8 | down: function(migration, DataTypes, done) {
9 | // add reverting commands here, calling 'done' when finished
10 | migration.removeColumn("posts", "bumped_at");
11 | done();
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/migrations/20140320182742-add_bumped_index_to_posts.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | up: function(migration, DataTypes, done) {
3 | // add altering commands here, calling 'done' when finished
4 | migration.addIndex('posts', [ 'bumped_at'], { indexName: "bumpIndex" });
5 | migration.addIndex('posts', [ 'thread_id'], { indexName: "threadIndex" });
6 | migration.addIndex('posts', [ 'parent_id'], { indexName: "parentIndex" });
7 | migration.addIndex('posts', [ 'board_id'], { indexName: "boardIndex" });
8 | done()
9 | },
10 | down: function(migration, DataTypes, done) {
11 | // add reverting commands here, calling 'done' when finished
12 | migration.removeIndex('posts', [ 'bumped_at'], { indexName: "bumpIndex" });
13 | migration.removeIndex('posts', [ 'thread_id'], { indexName: "threadIndex" });
14 | migration.removeIndex('posts', [ 'parent_id'], { indexName: "parentIndex" });
15 | migration.removeIndex('posts', [ 'board_id'], { indexName: "boardIndex" });
16 | done()
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/migrations/20140320213029-add_bans_table.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | up: function(migration, DataTypes, done) {
3 | // add altering commands here, calling 'done' when finished
4 | migration.createTable('Bans', {
5 | id: DataTypes.INTEGER,
6 | ip: DataTypes.STRING,
7 | from: DataTypes.STRING,
8 | browser: DataTypes.STRING,
9 | tripcode: DataTypes.STRING,
10 | reason_id: DataTypes.INTEGER,
11 | created_at: DataTypes.DATE,
12 | updated_at: DataTypes.DATE,
13 | post_id: DataTypes.INTEGER,
14 | hours: DataTypes.INTEGER,
15 | board: DataTypes.STRING
16 | });
17 | done();
18 | },
19 | down: function(migration, DataTypes, done) {
20 | // add reverting commands here, calling 'done' when finished
21 | migration.dropTable('Bans');
22 | done();
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/migrations/20140320214332-add_ips_table.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | up: function(migration, DataTypes, done) {
3 | // add altering commands here, calling 'done' when finished
4 | migration.createTable('IPs', {
5 | id: DataTypes.INTEGER,
6 | ip: DataTypes.STRING,
7 | browser: DataTypes.STRING,
8 | post_id: DataTypes.INTEGER,
9 | created_at: DataTypes.DATE,
10 | updated_at: DataTypes.DATE
11 | });
12 | done();
13 | },
14 | down: function(migration, DataTypes, done) {
15 | // add reverting commands here, calling 'done' when finished
16 | migration.dropTable('ips');
17 | done();
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/migrations/20140410104732-add_archives_table.js:
--------------------------------------------------------------------------------
1 | var Sequelize = require("sequelize");
2 | var archive = new Sequelize('database', 'username', 'password', {
3 | dialect: 'sqlite',
4 | storage: 'ab.sqlite',
5 | define: {
6 | sync: { force: true },
7 | underscored: true
8 | }
9 | });
10 |
11 | module.exports = {
12 | up: function(migration, DataTypes, done) {
13 | // add altering commands here, calling 'done' when finished
14 | var old_migrator = migration.migrator.sequelize;
15 | migration.migrator.sequelize = archive;
16 | migration.queryInterface.sequelize = archive;
17 |
18 | migration.createTable('ArchivedPosts', {
19 | id: DataTypes.INTEGER,
20 | board_id: DataTypes.STRING,
21 | created_at: DataTypes.DATE,
22 | updated_at: DataTypes.DATE,
23 | title: DataTypes.STRING,
24 | text: DataTypes.TEXT,
25 | thread_id: DataTypes.INTEGER,
26 | parent_id: DataTypes.INTEGER,
27 | tripcode: DataTypes.STRING,
28 | author: DataTypes.STRING,
29 | replies: DataTypes.INTEGER,
30 | downs: DataTypes.INTEGER,
31 | ups: DataTypes.INTEGER,
32 | bumped_at: DataTypes.DATE
33 | }, {
34 | storage: 'ab.sqlite',
35 | dialect: 'sqlite'
36 | });
37 |
38 | migration.migrator.sequelize = old_migrator;
39 | migration.queryInterface.sequelize = old_migrator;
40 | done();
41 | },
42 | down: function(migration, DataTypes, done) {
43 | // add reverting commands here, calling 'done' when finished
44 | var old_migrator = migration.migrator.sequelize;
45 | migration.migrator.sequelize = archive;
46 | migration.queryInterface.sequelize = archive;
47 | migration.dropTable("ArchivedPosts", { storage: 'ab.sqlite' });
48 | migration.migrator.sequelize = old_migrator;
49 | migration.queryInterface.sequelize = old_migrator;
50 | done();
51 | }
52 | }
53 |
--------------------------------------------------------------------------------
/migrations/20140520201926-add_links_table.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | up: function(migration, DataTypes, done) {
3 | // add altering commands here, calling 'done' when finished
4 | // add altering commands here, calling 'done' when finished
5 | migration.createTable('Links', {
6 | id: { type: DataTypes.INTEGER, primaryKey: true },
7 | ip: DataTypes.STRING,
8 | ups: DataTypes.INTEGER,
9 | href: DataTypes.STRING,
10 | title: DataTypes.STRING,
11 | downs: DataTypes.INTEGER,
12 | image: DataTypes.BOOLEAN,
13 | author: DataTypes.STRING,
14 | tripcode: DataTypes.STRING,
15 | created_at: DataTypes.DATE,
16 | updated_at: DataTypes.DATE,
17 | post_id: DataTypes.INTEGER,
18 | board: DataTypes.STRING
19 | });
20 | done()
21 | },
22 | down: function(migration, DataTypes, done) {
23 | // add reverting commands here, calling 'done' when finished
24 | migration.dropTable('Links');
25 | done()
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/migrations/20150304120553-add_burtles_to_post.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | up: function(migration, DataTypes, done) {
3 | // add altering commands here, calling 'done' when finished
4 | migration.addColumn("posts", "burtles", DataTypes.INTEGER);
5 | done()
6 | },
7 | down: function(migration, DataTypes, done) {
8 | // add reverting commands here, calling 'done' when finished
9 | migration.removeColumn("posts", "burtles");
10 | done()
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/migrations/20150416212010-add_actions_table.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | up: function(migration, DataTypes, done) {
3 | // add altering commands here, calling 'done' when finished
4 | migration.createTable('Actions', {
5 | id: { type: DataTypes.INTEGER, primaryKey: true, autoIncrement: true },
6 | object: DataTypes.STRING,
7 | actor: DataTypes.STRING,
8 | action: DataTypes.STRING,
9 | count: DataTypes.INTEGER,
10 | created_at: DataTypes.DATE,
11 | updated_at: DataTypes.DATE
12 | });
13 |
14 | done();
15 | },
16 | down: function(migration, DataTypes, done) {
17 | // add reverting commands here, calling 'done' when finished
18 | migration.dropTable('Actions');
19 | done()
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/migrations/20151005130358-add_board_config_table.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | up: function(migration, DataTypes, done) {
3 | // add altering commands here, calling 'done' when finished
4 | migration.createTable('BoardConfigs', {
5 | board_id: { type: DataTypes.STRING, primaryKey: true},
6 | config: DataTypes.TEXT,
7 | author: DataTypes.STRING,
8 | tripcode: DataTypes.STRING,
9 | created_at: DataTypes.DATE,
10 | updated_at: DataTypes.DATE
11 | });
12 | done();
13 | },
14 | down: function(migration, DataTypes, done) {
15 | // add reverting commands here, calling 'done' when finished
16 | migration.dropTable('BoardConfigs');
17 | done()
18 | }
19 | };
20 |
--------------------------------------------------------------------------------
/migrations/20151005132417-add_board_claims_table.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | up: function(migration, DataTypes, done) {
3 | // add altering commands here, calling 'done' when finished
4 | migration.createTable('BoardClaims', {
5 | board_id: { type: DataTypes.STRING },
6 | id: { type: DataTypes.INTEGER, primaryKey: true, autoIncrement: true},
7 | accepted: DataTypes.BOOLEAN,
8 | author: DataTypes.STRING,
9 | tripcode: DataTypes.STRING,
10 | created_at: DataTypes.DATE,
11 | updated_at: DataTypes.DATE
12 | });
13 | done();
14 | },
15 | down: function(migration, DataTypes, done) {
16 | // add reverting commands here, calling 'done' when finished
17 | migration.dropTable('BoardClaims');
18 | done()
19 | }
20 | };
21 |
--------------------------------------------------------------------------------
/migrations/20151114192529-add_trophies_table.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | up: function(migration, DataTypes, done) {
3 | // add altering commands here, calling 'done' when finished
4 | migration.createTable('Trophies', {
5 | id: { type: DataTypes.INTEGER, primaryKey: true, autoIncrement: true },
6 | trophy: DataTypes.STRING,
7 | actor: DataTypes.STRING,
8 | anon: DataTypes.STRING,
9 | post_id: DataTypes.INTEGER,
10 | created_at: DataTypes.DATE,
11 | updated_at: DataTypes.DATE
12 | });
13 |
14 | done();
15 | },
16 | down: function(migration, DataTypes, done) {
17 | // add reverting commands here, calling 'done' when finished
18 | migration.dropTable('Trophies');
19 | done()
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/migrations/20151116104703-add_anon_id_to_trophies.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | up: function(migration, DataTypes, done) {
3 | // add altering commands here, calling 'done' when finished
4 | migration.addColumn("trophies", "anon_id", DataTypes.INTEGER);
5 | done();
6 | },
7 | down: function(migration, DataTypes, done) {
8 | // add reverting commands here, calling 'done' when finished
9 | migration.removeColumn("trophies", "anon_id");
10 | done();
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "atob.sf",
3 | "description": "another superfluous app",
4 | "version": "0.0.1",
5 | "private": true,
6 | "dependencies": {
7 | "Faker": "^0.7.0",
8 | "body-parser": "x",
9 | "cheerio": "^0.13.1",
10 | "escape-html": "^1.0.1",
11 | "rss": "x",
12 | "sentiment": "x",
13 | "sequelize": "^1.7.0",
14 | "sqlite3": "^4.0.0",
15 | "superfluous": "x",
16 | "ua-parser": "x"
17 | },
18 | "scripts": {
19 | "test": "bash scripts/run_tests.sh",
20 | "start": "node app.js"
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/routes.json:
--------------------------------------------------------------------------------
1 | {
2 | "/b/" : "boards",
3 | "/d/" : "data",
4 | "/": "home",
5 | "/a/" : "archives",
6 | "/p/" : "posts",
7 | "/u/" : "profiles",
8 | "/r/" : "rss",
9 | "/s/" : "search"
10 | }
11 |
--------------------------------------------------------------------------------
/scripts/add_user.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | EXPECTED_ARGS=1
4 |
5 | if [ $# -ne $EXPECTED_ARGS ]; then
6 | echo "Usage: ./$0 "
7 | exit
8 | fi
9 |
10 | # WE NEED THE HTPASSWD COMMAND
11 | if [ -e config/users.htpasswd ]; then
12 | htpasswd -s config/users.htpasswd $1
13 | else
14 | htpasswd -s -c config/users.htpasswd $1
15 | fi
16 |
--------------------------------------------------------------------------------
/scripts/clean_old_posts.sh:
--------------------------------------------------------------------------------
1 | NOW=`date +"%Y-%m-%d-%R"`
2 | mkdir bak 2> /dev/null
3 | cp db.sqlite bak/${NOW}.sqlite
4 | /usr/bin/node scripts/garbage_collection.js >> garbage_collector.log
5 | echo "vacuum;" | /usr/bin/sqlite3 db.sqlite
6 |
--------------------------------------------------------------------------------
/scripts/create_component.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | COMPONENT="${1}"
4 |
5 | mkdir components/${COMPONENT}
6 | cp components/template/* components/${COMPONENT} -R
7 |
8 | cd components/${COMPONENT}
9 | rename s/template/${COMPONENT}/ *
10 | sed -i "s/TEMPLATE/${COMPONENT}/g" package.json
11 | sed -i "s/TEMPLATE/${COMPONENT}/g" *.js
12 |
--------------------------------------------------------------------------------
/scripts/create_partial.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | if [[ $# == 0 ]] ; then
4 | echo "Usage: $) [controller] partial"
5 | echo ""
6 | echo "Creates a new partial under the helper (or controller) directory with"
7 | echo "name partial.html.erb"
8 | fi
9 |
10 | CONTROLLER=""
11 | if [[ $# == 1 ]] ; then
12 | PARTIAL="$1"
13 | DEST=app/static/templates/helpers/${CONTROLLER}/
14 | fi
15 |
16 | if [[ $# == 2 ]] ; then
17 |
18 | CONTROLLER="$1"
19 | PARTIAL="$2"
20 |
21 | DEST=app/static/templates/partials/${CONTROLLER}/
22 | fi
23 |
24 | mkdir -p $DEST
25 | cat > ${DEST}/${PARTIAL}.html.erb << TEMPLATE
26 |
27 | A partial named marshall
28 |
29 | Maybe it should be replaced by something else?
30 |
31 | TEMPLATE
32 |
33 | echo "Created partial ${DEST}/${PARTIAL}.html.erb"
34 |
--------------------------------------------------------------------------------
/scripts/create_react_component.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | COMPONENT="${1}"
4 |
5 | mkdir react/${COMPONENT}
6 | cp react/react_template/* react/${COMPONENT}
7 |
8 | cd react/${COMPONENT}
9 | rename s/template/${COMPONENT}/ *
10 | sed -i "s/TEMPLATE/${COMPONENT}/g" package.json
11 | sed -i "s/TEMPLATE/${COMPONENT}/g" *.js
12 |
--------------------------------------------------------------------------------
/scripts/garbage_collection.js:
--------------------------------------------------------------------------------
1 | var superfluous = require("superfluous");
2 |
3 | var Post = require_app("models/post");
4 | var ArchivedPost = require_app("models/archived_post");
5 | var Action = require_app("models/action");
6 | var Board = require_app("models/board");
7 | var BoardConfig = require_app("models/board_config");
8 | var IP = require_app("models/ip");
9 | var HIDDEN_BOARDS = require_app("server/hidden_boards");
10 |
11 | var MAX_POSTS = 30;
12 | var ONE_WEEK = (1000 * 60 * 60 * 24 * 7);
13 |
14 | function collect_garbage() {
15 | var one_week_ago = new Date(+new Date() - ONE_WEEK);
16 | var one_month_ago = new Date(+new Date() - ONE_WEEK * 4);
17 | IP.destroy({
18 | created_at: {
19 | lt: one_week_ago.toISOString()
20 | }
21 | });
22 |
23 | // For now, we keep actions in the DB until they get to be too much
24 | // Action.destroy({
25 | // updated_at: {
26 | // lt: one_month_ago.toISOString()
27 | // }
28 | // });
29 |
30 | // Destroy all posts on chat board older than one month
31 | Post.destroy({
32 | created_at: {
33 | lt: one_month_ago.toISOString()
34 | },
35 | board_id: {
36 | eq: "chat"
37 | }
38 | });
39 |
40 |
41 | // Destroy all posts past the board limits
42 | Post.findAll({
43 | where: {
44 | thread_id: null,
45 | parent_id: null
46 | }
47 | }).success(function(posts) {
48 | var by_board = _.groupBy(posts, function(b) {
49 | return b.board_id;
50 | });
51 |
52 | _.each(by_board, function(val, key) {
53 | // let's not clean up any hidden boards for now
54 | if (_.contains(HIDDEN_BOARDS, key)) {
55 | console.log("SKIPPING BOARD", key, val.length);
56 | return;
57 | }
58 |
59 |
60 | BoardConfig.find({where: {board_id: key }}).success(function(board_config) {
61 | if (val.length > MAX_POSTS) {
62 | console.log("BOARD HAS TOO MANY POSTS", key, val.length);
63 | var sorted = _.sortBy(val, function(v) {
64 | return -v.bumped_at || -v.updated_at;
65 | });
66 |
67 | var keep_posts = sorted.slice(0, MAX_POSTS);
68 | var delete_posts = sorted.slice(MAX_POSTS, sorted.length);
69 |
70 | console.log("TO KEEP POSTS", keep_posts.length);
71 |
72 | _.each(delete_posts, function(post) {
73 | if (board_config && board_config.getSetting("starred") === post.id) {
74 | console.log("SAVING STARRED POST", post);
75 | return;
76 | }
77 | var archive = false;
78 | if ((post.replies - post.downs) > 50 || post.ups > 10) {
79 | console.log(post.replies, post.ups);
80 | archive = true;
81 | ArchivedPost.findOrCreate({ id: post.id }, post.dataValues);
82 | }
83 |
84 | post.destroy();
85 | post.getChildren().success(function(children) {
86 | console.log("CHILDREN IDS ARE", _.map(children, function(p) { return p.id; }));
87 |
88 | if (archive) {
89 | _.each(children, function(p) {
90 | ArchivedPost.findOrCreate({ id: p.id }, p.dataValues);
91 | });
92 | }
93 |
94 | Post.destroy({
95 | parent_id: post.id
96 | });
97 | });
98 |
99 | });
100 |
101 | }
102 |
103 | });
104 |
105 | });
106 | });
107 | }
108 |
109 | module.exports = {
110 | collect_garbage: collect_garbage
111 | };
112 |
113 |
114 | collect_garbage();
115 |
--------------------------------------------------------------------------------
/scripts/generate_unit_tests.sh:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 |
3 | TEST_PATH=${1}
4 | FILES_IN_PATH="/bin/ls ${TEST_PATH}"
5 | FILES=`${FILES_IN_PATH}`
6 |
7 | mkdir -p ${TEST_PATH}/test > /dev/null
8 |
9 | for f in ${FILES}; do
10 | echo $f | grep "\.js" > /dev/null
11 | is_js=$?
12 | test_path="${TEST_PATH}/test/${f}"
13 |
14 | test -f $test_path
15 | test_exists=$?
16 | if [[ $test_exists != 0 ]]; then
17 | if [[ $is_js == 0 ]]; then
18 | echo "Creating test for $f"
19 | node core/server/test_helper.js ${TEST_PATH}/${f} > ${test_path}
20 | fi
21 | else
22 | echo "Test already exists for $f, not creating test file";
23 | fi
24 | done
25 |
26 |
--------------------------------------------------------------------------------
/scripts/grab_all_archives.sh:
--------------------------------------------------------------------------------
1 | cp db.sqlite db.sqlite.bak
2 |
3 | for i in bak/*.sqlite; do
4 | echo $i;
5 | cp $i db.sqlite
6 | node scripts/garbage_collection.js
7 | done
8 |
9 | cp bak/003.bak db.sqlite
10 | node scripts/garbage_collection.js
11 |
12 | cp db.sqlite.bak db.sqlite
13 |
14 | rm db.sqlite.bak
15 |
--------------------------------------------------------------------------------
/scripts/ip_conversion.js:
--------------------------------------------------------------------------------
1 | var superfluous = require("superfluous");
2 |
3 | var IP = require_app("models/ip");
4 |
5 | function translate_ips() {
6 | var count = 0;
7 | IP.findAll({}).success(function(ips) {
8 | _.each(ips, function(ip) {
9 | if (ip.ip.indexOf(".") !== -1) {
10 | console.log("IP", ip.ip, "TO", IP.toHash(ip.ip), ip.id);
11 | ip.ip = IP.toHash(ip.ip);
12 | count += 1;
13 | IP.update( {ip: ip.ip}, { post_id: ip.post_id });
14 | }
15 | });
16 |
17 | console.log("Translated", count, "IPS to hashes");
18 |
19 | });
20 | }
21 |
22 |
23 | module.exports = {
24 | translate_ips: translate_ips
25 | };
26 |
27 |
28 | translate_ips();
29 |
--------------------------------------------------------------------------------
/scripts/link_collection.js:
--------------------------------------------------------------------------------
1 | var superfluous = require("superfluous");
2 | var post_links = require_app("server/post_links");
3 | var Post = require_app("models/post");
4 |
5 | function collect_links() {
6 | Post.findAll({ }).success(function(posts) {
7 | _.each(posts, function(post) {
8 | post_links.find_and_create_links(post);
9 | });
10 | });
11 | }
12 |
13 | module.exports = {
14 | collect_links: collect_links
15 | };
16 |
17 |
18 | collect_links();
19 |
--------------------------------------------------------------------------------
/scripts/pocket_collection.js:
--------------------------------------------------------------------------------
1 | var superfluous = require("superfluous");
2 | var anon_pocket = require_app("server/anon_pocket");
3 | var Post = require_app("models/post");
4 |
5 | function collect_links() {
6 | Post.findAll({ }).success(function(posts) {
7 | _.each(posts, function(post) {
8 | anon_pocket.find_and_create_items(post);
9 | });
10 | });
11 | }
12 |
13 | module.exports = {
14 | collect_links: collect_links
15 | };
16 |
17 |
18 | collect_links();
19 |
--------------------------------------------------------------------------------
/scripts/run_client_tests.sh:
--------------------------------------------------------------------------------
1 | FAIL=0
2 |
3 | which phantomjs > /dev/null
4 | HAS_PHANTOMJS=$?
5 |
6 | which mocha-phantomjs > /dev/null
7 | HAS_MOCHA_PHANTOM=$?
8 |
9 | if [[ $HAS_PHANTOMJS == 0 && $HAS_MOCHA_PHANTOM == 0 ]]; then
10 | echo "Starting server for client tests on 4200"
11 | HTTPS_PORT=4300 PORT=4200 node app > /dev/null &
12 | PID=$!
13 | sleep 1
14 | echo "Running client tests"
15 | mocha-phantomjs http://localhost:4200/tester || FAIL=1
16 | kill $PID
17 | else
18 | echo "WARNING: Couldn't find phantomjs, skipping client tests"
19 | fi
20 |
21 | exit $FAIL
22 |
--------------------------------------------------------------------------------
/scripts/run_component_tests.sh:
--------------------------------------------------------------------------------
1 | FAIL=0
2 | for component in components/*; do
3 | if test -f ${component}/test/server.js; then
4 | if [[ ${component} == "components/template" ]]; then
5 | continue;
6 | fi
7 |
8 | echo "Running component tests for $component"
9 | mocha ${component}/test/server || FAIL=1;
10 |
11 | fi
12 | done
13 | exit $FAIL
14 |
--------------------------------------------------------------------------------
/scripts/run_controller_tests.sh:
--------------------------------------------------------------------------------
1 | FAIL=0
2 | for controller in app/controllers/*; do
3 | echo "Running controller tests for $controller"
4 | mocha $controller/test/server || FAIL=1;
5 | done
6 | exit $FAIL
7 |
--------------------------------------------------------------------------------
/scripts/run_tests.sh:
--------------------------------------------------------------------------------
1 | FAIL=0
2 | bash scripts/run_controller_tests.sh || FAIL=1
3 | bash scripts/run_component_tests.sh || FAIL=1
4 | bash scripts/run_unit_tests.sh || FAIL=1
5 | bash scripts/run_client_tests.sh || FAIL=1
6 | exit $FAIL
7 |
--------------------------------------------------------------------------------
/scripts/run_unit_tests.sh:
--------------------------------------------------------------------------------
1 | FAIL=0
2 |
3 | echo "Running core unit tests"
4 | mocha core/server/test || FAIL=1
5 | echo "Running app unit tests"
6 | mocha app/server/test || FAIL=1
7 | exit $FAIL
8 |
--------------------------------------------------------------------------------
/scripts/setup_certificates.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | CONFIG_DIR="$(dirname "$0")"
4 | mkdir -p $CONFIG_DIR/../config/certs && cd $CONFIG_DIR/../config/certs
5 | echo `pwd`
6 | openssl genrsa -passout "pass:foozle" -des3 -out server.key 1024
7 | openssl rsa -in server.key -passin "pass:foozle" -out server.key.insecure
8 | mv server.key server.key.secure && mv server.key.insecure server.key
9 | openssl req -new -key server.key -out server.csr -subj "/C=US/ST=CA/L=San Francisco/O=Snorkel/OU=Engineering/CN=localhost"
10 | openssl x509 -req -days 365 -in server.csr -signkey server.key -out server.crt
11 |
--------------------------------------------------------------------------------