1
0
mirror of https://github.com/BoostIo/Boostnote synced 2025-12-13 01:36:22 +00:00
snippet fields
This commit is contained in:
Rokt33r
2015-05-30 03:37:43 +09:00
parent 7c9d276a1f
commit b8e10f320d
10 changed files with 376 additions and 32 deletions

View File

@@ -10,11 +10,6 @@ module.exports = {
src:'node_modules/angular/angular.js',
cdn:'https://ajax.googleapis.com/ajax/libs/angularjs/1.3.15/angular.min.js'
},
{
name:'angular-ui-ace',
src:'node_modules/@rokt33r/angular-ui-ace/src/ui-ace.js',
cdn:'https://cdn.rawgit.com/angular-ui/ui-ace/v0.2.3/ui-ace.min.js'
},
{
name:'angular-bootstrap',
src:'node_modules/angular-bootstrap/dist/ui-bootstrap-tpls.js',

13
src/directives/tags.js Normal file
View File

@@ -0,0 +1,13 @@
angular.module('codexen.directives')
.directive('tags', function () {
return {
restrict:'A',
template: '<p class="tags" ng-if="tags.length">'+
'<i class="fa fa-tags"></i> '+
'<a ng-repeat="tag in tags" href="#">#<span ng-bind="tag.name"></span></a>'+
'</p>',
scope:{
tags: '='
}
}
})

337
src/directives/ui-ace.js Normal file
View File

@@ -0,0 +1,337 @@
'use strict';
/**
* Binds a ACE Editor widget
*/
angular.module('ui.ace', [])
.constant('uiAceConfig', {})
.directive('uiAce', ['uiAceConfig', function (uiAceConfig) {
if (angular.isUndefined(window.ace)) {
throw new Error('ui-ace need ace to work... (o rly?)');
}
/**
* Sets editor options such as the wrapping mode or the syntax checker.
*
* The supported options are:
*
* <ul>
* <li>showGutter</li>
* <li>useWrapMode</li>
* <li>onLoad</li>
* <li>theme</li>
* <li>mode</li>
* </ul>
*
* @param acee
* @param session ACE editor session
* @param {object} opts Options to be set
*/
var setOptions = function(acee, session, opts) {
// sets the ace worker path, if running from concatenated
// or minified source
if (angular.isDefined(opts.workerPath)) {
var config = window.ace.require('ace/config');
config.set('workerPath', opts.workerPath);
}
// ace requires loading
if (angular.isDefined(opts.require)) {
opts.require.forEach(function (n) {
window.ace.require(n);
});
}
// Boolean options
if (angular.isDefined(opts.showGutter)) {
acee.renderer.setShowGutter(opts.showGutter);
}
if (angular.isDefined(opts.useWrapMode)) {
session.setUseWrapMode(opts.useWrapMode);
}
if (angular.isDefined(opts.showInvisibles)) {
acee.renderer.setShowInvisibles(opts.showInvisibles);
}
if (angular.isDefined(opts.showIndentGuides)) {
acee.renderer.setDisplayIndentGuides(opts.showIndentGuides);
}
if (angular.isDefined(opts.useSoftTabs)) {
session.setUseSoftTabs(opts.useSoftTabs);
}
if (angular.isDefined(opts.showPrintMargin)) {
acee.setShowPrintMargin(opts.showPrintMargin);
}
if (angular.isDefined(opts.maxLines)) {
if(opts.maxLines < 0) opts.maxLines = Infinity
acee.setOptions({
maxLines: opts.maxLines
})
}
// commands
if (angular.isDefined(opts.disableSearch) && opts.disableSearch) {
acee.commands.addCommands([
{
name: 'unfind',
bindKey: {
win: 'Ctrl-F',
mac: 'Command-F'
},
exec: function () {
return false;
},
readOnly: true
}
]);
}
// Basic options
if (angular.isString(opts.theme)) {
acee.setTheme('ace/theme/' + opts.theme);
}
if (angular.isString(opts.mode)) {
session.setMode('ace/mode/' + opts.mode);
}
// Advanced options
if (angular.isDefined(opts.firstLineNumber)) {
if (angular.isNumber(opts.firstLineNumber)) {
session.setOption('firstLineNumber', opts.firstLineNumber);
} else if (angular.isFunction(opts.firstLineNumber)) {
session.setOption('firstLineNumber', opts.firstLineNumber());
}
}
// advanced options
var key, obj;
if (angular.isDefined(opts.advanced)) {
for (key in opts.advanced) {
// create a javascript object with the key and value
obj = { name: key, value: opts.advanced[key] };
// try to assign the option to the ace editor
acee.setOption(obj.name, obj.value);
}
}
// advanced options for the renderer
if (angular.isDefined(opts.rendererOptions)) {
for (key in opts.rendererOptions) {
// create a javascript object with the key and value
obj = { name: key, value: opts.rendererOptions[key] };
// try to assign the option to the ace editor
acee.renderer.setOption(obj.name, obj.value);
}
}
// onLoad callbacks
angular.forEach(opts.callbacks, function (cb) {
if (angular.isFunction(cb)) {
cb(acee);
}
});
};
return {
restrict: 'EA',
require: '?ngModel',
link: function (scope, elm, attrs, ngModel) {
/**
* Corresponds the uiAceConfig ACE configuration.
* @type object
*/
var options = uiAceConfig.ace || {};
/**
* uiAceConfig merged with user options via json in attribute or data binding
* @type object
*/
var opts = angular.extend({}, options, scope.$eval(attrs.uiAce));
/**
* ACE editor
* @type object
*/
var acee = window.ace.edit(elm[0]);
acee.$blockScrolling = Infinity
/**
* ACE editor session.
* @type object
* @see [EditSession]{@link http://ace.c9.io/#nav=api&api=edit_session}
*/
var session = acee.getSession();
/**
* Reference to a change listener created by the listener factory.
* @function
* @see listenerFactory.onChange
*/
var onChangeListener;
/**
* Reference to a blur listener created by the listener factory.
* @function
* @see listenerFactory.onBlur
*/
var onBlurListener;
/**
* Calls a callback by checking its existing. The argument list
* is variable and thus this function is relying on the arguments
* object.
* @throws {Error} If the callback isn't a function
*/
var executeUserCallback = function () {
/**
* The callback function grabbed from the array-like arguments
* object. The first argument should always be the callback.
*
* @see [arguments]{@link https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions_and_function_scope/arguments}
* @type {*}
*/
var callback = arguments[0];
/**
* Arguments to be passed to the callback. These are taken
* from the array-like arguments object. The first argument
* is stripped because that should be the callback function.
*
* @see [arguments]{@link https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions_and_function_scope/arguments}
* @type {Array}
*/
var args = Array.prototype.slice.call(arguments, 1);
if (angular.isDefined(callback)) {
scope.$evalAsync(function () {
if (angular.isFunction(callback)) {
callback(args);
} else {
throw new Error('ui-ace use a function as callback.');
}
});
}
};
/**
* Listener factory. Until now only change listeners can be created.
* @type object
*/
var listenerFactory = {
/**
* Creates a change listener which propagates the change event
* and the editor session to the callback from the user option
* onChange. It might be exchanged during runtime, if this
* happens the old listener will be unbound.
*
* @param callback callback function defined in the user options
* @see onChangeListener
*/
onChange: function (callback) {
return function (e) {
var newValue = session.getValue();
if (ngModel && newValue !== ngModel.$viewValue &&
// HACK make sure to only trigger the apply outside of the
// digest loop 'cause ACE is actually using this callback
// for any text transformation !
!scope.$$phase && !scope.$root.$$phase) {
scope.$evalAsync(function () {
ngModel.$setViewValue(newValue);
});
}
executeUserCallback(callback, e, acee);
};
},
/**
* Creates a blur listener which propagates the editor session
* to the callback from the user option onBlur. It might be
* exchanged during runtime, if this happens the old listener
* will be unbound.
*
* @param callback callback function defined in the user options
* @see onBlurListener
*/
onBlur: function (callback) {
return function () {
executeUserCallback(callback, acee);
};
}
};
attrs.$observe('readonly', function (value) {
acee.setReadOnly(!!value || value === '');
});
// Value Blind
if (ngModel) {
ngModel.$formatters.push(function (value) {
if (angular.isUndefined(value) || value === null) {
return '';
}
else if (angular.isObject(value) || angular.isArray(value)) {
throw new Error('ui-ace cannot use an object or an array as a model');
}
return value;
});
ngModel.$render = function () {
session.setValue(ngModel.$viewValue);
};
}
// Listen for option updates
var updateOptions = function (current, previous) {
if (current === previous) return;
opts = angular.extend({}, options, scope.$eval(attrs.uiAce));
opts.callbacks = [ opts.onLoad ];
if (opts.onLoad !== options.onLoad) {
// also call the global onLoad handler
opts.callbacks.unshift(options.onLoad);
}
// EVENTS
// unbind old change listener
session.removeListener('change', onChangeListener);
// bind new change listener
onChangeListener = listenerFactory.onChange(opts.onChange);
session.on('change', onChangeListener);
// unbind old blur listener
//session.removeListener('blur', onBlurListener);
acee.removeListener('blur', onBlurListener);
// bind new blur listener
onBlurListener = listenerFactory.onBlur(opts.onBlur);
acee.on('blur', onBlurListener);
setOptions(acee, session, opts);
};
scope.$watch(attrs.uiAce, updateOptions, /* deep watch */ true);
// set the options here, even if we try to watch later, if this
// line is missing things go wrong (and the tests will also fail)
updateOptions(options);
elm.on('$destroy', function () {
acee.session.$stopWorker();
acee.destroy();
});
scope.$watch(function() {
return [elm[0].offsetWidth, elm[0].offsetHeight];
}, function() {
acee.resize();
acee.renderer.updateFull();
}, true);
scope.Infinity = -1
}
};
}]);

View File

@@ -26,7 +26,6 @@
<script src="vendor/angular.js"></script>
<script src="vendor/angular-sanitize.js"></script>
<script src="vendor/angular-ui-router.js"></script>
<script src="vendor/ui-ace.js"></script>
<script src="vendor/ui-bootstrap-tpls.js"></script>
<script src="vendor/select.js"></script>
<script src="vendor/satellizer.js"></script>

View File

@@ -18,9 +18,8 @@ angular.module('codexen.modals')
vm.submit = function () {
var params = {
title: vm.title,
description: vm.description,
prefix: vm.prefix,
callSign: vm.callSign,
mode: vm.mode==null?null:vm.mode.name.toLowerCase(),
content: vm.content,
tags: angular.isArray(vm.tags)?vm.tags.map(function (tag) { return {_id: tag._id, name: tag.name} }):[]

View File

@@ -1,6 +1,6 @@
<div class="new-snippet-modal">
<div class="modal-header">
<input ng-model="vm.title" type="title" class="form-control input-lg" placeholder="Title">
New Snippet
</div>
<div class="modal-body">
@@ -8,7 +8,7 @@
<textarea ng-model="vm.description" name="description" class="form-control" placeholder="Description..."></textarea>
</div>
<div class="form-group">
<input ng-model="vm.prefix" type="text" name="prefix" class="inline-form-control" placeholder="Prefix">
<input ng-model="vm.callSign" type="text" name="callSign" class="inline-form-control" placeholder="Call sign">
<ui-select ng-model="vm.mode" style="display: inline-block;" on-select="vm.log(vm.mode.name.toLowerCase())" theme="bootstrap">
<ui-select-match placeholder="Select Type">{{$select.selected.name}}</ui-select-match>
@@ -18,13 +18,15 @@
</ui-select>
</div>
<div
ui-ace="{
mode: vm.mode.name.toLowerCase()
<div class="form-group">
<div
ui-ace="{
mode: vm.mode.name.toLowerCase()
}"
ng-model="vm.content"
></div>
}"
ng-model="vm.content"
></div>
</div>
<div class="form-group">
<ui-select multiple tagging="vm.transform" tagging-tokens="SPACE|,|/" ng-model="vm.tags" theme="bootstrap">
@@ -34,8 +36,6 @@
<div><span ng-bind-html="tag.name | highlight: $select.search"></span><span ng-if="tag.isTag">(new)</span></div>
</ui-select-choices>
</ui-select>
</div>
</div>

View File

@@ -5,6 +5,9 @@
App: <a href="https://github.com/Rokt33r/codexen-app">Rokt33r/codexen-app</a><br>
Server: <a href="https://github.com/Rokt33r/codexen-server">Rokt33r/codexen-server</a>
</p>
<p>
© 2015 MAISIN&CO.,Inc.
</p>
</div>

View File

@@ -1,9 +1,7 @@
<div class="snippets-detail-state">
<div class="detail-header">
<span class="detail-header-title">
<small>Title : </small>
<span ng-bind="vm.snippet.title"></span></span>
<span class="detail-header-title"><small>call sign : </small><span ng-bind="vm.snippet.callSign"></span></span>
<span class="detail-header-control pull-right">
<button type="button" name="button" class="btn btn-default"><i class="fa fa-share"></i></button>
<button type="button" name="button" class="btn btn-default"><i class="fa fa-edit"></i></button>
@@ -18,15 +16,15 @@
</div>
<div ng-if="vm.isLoaded" class="">
<p class="tags" ng-if="vm.snippet.tags">
<small><i class="fa fa-tags"></i></small> <a ng-repeat="tag in vm.snippet.tags" ng-bind="tag.name" href="#" class="label label-default"></a>
</p>
<label>Description</label>
<p ng-bind="vm.snippet.description"></p>
<div tags="vm.snippet.tags"></div>
<label>Content</label>
<div ui-ace="{
readonly: true,
showGutter: false,
useWrapMode : true,
mode:vm.snippet.mode,
maxLines: -1,
readOnly: true,
rendererOptions: {
maxLinks: Infinity
}

View File

@@ -29,14 +29,11 @@
<img width="25" height="25" src="http://www.gravatar.com/avatar/ea0b6ad1c11700120d1af08810caa19d" alt="" />
</div>
<div class="media-body">
<h4 ng-bind="snippet.title"></h4>
<h4 ng-bind="snippet.description"></h4>
<p class="created-at">created at <span ng-bind="snippet.createdAt"></span></p>
</div>
</div>
<p ng-bind="snippet.description"></p>
<p class="tags" ng-if="snippet.tags.length">
<small><i class="fa fa-tags"></i></small> <a ng-repeat="tag in snippet.tags" ng-bind="tag.name" href="#" class="label label-default"></a>
</p>
<div tags="snippet.tags"></div>
</li>
</ul>
</div>

View File

@@ -47,6 +47,8 @@ $snippet-list-item-hover-bg: #EEE;
bottom: 0;
left: $left-pane-width;
right: 0;
overflow-x: hidden;
overflow-y: auto;
}
}
@@ -69,13 +71,14 @@ $snippet-list-item-hover-bg: #EEE;
.detail-body{
padding: 5px 10px;
.ace_editor {
height: 500px;
min-height: 300px;
border: solid 1px $border-color;
border-radius: 5px;
}
}
}
.tags{
word-break: break-all;
a{
margin: 0 2px;
}