From 6730e732ce9ad1b82b6dd4700b148f4d8f8dc99d Mon Sep 17 00:00:00 2001 From: Jonny Barnes Date: Wed, 8 Nov 2017 14:38:57 +0000 Subject: [PATCH 1/7] Get rid of old es5 javascript files --- resources/assets/js/form-save.js | 69 ------- resources/assets/js/links.js | 39 ---- resources/assets/js/maps.js | 50 ----- resources/assets/js/newnote.js | 314 ------------------------------- resources/assets/js/newplace.js | 44 ----- 5 files changed, 516 deletions(-) delete mode 100644 resources/assets/js/form-save.js delete mode 100644 resources/assets/js/links.js delete mode 100644 resources/assets/js/maps.js delete mode 100644 resources/assets/js/newnote.js delete mode 100644 resources/assets/js/newplace.js diff --git a/resources/assets/js/form-save.js b/resources/assets/js/form-save.js deleted file mode 100644 index 20d8f0a7..00000000 --- a/resources/assets/js/form-save.js +++ /dev/null @@ -1,69 +0,0 @@ -/* global alertify, store */ -var feature = { - addEventListener : !!window.addEventListener, - querySelectorAll : !!document.querySelectorAll -}; - -if (feature.addEventListener && feature.querySelectorAll) { - var keys = getKeys(); - for (var i = 0; i < keys.length; i++) { - if (store.get(keys[i])) { - var formId = keys[i].split('~')[1]; - document.getElementById(formId).value = store.get(keys[i]); - } - } -} - -var timerId = window.setInterval(function() { - var saved = false; - var inputs = document.querySelectorAll('input[type=text], textarea'); - for (var i = 0; i < inputs.length; i++) { - var key = getFormElement(inputs[i]).id + '~' + inputs[i].id; - if (store.get(key) !== inputs[i].value && inputs[i].value !== '') { - store.set(key, inputs[i].value); - saved = true; - } - } - if (saved === true) { - alertify.logPosition('top right'); - alertify.success('Auto saved text'); - } -}, 5000); -var forms = document.querySelectorAll('form'); -for (var f = 0; f < forms.length; f++) { - var form = forms[f]; - form.addEventListener('submit', function() { - window.clearInterval(timerId); - var formId = form.id; - var storedKeys = store.keys(); - for (var i = 0; i < storedKeys.length; i++) { - if (storedKeys[i].indexOf(formId) > -1) { - store.remove(storedKeys[i]); - } - } - }); -} -function getKeys() { - var keys = []; - var formFields = document.querySelectorAll('input[type=text], textarea'); - for (var f = 0; f < formFields.length; f++) { - var parent = getFormElement(formFields[f]); - if (parent !== false) { - var key = parent.id + '~' + formFields[f].id; - keys.push(key); - } - } - return keys; -} -function getFormElement(elem) { - if (elem.nodeName.toLowerCase() !== 'body') { - var parent = elem.parentNode; - if (parent.nodeName.toLowerCase() === 'form') { - return parent; - } else { - return getFormElement(parent); - } - } else { - return false; - } -} diff --git a/resources/assets/js/links.js b/resources/assets/js/links.js deleted file mode 100644 index 26403569..00000000 --- a/resources/assets/js/links.js +++ /dev/null @@ -1,39 +0,0 @@ -/* global Autolinker */ -//the autlinker object -var autolinker = new Autolinker(); - -//the youtube regex -var ytidregex = /watch\?v=([A-Za-z0-9\-_]+)/; - -var spotifyregex = /https\:\/\/play\.spotify\.com\/(.*)\b/; - -//grab the notes and loop through them -var notes = document.querySelectorAll('.e-content'); -for (var i = 0; i < notes.length; i++) { - //get Youtube ID - var ytid = notes[i].textContent.match(ytidregex); - if (ytid !== null) { - var yid = ytid[1]; - var yiframe = document.createElement('iframe'); - yiframe.classList.add('youtube'); - yiframe.setAttribute('src', '//www.youtube.com/embed/' + yid); - yiframe.setAttribute('frameborder', 0); - yiframe.setAttribute('allowfullscreen', 'true'); - notes[i].appendChild(yiframe); - } - //get Spotify ID - var spotifyid = notes[i].textContent.match(spotifyregex); - if (spotifyid !== null) { - var sid = spotifyid[1].replace('/', ':'); - var siframe = document.createElement('iframe'); - siframe.classList.add('spotify'); - siframe.setAttribute('src', 'https://embed.spotify.com/?uri=spotify:' + sid); - siframe.setAttribute('frameborder', 0); - siframe.setAttribute('allowtransparency', 'true'); - notes[i].appendChild(siframe); - } - //now linkify everything - var orig = notes[i].innerHTML; - var linked = autolinker.link(orig); - notes[i].innerHTML = linked; -} diff --git a/resources/assets/js/maps.js b/resources/assets/js/maps.js deleted file mode 100644 index 6d85beea..00000000 --- a/resources/assets/js/maps.js +++ /dev/null @@ -1,50 +0,0 @@ -/* global mapboxgl */ -//This code runs on page load and looks for
, then adds map -var mapDivs = document.querySelectorAll('.map'); -mapboxgl.accessToken = 'pk.eyJ1Ijoiam9ubnliYXJuZXMiLCJhIjoiY2l2cDhjYW04MDAwcjJ0cG1uZnhqcm82ayJ9.qA2zeVA-nsoMh9IFrd5KQw'; -for (var i = 0; i < mapDivs.length; i++) { - var mapDiv = mapDivs[i]; - var latitude = mapDiv.dataset.latitude; - var longitude = mapDiv.dataset.longitude; - var el = document.createElement('div'); - el.classList.add('marker'); - var mapMenu = document.createElement('div'); - mapMenu.classList.add('map-menu'); - var streetsInput = document.createElement('input'); - streetsInput.setAttribute('id', 'streets'); - streetsInput.setAttribute('type', 'radio'); - streetsInput.setAttribute('name', 'toggle'); - streetsInput.setAttribute('value', 'streets'); - streetsInput.setAttribute('checked', 'checked'); - streetsInput.addEventListener('click', function () { - map.setStyle('mapbox://styles/mapbox/streets-v9'); - }); - var streetsLabel = document.createElement('label'); - streetsLabel.setAttribute('for', 'streets'); - streetsLabel.appendChild(document.createTextNode('Streets')); - var satelliteInput = document.createElement('input'); - satelliteInput.setAttribute('id', 'satellite'); - satelliteInput.setAttribute('type', 'radio'); - satelliteInput.setAttribute('name', 'toggle'); - satelliteInput.setAttribute('value', 'streets'); - satelliteInput.addEventListener('click', function () { - map.setStyle('mapbox://styles/mapbox/satellite-v9'); - }); - var satelliteLabel = document.createElement('label'); - satelliteLabel.setAttribute('for', 'satellite'); - satelliteLabel.appendChild(document.createTextNode('Satellite')); - mapMenu.appendChild(streetsInput); - mapMenu.appendChild(streetsLabel); - mapMenu.appendChild(satelliteInput); - mapMenu.appendChild(satelliteLabel); - var map = new mapboxgl.Map({ - container: mapDiv, - style: 'mapbox://styles/mapbox/streets-v9', - center: [longitude, latitude], - zoom: 15, - scrollZoom: false - }); - map.addControl(new mapboxgl.NavigationControl()); - new mapboxgl.Marker(el, {offset: [-10, -20]}).setLngLat([longitude, latitude]).addTo(map); - mapDiv.appendChild(mapMenu); -} diff --git a/resources/assets/js/newnote.js b/resources/assets/js/newnote.js deleted file mode 100644 index 384f3721..00000000 --- a/resources/assets/js/newnote.js +++ /dev/null @@ -1,314 +0,0 @@ -/* global mapboxgl, alertify */ -if ('geolocation' in navigator) { - var button = document.querySelector('#locate'); - if (button.addEventListener) { - //if we have javascript, event listeners and geolocation, make the locate - //button clickable and add event - button.disabled = false; - button.addEventListener('click', getLocation); - } -} - -function getLocation() { - navigator.geolocation.getCurrentPosition(function (position) { - //the locate button has been clicked so add the places/map - addPlacesMap(position.coords.latitude, position.coords.longitude, position.coords.accuracy); - }); -} - -function addPlacesMap(latitude, longitude, uncertainty) { - //get the nearby places - fetch('/places/near/' + latitude + '/' + longitude + '?u=' + uncertainty, { - credentials: 'same-origin', - method: 'get' - }).then(function (response) { - return response.json(); - }).then(function (j) { - if (j.error === true) { - alertify.reset(); - alertify.error(j.error_description); - } - if (j.places.length > 0) { - var i; - var places = []; - for (i = 0; i < j.places.length; ++i) { - var latlng = parseLocation(j.places[i].location); - var name = j.places[i].name; - var uri = j.places[i].uri; - places.push([name, uri, latlng[0], latlng[1]]); - } - //add a map with the nearby places - addMap(latitude, longitude, places); - } else { - //add a map with just current location - addMap(latitude, longitude); - } - }).catch(function (err) { - console.error(err); - }); -} - -function addMap(latitude, longitude, places) { - //make places null if not supplied - if (arguments.length == 2) { - places = null; - } - // the form has a fieldset element that we are actually targetting - var form = document.querySelector('.note-ui'); - var mapDiv = document.createElement('div'); - mapDiv.classList.add('map'); - //add the map div - form.appendChild(mapDiv); - //set up the mapbox gl map - mapboxgl.accessToken = 'pk.eyJ1Ijoiam9ubnliYXJuZXMiLCJhIjoiY2l2cDhjYW04MDAwcjJ0cG1uZnhqcm82ayJ9.qA2zeVA-nsoMh9IFrd5KQw'; - var map = new mapboxgl.Map({ - container: mapDiv, - style: 'mapbox://styles/mapbox/streets-v9', - center: [longitude, latitude], - zoom: 15 - }); - map.addControl(new mapboxgl.NavigationControl()); - //create the current location marker - var el = document.createElement('div'); - el.classList.add('marker'); - //create the map style menu - var mapMenu = document.createElement('div'); - mapMenu.classList.add('map-menu'); - var streetsInput = document.createElement('input'); - streetsInput.setAttribute('id', 'streets'); - streetsInput.setAttribute('type', 'radio'); - streetsInput.setAttribute('name', 'toggle'); - streetsInput.setAttribute('value', 'streets'); - streetsInput.setAttribute('checked', 'checked'); - streetsInput.addEventListener('click', function () { - map.setStyle('mapbox://styles/mapbox/streets-v9'); - }); - var streetsLabel = document.createElement('label'); - streetsLabel.setAttribute('for', 'streets'); - streetsLabel.appendChild(document.createTextNode('Streets')); - var satelliteInput = document.createElement('input'); - satelliteInput.setAttribute('id', 'satellite'); - satelliteInput.setAttribute('type', 'radio'); - satelliteInput.setAttribute('name', 'toggle'); - satelliteInput.setAttribute('value', 'streets'); - satelliteInput.addEventListener('click', function () { - map.setStyle('mapbox://styles/mapbox/satellite-v9'); - }); - var satelliteLabel = document.createElement('label'); - satelliteLabel.setAttribute('for', 'satellite'); - satelliteLabel.appendChild(document.createTextNode('Satellite')); - mapMenu.appendChild(streetsInput); - mapMenu.appendChild(streetsLabel); - mapMenu.appendChild(satelliteInput); - mapMenu.appendChild(satelliteLabel); - //add the map menu - mapDiv.appendChild(mapMenu); - //add a marker for the current location - new mapboxgl.Marker(el, {offset: [-10, -20]}).setLngLat([longitude, latitude]).addTo(map); - //create containing div for flexbox - var containingDiv = document.createElement('div'); - //create the - places.forEach(function (item) { - var option = document.createElement('option'); - option.setAttribute('value', item[1]); - var text = document.createTextNode(item[0]); - option.appendChild(text); - option.dataset.latitude = item[2]; - option.dataset.longitude = item[3]; - selectEl.appendChild(option); - var placeMarkerIcon = document.createElement('div'); - placeMarkerIcon.classList.add('marker'); - new mapboxgl.Marker(placeMarkerIcon, {offset: [-10, -20]}).setLngLat([item[3], item[2]]).addTo(map); - placeMarkerIcon.addEventListener('click', function () { - map.flyTo({ - center: [ - item[3], - item[2] - ] - }); - selectPlace(item[1]); - }); - }); - //add an event listener - selectEl.addEventListener('change', function () { - if (selectEl.value !== 'no-location') { - var placeLat = selectEl[selectEl.selectedIndex].dataset.latitude; - var placeLon = selectEl[selectEl.selectedIndex].dataset.longitude; - map.flyTo({ - center: [ - placeLon, - placeLat - ] - }); - } - }); - } - //add a button to add a new place - var newLocButton = document.createElement('button'); - newLocButton.setAttribute('type', 'button'); - newLocButton.setAttribute('id', 'create-new-place'); - newLocButton.appendChild(document.createTextNode('Create New Place?')); - //the event listener - newLocButton.addEventListener('click', function() { - //add the form elements - var nameDiv = document.createElement('div'); - var nameLabel = document.createElement('label'); - nameLabel.setAttribute('for', 'place-name'); - nameLabel.classList.add('place-label'); - nameLabel.appendChild(document.createTextNode('Place Name:')); - var nameEl = document.createElement('input'); - nameEl.setAttribute('placeholder', 'Name'); - nameEl.setAttribute('name', 'place-name'); - nameEl.setAttribute('id', 'place-name'); - nameEl.setAttribute('type', 'text'); - nameDiv.appendChild(nameLabel); - nameDiv.appendChild(nameEl); - var descDiv = document.createElement('div'); - var descLabel = document.createElement('label'); - descLabel.setAttribute('for', 'place-description'); - descLabel.classList.add('place-label'); - descLabel.appendChild(document.createTextNode('Place Description:')); - var descEl = document.createElement('input'); - descEl.setAttribute('placeholder', 'Description'); - descEl.setAttribute('name', 'place-description'); - descEl.setAttribute('id', 'place-description'); - descEl.setAttribute('type', 'text'); - descDiv.appendChild(descLabel); - descDiv.appendChild(descEl); - var latDiv = document.createElement('div'); - var latLabel = document.createElement('label'); - latLabel.setAttribute('for', 'place-latitude'); - latLabel.classList.add('place-label'); - latLabel.appendChild(document.createTextNode('Place Latitude:')); - var latEl = document.createElement('input'); - latEl.setAttribute('name', 'place-latitude'); - latEl.setAttribute('id', 'place-latitude'); - latEl.setAttribute('type', 'text'); - latEl.value = getLatitudeFromMapbox(map.getCenter()); - latDiv.appendChild(latLabel); - latDiv.appendChild(latEl); - var lonDiv = document.createElement('div'); - var lonLabel = document.createElement('label'); - lonLabel.setAttribute('for', 'place-longitude'); - lonLabel.classList.add('place-label'); - lonLabel.appendChild(document.createTextNode('Place Longitude:')); - var lonEl = document.createElement('input'); - lonEl.setAttribute('name', 'place-longitude'); - lonEl.setAttribute('id', 'place-longitude'); - lonEl.setAttribute('type', 'text'); - lonEl.value = getLongitudeFromMapbox(map.getCenter()); - lonDiv.appendChild(lonLabel); - lonDiv.appendChild(lonEl); - var placeSubmit = document.createElement('button'); - placeSubmit.setAttribute('id', 'place-submit'); - placeSubmit.setAttribute('value', 'Submit New Place'); - placeSubmit.setAttribute('name', 'place-submit'); - placeSubmit.setAttribute('type', 'button'); - placeSubmit.appendChild(document.createTextNode('Submit New Place')); - form.appendChild(nameDiv); - form.appendChild(descDiv); - form.appendChild(latDiv); - form.appendChild(lonDiv); - form.appendChild(placeSubmit); - //the event listener for the new place form - placeSubmit.addEventListener('click', function () { - //create the form data to send - var formData = new FormData(); - formData.append('place-name', document.querySelector('#place-name').value); - formData.append('place-description', document.querySelector('#place-description').value); - formData.append('place-latitude', document.querySelector('#place-latitude').value); - formData.append('place-longitude', document.querySelector('#place-longitude').value); - //post the new place - fetch('/places/new', { - //send cookies with the request - credentials: 'same-origin', - method: 'post', - body: formData - }) - .then(function (response) { - return response.json(); - }) - .then(function (placeJson) { - if (placeJson.error === true) { - throw new Error(placeJson.error_description); - } - //remove un-needed form elements - //iterate through labels and remove parent div elements - var labels = document.querySelectorAll('.place-label'); - for (var i = 0; i < labels.length; ++i) { - form.removeChild(labels[i].parentNode); - } - form.removeChild(document.querySelector('#place-submit')); - var newPlaceButton = document.querySelector('#create-new-place'); - //in order to remove a DOM Node, you need to run removeChild on the parent Node - newPlaceButton.parentNode.removeChild(newPlaceButton); - //add place marker - var newOption = document.createElement('option'); - newOption.setAttribute('value', placeJson.uri); - newOption.appendChild(document.createTextNode(placeJson.name)); - newOption.dataset.latitude = placeJson.latitude; - newOption.dataset.longitude = placeJson.longitude; - selectEl.appendChild(newOption); - var newPlaceMarkerIcon = document.createElement('div'); - newPlaceMarkerIcon.classList.add('marker'); - new mapboxgl.Marker(newPlaceMarkerIcon, {offset: [-10, -20]}).setLngLat([placeJson.longitude, placeJson.latitude]).addTo(map); - map.flyTo({center: [placeJson.longitude, placeJson.latitude]}); - - newPlaceMarkerIcon.addEventListener('click', function () { - map.flyTo({center: [placeJson.longitude, placeJson.latitude]}); - selectPlace(placeJson.uri); - }); - //make selected - selectPlace(placeJson.uri); - }).catch(function (placeError) { - alertify.reset(); - alertify.error(placeError); - }); - }); - }); - containingDiv.appendChild(newLocButton); -} - -function parseLocation(point) { - var re = /\((.*)\)/; - var resultArray = re.exec(point); - var location = resultArray[1].split(' '); - - return [location[1], location[0]]; -} - -function selectPlace(uri) { - document.querySelector('select [value="' + uri + '"]').selected = true; -} - -function getLatitudeFromMapbox(lnglat) { - var resultArray = /\((.*)\)/.exec(lnglat); - var location = resultArray[1].split(' '); - - return location[1]; -} - -function getLongitudeFromMapbox(lnglat) { - var resultArray = /\((.*)\)/.exec(lnglat); - var location = resultArray[1].split(' '); - - return location[0].replace(',', ''); -} diff --git a/resources/assets/js/newplace.js b/resources/assets/js/newplace.js deleted file mode 100644 index 548b5fff..00000000 --- a/resources/assets/js/newplace.js +++ /dev/null @@ -1,44 +0,0 @@ -/* global L */ -var button = document.querySelector('#locate'); - -if (button.addEventListener) { - button.addEventListener('click', getLocation); -} else { - button.attachEvent('onclick', getLocation); -} - -function getLocation() { - if ('geolocation' in navigator) { - navigator.geolocation.getCurrentPosition(function(position) { - updateForm(position.coords.latitude, position.coords.longitude); - addMap(position.coords.latitude, position.coords.longitude); - }); - } -} - -function updateForm(latitude, longitude) { - var inputLatitude = document.querySelector('#latitude'); - var inputLongitude = document.querySelector('#longitude'); - inputLatitude.value = latitude; - inputLongitude.value = longitude; -} - -function addMap(latitude, longitude) { - var form = document.querySelector('form'); - var div = document.createElement('div'); - div.setAttribute('id', 'map'); - form.appendChild(div); - L.mapbox.accessToken = 'pk.eyJ1Ijoiam9ubnliYXJuZXMiLCJhIjoiVlpndW1EYyJ9.aP9fxAqLKh7lj0LpFh5k1w'; - var map = L.mapbox.map('map', 'jonnybarnes.gnoihnim') - .setView([latitude, longitude], 15) - .addLayer(L.mapbox.tileLayer('jonnybarnes.gnoihnim', { - detectRetina: true - })); - var marker = L.marker([latitude, longitude], { - draggable: true - }).addTo(map); - marker.on('dragend', function () { - var markerLocation = marker.getLatLng(); - updateForm(markerLocation.lat, markerLocation.lng); - }); -} From 6e764012709fd14a9339f0e0a0265f498490d592 Mon Sep 17 00:00:00 2001 From: Jonny Barnes Date: Wed, 8 Nov 2017 16:19:21 +0000 Subject: [PATCH 2/7] Fix tag text colour once visited on bookmarks page --- changelog.md | 3 +++ public/assets/css/app.css | 2 +- public/assets/css/app.css.br | Bin 3496 -> 3496 bytes public/assets/css/app.css.gz | Bin 3721 -> 3724 bytes public/assets/css/app.css.map | 2 +- resources/assets/sass/_tags.scss | 3 ++- 6 files changed, 7 insertions(+), 3 deletions(-) diff --git a/changelog.md b/changelog.md index 394482ab..938f6655 100644 --- a/changelog.md +++ b/changelog.md @@ -1,5 +1,8 @@ # Changelog +## Version {next} + - Fix style of tags on bookmarks page that had been visited + ## Version 0.12.4 (2017-11-07) - Pull in newer version of my linkify extension to fix errors diff --git a/public/assets/css/app.css b/public/assets/css/app.css index 5b52b597..565aec45 100644 --- a/public/assets/css/app.css +++ b/public/assets/css/app.css @@ -1,2 +1,2 @@ -html{-webkit-box-sizing:border-box;box-sizing:border-box}*,*::before,*::after{-webkit-box-sizing:inherit;box-sizing:inherit}html{font-size:10px;font-family:"filson-soft"}a.u-syndication{text-decoration:none}#topheader{display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-orient:horizontal;-webkit-box-direction:normal;-ms-flex-flow:row;flex-flow:row;-webkit-box-align:center;-ms-flex-align:center;align-items:center;width:100%;min-height:5rem}#topheader h1{font-size:2rem;padding:0 2rem}#topheader nav{font-size:2rem}main{display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-orient:vertical;-webkit-box-direction:normal;-ms-flex-direction:column;flex-direction:column;-webkit-box-align:stretch;-ms-flex-align:stretch;align-items:stretch;max-width:512px;margin:0 auto;padding:0 0.5rem}.h-entry{font-size:2rem}.top-space{padding-top:2rem}.mini-h-card{position:relative}.mini-h-card .p-name{position:relative}.mini-h-card:hover .p-name{z-index:100}.mini-h-card:hover .hovercard{display:-webkit-box;display:-ms-flexbox;display:flex}.hovercard{position:absolute;-webkit-box-orient:horizontal;-webkit-box-direction:normal;-ms-flex-direction:row;flex-direction:row;-webkit-box-pack:justify;-ms-flex-pack:justify;justify-content:space-between;-webkit-box-align:end;-ms-flex-align:end;align-items:flex-end;background:white;width:30rem;left:-10px;top:-10px;z-index:50;padding:2rem 1rem 1rem;border-radius:2px;-webkit-box-shadow:3px 3px 2px 1px #101314;box-shadow:3px 3px 2px 1px #101314;display:none}.mini-h-card .social-icon{width:auto;height:2rem}.mini-h-card .u-photo{height:10rem}.note{display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-orient:vertical;-webkit-box-direction:normal;-ms-flex-direction:column;flex-direction:column;font-size:2rem}.note img{max-width:100%;max-height:80vh}.note-metadata{display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-orient:horizontal;-webkit-box-direction:normal;-ms-flex-direction:row;flex-direction:row;-webkit-box-pack:justify;-ms-flex-pack:justify;justify-content:space-between}.icon{width:auto;height:1em}.pagination{display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-orient:horizontal;-webkit-box-direction:normal;-ms-flex-direction:row;flex-direction:row;-webkit-box-pack:space-evenly;-ms-flex-pack:space-evenly;justify-content:space-evenly;font-size:2rem;list-style-type:none}main .contact{font-size:2rem;display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-orient:horizontal;-webkit-box-direction:reverse;-ms-flex-direction:row-reverse;flex-direction:row-reverse;-webkit-box-pack:justify;-ms-flex-pack:justify;justify-content:space-between;margin-top:2rem}.contact .u-photo{width:auto;height:8rem}#projects{font-size:2rem}body>div.h-card{max-width:512px;margin:0 auto;font-size:1.5rem}footer{max-width:512px;margin:0 auto;display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-orient:vertical;-webkit-box-direction:normal;-ms-flex-direction:column;flex-direction:column;-webkit-box-align:center;-ms-flex-align:center;align-items:center}.p-bridgy-twitter-content,.p-bridgy-facebook-content{display:none}span[role=img][aria-label],span[role=img][aria-label]{position:relative}span[role=img][aria-label]:focus::after,span[role=img][aria-label]:hover::after{position:absolute;display:block;z-index:1;bottom:1.5em;left:0;max-width:5em;padding:0.5em 0.75em;border:0.05em solid #fff;border-radius:0.2em;-webkit-box-shadow:0.15em 0.15em 0.5em #000;box-shadow:0.15em 0.15em 0.5em #000;content:attr(aria-label);background-color:rgba(0,0,0,0.85);color:#fff;font-size:80%;-webkit-animation:TOOLTIP 0.1s ease-out 1;animation:TOOLTIP 0.1s ease-out 1}@-webkit-keyframes TOOLTIP{from{bottom:0.5em;background-color:transparent;border:0.05em solid rgba(255,255,255,0);color:rgba(255,255,255,0);-webkit-box-shadow:0 0 0 #000;box-shadow:0 0 0 #000}to{bottom:1.5em;background-color:rgba(0,0,0,0.85);border:0.05em solid #fff;color:#fff;-webkit-box-shadow:0.15em 0.15em 0.5em #000;box-shadow:0.15em 0.15em 0.5em #000}}@keyframes TOOLTIP{from{bottom:0.5em;background-color:transparent;border:0.05em solid rgba(255,255,255,0);color:rgba(255,255,255,0);-webkit-box-shadow:0 0 0 #000;box-shadow:0 0 0 #000}to{bottom:1.5em;background-color:rgba(0,0,0,0.85);border:0.05em solid #fff;color:#fff;-webkit-box-shadow:0.15em 0.15em 0.5em #000;box-shadow:0.15em 0.15em 0.5em #000}}@media print{span[role=img][aria-label]::after{content:" (" attr(aria-label) ") "}}.map{height:200px}.mapboxgl-ctrl-logo{border-bottom:none}.marker{background-image:url(data:image/png;base64,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);background-size:contain;width:20px;height:20px}.map-menu{position:absolute;top:0;left:0;background:white;padding:0.4rem}.map-menu label{margin-left:3px;margin-right:3px}body{background-color:var(--brwhite);color:var(--black)}#topheader{background-color:var(--black);color:var(--white)}a,a:visited{color:var(--blue)}#topheader a{text-decoration:none}h1 a{text-decoration:none}.tags{margin:0;overflow:hidden;padding:0}.tags li{float:left;list-style-type:none}.tag{background:var(--white);border-radius:3px 0 0 3px;color:var(--black);display:inline-block;height:2.6rem;line-height:2.6rem;padding:0 2rem 0 1rem;position:relative;margin:0 1rem 1rem 0;text-decoration:none;-webkit-transition:color 0.2s;transition:color 0.2s}.tag::after{background:var(--brwhite);border-bottom:1.3rem solid transparent;border-left:1rem solid var(--white);border-top:1.3rem solid transparent;content:'';position:absolute;right:0;top:0}.tag:hover{background-color:var(--red);color:var(--black)}.tag:hover::after{border-left-color:var(--red)} +html{-webkit-box-sizing:border-box;box-sizing:border-box}*,*::before,*::after{-webkit-box-sizing:inherit;box-sizing:inherit}html{font-size:10px;font-family:"filson-soft"}a.u-syndication{text-decoration:none}#topheader{display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-orient:horizontal;-webkit-box-direction:normal;-ms-flex-flow:row;flex-flow:row;-webkit-box-align:center;-ms-flex-align:center;align-items:center;width:100%;min-height:5rem}#topheader h1{font-size:2rem;padding:0 2rem}#topheader nav{font-size:2rem}main{display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-orient:vertical;-webkit-box-direction:normal;-ms-flex-direction:column;flex-direction:column;-webkit-box-align:stretch;-ms-flex-align:stretch;align-items:stretch;max-width:512px;margin:0 auto;padding:0 0.5rem}.h-entry{font-size:2rem}.top-space{padding-top:2rem}.mini-h-card{position:relative}.mini-h-card .p-name{position:relative}.mini-h-card:hover .p-name{z-index:100}.mini-h-card:hover .hovercard{display:-webkit-box;display:-ms-flexbox;display:flex}.hovercard{position:absolute;-webkit-box-orient:horizontal;-webkit-box-direction:normal;-ms-flex-direction:row;flex-direction:row;-webkit-box-pack:justify;-ms-flex-pack:justify;justify-content:space-between;-webkit-box-align:end;-ms-flex-align:end;align-items:flex-end;background:white;width:30rem;left:-10px;top:-10px;z-index:50;padding:2rem 1rem 1rem;border-radius:2px;-webkit-box-shadow:3px 3px 2px 1px #101314;box-shadow:3px 3px 2px 1px #101314;display:none}.mini-h-card .social-icon{width:auto;height:2rem}.mini-h-card .u-photo{height:10rem}.note{display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-orient:vertical;-webkit-box-direction:normal;-ms-flex-direction:column;flex-direction:column;font-size:2rem}.note img{max-width:100%;max-height:80vh}.note-metadata{display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-orient:horizontal;-webkit-box-direction:normal;-ms-flex-direction:row;flex-direction:row;-webkit-box-pack:justify;-ms-flex-pack:justify;justify-content:space-between}.icon{width:auto;height:1em}.pagination{display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-orient:horizontal;-webkit-box-direction:normal;-ms-flex-direction:row;flex-direction:row;-webkit-box-pack:space-evenly;-ms-flex-pack:space-evenly;justify-content:space-evenly;font-size:2rem;list-style-type:none}main .contact{font-size:2rem;display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-orient:horizontal;-webkit-box-direction:reverse;-ms-flex-direction:row-reverse;flex-direction:row-reverse;-webkit-box-pack:justify;-ms-flex-pack:justify;justify-content:space-between;margin-top:2rem}.contact .u-photo{width:auto;height:8rem}#projects{font-size:2rem}body>div.h-card{max-width:512px;margin:0 auto;font-size:1.5rem}footer{max-width:512px;margin:0 auto;display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-orient:vertical;-webkit-box-direction:normal;-ms-flex-direction:column;flex-direction:column;-webkit-box-align:center;-ms-flex-align:center;align-items:center}.p-bridgy-twitter-content,.p-bridgy-facebook-content{display:none}span[role=img][aria-label],span[role=img][aria-label]{position:relative}span[role=img][aria-label]:focus::after,span[role=img][aria-label]:hover::after{position:absolute;display:block;z-index:1;bottom:1.5em;left:0;max-width:5em;padding:0.5em 0.75em;border:0.05em solid #fff;border-radius:0.2em;-webkit-box-shadow:0.15em 0.15em 0.5em #000;box-shadow:0.15em 0.15em 0.5em #000;content:attr(aria-label);background-color:rgba(0,0,0,0.85);color:#fff;font-size:80%;-webkit-animation:TOOLTIP 0.1s ease-out 1;animation:TOOLTIP 0.1s ease-out 1}@-webkit-keyframes TOOLTIP{from{bottom:0.5em;background-color:transparent;border:0.05em solid rgba(255,255,255,0);color:rgba(255,255,255,0);-webkit-box-shadow:0 0 0 #000;box-shadow:0 0 0 #000}to{bottom:1.5em;background-color:rgba(0,0,0,0.85);border:0.05em solid #fff;color:#fff;-webkit-box-shadow:0.15em 0.15em 0.5em #000;box-shadow:0.15em 0.15em 0.5em #000}}@keyframes TOOLTIP{from{bottom:0.5em;background-color:transparent;border:0.05em solid rgba(255,255,255,0);color:rgba(255,255,255,0);-webkit-box-shadow:0 0 0 #000;box-shadow:0 0 0 #000}to{bottom:1.5em;background-color:rgba(0,0,0,0.85);border:0.05em solid #fff;color:#fff;-webkit-box-shadow:0.15em 0.15em 0.5em #000;box-shadow:0.15em 0.15em 0.5em #000}}@media print{span[role=img][aria-label]::after{content:" (" attr(aria-label) ") "}}.map{height:200px}.mapboxgl-ctrl-logo{border-bottom:none}.marker{background-image:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAMgAAADICAMAAACahl6sAAAAA3NCSVQICAjb4U/gAAAACXBIWXMAAAsTAAALEwEAmpwYAAACxFBMVEUAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADMyaeDAAAA63RSTlMAAQIDBAUGBwgJCgsMDQ4PEBESExQVFhcYGRobHB0eHyAhIiMkJSYnKCkqKywtLi8wMTIzNDU2Nzg5Ozw9Pj9AQUJERUZHSElKS05PUlNVVldYWVpbXF1fYGFiY2RmZ2hpa2xtbm9wcXJzdHV2d3h5ent8fX+AgYKDhIWGh4iJiouMjo+QkZOUlZaXmJmam5ydnp+goaKjpKWmp6ipqqutrq+xsrO0tbe4ubq7vL2+v8DBwsPExcbHyMnKy8zP0NHS09TV1tfY2drb3N3f4OHi4+Tl5ujp6uvs7e7v8PHy8/T19vf4+fr7/P3+xn8cLwAAB2BJREFUGBntwYtjlWUdB/Dvuwtjo23CGPcxtlGAFhgWFCINSZciCYGKwLSbMwuQi4lgbkSTgYOAiYEI5a0JmQhRAYKBgmzJbSwgGTDYxs45nO8/0d0Mzu897+V53kv1+QD/9z8jd9T9ize/tfdw04VY+9mjf9hV/1xFWXEKQiV11Nytp5nIlfdq781HOBRWvHaBZuLvPVuWhoDLmbkjTgvOVN+CABu/qZ2WHZrTA4Fk3L2X9lxa2geBkzLlIO3rqBmIYBl/mM5ElmUjOPpuonPNkxEQqRUX6cqbn0EQFL1Dtzor4L9JF6jAK93hr4zlVOP4aPhpwH6qEvkO/DPsJBWqhF++9BGVqkuDL8raqNgvs+CDSVEqtysLniu9Qg3q0+Cxz7dSixcMeKrkNDVZCi/1PEptHoV3jDeoT3QMPDOXtnTEaEdTHjwyJkpLLm+rmjGm4IY0ILPXsImz1zXQmnoDnshrogVHnhiTjmv0v2/LFVowG554iUldXjEaid1Qvo9JRYfDAxOYzPlFeTAxYSeT+a0B7TIaaS72k1wkcfsRJjEd2i2gud+PQHJd5rXT1Nnu0KyonWauPpECS246TFPPQbMNNHN6PKzKep5mrg6BViUxmmgohA3zaaYOWv2UJvblw5ZZMcqihdBoQCdl+7Nh09Q4ZSuh0bOUNebDtgrKrvSFNjltFJ0ZBAeWUrYE2syg6OoEOJG6k6ITBnTZQdFiONPvLEWl0KQwTsm+VDg0kaJ10GQBJVe/AMdeo6Q1E3ocoWQlnBvUTskUaDGIkkt5cOFpStZDixmULIUb+W0UnIQWGyjo6ANXqikZDB2aKaiDO4VxCsqhwRBKSuHSDgpeggYzKThhwKWZFDRDgx9TUAW3cqIU5EC91ym4A67tpuCLUK+RiUW6wbUlFNwH5dKjTOx3cO92Cp6CckMpqIV7vSnYAuXupOBRKHCeib0D5e6loAwK7GFiR6DcTAo+CwW2MLFmKFdBQSEUWMvEWqHcQgp6QIFqJhY3oFolBRlQYDEFn4Jq1RRkQ4GlFORBtSUU9IMCtRRkQLW5FAyBAhuZWATKPUzBGCiwjYn9GcrdRcGDUOA4E9sP5YZS8Azcy4wzsc1QLiPOxF6FeyMo+BHUO8bEzhpw7VsUTId6L1PwObj2CwqGQ735FDwCt4xzTKwjDeqVUbAdbo2lYC806ElBfCBcWkNBDXQ4RME8uNP1AgVfhw4rKTiaClemU9IbOkymZBrcSGmg4ANo0YeS9w24MJmSWuhxgJKpcC79MCX3QI9nKPlTDhybTUkkG3qMo6gaThVcpuRtaJJ2kZLYWDhjbKPoB9Dl5xSd6glH5lN2E3SZRtkbKXBgXIyiRmiTG6GsBvYNO0dZJfTZShMLYdeAkzQxGvqU08xjsKfgA5poNqBPrxjNVBmw4cYmmqmBTr+mqZ9lwrLSFpq6FTrNormDQ2FNyg+v0tRJAzp176S5y+UGLCjaziSqoNfrTGb3zUgmY2E7kxkJvb7BpGJrSmAm7YE/MqkGaJbVyuRiG0dCkv3NY7RgAXRbR0ven1OA66Xf+WI7rYgXQrdxtKqxdvKwdHwsf+zcX7XRorehnXGMNkQb33x5fc3qTfV7WmjHg9BvEfVry4Z+xXFq9wK88Ba1uw1emErdGuCJri3UbA68sZx6RXvDG8Op1yvwyh5qdQe8Mp06HTXglcwWajQH3qmmPp358M5Q6rMJXtpObW6DlyZRlwPwVFoTNSmHtxZQj/NZ8FavTmqxDF7bQB3iJfDaKOpQD+/tpgZfhfemUL1D8EHaKSr3EPzwOFVryYIf8tqpWCX8sYpqRQvgj6FxKvUi/FJPpW6BX8ZTpZ3wz7tU6G74536q02jAP+mnqMzD8NP3qcqZrvBTzkUqshD+qqIabXnwV/8IlVgOv9VRhVgR/HZjnApsgv9epQI3w39fpntbEQS/oWulCIK76NZeBMMBunQPgmEq3TlsIBhSP6QrDyAoyunG8TQERZdmuvBtBMf36NyZTARHt4/o2OMIkoV0qiUHQZJ7gQ49iWBZQmdaeyBYel6mI5UImmV0or0XgqZvBx2oRvDU0L4r/RA8Azpp2woE0Sra1VmAICqM0KZVCKa1tCc6CMFUEqUtaxFUdbQjWoSgGhyjDWsRXM/TumgxgmtwjJatQ5Ctp1XREgTZ4BgtWodgq6M10WIEW3GUlqxB0K2lFZFBCLqiCC1YjeBbzeQ6ByL4BnYyqRUIg5VMpqM/wqB/B5OoRjhU01xbH4RD7zaaqkJYVNJMax7CIq+VJp5CeCyirCUX4ZF7jqJ5CJO5lJzphjDJOk1BBcLlESbWlIFwyTjJhB5C2MxiIh+mI2zSGpjANITPFF7vYArCx3iX15mIMPoar7UH4bSL1/gKwulW/qdtCKt6flJ8JMJqRJyfsBnhtZH/Fv00wqs4wo/VIsyW81/a+iHMel3iPz2NcHuS/3AuF+GWfZZ/9xjC7rv8mxMZCLsuR/lX0xF+U0geTEH4GfvIMvw3KOV2aPcXaWsyKghlwmgAAAAASUVORK5CYII=);background-size:contain;width:20px;height:20px}.map-menu{position:absolute;top:0;left:0;background:white;padding:0.4rem}.map-menu label{margin-left:3px;margin-right:3px}body{background-color:var(--brwhite);color:var(--black)}#topheader{background-color:var(--black);color:var(--white)}a,a:visited{color:var(--blue)}#topheader a{text-decoration:none}h1 a{text-decoration:none}.tags{margin:0;overflow:hidden;padding:0}.tags li{float:left;list-style-type:none}.tag,.tag:visited{background:var(--white);border-radius:3px 0 0 3px;color:var(--black);display:inline-block;height:2.6rem;line-height:2.6rem;padding:0 2rem 0 1rem;position:relative;margin:0 1rem 1rem 0;text-decoration:none;-webkit-transition:color 0.2s;transition:color 0.2s}.tag::after{background:var(--brwhite);border-bottom:1.3rem solid transparent;border-left:1rem solid var(--white);border-top:1.3rem solid transparent;content:'';position:absolute;right:0;top:0}.tag:hover{background-color:var(--red);color:var(--black)}.tag:hover::after{border-left-color:var(--red)} /*# sourceMappingURL=app.css.map */ \ No newline at end of file diff --git a/public/assets/css/app.css.br b/public/assets/css/app.css.br index 5cc0e47f7faf04ad00ae1e7fda0b50041b86a5c6..846d2bfe0a6494dfb1912b3c58cf510ade3791c5 100644 GIT binary patch literal 3496 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 HYqm824r%3Y diff --git a/public/assets/css/app.css.gz b/public/assets/css/app.css.gz index 98685647c346aaceb67c7b252cff617d4daf412a..bd0c798f4f596da8d42bedb3ccfff43f6a996f79 100644 GIT binary patch literal 3724 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 Date: Thu, 9 Nov 2017 11:29:46 +0000 Subject: [PATCH 3/7] Add note styles to the notes listed on the tagged page --- resources/views/notes/tagged.blade.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/resources/views/notes/tagged.blade.php b/resources/views/notes/tagged.blade.php index 98d349e9..2c1d8b2f 100644 --- a/resources/views/notes/tagged.blade.php +++ b/resources/views/notes/tagged.blade.php @@ -5,7 +5,7 @@ @section('content')

Notes tagged with {{ $tag }}

@foreach ($notes as $note) -
+
{!! $note->note !!} {{ $note->humandiff }}
From 14cba199036aa1d0a16dcefa812ddceae255bdd5 Mon Sep 17 00:00:00 2001 From: Jonny Barnes Date: Thu, 9 Nov 2017 11:30:54 +0000 Subject: [PATCH 4/7] Remove unused validation rule, and remove note tagging code, its now in the references observer --- app/Providers/AppServiceProvider.php | 33 ++-------------------------- 1 file changed, 2 insertions(+), 31 deletions(-) diff --git a/app/Providers/AppServiceProvider.php b/app/Providers/AppServiceProvider.php index 0a3ffb87..2fe35bbd 100644 --- a/app/Providers/AppServiceProvider.php +++ b/app/Providers/AppServiceProvider.php @@ -2,10 +2,9 @@ namespace App\Providers; -use App\Tag; use App\Note; -use Validator; use Illuminate\Http\Request; +use App\Observers\NoteObserver; use Laravel\Dusk\DuskServiceProvider; use Illuminate\Support\ServiceProvider; @@ -18,35 +17,7 @@ class AppServiceProvider extends ServiceProvider */ public function boot() { - // Validate photos for a maximum filesize - Validator::extend('photosize', function ($attribute, $value, $parameters, $validator) { - if ($value[0] !== null) { - foreach ($value as $file) { - if ($file->getSize() > 5000000) { - return false; - } - } - } - - return true; - }); - - //Add tags for notes - Note::created(function ($note) { - $tagsToAdd = []; - preg_match_all('/#([^\s<>]+)\b/', $note->note, $tags); - foreach ($tags[1] as $tag) { - $tag = Tag::normalizeTag($tag); - } - $tags = array_unique($tags[1]); - foreach ($tags as $tag) { - $tag = Tag::firstOrCreate(['tag' => $tag]); - $tagsToAdd[] = $tag->id; - } - if (count($tagsToAdd) > 0) { - $note->tags()->attach($tagsToAdd); - } - }); + Note::observe(NoteObserver::class); // Request AS macro Request::macro('wantsActivityStream', function () { From 467b60777437c1bec9d7a473781c3969e1fb0a36 Mon Sep 17 00:00:00 2001 From: Jonny Barnes Date: Thu, 9 Nov 2017 11:31:17 +0000 Subject: [PATCH 5/7] use model observer to manage tagging of notes --- app/Note.php | 2 +- app/Observers/NoteObserver.php | 76 ++++++++++++++++++++++++++++++++++ app/Tag.php | 18 +------- 3 files changed, 79 insertions(+), 17 deletions(-) create mode 100644 app/Observers/NoteObserver.php diff --git a/app/Note.php b/app/Note.php index dc932019..ef94e327 100644 --- a/app/Note.php +++ b/app/Note.php @@ -462,7 +462,7 @@ class Note extends Model $name = str_replace('#', '', $name); $replacements[$name] = ''; diff --git a/app/Observers/NoteObserver.php b/app/Observers/NoteObserver.php new file mode 100644 index 00000000..4b9dc1dd --- /dev/null +++ b/app/Observers/NoteObserver.php @@ -0,0 +1,76 @@ +getTagsFromNote($note->getAttributes()['note']); + + if (count($tags) === 0) { + return; + } + + $tags->transform(function ($tag) { + return Tag::firstOrCreate(['tag' => $tag]); + }); + + $note->tags()->attach($tags->map(function ($tag) { + return $tag->id; + })); + } + + /** + * Listen to the Note updated event. + * + * @param \App\Note $Note + * @return void + */ + public function updated(Note $note) + { + $tags = $this->getTagsFromNote($note->getAttributes()['note']); + if (count($tags) === 0) { + return; + } + + $tags->transform(function ($tag) { + return Tag::firstOrCreate(['tag' => $tag]); + }); + + $note->tags()->sync($tags->map(function ($tag) { + return $tag->id; + })); + } + + /** + * Listen to the Note deleting event. + * + * @param \App\Note $note + * @return void + */ + public function deleting(Note $note) + { + $note->tags()->detach(); + } + + public function getTagsFromNote($note) + { + preg_match_all('/#([^\s<>]+)\b/', $note, $tags); + if (array_get($tags, '1') === null) { + return []; + } + + return collect($tags[1])->map(function ($tag) { + return Tag::normalize($tag); + })->unique(); + } +} diff --git a/app/Tag.php b/app/Tag.php index 0e7b6e3e..8b876d2e 100644 --- a/app/Tag.php +++ b/app/Tag.php @@ -6,13 +6,6 @@ use Illuminate\Database\Eloquent\Model; class Tag extends Model { - /** - * The database table used by the model. - * - * @var string - */ - protected $table = 'tags'; - /** * Define the relationship with tags. * @@ -31,13 +24,6 @@ class Tag extends Model return $this->belongsToMany('App\Bookmark'); } - /** - * The attributes excluded from the model's JSON form. - * - * @var array - */ - protected $hidden = ['deleted']; - /** * We shall set a blacklist of non-modifiable model attributes. * @@ -52,7 +38,7 @@ class Tag extends Model */ public function setTagAttribute($value) { - $this->attributes['tag'] = $this->normalizeTag($value); + $this->attributes['tag'] = $this->normalize($value); } /** @@ -61,7 +47,7 @@ class Tag extends Model * * @param string */ - public static function normalizeTag($tag) + public static function normalize($tag) { return mb_strtolower( preg_replace( From a8c376a4c9d3895a69a8d6af5b752892960c1155 Mon Sep 17 00:00:00 2001 From: Jonny Barnes Date: Thu, 9 Nov 2017 11:32:16 +0000 Subject: [PATCH 6/7] Update changelog --- changelog.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/changelog.md b/changelog.md index 938f6655..2d947abb 100644 --- a/changelog.md +++ b/changelog.md @@ -2,6 +2,8 @@ ## Version {next} - Fix style of tags on bookmarks page that had been visited + - Fix style of notes listed on `/notes/tagged/tag` + - Move code manging tagging of notes to NoteObserver ## Version 0.12.4 (2017-11-07) - Pull in newer version of my linkify extension to fix errors From 5b8799d5781027e15784899bf2fd1510ed1c8eee Mon Sep 17 00:00:00 2001 From: Jonny Barnes Date: Thu, 9 Nov 2017 11:32:50 +0000 Subject: [PATCH 7/7] Bump version number to 0.12.5 --- changelog.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/changelog.md b/changelog.md index 2d947abb..4f2825d9 100644 --- a/changelog.md +++ b/changelog.md @@ -1,6 +1,6 @@ # Changelog -## Version {next} +## Version 0.12.5 (2017-11-09) - Fix style of tags on bookmarks page that had been visited - Fix style of notes listed on `/notes/tagged/tag` - Move code manging tagging of notes to NoteObserver