diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..a5806d1 --- /dev/null +++ b/.gitignore @@ -0,0 +1,5 @@ +.idea +dist/ +node_modules/ +config/ +ftpinfo.js \ No newline at end of file diff --git a/gulpfile.js b/gulpfile.js new file mode 100644 index 0000000..26f0102 --- /dev/null +++ b/gulpfile.js @@ -0,0 +1,69 @@ +var gulp = require('gulp'), + sourcemaps = require('gulp-sourcemaps'), + sass = require('gulp-sass'), + bulkSass = require('gulp-sass-glob-import'), + autoprefixer = require('gulp-autoprefixer'), + cssnano = require('gulp-cssnano'), + babel = require('gulp-babel'), + rollup = require('gulp-rollup'), + uglify = require('gulp-uglify'), + ftp = require('vinyl-ftp'), + ftpinfo = require('./ftpinfo'); + +gulp.task('sass', function(){ + gulp.src('src/scss/app.scss') + .pipe(sourcemaps.init()) + .pipe(bulkSass()) + .pipe(sass()) + .pipe(autoprefixer('last 2 version')) + .pipe(cssnano()) + .pipe(sourcemaps.write('./maps')) + .pipe(gulp.dest('dist/css')); +}); + +gulp.task('js', function(){ + gulp.src('src/js/**/*.js') + .pipe(sourcemaps.init()) + .pipe(rollup({ + entry: 'src/js/app.js' + })) + .pipe(babel({ presets: ['es2015'] })) + .pipe(uglify()) + .pipe(sourcemaps.write('./maps')) + .pipe(gulp.dest('dist/js')); +}); + +gulp.task('copy', function(){ + gulp.src(['src/*.php', 'src/assets/**']) + .pipe(gulp.dest('dist')); +}); + +gulp.task('build', ['js', 'sass', 'copy']); + +gulp.task('watch', function(){ + gulp.watch('src/scss/**/*.scss', ['sass']); + gulp.watch('src/js/**/*.js', ['js']); + gulp.watch(['src/*.php', 'src/assets/**'], ['copy']); +}); + +gulp.task('ftp-test', function(){ + var connection = ftp.create(ftpinfo); + var globs = [ 'dist/**' ]; + return gulp.src(globs, { base: 'dist', buffer: false }) + .pipe(connection.dest('/test.kinklist.info')); +}); + +gulp.task('ftp-prod', function(){ + var connection = ftp.create(ftpinfo); + var globs = [ 'dist/**' ]; + return gulp.src(globs, { base: 'dist', buffer: false }) + .pipe(connection.dest('/public_html')); +}); + +gulp.task('deploy-test', ['build', 'ftp-test']); + +gulp.task('deploy-prod', ['build', 'ftp-prod']); + +gulp.task('dev', ['build', 'watch']); + +gulp.task('default', ['dev']); \ No newline at end of file diff --git a/user/Nits_3069.html b/old/user/Nits_3069.html similarity index 100% rename from user/Nits_3069.html rename to old/user/Nits_3069.html diff --git a/v0.1.html b/old/v0.1.html similarity index 100% rename from v0.1.html rename to old/v0.1.html diff --git a/v0.5.html b/old/v0.5.html similarity index 100% rename from v0.5.html rename to old/v0.5.html diff --git a/v0.6.1.html b/old/v0.6.1.html similarity index 100% rename from v0.6.1.html rename to old/v0.6.1.html diff --git a/v0.6.html b/old/v0.6.html similarity index 100% rename from v0.6.html rename to old/v0.6.html diff --git a/v0.7.1.html b/old/v0.7.1.html similarity index 100% rename from v0.7.1.html rename to old/v0.7.1.html diff --git a/v0.7.2.html b/old/v0.7.2.html similarity index 100% rename from v0.7.2.html rename to old/v0.7.2.html diff --git a/v0.7.html b/old/v0.7.html similarity index 100% rename from v0.7.html rename to old/v0.7.html diff --git a/v0.8.1.html b/old/v0.8.1.html similarity index 100% rename from v0.8.1.html rename to old/v0.8.1.html diff --git a/v0.8.2.html b/old/v0.8.2.html similarity index 100% rename from v0.8.2.html rename to old/v0.8.2.html diff --git a/v0.8.html b/old/v0.8.html similarity index 100% rename from v0.8.html rename to old/v0.8.html diff --git a/v0.9.1.html b/old/v0.9.1.html similarity index 100% rename from v0.9.1.html rename to old/v0.9.1.html diff --git a/v0.9.2.html b/old/v0.9.2.html similarity index 100% rename from v0.9.2.html rename to old/v0.9.2.html diff --git a/v0.9.4.html b/old/v0.9.4.html similarity index 100% rename from v0.9.4.html rename to old/v0.9.4.html diff --git a/v0.9.9.html b/old/v0.9.9.html similarity index 100% rename from v0.9.9.html rename to old/v0.9.9.html diff --git a/v0.9.9b.html b/old/v0.9.9b.html similarity index 100% rename from v0.9.9b.html rename to old/v0.9.9b.html diff --git a/v0.9.html b/old/v0.9.html similarity index 100% rename from v0.9.html rename to old/v0.9.html diff --git a/v1.0.1.html b/old/v1.0.1.html similarity index 100% rename from v1.0.1.html rename to old/v1.0.1.html diff --git a/v1.0.2.html b/old/v1.0.2.html similarity index 100% rename from v1.0.2.html rename to old/v1.0.2.html diff --git a/v1.html b/old/v1.html similarity index 100% rename from v1.html rename to old/v1.html diff --git a/package.json b/package.json new file mode 100644 index 0000000..8dc08b5 --- /dev/null +++ b/package.json @@ -0,0 +1,40 @@ +{ + "name": "kinklist", + "version": "2.0.0", + "description": "Improved kinklist to make listing your kinks as easy as could be", + "main": "index.js", + "scripts": { + "test": "echo \"Error: no test specified\" && exit 1" + }, + "repository": { + "type": "git", + "url": "git+https://github.com/Goctionni/KinkList.git" + }, + "keywords": [ + "js", + "canvas", + "es6", + "html", + "css", + "scss" + ], + "author": "Goctionni", + "license": "MIT", + "bugs": { + "url": "https://github.com/Goctionni/KinkList/issues" + }, + "homepage": "https://github.com/Goctionni/KinkList#readme", + "devDependencies": { + "babel-preset-es2015": "^6.24.1", + "gulp": "^3.9.1", + "gulp-autoprefixer": "^3.1.1", + "gulp-babel": "^6.1.2", + "gulp-cssnano": "^2.1.2", + "gulp-rollup": "^2.11.0", + "gulp-sass": "^3.1.0", + "gulp-sass-glob-import": "^0.1.0", + "gulp-sourcemaps": "^2.6.0", + "gulp-uglify": "^2.1.2", + "vinyl-ftp": "^0.6.0" + } +} diff --git a/src/index.php b/src/index.php new file mode 100644 index 0000000..290daaa --- /dev/null +++ b/src/index.php @@ -0,0 +1,212 @@ + + + + Kinklist + + + + + + + +
+ +

