diff --git a/css/glowingbear.css b/css/glowingbear.css index 46e1243..a6d10d8 100644 --- a/css/glowingbear.css +++ b/css/glowingbear.css @@ -84,6 +84,25 @@ input[type=text], input[type=password], #sendMessage { margin-bottom: 5px !important; } +.btn-send-image { + position: relative; + overflow: hidden; + cursor: pointer; +} + +.imgur-upload { + position: absolute; + bottom: 0; + right: 0; + cursor: inherit; + font-size: 1000px !important; + height: 300px; + margin: 0; + padding: 0; + opacity: 0; + filter: ~"alpha(opacity=0)"; +} + .input-group { width: 100%; } @@ -157,6 +176,14 @@ input[type=text], input[type=password], #sendMessage { padding-right: 6px; } +.upload-error { + width: 100%; + position: absolute; + top: 0; + left: 0; + z-index: 4; +} + #sidebar { position: fixed; width: 140px; @@ -330,6 +357,25 @@ td.time { padding-right: 100px; } +#inputform { + position: relative; +} + +#imgur-upload-progress { + width: 100%; + height: auto; + position: absolute; + bottom: 100%; + left: 0; +} + + .imgur-progress-bar { + width: 0%; + height: 5px; + margin-top: 1px; + background: #428BCA; + } + /* fix for mobile firefox which ignores :hover */ .nav-pills > li > a:active, .nav-pills > li > a:active span { text-decoration: none; diff --git a/css/themes/dark.css b/css/themes/dark.css index 9e196f7..f219459 100644 --- a/css/themes/dark.css +++ b/css/themes/dark.css @@ -72,12 +72,13 @@ input[type=text], input[type=password], #sendMessage, .badge { box-shadow: 0px 1px 0px rgba(255, 255, 255, 0.1), 0px 1px 7px 0px rgba(0, 0, 0, 0.8) inset; } -input[type=text], input[type=password], #sendMessage, .badge, .btn-send { +input[type=text], input[type=password], #sendMessage, .badge, .btn-send, .btn-send-image { color: #ccc; background: none repeat scroll 0% 0% rgba(0, 0, 0, 0.3); } -.btn-send:hover, .btn-send:focus { +.btn-send:hover, .btn-send:focus, +.btn-send-image:hover, .btn-send-image:focus { background-color: #555; color: white; } @@ -271,7 +272,7 @@ input[type=text], input[type=password], #sendMessage, .badge, .btn-send { .cob-chat { } .cob-chat_time { - color: #999; + color: #999; } .cob-chat_time_delimiters { } diff --git a/css/themes/light.css b/css/themes/light.css index b292136..6a5c109 100644 --- a/css/themes/light.css +++ b/css/themes/light.css @@ -22,7 +22,8 @@ html { background-color: #222; } -.btn-send { +.btn-send, +.btn-send-image, { background: none repeat scroll 0% 0% rgba(255, 255, 255, 0.3); color: #428BCA; } @@ -257,7 +258,7 @@ select.form-control, select option, input[type=text], input[type=password], #sen .cob-chat { } .cob-chat_time { - color: #999; + color: #999; } .cob-chat_time_delimiters { } diff --git a/directives/input.html b/directives/input.html index 9b23817..38a76f7 100644 --- a/directives/input.html +++ b/directives/input.html @@ -1,9 +1,14 @@ -
+
+
+
diff --git a/index.html b/index.html index fa71eb2..223280e 100644 --- a/index.html +++ b/index.html @@ -33,14 +33,20 @@ + + + +
+

Upload error: Image upload failed.

+

logo @@ -257,7 +263,7 @@ $ openssl req -nodes -newkey rsa:4096 -keyout relay.pem -x509 -days 365 -out rel

