define(
'tinymce.themes.mobile.ui.SerialisedDialog',
[
'ephox.alloy.api.behaviour.AddEventsBehaviour',
'ephox.alloy.api.behaviour.Behaviour',
'ephox.alloy.api.behaviour.Disabling',
'ephox.alloy.api.behaviour.Highlighting',
'ephox.alloy.api.behaviour.Keying',
'ephox.alloy.api.behaviour.Receiving',
'ephox.alloy.api.behaviour.Representing',
'ephox.alloy.api.component.Memento',
'ephox.alloy.api.events.AlloyEvents',
'ephox.alloy.api.events.AlloyTriggers',
'ephox.alloy.api.events.NativeEvents',
'ephox.alloy.api.ui.Button',
'ephox.alloy.api.ui.Container',
'ephox.alloy.api.ui.Form',
'ephox.boulder.api.FieldSchema',
'ephox.boulder.api.ValueSchema',
'ephox.katamari.api.Arr',
'ephox.katamari.api.Cell',
'ephox.katamari.api.Option',
'ephox.katamari.api.Singleton',
'ephox.sugar.api.properties.Css',
'ephox.sugar.api.search.SelectorFilter',
'ephox.sugar.api.search.SelectorFind',
'ephox.sugar.api.view.Width',
'tinymce.themes.mobile.channels.Receivers',
'tinymce.themes.mobile.model.SwipingModel',
'tinymce.themes.mobile.style.Styles',
'tinymce.themes.mobile.util.UiDomFactory'
],
function (
AddEventsBehaviour, Behaviour, Disabling, Highlighting, Keying, Receiving, Representing, Memento, AlloyEvents, AlloyTriggers, NativeEvents, Button, Container,
Form, FieldSchema, ValueSchema, Arr, Cell, Option, Singleton, Css, SelectorFilter, SelectorFind, Width, Receivers, SwipingModel, Styles, UiDomFactory
) {
var sketch = function (rawSpec) {
var navigateEvent = 'navigateEvent';
var wrapperAdhocEvents = 'serializer-wrapper-events';
var formAdhocEvents = 'form-events';
var schema = ValueSchema.objOf([
FieldSchema.strict('fields'),
// Used for when datafields are present.
FieldSchema.defaulted('maxFieldIndex', rawSpec.fields.length - 1),
FieldSchema.strict('onExecute'),
FieldSchema.strict('getInitialValue'),
FieldSchema.state('state', function () {
return {
dialogSwipeState: Singleton.value(),
currentScreen: Cell(0)
};
})
]);
var spec = ValueSchema.asRawOrDie('SerialisedDialog', schema, rawSpec);
var navigationButton = function (direction, directionName, enabled) {
return Button.sketch({
dom: UiDomFactory.dom('<span class="${prefix}-icon-' + directionName + ' ${prefix}-icon"></span>'),
action: function (button) {
AlloyTriggers.emitWith(button, navigateEvent, { direction: direction });
},
buttonBehaviours: Behaviour.derive([
Disabling.config({
disableClass: Styles.resolve('toolbar-navigation-disabled'),
disabled: !enabled
})
])
});
};
var reposition = function (dialog, message) {
SelectorFind.descendant(dialog.element(), '.' + Styles.resolve('serialised-dialog-chain')).each(function (parent) {
Css.set(parent, 'left', (-spec.state.currentScreen.get() * message.width) + 'px');
});
};
var navigate = function (dialog, direction) {
var screens = SelectorFilter.descendants(dialog.element(), '.' + Styles.resolve('serialised-dialog-screen'));
SelectorFind.descendant(dialog.element(), '.' + Styles.resolve('serialised-dialog-chain')).each(function (parent) {
if ((spec.state.currentScreen.get() + direction) >= 0 && (spec.state.currentScreen.get() + direction) < screens.length) {
Css.getRaw(parent, 'left').each(function (left) {
var currentLeft = parseInt(left, 10);
var w = Width.get(screens[0]);
Css.set(parent, 'left', (currentLeft - (direction * w)) + 'px');
});
spec.state.currentScreen.set(spec.state.currentScreen.get() + direction);
}
});
};
// Unfortunately we need to inspect the DOM to find the input that is currently on screen
var focusInput = function (dialog) {
var inputs = SelectorFilter.descendants(dialog.element(), 'input');
var optInput = Option.from(inputs[spec.state.currentScreen.get()]);
optInput.each(function (input) {
dialog.getSystem().getByDom(input).each(function (inputComp) {
AlloyTriggers.dispatchFocus(dialog, inputComp.element());
});
});
var dotitems = memDots.get(dialog);
Highlighting.highlightAt(dotitems, spec.state.currentScreen.get());
};
var resetState = function () {
spec.state.currentScreen.set(0);
spec.state.dialogSwipeState.clear();
};
var memForm = Memento.record(
Form.sketch(function (parts) {
return {
dom: UiDomFactory.dom('<div class="${prefix}-serialised-dialog"></div>'),
components: [
Container.sketch({
dom: UiDomFactory.dom('<div class="${prefix}-serialised-dialog-chain" style="left: 0px; position: absolute;"></div>'),
components: Arr.map(spec.fields, function (field, i) {
return i <= spec.maxFieldIndex ? Container.sketch({
dom: UiDomFactory.dom('<div class="${prefix}-serialised-dialog-screen"></div>'),
components: Arr.flatten([
[ navigationButton(-1, 'previous', (i > 0)) ],
[ parts.field(field.name, field.spec) ],
[ navigationButton(+1, 'next', (i < spec.maxFieldIndex)) ]
])
}) : parts.field(field.name, field.spec);
})
})
],
formBehaviours: Behaviour.derive([
Receivers.orientation(function (dialog, message) {
reposition(dialog, message);
}),
Keying.config({
mode: 'special',
focusIn: function (dialog/*, specialInfo */) {
focusInput(dialog);
},
onTab: function (dialog/*, specialInfo */) {
navigate(dialog, +1);
return Option.some(true);
},
onShiftTab: function (dialog/*, specialInfo */) {
navigate(dialog, -1);
return Option.some(true);
}
}),
AddEventsBehaviour.config(formAdhocEvents, [
AlloyEvents.runOnAttached(function (dialog, simulatedEvent) {
// Reset state to first screen.
resetState();
var dotitems = memDots.get(dialog);
Highlighting.highlightFirst(dotitems);
spec.getInitialValue(dialog).each(function (v) {
Representing.setValue(dialog, v);
});
}),
AlloyEvents.runOnExecute(spec.onExecute),
AlloyEvents.run(NativeEvents.transitionend(), function (dialog, simulatedEvent) {
if (simulatedEvent.event().raw().propertyName === 'left') {
focusInput(dialog);
}
}),
AlloyEvents.run(navigateEvent, function (dialog, simulatedEvent) {
var direction = simulatedEvent.event().direction();
navigate(dialog, direction);
})
])
])
};
})
);
var memDots = Memento.record({
dom: UiDomFactory.dom('<div class="${prefix}-dot-container"></div>'),
behaviours: Behaviour.derive([
Highlighting.config({
highlightClass: Styles.resolve('dot-active'),
itemClass: Styles.resolve('dot-item')
})
]),
components: Arr.bind(spec.fields, function (_f, i) {
return i <= spec.maxFieldIndex ? [
UiDomFactory.spec('<div class="${prefix}-dot-item ${prefix}-icon-full-dot ${prefix}-icon"></div>')
] : [];
})
});
return {
dom: UiDomFactory.dom('<div class="${prefix}-serializer-wrapper"></div>'),
components: [
memForm.asSpec(),
memDots.asSpec()
],
behaviours: Behaviour.derive([
Keying.config({
mode: 'special',
focusIn: function (wrapper) {
var form = memForm.get(wrapper);
Keying.focusIn(form);
}
}),
AddEventsBehaviour.config(wrapperAdhocEvents, [
AlloyEvents.run(NativeEvents.touchstart(), function (wrapper, simulatedEvent) {
spec.state.dialogSwipeState.set(
SwipingModel.init(simulatedEvent.event().raw().touches[0].clientX)
);
}),
AlloyEvents.run(NativeEvents.touchmove(), function (wrapper, simulatedEvent) {
spec.state.dialogSwipeState.on(function (state) {
simulatedEvent.event().prevent();
spec.state.dialogSwipeState.set(
SwipingModel.move(state, simulatedEvent.event().raw().touches[0].clientX)
);
});
}),
AlloyEvents.run(NativeEvents.touchend(), function (wrapper/*, simulatedEvent */) {
spec.state.dialogSwipeState.on(function (state) {
var dialog = memForm.get(wrapper);
// Confusing
var direction = -1 * SwipingModel.complete(state);
navigate(dialog, direction);
});
})
])
])
};
};
return {
sketch: sketch
};
}
);
|