Kink list

+
+
Not Entered
+
Favorite
+
Like
+
Okay
+
Maybe
+
No
+
+
+ + +
Loading
+
+ +
+
+
+ + +
+
+
+
+
+

+

+ +
+
+
+
+
+ + + \ No newline at end of file diff --git a/src/js/app.js b/src/js/app.js new file mode 100644 index 0000000..3ddb40d --- /dev/null +++ b/src/js/app.js @@ -0,0 +1,853 @@ +var log = function(val, base) { + return Math.log(val) / Math.log(base); +}; +var strToClass = function(str){ + var className = ""; + str = str.toLowerCase(); + var validChars = 'abcdefghijklmnopqrstuvwxyz'; + var newWord = false; + for(var i = 0; i < str.length; i++) { + var chr = str[i]; + if(validChars.indexOf(chr) >= 0) { + if(newWord) chr = chr.toUpperCase(); + className += chr; + newWord = false; + } + else { + newWord = true; + } + } + return className; +}; +var addCssRule = function(selector, rules){ + var sheet = document.styleSheets[0]; + if("insertRule" in sheet) { + sheet.insertRule(selector + "{" + rules + "}", 0); + } + else if("addRule" in sheet) { + sheet.addRule(selector, rules, 0); + } +}; + +var kinks = {}; +var inputKinks = {} +var colors = {} +var level = {}; + +$(function(){ + + var imgurClientId = '9db53e5936cd02f'; + + inputKinks = { + $columns: [], + createCategory: function(name, fields){ + var $category = $('
') + .addClass('cat-' + strToClass(name)) + .data('category', name) + .append($('

') + .text(name)); + + var $table = $('').data('fields', fields); + var $thead = $('').appendTo($table); + for(var i = 0; i < fields.length; i++) { + $('').appendTo($table); + $category.append($table); + + return $category; + }, + createChoice: function(){ + var $container = $('
').addClass('choices'); + var levels = Object.keys(level); + for(var i = 0; i < levels.length; i++) { + $('
').data('kink', name).addClass('kinkRow'); + for(var i = 0; i < fields.length; i++) { + var $choices = inputKinks.createChoice(); + $choices.data('field', fields[i]); + $choices.addClass('choice-' + strToClass(fields[i])); + $('
').addClass('choicesCol').text(fields[i]).appendTo($thead); + } + $('').appendTo($thead); + $('
').append($choices).appendTo($row); + } + $('').text(name).appendTo($row); + $row.addClass('kink-' + strToClass(name)); + return $row; + }, + createColumns: function(){ + var colClasses = ['100', '50', '33', '25']; + + var numCols = Math.floor((document.body.scrollWidth - 20) / 400); + if(!numCols) numCols = 1; + if(numCols > 4) numCols = 4; + var colClass = 'col' + colClasses[numCols - 1]; + + inputKinks.$columns = []; + for(var i = 0; i < numCols; i++){ + inputKinks.$columns.push($('
').addClass('col ' + colClass).appendTo($('#InputList'))); + } + }, + placeCategories: function($categories){ + var $body = $('body'); + var totalHeight = 0; + for(var i = 0; i < $categories.length; i++) { + var $clone = $categories[i].clone().appendTo($body); + var height = $clone.height();; + totalHeight += height; + $clone.remove(); + } + + var colHeight = totalHeight / (inputKinks.$columns.length); + var colIndex = 0; + for(var i = 0; i < $categories.length; i++) { + var curHeight = inputKinks.$columns[colIndex].height(); + var catHeight = $categories[i].height(); + if(curHeight + (catHeight / 2) > colHeight) colIndex++; + while(colIndex >= inputKinks.$columns.length) { + colIndex--; + } + inputKinks.$columns[colIndex].append($categories[i]); + } + }, + fillInputList: function(){ + $('#InputList').empty(); + inputKinks.createColumns(); + + var $categories = []; + var kinkCats = Object.keys(kinks); + for(var i = 0; i < kinkCats.length; i++) { + var catName = kinkCats[i]; + var category = kinks[catName]; + var fields = category.fields; + var kinkArr = category.kinks; + + var $category = inputKinks.createCategory(catName, fields); + var $tbody = $category.find('tbody'); + for(var k = 0; k < kinkArr.length; k++) { + $tbody.append(inputKinks.createKink(fields, kinkArr[k])); + } + + $categories.push($category); + } + inputKinks.placeCategories($categories); + + // Make things update hash + $('#InputList').find('button.choice').on('click', function(){ + location.hash = inputKinks.updateHash(); + }); + }, + init: function(){ + // Set up DOM + inputKinks.fillInputList(); + + // Read hash + inputKinks.parseHash(); + + // Make export button work + $('#Export').on('click', inputKinks.export); + $('#URL').on('click', function(){ this.select(); }); + + // On resize, redo columns + (function(){ + + var lastResize = 0; + $(window).on('resize', function(){ + var curTime = (new Date()).getTime(); + lastResize = curTime; + setTimeout(function(){ + if(lastResize === curTime) { + inputKinks.fillInputList(); + inputKinks.parseHash(); + } + }, 500); + }); + + })(); + }, + hashChars: "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-_.=+*^!@", + maxPow: function(base, maxVal) { + var maxPow = 1; + for(var pow = 1; Math.pow(base, pow) <= maxVal; pow++){ + maxPow = pow; + } + return maxPow; + }, + prefix: function(input, len, char){ + while(input.length < len) { + input = char + input; + } + return input; + }, + drawLegend: function(context){ + context.font = "bold 13px Arial"; + context.fillStyle = '#000000'; + + var levels = Object.keys(colors); + var x = context.canvas.width - 15 - (120 * levels.length); + for(var i = 0; i < levels.length; i++) { + context.beginPath(); + context.arc(x + (120 * i), 17, 8, 0, 2 * Math.PI, false); + context.fillStyle = colors[levels[i]]; + context.fill(); + context.strokeStyle = 'rgba(0, 0, 0, 0.5)' + context.lineWidth = 1; + context.stroke(); + + context.fillStyle = '#000000'; + context.fillText(levels[i], x + 15 + (i * 120), 22); + } + }, + setupCanvas: function(width, height, username){ + $('canvas').remove(); + var canvas = document.createElement('canvas'); + canvas.width = width; + canvas.height = height; + + var $canvas = $(canvas); + $canvas.css({ + width: width, + height: height + }); + // $canvas.insertBefore($('#InputList')); + + var context = canvas.getContext('2d'); + context.fillStyle = '#FFFFFF'; + context.fillRect(0, 0, canvas.width, canvas.height); + + context.font = "bold 24px Arial"; + context.fillStyle = '#000000'; + context.fillText('Kinklist ' + username, 5, 25); + + inputKinks.drawLegend(context); + return { context: context, canvas: canvas }; + }, + drawCallHandlers: { + simpleTitle: function(context, drawCall){ + context.fillStyle = '#000000'; + context.font = "bold 18px Arial"; + context.fillText(drawCall.data, drawCall.x, drawCall.y + 5); + }, + titleSubtitle: function(context, drawCall){ + context.fillStyle = '#000000'; + context.font = "bold 18px Arial"; + context.fillText(drawCall.data.category, drawCall.x, drawCall.y + 5); + + var fieldsStr = drawCall.data.fields.join(', '); + context.font = "italic 12px Arial"; + context.fillText(fieldsStr, drawCall.x, drawCall.y + 20); + }, + kinkRow: function(context, drawCall){ + context.fillStyle = '#000000'; + context.font = "12px Arial"; + + var x = drawCall.x + 5 + (drawCall.data.choices.length * 20); + var y = drawCall.y - 6; + context.fillText(drawCall.data.text, x, y); + + // Circles + for(var i = 0; i < drawCall.data.choices.length; i++){ + var choice = drawCall.data.choices[i]; + var color = colors[choice]; + + var x = 10 + drawCall.x + (i * 20); + var y = drawCall.y - 10; + + context.beginPath(); + context.arc(x, y, 8, 0, 2 * Math.PI, false); + context.fillStyle = color; + context.fill(); + context.strokeStyle = 'rgba(0, 0, 0, 0.5)' + context.lineWidth = 1; + context.stroke(); + } + + } + }, + export: function(){ + var username = prompt("Please enter your name"); + if(typeof username !== 'string') return; + else if (username.length ) username = '(' + username + ')'; + + $('#Loading').fadeIn(); + $('#URL').fadeOut(); + + // Constants + var numCols = 6; + var columnWidth = 250; + var simpleTitleHeight = 35; + var titleSubtitleHeight = 50; + var rowHeight = 25; + var offsets = { + left: 10, + right: 10, + top: 50, + bottom: 10 + }; + + // Find out how many we have of everything + var numCats = $('.kinkCategory').length; + var dualCats = $('.kinkCategory th + th + th').length; + var simpleCats = numCats - dualCats; + var numKinks = $('.kinkRow').length; + + // Determine the height required for all categories and kinks + var totalHeight = ( + (numKinks * rowHeight) + + (dualCats * titleSubtitleHeight) + + (simpleCats * simpleTitleHeight) + ); + + // Initialize columns and drawStacks + var columns = []; + for(var i = 0; i < numCols; i++){ + columns.push({ height: 0, drawStack: []}); + } + + // Create drawcalls and place them in the drawStack + // for the appropriate column + var avgColHeight = totalHeight / numCols; + var columnIndex = 0; + $('.kinkCategory').each(function(){ + var $cat = $(this); + var catName = $cat.data('category'); + var category = kinks[catName]; + var fields = category.fields; + var catKinks = category.kinks; + + var catHeight = 0; + catHeight += (fields.length === 1) ? simpleTitleHeight : titleSubtitleHeight; + catHeight += (catKinks.length * rowHeight); + + // Determine which column to place this category in + if((columns[columnIndex].height + (catHeight / 2)) > avgColHeight) columnIndex++; + while(columnIndex >= numCols) columnIndex--; + var column = columns[columnIndex]; + + // Drawcall for title + var drawCall = { y: column.height }; + column.drawStack.push(drawCall); + if(fields.length < 2) { + column.height += simpleTitleHeight; + drawCall.type = 'simpleTitle'; + drawCall.data = catName; + } + else { + column.height += titleSubtitleHeight; + drawCall.type = 'titleSubtitle'; + drawCall.data = { + category: catName, + fields: fields + }; + } + + // Drawcalls for kinks + $cat.find('.kinkRow').each(function(){ + var $kinkRow = $(this); + var drawCall = { y: column.height, type: 'kinkRow', data: { + choices: [], + text: $kinkRow.data('kink') + }}; + column.drawStack.push(drawCall); + column.height += rowHeight; + + // Add choices + $kinkRow.find('.choices').each(function(){ + var $selection = $(this).find('.choice.selected'); + var selection = ($selection.length > 0) + ? $selection.data('level') + : Object.keys(level)[0]; + + drawCall.data.choices.push(selection); + }); + }); + }); + + var tallestColumnHeight = 0; + for(var i = 0; i < columns.length; i++){ + if(tallestColumnHeight < columns[i].height) { + tallestColumnHeight = columns[i].height; + } + } + + var canvasWidth = offsets.left + offsets.right + (columnWidth * numCols); + var canvasHeight = offsets.top + offsets.bottom + tallestColumnHeight; + var setup = inputKinks.setupCanvas(canvasWidth, canvasHeight, username); + var context = setup.context; + var canvas = setup.canvas; + + for(var i = 0; i < columns.length; i++) { + var column = columns[i]; + var drawStack = column.drawStack; + + var drawX = offsets.left + (columnWidth * i); + for(var j = 0; j < drawStack.length; j++){ + var drawCall = drawStack[j]; + drawCall.x = drawX; + drawCall.y += offsets.top; + inputKinks.drawCallHandlers[drawCall.type](context, drawCall); + } + } + + //return $(canvas).insertBefore($('#InputList')); + + // Send canvas to imgur + $.ajax({ + url: 'https://api.imgur.com/3/image', + type: 'POST', + headers: { + // Your application gets an imgurClientId from Imgur + Authorization: 'Client-ID ' + imgurClientId, + Accept: 'application/json' + }, + data: { + // convert the image data to base64 + image: canvas.toDataURL().split(',')[1], + type: 'base64' + }, + success: function(result) { + $('#Loading').hide(); + var url = 'https://i.imgur.com/' + result.data.id + '.png'; + $('#URL').val(url).fadeIn(); + }, + fail: function(){ + $('#Loading').hide(); + alert('Failed to upload to imgur, could not connect'); + } + }); + }, + encode: function(base, input){ + var hashBase = inputKinks.hashChars.length; + var outputPow = inputKinks.maxPow(hashBase, Number.MAX_SAFE_INTEGER); + var inputPow = inputKinks.maxPow(base, Math.pow(hashBase, outputPow)); + + var output = ""; + var numChunks = Math.ceil(input.length / inputPow); + var inputIndex = 0; + for(var chunkId = 0; chunkId < numChunks; chunkId++) { + var inputIntValue = 0; + for(var pow = 0; pow < inputPow; pow++) { + var inputVal = input[inputIndex++]; + if(typeof inputVal === "undefined") break; + var val = inputVal * Math.pow(base, pow); + inputIntValue += val; + } + + var outputCharValue = ""; + while(inputIntValue > 0) { + var maxPow = Math.floor(log(inputIntValue, hashBase)); + var powVal = Math.pow(hashBase, maxPow); + var charInt = Math.floor(inputIntValue / powVal); + var subtract = charInt * powVal; + var char = inputKinks.hashChars[charInt]; + outputCharValue += char; + inputIntValue -= subtract; + } + var chunk = inputKinks.prefix(outputCharValue, outputPow, inputKinks.hashChars[0]); + output += chunk; + } + return output; + }, + decode: function(base, output){ + var hashBase = inputKinks.hashChars.length; + var outputPow = inputKinks.maxPow(hashBase, Number.MAX_SAFE_INTEGER); + + var values = []; + var numChunks = Math.max(output.length / outputPow) + for(var i = 0; i < numChunks; i++){ + var chunk = output.substring(i * outputPow, (i + 1) * outputPow); + var chunkValues = inputKinks.decodeChunk(base, chunk); + for(var j = 0; j < chunkValues.length; j++) { + values.push(chunkValues[j]); + } + } + return values; + }, + decodeChunk: function(base, chunk){ + var hashBase = inputKinks.hashChars.length; + var outputPow = inputKinks.maxPow(hashBase, Number.MAX_SAFE_INTEGER); + var inputPow = inputKinks.maxPow(base, Math.pow(hashBase, outputPow)); + + var chunkInt = 0; + for(var i = 0; i < chunk.length; i++) { + var char = chunk[i]; + var charInt = inputKinks.hashChars.indexOf(char); + var pow = chunk.length - 1 - i; + var intVal = Math.pow(hashBase, pow) * charInt; + chunkInt += intVal; + } + var chunkIntCopy = chunkInt; + + var output = []; + for(var pow = inputPow - 1; pow >= 0; pow--) { + var posBase = Math.floor(Math.pow(base, pow)); + var posVal = Math.floor(chunkInt / posBase); + var subtract = posBase * posVal; + output.push(posVal); + chunkInt -= subtract; + } + output.reverse(); + return output; + }, + updateHash: function(){ + var hashValues = []; + $('#InputList .choices').each(function(){ + var $this = $(this); + var lvlInt = $this.find('.selected').data('levelInt'); + if(!lvlInt) lvlInt = 0; + hashValues.push(lvlInt); + }); + return inputKinks.encode(Object.keys(colors).length, hashValues); + }, + parseHash: function(){ + var hash = location.hash.substring(1); + if(hash.length < 10) return; + + var values = inputKinks.decode(Object.keys(colors).length, hash); + var valueIndex = 0; + $('#InputList .choices').each(function(){ + var $this = $(this); + var value = values[valueIndex++]; + $this.children().eq(value).addClass('selected'); + }); + }, + saveSelection: function(){ + var selection = []; + $('.choice.selected').each(function(){ + // .choice selector + var selector = '.' + this.className.replace(/ /g, '.'); + // .choices selector + selector = '.' + $(this).closest('.choices')[0].className.replace(/ /g, '.') + ' ' + selector; + // .kinkRow selector + selector = '.' + $(this).closest('tr.kinkRow')[0].className.replace(/ /g, '.') + ' ' + selector; + // .kinkCategory selector + selector = '.' + $(this).closest('.kinkCategory')[0].className.replace(/ /g, '.') + ' ' + selector; + selector = selector.replace('.selected', ''); + selection.push(selector); + }); + return selection; + }, + inputListToText: function(){ + var KinksText = ""; + var kinkCats = Object.keys(kinks); + for(var i = 0; i < kinkCats.length; i++){ + var catName = kinkCats[i]; + var catFields = kinks[catName].fields; + var catKinks = kinks[catName].kinks; + KinksText += '#' + catName + "\r\n"; + KinksText += '(' + catFields.join(', ') + ")\r\n"; + for(var j = 0; j < catKinks.length; j++){ + KinksText += '* ' + catKinks[j] + "\r\n"; + } + KinksText += "\r\n"; + } + return KinksText; + }, + restoreSavedSelection: function(selection){ + setTimeout(function(){ + for(var i = 0; i < selection.length; i++){ + var selector = selection[i]; + $(selector).addClass('selected'); + } + location.hash = inputKinks.updateHash(); + }, 300); + }, + parseKinksText: function(kinksText){ + var newKinks = {}; + var lines = kinksText.replace(/\r/g, '').split("\n"); + + var cat = null; + var catName = null; + for(var i = 0; i < lines.length; i++){ + var line = lines[i]; + if(!line.length) continue; + + if(line[0] === '#') { + if(catName){ + if(!(cat.fields instanceof Array) || cat.fields.length < 1){ + alert(catName + ' does not have any fields defined!'); + return; + } + if(!(cat.kinks instanceof Array) || cat.kinks.length < 1){ + alert(catName + ' does not have any kinks listed!'); + return; + } + newKinks[catName] = cat; + } + catName = line.substring(1).trim(); + cat = { kinks: [] }; + } + if(!catName) continue; + if(line[0] === '(') { + cat.fields = line.substring(1, line.length - 1).trim().split(','); + for(var j = 0; j < cat.fields.length; j++){ + cat.fields[j] = cat.fields[j].trim(); + } + } + if(line[0] === '*'){ + var kink = line.substring(1).trim(); + cat.kinks.push(kink); + } + } + if(catName && !newKinks[catName]){ + if(!(cat.fields instanceof Array) || cat.fields.length < 1){ + alert(catName + ' does not have any fields defined!'); + return; + } + if(!(cat.kinks instanceof Array) || cat.kinks.length < 1){ + alert(catName + ' does not have any kinks listed!'); + return; + } + newKinks[catName] = cat; + } + return newKinks; + } + }; + + $('#Edit').on('click', function(){ + var KinksText = inputKinks.inputListToText(); + $('#Kinks').val(KinksText.trim()); + $('#EditOverlay').fadeIn(); + }); + $('#EditOverlay').on('click', function(){ + $(this).fadeOut(); + }); + $('#KinksOK').on('click', function(){ + var selection = inputKinks.saveSelection(); + try { + var kinksText = $('#Kinks').val(); + kinks = inputKinks.parseKinksText(kinksText); + inputKinks.fillInputList(); + } + catch(e){ + alert('An error occured trying to parse the text entered, please correct it and try again'); + return; + } + inputKinks.restoreSavedSelection(selection); + $('#EditOverlay').fadeOut(); + }); + $('.overlay > *').on('click', function(e){ + e.stopPropagation(); + }); + + var stylesheet = document.styleSheets[0]; + $('.legend .choice').each(function(){ + var $choice = $(this); + var $parent = $choice.parent(); + var text = $parent.text().trim(); + var color = $choice.data('color'); + var cssClass = this.className.replace('choice ', '').trim(); + + addCssRule('.choice.' + cssClass, 'background-color: ' + color + ';'); + colors[text] = color; + level[text] = cssClass; + }); + + kinks = inputKinks.parseKinksText($('#Kinks').text().trim()); + inputKinks.init(); + + (function(){ + var $popup = $('#InputOverlay'); + var $previous = $('#InputPrevious'); + var $next = $('#InputNext'); + + // current + var $category = $('#InputCategory'); + var $field = $('#InputField'); + var $options = $('#InputValues'); + + function getChoiceValue($choices){ + var $selected = $choices.find('.choice.selected'); + return $selected.data('level'); + } + + function getChoicesElement(category, kink, field){ + var selector = '.cat-' + strToClass(category); + selector += ' .kink-' + strToClass(kink); + selector += ' .choice-' + strToClass(field); + + var $choices = $(selector); + return $choices; + } + + inputKinks.getAllKinks = function(){ + var list = []; + + var categories = Object.keys(kinks); + for(var i = 0; i < categories.length; i++){ + var category = categories[i]; + var fields = kinks[category].fields; + var kinkArr = kinks[category].kinks; + + for(var j = 0; j < fields.length; j++) { + var field = fields[j]; + for(var k = 0; k < kinkArr.length; k++){ + var kink = kinkArr[k]; + var $choices = getChoicesElement(category, kink, field); + var value = getChoiceValue($choices); + var obj = { category: category, kink: kink, field: field, value: value, $choices: $choices, showField: (fields.length >= 2)}; + list.push(obj); + } + } + + } + return list; + }; + + inputKinks.inputPopup = { + numPrev: 3, + numNext: 3, + allKinks: [], + kinkByIndex: function(i){ + var numKinks = inputKinks.inputPopup.allKinks.length; + i = (numKinks + i) % numKinks; + return inputKinks.inputPopup.allKinks[i]; + }, + generatePrimary: function(kink){ + var $container = $('
'); + var btnIndex = 0; + $('.legend > div').each(function(){ + var $btn = $(this).clone(); + $btn.addClass('big-choice'); + $btn.appendTo($container); + + $('') + .addClass('btn-num-text') + .text(btnIndex++) + .appendTo($btn) + + var text = $btn.text().trim().replace(/[0-9]/g, ''); + if(kink.value === text) { + $btn.addClass('selected'); + } + + $btn.on('click', function(){ + $container.find('.big-choice').removeClass('selected'); + $btn.addClass('selected'); + kink.value = text; + $options.fadeOut(200, function(){ + $options.show(); + inputKinks.inputPopup.showNext(); + }); + var choiceClass = strToClass(text); + kink.$choices.find('.' + choiceClass).click(); + }); + }); + return $container; + }, + generateSecondary: function(kink){ + var $container = $('
'); + $('').addClass(level[kink.value]).appendTo($container); + $('').text(kink.category).appendTo($container); + if(kink.showField){ + $('').text(kink.field).appendTo($container); + } + $('').text(kink.kink).appendTo($container); + return $container; + }, + showIndex: function(index){ + $previous.html(''); + $next.html(''); + $options.html(''); + $popup.data('index', index); + + // Current + var currentKink = inputKinks.inputPopup.kinkByIndex(index); + var $currentKink = inputKinks.inputPopup.generatePrimary(currentKink); + $options.append($currentKink); + $category.text(currentKink.category); + $field.text((currentKink.showField ? '(' + currentKink.field + ') ' : '') + currentKink.kink); + $options.append($currentKink); + + // Prev + for(var i = inputKinks.inputPopup.numPrev; i > 0; i--){ + var prevKink = inputKinks.inputPopup.kinkByIndex(index - i); + var $prevKink = inputKinks.inputPopup.generateSecondary(prevKink); + $previous.append($prevKink); + (function(skip){ + $prevKink.on('click', function(){ + inputKinks.inputPopup.showPrev(skip); + }); + })(i); + } + // Next + for(var i = 1; i <= inputKinks.inputPopup.numNext; i++){ + var nextKink = inputKinks.inputPopup.kinkByIndex(index + i); + var $nextKink = inputKinks.inputPopup.generateSecondary(nextKink); + $next.append($nextKink); + (function(skip){ + $nextKink.on('click', function(){ + inputKinks.inputPopup.showNext(skip); + }); + })(i); + } + }, + showPrev: function(skip){ + if(typeof skip !== "number") skip = 1; + var index = $popup.data('index') - skip; + var numKinks = inputKinks.inputPopup.allKinks.length; + index = (numKinks + index) % numKinks; + inputKinks.inputPopup.showIndex(index); + }, + showNext: function(skip){ + if(typeof skip !== "number") skip = 1; + var index = $popup.data('index') + skip; + var numKinks = inputKinks.inputPopup.allKinks.length; + index = (numKinks + index) % numKinks; + inputKinks.inputPopup.showIndex(index); + }, + show: function(){ + inputKinks.inputPopup.allKinks = inputKinks.getAllKinks(); + inputKinks.inputPopup.showIndex(0); + $popup.fadeIn(); + } + }; + + $(window).on('keydown', function(e){ + if(e.altKey || e.shiftKey || e.ctrlKey) return; + if(!$popup.is(':visible')) return; + + if(e.keyCode === 38) { + inputKinks.inputPopup.showPrev(); + e.preventDefault(); + e.stopPropagation(); + } + if(e.keyCode === 40) { + inputKinks.inputPopup.showNext(); + e.preventDefault(); + e.stopPropagation(); + } + + var btn = -1; + if(e.keyCode >= 96 && e.keyCode <= 101) { + btn = e.keyCode - 96; + } + else if(e.keyCode >= 48 && e.keyCode <= 53) { + btn = e.keyCode - 48; + } + else { + return; + } + + var $btn = $options.find('.big-choice').eq(btn); + $btn.click(); + }); + $('#StartBtn').on('click', inputKinks.inputPopup.show); + $('#InputCurrent .closePopup, #InputOverlay').on('click', function(){ + $popup.fadeOut(); + }); + })(); +}); \ No newline at end of file diff --git a/src/scss/app.scss b/src/scss/app.scss new file mode 100644 index 0000000..e4f21bb --- /dev/null +++ b/src/scss/app.scss @@ -0,0 +1,502 @@ +body { + font-family: 'Verdana', 'Arial'; + font-size: 12px; +} +h2 { + padding: 0; + margin: 0; + margin-top: 10px; + margin-bottom: 5px; + font-size: 18px; +} +table { + border-collapse: collapse; + margin-bottom: 10px; + width: 100%; +} +th { + border: solid #999 1px; + border-right: none; + margin: 0px; + padding: 4px; + background-color: #666; + color: #FFF; +} +th.choicesCol { + box-sizing: border-box; + width: 125px; +} +th + th { + border-left: none; +} +th:last-child { + border-right: solid #999 1px; +} +td { + border-left: solid #999 1px; + border-bottom: solid #999 1px; + border-right: solid #999 1px; + margin: 0px; + padding: 4px; + padding-right: 2px; +} +@-moz-document url-prefix() { + td { + padding: 3.3px; + } +} +td + td { + border-left-style: none; +} +.choice { + box-sizing: border-box; + width: 15px; + height: 15px; + opacity: 0.35; + overflow: hidden; + text-indent: 100px; + border: solid #000 1px; + border-radius: 50%; + outline-style: none!important; + vertical-align: middle; + display: inline-block; + cursor: pointer; + font-size: 0; + padding: 0; +} +.choices .choice { + transition: all 0.3s ease-in-out; +} +.choice + .choice { + margin-left: 5px; +} +.choices .choice:hover { + opacity: 0.75; +} +.choice.selected, .selected > .choice { + opacity: 1; + border-width: 2px; +} + +.legend { + vertical-align: middle; + font-size: 14px; + div { + display: inline-block; + } + .choice { + opacity: 1; + cursor: default; + } +} + +.legend-text { + vertical-align: middle; +} + +#ExportWrapper { + width: 460px; + height: 36px; +} +#URL { + display: none; + position: absolute; + top: 3px; + box-sizing: border-box; + width: 300px; + height: 30px; + border-radius: 4px; + border: solid #CCC 1px; + font-size: 16px; + padding: 10px; + text-align: center; + color: #666; + font-weight: bold; +} +#Export { + position: absolute; + left: 310px; + box-sizing: border-box; + color: #FFF; + text-transform: uppercase; + background-color: #4980ae; + font-size: 18px; + width: 150px; + height: 36px; + border-style: none; + border-radius: 4px; + cursor: pointer; + transition: all 0.3s ease-in-out; + + &:hover { + opacity: 0.85; + } +} +#Loading { + display: none; + overflow: visible; + line-height: 26px; + font-size: 16px; + color: #999; + font-weight: bold; + position: absolute; + top: 4px; + left: 220px; +} +#Loading:before { + content: ''; + position: absolute; + box-sizing: border-box; + width: 26px; + height: 26px; + border-radius: 50%; + border: solid #999 2px; + border-top-color: transparent; + border-left-color: #CCC; + border-right-color: #666; + animation: spin .5s infinite linear; + margin-left: -40px; +} +@media (min-width: 1700px) { + .legend { + position: absolute; + top: 7px; + left: 160px; + } + .legend div { + width: 130px; + } + #ExportWrapper { + position: absolute; + top: -3px; + right: 46px; + } + h1 { + margin-bottom: 0; + } +} +@media (max-width: 1700px) and (min-width: 800px) { + .legend div { + width: 130px; + padding-bottom: 10px; + } + #ExportWrapper { + position: absolute; + top: -3px; + right: 46px; + } +} +@media (max-width: 800px) and (min-width: 598px) { + .legend div { + width: 180px; + padding-bottom: 10px; + padding-left: 10px; + } + #ExportWrapper { + position: relative; + margin-top: 10px; + margin-left: 5px; + } + #URL { + left: 155px; + width: 190px; + font-size: 10px; + } + #Export { + left: 0px; + } + #Loading { + left: 230px; + } +} +@media (max-width: 597px) { + body { + font-size: 10px; + } + table { + min-width: 345px; + } + .legend div { + width: 150px; + padding-bottom: 10px; + padding-left: 10px; + } + #ExportWrapper { + position: relative; + margin-top: 10px; + margin-left: 0px; + width: 345px; + } + #URL { + left: 155px; + width: 190px; + font-size: 10px; + } + #Export { + left: 0px; + } + #Loading { + left: 230px; + } +} +@keyframes spin { + 0% {transform: rotate(0deg);} + 100% {transform: rotate(-360deg);} +} + +#ExportWrapper :last-child:after { + content: ''; + display: block; + clear: both; +} +.kinkCategory { + +} +.col { + float: left; + box-sizing: border-box; + margin: 0; + padding: 5px; +} +.col.col25 { width: 25%; } +.col.col33 { width: 33.33333%; } +.col.col50 { width: 50%; } +.col.col100 { width: 100%; padding: 0px; } +.widthWrapper { + max-width: 1700px; + margin-left: auto; + margin-right: auto; + position: relative; +} +#Edit { + width: 18px; + height: 18px; + background-color: transparent; + background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABIAAAASCAYAAABWzo5XAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAALEgAACxIB0t1+/AAAABx0RVh0U29mdHdhcmUAQWRvYmUgRmlyZXdvcmtzIENTNui8sowAAAAWdEVYdENyZWF0aW9uIFRpbWUAMTAvMTIvMTUsb8MLAAABaElEQVQ4jZWUvY3jMBCF390mzpQ6I1wBS5A6oDuQOxi4Ap4rUAlqwR1M7IhQBYRCR1To7G1giJBkes/7gElGg4/zK7AgVaWI0FpLAIwxlsJWwtbRdR0BrKzruvw9pUTnHEMIP4NCCC8gay2991TVnGVVVSvYCyil9AJ6Z0sY5nJCCEwp5Rc/NefcE6Sqb4OMMRQRigiNMcWSU0pPkIgUIW3b5gmqKkmybdtVzOwnSZRKMcaQJOu6zr66rklylZn3PoO+7vf7P2x0Op2w2+1wuVyybxxHNE0DALjdbgCAaZrweDyw3+/xN8aIvu9hrd3y/qthGHA+n3E4HJDH773/dWnLpmfQdnqfNHs2EeEfkpymCU3TYBiGVerGGByPRwDA9XrFOI7FElX1WZpz7ldLOO9PCCHfIeb7qqrqY9C8hEvlHi1h1lqqKr33xZPZXv4KNMOcc6sX+77/8bdSBJUUY8xZisjqLJb6Bpjss/W5PAkOAAAAAElFTkSuQmCC'); + background-repeat: no-repeat; + float: left; + border-style: none; + outline-style: none!important; + margin-top: 6px; + margin-right: 4px; + opacity: 0.5; + cursor: pointer; +} +#Edit:hover { + opacity: 1; +} + +.overlay { + position: fixed; + top: 0; + left: 0; + right: 0; + bottom: 0; + background-color: rgba(0, 0, 0, 0.8); + display: none; +} +#EditOverlay #Kinks { + box-sizing: border-box; + position: absolute; + top: 10px; + bottom: 50px; + width: 330px; + left: 50%; + margin-left: -165px; + resize: none; + padding: 10px; + border-radius: 5px; + font-family: monospace, 'Courier new', Courier; +} +#EditOverlay #KinksOK { + box-sizing: border-box; + position: absolute; + bottom: 10px; + width: 330px; + height: 30px; + left: 50%; + margin-left: -165px; + color: #FFF; + text-transform: uppercase; + background-color: #4980ae; + font-size: 18px; + border-style: none; + border-radius: 5px; + cursor: pointer; +} + +#InputOverlay { + text-align: center; + white-space: nowrap; +} +#InputOverlay:before { + content: ''; + display: inline-block; + height: 100%; + vertical-align: middle; + margin-right: -0.25em; +} +#InputOverlay .widthWrapper { + display: inline-block; + vertical-align: middle; + width: 400px; + text-align: left; + max-width: 100%; +} +#InputOverlay .widthWrapper #InputCurrent, #InputOverlay .widthWrapper .kink-simple { + display: block; + box-sizing: border-box; + padding: 10px; + background-color: #EEE; +} +#InputOverlay .widthWrapper .kink-simple { + position: relative; + height: 40px; + line-height: 20px; + cursor: pointer; +} +#InputOverlay .widthWrapper .kink-simple .choice { + margin-right: 5px; +} +#InputOverlay .widthWrapper .kink-simple .txt-category { + position: absolute; + right: 5px; + top: 5px; + text-transform: uppercase; + font-size: 90%; + font-weight: bold; + opacity: 0.6; + line-height: 1em; +} +#InputOverlay .widthWrapper .kink-simple .txt-field, #InputOverlay .widthWrapper .kink-simple .txt-kink { + vertical-align: middle; +} +#InputOverlay .widthWrapper .kink-simple .txt-field:empty { + display: none; +} +#InputOverlay .widthWrapper .kink-simple .txt-field:before { + content: '('; +} +#InputOverlay .widthWrapper .kink-simple .txt-field:after { + content: ') '; +} + +#InputOverlay .widthWrapper #InputPrevious .kink-simple:first-child, +#InputOverlay .widthWrapper #InputNext .kink-simple:nth-child(3) { + background-color: #BBB; + font-size: 10px; + margin-left: 12px; + margin-right: 12px; + height: 33px; +} +#InputOverlay .widthWrapper #InputPrevious .kink-simple:nth-child(2), +#InputOverlay .widthWrapper #InputNext .kink-simple:nth-child(2) { + background-color: #CCC; + font-size: 11px; + margin-left: 6px; + margin-right: 6px; + height: 37px; +} +#InputOverlay .widthWrapper #InputPrevious .kink-simple:nth-child(3), +#InputOverlay .widthWrapper #InputNext .kink-simple:first-child { + background-color: #DDD; + margin-left: 3px; + margin-right: 3px; +} +#InputOverlay .widthWrapper #InputPrevious .kink-simple:first-child { + padding-bottom: 4px; + padding-top: 7px; +} +#InputOverlay .widthWrapper #InputNext .kink-simple:nth-child(3) { + padding-top: 4px; +} +#InputOverlay .widthWrapper #InputPrevious .kink-simple:nth-child(2) { + padding-bottom: 7px; + padding-top: 9px; +} +#InputOverlay .widthWrapper #InputNext .kink-simple:nth-child(2) { + padding-top: 7px; +} + +#InputPrevious .kink-simple { + border-top-left-radius: 2px; + border-top-right-radius: 2px; +} +#InputNext .kink-simple { + border-bottom-left-radius: 2px; + border-bottom-right-radius: 2px; +} + +#InputOverlay .widthWrapper #InputCurrent { + position: relative; +} +#InputOverlay .widthWrapper #InputCurrent .closePopup { + position: absolute; + top: 0; + right: 5px; + border-style: none; + background-color: transparent; + font-size: 30px; + cursor: pointer; + outline-style: none!important; + opacity: 0.65; +} +#InputOverlay .widthWrapper #InputCurrent .closePopup:hover { + opacity: 1; +} +#InputOverlay .widthWrapper #InputCurrent h2 { + text-transform: uppercase; + opacity: 0.6; + margin: 0; +} +#InputOverlay .widthWrapper #InputCurrent h3 { + margin-top: 3px; + margin-bottom: 0; + font-size: 14px; +} +#InputOverlay .widthWrapper #InputCurrent #InputValues .big-choice { + padding: 10px; + background-color: rgba(255,255,255,0.75); + border-radius: 4px; + margin-top: 5px; + cursor: pointer; +} +#InputOverlay .widthWrapper #InputCurrent #InputValues .big-choice.selected { + font-weight: bold; +} +#InputOverlay .widthWrapper #InputCurrent #InputValues .big-choice.selected .choice { + opacity: 1; +} +#InputOverlay .widthWrapper #InputCurrent #InputValues .big-choice:hover { + padding: 8px; + border: solid #999 2px; + background-color: rgba(255,255,255,1); +} +#InputOverlay .widthWrapper #InputCurrent #InputValues .big-choice .btn-num-text { + float: right; + display: inline-block; + border: solid #CCC 1px; + text-align: center; + width: 16px; + border-radius: 3px; +} +#StartBtn { + position: absolute; + top: -3px; + right: 5px; + box-sizing: border-box; + width: 36px; + height: 36px; + background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACQAAAAkCAYAAADhAJiYAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAALEgAACxIB0t1+/AAAABx0RVh0U29mdHdhcmUAQWRvYmUgRmlyZXdvcmtzIENTNui8sowAAAAWdEVYdENyZWF0aW9uIFRpbWUAMTAvMTUvMTWxuPuyAAACcElEQVRYhe3X30tTYRzH8fdz3FYTd1xamAuTLDbM3EwqNMnoQgxydVH0A6Ks/oG6Mvp1n1flVUJE3pjUTVPCCJxmEKTWRAuCApujkmhJoZu57ZwuwsCaOx7nbBf73D2H7/PldfjCc54j6CxXSaNI/xvwdzIgrWRAWsmAtJJ2IIOe4iLzejoqmym1lDAZ+UFEicatC8Vm6A++5Jb/Pm+nxlIHatl2keo8FwJBriGHREe8U7bjyrWz7/m51IHq1lWjqire4AAvJkf5qczGrduUvYHTRQdxyQ5dGN0gg8hCBc4MXyUQnliwbqO5kOO2/ZzyXQagONvGcO0DLrxp5m7As3ygxUZB4aTvEt1fnuGSHQgEnoleKnIdEEi8NymQ6h7555nocvJt9jsPJ3o4XFhHq/MaAkFF/xE+hD5p9kwKJLqccZ+HYjMcs9XTXnkdSUi0BToXhYEUnUM7rFvnYRqHryx6b1Ig1T2C6h4h0uDjqK0eSUi4ZAc9VbeRhMS76XFdGFjmkblkB3277yAbc3g/PY7D69bdc9lGNoexGi10fOzG7nWTb7KuLCh8YJAyy2YEgqE997AaLbQFOjnxqom1JiueXTd190xqZOZHOwFoLDrEVCxMm9/D+dfNSELiSVUrpZaSlQXNpTc4wJrHNX/W+SYr5bKdqBr/45soSxqZQWTNW/tDn+etV0umpbT93VtP8awSwZhl4EZZE/7wwgddsdmGJAQxRUktyPt1gIaCvTQU1Ca8eghAVVX6goO6QULPn2vBqnyatpylJm87RrHwu0TVKE+DQ7SMtSe8FSQNWomk3Z06A9JKBqSVDEgraQf6BTg8uT5wSV/pAAAAAElFTkSuQmCC'); + border-style: none; + border-radius: 4px; + cursor: pointer; +} +#StartBtn:hover { + opacity: 0.8; +} +@media (max-height: 500px) { + #InputPrevious, #InputNext { + display: none; + } +} \ No newline at end of file