ImageGrid/ui/experiments/editor.html

441 lines
9.4 KiB
HTML
Raw Normal View History

<html>
<style>
button,
details {
border: solid 1px gray;
border-radius: 4px;
margin: 1px;
background: white;
}
details {
display: inline-block;
width: 508px;
min-width: 500px;
overflow: visible;
}
.panel {
box-shadow: 5px 5px 30px -5px rgba(0,0,0,0.5);
opacity: 0.95;
}
.close-panel {
display: inline-block;
position: absolute;
right: 5px;
cursor: hand;
}
.close-panel:hover {
color: black;
font-weight: bold;
text-shadow: 1px 1px 2px 0px rgba(0,0,0,0.5);
}
details details {
width: 500px;
margin: 3px;
border: solid 1px silver;
box-shadow: none;
}
details > div {
margin: 10px;
}
summary {
padding-left: 3px;
background: silver;
}
details details summary {
background: #ddd;
}
input[type=range] {
width: 300px;
}
button:active {
background: silver;
}
button.reset {
border: solid 1px silver;
}
button.reset:hover {
border: solid 1px gray;
}
/*
button.state:focus {
background: silver;
}
*/
/* XXX this is not live...
input[type=range]:after {
content: attr(value);
color: black;
}
*/
div > span:first-child {
display: inline-block;
width: 100px;
}
/* range syling... */
input[type='range'] {
-webkit-appearance: none !important;
background: silver;
height: 4px;
border: solid 1px gray;
border-radius: 2px;
}
input[type='range']::-webkit-slider-thumb {
-webkit-appearance: none !important;
background: white;
height: 15px;
width: 15px;
border: solid 1px gray;
border-radius: 50%;
}
.value {
-webkit-appearance: none !important;
display: inline-block;
width: 40px;
text-align: right;
margin-left: 5px;
margin-right: 5px;
background: white;
border: none;
border-radius: 2px;
}
input::-webkit-outer-spin-button,
input::-webkit-inner-spin-button {
-webkit-appearance: none !important;
}
.title {
cursor: move;
}
</style>
<script src="jquery.js"></script>
<script src="jquery-ui.js"></script>
<script>
// XXX this exists in jli, remove when not needed...
jQuery.fn.sortChildren = function(func){
return $(this).each(function(_, e){
return $(e).append($(e).children().detach().get().sort(func))
})
}
// XXX make this add new filters in the correct position...
function updateFilter(e, f, v, order){
e = $(e)
var state = e
.css('-webkit-filter')
state = state == 'none' ? '' : state + ' '
// update existing filter...
if(RegExp(f).test(state)){
state = state.replace(RegExp(f+'\\s*\\([^\\)]*\\)'), f+'('+v+')')
// add new filter...
} else {
state += f+'('+v+')'
state = sortFilterStr(state, order)
}
e.css({
'-webkit-filter': state,
})
return v
}
function resetFilter(e, f){
e = $(e)
var state = e
.css('-webkit-filter')
state = state == 'none' ? '' : state + ' '
state = state.replace(RegExp(f+'\\s*\\([^\\)]*\\)'), '').trim()
e.css({
'-webkit-filter': state,
})
return e
}
function sortFilterStr(state, order){
order = order == null ? getSliderOrder() : order
state = state.split(/\s+/)
state.sort(function(a, b){
a = order.indexOf(a.replace(/\(.*/, ''))
b = order.indexOf(b.replace(/\(.*/, ''))
return a - b
})
return state.join(' ')
}
function getSliderOrder(){
return $('.filter-list').sortable('toArray')
}
// NOTE: this will return only the set filters...
function getFilterOrder(target){
return $(target)
.css('-webkit-filter')
.split(/\s*\([^\)]*\)\s*/g)
.slice(0, -1)
}
function sortFilterSliders(order){
return $('.filter-list').sortChildren(function(a, b){
a = order.indexOf(a.id)
b = order.indexOf(b.id)
return a - b
})
}
function resetPrevRange(e){
e = $(e).prev('input[type=range]')
e.val(e.attr('default'))
.change()
}
// XXX order the sliders...
function loadSliderState(target){
var res = $(target)
.css('-webkit-filter')
var state = res
.split(/\s*\(\s*|\s*\)\s*/g)
.reverse()
.slice(1)
// reset sliders do defaults...
$('input[type=range]').each(function(i, e){
e = $(e)
e.val(e.attr('default')).change()
})
// set the saved values...
while(state.length > 0){
// XXX avoid using ids...
var e = $('[filter='+state.pop()+']')
if(e.prop('normalize')){
e.val(v2r(parseFloat(state.pop()))).change()
} else {
e.val(parseFloat(state.pop())).change()
}
}
return res
}
var DEFAULT_FILTER_ORDER = [
'brightness',
'contrast',
'saturate',
'hue-rotate',
'grayscale',
'invert',
'sepia'
]
function saveState(target){
var l = $('.state').length
var state = $(target).css('-webkit-filter')
$('<button></button>')
.text(l)
.addClass('state '+l)
.attr({
state: state,
sliders: getSliderOrder().join(' ')
})
// load state...
.click(function(){
loadSliderState($(target).css('-webkit-filter', state))
sortFilterSliders($(this).attr('sliders').split(' '))
})
.appendTo($('.states'))
}
function clearStates(){
$('.state').remove()
}
function makeAbsRange(text, filter, target, min, max, dfl, step, translate, normalize){
min = min == null ? 0 : min
max = max == null ? 1 : max
dfl = dfl == null ? min : dfl
step = step == null ? 0.01 : step
translate = translate == null ? function(v){return v} : translate
normalize = normalize == null ? false : true
var elem = $('<div class="control range"></div>')
.attr({
id: filter,
})
$('<span class="title"/>')
.html(text)
.appendTo(elem)
var range = $('<input type="range">')
.attr({
filter: filter,
min: min,
max: max,
step: step,
default: dfl,
})
.prop('normalize', normalize)
.val(dfl)
.change(function(){
var val = this.valueAsNumber
value.val(val)
updateFilter(target, filter, translate(val))
})
.appendTo(elem)
var value = $('<input type="number" class="value"/>')
.attr({
min: min,
max: max,
step: step,
})
.val(dfl)
.change(function(){
range.val($(this).val()).change()
})
.appendTo(elem)
$('<button class="reset">&times;</button>')
.click(function(){
range.val(dfl).change()
resetFilter(target, filter)
})
.appendTo(elem)
return elem
}
function makeLogRange(text, filter, target){
return makeAbsRange(text, filter, target, -100, 100, 0, 0.1, r2v, true)
}
var C = 43.47
function r2v(r){
return Math.pow(Math.E, r/C)
}
function v2r(v){
return Math.log(v)*C
}
function makeControls(target){
// tool panel...
var panel = $('<details open/>')
.addClass('panel')
.css({
position: 'absolute',
top: '100px',
left: '100px',
})
.append($('<summary>Edit</summary>')
.append($('<span/>')
.addClass('close-panel')
.click(function(){
$(this).parents('.panel').hide()
return false
})
.html('&times;')))
.draggable({
containment: $('body'),
scroll: false,
})
// wrapper for sub-panels...
var content = $('<span class="content">')
.sortable({
forcePlaceholderSize: true,
start: function(e, ui){
ui.placeholder.height(ui.helper.outerHeight());
},
})
.appendTo(panel)
// filters...
$('<details open/>')
.append($('<summary>Filters</summary>'))
.append($('<div/>')
.append($('<div class="filter-list"/>')
.append(makeLogRange('Brightness:', 'brightness', target))
.append(makeLogRange('Contrast:', 'contrast', target))
.append(makeLogRange('Saturation:', 'saturate', target))
.append(makeAbsRange('Hue:', 'hue-rotate', target, -180, 180, 0, 0.5, function(v){ return v+'deg' }))
.append(makeAbsRange('Grayscale:', 'grayscale', target))
.append(makeAbsRange('Invert:', 'invert', target))
.append(makeAbsRange('Sepia:', 'sepia', target))
.sortable({
axis: 'y',
})
.on('sortstop', function(){
// update image filter order...
var img = $(target)
img.css('-webkit-filter', sortFilterStr(img.css('-webkit-filter')))
}))
.append($('<hr>'))
.append('Reset: ')
.append($('<button>Values</button>')
.click(function(){
$('.reset').click()
}))
.append($('<button>Order</button>')
.click(function(){
sortFilterSliders(DEFAULT_FILTER_ORDER)
}))
.append($('<button>All</button>')
.click(function(){
$('.reset').click()
sortFilterSliders(DEFAULT_FILTER_ORDER)
})))
//.append($('<hr>'))
//.append($('<div>NOTE: order of filters is segnificant, use '+
// 'dragging to arrange them as needed.</div>')))
.appendTo(content)
// snapshots...
$('<details open/>')
.append($('<summary>Snapshots</summary>'))
.append($('<div/>')
.append($('<div class="states"/>'))
.append($('<hr>'))
.append($('<button/>')
.click(function(){ saveState(target) })
.text('Save'))
.append($('<button/>')
.click(function(){ clearStates() })
.text('Clear')))
.appendTo(content)
return panel
}
function reloadControls(target){
clearStates()
var state = loadSliderState(target)
// nothing set -- default sort...
if(state == 'none'){
sortFilterSliders(DEFAULT_FILTER_ORDER)
// load existing sort state...
} else {
sortFilterSliders(getFilterOrder(target).concat(DEFAULT_FILTER_ORDER))
}
// make a snapshot...
saveState(target)
}
$(function(){
var target = '#image'
var panel = makeControls(target).hide()
$('body').append(panel)
$(target)
.click(function(){
panel.show()
})
saveState(target)
})
</script>
<body>
<img id="image" src="image.jpg" >
<p>click image to show editor panel...</p>
</body>
</html>