src/js/githubOauth.js
- import Molecule from './molecules/molecule.js'
- import GlobalVariables from './globalvariables.js'
- import { licenses } from './licenseOptions.js'
- import { extractBomTags, convertLinks } from './BOM.js'
- import { OAuth } from 'oauthio-web'
-
- /**
- * This function works like a class to sandbox interaction with GitHub.
- */
- export default function GitHubModule(){
- const { Octokit } = require("@octokit/rest")
- /**
- * The octokit instance which allows authenticated interaction with GitHub.
- * @type {object}
- */
- var octokit = new Octokit()
- /**
- * The HTML element which is the popup.
- * @type {object}
- */
- var popup = document.getElementById('projects-popup')
- /**
- * The name of the current repo.
- * @type {string}
- */
- var currentRepoName = null
- /**
- * The name of the currently logged in user.
- * @type {string}
- */
- var currentUser = null
- /**
- * The text to display at the top of the bill of materials.
- * @type {string}
- */
- var bomHeader = "###### Note: Do not edit this file directly, it is automatically generated from the CAD model \n# Bill Of Materials \n |Part|Number Needed|Price|Source| \n |----|----------|-----|-----|"
-
- /**
- * The text to display at the top of the ReadMe file.
- * @type {string}
- */
- var readmeHeader = "###### Note: Do not edit this file directly, it is automatically generated from the CAD model"
-
- /**
- * The timer used to trigger saving of the file.
- * @type {object}
- */
- var intervalTimer
-
- /**
- * The timer used to trigger saving of the file.
- * @type {object}
- */
- var page = 1
-
- //Github pop up event listeners
- document.getElementById("loginButton").addEventListener("mousedown", () => {
- this.tryLogin()
- })
-
- /**
- * Try to login using the oauth popup.
- */
- this.tryLogin = function(){
-
- // Initialize with OAuth.io app public key
- if(window.location.href.includes('private')){
- OAuth.initialize('6CQQE8MMCBFjdWEjevnTBMCQpsw') //app public key for repo scope
- }
- else{
- OAuth.initialize('BYP9iFpD7aTV9SDhnalvhZ4fwD8') //app public key for public_repo scope
- }
- // Use popup for oauth
- OAuth.popup('github').then(github => {
-
- /**
- * Oktokit object to access github
- * @type {object}
- */
- octokit = new Octokit({
- auth: github.access_token
- })
-
- //Test the authentication
- octokit.users.getAuthenticated({}).then(result => {
- currentUser = result.data.login
- this.showProjectsToLoad()
- })
- })
- }
-
- /**
- * Display projects which can be loaded in the popup.
- */
- this.showProjectsToLoad = function(){
- //Remove everything in the popup now
- while (popup.firstChild) {
- popup.removeChild(popup.firstChild)
- }
-
- //Close button (Mac style)
- if(GlobalVariables.topLevelMolecule && GlobalVariables.topLevelMolecule.name != "Maslow Create"){ //Only offer a close button if there is a project to go back to
- var closeButton = document.createElement("button")
- closeButton.setAttribute("class", "closeButton")
- closeButton.addEventListener("click", () => {
- popup.classList.add('off')
- })
- popup.appendChild(closeButton)
- }
- //Welcome title
- var welcome = document.createElement("div")
- welcome.setAttribute("style", " display: flex; margin: 10px; align-items: center;")
- popup.appendChild(welcome)
-
- var welcome1 = document.createElement("IMG")
- welcome1.setAttribute("src", "/imgs/maslow-logo.png" )
- welcome1.setAttribute("style", " height:25px; border-radius:50%;")
- welcome.appendChild(welcome1)
- var welcome2 = document.createElement("IMG")
- welcome2.setAttribute("src", "/imgs/maslowcreate.svg" )
- welcome2.setAttribute("style", "height:20px; padding: 10px;")
- welcome.appendChild(welcome2)
- var middleBrowseDiv = document.createElement("div")
- if (currentUser == null){
-
- var githubSign = document.createElement("button")
- githubSign.setAttribute("id", "loginButton2" )
- githubSign.setAttribute("class", "form browseButton githubSign")
- githubSign.setAttribute("style", "width: 90px; font-size: .7rem; margin-left: auto;")
- githubSign.textContent = "Login"
- welcome.appendChild(githubSign)
-
- var githubSignUp = document.createElement("button")
- githubSignUp.setAttribute("class", "form browseButton githubSign")
- githubSignUp.setAttribute("onclick", "window.open('https://github.com/join')")
- githubSignUp.setAttribute("style", "width: 130px; font-size: .7rem;margin-left: 5px;")
- githubSignUp.textContent = "Create an account"
- welcome.appendChild(githubSignUp)
-
- //Welcome title
- var welcome3 = document.createElement("div")
- welcome3.innerHTML = "Maslow Create User Projects"
- welcome3.setAttribute("style", "justify-content: flex-start; display: inline; width: 100%; font-size: 18px;")
- popup.appendChild(welcome3)
-
- middleBrowseDiv.setAttribute("style","margin-top:25px")
-
- githubSign.addEventListener("mousedown", () => {
- this.tryLogin()
- })
- }
-
- popup.classList.remove('off')
- popup.setAttribute("style", "padding: 0;text-align: center; background-color: #f9f6f6; border: 10px solid #3e3d3d;")
-
- var tabButtons = document.createElement("DIV")
- tabButtons.setAttribute("class", "tab")
- popup.appendChild(tabButtons)
-
- middleBrowseDiv.setAttribute("class", "middleBrowse")
- popup.appendChild(middleBrowseDiv)
-
- var searchIcon = document.createElement("IMG")
- searchIcon.setAttribute("src", '/imgs/search_icon.svg')
- searchIcon.setAttribute("style", "width: 20px; float: right; color: white; position: relative;right: 3px; opacity: 0.5;")
- middleBrowseDiv.appendChild(searchIcon)
-
- var searchBar = document.createElement("input")
- searchBar.setAttribute("type", "text")
- searchBar.setAttribute("contenteditable", "true")
- searchBar.setAttribute("placeholder", "Search for project..")
- searchBar.setAttribute("class", "menu_search")
- searchBar.setAttribute("id", "project_search")
- middleBrowseDiv.appendChild(searchBar)
-
- //Display option buttons
- var browseDisplay1 = document.createElement("div")
- browseDisplay1.setAttribute("class", "browseDisplay")
- var listPicture = document.createElement("IMG")
- listPicture.setAttribute("src", '/imgs/list-with-dots.svg') //https://www.freeiconspng.com/img/1454
- listPicture.setAttribute("style", "height: 75%;padding: 3px;")
- browseDisplay1.appendChild(listPicture)
- middleBrowseDiv.appendChild(browseDisplay1)
- var browseDisplay2 = document.createElement("div")
- browseDisplay2.setAttribute("class", "browseDisplay active_filter")
- browseDisplay2.setAttribute("id", "thumb")
- var listPicture2 = document.createElement("IMG")
- listPicture2.setAttribute("src", '/imgs/thumb_icon.png')
- listPicture2.setAttribute("style", "height: 80%;padding: 3px;")
- browseDisplay2.appendChild(listPicture2)
- middleBrowseDiv.appendChild(browseDisplay2)
-
- //Input to search for projects
-
- searchBar.addEventListener('keydown', (e) => {
-
- this.loadProjectsBySearch("yoursButton",e, searchBar.value, "updated")
- this.loadProjectsBySearch("githubButton",e, searchBar.value, "stars") // updated just sorts content by most recently updated
- })
-
-
- this.projectsSpaceDiv = document.createElement("DIV")
- this.projectsSpaceDiv.setAttribute("class", "float-left-div")
- this.projectsSpaceDiv.setAttribute("style", "overflow-x: hidden; margin-top: 10px;")
- popup.appendChild(this.projectsSpaceDiv)
-
- const pageChange = document.createElement("div")
- const pageBack = document.createElement("button")
- pageBack.setAttribute("id", "back")
- pageBack.setAttribute("class", "page_change")
- pageBack.innerHTML = "‹"
-
- const pageForward = document.createElement("button")
- pageChange.appendChild(pageBack)
- pageChange.appendChild(pageForward)
- pageForward.setAttribute("id", "forward")
- pageForward.setAttribute("class", "page_change")
- pageForward.innerHTML = "›"
-
- popup.appendChild(pageChange)
-
-
- this.openTab(page)
-
- //Event listeners
-
- browseDisplay1.addEventListener("click", () => {
- // titlesDiv.style.display = "flex"
- browseDisplay2.classList.remove("active_filter")
- this.openTab(page)
- })
- browseDisplay2.addEventListener("click", () => {
- // titlesDiv.style.display = "none"
- browseDisplay2.classList.add("active_filter")
- this.openTab(page)
- })
- pageForward.addEventListener("click", () => {
- if (page >=1){ page +=1 }
- this.openTab(page)
- })
- pageBack.addEventListener("click", () => {
- if (page >1){page -=1}
- this.openTab(page)
- })
-
- }
-
- /**
- * Search for the name of a project and then return results which match that search.
- */
- this.loadProjectsBySearch = async function(tabName, ev, searchString, sorting, pageNumber, clear = true){
-
- if(ev.key == "Enter"){
- //Remove projects shown now
- if(clear){
- while (this.projectsSpaceDiv.firstChild) {
- this.projectsSpaceDiv.removeChild(this.projectsSpaceDiv.firstChild)
- }
- }
- // add initial projects to div
-
- //New project div
- if (currentUser !== null && clear){
- var browseDiv = document.createElement("div")
- browseDiv.setAttribute("class", "browseDiv")
- this.projectsSpaceDiv.appendChild(browseDiv)
-
- var createNewProject = document.createElement("div")
- createNewProject.setAttribute("class", "newProject")
-
- browseDiv.appendChild(createNewProject)
- this.NewProject("New Project", null, true, "")
- }
- //header for project list style display
- var titlesDiv = document.createElement("div")
- titlesDiv.setAttribute("id","titlesDiv")
- var titles = document.createElement("div")
- titles.innerHTML = ""
- titles.setAttribute("class","browseColumn")
- titlesDiv.appendChild(titles)
- var titles2 = document.createElement("div")
- titles2.innerHTML = "Project"
- titles2.setAttribute("class","browseColumn")
- titlesDiv.appendChild(titles2)
- var titles3 = document.createElement("div")
- titles3.innerHTML = "Creator"
- titles3.setAttribute("class","browseColumn")
- titlesDiv.appendChild(titles3)
- var titles4 = document.createElement("div")
- titles4.innerHTML = "Created on"
- titles4.setAttribute("class","browseColumn")
- titlesDiv.appendChild(titles4)
- var titles5 = document.createElement("div")
- titles5.innerHTML = "Last Modified"
- titles5.setAttribute("class","browseColumn")
- titlesDiv.appendChild(titles5)
-
- if (!document.getElementById("thumb").classList.contains("active_filter")){
- titlesDiv.style.display = "flex"
- titlesDiv.style.marginTop = "10px"
- browseDiv.style.width = "100%"
- createNewProject.style.height = "80px"
- }
-
- this.projectsSpaceDiv.appendChild(titlesDiv)
-
- //Load projects
- var query
- var owned
-
- var sortMethod = sorting //drop down input. temporarily inactive until we figure some better way to sort
- if(tabName == "yoursButton"){
- owned = true
- query = searchString + ' ' + 'fork:true user:' + currentUser + ' topic:maslowcreate'
- }
- else{
- owned = false
- query = searchString + ' topic:maslowcreate -user:' + currentUser
- }
-
- //Figure out how many repos this user has, search will throw an error if they have 0;
- // octokit.repos.list({
- // affiliation: 'owner',
- // })
-
- return octokit.search.repos({
- q: query,
- sort: sortMethod,
- per_page: 50,
- page: pageNumber,
- headers: {
- accept: 'application/vnd.github.mercy-preview+json'
- }
- }).then(result => {
- result.data.items.forEach(repo => {
- const thumbnailPath = "https://raw.githubusercontent.com/"+repo.full_name+"/master/project.svg?sanitize=true"
-
- this.addProject(repo.name, repo.id, repo.owner.login, repo.created_at, repo.updated_at, owned, thumbnailPath)
- })
-
- })
- }
- }
-
- /**
- * Adds a new project to the load projects display.
- */
- this.NewProject = function(projectName, id, owned, thumbnailPath){
- //create a project element to display
-
- var project = document.createElement("DIV")
- project.classList.add("newProjectdiv")
-
- var projectPicture = document.createElement("IMG")
- projectPicture.setAttribute("src", thumbnailPath)
- projectPicture.setAttribute("onerror", "this.src='/defaultThumbnail.svg'")
- projectPicture.setAttribute("style", "height: 80%; float: left;")
- project.appendChild(projectPicture)
-
- var projectText = document.createElement("span")
- projectText.innerHTML = "Start a new project"
- projectText.setAttribute("style","align-self: center")
- project.appendChild(projectText)
-
- document.querySelector(".newProject").appendChild(project)
-
- project.addEventListener('click', () => {
- this.projectClicked(projectName, id, owned)
- })
-
- }
-
- /**
- * Adds a new project to the load projects display.
- */
- this.addProject = function(projectName, id, owner, createdAt, updatedAt, owned, thumbnailPath){
-
- this.projectsSpaceDiv.classList.remove("float-left-div-thumb")
- var project = document.createElement("DIV")
- var projectPicture = document.createElement("IMG")
- projectPicture.setAttribute("src", thumbnailPath)
- projectPicture.setAttribute("onerror", "this.src='/defaultThumbnail.svg'")
- project.appendChild(projectPicture)
- project.setAttribute("id", projectName)
- project.classList.add("project")
-
- if (owned){
- project.classList.add("mine")
- }
-
- //create a project element to display
- if (document.getElementById("thumb").classList.contains("active_filter")){
-
- projectPicture.setAttribute("style", "width: 100%; height: 80%;")
- project.appendChild(document.createElement("BR"))
-
- var shortProjectName
- if(projectName.length > 13){
- shortProjectName = document.createTextNode(projectName.substr(0,9)+"..")
- }
- else{
- shortProjectName = document.createTextNode(projectName)
- }
- project.setAttribute("title",projectName)
- project.appendChild(shortProjectName)
- }
- else{
- project.setAttribute("style", "display:flex; flex-direction:row; flex-wrap:wrap; width: 100%; border-bottom: 1px solid darkgrey;")
- projectPicture.setAttribute("class", "browseColumn")
-
- shortProjectName = document.createElement("DIV")
- shortProjectName.innerHTML = projectName
- shortProjectName.setAttribute("class", "browseColumn")
- project.appendChild(shortProjectName)
-
- var ownerName = document.createElement("DIV")
- var ownerNameIn = document.createTextNode(owner)
- ownerName.appendChild(ownerNameIn)
- ownerName.setAttribute("class", "browseColumn")
- project.appendChild(ownerName)
-
- var date = new Date(createdAt)
- var months = ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"]
- var createdTime = document.createElement("DIV")
- createdTime.setAttribute("class", "browseColumn")
- var createdTimeIn = document.createTextNode(months[date.getMonth()] + " " + date.getFullYear())
- createdTime.appendChild(createdTimeIn)
- project.appendChild(createdTime)
-
- var updated = new Date(updatedAt)
- var updatedTime = document.createElement("DIV")
- var updatedTimeIn = document.createTextNode(months[updated.getMonth()] + " " + date.getFullYear())
- updatedTime.appendChild(updatedTimeIn)
- updatedTime.setAttribute("class", "browseColumn")
- project.appendChild(updatedTime)
-
- }
-
- this.projectsSpaceDiv.appendChild(project)
-
- project.addEventListener('click', () => {
- this.projectClicked(projectName, id, owned)
- })
- }
-
- /**
- * Runs when you click on a project.
- */
- this.projectClicked = function(projectName, projectID, owned){
- //runs when you click on one of the projects
- if(projectName == "New Project"){
- this.createNewProjectPopup()
- }
- else if(owned){
- this.loadProject(projectName)
- }
- else{
- window.open('/run?'+projectID)
- }
- }
-
- /**
- * Runs owned search first and then full github search
- */
- this.openTab = function(page) {
-
- // Show the current tab, and add an "active" class to the button that opened the tab
- //Click on the search bar so that when you start typing it shows updateCommands
- document.getElementById('menuInput').focus()
-
- this.loadProjectsBySearch("yoursButton", {key: "Enter"}, document.getElementById("project_search").value, "updated", page, true)
- .then( () => {
- this.loadProjectsBySearch("githubButton", {key: "Enter"}, document.getElementById("project_search").value, "stars", page, false)
- })
- }
-
- /**
- * The popup to create a new project (giving it a name and whatnot).
- */
- this.createNewProjectPopup = function(){
- //Clear the popup and populate the fields we will need to create the new repo
-
- while (popup.firstChild) {
- popup.removeChild(popup.firstChild)
- }
- popup.setAttribute("style", "padding:2% 0")
- //Project name
- // <div class="form">
- var createNewProjectDiv = document.createElement("DIV")
- createNewProjectDiv.setAttribute("class", "form")
- createNewProjectDiv.setAttribute("style", "color:whitesmoke")
-
- //Add a title
- var header = document.createElement("H1")
- var title = document.createTextNode("Create a new project")
- header.appendChild(title)
- createNewProjectDiv.appendChild(header)
-
- //Create the form object
- var form = document.createElement("form")
- form.setAttribute("class", "login-form")
- createNewProjectDiv.appendChild(form)
-
- //Create the name field
- var name = document.createElement("input")
- name.setAttribute("id","project-name")
- name.setAttribute("type","text")
- name.setAttribute("placeholder","Project name")
- form.appendChild(name)
-
- //Add the description field
- var description = document.createElement("input")
- description.setAttribute("id", "project-description")
- description.setAttribute("type", "text")
- description.setAttribute("placeholder", "Project description")
- form.appendChild(description)
-
- //Grab all of the available licenses
- var licenseOptions = document.createElement('select')
- licenseOptions.setAttribute("id", "license-options")
- Object.keys(licenses).forEach( key => {
- var option = document.createElement('option')
- option.value = key
- option.text = key
- licenseOptions.appendChild(option)
- })
-
- form.appendChild(licenseOptions)
-
- //Add the button
- var createButton = document.createElement("button")
- createButton.setAttribute("type", "button")
- createButton.setAttribute("style", "height: 50px; border: 1px solid whitesmoke;")
- createButton.addEventListener('click', () => {
- this.createNewProject()
- })
- var buttonText = document.createTextNode("Create Project")
- createButton.appendChild(buttonText)
- form.appendChild(createButton)
-
- popup.appendChild(createNewProjectDiv)
-
- }
-
- /**
- * Open a new tab with a sharable copy of the project.
- */
- this.shareOpenedProject = function(){
- alert("A page with a shareable url to this project will open in a new window. Share the link to that page with anyone you would like to share the project with.")
-
-
- octokit.repos.get({
- owner: currentUser,
- repo: currentRepoName
- }).then(result => {
- var ID = result.data.id
- window.open('/run?'+ID)
- })
- }
-
- /**
- * Open a new tab with the github page for the project.
- */
- this.openGitHubPage = function(){
- //Open the github page for the current project in a new tab
- octokit.repos.get({
- owner: currentUser,
- repo: currentRepoName
- }).then(result => {
- var url = result.data.html_url
- window.open(url)
- })
- }
-
- /**
- * Open a new tab with the README page for the project.
- */
- this.openREADMEPage = function(){
- //Open the github page for the current project in a new tab
- octokit.repos.get({
- owner: currentUser,
- repo: currentRepoName
- }).then(result => {
- var url = result.data.html_url + '/blob/master/README.md'
- window.open(url)
- })
- }
-
- /**
- * Open a new tab with the Bill Of Materials page for the project.
- */
- this.openBillOfMaterialsPage = function(){
- //Open the github page for the current project in a new tab
- octokit.repos.get({
- owner: currentUser,
- repo: currentRepoName
- }).then(result => {
- var url = result.data.html_url + '/blob/master/BillOfMaterials.md'
- window.open(url)
- })
- }
-
- /**
- * Search github for projects which match a string.
- */
- this.searchGithub = async (searchString,owned) => {
- //Load projects
- var query
-
- if(owned){
- query = searchString + ' ' + 'user:' + currentUser + ' topic:maslowcreate'
- }
- else{
- query = searchString + ' topic:maslowcreate -user:' + currentUser
- }
-
- return await octokit.search.repos({
- q: query,
- sort: 'stars',
- per_page: 10,
- page: 1,
- headers: {
- accept: 'application/vnd.github.mercy-preview+json'
- }
- })
- }
-
- /**
- * Send user to GitHub settings page to delete project.
- */
- this.deleteProject = function(){
- //Open the github page for the current project in a new tab
- octokit.repos.get({
- owner: currentUser,
- repo: currentRepoName
- }).then(result => {
- var url = result.data.html_url + '/settings'
- window.open(url)
- })
- }
-
- /**
- * Open pull request if it's a forked project.
- */
- this.makePullRequest = function(){
-
- //Open the github page for making a pull request to the current project in a new tab
- octokit.repos.get({
- owner: currentUser,
- repo: currentRepoName
- }).then(result => {
-
- const webString = "https://github.com/" + result.data.parent.full_name + "/compare/" + result.data.parent.default_branch + "..." + result.data.owner.login + ":" + result.data.default_branch
-
- window.open(webString)
- })
- }
-
- /**
- * Creates a new blank project.
- */
- this.createNewProject = function(){
-
- if(typeof intervalTimer != undefined){
- clearInterval(intervalTimer) //Turn of auto saving
- }
-
- //Get name and description
- const name = document.getElementById('project-name').value
- const description = document.getElementById('project-description').value
- const licenseText = licenses[document.getElementById('license-options').value]
-
- //Load a blank project
- GlobalVariables.topLevelMolecule = new Molecule({
- x: 0,
- y: 0,
- topLevel: true,
- name: name,
- atomType: "Molecule",
- uniqueID: GlobalVariables.generateUniqueID()
- })
-
- GlobalVariables.currentMolecule = GlobalVariables.topLevelMolecule
-
- //Create a new repo
- octokit.repos.createForAuthenticatedUser({
- name: name,
- description: description
- }).then(result => {
- //Once we have created the new repo we need to create a file within it to store the project in
- currentRepoName = result.data.name
- var jsonRepOfProject = GlobalVariables.topLevelMolecule.serialize()
- jsonRepOfProject.filetypeVersion = 1
- jsonRepOfProject.circleSegmentSize = GlobalVariables.circleSegmentSize
- const projectContent = window.btoa(JSON.stringify(jsonRepOfProject, null, 4))
-
- octokit.repos.createOrUpdateFileContents({
- owner: currentUser,
- repo: currentRepoName,
- path: "project.maslowcreate",
- message: "initialize repo",
- content: projectContent
- }).then(() => {
- //Then create the BOM file
- var content = window.btoa(bomHeader) // create a file with just the header in it and base64 encode it
- octokit.repos.createOrUpdateFileContents({
- owner: currentUser,
- repo: currentRepoName,
- path: "BillOfMaterials.md",
- message: "initialize BOM",
- content: content
- }).then(() => {
- //Then create the README file
- content = window.btoa(readmeHeader) // create a file with just the word "init" in it and base64 encode it
- octokit.repos.createOrUpdateFileContents({
- owner: currentUser,
- repo: currentRepoName,
- path: "README.md",
- message: "initialize README",
- content: content
- }).then(() => {
- octokit.repos.createOrUpdateFileContents({
- owner: currentUser,
- repo: currentRepoName,
- path: "project.svg",
- message: "SVG Picture",
- content: ""
- }).then(()=>{
- octokit.repos.createOrUpdateFileContents({
- owner: currentUser,
- repo: currentRepoName,
- path: ".gitattributes",
- message: "Create gitattributes",
- content: window.btoa("data binary")
- }).then(()=>{
- octokit.repos.createOrUpdateFileContents({
- owner: currentUser,
- repo: currentRepoName,
- path: "data.json",
- message: "Data file",
- content: ""
- }).then(()=>{
- octokit.repos.createOrUpdateFileContents({
- owner: currentUser,
- repo: currentRepoName,
- path: "LICENSE.txt",
- message: "Establish license",
- content: window.btoa(licenseText)
- }).then(()=>{
- intervalTimer = setInterval(() => { this.saveProject() }, 1200000) //Save the project regularly
- })
- })
- })
- })
- })
- })
- })
-
- //Update the project topics
- octokit.repos.replaceAllTopics({
- owner: currentUser,
- repo: currentRepoName,
- names: ["maslowcreate", "maslowcreate-project"],
- headers: {
- accept: 'application/vnd.github.mercy-preview+json'
- }
- })
- })
-
- GlobalVariables.currentMolecule.backgroundClick()
-
- //Clear and hide the popup
- while (popup.firstChild) {
- popup.removeChild(popup.firstChild)
- }
- popup.classList.add('off')
-
-
- }
-
- /**
- * Save the current project to github.
- */
- this.saveProject = function(){
-
- //Save the current project into the github repo
- if(currentRepoName != null){
-
- //Store the target repo incase a new project is loaded during the save
- const saveRepoName = currentRepoName
- const saveUser = currentUser
-
- if(typeof intervalTimer != undefined){
- clearInterval(intervalTimer) //Turn off auto saving to prevent it from saving again during this save
- }
- this.progressSave(0)
- // var shape = null
-
- // if(GlobalVariables.topLevelMolecule.value != null && typeof GlobalVariables.topLevelMolecule.value != 'number'){
- // shape = GlobalVariables.topLevelMolecule.value
- // }
-
- const passBOMOn = (bomItems) => {
- const values = {op: "svg", readPath: GlobalVariables.topLevelMolecule.path}
- const {answer} = window.ask(values)
- answer.then( answer => {
- this.progressSave(10)
-
- var contentSvg = answer //Would compute the svg picture here
-
- var bomContent = bomHeader
-
- var totalParts = 0
- var totalCost = 0
- if(bomItems != undefined){
- bomItems.forEach(item => {
- totalParts += item.numberNeeded
- totalCost += item.costUSD
- bomContent = bomContent + "\n|" + item.BOMitemName + "|" + item.numberNeeded + "|$" + item.costUSD.toFixed(2) + "|" + convertLinks(item.source) + "|"
- })
- }
- bomContent = bomContent + "\n|" + "Total: " + "|" + totalParts + "|$" + totalCost.toFixed(2) + "|" + " " + "|"
- bomContent = bomContent+"\n\n 3xCOG MSRP: $" + (3*totalCost).toFixed(2)
-
- var readmeContent = readmeHeader + "\n\n" + "# " + saveRepoName + "\n\n\n\n"
- GlobalVariables.topLevelMolecule.requestReadme().forEach(item => {
- readmeContent = readmeContent + item + "\n\n\n"
- })
-
- var jsonRepOfProject = GlobalVariables.topLevelMolecule.serialize()
- jsonRepOfProject.filetypeVersion = 1
- jsonRepOfProject.circleSegmentSize = GlobalVariables.circleSegmentSize
- const projectContent = JSON.stringify(jsonRepOfProject, null, 4)
-
- var decoder = new TextDecoder('utf8')
- var finalSVG = decoder.decode(contentSvg)
-
-
- const askJsonVals = {op: "getJSON", readPath: GlobalVariables.topLevelMolecule.path}
- const {answer: answer2} = window.ask(askJsonVals)
- answer2.then( JSONData => {
-
- this.createCommit(octokit,{
- owner: saveUser,
- repo: saveRepoName,
- changes: {
- files: {
- 'BillOfMaterials.md': bomContent,
- 'README.md': readmeContent,
- 'project.svg': finalSVG,
- 'project.maslowcreate': projectContent,
- 'data.json': JSONData
- },
- commit: 'Autosave'
- }
- })
- })
-
- intervalTimer = setInterval(() => this.saveProject(), 1200000)
- })
- }
- extractBomTags(GlobalVariables.topLevelMolecule.path, passBOMOn)
- }
- }
-
- /**
- * Creates saving/saved pop up
- */
- this.progressSave = function (progress, saving = true) {
-
- progress = Math.max(0, progress) //Make it so the progress can't be displayed negitive
-
- var popUp = document.getElementById("popUp")
- let popUpBox = document.querySelector('#Progress_Status')
- //var width = 1;
- popUp.setAttribute("style","display:block")
- popUpBox.setAttribute("style","display:block")
-
- if (progress >= 100) {
- popUp.style.width = progress + '%'
- if(saving){
- popUp.textContent = "Project Saved"
- setTimeout(function() {
- popUp.setAttribute("style","display:none")
- popUpBox.setAttribute("style","display:none")
- }, 4000)
- }
- else{
- popUp.setAttribute("style","display:none")
- popUpBox.setAttribute("style","display:none")
- }
- } else {
- if(saving){
- popUp.textContent = "Saving..."
- }
- else{
- popUp.textContent = "Loading..."+progress.toFixed(1)+"%"
- }
- popUp.style.width = progress + '%'
- }
- }
-
- /**
- * Create a commit as part of the saving process.
- */
- this.createCommit = async function(octokit, { owner, repo, base, changes }) {
- this.progressSave(30)
- let response
-
- if (!base) {
- response = await octokit.repos.get({ owner, repo })
- base = response.data.default_branch
- }
- this.progressSave(40)
-
- response = await octokit.repos.listCommits({
- owner,
- repo,
- sha: base,
- per_page: 1
- })
-
- let latestCommitSha = response.data[0].sha
- const treeSha = response.data[0].commit.tree.sha
- this.progressSave(60)
-
- response = await octokit.git.createTree({
- owner,
- repo,
- base_tree: treeSha,
- tree: Object.keys(changes.files).map(path => {
- if(changes.files[path] != null){
- return {
- path,
- mode: '100644',
- content: changes.files[path]
- }
- }
- else{
- return {
- path,
- mode: '100644',
- sha: null
- }
- }
- })
- })
- const newTreeSha = response.data.sha
- this.progressSave(80)
-
- response = await octokit.git.createCommit({
- owner,
- repo,
- message: changes.commit,
- tree: newTreeSha,
- parents: [latestCommitSha]
- })
- latestCommitSha = response.data.sha
-
- this.progressSave(90)
-
- await octokit.git.updateRef({
- owner,
- repo,
- sha: latestCommitSha,
- ref: "heads/" + base,
- force: true
- })
- this.progressSave(100)
- console.warn("Project saved")
-
- }
-
- /**
- * Loads a project from github by name.
- */
- this.loadProject = async function(projectName){
-
- this.totalAtomCount = 0
- this.numberOfAtomsToLoad = 0
-
- GlobalVariables.startTime = new Date().getTime()
-
- if(typeof intervalTimer != undefined){
- clearInterval(intervalTimer) //Turn off auto saving
- }
-
- //Clear and hide the popup
- while (popup.firstChild) {
- popup.removeChild(popup.firstChild)
- }
- popup.classList.add('off')
-
- currentRepoName = projectName
-
- //Load a blank project
- GlobalVariables.topLevelMolecule = new Molecule({
- x: 0,
- y: 0,
- topLevel: true,
- atomType: "Molecule"
- })
-
- GlobalVariables.currentMolecule = GlobalVariables.topLevelMolecule
-
- octokit.repos.getContent({
- owner: currentUser,
- repo: projectName,
- path: 'project.maslowcreate'
- }).then(result => {
- //content will be base64 encoded
- let rawFile = JSON.parse(atob(result.data.content))
-
- if(rawFile.circleSegmentSize){
- GlobalVariables.circleSegmentSize = rawFile.circleSegmentSize
- }
-
- if(rawFile.filetypeVersion == 1){
- GlobalVariables.topLevelMolecule.deserialize(rawFile)
- }
- else{
- GlobalVariables.topLevelMolecule.deserialize(this.convertFromOldFormat(rawFile))
- }
- })
- octokit.repos.get({
- owner: currentUser,
- repo: currentRepoName
- }).then(result => {
- GlobalVariables.fork = result.data.fork
- if(!GlobalVariables.fork){
- document.getElementById("pull_top").style.display = "none"
- }
- else{
- document.getElementById("pull_top").style.display = "inline"
- }
- })
-
- }
-
- this.convertFromOldFormat = function(json){
-
- var listOfMoleculeAtoms = json.molecules
-
- //Find the top level molecule
- var projectObject = listOfMoleculeAtoms.filter((molecule) => { return molecule.topLevel == true })[0]
- //Remove that element from the listOfMoleculeAtoms
- listOfMoleculeAtoms.splice(listOfMoleculeAtoms.findIndex(e => e.topLevel == true),1)
-
- //Recursive function to walk the tree and find molecule placeholders
- function walkForMolecules(projectObject){
- projectObject.allAtoms.forEach(function(atom, allAtomsIndex, allAtomsObject) {
- if(atom.atomType == "Molecule"){
-
- if(atom.allAtoms != undefined){ //If this molecule has allAtoms
- walkForMolecules(atom)//Walk it
- }
- else{ //Else replace it with a version which does have allAtoms from the list
- //Find the version in molecules list and plug it in
- allAtomsObject[allAtomsIndex] = listOfMoleculeAtoms.filter((molecule) => { return molecule.uniqueID == atom.uniqueID })[0]
- //Remove that element from the listOfMoleculeAtoms
- listOfMoleculeAtoms.splice(listOfMoleculeAtoms.findIndex(e => e.uniqueID == atom.uniqueID),1)
- }
- }
- })
- }
-
- //Find any placeholder molecules in there (this needs to be a full tree walk for everything to work)
- while(listOfMoleculeAtoms.length > 0){
- walkForMolecules(projectObject)
- }
-
- return projectObject
- }
-
- /**
- * Begins the automatic process of saving the project
- */
- this.beginAutosave = function(){
- intervalTimer = setInterval(() => this.saveProject(), 120000) //Save the project regularly
- }
-
- /**
- * Loads a project from github by its github ID.
- */
- this.getProjectByID = async function(id, saveUserInfo){
- let repo = await octokit.request('GET /repositories/:id', {id})
- //Find out the owners info;
- const user = repo.data.owner.login
- const repoName = repo.data.name
- const description = repo.data.description
- //Get the file contents
- let result = await octokit.repos.getContent({
- owner: user,
- repo: repoName,
- path: 'project.maslowcreate'
- })
-
- //If this is the top level we will save the rep info at the top level
- if(saveUserInfo){
- currentUser = user
- currentRepoName = repoName
- }
-
- let rawFile = JSON.parse(atob(result.data.content))
-
- rawFile.description = description
-
- if(rawFile.filetypeVersion == 1){
- return rawFile
- }
- else{
- return this.convertFromOldFormat(rawFile)
- }
- }
-
- /**
- * Loads a project's data from github by its github ID.
- */
- this.getProjectDataByID = async function(id){
- let repo = await octokit.request('GET /repositories/:id', {id})
- //Find out the owners info;
- const user = repo.data.owner.login
- const repoName = repo.data.name
-
- try{
- let jsonData = await octokit.repos.getContent({
- owner: user,
- repo: repoName,
- path: 'data.json'
- })
-
- jsonData = atob(jsonData.data.content)
- return jsonData
-
- }catch(err){
- console.warn("Unable to load project data from github...using full model")
- return false
- }
- }
-
- /**
- * Export a molecule as a new github project.
- */
- this.exportCurrentMoleculeToGithub = function(molecule){
-
- //Get name and description
- var name = molecule.name
- var description = "A stand alone molecule exported from Maslow Create"
-
- //Create a new repo
- octokit.repos.createForAuthenticatedUser({
- name: name,
- description: description
- }).then(result => {
- //Once we have created the new repo we need to create a file within it to store the project in
- var repoName = result.data.name
- var id = result.data.id
- var path = "project.maslowcreate"
- var content = window.btoa("init") // create a file with just the word "init" in it and base64 encode it
- octokit.repos.createOrUpdateFileContents({
- owner: currentUser,
- repo: repoName,
- path: path,
- message: "initialize repo",
- content: content
- }).then(() => {
-
- //Save the molecule into the newly created repo
-
- var path = "project.maslowcreate"
-
- molecule.topLevel = true //force the molecule to export in the long form as if it were the top level molecule
- var content = window.btoa(JSON.stringify(molecule.serialize({molecules: []}), null, 4)) //Convert the passed molecule object to a JSON string and then convert it to base64 encoding
-
- //Get the SHA for the file
- octokit.repos.getContent({
- owner: currentUser,
- repo: repoName,
- path: path
- }).then(result => {
- var sha = result.data.sha
-
- //Save the repo to the file
- octokit.repos.updateFile({
- owner: currentUser,
- repo: repoName,
- path: path,
- message: "export Molecule",
- content: content,
- sha: sha
- }).then(() => {
- //Replace the existing molecule now that we just exported
- molecule.replaceThisMoleculeWithGithub(id)
- })
- })
-
- })
-
- //Update the project topics
- octokit.repos.replaceTopics({
- owner: currentUser,
- repo: repoName,
- names: ["maslowcreate", "maslowcreate-molecule"],
- headers: {
- accept: 'application/vnd.github.mercy-preview+json'
- }
- })
-
- })
- }
-
- /**
- * Like a project on github by unique ID.
- */
- this.starProject = function(id){
- //Authenticate - Initialize with OAuth.io app public key
- OAuth.initialize('BYP9iFpD7aTV9SDhnalvhZ4fwD8')
- // Use popup for oauth
- OAuth.popup('github').then(github => {
-
- octokit = new Octokit({
- auth: github.access_token
- })
-
- octokit.request('GET /repositories/:id', {id}).then(result => {
- //Find out the information of who owns the project we are trying to like
-
- var user = result.data.owner.login
- var repoName = result.data.name
-
- octokit.repos.listTopics({
- owner: user,
- repo: repoName,
- headers: {
- accept: 'application/vnd.github.mercy-preview+json'
- }
- }).then(()=> {
- //Find out if the project has been starred and unstar if it is
- octokit.activity.checkStarringRepo({
- owner:user,
- repo: repoName
- }).then(() => {
- var button= document.getElementById("Star-button")
- button.setAttribute("class","browseButton")
- button.innerHTML = "Star"
- octokit.activity.unstarRepo({
- owner: user,
- repo: repoName
- })
- })
-
- }).then(() =>{
- var button= document.getElementById("Star-button")
- button.setAttribute("class","liked")
- button.innerHTML = "Starred"
- octokit.activity.starRepo({
- owner: user,
- repo: repoName
- })
- })
- })
- })
- }
- /**
- * Fork a project on github by unique ID.
- */
- this.forkByID = function(id){
-
- //Authenticate - Initialize with OAuth.io app public key
- OAuth.initialize('BYP9iFpD7aTV9SDhnalvhZ4fwD8')
- // Use popup for oauth
- OAuth.popup('github').then(github => {
-
- octokit = new Octokit({
- auth: github.access_token
- })
-
- octokit.request('GET /repositories/:id', {id}).then(result => {
- //Find out the information of who owns the project we are trying to fork
- var user = result.data.owner.login
- var repoName = result.data.name
-
- octokit.repos.listTopics({
- owner: user,
- repo: repoName,
- headers: {
- accept: 'application/vnd.github.mercy-preview+json'
- }
- }).then(result => {
- var topics = result.data.names
-
- //Create a fork of the project with the found user name and repo name under your account
- octokit.repos.createFork({
- owner: user,
- repo: repoName,
- headers: {
- accept: 'application/vnd.github.mercy-preview+json'
- }
- }).then(result => {
- var repoName = result.data.name
- //Manually copy over the topics which are lost in forking
- octokit.repos.replaceTopics({
- owner: result.data.owner.login,
- repo: result.data.name,
- names: topics,
- headers: {
- accept: 'application/vnd.github.mercy-preview+json'
- }
- }).then(() => {
-
- //Remove everything in the popup now
- while (popup.firstChild) {
- popup.removeChild(popup.firstChild)
- }
-
- popup.classList.remove('off')
- popup.setAttribute("style", "text-align: center")
-
- var subButtonDiv = document.createElement('div')
- subButtonDiv.setAttribute("class", "form")
-
- //Add a title
- var title = document.createElement("H3")
- title.appendChild(document.createTextNode("A copy of the project '" + repoName + "' has been copied and added to your projects. You can view it by clicking the button below."))
- subButtonDiv.setAttribute('style','color:white;')
- subButtonDiv.appendChild(title)
- subButtonDiv.appendChild(document.createElement("br"))
-
- var form = document.createElement("form")
- subButtonDiv.appendChild(form)
- var button = document.createElement("button")
- button.setAttribute("type", "button")
- button.setAttribute("style", "border:2px solid white;")
- button.appendChild(document.createTextNode("View Projects"))
- button.addEventListener("click", () => {
- window.location.href = '/'
- })
- form.appendChild(button)
- popup.appendChild(subButtonDiv)
- })
- })
- })
- })
- })
- }
-
- /**
- * Upload or remove files from github. Files with null content will be deleted.
- * @param {object} files A dictionary with paths as keys and the content as the answer.
- */
- this.uploadAFile = async function(files){
-
- await this.createCommit(octokit,{
- owner: currentUser,
- repo: currentRepoName,
- changes: {
- files: files,
- commit: 'Upload file'
- }
- })
- }
-
- /**
- * Get a file from github. Calback is called after the retrieved.
- */
- this.getAFile = async function(filePath){
-
- const result = await octokit.repos.getContent({
- owner: currentUser,
- repo: currentRepoName,
- path: filePath
- })
-
- //content will be base64 encoded
- let rawFile = atob(result.data.content)
- return rawFile
- }
-
- /**
- * Get a link to the raw version of a file on GitHub
- */
- this.getAFileRawPath = function(filePath){
- const rawPath = "https://raw.githubusercontent.com/" + currentUser + "/" + currentRepoName + "/main/" + filePath
- return rawPath
- }
-
- }