kablam 0.0.4 → 0.0.8
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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
|