-
+
  • diff --git a/js/file-change.js b/js/file-change.js new file mode 100644 index 0000000..1b22359 --- /dev/null +++ b/js/file-change.js @@ -0,0 +1,23 @@ +(function() { +'use strict'; + +var weechat = angular.module('weechat'); + +weechat.directive('fileChange', ['$parse', function($parse) { + + return { + restrict: 'A', + link: function ($scope, element, attrs) { + var attrHandler = $parse(attrs.fileChange); + var handler = function (e) { + $scope.$apply(function () { + attrHandler($scope, { $event: e, files: e.target.files }); + }); + }; + element[0].addEventListener('change', handler, false); + } + }; + + }]); + +})(); diff --git a/js/imgur-drop-directive.js b/js/imgur-drop-directive.js new file mode 100644 index 0000000..7e114d0 --- /dev/null +++ b/js/imgur-drop-directive.js @@ -0,0 +1,49 @@ +(function() { +'use strict'; + +var weechat = angular.module('weechat'); + +weechat.directive('imgurDrop', ['connection','imgur','$rootScope', function(connection, imgur, $rootScope) { + return { + restrict: 'A', + link: function($scope, element, attr) { + var elem = element[0]; + elem.ondragover = function () { this.classList.add('imgur-drop-hover'); return false; }; + elem.ondragend = function () { this.classList.remove('imgur-drop-hover'); return false; }; + elem.ondrop = function(e) { + // Remove hover class + this.classList.remove('imgur-drop-hover'); + + // Get files + var files = e.dataTransfer.files; + + // Stop default behaviour + e.stopPropagation(); + e.preventDefault(); + + // Send image url after upload + var sendImageUrl = function(imageUrl) { + + // Send image + if(imageUrl !== undefined && imageUrl !== '') { + $rootScope.insertAtCaret(String(imageUrl)); + } + + }; + + // Check files + if(typeof files !== "undefined" && files.length > 0) { + + // Loop through files + for (var i = 0; i < files.length; i++) { + // Upload to imgur + imgur.process(files[i], sendImageUrl); + } + + } + }; + } + }; +}]); + +})(); diff --git a/js/imgur.js b/js/imgur.js new file mode 100644 index 0000000..a5a0fd0 --- /dev/null +++ b/js/imgur.js @@ -0,0 +1,128 @@ +(function() { +'use strict'; + +var weechat = angular.module('weechat'); + +weechat.factory('imgur', ['$rootScope', function($rootScope) { + + var process = function(image, callback) { + + // Is it an image? + if (!image || !image.type.match(/image.*/)) return; + + // New file reader + var reader = new FileReader(); + + // When image is read + reader.onload = function (event) { + var image = event.target.result.split(',')[1]; + upload(image, callback); + }; + + // Read image as data url + reader.readAsDataURL(image); + + }; + + // Upload image to imgur from base64 + var upload = function( base64img, callback ) { + // Set client ID (Glowing Bear) + var clientId = "164efef8979cd4b"; + + // Progress bars container + var progressBars = document.getElementById("imgur-upload-progress"), + currentProgressBar = document.createElement("div"); + + // Set progress bar attributes + currentProgressBar.className='imgur-progress-bar'; + currentProgressBar.style.width = '0'; + + // Append progress bar + progressBars.appendChild(currentProgressBar); + + // Create new form data + var fd = new FormData(); + fd.append("image", base64img); // Append the file + fd.append("type", "base64"); // Set image type to base64 + + // Create new XMLHttpRequest + var xhttp = new XMLHttpRequest(); + + // Post request to imgur api + xhttp.open("POST", "https://api.imgur.com/3/image", true); + + // Set headers + xhttp.setRequestHeader("Authorization", "Client-ID " + clientId); + xhttp.setRequestHeader("Accept", "application/json"); + + // Handler for response + xhttp.onload = function() { + + // Remove progress bar + currentProgressBar.parentNode.removeChild(currentProgressBar); + + // Check state and response status + if(xhttp.status === 200) { + + // Get response text + var response = JSON.parse(xhttp.responseText); + + // Send link as message + if( response.data && response.data.link ) { + + if (callback && typeof(callback) === "function") { + callback(response.data.link); + } + + } else { + showErrorMsg(); + } + + } else { + showErrorMsg(); + } + + }; + + if( "upload" in xhttp ) { + + // Set progress + xhttp.upload.onprogress = function (event) { + + // Check if we can compute progress + if (event.lengthComputable) { + // Complete in percent + var complete = (event.loaded / event.total * 100 | 0); + + // Set progress bar width + currentProgressBar.style.width = complete + '%'; + } + }; + + } + + // Send request with form data + xhttp.send(fd); + + }; + + var showErrorMsg = function() { + // Show error msg + $rootScope.uploadError = true; + $rootScope.$apply(); + + // Hide after 5 seconds + setTimeout(function(){ + // Hide error msg + $rootScope.uploadError = false; + $rootScope.$apply(); + }, 5000); + }; + + return { + process: process + }; + +}]); + +})(); diff --git a/js/inputbar.js b/js/inputbar.js index e57144a..9d18506 100644 --- a/js/inputbar.js +++ b/js/inputbar.js @@ -14,11 +14,12 @@ weechat.directive('inputBar', function() { command: '=command' }, - controller: ['$rootScope', '$scope', '$element', '$log', 'connection', 'models', 'IrcUtils', 'settings', function($rootScope, + controller: ['$rootScope', '$scope', '$element', '$log', 'connection', 'imgur', 'models', 'IrcUtils', 'settings', function($rootScope, $scope, $element, //XXX do we need this? don't seem to be using it $log, connection, //XXX we should eliminate this dependency and use signals instead + imgur, models, IrcUtils, settings) { @@ -69,6 +70,49 @@ weechat.directive('inputBar', function() { }, 0); }; + $rootScope.insertAtCaret = function(toInsert) { + // caret position in the input bar + var inputNode = $scope.getInputNode(), + caretPos = inputNode.selectionStart; + + var prefix = $scope.command.substring(0, caretPos), + suffix = $scope.command.substring(caretPos, $scope.command.length); + // Add spaces if missing + if (prefix.length > 0 && prefix[prefix.length - 1] !== ' ') { + prefix += ' '; + } + if (suffix.length > 0 && suffix[0] !== ' ') { + suffix = ' '.concat(suffix); + } + $scope.command = prefix + toInsert + suffix; + + setTimeout(function() { + inputNode.focus(); + var pos = $scope.command.length - suffix.length; + inputNode.setSelectionRange(pos, pos); + // force refresh? + $scope.$apply(); + }, 0); + }; + + $scope.uploadImage = function($event, files) { + // Send image url after upload + var sendImageUrl = function(imageUrl) { + // Send image + if(imageUrl !== undefined && imageUrl !== '') { + $rootScope.insertAtCaret(String(imageUrl)); + } + }; + + if(typeof files !== "undefined" && files.length > 0) { + // Loop through files + for (var i = 0; i < files.length; i++) { + // Process image + imgur.process(files[i], sendImageUrl); + } + + } + }; // Send the message to the websocket $scope.sendMessage = function() {