Added support to copy T#xxx into clipboard by pressing "ctrl+alt+y" if ticket is open in ticket zoom view.
This commit is contained in:
parent
a6cdde345c
commit
5e090a8976
5 changed files with 146 additions and 2 deletions
|
@ -24,6 +24,11 @@ Source: https://github.com/sliptree/bootstrap-tokenfield
|
||||||
Copyright 2013-2014 Sliptree and other contributors
|
Copyright 2013-2014 Sliptree and other contributors
|
||||||
License: MIT license
|
License: MIT license
|
||||||
-----------------------------------------------------------------------------
|
-----------------------------------------------------------------------------
|
||||||
|
clipboard.js
|
||||||
|
Source: https://github.com/lgarron/clipboard.js
|
||||||
|
Copyright: 2015 Lucas Garron (https://garron.net/)
|
||||||
|
License: MIT license
|
||||||
|
-----------------------------------------------------------------------------
|
||||||
cropper.js
|
cropper.js
|
||||||
Source: https://github.com/fengyuanchen/cropper
|
Source: https://github.com/fengyuanchen/cropper
|
||||||
Copyright: 2014-2015 Fengyuan Chen and contributors
|
Copyright: 2014-2015 Fengyuan Chen and contributors
|
||||||
|
|
|
@ -281,7 +281,7 @@ class App.ControllerForm extends App.Controller
|
||||||
|
|
||||||
# lookup relation if needed
|
# lookup relation if needed
|
||||||
if action.bind.relation
|
if action.bind.relation
|
||||||
data = App[action.bind.relation].find( value )
|
data = App[action.bind.relation].find(value)
|
||||||
value = data.name
|
value = data.name
|
||||||
|
|
||||||
# check if value is used in condition
|
# check if value is used in condition
|
||||||
|
|
|
@ -240,6 +240,20 @@ App.Config.set(
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
{
|
||||||
|
where: 'Used in object views'
|
||||||
|
shortcuts: [
|
||||||
|
{
|
||||||
|
key: 'y'
|
||||||
|
hotkeys: true
|
||||||
|
description: 'Copy current object number (e. g. Ticket#) into clipboard'
|
||||||
|
callback: ->
|
||||||
|
App.Event.trigger('keyboard_shortcuts_close')
|
||||||
|
text = $('.active.content .js-objectNumber').data('number') || ''
|
||||||
|
clipboard.copy(text)
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
|
|
125
app/assets/javascripts/app/lib/base/clipboard.js
Normal file
125
app/assets/javascripts/app/lib/base/clipboard.js
Normal file
|
@ -0,0 +1,125 @@
|
||||||
|
// https://github.com/lgarron/clipboard.js
|
||||||
|
window.clipboard = (function(){
|
||||||
|
|
||||||
|
var clipboard = {};
|
||||||
|
|
||||||
|
clipboard.copy = (function() {
|
||||||
|
var _intercept = false;
|
||||||
|
var _data = null; // Map from data type (e.g. "text/html") to value.
|
||||||
|
|
||||||
|
function cleanup() {
|
||||||
|
_intercept = false;
|
||||||
|
_data = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
document.addEventListener("copy", function(e) {
|
||||||
|
if (_intercept) {
|
||||||
|
for (var key in _data) {
|
||||||
|
e.clipboardData.setData(key, _data[key]);
|
||||||
|
}
|
||||||
|
e.preventDefault();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return function(data) {
|
||||||
|
return new Promise(function(resolve, reject) {
|
||||||
|
_intercept = true;
|
||||||
|
if (typeof data === "string") {
|
||||||
|
_data = {"text/plain": data};
|
||||||
|
} else if (data instanceof Node) {
|
||||||
|
_data = {"text/html": new XMLSerializer().serializeToString(data)};
|
||||||
|
} else {
|
||||||
|
_data = data;
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
if (document.execCommand("copy")) {
|
||||||
|
// document.execCommand is synchronous: http://www.w3.org/TR/2015/WD-clipboard-apis-20150421/#integration-with-rich-text-editing-apis
|
||||||
|
// So we can call resolve() back here.
|
||||||
|
cleanup();
|
||||||
|
resolve();
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
throw new Error("Unable to copy. Perhaps it's not available in your browser?");
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
cleanup();
|
||||||
|
reject(e);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
|
})();
|
||||||
|
|
||||||
|
clipboard.paste = (function() {
|
||||||
|
var _intercept = false;
|
||||||
|
var _resolve;
|
||||||
|
var _dataType;
|
||||||
|
|
||||||
|
document.addEventListener("paste", function(e) {
|
||||||
|
if (_intercept) {
|
||||||
|
_intercept = false;
|
||||||
|
e.preventDefault();
|
||||||
|
var resolve = _resolve;
|
||||||
|
_resolve = null;
|
||||||
|
resolve(e.clipboardData.getData(_dataType));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return function(dataType) {
|
||||||
|
return new Promise(function(resolve, reject) {
|
||||||
|
_intercept = true;
|
||||||
|
_resolve = resolve;
|
||||||
|
_dataType = dataType || "text/plain";
|
||||||
|
try {
|
||||||
|
if (!document.execCommand("paste")) {
|
||||||
|
_intercept = false;
|
||||||
|
reject(new Error("Unable to paste. Pasting only works in Internet Explorer at the moment."));
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
_intercept = false;
|
||||||
|
reject(new Error(e));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
|
})();
|
||||||
|
|
||||||
|
// Handle IE behaviour.
|
||||||
|
if (typeof ClipboardEvent === "undefined" &&
|
||||||
|
typeof window.clipboardData !== "undefined" &&
|
||||||
|
typeof window.clipboardData.setData !== "undefined") {
|
||||||
|
|
||||||
|
/*! promise-polyfill 2.0.1 */
|
||||||
|
(function(a){function b(a,b){return function(){a.apply(b,arguments)}}function c(a){if("object"!=typeof this)throw new TypeError("Promises must be constructed via new");if("function"!=typeof a)throw new TypeError("not a function");this._state=null,this._value=null,this._deferreds=[],i(a,b(e,this),b(f,this))}function d(a){var b=this;return null===this._state?void this._deferreds.push(a):void j(function(){var c=b._state?a.onFulfilled:a.onRejected;if(null===c)return void(b._state?a.resolve:a.reject)(b._value);var d;try{d=c(b._value)}catch(e){return void a.reject(e)}a.resolve(d)})}function e(a){try{if(a===this)throw new TypeError("A promise cannot be resolved with itself.");if(a&&("object"==typeof a||"function"==typeof a)){var c=a.then;if("function"==typeof c)return void i(b(c,a),b(e,this),b(f,this))}this._state=!0,this._value=a,g.call(this)}catch(d){f.call(this,d)}}function f(a){this._state=!1,this._value=a,g.call(this)}function g(){for(var a=0,b=this._deferreds.length;b>a;a++)d.call(this,this._deferreds[a]);this._deferreds=null}function h(a,b,c,d){this.onFulfilled="function"==typeof a?a:null,this.onRejected="function"==typeof b?b:null,this.resolve=c,this.reject=d}function i(a,b,c){var d=!1;try{a(function(a){d||(d=!0,b(a))},function(a){d||(d=!0,c(a))})}catch(e){if(d)return;d=!0,c(e)}}var j=c.immediateFn||"function"==typeof setImmediate&&setImmediate||function(a){setTimeout(a,1)},k=Array.isArray||function(a){return"[object Array]"===Object.prototype.toString.call(a)};c.prototype["catch"]=function(a){return this.then(null,a)},c.prototype.then=function(a,b){var e=this;return new c(function(c,f){d.call(e,new h(a,b,c,f))})},c.all=function(){var a=Array.prototype.slice.call(1===arguments.length&&k(arguments[0])?arguments[0]:arguments);return new c(function(b,c){function d(f,g){try{if(g&&("object"==typeof g||"function"==typeof g)){var h=g.then;if("function"==typeof h)return void h.call(g,function(a){d(f,a)},c)}a[f]=g,0===--e&&b(a)}catch(i){c(i)}}if(0===a.length)return b([]);for(var e=a.length,f=0;f<a.length;f++)d(f,a[f])})},c.resolve=function(a){return a&&"object"==typeof a&&a.constructor===c?a:new c(function(b){b(a)})},c.reject=function(a){return new c(function(b,c){c(a)})},c.race=function(a){return new c(function(b,c){for(var d=0,e=a.length;e>d;d++)a[d].then(b,c)})},"undefined"!=typeof module&&module.exports?module.exports=c:a.Promise||(a.Promise=c)})(this);
|
||||||
|
|
||||||
|
clipboard.copy = function(data) {
|
||||||
|
return new Promise(function(resolve, reject) {
|
||||||
|
// IE supports string and URL types: https://msdn.microsoft.com/en-us/library/ms536744(v=vs.85).aspx
|
||||||
|
// We only support the string type for now.
|
||||||
|
if (typeof data !== "string" && !("text/plain" in data)) {
|
||||||
|
throw new Error("You must provide a text/plain type.");
|
||||||
|
}
|
||||||
|
|
||||||
|
var strData = (typeof data === "string" ? data : data["text/plain"]);
|
||||||
|
var copySucceeded = window.clipboardData.setData("Text", strData);
|
||||||
|
if (copySucceeded) {
|
||||||
|
resolve();
|
||||||
|
} else {
|
||||||
|
reject(new Error("Copying was rejected."));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
clipboard.paste = function() {
|
||||||
|
return new Promise(function(resolve, reject) {
|
||||||
|
var strData = window.clipboardData.getData("Text");
|
||||||
|
if (strData) {
|
||||||
|
resolve(strData);
|
||||||
|
} else {
|
||||||
|
// The user rejected the paste request.
|
||||||
|
reject(new Error("Pasting was rejected."));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
return clipboard;
|
||||||
|
})();
|
|
@ -1,3 +1,3 @@
|
||||||
<small class="task-subline">
|
<small class="task-subline">
|
||||||
<%- @C('ticket_hook') %> <span class="ticket-number"><%- @ticket.number %></span> - <%- @T('created') %> <%- @humanTime(@ticket.created_at) %> <% if !@isCustomer && @ticket.escalation_time: %> - <%- @T('escalation') %> <%- @humanTime(@ticket.escalation_time, true) %><% end %>
|
<%- @C('ticket_hook') %> <span class="ticket-number js-objectNumber" data-number="<%- @C('ticket_hook') %><%- @ticket.number %>"><%- @ticket.number %></span> - <%- @T('created') %> <%- @humanTime(@ticket.created_at) %> <% if !@isCustomer && @ticket.escalation_time: %> - <%- @T('escalation') %> <%- @humanTime(@ticket.escalation_time, true) %><% end %>
|
||||||
</small>
|
</small>
|
Loading…
Reference in a new issue