kablam 0.0.4 → 0.0.8
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.DS_Store +0 -0
- data/app/assets/javascripts/kablam/ajax.js +531 -0
- data/app/assets/javascripts/kablam/forms.js +167 -0
- data/app/assets/javascripts/kablam/messaging.js +95 -0
- data/app/assets/stylesheets/kablam.scss +25 -0
- data/app/channels/chat_channel.rb +12 -0
- data/app/controllers/concerns/api_settings.rb +41 -0
- data/app/controllers/data_controller.rb +133 -0
- data/app/views/data/create.js.erb +10 -0
- data/app/views/data/destroy.js.erb +12 -0
- data/app/views/data/form.html.erb +1 -0
- data/app/views/data/undo.js.erb +2 -0
- data/app/views/data/update.js.erb +9 -0
- data/app/views/kablam_forms/_form_generator.html.erb +1 -1
- data/app/views/kablam_forms/fields/_checkbox_array.html.erb +3 -3
- data/app/views/kablam_forms/fields/_checkbox_boolean.html.erb +3 -3
- data/app/views/kablam_forms/fields/_file_upload.html.erb +2 -2
- data/app/views/kablam_forms/fields/_input.html.erb +2 -2
- data/app/views/kablam_forms/fields/_multi_inputs.html.erb +2 -2
- data/app/views/kablam_forms/fields/_pretext.html.erb +1 -1
- data/app/views/kablam_forms/fields/_select.html.erb +3 -3
- data/app/views/kablam_forms/fields/_text.html.erb +3 -3
- data/kablam.gemspec +2 -1
- data/lib/generators/kablam/USAGE +5 -2
- data/lib/generators/kablam/install_generator.rb +37 -0
- data/lib/generators/kablam/messaging_generator.rb +40 -0
- data/lib/generators/kablam/templates/chat.rb +44 -0
- data/lib/generators/kablam/templates/kablam.rb +45 -0
- data/lib/generators/kablam/templates/message.rb +53 -0
- data/lib/kablam.rb +35 -0
- data/lib/kablam/forms.rb +26 -21
- data/lib/kablam/kablam_record.rb +61 -0
- metadata +32 -2
- data/lib/generators/kablam/kablam_generator.rb +0 -12
@@ -0,0 +1,167 @@
|
|
1
|
+
function byId(e){return document.getElementById(e);}
|
2
|
+
function newEl(tag){return document.createElement(tag);}
|
3
|
+
function newTxt(txt){return document.createTextNode(txt);}
|
4
|
+
function hide(e) {
|
5
|
+
var contentId = document.getElementById(e);
|
6
|
+
contentId.style.display = "none";
|
7
|
+
}
|
8
|
+
// random hex string generator
|
9
|
+
function randHex(len) {
|
10
|
+
var maxlen = 8,
|
11
|
+
min = Math.pow(16,Math.min(len,maxlen)-1)
|
12
|
+
max = Math.pow(16,Math.min(len,maxlen)) - 1,
|
13
|
+
n = Math.floor( Math.random() * (max-min+1) ) + min,
|
14
|
+
r = n.toString(16);
|
15
|
+
while ( r.length < len ) {
|
16
|
+
r = r + randHex( len - maxlen );
|
17
|
+
}
|
18
|
+
return r;
|
19
|
+
};
|
20
|
+
|
21
|
+
function insertAndExecute(id, text) {
|
22
|
+
byId(id).innerHTML = text;
|
23
|
+
var scripts = Array.prototype.slice.call(byId(id).getElementsByTagName("script"));
|
24
|
+
for (var i = 0; i < scripts.length; i++) {
|
25
|
+
if (scripts[i].src != "") {
|
26
|
+
var tag = newEl("script");
|
27
|
+
tag.src = scripts[i].src;
|
28
|
+
document.getElementsByTagName("head")[0].appendChild(tag);
|
29
|
+
}
|
30
|
+
else {
|
31
|
+
eval(scripts[i].innerHTML);
|
32
|
+
}
|
33
|
+
}
|
34
|
+
}
|
35
|
+
|
36
|
+
function load(target, url) {
|
37
|
+
AjaxRequest.get(
|
38
|
+
{
|
39
|
+
'url':url,
|
40
|
+
'onSuccess':function(req){ insertAndExecute(target, req.responseText); }
|
41
|
+
}
|
42
|
+
);
|
43
|
+
}
|
44
|
+
|
45
|
+
var W3CDOM = (document.createElement && document.getElementsByTagName);
|
46
|
+
|
47
|
+
function initFileUploads(opt) {
|
48
|
+
if (!W3CDOM) return;
|
49
|
+
var fakeFileUpload = document.createElement('div');
|
50
|
+
fakeFileUpload.className = 'kablam_fakefile';
|
51
|
+
var upload_icon = document.createElement('i');
|
52
|
+
upload_icon.className = opt['icon'];
|
53
|
+
var input = document.createElement('input');
|
54
|
+
input.type = "text";
|
55
|
+
input.className = opt['class'];
|
56
|
+
|
57
|
+
fakeFileUpload.appendChild(upload_icon);
|
58
|
+
fakeFileUpload.appendChild(input);
|
59
|
+
|
60
|
+
var x = document.getElementsByClassName('kablam_file');
|
61
|
+
for (var i=0;i<x.length;i++) {
|
62
|
+
if (x[i].type != 'file') continue;
|
63
|
+
if (x[i].parentElement.className != 'kablam_fileinput_init') continue;
|
64
|
+
|
65
|
+
x[i].parentElement.className = 'kablam_fileinputs';
|
66
|
+
var clone = fakeFileUpload.cloneNode(true);
|
67
|
+
x[i].parentElement.appendChild(clone);
|
68
|
+
x[i].relatedElement = clone.getElementsByTagName('input')[0];
|
69
|
+
|
70
|
+
x[i].onchange = x[i].onmouseout = function () {
|
71
|
+
path = this.value
|
72
|
+
path = path.replace( /fakepath\\/, '...' );
|
73
|
+
path = path.replace('C:\\', '');
|
74
|
+
this.relatedElement.value = path;
|
75
|
+
}
|
76
|
+
}
|
77
|
+
}
|
78
|
+
|
79
|
+
function addInput(id, name, classes){
|
80
|
+
var input_set = document.getElementById(id);
|
81
|
+
var new_set = document.createElement('div');
|
82
|
+
new_set.id = randHex(12);
|
83
|
+
new_set.className = classes['group'];
|
84
|
+
|
85
|
+
var input = document.createElement('input');
|
86
|
+
input.type = "text";
|
87
|
+
input.className = classes['input'];
|
88
|
+
input.name = name;
|
89
|
+
var button = document.createElement('a');
|
90
|
+
button.className = classes['btn'];
|
91
|
+
button.setAttribute("onclick", "event.preventDefault();rmParent(this);");
|
92
|
+
var icon = document.createElement('i');
|
93
|
+
icon.className = classes['icon'];
|
94
|
+
|
95
|
+
button.appendChild(icon);
|
96
|
+
new_set.appendChild(input);
|
97
|
+
new_set.appendChild(button);
|
98
|
+
input_set.appendChild(new_set);
|
99
|
+
}
|
100
|
+
|
101
|
+
function rmParent(el){
|
102
|
+
el.parentElement.remove();
|
103
|
+
}
|
104
|
+
|
105
|
+
|
106
|
+
Object.defineProperty(HTMLElement, 'From', {
|
107
|
+
enumerable: false,
|
108
|
+
value: (function (document) {
|
109
|
+
//https://www.measurethat.net/Benchmarks/Show/2149/0/element-creation-speed
|
110
|
+
var rgx = /(\S+)=(["'])(.*?)(?:\2)|(\w+)/g;
|
111
|
+
return function CreateElementFromHTML(html) {
|
112
|
+
html = html.trim();
|
113
|
+
var bodystart = html.indexOf('>') + 1, bodyend = html.lastIndexOf('<');
|
114
|
+
var elemStart = html.substr(0, bodystart);
|
115
|
+
var innerHTML = html.substr(bodystart, bodyend - bodystart);
|
116
|
+
rgx.lastIndex = 0;
|
117
|
+
var elem = document.createElement(rgx.exec(elemStart)[4]);
|
118
|
+
var match; while ((match = rgx.exec(elemStart))) {
|
119
|
+
if (match[1] === undefined) {
|
120
|
+
elem.setAttribute(match[4], "");
|
121
|
+
} else {
|
122
|
+
elem.setAttribute(match[1], match[3]);
|
123
|
+
}
|
124
|
+
}
|
125
|
+
elem.innerHTML = innerHTML;
|
126
|
+
return elem;
|
127
|
+
};
|
128
|
+
}(window.document))
|
129
|
+
});
|
130
|
+
|
131
|
+
|
132
|
+
function submitRemove(form_id){
|
133
|
+
var form = document.getElementById(form_id);
|
134
|
+
form.addEventListener("ajax:success", function(e) {
|
135
|
+
e.preventDefault();
|
136
|
+
form.remove();
|
137
|
+
});
|
138
|
+
}
|
139
|
+
|
140
|
+
function submitToTop(form_id){
|
141
|
+
var form = document.getElementById(form_id);
|
142
|
+
form.addEventListener("ajax:success", function(e) {
|
143
|
+
e.preventDefault();
|
144
|
+
scrollToTop(500);
|
145
|
+
});
|
146
|
+
}
|
147
|
+
|
148
|
+
function scrollToTop(scrollDuration) {
|
149
|
+
const scrollHeight = window.scrollY,
|
150
|
+
scrollStep = Math.PI / ( scrollDuration / 15 ),
|
151
|
+
cosParameter = scrollHeight / 2;
|
152
|
+
var scrollCount = 0,
|
153
|
+
scrollMargin,
|
154
|
+
scrollInterval = setInterval( function() {
|
155
|
+
if ( window.scrollY != 0 ) {
|
156
|
+
scrollCount = scrollCount + 1;
|
157
|
+
scrollMargin = cosParameter - cosParameter * Math.cos( scrollCount * scrollStep );
|
158
|
+
window.scrollTo( 0, ( scrollHeight - scrollMargin ) );
|
159
|
+
}
|
160
|
+
else clearInterval(scrollInterval);
|
161
|
+
}, 15 );
|
162
|
+
}
|
163
|
+
|
164
|
+
function toggle_visibility(id) {
|
165
|
+
var e = document.getElementById(id);
|
166
|
+
e.style.display = 'none';
|
167
|
+
}
|
@@ -0,0 +1,95 @@
|
|
1
|
+
var scrollPosition;
|
2
|
+
document.addEventListener('turbolinks:load', refreshScroll());
|
3
|
+
|
4
|
+
function refreshScroll(){
|
5
|
+
if (scrollPosition) {
|
6
|
+
window.scrollTo.apply(window, scrollPosition);
|
7
|
+
scrollPosition = null;
|
8
|
+
}
|
9
|
+
}
|
10
|
+
|
11
|
+
function TurboRefresh(){
|
12
|
+
scrollPosition = [window.scrollX, window.scrollY];
|
13
|
+
Turbolinks.visit(window.location.toString(), { action: 'replace' });
|
14
|
+
}
|
15
|
+
|
16
|
+
function Sound(path) {
|
17
|
+
var audio = new Audio(path);
|
18
|
+
audio.play();
|
19
|
+
}
|
20
|
+
|
21
|
+
function TopDot(dot_id, dot_class="dot bg-officeblue"){
|
22
|
+
var topdotspan = document.getElementById(dot_id);
|
23
|
+
topdotspan.className = dot_class;
|
24
|
+
}
|
25
|
+
|
26
|
+
|
27
|
+
function RemoveChatsSub(chatid) {
|
28
|
+
var all = App.cable.subscriptions.subscriptions;
|
29
|
+
var all_chats_sub_id = all.map(function (x) {return JSON.parse(x.identifier).id});
|
30
|
+
var index_of_subed_chats = [];
|
31
|
+
all_chats_sub_id.forEach(function(item,index) {
|
32
|
+
if (chatid != undefined) {
|
33
|
+
if (item != undefined && item != chatid) {
|
34
|
+
index_of_subed_chats.push(index);
|
35
|
+
}
|
36
|
+
} else {
|
37
|
+
if (item != undefined) {
|
38
|
+
index_of_subed_chats.push(index);
|
39
|
+
}
|
40
|
+
}
|
41
|
+
});
|
42
|
+
index_of_subed_chats.forEach(function(item) {
|
43
|
+
App.cable.subscriptions.remove(all[item]);
|
44
|
+
});
|
45
|
+
}
|
46
|
+
|
47
|
+
|
48
|
+
function RemoveNotificationDot(dot_id){
|
49
|
+
var dotspan = document.getElementById(dot_id);
|
50
|
+
dotspan.className = "";
|
51
|
+
}
|
52
|
+
|
53
|
+
function UserChatSub(chat_div_id, chat_id, channel, user_id, options={}){
|
54
|
+
RemoveChatsSub();
|
55
|
+
var chatdiv = document.getElementById(chat_div_id);
|
56
|
+
|
57
|
+
App.cable.subscriptions.create({ channel: channel, id: chat_id }, {
|
58
|
+
received: function(data) {
|
59
|
+
HtmlNode(data, options);
|
60
|
+
if (data['chat']['sender_id'] != user_id) {
|
61
|
+
TopDot();
|
62
|
+
}
|
63
|
+
}
|
64
|
+
})
|
65
|
+
}
|
66
|
+
|
67
|
+
function HtmlNode(data, options={}){
|
68
|
+
options = {
|
69
|
+
outerDivClass: options.outerDivClass || "pt3 ph3 ph2-l bg-white br2 shadow1 mb3",
|
70
|
+
innerDivClass: options.innerDivClass || "dt w-100 pb2 ph2 mt2",
|
71
|
+
imgDivClass: options.imgDivClass || "dtc w2 w3-ns v-mid",
|
72
|
+
imgClass: options.imgClass || "ba b--black-10 db br-100 w2 w3-ns h2 h3-ns",
|
73
|
+
userDivClass: options.userDivClass || "dtc v-mid pl3",
|
74
|
+
userNameClass: options.userNameClass || "f6 f5-ns fw6 lh-title black mv0",
|
75
|
+
timestampClass: options.timestampClass || "f6 fw4 mt0 mb0 black-60 i",
|
76
|
+
messageDivClass: options.messageDivClass || "ph3-l pb2",
|
77
|
+
messageClass: options.messageClass || "f5 lh-copy measure pb3"
|
78
|
+
}
|
79
|
+
|
80
|
+
var new_message = document.createElement("div");
|
81
|
+
|
82
|
+
new_message.className = options.outerDivClass;
|
83
|
+
var content = data['chat'].content.replace(/(?:\r\n|\r|\n)/g, '<br>');
|
84
|
+
new_message.innerHTML = `<div class="${options.innerDivClass}">
|
85
|
+
<div class="${options.imgDivClass}">
|
86
|
+
<img src="${data['chat'].image}" class="${options.imgClass}"/>
|
87
|
+
</div>
|
88
|
+
<div class="${options.userDivClass}">
|
89
|
+
<h1 class="${options.userNameClass}">${data['chat'].username}</h1>
|
90
|
+
<h2 class="${options.timestampClass}">${data['chat'].created_at}</h2>
|
91
|
+
</div>
|
92
|
+
</div>
|
93
|
+
<div class="${options.messageDivClass}"><p class="${options.messageClass}">${content}</p></div>`;
|
94
|
+
chatdiv.insertAdjacentElement('afterbegin', new_message);
|
95
|
+
}
|
@@ -0,0 +1,25 @@
|
|
1
|
+
div.kablam_fileinputs {
|
2
|
+
position: relative;
|
3
|
+
}
|
4
|
+
|
5
|
+
div.kablam_fakefile {
|
6
|
+
position: absolute;
|
7
|
+
top: 0px;
|
8
|
+
left: 0px;
|
9
|
+
z-index: 1;
|
10
|
+
width: 100%;
|
11
|
+
height: 100%;
|
12
|
+
text-align: center;
|
13
|
+
}
|
14
|
+
|
15
|
+
input.kablam_file {
|
16
|
+
position: relative;
|
17
|
+
text-align: right;
|
18
|
+
-moz-opacity:0 ;
|
19
|
+
filter: opacity(0);
|
20
|
+
opacity: 0;
|
21
|
+
z-index: 2;
|
22
|
+
}
|
23
|
+
.hidden {
|
24
|
+
display: none;
|
25
|
+
}
|
@@ -0,0 +1,12 @@
|
|
1
|
+
class ChatChannel < ApplicationCable::Channel
|
2
|
+
def subscribed
|
3
|
+
# stream_from "some_channel"
|
4
|
+
stream_from "ChatChannel_#{params[:id]}"
|
5
|
+
|
6
|
+
puts "subscribed to chat #{params[:id]}"
|
7
|
+
end
|
8
|
+
|
9
|
+
def unsubscribed
|
10
|
+
# Any cleanup needed when channel is unsubscribed
|
11
|
+
end
|
12
|
+
end
|
@@ -0,0 +1,41 @@
|
|
1
|
+
module Concerns
|
2
|
+
module ApiSettings
|
3
|
+
extend ActiveSupport::Concern
|
4
|
+
require 'securerandom'
|
5
|
+
protected
|
6
|
+
def send_to_bucket(bucket, upload_item)
|
7
|
+
puts "uploading..."
|
8
|
+
path = ENV.fetch("BUCKET_TRANSFER_API")
|
9
|
+
bucket = ENV.fetch("BUCKET_NAME")
|
10
|
+
|
11
|
+
|
12
|
+
post_params = {"oss"=> {
|
13
|
+
"bucket" => bucket,
|
14
|
+
"user_id" => "TEST",
|
15
|
+
"attachment" => upload_item,
|
16
|
+
"encrypted_name" => SecureRandom.hex
|
17
|
+
}}
|
18
|
+
response = RestClient.post(path, post_params)
|
19
|
+
JSON.parse(response.body)["url"]
|
20
|
+
end
|
21
|
+
|
22
|
+
def slack_it(url, content)
|
23
|
+
# url must be a SLACK webhook
|
24
|
+
message =
|
25
|
+
{
|
26
|
+
"attachments": [
|
27
|
+
{
|
28
|
+
"fallback": "",
|
29
|
+
"color": "#36a64f",
|
30
|
+
"pretext": "#{content[:pretext] || '---'}",
|
31
|
+
"author_name": "#{content[:author] || '-no_author_provided-'}",
|
32
|
+
"title": "#{content[:title] || '-no_title_provided-'}",
|
33
|
+
"text": "#{content[:text] || '-no_text_provided-'}"
|
34
|
+
}
|
35
|
+
]
|
36
|
+
}.to_json
|
37
|
+
|
38
|
+
RestClient.post(url, message)
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
@@ -0,0 +1,133 @@
|
|
1
|
+
require 'rest-client'
|
2
|
+
|
3
|
+
class DataController < ApplicationController
|
4
|
+
protect_from_forgery with: :exception
|
5
|
+
before_action :set_model, only: [:create, :update, :undo, :destroy, :form]
|
6
|
+
before_action :set_object, only: [:update, :destroy]
|
7
|
+
before_action :set_undo_object, only: [:undo]
|
8
|
+
include Concerns::ApiSettings
|
9
|
+
|
10
|
+
def form
|
11
|
+
# Automatically Render Vanilla Form for use w/ JS.load
|
12
|
+
if params[:id].present?
|
13
|
+
set_object
|
14
|
+
else
|
15
|
+
@object = @model.new
|
16
|
+
set_ref
|
17
|
+
end
|
18
|
+
render layout: false
|
19
|
+
end
|
20
|
+
|
21
|
+
def create
|
22
|
+
@object = @model.new(model_params)
|
23
|
+
|
24
|
+
if @object.save
|
25
|
+
slack?(:create)
|
26
|
+
respond_to do |format|
|
27
|
+
format.js
|
28
|
+
format.html do
|
29
|
+
redirect_to request.referrer
|
30
|
+
flash[:notice] = :Created
|
31
|
+
end
|
32
|
+
end
|
33
|
+
else
|
34
|
+
# TODO: Add some failure handling 👍
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
def update
|
39
|
+
if @object.update(model_params)
|
40
|
+
slack?(:update)
|
41
|
+
respond_to do |format|
|
42
|
+
format.js
|
43
|
+
format.html do
|
44
|
+
redirect_to request.referrer
|
45
|
+
flash[:notice] = :Destroyed
|
46
|
+
end
|
47
|
+
end
|
48
|
+
else
|
49
|
+
# ADD FAILURE HANDLING
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
def destroy
|
54
|
+
if @object.destroy
|
55
|
+
slack?(:destroy)
|
56
|
+
respond_to do |format|
|
57
|
+
format.js
|
58
|
+
format.html do
|
59
|
+
redirect_to request.referrer
|
60
|
+
flash[:notice] = :Destroyed
|
61
|
+
end
|
62
|
+
end
|
63
|
+
else
|
64
|
+
# TODO: Add some failure handling 👍
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
def undo
|
69
|
+
@object.update(undo_params)
|
70
|
+
end
|
71
|
+
|
72
|
+
def slack
|
73
|
+
render status: 200, json: params['challenge']
|
74
|
+
end
|
75
|
+
|
76
|
+
private
|
77
|
+
|
78
|
+
def slack?(crud_type)
|
79
|
+
if @model.slack_hook.present? && @object.slack_message[crud_type].present?
|
80
|
+
slack_it(@model.slack_hook, @object.slack_message[crud_type])
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
def set_object
|
85
|
+
@object = @model.find(params[:id])
|
86
|
+
end
|
87
|
+
|
88
|
+
def set_undo_object
|
89
|
+
@object = @model.unscoped.where.not(destroyed_at: nil).find(params[:id])
|
90
|
+
end
|
91
|
+
|
92
|
+
def set_model
|
93
|
+
db_index = Hash[ActiveRecord::Base.connection.tables.collect{|c| [c, c.classify]}].except("schema_migrations", "ar_internal_metadata")
|
94
|
+
@model = db_index[params[:name]].constantize # "users" => User
|
95
|
+
end
|
96
|
+
|
97
|
+
def undo_params
|
98
|
+
params.require(@model.to_s.underscore.to_sym).permit :destroyed_at
|
99
|
+
end
|
100
|
+
|
101
|
+
def base_params
|
102
|
+
arr = @model.attribute_names.map { |e| e.to_sym }
|
103
|
+
arr.delete(:id)
|
104
|
+
arr.delete(:created_at)
|
105
|
+
arr.delete(:updated_at)
|
106
|
+
arr.map! do |attr|
|
107
|
+
new_val = (@model.new.send(attr) == []) ? {attr=>[]} : attr
|
108
|
+
new_val
|
109
|
+
end
|
110
|
+
params.require(@model.to_s.underscore.to_sym).permit(arr)
|
111
|
+
end
|
112
|
+
|
113
|
+
def model_params
|
114
|
+
result = base_params
|
115
|
+
upload_items = base_params.select{|k,v| v.is_a? ActionDispatch::Http::UploadedFile }
|
116
|
+
upload_items.each do |k,v|
|
117
|
+
result[k] = send_to_bucket(v)
|
118
|
+
end
|
119
|
+
result
|
120
|
+
end
|
121
|
+
|
122
|
+
def set_ref
|
123
|
+
owners = @model.reflect_on_all_associations(:belongs_to) || []
|
124
|
+
if owners.count < 1
|
125
|
+
return true
|
126
|
+
elsif owners.count == 1
|
127
|
+
foreign_key = owners.first.name.to_s+"_id"
|
128
|
+
@object.write_attribute(foreign_key, params[:ref_id])
|
129
|
+
else
|
130
|
+
p "NOT ENOUGH REF'S ACCOUNTED FOR! REWRITE FORMS??"
|
131
|
+
end
|
132
|
+
end
|
133
|
+
end
|