Merge pull request #665 from glowing-bear/feature-imgur
Feature: imgur button
This commit is contained in:
commit
8a7a173cb7
9 changed files with 311 additions and 8 deletions
|
@ -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;
|
||||
|
|
|
@ -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 {
|
||||
}
|
||||
|
|
|
@ -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 {
|
||||
}
|
||||
|
|
|
@ -1,9 +1,14 @@
|
|||
<form class="form form-horizontal" id="inputform" ng-submit="sendMessage()">
|
||||
<form class="form form-horizontal" id="inputform" ng-submit="sendMessage()" imgur-drop>
|
||||
<div class="input-group">
|
||||
<textarea id="{{inputId}}" class="form-control favorite-font" ng-trim="false" rows="1" ng-change="inputChanged()" autocomplete="on" ng-model="command" ng-focus="hideSidebar()">
|
||||
</textarea>
|
||||
<span class="input-group-btn">
|
||||
<label class="btn btn-send-image unselectable" for="imgur-upload" title="Send image">
|
||||
<i class="glyphicon glyphicon-picture"></i>
|
||||
<input type="file" accept="image/*" multiple title="Send image" id="imgur-upload" class="imgur-upload" file-change="uploadImage($event, files)">
|
||||
</label>
|
||||
<button class="btn btn-send unselectable"><i class="glyphicon glyphicon-send"></i></button>
|
||||
</span>
|
||||
</div>
|
||||
<div id="imgur-upload-progress"></div>
|
||||
</form>
|
||||
|
|
|
@ -33,14 +33,20 @@
|
|||
<script type="text/javascript" src="js/filters.js"></script>
|
||||
<script type="text/javascript" src="js/handlers.js"></script>
|
||||
<script type="text/javascript" src="js/connection.js"></script>
|
||||
<script type="text/javascript" src="js/file-change.js"></script>
|
||||
<script type="text/javascript" src="js/imgur-drop-directive.js"></script>
|
||||
<script type="text/javascript" src="js/inputbar.js"></script>
|
||||
<script type="text/javascript" src="js/plugin-directive.js"></script>
|
||||
<script type="text/javascript" src="js/websockets.js"></script>
|
||||
<script type="text/javascript" src="js/models.js"></script>
|
||||
<script type="text/javascript" src="js/plugins.js"></script>
|
||||
<script type="text/javascript" src="js/imgur.js"></script>
|
||||
<script type="text/javascript" src="3rdparty/favico-0.3.5.min.js"></script>
|
||||
</head>
|
||||
<body ng-controller="WeechatCtrl" ng-keydown="handleKeyPress($event)" ng-keyup="handleKeyRelease($event)" ng-keypress="handleKeyPress($event)" ng-class="{'no-overflow': connected}" ng-init="init()" lang="en-US">
|
||||
<div class="alert alert-danger upload-error" ng-show="uploadError">
|
||||
<p><strong>Upload error:</strong> Image upload failed.</p>
|
||||
</div>
|
||||
<div ng-hide="connected" class="container">
|
||||
<h2>
|
||||
<img alt="logo" src="assets/img/glowing-bear.svg">
|
||||
|
@ -257,7 +263,7 @@ $ openssl req -nodes -newkey rsa:4096 -keyout relay.pem -x509 -days 365 -out rel
|
|||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div id="bufferlines" class="favorite-font" ng-swipe-right="showSidebar()" ng-swipe-left="hideSidebar()" ng-class="{'withnicklist': showNicklist}">
|
||||
<div id="bufferlines" class="favorite-font" ng-swipe-right="showSidebar()" ng-swipe-left="hideSidebar()" ng-class="{'withnicklist': showNicklist}" imgur-drop>
|
||||
<div id="nicklist" ng-if="showNicklist" ng-swipe-right="closeNick()" class="vertical-line-left">
|
||||
<ul class="nicklistgroup list-unstyled" ng-repeat="group in nicklist">
|
||||
<li ng-repeat="nick in group.nicks|orderBy:'name'">
|
||||
|
|
23
js/file-change.js
Normal file
23
js/file-change.js
Normal file
|
@ -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);
|
||||
}
|
||||
};
|
||||
|
||||
}]);
|
||||
|
||||
})();
|
49
js/imgur-drop-directive.js
Normal file
49
js/imgur-drop-directive.js
Normal file
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
};
|
||||
}
|
||||
};
|
||||
}]);
|
||||
|
||||
})();
|
128
js/imgur.js
Normal file
128
js/imgur.js
Normal file
|
@ -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
|
||||
};
|
||||
|
||||
}]);
|
||||
|
||||
})();
|
|
@ -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() {
|
||||
|
|
Loading…
Reference in a new issue